diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..a94d657c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Please create a bug report to help us solve any problems and improve the software +title: '' +labels: bug +assignees: '' +--- + +## Issue details + +### Description + +Please give a short description of the problem in one or two sentences... + +### Environment / Versions + +#### Software + + * OS: + * Node.js: + * whiteflag-js: + * (please list allother relevant software components) + +#### Blockchain + +Name of the used blockchain, if relevant... + +### Symptoms and Problem details + +#### Steps to reproduce + +Please describe what action(s) you performed... + +#### Expected result + +Please describe what you did expect to happen... + +#### Actual result + +Please describe what actually happened. Include relevant debug or trace level logs... diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..7cf2ae45 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,32 @@ +--- +name: Feature request +about: Feel free to suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +## Issue details + +### Description + +Please give a short description of the request in one or two sentences... + +### Rationale + +Please describe why you think the feature should be included in this software or added to the documentation. E.g. it is specified in the protocol standard but not yet implemented, it solves a practical problem, it enhances usability or interoperability, etc.... + +### Solution + +#### Specification + +Please provide a clear and exact description of the functionality you want to be added to the software. Refer to the correct parts of any standard, specification, etc. if applicable... + +#### Affected components + +Please list any component (including documentation) that is likely to be affected if this request is implemented... + +#### Alternatives + +Please give a clear and concise description of any alternative solutions or features you've considered... diff --git a/.github/workflows/nodejs.ubuntu.test.yaml b/.github/workflows/nodejs.ubuntu.test.yaml new file mode 100644 index 00000000..b962aa07 --- /dev/null +++ b/.github/workflows/nodejs.ubuntu.test.yaml @@ -0,0 +1,23 @@ +name: Ubuntu Test +on: + push: + branches: + - master + - develop +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04, ubuntu-latest] + node-version: [22, 24, 25] + steps: + - uses: actions/checkout@v2 + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: Install dependencies + run: npm install + - name: Run tests + run: npm test diff --git a/.github/workflows/nodejs.windows.test.yaml b/.github/workflows/nodejs.windows.test.yaml new file mode 100644 index 00000000..f7170a80 --- /dev/null +++ b/.github/workflows/nodejs.windows.test.yaml @@ -0,0 +1,23 @@ +name: Windows Test +on: + push: + branches: + - master + - develop +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest] + node-version: [22.x, 24.x, 25.x] + steps: + - uses: actions/checkout@v2 + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: Install dependencies + run: npm install + - name: Run tests + run: npm test diff --git a/LICENSE.md b/LICENSE.md index 96a6ed85..66129f4e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,18 +1,18 @@ -# Whiteflag API Software Dedication to the Public Domain +# Whiteflag Software Dedication to the Public Domain ## Preamble All persons and organisations that contributed to the initial development -of the Whiteflag Protocol and the Whiteflag API software did so -disinterestedly. +of the Whiteflag Protocol and the Whiteflag JavaScript Library software did +so disinterestedly. -## Whiteflag API Software CC0-1.0 Statement +## Whiteflag JavaScript Library CC0-1.0 Statement -Excluding third party software that is part of this distribution as -listed below, the Whiteflag API software and associated documentation is -dedicated to the public domain under the [Creative Commons CC0-1.0 Universal Public Domain Dedication](http://creativecommons.org/publicdomain/zero/1.0/) +Excluding third party software that may be part of this distribution, the +Whiteflag JavaScript Library software and associated documentation is dedicated +to the public domain under the [Creative Commons CC0-1.0 Universal Public Domain Dedication](http://creativecommons.org/publicdomain/zero/1.0/) statement, meaning that to the extent possible under law, the authors -and their organisations have waived all copyright and related or neighboring +and their organisations have waived all copyright and related or neighbouring rights to this work, allowing anyone to copy, modify, distribute and implement the work, even for commercial purposes, all without asking permission. @@ -26,12 +26,12 @@ the work, you should not imply endorsement by the authors or the affirmer. ## Third Party Software Dependencies -The Whiteflag API software requires a number of third party software packages, -which are not part of this distribution. These third party software packages -need to be obtained through their respective official distribution channels, -and may be licensed. +Where the Whiteflag JavaScript Library software requires third party +software packages, which are not part of this distribution. These third party +software packages need to be obtained through their respective official +distribution channels, and may be licensed. -## Legal Restrictions for the Usage of the Whiteflag API Software +## Legal Restrictions for the Usage of the Whiteflag software WARNING: THE USAGE OF SIGNS AND SIGNALS WITH THIS SOFTWARE IS SUBJECT TO LOCAL AND/OR INTERNATIONAL LAWS. Please take note of the following when diff --git a/README.md b/README.md index 7fb5dc02..b4f3ab2a 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,31 @@ # Whiteflag JavaScript Library +![GitHub latest release](https://img.shields.io/github/v/release/whiteflagprotocol/whiteflag-js?label=latest&logo=github&sort=semver) +![Ubuntu Test](https://github.com/WhiteflagProtocol/whiteflag-js/workflows/Ubuntu%20Test/badge.svg) +![Windows Test](https://github.com/WhiteflagProtocol/whiteflag-js/workflows/Windows%20Test/badge.svg) + ## Introduction [Whiteflag](https://www.whiteflagprotocol.org) is a fully neutral and -secure communciations protocol based on blockchain technology. It enables +secure communications protocol based on blockchain technology. It enables near real-time communication in armed conflicts and disasters to exchange early warning and status information to create shared situational awareness. The [Whiteflag Protocol specification](https://standard.whiteflagprotocol.org) is an open standard. The Whiteflag JavaScript Library (WFJSL) will be the reference implementation -of the Whiteflag Protocol in [TypeScript](https://www.typescriptlang.org/), +of the Whiteflag Protocol written in [TypeScript](https://developer.mozilla.org/en-US/docs/Glossary/TypeScript), and compiled to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), to support the development of Whiteflag-enabled applications in JavaScript. -The WFJSL is currently **in development** in order to seperate the Whiteflag +The WFJSL is currently **in development** in order to separate the Whiteflag core protocol code from the current reference implementation, i.e. the [Whiteflag API](https://api.whiteflagprotocol.org). This makes the code better to maintain and test in support of future Whiteflag protocol development, while ensuring reusability for other implementations. The WFJSL is to include all Whiteflag protocol features, but with minimal -dependencies. Therefore, the library is independent from any specific +dependencies. Therefore, the library is independent of any specific blockchain, database solution, user interface, etc. Version 1 of the WFJSL corresponds with version 1 of the protocol, and @@ -31,10 +35,20 @@ functionality and corresponding `1.x` subversions are defined as ## Documentation -All detailed documentation of the WFJSL programming interface will become -available at [js.whiteflagprotocol.org](https://js.whiteflagprotocol.org/). +Detailed documentation of the WFJSL programming interface is available +at [js.whiteflagprotocol.org](https://js.whiteflagprotocol.org/). The documentation is also found in this repository in the `docs/` directory. +## Testing + +Testing of the software is done with the [Mocha](https://mochajs.org/) +test framework. To do a full test and run all the test scripts, use the +following NPM command in the project root: + +```{sh} +npm test +``` + ## License and Third Party Software The WFJSL software is dedicated to the public domain under the @@ -42,7 +56,4 @@ The WFJSL software is dedicated to the public domain under the statement. See `LICENSE.md` for details. The library may require third party software packages, which are not part of -this distribution and may be licenced differently. The third party software -dependencies of the WFJSL are: - -* the [Mocha](https://mochajs.org/) for testing the software +this distribution and may be licensed differently. diff --git a/dist/core/LICENSE.md b/dist/core/LICENSE.md new file mode 100644 index 00000000..66129f4e --- /dev/null +++ b/dist/core/LICENSE.md @@ -0,0 +1,53 @@ +# Whiteflag Software Dedication to the Public Domain + +## Preamble + +All persons and organisations that contributed to the initial development +of the Whiteflag Protocol and the Whiteflag JavaScript Library software did +so disinterestedly. + +## Whiteflag JavaScript Library CC0-1.0 Statement + +Excluding third party software that may be part of this distribution, the +Whiteflag JavaScript Library software and associated documentation is dedicated +to the public domain under the [Creative Commons CC0-1.0 Universal Public Domain Dedication](http://creativecommons.org/publicdomain/zero/1.0/) +statement, meaning that to the extent possible under law, the authors +and their organisations have waived all copyright and related or neighbouring +rights to this work, allowing anyone to copy, modify, distribute and implement +the work, even for commercial purposes, all without asking permission. + +In no way are the patent or trademark rights of any person affected by this +dedication to the public domain, nor are the rights that other persons may +have in the work or in how the work is used, such as publicity or privacy +rights. Unless expressly stated otherwise, the authors and their organisations +make no warranties about the work, and disclaim liability for all uses of the +work, to the fullest extent permitted by applicable law. When using or citing +the work, you should not imply endorsement by the authors or the affirmer. + +## Third Party Software Dependencies + +Where the Whiteflag JavaScript Library software requires third party +software packages, which are not part of this distribution. These third party +software packages need to be obtained through their respective official +distribution channels, and may be licensed. + +## Legal Restrictions for the Usage of the Whiteflag software + +WARNING: THE USAGE OF SIGNS AND SIGNALS WITH THIS SOFTWARE IS SUBJECT +TO LOCAL AND/OR INTERNATIONAL LAWS. Please take note of the following when +using this software: + +- This software only provides an additional means of communication for + certain signs and signals, and it does not replace any existing + standard, regulation or means of communication, whether mandatory or + not, including but not limited to physical signs, radio communications, + official registers, etc. + +- The usage of protective signs is subject to International Humanitarian + Law. Misuse of protective signs is a punishable violation under local + and international laws. + +- The usage of emergency signals may be subject to various regulations and + standards, depending on location and context. The misuse of emergency + signals, including misuse of the duress functionality, may be a + punishable violation under local and international laws. diff --git a/dist/core/README.md b/dist/core/README.md new file mode 100644 index 00000000..85abfafb --- /dev/null +++ b/dist/core/README.md @@ -0,0 +1,104 @@ +# WFJSL Core Implementation + +## Overview + +The Whiteflag JavaScript Library (WFJSL) is an implementation of the Whiteflag +Protocol written in [TypeScript](https://developer.mozilla.org/en-US/docs/Glossary/TypeScript), +and compiled to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), +to support the development of Whiteflag-enabled applications in JavaScript. + +The `@whiteflagprotocol/core` package provides the modules that define +Whiteflag core protocol features as specified in the Whiteflag standard. +As such, this package is not a fully functional implementation of the +protocol, but separates core protocol functions from implementation-specific +design decisions. Therefore, this package is not intended to be used directly +by implementations of Whiteflag; instead, the `@whiteflagprotocol/main` should +be used. + +This description provides a generic overview of the WFJSL core package. +Please see the [WFJSL TypeDoc documentation](https://js.whiteflagprotocol.org/typedoc) +for a detailed description of all classes and functions. + +## Whiteflag Message class + +The Whiteflag message class `WfCoreMessage` defined in the `message` module +represents a Whiteflag message. The class contains the methods to create, +set field values, encode and encrypt a Whiteflag message. Please note that +there normally is no need to use the `WfCoreMessage` directly. Instead, the +`WfMessage` child class of the `@whiteflagprotocol/main` package is the main +class to use for Whiteflag message, as this extended class provides methods to +process the metadata required for full protocol functionality. + +A new message may be created using the constructor, or by using a static +factory method. For example, creating a new FreeText message (message +code `F`) and set the `Text` field, may be done as follows: + +```{javascript} +let wfMessage = new WfCoreMessage('F'); +WfCoreMessage.set('Text', 'Example text to be sent with the FreeText message'); +``` + +The `encode()` method encodes the message. The `WfCoreMessage` class +automatically verifies the fields and values when encoding and decoding. +Encoding and decoding are asynchronous, meaning the functions return +[Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). +Once encoded, the methods `toHex()` and `toU8a()` may be used to obtain the +encoded message as a hexadecimal string or a UInt8array, respectively. + +```{javascript} +await wfMessage.encode(); +const hexMessage = wfMessage.toHex(); +``` + +If a message is encoded, or decoded, the message is "final", meaning its +content cannot be changed. Decoding a message is done using a one of the +static factory methods such as `fromHex(...)` or `fromU8a(...)`, since the +message type is probably not known before decoding. + +```{javascript} +wfMessage = await WfCoreMessage.fromHex(hexMessage); +``` + +Encryption and decryption is automatically performed upon encoding and +decoding, based on the value of the `EncryptionIndicator` field in the message +header. Since the `WfCoreMessage` class does not hold any metadata, all +encryption and decryption parameters must be provided to the respective method +when encoding or decoding a message. + +The `message` module, also provides the following functions. These functions +are used by the `WfCoreMessage` class, but may also be used for alternative +processing of Whiteflag messages. + +| Function | Purpose | +|-------------------|--------------------------------------------------| +| `isValidMessage` | Checks if an object is a valid Whiteflag message | +| `validateMessage` | Checks a message object for validation errors | +| `encryptMessage` | Encrypts a binary encoded message | +| `decryptMessage` | Decrypts an encrypted binary message | + +Both validation functions may be used for both plain JavaScript objects and +objects of the `WfCoreMessage` class. The encryption and decryption functions +work only on binary encoded messages provided as a `BinaryBuffer`. + +## Whiteflag message field encoding + +The `codec` module provides the encoding and decoding for each field in +a Whiteflag message i.a.w. the Whiteflag specification. The available field +encodings are defined with the `WfCodec` enum. For the encoding, decoding, and +verification of field values, the module provides the following functions. + +| Function | Purpose | +|----------------|------------------------------------| +| `encodeField` | Encodes a Whiteflag message field | +| `decodeField` | Decodes a Whiteflag message field | +| `isValidValue` | Checks if the field value is valid | + +These functions are used by the `WfCoreMessage` class when encoding and +decoding a message. + +## Whiteflag versions + +Currently, only one version of the Whiteflag protocol has been developed. For +ease of implementation of future Whiteflag versions, WFJSL functions and +classes take the Whiteflag version into account. The `versions` module defines +the available Whiteflag versions with the `WfVersions` enum. diff --git a/dist/core/index.d.ts b/dist/core/index.d.ts new file mode 100644 index 00000000..19dd00ab --- /dev/null +++ b/dist/core/index.d.ts @@ -0,0 +1,10 @@ +/** + * @module core + * @summary Whiteflag JS core protocol functionality + * @document docs/md/modules.md + * @primaryExport + */ +export { encodeField, decodeField, isValidValue } from './lib/codec.ts'; +export { WfProtocolError, WfErrorCode } from './lib/errors.ts'; +export { WfMsgType, WfCoreMessage, isValidMessage, validateMessage, encryptMessage, decryptMessage } from './lib/message.ts'; +export { WfVersion } from './lib/versions.ts'; diff --git a/dist/core/index.js b/dist/core/index.js new file mode 100644 index 00000000..1f76546d --- /dev/null +++ b/dist/core/index.js @@ -0,0 +1,5 @@ +'use strict'; +export { encodeField, decodeField, isValidValue } from "./lib/codec.js"; +export { WfProtocolError, WfErrorCode } from "./lib/errors.js"; +export { WfMsgType, WfCoreMessage, isValidMessage, validateMessage, encryptMessage, decryptMessage } from "./lib/message.js"; +export { WfVersion } from "./lib/versions.js"; diff --git a/dist/core/lib/codec.d.ts b/dist/core/lib/codec.d.ts new file mode 100644 index 00000000..429e464f --- /dev/null +++ b/dist/core/lib/codec.d.ts @@ -0,0 +1,63 @@ +/** + * @module core/codec + * @summary Whiteflag JS message field encoding and decoding module + */ +export { WfCodec, encodeField, decodeField, isValidValue }; +import { BinaryBuffer } from '@whiteflagprotocol/util'; +import { WfVersion } from './versions.ts'; +/** + * Whiteflag field encodings, defining the encoding of Whiteflag + * message fields as defined by the Whiteflag specification + * @enum WfCodec + * @wfversion v1-draft.7 + * @wfreference 4.1.2 Message Encoding + */ +declare enum WfCodec { + /** Binary field */ + BIN = "binary", + /** Decimal field */ + DEC = "decimal", + /** HExadecimal field */ + HEX = "hexadecimal", + /** UTF-8 / ASCII text field */ + UTF8 = "utf-8", + /** Datetime field */ + DATETIME = "datetime", + /** Duration field */ + DURATION = "duration", + /** Latitude field */ + LAT = "latitude", + /** Longitude field */ + LONG = "longitude" +} +/** + * Encodes a Whiteflag message field + * @function encodeField + * @wfversion v1-draft.7 + * @wfreference 4.1.2 Message Encoding, 4.1.3 Message Compression + * @param value the message field value + * @param codec the message field encoding: 'utf-8', 'bin', 'dec', 'hex', 'datetime', 'duration', 'lat', 'long' + * @param version the version of the Whiteflag specification + * @returns a binary buffer with the compressed encoded field + */ +declare function encodeField(value: string, codec: WfCodec, version?: WfVersion): BinaryBuffer; +/** + * Decodes a Whiteflag message field + * @function decodeField + * @wfversion v1-draft.7 + * @wfreference 4.1.2 Message Encoding, 4.1.3 Message Compression + * @param buffer a binary buffer with the encoded field + * @param codec the message field encoding: 'utf-8', 'bin', 'dec', 'hex', 'datetime', 'duration', 'lat', 'long' + * @param version the version of the Whiteflag specification + * @returns a string with the decoded field value + */ +declare function decodeField(buffer: BinaryBuffer, codec: WfCodec, version?: WfVersion): string; +/** + * Checks if the field value is valid + * @function isValidValue + * @param value the field value + * @param codec the field encoding + * @param version the Whiteflag protocol version + * @returns true if valid, else false + */ +declare function isValidValue(value: string, codec: WfCodec, version?: WfVersion): boolean; diff --git a/dist/core/lib/codec.js b/dist/core/lib/codec.js new file mode 100644 index 00000000..eace0c68 --- /dev/null +++ b/dist/core/lib/codec.js @@ -0,0 +1,228 @@ +'use strict'; +export { WfCodec, encodeField, decodeField, isValidValue }; +import { BinaryBuffer } from '@whiteflagprotocol/util'; +import { WfVersion } from "./versions.js"; +import fieldSpec_v1 from '../static/v1/wf-field-encoding.json' with { type: 'json' }; +const NOCHAR = ''; +const HEXRADIX = 16; +const BYTELENGTH = 8; +const QUADBIT = 4; +var WfCodec; +(function (WfCodec) { + WfCodec["BIN"] = "binary"; + WfCodec["DEC"] = "decimal"; + WfCodec["HEX"] = "hexadecimal"; + WfCodec["UTF8"] = "utf-8"; + WfCodec["DATETIME"] = "datetime"; + WfCodec["DURATION"] = "duration"; + WfCodec["LAT"] = "latitude"; + WfCodec["LONG"] = "longitude"; +})(WfCodec || (WfCodec = {})); +const FIELDS = compileFieldCodecs(); +function encodeField(value, codec, version = WfVersion.v1) { + if (!isValidValue(value, codec, version)) { + throw new Error(`Value of ${codec} field does not match ${FIELDS[codec][version].pattern} pattern`); + } + switch (codec) { + case WfCodec.BIN: { + return encodeBin(value); + } + case WfCodec.DEC: + case WfCodec.HEX: { + return encodeBDX(value); + } + case WfCodec.UTF8: { + return encodeUTF(value); + } + case WfCodec.DATETIME: { + return encodeDatum(value); + } + case WfCodec.DURATION: { + return encodeDatum(value); + } + case WfCodec.LAT: { + return encodeLatLong(value); + } + case WfCodec.LONG: { + return encodeLatLong(value); + } + default: { + throw new Error(`Invalid message field encoding: ${codec}`); + } + } +} +function decodeField(buffer, codec, version = WfVersion.v1) { + if (FIELDS[codec][version].length > 0 + && buffer.length !== FIELDS[codec][version].length) { + throw new Error(`Invalid ${codec} binary field length: ${buffer.length} bits`); + } + switch (codec) { + case WfCodec.BIN: { + return decodeBin(buffer); + } + case WfCodec.DEC: + case WfCodec.HEX: { + return decodeBDX(buffer); + } + case WfCodec.UTF8: { + return decodeUTF(buffer); + } + case WfCodec.DATETIME: { + return decodeDatetime(buffer); + } + case WfCodec.DURATION: { + return decodeDuration(buffer); + } + case WfCodec.LAT: { + return decodeLat(buffer); + } + case WfCodec.LONG: { + return decodeLong(buffer); + } + default: { + throw new Error(`Invalid message field encoding: ${codec}`); + } + } +} +function isValidValue(value, codec, version = WfVersion.v1) { + return FIELDS[codec][version].regex.test(value); +} +function compileFieldCodecs() { + const codec = {}; + for (const type of Object.values(WfCodec)) { + codec[type] = {}; + { + const version = WfVersion.v1; + codec[type][version] = fieldSpec_v1[type]; + codec[type][version].regex = new RegExp(codec[type][version].pattern); + } + } + return codec; +} +function encodeBin(binStr) { + const bitLength = binStr.length; + const byteLength = Math.ceil(bitLength / BYTELENGTH); + let buffer = new Uint8Array(byteLength); + for (let bitIndex = 0; bitIndex < bitLength; bitIndex++) { + if (binStr.substring(bitIndex, bitIndex + 1) === '1') { + const byteCursor = Math.floor(bitIndex / BYTELENGTH); + const bitPosition = bitIndex % BYTELENGTH; + buffer[byteCursor] |= (0x80 >>> bitPosition); + } + } + return BinaryBuffer.fromU8a(buffer, bitLength); +} +function decodeBin(buffer) { + const bitLength = buffer.length; + const byteArray = buffer.toU8a(); + let binStr = ''; + for (let bitIndex = 0; bitIndex < bitLength; bitIndex++) { + const byteCursor = Math.floor(bitIndex / BYTELENGTH); + const bitPosition = bitIndex % BYTELENGTH; + if ((byteArray[byteCursor] >>> (BYTELENGTH - bitPosition - 1) & 1) == 1) { + binStr += '1'; + } + else { + binStr += '0'; + } + } + return binStr.toLowerCase(); +} +function encodeBDX(bdxString) { + const bitLength = bdxString.length * QUADBIT; + const buffer = new Uint8Array(Math.ceil(bitLength / BYTELENGTH)); + for (let i = 0; i < buffer.length; i++) { + const d = i * 2; + buffer[i] |= parseInt(bdxString.substring(d, d + 1) + '0', HEXRADIX); + buffer[i] |= parseInt('0' + bdxString.substring(d + 1, d + 2), HEXRADIX); + } + return BinaryBuffer.fromU8a(buffer, bitLength); +} +function decodeBDX(buffer) { + const bitLength = buffer.length - (buffer.length % QUADBIT); + const byteArray = buffer.extractU8a(0, bitLength); + let bdxString = ''; + for (let bitIndex = 0; bitIndex < bitLength; bitIndex += BYTELENGTH) { + const byteCursor = Math.floor(bitIndex / BYTELENGTH); + const byte = (byteArray[byteCursor] >> QUADBIT) & 0xF; + bdxString += byte.toString(HEXRADIX); + if ((bitIndex + QUADBIT) < bitLength) { + const byte = byteArray[byteCursor] & 0xF; + bdxString += byte.toString(HEXRADIX); + } + } + return bdxString.toLowerCase(); +} +function encodeUTF(utfString) { + const bitLength = utfString.length * BYTELENGTH; + const buffer = new Uint8Array(utfString.length); + for (let i = 0; i < buffer.length; i++) { + buffer[i] = utfString.charCodeAt(i); + } + return BinaryBuffer.fromU8a(buffer, bitLength); +} +function decodeUTF(buffer) { + const bitLength = buffer.length - (buffer.length % BYTELENGTH); + return String.fromCharCode(...buffer.extractU8a(0, bitLength)); +} +function encodeDatum(datumStr) { + return encodeBDX(datumStr.replace(/[-+:.A-Z]/g, NOCHAR)); +} +function decodeDatum(buffer) { + return decodeBDX(buffer); +} +function decodeDatetime(buffer) { + const value = decodeDatum(buffer); + return [ + value.slice(0, 4), '-', + value.slice(4, 6), '-', + value.slice(6, 8), 'T', + value.slice(8, 10), ':', + value.slice(10, 12), ':', + value.slice(12), 'Z' + ].join(NOCHAR); +} +function decodeDuration(buffer) { + const value = decodeDatum(buffer); + return [ + 'P', + value.slice(0, 2), 'D', + value.slice(2, 4), 'H', + value.slice(4), 'M' + ].join(NOCHAR); +} +function encodeLatLong(latlongStr) { + let buffer = encodeDatum(latlongStr); + if (latlongStr.startsWith('-')) { + buffer.insertBytes([0x00], 1); + } + ; + if (latlongStr.startsWith('+')) { + buffer.insertBytes([0x80], 1); + } + return buffer; +} +function decodeLatLong(buffer) { + const latlongStr = decodeDatum(buffer.extract(1, buffer.length)); + if (buffer.extractU8a(0, 1)[0] === 0x80) { + return '+' + latlongStr; + } + if (buffer.extractU8a(0, 1)[0] === 0x00) { + return '-' + latlongStr; + } + throw new SyntaxError('Invalid latlong encoding'); +} +function decodeLat(buffer) { + const value = decodeLatLong(buffer); + return [ + value.slice(0, 3), '.', + value.slice(3) + ].join(NOCHAR); +} +function decodeLong(buffer) { + const value = decodeLatLong(buffer); + return [ + value.slice(0, 4), '.', + value.slice(4) + ].join(NOCHAR); +} diff --git a/dist/core/lib/errors.d.ts b/dist/core/lib/errors.d.ts new file mode 100644 index 00000000..5cb0f4c0 --- /dev/null +++ b/dist/core/lib/errors.d.ts @@ -0,0 +1,51 @@ +/** + * @module core/errors + * @summary Whiteflag JS error classes + * @document docs/md/errors.md + */ +export { WfProtocolError, WfErrorCode, catchedError }; +/** + * Error class for Whiteflag protocol and message errors + * @class ProtocolError + * @extends {DomainError} + */ +declare class WfProtocolError extends Error { + /** The Whiteflag protocol error code */ + code: string; + /** Underlying causes of the error */ + causes: string[]; + /** + * Constructor for protocol errors + * @param message a human readable error message + * @param causes underlying errors causing this error + * @param code constant identifying the error + */ + constructor(message: string, causes: any, code?: WfErrorCode); +} +/** + * Defines Whiteflag protocol errors + * @enum WfErrorCode + */ +declare enum WfErrorCode { + /** Generic Whiteflag protocol error */ + PROTOCOL = "WF_PROTOCOL_ERROR", + /** Incorrect or missingWhiteflag message meta data */ + METAHEADER = "WF_METAHEADER_ERROR", + /** Whiteflag message format error */ + FORMAT = "WF_FORMAT_ERROR", + /** Whiteflag message reference error */ + REFERENCE = "WF_REFERENCE_ERROR", + /** Whiteflag message authentication error */ + AUTHENTICATION = "WF_AUTH_ERROR", + /** Whiteflag signature error */ + SIGNATURE = "WF_SIGN_ERROR", + /** Whiteflag encryption error */ + ENCRYPTION = "WF_ENCRYPTION_ERROR" +} +/** + * Processes a catched error in a type safe manner + * @param msg a generic message to use if no specific error message + * @param err the error to handle + * @returns a new error object + */ +declare function catchedError(msg?: string, err?: any): WfProtocolError; diff --git a/dist/core/lib/errors.js b/dist/core/lib/errors.js new file mode 100644 index 00000000..cf88d4e6 --- /dev/null +++ b/dist/core/lib/errors.js @@ -0,0 +1,41 @@ +'use strict'; +export { WfProtocolError, WfErrorCode, catchedError }; +class WfProtocolError extends Error { + code; + causes; + constructor(message, causes, code = WfErrorCode.PROTOCOL) { + super(message); + this.name = 'WfProtocolError'; + this.code = code; + this.causes = []; + if (Array.isArray(causes)) + this.causes = causes; + if (causes instanceof Error) + this.causes = [causes.message]; + if (typeof causes === 'string') + this.causes = [causes]; + } +} +var WfErrorCode; +(function (WfErrorCode) { + WfErrorCode["PROTOCOL"] = "WF_PROTOCOL_ERROR"; + WfErrorCode["METAHEADER"] = "WF_METAHEADER_ERROR"; + WfErrorCode["FORMAT"] = "WF_FORMAT_ERROR"; + WfErrorCode["REFERENCE"] = "WF_REFERENCE_ERROR"; + WfErrorCode["AUTHENTICATION"] = "WF_AUTH_ERROR"; + WfErrorCode["SIGNATURE"] = "WF_SIGN_ERROR"; + WfErrorCode["ENCRYPTION"] = "WF_ENCRYPTION_ERROR"; +})(WfErrorCode || (WfErrorCode = {})); +function catchedError(msg = 'Unspecified error', err = new Error('Unspecified error')) { + let code = WfErrorCode.PROTOCOL; + let message = msg; + let causes = []; + if (err instanceof Error) + causes = [err.message]; + if (err instanceof WfProtocolError) { + msg = err.message; + causes = err.causes; + code = err.code; + } + return new WfProtocolError(message, causes, code); +} diff --git a/dist/core/lib/message.d.ts b/dist/core/lib/message.d.ts new file mode 100644 index 00000000..a9810de3 --- /dev/null +++ b/dist/core/lib/message.d.ts @@ -0,0 +1,251 @@ +/** + * @module core/message + * @summary Whiteflag JS message class + */ +export { WfMsgType, WfCoreMessage, isValidMessage, validateMessage, encryptMessage, decryptMessage }; +import { WfCryptoMethod } from '@whiteflagprotocol/crypto'; +import { BinaryBuffer } from '@whiteflagprotocol/util'; +import { WfVersion } from './versions.ts'; +/** + * Whiteflag message types, defining the types of Whiteflag message + * as specified by the Whiteflag standard + * @enum WfFieldType + * @wfversion v1-draft.7 + * @wfreference 2.4.2.1 Functional Messages, 2.4.2.2 Management Messages + * + */ +declare enum WfMsgType { + /** Authentication message */ + A = "A", + /** Cryptographic support message */ + K = "K", + /** Test message */ + T = "T", + /** Protection sign */ + P = "P", + /** Protection sign */ + D = "D", + /** Status signal */ + S = "S", + /** Emergency signal */ + E = "E", + /** Infrstructure sign */ + I = "I", + /** Mission signal */ + M = "M", + /** Request signal */ + Q = "Q", + /** Reference message */ + R = "R", + /** Free text message */ + F = "F" +} +/** + * A core Whiteflag message as defined by the Whiteflag specification + * @class WfCoreMessage + * @wfversion v1-draft.7 + * @wfreference 4 Message Format + * @remarks Ths class represents a core Whiteflag message as + * defined by the Whiteflag specification. It has a message header and + * a message body which contain the message fields as specified for the + * message type. It performs the encoding/encryption and decoding/decryption + * to and from binary messages. Since the processing of Whiteflag messges + * in accordance with the protocol requires additional metadata, the extrended + * `WfMessage` class of the `@whitelag/protol` package should normallly be + * used instead of this class. + */ +declare class WfCoreMessage { + /** The message type */ + private type; + /** The Whiteflag protocol version */ + private version; + /** The message header containing the generic header fields */ + private header; + /** The message body containing the message type specific body fields */ + private body; + /** The binary encoded message */ + private binary; + /** Indicates if message is final and cannot be altered */ + private final; + /** + * Constructor for a Whiteflag message + * @param type the Whiteflag message type + * @param version the Whiteflag protocol version + * @param binary the binary encoded message, if available at creation + */ + constructor(type: WfMsgType, version?: WfVersion, binary?: BinaryBuffer); + /** + * Creates new Whiteflag message from a binary buffer + * @function fromBinary + * @param message a binary buffer with the encoded message + * @param ikm the input key material to derive the encryption key, if the message is encrypted + * @param address the binary encoded originator address, if the message is encrypted + * @param iv the initialisation vector, if required for the encryption method + * @returns a new Whiteflag message object with the decoded message + */ + static fromBinary(message: BinaryBuffer, ikm?: Uint8Array, address?: Uint8Array, iv?: Uint8Array): Promise; + /** + * Creates new Whiteflag message from a plain object + * @function fromObject + * @param message a plain JavaScript object with message header and body + * @returns a new Whiteflag message object + */ + static fromObject(message: any): Promise; + /** + * Creates new Whiteflag message from a hexadecimal encoded string + * @param message atring with the hexadecimal encoded message + * @param ikm the hexadecimalinput key material to derive the encryption key, if the message is encrypted + * @param address the hexadecimal encoded originator address, if the message is encrypted + * @param iv the hexadecimal initialisation vector, if required for the encryption method + * @returns a new Whiteflag message object with the decoded message + */ + static fromHex(message: string, ikm?: string, address?: string, iv?: string): Promise; + /** + * Creates new Whiteflag message from a binary encoded message + * @param message a Uint8Array with the binary encoded message + * @param ikm the input key material to derive the encryption key, if the message is encrypted + * @param address the binary encoded originator address, if the message is encrypted + * @param iv the initialisation vector, if required for the encryption method + * @returns a new Whiteflag message object with the decoded message + */ + static fromU8a(message: Uint8Array, ikm?: Uint8Array, address?: Uint8Array, iv?: Uint8Array): Promise; + /** + * Indicates if the message has already been encoded + * @function isEncoded + * @returns true if message has been encoded, else false + */ + isEncoded(): boolean; + /** + * Indicates if the message is valid, i.e. if all fields contain valid values + * @function isValid + * @returns true if message is valid, else false + */ + isValid(): boolean; + /** + * Returns message validation errors + * @function validate + * @returns an array of validation errors + */ + validate(): string[]; + /** + * Decodes an unencrypted binary encoded message + * @param message a binary encoded message + * @returns a new Whiteflag message object + */ + decode(message: BinaryBuffer): Promise; + /** + * Encodes the message, making the contents final + * @function encode + * @param ikm the input key material to derive the encryption key, if the message is to be encrypted + * @param address the binary encoded originator address, if the message is to be encrypted + * @param iv the initialisation vector, if required for the encryption method + * @returns this Whitedlag message object with the encoded message + */ + encode(ikm?: Uint8Array, address?: Uint8Array, iv?: Uint8Array): Promise; + /** + * Returns the value of the specified message field + * @function get + * @param fieldName the name of the message field + * @returns the value of the message field + */ + get(fieldName: string): string | null; + /** + * Sets the value of the specified message field, if the message has not been encoded + * @function set + * @param fieldName the name of the message field + * @param value the value to set + * @return true if succesful, else false + */ + set(fieldName: string, value: string): boolean; + /** + * Returns the Whiteflag message as a plain object + * @function toObject + * @returns the message as a plain object + */ + toObject(): Object; + /** + * Returns the Whiteflag message as a string + * @function toString + * @returns a concatinated string of field values + */ + toString(): string; + /** + * Returns the Whiteflag message encoded as a hexadecimal string + * @function toHex + * @returns a hexadecimal string with the encoded message + */ + toHex(): string; + /** + * Returns the encoded Whiteflag message as a UInt8array + * @function toU8a + * @returns a UInt8array with the encoded message + */ + toU8a(): Uint8Array; + /** + * Generates message header and sets known values + * @private + * @returns a Whiteflag message header object + */ + private generateHeader; + /** + * Decodes a field from a binary encoded message header + * @param message the binary encoded message + * @param field the header field to decode + * @returns the field value + */ + private decodeHeaderField; + /** + * Generates message body, or if specifed pseudo message body + * @private + * @@param type the pseudo message type, if test message body + * @returns a Whiteflag message body object + */ + private generateBody; + /** + * Decodes a field from a binary encoded message body + * @param message the binary encoded message + * @param field the body field to decode + * @param type the message type to override, e.g. for pseudo message body + * @param bitOffset the bit offset for dynamic fields + * @returns the field value + */ + private decodeBodyField; +} +/** + * Checks if an object is a valid Whiteflag message + * @function isValidMessage + * @param message the message object to validate + * @returns true if message is valid, else false + */ +declare function isValidMessage(message: any): boolean; +/** + * Checks a message object for validation errors + * @function validateMessage + * @param message the message object to validate + * @returns an array of validation errors + */ +declare function validateMessage(message: any): string[]; +/** + * Encrypts a binary encoded message + * @function encryptMessage + * @param message a binary buffer with the binary encoded message + * @param method the Whiteflag encryption method + * @param ikm the input key material to derive the encryption key + * @param address the binary encoded originator address + * @param iv the initialisation vector, if required for the encryption method + * @param version the Whiteflag protocol version + * @returns the encrypted message + */ +declare function encryptMessage(message: BinaryBuffer, method: WfCryptoMethod, ikm: Uint8Array, address: Uint8Array, iv?: Uint8Array, version?: WfVersion): Promise; +/** + * Decrypts an encrypted binary message + * @function decryptMessage + * @param message a binary buffer with the encrypted message + * @param method the Whiteflag encryption method + * @param ikm the input key material to derive the encryption key + * @param address the binary encoded originator address + * @param iv the initialisation vector, if required for the encryption method + * @param version the Whiteflag protocol version + * @returns the decrypted binary encoded message + */ +declare function decryptMessage(message: BinaryBuffer, method: WfCryptoMethod, ikm: Uint8Array, address: Uint8Array, iv?: Uint8Array, version?: WfVersion): Promise; diff --git a/dist/core/lib/message.js b/dist/core/lib/message.js new file mode 100644 index 00000000..75d990fc --- /dev/null +++ b/dist/core/lib/message.js @@ -0,0 +1,444 @@ +'use strict'; +export { WfMsgType, WfCoreMessage, isValidMessage, validateMessage, encryptMessage, decryptMessage }; +import { WfCryptoMethod, encrypt, decrypt, deriveKey } from '@whiteflagprotocol/crypto'; +import { BinaryBuffer, hexToU8a, isString } from '@whiteflagprotocol/util'; +import { WfVersion } from "./versions.js"; +import { WfProtocolError, WfErrorCode } from "./errors.js"; +import { decodeField, encodeField, isValidValue } from "./codec.js"; +import msgSpec_v1 from '../static/v1/wf-msg-structure.json' with { type: 'json' }; +const EMPTYPSTRING = ''; +const MSG_PREFIX = 'WF'; +const MSG_NOENCRYPT = '0'; +var WfMsgType; +(function (WfMsgType) { + WfMsgType["A"] = "A"; + WfMsgType["K"] = "K"; + WfMsgType["T"] = "T"; + WfMsgType["P"] = "P"; + WfMsgType["D"] = "D"; + WfMsgType["S"] = "S"; + WfMsgType["E"] = "E"; + WfMsgType["I"] = "I"; + WfMsgType["M"] = "M"; + WfMsgType["Q"] = "Q"; + WfMsgType["R"] = "R"; + WfMsgType["F"] = "F"; +})(WfMsgType || (WfMsgType = {})); +const MSGSPEC = compileMsgSpec(); +class WfCoreMessage { + type; + version = WfVersion.v1; + header = {}; + body = {}; + binary = BinaryBuffer.empty(); + final = false; + constructor(type, version = WfVersion.v1, binary) { + this.type = type; + this.version = version; + this.header = this.generateHeader(); + this.body = this.generateBody(); + if (binary instanceof BinaryBuffer) + this.binary = binary; + } + static async fromBinary(message, ikm, address, iv) { + let buffer = message; + const { prefix, version, encryption } = extractUnencryptedHeader(buffer); + if (!checkPrefix(prefix)) { + throw new WfProtocolError(`Message has no ${MSG_PREFIX} prefix`, null, WfErrorCode.FORMAT); + } + if (!checkVersion(version)) { + throw new WfProtocolError(`Undefined protocol version: ${version}`, null, WfErrorCode.FORMAT); + } + if (!checkEncryption(encryption)) { + throw new WfProtocolError(`Undefined encryption method: ${encryption}`, null, WfErrorCode.ENCRYPTION); + } + if (encryption !== MSG_NOENCRYPT) { + if (!ikm) + throw new Error('Missing encryption key'); + if (!address) + throw new Error('Missing orginator address'); + buffer = await decryptMessage(message, encryption, ikm, address, iv, version); + } + let type = extractHeaderField(buffer, 'MessageCode'); + if (!checkType(type)) { + throw new WfProtocolError(`Undefined message type: ${type}`, null, WfErrorCode.FORMAT); + } + const wfMessage = new this(type, version, message); + return wfMessage.decode(buffer); + } + static async fromObject(message) { + const errors = validateMessage(message); + if (errors.length > 0) + throw new WfProtocolError('Invalid message', errors, WfErrorCode.FORMAT); + const header = message.MessageHeader; + const body = message.MessageBody; + const wfMessage = new this(header['MessageCode'], header['Version']); + for (const field of Object.keys(header)) { + if (!wfMessage.set(field, header[field])) { + throw new WfProtocolError(`Header field ${field} could not be set`, null, WfErrorCode.FORMAT); + } + } + for (const field of Object.keys(body)) { + if (!wfMessage.set(field, body[field])) { + throw new WfProtocolError(`Body field ${field} could not be set`, null, WfErrorCode.FORMAT); + } + } + return wfMessage; + } + static async fromHex(message, ikm, address, iv) { + if (ikm && address && iv) { + return this.fromBinary(BinaryBuffer.fromHex(message), hexToU8a(ikm), hexToU8a(address), hexToU8a(iv)); + } + return this.fromBinary(BinaryBuffer.fromHex(message)); + } + static async fromU8a(message, ikm, address, iv) { + return this.fromBinary(BinaryBuffer.fromU8a(message), ikm, address, iv); + } + isEncoded() { + if (this.final) + return true; + return false; + } + isValid() { + if (this.validate().length > 0) + return false; + return true; + } + validate() { + return checkMsgSegments(this.header, this.body); + } + async decode(message) { + if (!this.final) { + let errors = []; + let msgSpec = MSGSPEC[this.type][this.version]; + for (const field of Object.keys(msgSpec.header)) { + if (!this.set(field, this.decodeHeaderField(message, field))) { + errors.push(`Header field ${field} could not be set`); + } + } + let type = this.type; + let offset = 0; + if (type === WfMsgType.T) { + const field = 'PseudoMessageCode'; + if (this.set(field, this.decodeBodyField(message, field))) { + const fieldSpec = MSGSPEC[type][this.version].body[field]; + offset = fieldSpec.endBit - fieldSpec.startBit; + type = this.body[field]; + } + else { + errors.push(`Body field ${field} could not be set`); + } + } + msgSpec = MSGSPEC[type][this.version]; + for (const field of Object.keys(msgSpec.body)) { + if (!this.set(field, this.decodeBodyField(message, field, type, offset))) { + errors.push(`Body field ${field} could not be set`); + } + } + if (errors.length === 0) + errors = this.validate(); + if (errors.length > 0) { + throw new WfProtocolError(`Cannot decode ${this.type} message`, errors, WfErrorCode.FORMAT); + } + this.final = true; + } + return this; + } + async encode(ikm, address, iv) { + if (!this.final) { + const errors = this.validate(); + if (errors.length > 0) { + throw new WfProtocolError('Cannot encode message', errors, WfErrorCode.FORMAT); + } + for (const field of Object.keys(this.header)) { + const encoding = MSGSPEC[this.type][this.version].header[field].encoding; + this.binary.append(encodeField(this.header[field], encoding, this.version)); + } + let type = this.type; + for (const field of Object.keys(this.body)) { + const encoding = MSGSPEC[type][this.version].body[field].encoding; + this.binary.append(encodeField(this.body[field], encoding, this.version)); + if (field === 'PseudoMessageCode') + type = this.body[field]; + } + if (this.header['EncryptionIndicator'] !== MSG_NOENCRYPT) { + if (!ikm) + throw new Error('Missing encryption key'); + if (!address) + throw new Error('Missing orginator address'); + this.binary = await encryptMessage(this.binary, this.header['EncryptionIndicator'], ikm, address, iv, this.header['Version']); + } + this.final = true; + } + return this; + } + get(fieldName) { + for (const field of Object.keys(this.header)) { + if (field === fieldName) + return this.header[field]; + } + for (const field of Object.keys(this.body)) { + if (field === fieldName) + return this.body[field]; + } + return null; + } + set(fieldName, value) { + if (this.final) + return false; + for (const field of Object.keys(this.header)) { + if (field === fieldName) { + if (field === 'Prefix' && value !== MSG_PREFIX) + return false; + if (field === 'Version' && value !== this.header[field]) + return false; + if (field === 'MessageCode' && value !== this.header[field]) + return false; + this.header[field] = value; + return true; + } + } + for (const field of Object.keys(this.body)) { + if (field === fieldName) { + if (field === 'PseudoMessageCode') { + this.body = this.generateBody(value); + } + else { + this.body[field] = value; + } + return true; + } + } + return false; + } + toObject() { + return { + MessageHeader: this.header, + MessageBody: this.body + }; + } + toString() { + let messageStr = EMPTYPSTRING; + if (this.isValid()) { + for (const field of Object.keys(this.header)) { + messageStr += this.header[field]; + } + for (const field of Object.keys(this.body)) { + messageStr += this.body[field]; + } + } + return messageStr; + } + toHex() { + if (this.final) + return this.binary.toHex(); + return EMPTYPSTRING; + } + toU8a() { + if (this.final) + return this.binary.toU8a(); + return new Uint8Array(0); + } + generateHeader() { + let header = {}; + for (const field of Object.keys(MSGSPEC[this.type][this.version].header)) { + header[field] = EMPTYPSTRING; + } + header['Prefix'] = MSG_PREFIX; + header['Version'] = this.version; + header['MessageCode'] = this.type; + return header; + } + decodeHeaderField(message, field) { + const msgSpec = MSGSPEC[this.type][this.version]; + return decodeField(message.extract(msgSpec.header[field]?.startBit, msgSpec.header[field]?.endBit), msgSpec.header[field]?.encoding); + } + generateBody(pseudoType) { + let body = {}; + let type = this.type; + if (checkType(pseudoType)) { + body['PseudoMessageCode'] = pseudoType; + type = pseudoType; + } + for (const field of Object.keys(MSGSPEC[type][this.version].body)) { + body[field] = EMPTYPSTRING; + } + return body; + } + decodeBodyField(message, field, type = this.type, bitOffset = 0) { + const msgSpec = MSGSPEC[type][this.version]; + return decodeField(message.extract(msgSpec.body[field]?.startBit + bitOffset, msgSpec.body[field]?.endBit + bitOffset), msgSpec.body[field]?.encoding); + } +} +function isValidMessage(message) { + if (validateMessage(message).length > 0) + return false; + return true; +} +function validateMessage(message) { + if (!(message instanceof Object)) + throw new TypeError('Not an object'); + if (message instanceof WfCoreMessage) + return message.validate(); + let errors = []; + if (!message?.MessageHeader || !(message?.MessageHeader instanceof Object)) { + errors.push('Missing or invalid message header'); + } + if (!message?.MessageBody || !(message?.MessageBody instanceof Object)) { + errors.push('Missing or invalid message body'); + } + if (errors.length > 0) + return errors; + errors.push(...checkMsgSegments(message.MessageHeader, message.MessageBody)); + return errors; +} +async function encryptMessage(message, method, ikm, address, iv, version = WfVersion.v1) { + const { unencrypted, encrypted: decrypted } = splitEncryptedMsg(message); + const key = await deriveKey(ikm, method, address, version); + const encrypted = await encrypt(decrypted, method, key, iv, version); + return mergeEncryptedMsg(unencrypted, encrypted); +} +async function decryptMessage(message, method, ikm, address, iv, version = WfVersion.v1) { + const { unencrypted, encrypted } = splitEncryptedMsg(message); + const key = await deriveKey(ikm, method, address, version); + const decrypted = await decrypt(encrypted, method, key, iv, version); + return mergeEncryptedMsg(unencrypted, decrypted); +} +function compileMsgSpec() { + const SIGNSIGNALTYPE = '$signsignal'; + const msgSpec = {}; + for (const type of Object.values(WfMsgType)) { + msgSpec[type] = {}; + { + const version = WfVersion.v1; + const headerSpec_v1 = compileMsgSpecRegex(msgSpec_v1.header); + const signsignalSpec_v1 = compileMsgSpecRegex(msgSpec_v1.body[SIGNSIGNALTYPE]); + msgSpec[type][version] = { header: {}, body: {} }; + msgSpec[type][version].header = headerSpec_v1; + if (SIGNSIGNALTYPE in msgSpec_v1.body[type]) { + msgSpec[type][version].body = signsignalSpec_v1; + } + else { + msgSpec[type][version].body = compileMsgSpecRegex(msgSpec_v1.body[type]); + } + } + } + return msgSpec; +} +function compileMsgSpecRegex(segSpec) { + for (const field of Object.keys(segSpec)) { + if (segSpec[field]?.pattern) { + segSpec[field].regex = new RegExp(segSpec[field].pattern); + } + } + return segSpec; +} +function checkMsgSegments(header, body) { + let errors = []; + if (!('Version' in header)) + errors.push('Missing protocol version'); + if (!('MessageCode' in header)) + errors.push('Missing message type code'); + if (errors.length > 0) + return errors; + errors.push(...checkMsgHeader(header, header['MessageCode'], header['Version'])); + errors.push(...checkMsgBody(body, header['MessageCode'], header['Version'])); + return errors; +} +function checkMsgHeader(header, type, version = WfVersion.v1) { + return checkFields(header, MSGSPEC[type][version].header, version); +} +function checkMsgBody(body, type, version = WfVersion.v1) { + return checkFields(body, MSGSPEC[type][version].body, version); +} +function checkFields(segment, segSpec, version = WfVersion.v1) { + let errors = []; + for (const field of Object.keys(segSpec)) { + if (!Object.hasOwn(segment, field)) { + errors.push(`Missing ${field} field`); + continue; + } + if (segSpec[field].encoding === EMPTYPSTRING) { + continue; + } + if (segment[field] === EMPTYPSTRING) { + errors.push(`${field} field has no value`); + continue; + } + if (segSpec[field]?.regex instanceof RegExp) { + if (!segSpec[field].regex.test(segment[field])) { + errors.push(`Value of ${field} field does not match ${segSpec[field].pattern} pattern`); + } + continue; + } + const encoding = segSpec[field].encoding; + if (!isValidValue(segment[field], encoding, version)) { + errors.push(`Value of ${field} field is not valid for ${encoding} encoding`); + } + } + return errors; +} +function checkEncryption(method) { + if (method === undefined) + return false; + if (isString(method)) { + if (method === MSG_NOENCRYPT) + return true; + if (Object.keys(WfCryptoMethod).includes(method)) + return true; + } + if (Object.values(WfCryptoMethod).includes(method)) + return true; + return false; +} +function checkPrefix(prefix) { + if (prefix === undefined) + return false; + if (prefix === MSG_PREFIX) + return true; + return false; +} +function checkType(type) { + if (type === undefined) + return false; + if (isString(type) + && Object.keys(WfMsgType).includes(type)) + return true; + if (Object.values(WfMsgType).includes(type)) + return true; + return false; +} +function checkVersion(version) { + if (version === undefined) + return false; + if (isString(version) + && Object.keys(WfVersion).includes(version)) + return true; + if (Object.values(WfVersion).includes(version)) + return true; + return false; +} +function extractUnencryptedHeader(message) { + return { + prefix: extractHeaderField(message, 'Prefix'), + version: extractHeaderField(message, 'Version'), + encryption: extractHeaderField(message, 'EncryptionIndicator') + }; +} +function extractHeaderField(message, field) { + const fieldSpec = MSGSPEC[WfMsgType.A][WfVersion.v1].header[field]; + return decodeField(message.extract(fieldSpec.startBit, fieldSpec.endBit), fieldSpec.encoding); +} +function splitEncryptedMsg(message) { + const split = MSGSPEC[WfMsgType.A][WfVersion.v1].header['EncryptionIndicator'].endBit; + return { + unencrypted: message.extract(0, split).toU8a(), + encrypted: message.extract(split, message.length).toU8a() + }; +} +function mergeEncryptedMsg(unenecrypted, encrypted) { + const split = MSGSPEC[WfMsgType.A][WfVersion.v1].header['EncryptionIndicator'].endBit; + return BinaryBuffer + .fromU8a(unenecrypted, split) + .appendU8a(encrypted); +} diff --git a/dist/core/lib/versions.d.ts b/dist/core/lib/versions.d.ts new file mode 100644 index 00000000..365d1daf --- /dev/null +++ b/dist/core/lib/versions.d.ts @@ -0,0 +1,13 @@ +/** + * @module core/versions + * @summary Whiteflag JS protocol versions + */ +export { WfVersion }; +/** + * Defines Whiteflag versions + * @enum WfVersion + */ +declare enum WfVersion { + /** Whiteflag version 1 */ + v1 = "1" +} diff --git a/dist/core/lib/versions.js b/dist/core/lib/versions.js new file mode 100644 index 00000000..fcb1b30b --- /dev/null +++ b/dist/core/lib/versions.js @@ -0,0 +1,6 @@ +'use strict'; +export { WfVersion }; +var WfVersion; +(function (WfVersion) { + WfVersion["v1"] = "1"; +})(WfVersion || (WfVersion = {})); diff --git a/dist/core/package.json b/dist/core/package.json index c0cc9ef0..392d87e2 100644 --- a/dist/core/package.json +++ b/dist/core/package.json @@ -1,6 +1,6 @@ { - "name": "@whiteflag/core", - "version": "0.1.0-dev", + "name": "@whiteflagprotocol/core", + "version": "1.0.0", "description": "Whiteflag JS core protocol functionality", "license": "CC0-1.0", "keywords": [ @@ -9,15 +9,23 @@ "core", "blockchain" ], + "directories": { + "lib": "lib" + }, + "dependencies": { + "@whiteflagprotocol/crypto": "^1.0.0", + "@whiteflagprotocol/util": "^1.0.0" + }, "homepage": "https://github.com/WhiteflagProtocol/whiteflag-js#readme", "repository": { "type": "git", - "directory": "packages/core", + "directory": "dist/core", "url": "https://github.com/WhiteflagProtocol/whiteflag-js.git" }, "bugs": { "url": "https://github.com/WhiteflagProtocol/whiteflag-js/issues" }, - "type": "commonjs", - "main": "index.js" + "type": "module", + "main": "index.js", + "types": "index.d.ts" } diff --git a/dist/core/static/v1/wf-field-encoding.json b/dist/core/static/v1/wf-field-encoding.json new file mode 100644 index 00000000..b73e4bc4 --- /dev/null +++ b/dist/core/static/v1/wf-field-encoding.json @@ -0,0 +1,35 @@ +{ + "$description": "Whiteflag protocol version 1 field encoding", + "binary": { + "pattern": "^[0-1]*$", + "length": -1 + }, + "decimal": { + "pattern": "^[0-9]*$", + "length": -1 + }, + "hexadecimal": { + "pattern": "^[a-fA-F0-9]*$", + "length": -1 + }, + "utf-8": { + "pattern": "^[\\u0000-\\u007F]*$", + "length": -1 + }, + "datetime": { + "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$", + "length": 56 + }, + "duration": { + "pattern": "^P[0-9]{2}D[0-9]{2}H[0-9]{2}M$", + "length": 24 + }, + "latitude": { + "pattern": "^[+-][0-9]{2}.[0-9]{5}$", + "length": 29 + }, + "longitude": { + "pattern": "^[+-][0-9]{3}.[0-9]{5}$", + "length": 33 + } +} diff --git a/dist/core/static/v1/wf-msg-structure.json b/dist/core/static/v1/wf-msg-structure.json new file mode 100644 index 00000000..c01b3ad3 --- /dev/null +++ b/dist/core/static/v1/wf-msg-structure.json @@ -0,0 +1,153 @@ +{ + "$description": "Whiteflag protocol version 1 message structure", + "header": { + "Prefix": { + "encoding": "utf-8", + "pattern": "^WF$", + "startBit": 0, + "endBit": 16 + }, + "Version": { + "encoding": "utf-8", + "pattern": "^[A-Z0-9]{1}$", + "startBit": 16, + "endBit": 24 + }, + "EncryptionIndicator": { + "encoding": "utf-8", + "pattern": "^[A-Z0-9]{1}$", + "startBit": 24, + "endBit": 32 + }, + "DuressIndicator": { + "encoding": "binary", + "pattern": "^[0-1]{1}$", + "startBit": 32, + "endBit": 33 + }, + "MessageCode": { + "encoding": "utf-8", + "pattern": "^[A-Z]{1}$", + "startBit": 33, + "endBit": 41 + }, + "ReferenceIndicator": { + "encoding": "hexadecimal", + "pattern": "^[a-fA-F0-9]{1}$", + "startBit": 41, + "endBit": 45 + }, + "ReferencedMessage": { + "encoding": "hexadecimal", + "pattern": "^[a-fA-F0-9]{64}$", + "startBit": 45, + "endBit": 301 + } + }, + "body": { + "A": { + "VerificationMethod": { + "encoding": "hexadecimal", + "startBit": 301, + "endBit": 305 + }, + "VerificationData": { + "encoding": "utf-8", + "startBit": 305, + "endBit": -1 + } + }, + "K": { + "CryptoDataType": { + "encoding": "hexadecimal", + "startBit": 301, + "endBit": 305 + }, + "CryptoData": { + "encoding": "hexadecimal", + "startBit": 305, + "endBit": -1 + } + }, + "T": { + "PseudoMessageCode": { + "encoding": "utf-8", + "startBit": 301, + "endBit": 309 + } + }, + "R": { + "ReferenceMethod": { + "encoding": "hexadecimal", + "startBit": 301, + "endBit": 305 + }, + "ReferenceData": { + "encoding": "utf-8", + "startBit": 305, + "endBit": -1 + } + }, + "F": { + "Text": { + "encoding": "utf-8", + "startBit": 301, + "endBit": -1 + } + }, + "P": { "$signsignal": true }, + "D": { "$signsignal": true }, + "S": { "$signsignal": true }, + "E": { "$signsignal": true }, + "I": { "$signsignal": true }, + "M": { "$signsignal": true }, + "Q": { "$signsignal": true }, + "$signsignal": { + "SubjectCode": { + "encoding": "hexadecimal", + "startBit": 301, + "endBit": 309 + }, + "DateTime": { + "encoding": "datetime", + "startBit": 309, + "endBit": 365 + }, + "Duration": { + "encoding": "duration", + "startBit": 365, + "endBit": 389 + }, + "ObjectType": { + "encoding": "hexadecimal", + "startBit": 389, + "endBit": 397 + }, + "ObjectLatitude": { + "encoding": "latitude", + "startBit": 397, + "endBit": 426 + }, + "ObjectLongitude": { + "encoding": "longitude", + "startBit": 426, + "endBit": 459 + }, + "ObjectSizeDim1": { + "encoding": "decimal", + "startBit": 459, + "endBit": 475 + }, + "ObjectSizeDim2": { + "encoding": "decimal", + "startBit": 475, + "endBit": 491 + }, + "ObjectOrientation": { + "encoding": "decimal", + "startBit": 491, + "endBit": 503 + } + } + } +} diff --git a/dist/crypto/LICENSE.md b/dist/crypto/LICENSE.md new file mode 100644 index 00000000..66129f4e --- /dev/null +++ b/dist/crypto/LICENSE.md @@ -0,0 +1,53 @@ +# Whiteflag Software Dedication to the Public Domain + +## Preamble + +All persons and organisations that contributed to the initial development +of the Whiteflag Protocol and the Whiteflag JavaScript Library software did +so disinterestedly. + +## Whiteflag JavaScript Library CC0-1.0 Statement + +Excluding third party software that may be part of this distribution, the +Whiteflag JavaScript Library software and associated documentation is dedicated +to the public domain under the [Creative Commons CC0-1.0 Universal Public Domain Dedication](http://creativecommons.org/publicdomain/zero/1.0/) +statement, meaning that to the extent possible under law, the authors +and their organisations have waived all copyright and related or neighbouring +rights to this work, allowing anyone to copy, modify, distribute and implement +the work, even for commercial purposes, all without asking permission. + +In no way are the patent or trademark rights of any person affected by this +dedication to the public domain, nor are the rights that other persons may +have in the work or in how the work is used, such as publicity or privacy +rights. Unless expressly stated otherwise, the authors and their organisations +make no warranties about the work, and disclaim liability for all uses of the +work, to the fullest extent permitted by applicable law. When using or citing +the work, you should not imply endorsement by the authors or the affirmer. + +## Third Party Software Dependencies + +Where the Whiteflag JavaScript Library software requires third party +software packages, which are not part of this distribution. These third party +software packages need to be obtained through their respective official +distribution channels, and may be licensed. + +## Legal Restrictions for the Usage of the Whiteflag software + +WARNING: THE USAGE OF SIGNS AND SIGNALS WITH THIS SOFTWARE IS SUBJECT +TO LOCAL AND/OR INTERNATIONAL LAWS. Please take note of the following when +using this software: + +- This software only provides an additional means of communication for + certain signs and signals, and it does not replace any existing + standard, regulation or means of communication, whether mandatory or + not, including but not limited to physical signs, radio communications, + official registers, etc. + +- The usage of protective signs is subject to International Humanitarian + Law. Misuse of protective signs is a punishable violation under local + and international laws. + +- The usage of emergency signals may be subject to various regulations and + standards, depending on location and context. The misuse of emergency + signals, including misuse of the duress functionality, may be a + punishable violation under local and international laws. diff --git a/dist/crypto/README.md b/dist/crypto/README.md new file mode 100644 index 00000000..94597c02 --- /dev/null +++ b/dist/crypto/README.md @@ -0,0 +1,79 @@ +# WFJSL Cryptographic Functions + +## Overview + +The Whiteflag JavaScript Library (WFJSL) is an implementation of the Whiteflag +Protocol written in [TypeScript](https://developer.mozilla.org/en-US/docs/Glossary/TypeScript), +and compiled to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), +to support the development of Whiteflag-enabled applications in JavaScript. + +The `@whiteflagprotocol/crypto` package provides cryptographic functions for +other Whiteflag packages. Therefore, it should normally not be necessary to +add this package as a dependency. + +The WFJSL uses the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) +for the basic implementation of Whiteflag cryptographic functions, such as +secret negotiation, encryption, and authentication. Putting these critical +security functions in a separate package makes them better inspectable, +testable and maintainable. + +Most cryptography functions are asynchronous and return a +[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). + +This description provides a generic overview of the WFJSL cryptography +package. Please see the [WFJSL TypeDoc documentation](https://js.whiteflagprotocol.org/typedoc) +for a detailed description of all classes and functions. + +## Hashing + +The Whiteflag cryptography package provides three hashing functions +with the `hash` module: + +| Function | Purpose | +|----------|---------------------------------------------------------------------------| +| `hkdf` | Hash-based Key Derivation Function using SHA-256 i.a.w. RFC 5869 | +| `hash` | Basic hashing function, using SHA-256 as default | +| `hmac` | Hash-Based Message Authentication Code function, using SHA-256 as default | + +The `hkdf` function is used by Whiteflag to derive encryption keys and +authentication tokens and bind them to the blockchain address of a specific +originator. The `hash` and `hmac` functions are wrappers easy common access +to the underlying algorithms through the Web Crypto API; they are used by +the `hkdf` function, but may also be used for other functionality as required. + +## Encryption + +The Whiteflag cryptography package provides the following functions for +message encryption with the `cipher` module: + +| Function | Purpose | +|-------------|---------------------------------------------------------------------------------------| +| `encrypt` | Encrypts a binary encoded Whiteflag message, based on the Whiteflag encryption method | +| `decrypt` | Decrypts a binary encoded Whiteflag message, based on the Whiteflag encryption method | +| `deriveKey` | Derives the encryption key based on the Whiteflag encryption method | + +The `encrypt` and `decrypt` functions take a binary encoded Whiteflag message, +along with a number of encryption parameters such as the encryption key, to +perform the encryption and decryption of messages. + +The `deriveKey` function uses the `hkdf` function with the input key material, +information parameter, salt, and key length for the encryption method, to +generate the Web Crypto API encryption key to be used with the `encrypt` and +`decrypt` functions i.a.w. the Whiteflag standard. + +The Whiteflag encryption methods are defined by the `WfCryptoMethod` enum. + +## Cryptographic Keys + +The Whiteflag cryptography package provides the following functions for +key generation with the `keys` module: + +| Function | Purpose | +|-----------------|----------------------------------------------| +| `createAesKey` | Creates an AES encryption and decryption key | +| `createHmacKey` | Creates an HMAC signing key | + +All key generation functions create a Web Crypto API `CryptoKey` object, +typically from a raw key generated or provided elsewhere. These keys are +primarily intended to provide the correct Web Crypto API keys to other +functions of the cryptography package. diff --git a/dist/crypto/index.d.ts b/dist/crypto/index.d.ts new file mode 100644 index 00000000..f285282b --- /dev/null +++ b/dist/crypto/index.d.ts @@ -0,0 +1,9 @@ +/** + * @module crypto + * @summary Whiteflag JS cryptographic classes and functions + * @document docs/md/modules.md + * @primaryExport + */ +export { WfCryptoMethod, encrypt, decrypt, deriveKey } from './lib/cipher.ts'; +export { hkdf, hash, hmac } from './lib/hash.ts'; +export { createAesKey, createHmacKey } from './lib/keys.ts'; diff --git a/dist/crypto/index.js b/dist/crypto/index.js new file mode 100644 index 00000000..6eb70e9c --- /dev/null +++ b/dist/crypto/index.js @@ -0,0 +1,4 @@ +'use strict'; +export { WfCryptoMethod, encrypt, decrypt, deriveKey } from "./lib/cipher.js"; +export { hkdf, hash, hmac } from "./lib/hash.js"; +export { createAesKey, createHmacKey } from "./lib/keys.js"; diff --git a/dist/crypto/lib/cipher.d.ts b/dist/crypto/lib/cipher.d.ts new file mode 100644 index 00000000..cd74a9fe --- /dev/null +++ b/dist/crypto/lib/cipher.d.ts @@ -0,0 +1,55 @@ +/** + * @module crypto/cipher + * @summary Whiteflag JS encryption and decryption functions + */ +export { WfCryptoMethod, encrypt, decrypt, deriveKey }; +import { WfVersion } from '@whiteflagprotocol/core'; +/** + * Whiteflag encryption methods, defining the encryption methods + * for Whiteflag messages as specified by the Whiteflag standard + * @enum WfCryptoMethod + * @wfversion v1-draft.7 + * @wfreference 5.2.4 Message Encryption + */ +declare enum WfCryptoMethod { + /** Whiteflag encryption method 1: negotiated key */ + ECDH = "1", + /** Whiteflag encryption method 2: pre-shared key */ + PSK = "2" +} +/** + * Encrypts a message based on the specified encryption method + * @function encrypt + * @wfversion v1-draft.7 + * @wfreference 5.2.4 Message Encryption + * @param message the message to be encrypted + * @param method the Whiteflag encryption method + * @param key the input key material for the encryption key + * @param iv the initialisation vector, if required for the method + * @param version the Whiteflag protocol version + */ +declare function encrypt(message: Uint8Array, method: WfCryptoMethod, key: CryptoKey, iv?: Uint8Array, version?: WfVersion): Promise; +/** + * Decrypts a message based on the specified encryption method + * @function decrypt + * @wfversion v1-draft.7 + * @wfreference 5.2.4 Message Encryption + * @param message the message to be decyrpted + * @param method the Whiteflag encryption method + * @param key the encryption key + * @param iv the initialisation vector, if required for the method + * @param version the Whiteflag protocol version + */ +declare function decrypt(message: Uint8Array, method: WfCryptoMethod, key: CryptoKey, iv?: Uint8Array, version?: WfVersion): Promise; +/** + * Derives the encryption key based on the Whiteflag encryption method + * @function deriveKey + * @wfversion v1-draft.7 + * @wfreference 5.2.3 Encryption Key and Authentication Token Derivation + * @param ikm the raw input key material + * @param info information to bind the key, e.g. the blockchain address of the originator + * @param method the Whiteflag encryption method + * @param version the Whiteflag protocol version + * @returns the encryption key + */ +declare function deriveKey(ikm: Uint8Array, method: WfCryptoMethod, info: Uint8Array, version?: WfVersion): Promise; diff --git a/dist/crypto/lib/cipher.js b/dist/crypto/lib/cipher.js new file mode 100644 index 00000000..8f9ae13a --- /dev/null +++ b/dist/crypto/lib/cipher.js @@ -0,0 +1,94 @@ +'use strict'; +export { WfCryptoMethod, encrypt, decrypt, deriveKey }; +import { WfVersion } from '@whiteflagprotocol/core'; +import { hexToU8a } from '@whiteflagprotocol/util'; +import { zeroise } from "./common.js"; +import { hkdf } from "./hash.js"; +import { createAesKey } from "./keys.js"; +import cryptoSpec_v1 from '../static/v1/wf-crypto-params.json' with { type: 'json' }; +const BYTELENGTH = 8; +var WfCryptoMethod; +(function (WfCryptoMethod) { + WfCryptoMethod["ECDH"] = "1"; + WfCryptoMethod["PSK"] = "2"; +})(WfCryptoMethod || (WfCryptoMethod = {})); +const PARAMS = compileCryptoParams(); +async function encrypt(message, method, key, iv, version = WfVersion.v1) { + switch (method) { + case WfCryptoMethod.ECDH: + case WfCryptoMethod.PSK: { + const parameters = getAesParameters(method, iv, version); + return encryptAes(message, key, parameters); + } + default: { + throw new Error(`Invalid encryption method: ${method}`); + } + } +} +async function decrypt(message, method, key, iv, version = WfVersion.v1) { + switch (method) { + case WfCryptoMethod.ECDH: + case WfCryptoMethod.PSK: { + const parameters = getAesParameters(method, iv, version); + return decryptAes(message, key, parameters); + } + default: { + throw new Error(`Invalid encryption method: ${method}`); + } + } +} +async function deriveKey(ikm, method, info, version = WfVersion.v1) { + const salt = hexToU8a(PARAMS[method][version].salt); + const keyLength = PARAMS[method][version].keyLength; + const rawKey = await hkdf(ikm, salt, info, keyLength); + zeroise(ikm); + switch (method) { + case WfCryptoMethod.ECDH: + case WfCryptoMethod.PSK: { + return createAesKey(rawKey, PARAMS[method][version].algorithm); + } + default: { + throw new Error(`Invalid encryption method: ${method}`); + } + } +} +function compileCryptoParams() { + const params = {}; + for (const method of Object.values(WfCryptoMethod)) { + params[method] = {}; + { + const version = WfVersion.v1; + params[method][version] = cryptoSpec_v1[method]; + } + } + return params; +} +async function encryptAes(data, key, parameters) { + const encrypted = await crypto.subtle.encrypt(parameters, key, data); + return new Uint8Array(encrypted); +} +async function decryptAes(data, key, parameters) { + const decrypted = await crypto.subtle.decrypt(parameters, key, data); + return new Uint8Array(decrypted); +} +function getAesParameters(method, iv, version = WfVersion.v1) { + switch (method) { + case WfCryptoMethod.ECDH: + case WfCryptoMethod.PSK: { + if (!iv) { + throw new Error(`Encryption method ${method} requires an initialisation vector`); + } + if (iv.length !== PARAMS[method][version].ivLength) { + throw new Error(`Invalid initialisation vector length for encryption method ${method}`); + } + return { + name: PARAMS[method][version].algorithm, + counter: iv, + length: PARAMS[method][version].ctrLength * BYTELENGTH + }; + } + default: { + throw new Error(`Invalid encryption method: ${method}`); + } + } +} diff --git a/dist/crypto/lib/common.d.ts b/dist/crypto/lib/common.d.ts new file mode 100644 index 00000000..f6a0400e --- /dev/null +++ b/dist/crypto/lib/common.d.ts @@ -0,0 +1,12 @@ +/** + * @module crypto/common + * @summary Whiteflag JS common crypto functions + */ +export { zeroise }; +/** + * Basic zeroisation function + * @function zeroise + * @param u8array typed array to zeroise + * @returns the zeroised typed array + */ +declare function zeroise(u8array: Uint8Array): Uint8Array; diff --git a/dist/crypto/lib/common.js b/dist/crypto/lib/common.js new file mode 100644 index 00000000..944dd46a --- /dev/null +++ b/dist/crypto/lib/common.js @@ -0,0 +1,5 @@ +'use strict'; +export { zeroise }; +function zeroise(u8array) { + return u8array.fill(0); +} diff --git a/dist/crypto/lib/hash.d.ts b/dist/crypto/lib/hash.d.ts new file mode 100644 index 00000000..cdb508e2 --- /dev/null +++ b/dist/crypto/lib/hash.d.ts @@ -0,0 +1,33 @@ +/** + * @module crypto/hash + * @summary Whiteflag JS hashing functions + */ +export { hkdf, hash, hmac }; +/** + * Hash-based Key Derivation Function using SHA-256 i.a.w. RFC 5869 + * @function hkdf + * @param ikm input key material + * @param salt salt + * @param info info + * @param keylen output key length in octets + * @returns generated key + */ +declare function hkdf(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array, keylen: number): Promise>; +/** + * Basic hashing function + * @function hash + * @param data data to hash + * @param length the required output length in octets; default is 32 + * @param algorithm the hash algorithm to be used; default is SHA-256 + * @returns the hash value + */ +declare function hash(data: Uint8Array, length?: number, algorithm?: string): Promise>; +/** + * Hash-Based Message Authentication Code function + * @function hmac + * @param rawKey the raw HMAC key + * @param message the message to authenticate + * @param algorithm the hash algorithm to be used; default is SHA-256 + * @returns the message authentication code + */ +declare function hmac(rawKey: Uint8Array, message: Uint8Array, algorithm?: string): Promise>; diff --git a/dist/crypto/lib/hash.js b/dist/crypto/lib/hash.js new file mode 100644 index 00000000..96d2772f --- /dev/null +++ b/dist/crypto/lib/hash.js @@ -0,0 +1,38 @@ +'use strict'; +export { hkdf, hash, hmac }; +import { zeroise } from "./common.js"; +import { createHmacKey } from "./keys.js"; +const HMAC = 'HMAC'; +const HASHALG = 'SHA-256'; +const HASHLEN = 32; +async function hkdf(ikm, salt, info, keylen) { + const prk = await hmac(salt, ikm); + zeroise(ikm); + let okm = new Uint8Array(keylen); + let t = new Uint8Array(HASHLEN); + let offset = 0; + const N = Math.ceil(keylen / HASHLEN); + for (let i = 1; i <= N; i++) { + let block = new Uint8Array(offset + info.length + 1); + block.set(t.slice(0, block.length)); + block.set(info.slice(0, info.length), offset); + block[offset + info.length] = i; + let hash = await hmac(prk, block); + t.set(hash.slice(0, t.length)); + offset = offset * (i - 1); + if (offset < okm.length) { + okm.set(hash.slice(0, (okm.length - offset)), offset); + } + offset = HASHLEN; + } + return okm; +} +async function hash(data, length = HASHLEN, algorithm = HASHALG) { + const hash = await crypto.subtle.digest(algorithm, data); + return new Uint8Array(hash, 0, length); +} +async function hmac(rawKey, message, algorithm = HASHALG) { + const key = await createHmacKey(rawKey, algorithm); + const mac = await crypto.subtle.sign(HMAC, key, message.buffer); + return new Uint8Array(mac); +} diff --git a/dist/crypto/lib/keys.d.ts b/dist/crypto/lib/keys.d.ts new file mode 100644 index 00000000..c42c28b5 --- /dev/null +++ b/dist/crypto/lib/keys.d.ts @@ -0,0 +1,21 @@ +/** + * @module crypto/keys + * @summary Whiteflag JS cryptographic key generation functions + */ +export { createAesKey, createHmacKey }; +/** + * Creates an AES encryption and decryption key + * @function createAesKey + * @param rawKey the raw key + * @param algorithm the AES mode to use the key for, default is CTR mode + * @returns the AES enrcyption key + */ +declare function createAesKey(rawKey: Uint8Array, algorithm?: string): Promise; +/** + * Creates an HMAC signing key + * @function createHmacKey + * @param rawKey the raw key + * @param algorithm the hashing algorithm, default is SHA-256 + * @returns the HMAC signing key + */ +declare function createHmacKey(rawKey: Uint8Array, algorithm?: string): Promise; diff --git a/dist/crypto/lib/keys.js b/dist/crypto/lib/keys.js new file mode 100644 index 00000000..5d1a6052 --- /dev/null +++ b/dist/crypto/lib/keys.js @@ -0,0 +1,21 @@ +'use strict'; +export { createAesKey, createHmacKey }; +const BYTELENGTH = 8; +const RAWKEY = 'raw'; +const NOTEXTRACTABLE = false; +const DEFAULT_HASHALG = 'SHA-256'; +const DEFAULT_ENCRYPTALG = 'AES-CTR'; +async function createAesKey(rawKey, algorithm = DEFAULT_ENCRYPTALG) { + const aesAlgorithm = { + name: algorithm, + length: rawKey.length * BYTELENGTH + }; + return crypto.subtle.importKey(RAWKEY, rawKey.buffer, aesAlgorithm, NOTEXTRACTABLE, ['encrypt', 'decrypt']); +} +async function createHmacKey(rawKey, algorithm = DEFAULT_HASHALG) { + const hmacAlgorithm = { + name: 'HMAC', + hash: { name: algorithm } + }; + return crypto.subtle.importKey(RAWKEY, rawKey.buffer, hmacAlgorithm, NOTEXTRACTABLE, ['sign']); +} diff --git a/dist/crypto/package.json b/dist/crypto/package.json index 5d0ad05a..c4b8cd9d 100644 --- a/dist/crypto/package.json +++ b/dist/crypto/package.json @@ -1,6 +1,6 @@ { - "name": "@whiteflag/crypto", - "version": "0.1.0-dev", + "name": "@whiteflagprotocol/crypto", + "version": "1.0.0", "description": "Whiteflag JS cryptographic classes and functions", "license": "CC0-1.0", "keywords": [ @@ -9,15 +9,23 @@ "cryptography", "blockchain" ], + "directories": { + "lib": "lib" + }, + "dependencies": { + "@whiteflagprotocol/core": "^1.0.0", + "@whiteflagprotocol/util": "^1.0.0" + }, "homepage": "https://github.com/WhiteflagProtocol/whiteflag-js#readme", "repository": { "type": "git", - "directory": "packages/crypto", + "directory": "dist/crypto", "url": "https://github.com/WhiteflagProtocol/whiteflag-js.git" }, "bugs": { "url": "https://github.com/WhiteflagProtocol/whiteflag-js/issues" }, - "type": "commonjs", - "main": "index.js" + "type": "module", + "main": "index.js", + "types": "index.d.ts" } diff --git a/dist/crypto/static/v1/wf-crypto-params.json b/dist/crypto/static/v1/wf-crypto-params.json new file mode 100644 index 00000000..51adc82a --- /dev/null +++ b/dist/crypto/static/v1/wf-crypto-params.json @@ -0,0 +1,19 @@ +{ + "$description": "Whiteflag protocol version 1 encryption methods", + "1": { + "$description": "Whiteflag encryption method 1: AES-256 in counter mode (CTR) with ECDH negotiated secret key", + "algorithm": "AES-CTR", + "keyLength": 32, + "ivLength": 16, + "ctrLength": 8, + "salt": "8ddb03085a2c15e69c35c224bce2952dca7878770724741cbce5a135328be0c0" + }, + "2": { + "$description": "Whiteflag encryption method 2: AES-256 in counter mode (CTR) with pre-shared secret key", + "algorithm": "AES-CTR", + "keyLength": 32, + "ivLength": 16, + "ctrLength": 8, + "salt": "c4d028bd45c876135e80ef7889835822a6f19a31835557d5854d1334e8497b56" + } +} diff --git a/dist/main/LICENSE.md b/dist/main/LICENSE.md new file mode 100644 index 00000000..66129f4e --- /dev/null +++ b/dist/main/LICENSE.md @@ -0,0 +1,53 @@ +# Whiteflag Software Dedication to the Public Domain + +## Preamble + +All persons and organisations that contributed to the initial development +of the Whiteflag Protocol and the Whiteflag JavaScript Library software did +so disinterestedly. + +## Whiteflag JavaScript Library CC0-1.0 Statement + +Excluding third party software that may be part of this distribution, the +Whiteflag JavaScript Library software and associated documentation is dedicated +to the public domain under the [Creative Commons CC0-1.0 Universal Public Domain Dedication](http://creativecommons.org/publicdomain/zero/1.0/) +statement, meaning that to the extent possible under law, the authors +and their organisations have waived all copyright and related or neighbouring +rights to this work, allowing anyone to copy, modify, distribute and implement +the work, even for commercial purposes, all without asking permission. + +In no way are the patent or trademark rights of any person affected by this +dedication to the public domain, nor are the rights that other persons may +have in the work or in how the work is used, such as publicity or privacy +rights. Unless expressly stated otherwise, the authors and their organisations +make no warranties about the work, and disclaim liability for all uses of the +work, to the fullest extent permitted by applicable law. When using or citing +the work, you should not imply endorsement by the authors or the affirmer. + +## Third Party Software Dependencies + +Where the Whiteflag JavaScript Library software requires third party +software packages, which are not part of this distribution. These third party +software packages need to be obtained through their respective official +distribution channels, and may be licensed. + +## Legal Restrictions for the Usage of the Whiteflag software + +WARNING: THE USAGE OF SIGNS AND SIGNALS WITH THIS SOFTWARE IS SUBJECT +TO LOCAL AND/OR INTERNATIONAL LAWS. Please take note of the following when +using this software: + +- This software only provides an additional means of communication for + certain signs and signals, and it does not replace any existing + standard, regulation or means of communication, whether mandatory or + not, including but not limited to physical signs, radio communications, + official registers, etc. + +- The usage of protective signs is subject to International Humanitarian + Law. Misuse of protective signs is a punishable violation under local + and international laws. + +- The usage of emergency signals may be subject to various regulations and + standards, depending on location and context. The misuse of emergency + signals, including misuse of the duress functionality, may be a + punishable violation under local and international laws. diff --git a/dist/main/README.md b/dist/main/README.md new file mode 100644 index 00000000..6ba56d48 --- /dev/null +++ b/dist/main/README.md @@ -0,0 +1,67 @@ +# WFJSL Main Interface + +## Overview + +The Whiteflag JavaScript Library (WFJSL) is an implementation of the Whiteflag +Protocol written in [TypeScript](https://developer.mozilla.org/en-US/docs/Glossary/TypeScript), +and compiled to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), +to support the development of Whiteflag-enabled applications in JavaScript. + +The `@whiteflagprotocol/main` package provides all classes and functions +required to implement the Whiteflag protocol. Normally this should be +the only dependency for projects implementing Whiteflag. + +This description provides a generic overview of the Whiteflag main package. +Please refer to the [WFJSL TypeDoc documentation](https://js.whiteflagprotocol.org/typedoc) +for a detailed description of all classes and functions. + +## Whiteflag message class + +The Whiteflag message class `WfMessage` defined in the `main` module +represents a Whiteflag message. + +This class extends the core Whiteflag message class `WfCoreMessage` by adding +metadata to the message, additional data conversions (such as to and from +JSON), and specific Whiteflag protocol features. This allows the class to be +used and integrated in larger functional applications in accordance with the +Whiteflag protocol specification. + +A new message may be created using the constructor, or by using a static +factory method. For example, creating a new FreeText message (message +code `F`) and set the `Text` field, may be done as follows: + +```{javascript} +let wfMessage = new WfCoreMessage('F'); +WfCoreMessage.set('Text', 'Example text to be sent with the FreeText message'); +``` + +The `encode()` method encodes the message. It automatically verifies the fields +and values when encoding and decoding. Encoding and decoding are asynchronous, +meaning the functions return [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). +Once encoded, the methods `toHex()` and `toU8a()` may be used to obtain the +encoded message as a hexadecimal string or a UInt8array, respectively. + +```{javascript} +await wfMessage.encode(); +const hexMessage = wfMessage.toHex(); +``` + +If a message is encoded, or decoded, the message is "final", meaning its +content cannot be changed. Decoding a message is done using a one of the +static factory methods such as `fromHex(...)` or `fromU8a(...)`, since the +message type is probably not known before decoding. + +```{javascript} +wfMessage = await WfCoreMessage.fromHex(hexMessage); +``` + +Encryption and decryption is automatically performed upon encoding and +decoding, based on the value of the `EncryptionIndicator` field in the message +header. + +## Whiteflag versions + +Currently, only one version of the Whiteflag protocol has been developed. For +ease of implementation of future Whiteflag versions, WFJSL functions and +classes take the Whiteflag version into account. The available Whiteflag +versions are defined with the `WfVersions` enum. diff --git a/dist/main/index.d.ts b/dist/main/index.d.ts new file mode 100644 index 00000000..185402f3 --- /dev/null +++ b/dist/main/index.d.ts @@ -0,0 +1,8 @@ +/** + * @module main + * @summary Whiteflag JS main package + * @document docs/md/modules.md + * @primaryExport + */ +export { WfMessage, WfMetaHeader } from './lib/message.ts'; +export { WfVersion } from '@whiteflagprotocol/core'; diff --git a/dist/main/index.js b/dist/main/index.js new file mode 100644 index 00000000..f991139d --- /dev/null +++ b/dist/main/index.js @@ -0,0 +1,3 @@ +'use strict'; +export { WfMessage } from "./lib/message.js"; +export { WfVersion } from '@whiteflagprotocol/core'; diff --git a/dist/main/lib/message.d.ts b/dist/main/lib/message.d.ts new file mode 100644 index 00000000..3a83b90a --- /dev/null +++ b/dist/main/lib/message.d.ts @@ -0,0 +1,89 @@ +/** + * @module main/message + * @summary Whiteflag JS message class + */ +export { WfMessage, WfMetaHeader }; +import { WfCoreMessage } from '@whiteflagprotocol/core'; +/** + * A Whiteflag message as defined by the Whiteflag specification + * @class WfMessage + * @wfversion v1-draft.7 + * @wfreference 4 Message Format + * @remarks This class extends the core Whiteflag message class by + * adding metadata to the message, additional data conversions (such as to and + * from JSON), and specific Whiteflag protocol features. This allows the class + * to be used and integrated in larger functional applications in accordance + * with the Whiteflag specification. + */ +declare class WfMessage extends WfCoreMessage { + /** The message metadata required for processing the message */ + protected meta: WfMetaHeader; + /** + * Constructor for a Whiteflag message + * @param type the Whiteflag message type + * @param version the Whiteflag protocol version + */ + constructor(type: string, version?: string); + /** + * Creates new Whiteflag message from a plain object + * @function fromObject + * @param message a plain JavaScript object with message header and body + * @returns a new Whiteflag message + * @throws {WfProtocolError} if message could not be created + */ + static fromJSON(message: string): Promise; + /** + * Returns the value of the metaheader field + * @function getMeta + * @param fieldName the name of the metaheader field + * @returns the value of the metaheader field + */ + getMeta(fieldName: string): string | null; + /** + * Sets the value of the specified metaheader field + * @function setMeta + * @param fieldName the name of the metaheader field + * @param value the value to set + * @return true if succesful, else false + */ + setMeta(fieldName: string, value: string): boolean; + /** + * Returns the Whiteflag message as a plain object + * @function toObject + * @returns the message as a plain object + */ + toObject(): Object; + /** + * Returns the Whiteflag message as a plain object + * @function toObject + * @returns the message as a plain object + */ + toJSON(): string; +} +/** + * Defines a Whiteflag message header object + * @interface WfMetaHeader + */ +interface WfMetaHeader { + [key: string]: any; + autoGenerated?: string; + blockchain?: string; + transceiveDirection?: string; + transmissionSuccess?: boolean; + transactionHash?: string; + transactionTime?: string; + transactionIndex?: number; + blockNumber?: number; + blockDepth?: number; + confirmed?: boolean; + recipientAddress?: string; + originatorAddress?: string; + originatorPubKey?: string; + originatorValid?: boolean; + referenceValid?: boolean; + formatValid?: boolean; + validationErrors?: string[]; + encodedMessage?: string; + encryptionInitVector?: string; + encryptionKeyInput?: string; +} diff --git a/dist/main/lib/message.js b/dist/main/lib/message.js new file mode 100644 index 00000000..3e0a5d55 --- /dev/null +++ b/dist/main/lib/message.js @@ -0,0 +1,32 @@ +'use strict'; +export { WfMessage }; +import { WfCoreMessage } from '@whiteflagprotocol/core'; +class WfMessage extends WfCoreMessage { + meta = {}; + constructor(type, version = '1') { + super(type, version); + } + static async fromJSON(message) { + const wfMessage = await this.fromObject(JSON.parse(message)); + return wfMessage; + } + getMeta(fieldName) { + for (const field of Object.keys(this.meta)) { + if (field === fieldName) + return this.meta[field]; + } + return null; + } + setMeta(fieldName, value) { + this.meta[fieldName] = value; + return true; + } + toObject() { + let message = super.toObject(); + message.MetaHeader = this.meta; + return message; + } + toJSON() { + return JSON.stringify(this.toObject()); + } +} diff --git a/dist/protocol/package.json b/dist/main/package.json similarity index 57% rename from dist/protocol/package.json rename to dist/main/package.json index 4896809f..e15a1c4e 100644 --- a/dist/protocol/package.json +++ b/dist/main/package.json @@ -1,6 +1,6 @@ { - "name": "@whiteflag/protocol", - "version": "0.1.0-dev", + "name": "@whiteflagprotocol/main", + "version": "1.0.0", "description": "Whiteflag JS main package", "license": "CC0-1.0", "keywords": [ @@ -8,15 +8,23 @@ "protocol", "blockchain" ], + "directories": { + "lib": "lib" + }, + "dependencies": { + "@whiteflagprotocol/crypto": "^1.0.0", + "@whiteflagprotocol/util": "^1.0.0" + }, "homepage": "https://github.com/WhiteflagProtocol/whiteflag-js#readme", "repository": { "type": "git", - "directory": "packages/protocol", + "directory": "dist/protocol", "url": "https://github.com/WhiteflagProtocol/whiteflag-js.git" }, "bugs": { "url": "https://github.com/WhiteflagProtocol/whiteflag-js/issues" }, - "type": "commonjs", - "main": "index.js" + "type": "module", + "main": "index.js", + "types": "index.d.ts" } diff --git a/dist/util/LICENSE.md b/dist/util/LICENSE.md new file mode 100644 index 00000000..66129f4e --- /dev/null +++ b/dist/util/LICENSE.md @@ -0,0 +1,53 @@ +# Whiteflag Software Dedication to the Public Domain + +## Preamble + +All persons and organisations that contributed to the initial development +of the Whiteflag Protocol and the Whiteflag JavaScript Library software did +so disinterestedly. + +## Whiteflag JavaScript Library CC0-1.0 Statement + +Excluding third party software that may be part of this distribution, the +Whiteflag JavaScript Library software and associated documentation is dedicated +to the public domain under the [Creative Commons CC0-1.0 Universal Public Domain Dedication](http://creativecommons.org/publicdomain/zero/1.0/) +statement, meaning that to the extent possible under law, the authors +and their organisations have waived all copyright and related or neighbouring +rights to this work, allowing anyone to copy, modify, distribute and implement +the work, even for commercial purposes, all without asking permission. + +In no way are the patent or trademark rights of any person affected by this +dedication to the public domain, nor are the rights that other persons may +have in the work or in how the work is used, such as publicity or privacy +rights. Unless expressly stated otherwise, the authors and their organisations +make no warranties about the work, and disclaim liability for all uses of the +work, to the fullest extent permitted by applicable law. When using or citing +the work, you should not imply endorsement by the authors or the affirmer. + +## Third Party Software Dependencies + +Where the Whiteflag JavaScript Library software requires third party +software packages, which are not part of this distribution. These third party +software packages need to be obtained through their respective official +distribution channels, and may be licensed. + +## Legal Restrictions for the Usage of the Whiteflag software + +WARNING: THE USAGE OF SIGNS AND SIGNALS WITH THIS SOFTWARE IS SUBJECT +TO LOCAL AND/OR INTERNATIONAL LAWS. Please take note of the following when +using this software: + +- This software only provides an additional means of communication for + certain signs and signals, and it does not replace any existing + standard, regulation or means of communication, whether mandatory or + not, including but not limited to physical signs, radio communications, + official registers, etc. + +- The usage of protective signs is subject to International Humanitarian + Law. Misuse of protective signs is a punishable violation under local + and international laws. + +- The usage of emergency signals may be subject to various regulations and + standards, depending on location and context. The misuse of emergency + signals, including misuse of the duress functionality, may be a + punishable violation under local and international laws. diff --git a/dist/util/README.md b/dist/util/README.md new file mode 100644 index 00000000..91b73752 --- /dev/null +++ b/dist/util/README.md @@ -0,0 +1,74 @@ +# WFJSL Utility Classes and Functions + +## Overview + +The Whiteflag JavaScript Library (WFJSL) is an implementation of the Whiteflag +Protocol written in [TypeScript](https://developer.mozilla.org/en-US/docs/Glossary/TypeScript), +and compiled to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), +to support the development of Whiteflag-enabled applications in JavaScript. + +The `@whiteflagprotocol/util` package provides common utilities for other +Whiteflag packages. These utilities include common data conversions, generic +helper functions, etc. for other Whiteflag packages. It should normally not be +necessary to add this package as a dependency, but its functionality might be +useful for other purposes. + +This description provides a generic overview of the WFJSL utility package. +Please see the [WFJSL TypeDoc documentation](https://js.whiteflagprotocol.org/typedoc) +for a detailed description of all classes and functions. + +## BinaryBuffer class + +The `binary` module of the Whiteflag utility package provides the +`BinaryBuffer` class. Objects of this class represent a binary encoded piece +of data, e.g. a Whiteflag message, that can be manipulated at bit level. + +Static methods to create a binary buffer: + +* `BinaryBuffer.empty()`: creates an empty binary buffer +* `BinaryBuffer.from(...)`: creates a binary buffer from another binary buffer +* `BinaryBuffer.fromBytes(...)`: creates a binary buffer from bytes in a number array +* `BinaryBuffer.fromHex(...)`: creates a binary buffer from a hexadecimal string +* `BinaryBuffer.fromU8a(...)`: creates a binary buffer from a Uint8Array + +Public methods to manipulate a binary buffer: + +* `BinaryBuffer.append(...)`: appends another binary buffer to the end of the binary buffer +* `BinaryBuffer.crop(...)`: shortens the binary buffer to the length of the specified bits +* `BinaryBuffer.extract(...)`: extracts the specified bits from the binary buffer +* `BinaryBuffer.insert(...)`: inserts another binary buffer at the start of the binary buffer +* `BinaryBuffer.shiftLeft(...)`: shifts bits in the buffer to the left, shrinking the buffer +* `BinaryBuffer.shiftRight(...)`: shifts bits in the buffer to the right, enlarging the buffer + +Some of these functions have an equivalent that allow to use a different +binary representation, e.g. `appendHex(...)` or `insertU8a(...)`. + +## Data conversions + +The `encoding` module provides generic functions to convert data +from one encoding to another. + +| Encoding | Description | Converts to | +|-------------|---------------------------------------------------------------|-----------------------------------------| +| Base64 | a string with a 64-character binary-to-text encoding | Base64url | +| Base64url | a string with a URL-safe 64-character binary-to-text encoding | Base64, Hexadecimal, String, UInt8Array | +| Hexadecimal | a string with a hexadecimal representation of a binary | Base64url, String, UInt8Array | +| Object | a plain JavaScript object | Base64url | +| Text | a string with UTF-8 characters | Base64url, Hexadecimal, UInt8Array | +| UInt8Array | an array of bytes representing a binary encoding | Base64url, Hexadecimal, String | + +For example `hexToB64u(...)` creates a base64url encoded string from a +hexadecimal string. The module also provides some additional helper functions +for different data encodings: + +* `isObject(...)` checks if something is an object +* `isString(...)` checks if something is a string +* `isBase64(...)` checks if a string is base64 encoded +* `isBase64u(...)` checks if a string is base64url encoded +* `isHex(...)` checks if a string is hexadecimal encoded +* `noHexPrefix(...)` removes the '0x' hex prefix if present + +## JSON Web Signature (JWS) + +Whiteflag uses JSON Web Signatures (JWS). To create, sign and convert JWSs +the `jws` module provides a common `Jws` class to other Whiteflag packages. diff --git a/dist/util/index.d.ts b/dist/util/index.d.ts new file mode 100644 index 00000000..d67595db --- /dev/null +++ b/dist/util/index.d.ts @@ -0,0 +1,9 @@ +/** + * @module util + * @summary Whiteflag JS common utility and helper functions + * @document docs/md/modules.md + * @primaryExport + */ +export * from './lib/binary.ts'; +export * from './lib/encoding.ts'; +export * from './lib/jws.ts'; diff --git a/dist/util/index.js b/dist/util/index.js new file mode 100644 index 00000000..a3520711 --- /dev/null +++ b/dist/util/index.js @@ -0,0 +1,4 @@ +'use strict'; +export * from "./lib/binary.js"; +export * from "./lib/encoding.js"; +export * from "./lib/jws.js"; diff --git a/dist/util/lib/binary.d.ts b/dist/util/lib/binary.d.ts new file mode 100644 index 00000000..5dd76784 --- /dev/null +++ b/dist/util/lib/binary.d.ts @@ -0,0 +1,220 @@ +/** + * @module util/binary + * @summary Whiteflag JS binary buffer class + */ +export { BinaryBuffer, cropBits, shiftRight, shiftLeft }; +/** + * A class representing a binary buffer + * @class BinaryBuffer + * @remarks Objects of this class represent a binary encoded piece of data, + * e.g. a Whiteflag message, that can be manipulated at bit level. This class + * provides the basic (not Whiteflag-specific) functionality for other + * Whiteflag packages to encode and decode binary Whiteflag messages. + */ +declare class BinaryBuffer { + /** The Uint8Array that holds the binary content */ + private buffer; + /** The number of used bits in the buffer */ + length: number; + /** + * Constructor for a binary buffer + * @param buffer the Uint8Array typed array to create the binary buffer from + * @param nBits the bit length of the buffer + */ + private constructor(); + /** + * Creates an empty binary buffer + * @function from + * @returns a new binary buffer + */ + static empty(): BinaryBuffer; + /** + * Creates a binary buffer from another binary buffer + * @function from + * @param binBuffer a binary buffer + * @returns a new binary buffer + */ + static from(binBuffer: BinaryBuffer): BinaryBuffer; + /** + * Creates a binary buffer from bytes in a number array + * @function fromBytes + * @param byteArray an array of numbers representing bytes + * @param nBits the number of used bits + * @returns a new binary buffer + */ + static fromBytes(byteArray: Array, nBits?: number): BinaryBuffer; + /** + * Creates a binary buffer from a hexadecimal string + * @function fromHex + * @param hexString a hexadecimal string + * @param nBits the number of used bits + * @returns a new binary buffer + */ + static fromHex(hexString: string, nBits?: number): BinaryBuffer; + /** + * Creates a binary buffer from a Uint8Array + * @function fromU8a + * @param u8array an array of 8-bit unsigned integers + * @param nBits the number of used bits + * @returns a new binary buffer + */ + static fromU8a(u8array: Uint8Array, nBits?: number): BinaryBuffer; + /** + * Appends another binary buffer to the binary buffer + * @function append + * @param binBuffer a binary buffer + * @returns the updated binary buffer + */ + append(binBuffer: BinaryBuffer): BinaryBuffer; + /** + * Appends bytes from a number array to the binary buffer + * @function appendBytes + * @param byteArray an array of numbers representing bytes + * @param nBits the number of used bits to append + * @returns the updated binary buffer + */ + appendBytes(byteArray: Array, nBits?: number): BinaryBuffer; + /** + * Appends a hexadecimal string to the binary buffer + * @function appendHex + * @param hexString a hexadecimal string + * @param nBits the number of used bits to append + * @returns the updated binary buffer + */ + appendHex(hexString: string, nBits?: number): BinaryBuffer; + /** + * Appends a Uint8Array to the binary buffer + * @function appendU8a + * @param u8array an array of 8-bit unsigned integers + * @param nBits the number of used bits to append + * @returns the updated binary buffer + */ + appendU8a(u8array: Uint8Array, nBits?: number): BinaryBuffer; + /** + * Shortens the binary buffer to the length of the specified bits + * @function crop + * @param nBits the number of used bits, or, if negative, the number of bits to remove + * @return the updated binary buffer + */ + crop(nBits: number): this; + /** + * Extracts the specified bits from the binary buffer + * @param startBit the first bit to extract (inclusive) + * @param endBit the final bit of the extraction (exclusive), negative means until end of buffer + * @returns a new binary buffer with the extracted bits + */ + extract(startBit: number, endBit?: number): BinaryBuffer; + /** + * Extracts the specified bits from the binary buffer to a hexadecimal string + * @param startBit the first bit to extract (inclusive) + * @param endBit the final bit of the extraction (exclusive), negative means until end of buffer + * @returns a hexadecimal string with the extracted data + */ + extractHex(startBit: number, endBit?: number): string; + /** + * Extracts the specified bits from the binary buffer to a Uint8Array + * @function extractU8a + * @param startBit the first bit to extract (inclusive) + * @param endBit the final bit of the extraction (exclusive), negative means until end of buffer + * @returns an array of 8-bit unsigned integers with the extracted data + */ + extractU8a(startBit: number, endBit?: number): Uint8Array; + /** + * Inserts bytes from a number array at the start of the binary buffer + * @function insertBytes + * @param byteArray an array of numbers representing bytes + * @param nBits the number of used bits to insert + * @returns the updated binary buffer + */ + insertBytes(byteArray: Array, nBits?: number): BinaryBuffer; + /** + * Inserts a hexadecimal string at the start of the binary buffer + * @function insertHex + * @param hexString a hexadecimal string + * @param nBits the number of used bits to insert + * @returns the updated binary buffer + */ + insertHex(hexString: string, nBits?: number): BinaryBuffer; + /** + * Inserts a Uint8Array at the start of the binary buffer + * @function insertU8a + * @param u8array an array of 8-bit unsigned integers + * @param nBits the number of used bits to insert + * @returns the updated binary buffer + */ + insertU8a(u8array: Uint8Array, nBits?: number): BinaryBuffer; + /** + * Shifts bits in the buffer to the left, shrinking the buffer + * @function shiftLeft + * @param shift the number of bits to shift to the left + * @returns the shifted binary buffer + */ + shiftLeft(shift: number): BinaryBuffer; + /** + * Shifts bits in the buffer to the right, enlarging the buffer + * @function shiftRight + * @param shift the number of bits to shift to the right + * @returns the shifted binary buffer + */ + shiftRight(shift: number): BinaryBuffer; + /** + * Gives the value of the binary buffer as a Uint8Array + * @function toU8a + * @returns an array of 8-bit unsigned integers + */ + toU8a(): Uint8Array; + /** + * Gives the value of the binary buffer as a hexadecimal string + * @function toHex + * @returns a hexadecimal string + */ + toHex(): string; + /** + * Calculates the number of bits to be stored in the buffer + * @private + * @param byteLength the actual byte length of the buffer containing the bitset + * @param nBits the specified bit length of the buffer, or, if negative, the number of bits to remove + * @returns the calculated bit length + */ + private calcBitLength; + /** + * Calculates the number of bytes required to hold a given number of bits + * @private + * @param nBits the number of used bits in the binary buffer + * @returns the required byte length of the binary buffer + */ + private calcByteLength; + /** + * Concatinates two bitsets + * @private + * @param u8array1 Uint8Array containing the first bitset + * @param nBits1 number of bits in the first bitset, i.e. which bits to take from the first Uint8Array + * @param u8array2 Uint8Array containing the second bitset + * @param nBits2 number of bits in the second bitset, i.e. which bits to take from the second Uint8Array + */ + private concatinate; +} +/** + * Shortens a Uint8Array to the length of the specified bits + * @function cropBits + * @param u8array the Uint8Array containing the bitset + * @param nBits the number of used bits, or, if negative, the number of bits to remove + * @return a new Uint8Array with the unused bits cleared + */ +declare function cropBits(u8array: Uint8Array, nBits: number): Uint8Array; +/** + * Shifts bits in a Uint8Array to the right modulo 8 + * @function shiftRight + * @param u8array the Uint8Array to be right shifted + * @param shift the nummber of bits to be right shifted by modulo 8 bits + * @returns a new Uint8Array with the right shifted bits + */ +declare function shiftRight(u8array: Uint8Array, shift: number): Uint8Array; +/** + * Shifts bits in a Uint8Array to the left modulo 8 + * @function shiftLeft + * @param u8array the Uint8Array to be left shifted + * @param shift the nummber of bits to be left shifted by modulo 8 bits + * @returns a new Uint8Array with the left shifted bits + */ +declare function shiftLeft(u8array: Uint8Array, shift: number): Uint8Array; diff --git a/dist/util/lib/binary.js b/dist/util/lib/binary.js new file mode 100644 index 00000000..d1ad6834 --- /dev/null +++ b/dist/util/lib/binary.js @@ -0,0 +1,227 @@ +'use strict'; +export { BinaryBuffer, cropBits, shiftRight, shiftLeft }; +import { isHex, hexToU8a, u8aToHex } from "./encoding.js"; +const BYTELENGTH = 8; +class BinaryBuffer { + buffer; + length; + constructor(buffer = new Uint8Array(0), nBits = 0) { + if (buffer.length > 0) { + this.length = this.calcBitLength(buffer.byteLength, nBits); + this.buffer = cropBits(buffer, this.length); + } + else { + this.length = 0; + this.buffer = buffer; + } + } + static empty() { + return new BinaryBuffer(); + } + static from(binBuffer) { + return new BinaryBuffer(binBuffer.toU8a(), binBuffer.length); + } + static fromBytes(byteArray, nBits = 0) { + return new BinaryBuffer(new Uint8Array(byteArray), nBits); + } + static fromHex(hexString, nBits = 0) { + if (!isHex(hexString)) + throw new TypeError('Invalid hexadecimal string'); + return new BinaryBuffer(hexToU8a(hexString), nBits); + } + static fromU8a(u8array, nBits = 0) { + return new BinaryBuffer(u8array, nBits); + } + append(binBuffer) { + return this.appendU8a(binBuffer.toU8a(), binBuffer.length); + } + appendBytes(byteArray, nBits = 0) { + return this.appendU8a(new Uint8Array(byteArray), nBits); + } + appendHex(hexString, nBits = 0) { + if (!isHex(hexString)) + throw new TypeError('Invalid hexadecimal string'); + return this.appendU8a(hexToU8a(hexString), nBits); + } + appendU8a(u8array, nBits = 0) { + const bitLength = this.length; + this.buffer = this.concatinate(this.buffer, bitLength, u8array, nBits); + this.length = bitLength + this.calcBitLength(u8array.byteLength, nBits); + return this; + } + crop(nBits) { + if (nBits === 0) + return this; + let length = nBits; + if (nBits > this.length) + length = this.length; + if (nBits < 0) + length = this.length + nBits; + if (length < 0) + length = 0; + this.buffer = cropBits(this.buffer, nBits); + this.length = length; + return this; + } + extract(startBit, endBit = -1) { + const lastBit = endBit < 0 ? this.length : endBit; + const buffer = this.extractU8a(startBit, lastBit); + const bitLength = lastBit - startBit; + return new BinaryBuffer(buffer, bitLength); + } + extractHex(startBit, endBit = -1) { + return u8aToHex(this.extractU8a(startBit, endBit)); + } + extractU8a(startBit, endBit = -1) { + const lastBit = endBit < 0 ? this.length : endBit; + if (startBit < 0) + throw RangeError('Starting bit cannot be less than 0'); + if (startBit >= this.length) + throw RangeError('Starting bit is larger than binary buffer length'); + if (startBit > lastBit) + throw RangeError('Starting bit is larger than ending bit'); + let bitLength = lastBit - startBit; + if (lastBit > this.length) + bitLength = this.length - startBit; + const startByte = Math.floor(startBit / BYTELENGTH); + const byteLength = this.calcByteLength(bitLength); + const shift = startBit % BYTELENGTH; + const buffer = new Uint8Array(this.buffer.slice(startByte, startByte + byteLength + (shift > 0 ? 1 : 0))); + return cropBits(shiftLeft(buffer, shift), bitLength); + } + insertBytes(byteArray, nBits = 0) { + return this.insertU8a(new Uint8Array(byteArray), nBits); + } + insertHex(hexString, nBits = 0) { + if (!isHex(hexString)) + throw new TypeError('Invalid hexadecimal string'); + return this.insertU8a(hexToU8a(hexString), nBits); + } + insertU8a(u8array, nBits = 0) { + const bitLength = this.length; + this.buffer = this.concatinate(u8array, nBits, this.buffer, bitLength); + this.length = bitLength + this.calcBitLength(u8array.byteLength, nBits); + return this; + } + shiftLeft(shift) { + if (shift < 0) + return this.shiftRight(-shift); + if (shift >= this.length) { + this.buffer = new Uint8Array(0); + this.length = 0; + return this; + } + const bitLength = this.length - shift; + const byteShift = Math.floor(shift / BYTELENGTH); + const buffer = new Uint8Array(this.calcByteLength(bitLength) + 1); + for (let i = 0; i < buffer.length; i++) { + buffer[i] = this.buffer[i + byteShift]; + } + this.buffer = cropBits(shiftLeft(buffer, shift), bitLength); + this.length = bitLength; + return this; + } + shiftRight(shift) { + if (shift < 0) + return this.shiftLeft(-shift); + const byteShift = Math.ceil(shift / BYTELENGTH); + const padding = new Uint8Array(byteShift); + this.buffer = this.concatinate(padding, shift, this.buffer, this.length); + this.length = this.length + shift; + return this; + } + toU8a() { + return new Uint8Array(this.buffer); + } + toHex() { + return u8aToHex(this.buffer); + } + calcBitLength(byteLength, nBits) { + const bitLength = byteLength * BYTELENGTH; + if (nBits < 1) + return Math.max(bitLength + nBits, 0); + if (nBits > bitLength) + return bitLength; + return nBits; + } + calcByteLength(nBits) { + return Math.ceil(nBits / BYTELENGTH); + } + concatinate(u8array1, nBits1, u8array2, nBits2) { + const bitLength1 = this.calcBitLength(u8array1.byteLength, nBits1); + const bitLength2 = this.calcBitLength(u8array2.byteLength, nBits2); + const bitLength = bitLength1 + bitLength2; + const byteLength = this.calcByteLength(bitLength); + const shift = bitLength1 % BYTELENGTH; + const bArray1 = cropBits(u8array1, bitLength1); + const bArray2 = shiftRight(cropBits(u8array2, bitLength2), shift); + const buffer = new Uint8Array(byteLength); + buffer.set(bArray1); + if (shift === 0) { + const offset = bArray1.byteLength; + const slice = bArray2.slice(0, buffer.byteLength - offset); + buffer.set(slice, offset); + } + else { + const offset = bArray1.byteLength; + const slice = bArray2.slice(1, buffer.byteLength - offset + 1); + buffer.set(slice, offset); + buffer[bArray1.byteLength - 1] |= bArray2[0]; + } + return buffer; + } +} +function cropBits(u8array, nBits) { + if (nBits === 0) + return new Uint8Array(u8array); + let byteLength; + let clearBits; + if (nBits > 0) { + byteLength = Math.ceil(nBits / BYTELENGTH); + clearBits = BYTELENGTH - (nBits % BYTELENGTH); + } + else { + byteLength = u8array.byteLength - Math.floor(-nBits / BYTELENGTH); + clearBits = -nBits % BYTELENGTH; + } + if (byteLength > u8array.byteLength) + return new Uint8Array(u8array); + if (byteLength < 1) + return new Uint8Array(0); + let buffer = new Uint8Array(u8array.slice(0, byteLength)); + if (clearBits < BYTELENGTH) + buffer[byteLength - 1] &= (0xFF << clearBits); + return buffer; +} +function shiftRight(u8array, shift) { + if (shift < 0) + return shiftLeft(u8array, -shift); + const byteLength = u8array.byteLength + 1; + const mod = shift % BYTELENGTH; + const mask = (0xFF >>> (BYTELENGTH - mod)); + if (mod === 0) + return new Uint8Array(u8array); + let buffer = new Uint8Array(byteLength); + for (let byteIndex = (byteLength - 1); byteIndex > 0; byteIndex--) { + buffer[byteIndex] |= ((0xFF & u8array[byteIndex - 1] & mask) << (BYTELENGTH - mod)); + buffer[byteIndex - 1] = ((0xFF & u8array[byteIndex - 1]) >>> mod); + } + return buffer; +} +function shiftLeft(u8array, shift) { + if (shift < 0) + return shiftRight(u8array, -shift); + const byteLength = u8array.byteLength; + const mod = shift % BYTELENGTH; + const mask = (0xFF << (BYTELENGTH - mod)); + if (mod === 0) + return new Uint8Array(u8array); + let buffer = new Uint8Array(u8array.byteLength); + for (let byteIndex = 0; byteIndex < byteLength; byteIndex++) { + buffer[byteIndex] = ((0xFF & u8array[byteIndex]) << mod); + } + for (let byteIndex = 0; byteIndex < (byteLength - 1); byteIndex++) { + buffer[byteIndex] |= ((0xFF & u8array[byteIndex + 1] & mask) >>> (BYTELENGTH - mod)); + } + return cropBits(buffer, -(shift % BYTELENGTH)); +} diff --git a/dist/util/lib/encoding.d.ts b/dist/util/lib/encoding.d.ts new file mode 100644 index 00000000..105812ae --- /dev/null +++ b/dist/util/lib/encoding.d.ts @@ -0,0 +1,159 @@ +/** + * @module util/encoding + * @summary Whiteflag JS common encodings and data conversions + */ +export { isObject, isString, isBase64, isBase64u, isHex, noHexPrefix, objToB64u, b64uToObj, b64ToB64u, b64uToB64, b64uToHex, b64uToString, b64uToU8a, hexToB64u, hexToString, hexToU8a, stringToB64u, stringToHex, stringToU8a, u8aToB64u, u8aToHex, u8aToString, }; +/** + * Checks if something is an object + * @function isObject + * @param obj something that might be an object + * @returns true if object, else false + */ +declare function isObject(obj: any): boolean; +/** + * Checks if something is a string + * @function isString + * @param charString something that might be a string + * @returns true if string, else false + */ +declare function isString(charString: any): boolean; +/** + * Checks if a string is base64 encoded + * @function isBase64 + * @param base64 a string that might be base64 encoded + * @returns true if base64 encoded, else false + */ +declare function isBase64(base64: string): boolean; +/** + * Checks if a string is base64url encoded + * @function isBase64u + * @param base64u a string that might be base64url encoded + * @returns true if base64url encoded, else false + */ +declare function isBase64u(base64u: string): boolean; +/** + * Checks if a string is hexadecimal encoded + * @function hexString + * @param hexString a string that might be hexadecimal encoded + * @returns true if hexadecimal encoded, else false + */ +declare function isHex(hexString: string): boolean; +/** + * Removes the '0x' hex prefix if present + * @function hexString + * @param hexString a hexadecimal encoded string + * @returns the the string without the hex prefix + */ +declare function noHexPrefix(hexString: string): string; +/** + * Creates a base64URL encoded JSON string from an object + * @function objToB64u + * @param obj the object to be encoded + * @returns a base64URL encoded JSON string + */ +declare function objToB64u(obj: Object): string; +/** + * Creates an object from a base64URL encoded JSON string + * @function b64uToObj + * @param base64u a base64URL encoded JSON string + * @returns an object with the data from the JSON object + */ +declare function b64uToObj(base64u: string): Object; +/** + * Convert base64 to base64url + * @function base64 + * @param base64 a base64 encoded string + * @returns a base64url encoded string + */ +declare function b64ToB64u(base64: string): string; +/** + * Convert base64url to base64 + * @function base64u + * @param base64u a base64url encoded string + * @returns a base64 encoded string + */ +declare function b64uToB64(base64u: string): string; +/** + * Creates hexadecimal string from a base64url encoded string + * @function b64uToHex + * @param b64uString a base64url encoded string + * @returns a hexadecimal encoded string + */ +declare function b64uToHex(b64uString: string): string; +/** + * Creates a standard string from a base64url encoded string + * @function b64uToString + * @param b64uString a base64url encoded string + * @returns a standard string + */ +declare function b64uToString(b64uString: string): string; +/** + * Creates a UInt8 typed array from a base64url encoded string + * @function b64uToU8a + * @param b64uString a base64url encoded string + * @returns a UInt8 typed array + */ +declare function b64uToU8a(b64uString: string): Uint8Array; +/** + * Creates a base64url encoded string from a hexadecimal string + * @function hexToB64u + * @param hexString a hexadecimal string + * @returns a base64url encoded string + */ +declare function hexToB64u(hexString: string): string; +/** + * Creates a regular string from a hexadecimal string + * @function hexToString + * @param hexString a hexadecimal string + * @returns a regular string + */ +declare function hexToString(hexString: string): string; +/** + * Creates a UInt8 typed array from a hexadecimal string + * @function hexToU8a + * @param hexString a hexadecimal string + * @returns an array of 8-bit unsigned integers + */ +declare function hexToU8a(hexString: string): Uint8Array; +/** + * Creates a base64url encoded string from a regular string + * @function stringToB64u + * @param charString a regular character string + * @returns a base64url encoded string + */ +declare function stringToB64u(charString: string): string; +/** + * Creates a hexadecimal string from a regular string + * @function stringToHex + * @param charString a regular character string + * @returns a hexadecimal string + */ +declare function stringToHex(charString: string): string; +/** + * Creates a UInt8 typed array from a regular string + * @function stringToU8a + * @param charString a regular character string + * @returns an array of 8-bit unsigned integers + */ +declare function stringToU8a(charString: string): Uint8Array; +/** + * Creates a base64url encoded string from a UInt8 typed array + * @function u8aToB64u + * @param u8array an array of 8-bit unsigned integers + * @returns a base64url encoded string + */ +declare function u8aToB64u(u8array: Uint8Array): string; +/** + * Creates a hexadecimal string from an Uint8Array + * @function u8aToHex + * @param u8array an array of 8-bit unsigned integers + * @returns a hexadecimal string + */ +declare function u8aToHex(u8array: Uint8Array): string; +/** + * Creates a standard string from a UInt8 typed array + * @function u8aToString + * @param u8array a UInt8 typed array + * @returns a standard string + */ +declare function u8aToString(u8array: Uint8Array): string; diff --git a/dist/util/lib/encoding.js b/dist/util/lib/encoding.js new file mode 100644 index 00000000..dcd0c3f6 --- /dev/null +++ b/dist/util/lib/encoding.js @@ -0,0 +1,110 @@ +'use strict'; +export { isObject, isString, isBase64, isBase64u, isHex, noHexPrefix, objToB64u, b64uToObj, b64ToB64u, b64uToB64, b64uToHex, b64uToString, b64uToU8a, hexToB64u, hexToString, hexToU8a, stringToB64u, stringToHex, stringToU8a, u8aToB64u, u8aToHex, u8aToString, }; +const NOSEPARATOR = ''; +const HEXBYTELENGTH = 2; +const HEXRADIX = 16; +const HEXPREFIX = '0x'; +const REGEX_BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; +const REGEX_BASE64U = /^(?:[A-Za-z0-9_-]+)$/; +const REGEX_HEXSTRING = /^(0x|0X)?(?:[a-fA-F0-9]{2})+$/; +function isObject(obj) { + return (typeof obj === 'object' || obj instanceof Object); +} +function isString(charString) { + return (typeof charString === 'string' || charString instanceof String); +} +function isBase64(base64) { + return REGEX_BASE64.test(base64); +} +function isBase64u(base64u) { + return REGEX_BASE64U.test(base64u); +} +function isHex(hexString) { + return REGEX_HEXSTRING.test(hexString); +} +function noHexPrefix(hexString) { + if (hexString.startsWith(HEXPREFIX)) { + return hexString.substring(2).toLowerCase(); + } + return hexString.toLowerCase(); +} +function objToB64u(obj) { + return stringToB64u(JSON.stringify(obj)); +} +function b64uToObj(base64u) { + return JSON.parse(b64uToString(base64u)); +} +function b64ToB64u(base64) { + return base64 + .replace(/=/g, '') + .replace(/\+/g, '-') + .replace(/\//g, '_'); +} +function b64uToB64(base64u) { + let base64 = base64u + .replace(/\-/g, '+') + .replace(/_/g, '/'); + switch (base64.length % 4) { + case 2: + base64 += "=="; + break; + case 3: + base64 += "="; + break; + } + ; + return base64; +} +function b64uToHex(b64uString) { + return stringToHex(b64uToString(b64uString)); +} +function b64uToString(b64uString) { + return atob(b64uToB64(b64uString)); +} +function b64uToU8a(b64uString) { + return stringToU8a(b64uToString(b64uString)); +} +function hexToB64u(hexString) { + return u8aToB64u(hexToU8a(hexString)); +} +function hexToString(hexString) { + return u8aToString(hexToU8a(hexString)); +} +function hexToU8a(hexString) { + const hex = noHexPrefix(hexString); + const u8array = new Uint8Array(hex.length / HEXBYTELENGTH); + for (let i = 0; i < hex.length; i += HEXBYTELENGTH) { + u8array[i / HEXBYTELENGTH] = parseInt(hex.slice(i, i + HEXBYTELENGTH), HEXRADIX); + } + return u8array; +} +function stringToB64u(charString) { + return b64ToB64u(btoa(charString)); +} +function stringToHex(charString) { + let hexString = ''; + for (let i = 0; i < charString.length; i++) { + hexString += charString + .charCodeAt(i).toString(HEXRADIX) + .padStart(HEXBYTELENGTH, '0'); + } + return hexString.toLowerCase(); +} +function stringToU8a(charString) { + return Uint8Array.from(charString, char => char.charCodeAt(0)); +} +function u8aToB64u(u8array) { + return stringToB64u(u8aToString(u8array)); +} +function u8aToHex(u8array) { + let hexArray = []; + for (const byte of u8array) { + hexArray.push(byte + .toString(HEXRADIX) + .padStart(HEXBYTELENGTH, '0')); + } + return hexArray.join(NOSEPARATOR).toLowerCase(); +} +function u8aToString(u8array) { + return String.fromCharCode(...u8array); +} diff --git a/dist/util/lib/jws.d.ts b/dist/util/lib/jws.d.ts new file mode 100644 index 00000000..a6c15bab --- /dev/null +++ b/dist/util/lib/jws.d.ts @@ -0,0 +1,100 @@ +/** + * @module util/jws + * @summary Whiteflag JS JSON Web Signature class and functions + */ +export { Jws }; +/** + * A class representing a JSON Web Token (JWS) + * @class Jws + * @remarks Whiteflag uses JSON Web Signatures (JWS) for one of its + * authentication methods. This class provides the basic (not Whiteflag- + * specific) functionality to create, sign and convert JWSs for other + * Whiteflag packages. + */ +declare class Jws { + /** The protected the JWS protected header */ + private protected; + /** The JWS payload */ + private payload; + /** The JWS signature */ + private signature; + /** + * Constructor for a Whiteflag message + * @private + * @param header the JWS header, which will automatically be protected + * @param payload the JWS payload + * @param signature the JWS signature + */ + private constructor(); + /** + * Creates a new JWS from a payload + * @function fromPayload + * @param payload the JWS payload + * @returns a new Binary Array + */ + static fromPayload(payload: Object): Jws; + /** + * Creates a new JWS object from a plain javaScript object + * @function fromObject + * @param jws a plain object + * @returns a new JWS object + */ + static fromObject(jws: any): Jws; + /** + * Creates a new JWS object from a compact serialised JWS string + * @function fromCompact + * @param jws a compact serialised JWS string + * @returns a new JWS object + */ + static fromCompact(jws: string): Jws; + /** + * Indicates if the JWS has been signed + * @function isSigned + * @returns true if signed, else false + */ + isSigned(): boolean; + /** + * Returns the JWS signature input + * @function getSignInput + * @returns a string with the input to be signed by the signing algorithm + */ + getSignInput(): string; + /** + * Sets the identifier of the signing algorithm, if not yet signed + * @function setSignAlgorithm + * @param algorithm the identifier of the algorithm used to sign the payload + * @returns true if identifier could be set, false if already signed + */ + setSignAlgorithm(algorithm: string): boolean; + /** + * Sets the signature, if not yet signed + * @function setSignature + * @param signature the base64url encoded signature + * @returns true if signature could be added, false if already signed + */ + setSignature(signature: string): boolean; + /** + * Returns the JWS signature + * @function getSignature + * @returns a string with the the JWS signature + */ + getSignature(): string; + /** + * Returns a full JWS + * @function toFull + * @returns the JWS as a full JWS plain JavaScript object + */ + toFull(): Object; + /** + * Returns a flattened JWS + * @function toFlat + * @returns the JWS as a flattened JWS plain JavaScript object + */ + toFlat(): Object; + /** + * Return a compact serialised JWS as a compact serialized string + * @function toCompact + * @returns the JWS as a compact serialized JWS string + */ + toCompact(): string; +} diff --git a/dist/util/lib/jws.js b/dist/util/lib/jws.js new file mode 100644 index 00000000..d3ccb92a --- /dev/null +++ b/dist/util/lib/jws.js @@ -0,0 +1,121 @@ +'use strict'; +export { Jws }; +import { isObject, isString, isBase64u, objToB64u, b64uToObj } from "./encoding.js"; +var JwsFormat; +(function (JwsFormat) { + JwsFormat["COMPACT"] = "JWS_COMPACT"; + JwsFormat["FLAT"] = "JWS_FLATTENED"; + JwsFormat["FULL"] = "JWS_FULL"; +})(JwsFormat || (JwsFormat = {})); +const JWSSEPARATOR = '.'; +const REGEX_FLAT = /e[yw][A-Za-z0-9-_]+/; +const REGEX_COMPACT = /e[yw][A-Za-z0-9-_]+\.(e[yw][A-Za-z0-9-_]+\.)?[A-Za-z0-9-_]+/; +class Jws { + protected = { alg: '' }; + payload = { iat: 0 }; + signature = ''; + constructor(header, payload, signature = '') { + this.protected = header; + this.payload = payload; + this.signature = signature; + } + static fromPayload(payload) { + return new Jws({}, payload, ''); + } + static fromObject(jws) { + switch (jwsType(jws)) { + case JwsFormat.FULL: { + return new Jws(jws?.protected, jws?.payload, jws?.signature); + } + case JwsFormat.FLAT: { + return new Jws(b64uToObj(jws.protected), b64uToObj(jws.payload), jws?.signature); + } + case JwsFormat.COMPACT: { + return this.fromCompact(jws); + } + } + } + static fromCompact(jws) { + if (jwsType(jws) !== JwsFormat.COMPACT) { + throw new TypeError('Invalid compact serialised JWS string'); + } + const jwsArray = jws.split(JWSSEPARATOR); + let header = {}; + if (jwsArray.length > 0) + header = b64uToObj(jwsArray[0]); + let payload = {}; + if (jwsArray.length > 1) + payload = b64uToObj(jwsArray[1]); + let signature = ''; + if (jwsArray.length > 2) + signature = jwsArray[2]; + return new Jws(header, payload, signature); + } + isSigned() { + return (this.signature.length > 0); + } + getSignInput() { + if (!this.isSigned()) { + this.payload.iat = Math.floor(Date.now() / 1000); + } + return objToB64u(this.protected) + JWSSEPARATOR + objToB64u(this.payload); + } + setSignAlgorithm(algorithm) { + if (this.isSigned()) + return false; + this.protected.alg = algorithm; + return true; + } + setSignature(signature) { + if (this.isSigned()) + return false; + if (!isBase64u(signature)) { + throw new TypeError('Signature is not base64url encoded'); + } + this.signature = signature; + return true; + } + getSignature() { + return this.signature; + } + toFull() { + return { + protected: this.protected, + payload: this.payload, + signature: this.signature + }; + } + toFlat() { + return { + protected: objToB64u(this.protected), + payload: objToB64u(this.payload), + signature: this.signature + }; + } + toCompact() { + let compactJws = objToB64u(this.protected) + + JWSSEPARATOR + + objToB64u(this.payload); + if (this.isSigned()) { + compactJws = compactJws + + JWSSEPARATOR + + this.signature; + } + return compactJws; + } +} +function jwsType(jws) { + if (isString(jws) && REGEX_COMPACT.test(jws)) { + return JwsFormat.COMPACT; + } + if (isObject(jws)) { + if (isObject(jws.protected) && isObject(jws.payload)) { + return JwsFormat.FULL; + } + if (isString(jws.protected) && REGEX_FLAT.test(jws.protected) + && isString(jws.payload) && REGEX_FLAT.test(jws.payload)) { + return JwsFormat.FLAT; + } + } + throw new TypeError('Invalid JWS representation or encoding'); +} diff --git a/dist/util/package.json b/dist/util/package.json index 9d35e3b7..07573bec 100644 --- a/dist/util/package.json +++ b/dist/util/package.json @@ -1,6 +1,6 @@ { - "name": "@whiteflag/util", - "version": "0.1.0-dev", + "name": "@whiteflagprotocol/util", + "version": "1.0.0", "description": "Whiteflag JS common utility and helper functions", "license": "CC0-1.0", "keywords": [ @@ -9,15 +9,19 @@ "utility", "blockchain" ], + "directories": { + "lib": "lib" + }, "homepage": "https://github.com/WhiteflagProtocol/whiteflag-js#readme", "repository": { "type": "git", - "directory": "packages/util", + "directory": "dist/util", "url": "https://github.com/WhiteflagProtocol/whiteflag-js.git" }, "bugs": { "url": "https://github.com/WhiteflagProtocol/whiteflag-js/issues" }, - "type": "commonjs", - "main": "index.js" + "type": "module", + "main": "index.js", + "types": "index.d.ts" } diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 00000000..21740f58 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +js.whiteflagprotocol.org \ No newline at end of file diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 00000000..b9a27cc2 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,3 @@ +theme: jekyll-theme-slate +title: Whiteflag JavaScript Library +description: Reference implementation of the Whiteflag protocol in TypeScript / JavaScript \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..8a2f389e --- /dev/null +++ b/docs/index.md @@ -0,0 +1,35 @@ +# General Description and Overview + +| [WFJSL Documentation Home](../index.md) | [WFJSL TypeDoc Reference](../typedoc) | [Whiteflag Specification](https://standard.whiteflagprotocol.org) | + +## Introduction + +The Whiteflag JavaScript Library (WFJSL) will be the reference implementation +of the Whiteflag Protocol written in [TypeScript](https://developer.mozilla.org/en-US/docs/Glossary/TypeScript), +and compiled to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), +to support the development of Whiteflag-enabled applications in JavaScript. + +The WFJSL is currently **in development** in order to separate the Whiteflag +core protocol code from the current reference implementation, i.e. the +[Whiteflag API](https://api.whiteflagprotocol.org). This makes the code +better to maintain and test in support of future Whiteflag protocol +development, while ensuring reusability for other implementations. + +The WFJSL is to include all Whiteflag protocol features, but with minimal +dependencies. Therefore, the library is independent of any specific +blockchain, database solution, user interface, etc. + +## Documentation + +### Detailed API and Source Code References + +* [WFJSL TypeDoc Reference](typedoc) +* [Package Structure](md/packages.md) + +### Source Code Description + +* [Main WFSJL Interface](md/main.md) +* [Core Protocol Implementation](md/core.md) +* [Cryptographic Functions](md/crypto.md) +* [Utility Classes and Functions](md/utility.md) +* [Error Handling](md/errors.md) diff --git a/docs/md/core.md b/docs/md/core.md new file mode 100644 index 00000000..05fc6944 --- /dev/null +++ b/docs/md/core.md @@ -0,0 +1,101 @@ +# WFJSL Core Implementation + +| [WFJSL Documentation Home](../index.md) | [WFJSL TypeDoc Reference](../typedoc) | [Whiteflag Specification](https://standard.whiteflagprotocol.org) | + +## Overview + +The `@whiteflagprotocol/core` package provides the modules that define +Whiteflag core protocol features as specified in the Whiteflag standard. +As such, this package is not a fully functional implementation of the +protocol, but separates core protocol functions from implementation-specific +design decisions. Therefore, this package is not intended to be used directly +by implementations of Whiteflag; instead, the `@whiteflagprotocol/main` should +be used. + +This description provides a generic overview of the Whiteflag core package. +Please refer to the [WFJSL TypeDoc documentation](../typedoc) for a detailed +description of all classes and functions. + +## Whiteflag message class + +The Whiteflag message class `WfCoreMessage` defined in the `message` module +represents a Whiteflag message. The class contains the methods to create, +set field values, encode and encrypt a Whiteflag message. Please note that +there normally is no need to use the `WfCoreMessage` directly. Instead, the +`WfMessage` child class of the `@whiteflagprotocol/main` package is the main +class to use for Whiteflag message, as this extended class provides methods to +process the metadata required for full protocol functionality. + +A new message may be created using the constructor, or by using a static +factory method. For example, creating a new FreeText message (message +code `F`) and set the `Text` field, may be done as follows: + +```{javascript} +let wfMessage = new WfCoreMessage('F'); +WfCoreMessage.set('Text', 'Example text to be sent with the FreeText message'); +``` + +The `encode()` method encodes the message. The `WfCoreMessage` class +automatically verifies the fields and values when encoding and decoding. +Encoding and decoding are asynchronous, meaning the functions return +[Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). +Once encoded, the methods `toHex()` and `toU8a()` may be used to obtain the +encoded message as a hexadecimal string or a UInt8array, respectively. + +```{javascript} +await wfMessage.encode(); +const hexMessage = wfMessage.toHex(); +``` + +If a message is encoded, or decoded, the message is "final", meaning its +content cannot be changed. Decoding a message is done using a one of the +static factory methods such as `fromHex(...)` or `fromU8a(...)`, since the +message type is probably not known before decoding. + +```{javascript} +wfMessage = await WfCoreMessage.fromHex(hexMessage); +``` + +Encryption and decryption is automatically performed upon encoding and +decoding, based on the value of the `EncryptionIndicator` field in the message +header. Since the `WfCoreMessage` class does not hold any metadata, all +encryption and decryption parameters must be provided to the respective method +when encoding or decoding a message. + +The `message` module, also provides the following functions. These functions +are used by the `WfCoreMessage` class, but may also be used for alternative +processing of Whiteflag messages. + +| Function | Purpose | +|-------------------|--------------------------------------------------| +| `isValidMessage` | Checks if an object is a valid Whiteflag message | +| `validateMessage` | Checks a message object for validation errors | +| `encryptMessage` | Encrypts a binary encoded message | +| `decryptMessage` | Decrypts an encrypted binary message | + +Both validation functions may be used for both plain JavaScript objects and +objects of the `WfCoreMessage` class. The encryption and decryption functions +work only on binary encoded messages provided as a `BinaryBuffer`. + +## Whiteflag encoding + +The `codec` module provides the encoding and decoding for each field in +a Whiteflag message i.a.w. the Whiteflag specification. The available field +encodings are defined with the `WfCodec` enum. For the encoding, decoding, and +verification of field values, the module provides the following functions. + +| Function | Purpose | +|----------------|------------------------------------| +| `encodeField` | Encodes a Whiteflag message field | +| `decodeField` | Decodes a Whiteflag message field | +| `isValidValue` | Checks if the field value is valid | + +These functions are used by the `WfCoreMessage` class when encoding and +decoding a message. + +## Whiteflag versions + +Currently, only one version of the Whiteflag protocol has been developed. For +ease of implementation of future Whiteflag versions, WFJSL functions and +classes take the Whiteflag version into account. The `versions` module defines +the available Whiteflag versions with the `WfVersions` enum. diff --git a/docs/md/crypto.md b/docs/md/crypto.md new file mode 100644 index 00000000..8b59e270 --- /dev/null +++ b/docs/md/crypto.md @@ -0,0 +1,76 @@ +# WFJSL Cryptographic Functions + +| [WFJSL Documentation Home](../index.md) | [WFJSL TypeDoc Reference](../typedoc) | [Whiteflag Specification](https://standard.whiteflagprotocol.org) | + +## Overview + +The `@whiteflagprotocol/crypto` package provides cryptographic functions for +other Whiteflag packages. Therefore, it should normally not be necessary to +add this package as a dependency. + +The WFJSL uses the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) +for the basic implementation of Whiteflag cryptographic functions, such as +secret negotiation, encryption, and authentication. Putting these critical +security functions in a separate package makes them better inspectable, +testable and maintainable. + +Most cryptography functions are asynchronous and return a +[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). + +This description provides a generic overview of the Whiteflag cryptography +package. Please refer to the [WFJSL TypeDoc documentation](../typedoc) for a +detailed description of all classes and functions. + +## Hashing + +The Whiteflag cryptography package provides three hashing functions +with the `hash` module: + +| Function | Purpose | +|----------|---------------------------------------------------------------------------| +| `hkdf` | Hash-based Key Derivation Function using SHA-256 i.a.w. RFC 5869 | +| `hash` | Basic hashing function, using SHA-256 as default | +| `hmac` | Hash-Based Message Authentication Code function, using SHA-256 as default | + +The `hkdf` function is used by Whiteflag to derive encryption keys and +authentication tokens and bind them to the blockchain address of a specific +originator. The `hash` and `hmac` functions are wrappers easy common access +to the underlying algorithms through the Web Crypto API; they are used by +the `hkdf` function, but may also be used for other functionality as required. + +## Encryption + +The Whiteflag cryptography package provides the following functions for +message encryption with the `cipher` module: + +| Function | Purpose | +|-------------|---------------------------------------------------------------------------------------| +| `encrypt` | Encrypts a binary encoded Whiteflag message, based on the Whiteflag encryption method | +| `decrypt` | Decrypts a binary encoded Whiteflag message, based on the Whiteflag encryption method | +| `deriveKey` | Derives the encryption key based on the Whiteflag encryption method | + +The `encrypt` and `decrypt` functions take a binary encoded Whiteflag message, +along with a number of encryption parameters such as the encryption key, to +perform the encryption and decryption of messages. + +The `deriveKey` function uses the `hkdf` function with the input key material, +information parameter, salt, and key length for the encryption method, to +generate the Web Crypto API encryption key to be used with the `encrypt` and +`decrypt` functions i.a.w. the Whiteflag standard. + +The Whiteflag encryption methods are defined by the `WfCryptoMethod` enum. + +## Cryptographic Keys + +The Whiteflag cryptography package provides the following functions for +key generation with the `keys` module: + +| Function | Purpose | +|-----------------|----------------------------------------------| +| `createAesKey` | Creates an AES encryption and decryption key | +| `createHmacKey` | Creates an HMAC signing key | + +All key generation functions create a Web Crypto API `CryptoKey` object, +typically from a raw key generated or provided elsewhere. These keys are +primarily intended to provide the correct Web Crypto API keys to other +functions of the cryptography package. diff --git a/docs/md/errors.md b/docs/md/errors.md index 091168f1..9bb72d6e 100644 --- a/docs/md/errors.md +++ b/docs/md/errors.md @@ -1,12 +1,16 @@ -# Whiteflag API Error Handling +# WFJSL Error Handling + +| [WFJSL Documentation Home](../index.md) | [WFJSL TypeDoc Reference](../typedoc) | [Whiteflag Specification](https://standard.whiteflagprotocol.org) | + +## Overview In addition to the standard JavaScript `Error` class, WFJSL provides an -additional error class for Whiteflag protocol errors: the `ProtocolError` +additional error class for Whiteflag protocol errors: the `WfProtocolError` class for the handling of Whiteflag message format and protocol errors. ## Usage of error classes -To use the ProtocolError class, import it with an object destructor: +To use the `WfProtocolError` class, import it with an object destructor: ```javascript const { WfProtocolError } = require('whiteflag-js'); @@ -20,14 +24,14 @@ err = new WfProtocolError(message, causes, code); with the following arguments: -* `message` is similar to the property of the generic Error class, i.e. a string with a human readable description of the error -* `causes` is an additional property in the form of an array that may contain a human readable stack of underlying causes +* `message` is similar to the property of the generic Error class, i.e. a string with a human-readable description of the error +* `causes` is an additional property in the form of an array that may contain a human-readable stack of underlying causes * `code` is a property of type string, identifying the type of error as described below for both classes -### `ProtocolError` class error codes +## `ProtocolError` class error codes * `WF_PROTOCOL_ERROR`: generic Whiteflag protocol error (default) -* `WF_METAHEADER_ERROR`: incorrect Whiteflag message metaheader +* `WF_METAHEADER_ERROR`: incorrect Whiteflag message metadata * `WF_FORMAT_ERROR`: Whiteflag message format error * `WF_REFERENCE_ERROR`: Whiteflag message reference error * `WF_AUTH_ERROR`: Whiteflag message authentication error diff --git a/docs/md/main.md b/docs/md/main.md new file mode 100644 index 00000000..0a907dae --- /dev/null +++ b/docs/md/main.md @@ -0,0 +1,64 @@ +# WFJSL Main Interface + +| [WFJSL Documentation Home](../index.md) | [WFJSL TypeDoc Reference](../typedoc) | [Whiteflag Specification](https://standard.whiteflagprotocol.org) | + +## Overview + +The `@whiteflagprotocol/main` package provides all classes and functions +required to implement the Whiteflag protocol. Normally this should be +the only dependency for projects implementing Whiteflag. + +This description provides a generic overview of the Whiteflag main package. +Please refer to the [WFJSL TypeDoc documentation](../typedoc) for a detailed +description of all classes and functions. + +## Whiteflag message class + +The Whiteflag message class `WfMessage` defined in the `main` module +represents a Whiteflag message. + +This class extends the core Whiteflag message class `WfCoreMessage` by adding +metadata to the message, additional data conversions (such as to and from +JSON), and specific Whiteflag protocol features. This allows the class to be +used and integrated in larger functional applications in accordance with the +Whiteflag protocol specification. + +A new message may be created using the constructor, or by using a static +factory method. For example, creating a new FreeText message (message +code `F`) and set the `Text` field, may be done as follows: + +```{javascript} +let wfMessage = new WfCoreMessage('F'); +WfCoreMessage.set('Text', 'Example text to be sent with the FreeText message'); +``` + +The `encode()` method encodes the message. It automatically verifies the fields +and values when encoding and decoding. Encoding and decoding are asynchronous, +meaning the functions return [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). +Once encoded, the methods `toHex()` and `toU8a()` may be used to obtain the +encoded message as a hexadecimal string or a UInt8array, respectively. + +```{javascript} +await wfMessage.encode(); +const hexMessage = wfMessage.toHex(); +``` + +If a message is encoded, or decoded, the message is "final", meaning its +content cannot be changed. Decoding a message is done using a one of the +static factory methods such as `fromHex(...)` or `fromU8a(...)`, since the +message type is probably not known before decoding. + +```{javascript} +wfMessage = await WfCoreMessage.fromHex(hexMessage); +``` + +Encryption and decryption is automatically performed upon encoding and +decoding, based on the value of the `EncryptionIndicator` field in the message +header. + +## Whiteflag versions + +Currently, only one version of the Whiteflag protocol has been developed. For +ease of implementation of future Whiteflag versions, WFJSL functions and +classes take the Whiteflag version into account. The available Whiteflag +versions are defined with the `WfVersions` enum. diff --git a/docs/md/modules.md b/docs/md/modules.md deleted file mode 100644 index 2705a189..00000000 --- a/docs/md/modules.md +++ /dev/null @@ -1,42 +0,0 @@ -# Whiteflag API JavaScript Modules - -The Whiteflag Javascript Library is written in TypeScript. The source code -module files are organised in the following packages: - -| Package | Purpose | -|------------|---------------------------------| -| `protocol` | Main Whiteflag protocol modules | -| `core` | Core Whiteflag protocol modules | -| `crypto` | Cryptographic modules | -| `util` | Utility modules | - -See also `README.md` for a general overview of the project, and -`CONTRIBUTING.md` for a description of the repository structure and -development guidelines for the source code. - -## Protocol package - -The `@whiteflag/protocol` package provides all classes and functions -required to implement the Whiteflag protocol. Normally this should be -the only dependency for projects implementing Whiteflag. - -## Core package - -The `@whiteflag/core` package provides the modules that define Whiteflag -core protocol features as specified in the Whiteflag standard. As such, -this package is not a fully functional implementation of the protocol. This -is to seperate core protocol features from implementation-specific design -decisions. - -## Cryptographic package - -The `@whiteflag/crypto` package provides the modules that define Whiteflag -cryptographic implementation independent cryptographic functionality used -by other packages. Seperating these critical security functions makes them -better inspectable, testable and maintainable. - -## Utility package - -The `@whiteflag/util` package provides the modules with utility functions -for other Whiteflag protocol packages. These utilities include common data -conversions, generic helper functions, etc. diff --git a/docs/md/packages.md b/docs/md/packages.md new file mode 100644 index 00000000..e8c1d3c3 --- /dev/null +++ b/docs/md/packages.md @@ -0,0 +1,49 @@ +# WFJSL Package Structure + +| [WFJSL Documentation Home](../index.md) | [WFJSL TypeDoc Reference](../typedoc) | [Whiteflag Specification](https://standard.whiteflagprotocol.org) | + +## Overview + +The Whiteflag JavaScript Library is written in TypeScript. The source code +module files are organized in the following packages: + +| Package | Purpose | +|----------|---------------------------------| +| `main` | Main Whiteflag protocol modules | +| `core` | Core Whiteflag protocol modules | +| `crypto` | Cryptographic modules | +| `util` | Utility modules | + +Please refer to the [WFJSL TypeDoc documentation](../typedoc) for a detailed +description of all classes and functions. + +See also `README.md` for a general overview of the project, and +`CONTRIBUTING.md` for a description of the repository structure and +development guidelines for the source code. + +## Main package + +The [`@whiteflagprotocol/main`](main.md) package provides all classes and functions +required to implement the Whiteflag protocol. Normally this should be +the only dependency for projects implementing Whiteflag. + +## Core package + +The [`@whiteflagprotocol/core`](core.md) package provides the modules that define +Whiteflag core protocol features as specified in the Whiteflag standard. +As such, this package is not a fully functional implementation of the +protocol. This is to separate core protocol features from +implementation-specific design decisions. + +## Cryptographic package + +The [`@whiteflagprotocol/crypto`](crypto.md) package provides the modules that define +Whiteflag cryptographic implementation independent cryptographic functionality +used by other packages. Separating these critical security functions makes +them better inspectable, testable and maintainable. + +## Utility package + +The [`@whiteflagprotocol/util`](util.md) package provides the modules with utility +functions for other Whiteflag protocol packages. These utilities include +common data conversions, generic helper functions, etc. diff --git a/docs/md/util.md b/docs/md/util.md new file mode 100644 index 00000000..b51ec798 --- /dev/null +++ b/docs/md/util.md @@ -0,0 +1,71 @@ +# WFJSL Utility Classes and Functions + +| [WFJSL Documentation Home](../index.md) | [WFJSL TypeDoc Reference](../typedoc) | [Whiteflag Specification](https://standard.whiteflagprotocol.org) | + +## Overview + +The `@whiteflagprotocol/util` package provides common utilities for other +Whiteflag packages. These utilities include common data conversions, generic +helper functions, etc. for other Whiteflag packages. It should normally not be +necessary to add this package as a dependency, but its functionality might be +useful for other purposes. + +This description provides a generic overview of the Whiteflag utility package. +Please refer to the [TypeDoc documentation](../typedoc) for a detailed +description of all classes and functions. + +## BinaryBuffer class + +The `binary` module of the Whiteflag utility package provides the +`BinaryBuffer` class. Objects of this class represent a binary encoded piece +of data, e.g. a Whiteflag message, that can be manipulated at bit level. + +Static methods to create a binary buffer: + +* `BinaryBuffer.empty()`: creates an empty binary buffer +* `BinaryBuffer.from(...)`: creates a binary buffer from another binary buffer +* `BinaryBuffer.fromBytes(...)`: creates a binary buffer from bytes in a number array +* `BinaryBuffer.fromHex(...)`: creates a binary buffer from a hexadecimal string +* `BinaryBuffer.fromU8a(...)`: creates a binary buffer from a Uint8Array + +Public methods to manipulate a binary buffer: + +* `BinaryBuffer.append(...)`: appends another binary buffer to the end of the binary buffer +* `BinaryBuffer.crop(...)`: shortens the binary buffer to the length of the specified bits +* `BinaryBuffer.extract(...)`: extracts the specified bits from the binary buffer +* `BinaryBuffer.insert(...)`: inserts another binary buffer at the start of the binary buffer +* `BinaryBuffer.shiftLeft(...)`: shifts bits in the buffer to the left, shrinking the buffer +* `BinaryBuffer.shiftRight(...)`: shifts bits in the buffer to the right, enlarging the buffer + +Some of these functions have an equivalent that allow to use a different +binary representation, e.g. `appendHex(...)` or `insertU8a(...)`. + +## Data conversions + +The `encoding` module provides generic functions to convert data +from one encoding to another. + +| Encoding | Description | Converts to | +|-------------|---------------------------------------------------------------|-----------------------------------------| +| Base64 | a string with a 64-character binary-to-text encoding | Base64url | +| Base64url | a string with a URL-safe 64-character binary-to-text encoding | Base64, Hexadecimal, String, UInt8Array | +| Hexadecimal | a string with a hexadecimal representation of a binary | Base64url, String, UInt8Array | +| Object | a plain JavaScript object | Base64url | +| Text | a string with UTF-8 characters | Base64url, Hexadecimal, UInt8Array | +| UInt8Array | an array of bytes representing a binary encoding | Base64url, Hexadecimal, String | + +For example `hexToB64u(...)` creates a base64url encoded string from a +hexadecimal string. The module also provides some additional helper functions +for different data encodings: + +* `isObject(...)` checks if something is an object +* `isString(...)` checks if something is a string +* `isBase64(...)` checks if a string is base64 encoded +* `isBase64u(...)` checks if a string is base64url encoded +* `isHex(...)` checks if a string is hexadecimal encoded +* `noHexPrefix(...)` removes the '0x' hex prefix if present + +## JSON Web Signature (JWS) + +Whiteflag uses JSON Web Signatures (JWS). To create, sign and convert JWSs +the `jws` module provides a common `Jws` class to other Whiteflag packages. diff --git a/docs/typedoc/assets/hierarchy.js b/docs/typedoc/assets/hierarchy.js new file mode 100644 index 00000000..fda42cbe --- /dev/null +++ b/docs/typedoc/assets/hierarchy.js @@ -0,0 +1 @@ +window.hierarchyData = "eJyNzb0OwjAMBOB3uTlQ0Q6NvDIzM1QdQurSiPxIcZiqvDuqOpUBsXg4+e5bkVMqAhp0Pypknj3b4lIU0ArdbzeawCDc52vKfGMR82QovFycQJdWK7yzB8GmzI13jybsP82hcV5K8FCw3oiAUGQ6bROnPVCwi/NT5gga2k6PVaHt9MH/YQfj4pf9n1tr/QBt31Xp" \ No newline at end of file diff --git a/docs/typedoc/assets/highlight.css b/docs/typedoc/assets/highlight.css new file mode 100644 index 00000000..2c7e1362 --- /dev/null +++ b/docs/typedoc/assets/highlight.css @@ -0,0 +1,78 @@ +:root { + --light-hl-0: #0000FF; + --dark-hl-0: #569CD6; + --light-hl-1: #000000; + --dark-hl-1: #D4D4D4; + --light-hl-2: #0070C1; + --dark-hl-2: #4FC1FF; + --light-hl-3: #001080; + --dark-hl-3: #9CDCFE; + --light-hl-4: #795E26; + --dark-hl-4: #DCDCAA; + --light-hl-5: #008000; + --dark-hl-5: #6A9955; + --light-hl-6: #098658; + --dark-hl-6: #B5CEA8; + --light-hl-7: #AF00DB; + --dark-hl-7: #C586C0; + --light-code-background: #FFFFFF; + --dark-code-background: #1E1E1E; +} + +@media (prefers-color-scheme: light) { :root { + --hl-0: var(--light-hl-0); + --hl-1: var(--light-hl-1); + --hl-2: var(--light-hl-2); + --hl-3: var(--light-hl-3); + --hl-4: var(--light-hl-4); + --hl-5: var(--light-hl-5); + --hl-6: var(--light-hl-6); + --hl-7: var(--light-hl-7); + --code-background: var(--light-code-background); +} } + +@media (prefers-color-scheme: dark) { :root { + --hl-0: var(--dark-hl-0); + --hl-1: var(--dark-hl-1); + --hl-2: var(--dark-hl-2); + --hl-3: var(--dark-hl-3); + --hl-4: var(--dark-hl-4); + --hl-5: var(--dark-hl-5); + --hl-6: var(--dark-hl-6); + --hl-7: var(--dark-hl-7); + --code-background: var(--dark-code-background); +} } + +:root[data-theme='light'] { + --hl-0: var(--light-hl-0); + --hl-1: var(--light-hl-1); + --hl-2: var(--light-hl-2); + --hl-3: var(--light-hl-3); + --hl-4: var(--light-hl-4); + --hl-5: var(--light-hl-5); + --hl-6: var(--light-hl-6); + --hl-7: var(--light-hl-7); + --code-background: var(--light-code-background); +} + +:root[data-theme='dark'] { + --hl-0: var(--dark-hl-0); + --hl-1: var(--dark-hl-1); + --hl-2: var(--dark-hl-2); + --hl-3: var(--dark-hl-3); + --hl-4: var(--dark-hl-4); + --hl-5: var(--dark-hl-5); + --hl-6: var(--dark-hl-6); + --hl-7: var(--dark-hl-7); + --code-background: var(--dark-code-background); +} + +.hl-0 { color: var(--hl-0); } +.hl-1 { color: var(--hl-1); } +.hl-2 { color: var(--hl-2); } +.hl-3 { color: var(--hl-3); } +.hl-4 { color: var(--hl-4); } +.hl-5 { color: var(--hl-5); } +.hl-6 { color: var(--hl-6); } +.hl-7 { color: var(--hl-7); } +pre, code { background: var(--code-background); } diff --git a/docs/typedoc/assets/icons.js b/docs/typedoc/assets/icons.js new file mode 100644 index 00000000..58882d76 --- /dev/null +++ b/docs/typedoc/assets/icons.js @@ -0,0 +1,18 @@ +(function() { + addIcons(); + function addIcons() { + if (document.readyState === "loading") return document.addEventListener("DOMContentLoaded", addIcons); + const svg = document.body.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "svg")); + svg.innerHTML = `MMNEPVFCICPMFPCPTTAAATR`; + svg.style.display = "none"; + if (location.protocol === "file:") updateUseElements(); + } + + function updateUseElements() { + document.querySelectorAll("use").forEach(el => { + if (el.getAttribute("href").includes("#icon-")) { + el.setAttribute("href", el.getAttribute("href").replace(/.*#/, "#")); + } + }); + } +})() \ No newline at end of file diff --git a/docs/typedoc/assets/icons.svg b/docs/typedoc/assets/icons.svg new file mode 100644 index 00000000..50ad5799 --- /dev/null +++ b/docs/typedoc/assets/icons.svg @@ -0,0 +1 @@ +MMNEPVFCICPMFPCPTTAAATR \ No newline at end of file diff --git a/docs/typedoc/assets/main.js b/docs/typedoc/assets/main.js new file mode 100644 index 00000000..64b80ab2 --- /dev/null +++ b/docs/typedoc/assets/main.js @@ -0,0 +1,60 @@ +"use strict"; +window.translations={"copy":"Copy","copied":"Copied!","normally_hidden":"This member is normally hidden due to your filter settings.","hierarchy_expand":"Expand","hierarchy_collapse":"Collapse","folder":"Folder","search_index_not_available":"The search index is not available","search_no_results_found_for_0":"No results found for {0}","kind_1":"Project","kind_2":"Module","kind_4":"Namespace","kind_8":"Enumeration","kind_16":"Enumeration Member","kind_32":"Variable","kind_64":"Function","kind_128":"Class","kind_256":"Interface","kind_512":"Constructor","kind_1024":"Property","kind_2048":"Method","kind_4096":"Call Signature","kind_8192":"Index Signature","kind_16384":"Constructor Signature","kind_32768":"Parameter","kind_65536":"Type Literal","kind_131072":"Type Parameter","kind_262144":"Accessor","kind_524288":"Get Signature","kind_1048576":"Set Signature","kind_2097152":"Type Alias","kind_4194304":"Reference","kind_8388608":"Document"}; +"use strict";(()=>{var Ke=Object.create;var he=Object.defineProperty;var Ge=Object.getOwnPropertyDescriptor;var Ze=Object.getOwnPropertyNames;var Xe=Object.getPrototypeOf,Ye=Object.prototype.hasOwnProperty;var et=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var tt=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ze(e))!Ye.call(t,i)&&i!==n&&he(t,i,{get:()=>e[i],enumerable:!(r=Ge(e,i))||r.enumerable});return t};var nt=(t,e,n)=>(n=t!=null?Ke(Xe(t)):{},tt(e||!t||!t.__esModule?he(n,"default",{value:t,enumerable:!0}):n,t));var ye=et((me,ge)=>{(function(){var t=function(e){var n=new t.Builder;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),n.searchPipeline.add(t.stemmer),e.call(n,n),n.build()};t.version="2.3.9";t.utils={},t.utils.warn=(function(e){return function(n){e.console&&console.warn&&console.warn(n)}})(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var n=Object.create(null),r=Object.keys(e),i=0;i0){var d=t.utils.clone(n)||{};d.position=[a,l],d.index=s.length,s.push(new t.Token(r.slice(a,o),d))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. +`,e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(r){var i=t.Pipeline.registeredFunctions[r];if(i)n.add(i);else throw new Error("Cannot load unregistered function: "+r)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(n){t.Pipeline.warnIfFunctionNotRegistered(n),this._stack.push(n)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");r=r+1,this._stack.splice(r,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");this._stack.splice(r,0,n)},t.Pipeline.prototype.remove=function(e){var n=this._stack.indexOf(e);n!=-1&&this._stack.splice(n,1)},t.Pipeline.prototype.run=function(e){for(var n=this._stack.length,r=0;r1&&(oe&&(r=s),o!=e);)i=r-n,s=n+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(oc?d+=2:a==c&&(n+=r[l+1]*i[d+1],l+=2,d+=2);return n},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),n=1,r=0;n0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var c=s.node.edges["*"];else{var c=new t.TokenSet;s.node.edges["*"]=c}if(s.str.length==0&&(c.final=!0),i.push({node:c,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new t.TokenSet;s.node.edges["*"]=l}s.str.length==1&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var d=s.str.charAt(0),f=s.str.charAt(1),p;f in s.node.edges?p=s.node.edges[f]:(p=new t.TokenSet,s.node.edges[f]=p),s.str.length==1&&(p.final=!0),i.push({node:p,editsRemaining:s.editsRemaining-1,str:d+s.str.slice(2)})}}}return r},t.TokenSet.fromString=function(e){for(var n=new t.TokenSet,r=n,i=0,s=e.length;i=e;n--){var r=this.uncheckedNodes[n],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}};t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(n){var r=new t.QueryParser(e,n);r.parse()})},t.Index.prototype.query=function(e){for(var n=new t.Query(this.fields),r=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),c=0;c1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,n){var r=e[this._ref],i=Object.keys(this._fields);this._documents[r]=n||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,n;do e=this.next(),n=e.charCodeAt(0);while(n>47&&n<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var n=e.next();if(n==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(n.charCodeAt(0)==92){e.escapeCharacter();continue}if(n==":")return t.QueryLexer.lexField;if(n=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(n=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(n=="+"&&e.width()===1||n=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(n.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,n){this.lexer=new t.QueryLexer(e),this.query=n,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var n=e.peekLexeme();if(n!=null)switch(n.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+n.type;throw n.str.length>=1&&(r+=" with value '"+n.str+"'"),new t.QueryParseError(r,n.start,n.end)}},t.QueryParser.parsePresence=function(e){var n=e.consumeLexeme();if(n!=null){switch(n.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+n.str+"'";throw new t.QueryParseError(r,n.start,n.end)}var i=e.peekLexeme();if(i==null){var r="expecting term or field, found nothing";throw new t.QueryParseError(r,n.start,n.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(r,i.start,i.end)}}},t.QueryParser.parseField=function(e){var n=e.consumeLexeme();if(n!=null){if(e.query.allFields.indexOf(n.str)==-1){var r=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+n.str+"', possible fields: "+r;throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.fields=[n.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,n.start,n.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var n=e.consumeLexeme();if(n!=null){e.currentClause.term=n.str.toLowerCase(),n.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var r=e.peekLexeme();if(r==null){e.nextClause();return}switch(r.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new t.QueryParseError(i,r.start,r.end)}}},t.QueryParser.parseEditDistance=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.editDistance=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="boost must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.boost=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},(function(e,n){typeof define=="function"&&define.amd?define(n):typeof me=="object"?ge.exports=n():e.lunr=n()})(this,function(){return t})})()});var M,G={getItem(){return null},setItem(){}},K;try{K=localStorage,M=K}catch{K=G,M=G}var S={getItem:t=>M.getItem(t),setItem:(t,e)=>M.setItem(t,e),disableWritingLocalStorage(){M=G},disable(){localStorage.clear(),M=G},enable(){M=K}};window.TypeDoc||={disableWritingLocalStorage(){S.disableWritingLocalStorage()},disableLocalStorage:()=>{S.disable()},enableLocalStorage:()=>{S.enable()}};window.translations||={copy:"Copy",copied:"Copied!",normally_hidden:"This member is normally hidden due to your filter settings.",hierarchy_expand:"Expand",hierarchy_collapse:"Collapse",search_index_not_available:"The search index is not available",search_no_results_found_for_0:"No results found for {0}",folder:"Folder",kind_1:"Project",kind_2:"Module",kind_4:"Namespace",kind_8:"Enumeration",kind_16:"Enumeration Member",kind_32:"Variable",kind_64:"Function",kind_128:"Class",kind_256:"Interface",kind_512:"Constructor",kind_1024:"Property",kind_2048:"Method",kind_4096:"Call Signature",kind_8192:"Index Signature",kind_16384:"Constructor Signature",kind_32768:"Parameter",kind_65536:"Type Literal",kind_131072:"Type Parameter",kind_262144:"Accessor",kind_524288:"Get Signature",kind_1048576:"Set Signature",kind_2097152:"Type Alias",kind_4194304:"Reference",kind_8388608:"Document"};var pe=[];function X(t,e){pe.push({selector:e,constructor:t})}var Z=class{alwaysVisibleMember=null;constructor(){this.createComponents(document.body),this.ensureFocusedElementVisible(),this.listenForCodeCopies(),window.addEventListener("hashchange",()=>this.ensureFocusedElementVisible()),document.body.style.display||(this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}createComponents(e){pe.forEach(n=>{e.querySelectorAll(n.selector).forEach(r=>{r.dataset.hasInstance||(new n.constructor({el:r,app:this}),r.dataset.hasInstance=String(!0))})})}filterChanged(){this.ensureFocusedElementVisible()}showPage(){document.body.style.display&&(document.body.style.removeProperty("display"),this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}scrollToHash(){if(location.hash){let e=document.getElementById(location.hash.substring(1));if(!e)return;e.scrollIntoView({behavior:"instant",block:"start"})}}ensureActivePageVisible(){let e=document.querySelector(".tsd-navigation .current"),n=e?.parentElement;for(;n&&!n.classList.contains(".tsd-navigation");)n instanceof HTMLDetailsElement&&(n.open=!0),n=n.parentElement;if(e&&!rt(e)){let r=e.getBoundingClientRect().top-document.documentElement.clientHeight/4;document.querySelector(".site-menu").scrollTop=r,document.querySelector(".col-sidebar").scrollTop=r}}updateIndexVisibility(){let e=document.querySelector(".tsd-index-content"),n=e?.open;e&&(e.open=!0),document.querySelectorAll(".tsd-index-section").forEach(r=>{r.style.display="block";let i=Array.from(r.querySelectorAll(".tsd-index-link")).every(s=>s.offsetParent==null);r.style.display=i?"none":"block"}),e&&(e.open=n)}ensureFocusedElementVisible(){if(this.alwaysVisibleMember&&(this.alwaysVisibleMember.classList.remove("always-visible"),this.alwaysVisibleMember.firstElementChild.remove(),this.alwaysVisibleMember=null),!location.hash)return;let e=document.getElementById(location.hash.substring(1));if(!e)return;let n=e.parentElement;for(;n&&n.tagName!=="SECTION";)n=n.parentElement;if(!n)return;let r=n.offsetParent==null,i=n;for(;i!==document.body;)i instanceof HTMLDetailsElement&&(i.open=!0),i=i.parentElement;if(n.offsetParent==null){this.alwaysVisibleMember=n,n.classList.add("always-visible");let s=document.createElement("p");s.classList.add("warning"),s.textContent=window.translations.normally_hidden,n.prepend(s)}r&&e.scrollIntoView()}listenForCodeCopies(){document.querySelectorAll("pre > button").forEach(e=>{let n;e.addEventListener("click",()=>{e.previousElementSibling instanceof HTMLElement&&navigator.clipboard.writeText(e.previousElementSibling.innerText.trim()),e.textContent=window.translations.copied,e.classList.add("visible"),clearTimeout(n),n=setTimeout(()=>{e.classList.remove("visible"),n=setTimeout(()=>{e.textContent=window.translations.copy},100)},1e3)})})}};function rt(t){let e=t.getBoundingClientRect(),n=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(e.bottom<0||e.top-n>=0)}var fe=(t,e=100)=>{let n;return()=>{clearTimeout(n),n=setTimeout(()=>t(),e)}};var Ie=nt(ye(),1);async function R(t){let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0)),r=new Blob([e]).stream().pipeThrough(new DecompressionStream("deflate")),i=await new Response(r).text();return JSON.parse(i)}var Y="closing",ae="tsd-overlay";function it(){let t=Math.abs(window.innerWidth-document.documentElement.clientWidth);document.body.style.overflow="hidden",document.body.style.paddingRight=`${t}px`}function st(){document.body.style.removeProperty("overflow"),document.body.style.removeProperty("padding-right")}function xe(t,e){t.addEventListener("animationend",()=>{t.classList.contains(Y)&&(t.classList.remove(Y),document.getElementById(ae)?.remove(),t.close(),st())}),t.addEventListener("cancel",n=>{n.preventDefault(),ve(t)}),e?.closeOnClick&&document.addEventListener("click",n=>{t.open&&!t.contains(n.target)&&ve(t)},!0)}function Ee(t){if(t.open)return;let e=document.createElement("div");e.id=ae,document.body.appendChild(e),t.showModal(),it()}function ve(t){if(!t.open)return;document.getElementById(ae)?.classList.add(Y),t.classList.add(Y)}var I=class{el;app;constructor(e){this.el=e.el,this.app=e.app}};var be=document.head.appendChild(document.createElement("style"));be.dataset.for="filters";var le={};function we(t){for(let e of t.split(/\s+/))if(le.hasOwnProperty(e)&&!le[e])return!0;return!1}var ee=class extends I{key;value;constructor(e){super(e),this.key=`filter-${this.el.name}`,this.value=this.el.checked,this.el.addEventListener("change",()=>{this.setLocalStorage(this.el.checked)}),this.setLocalStorage(this.fromLocalStorage()),be.innerHTML+=`html:not(.${this.key}) .tsd-is-${this.el.name} { display: none; } +`,this.app.updateIndexVisibility()}fromLocalStorage(){let e=S.getItem(this.key);return e?e==="true":this.el.checked}setLocalStorage(e){S.setItem(this.key,e.toString()),this.value=e,this.handleValueChange()}handleValueChange(){this.el.checked=this.value,document.documentElement.classList.toggle(this.key,this.value),le[`tsd-is-${this.el.name}`]=this.value,this.app.filterChanged(),this.app.updateIndexVisibility()}};var Le=0;async function Se(t,e){if(!window.searchData)return;let n=await R(window.searchData);t.data=n,t.index=Ie.Index.load(n.index),e.innerHTML=""}function _e(){let t=document.getElementById("tsd-search-trigger"),e=document.getElementById("tsd-search"),n=document.getElementById("tsd-search-input"),r=document.getElementById("tsd-search-results"),i=document.getElementById("tsd-search-script"),s=document.getElementById("tsd-search-status");if(!(t&&e&&n&&r&&i&&s))throw new Error("Search controls missing");let o={base:document.documentElement.dataset.base};o.base.endsWith("/")||(o.base+="/"),i.addEventListener("error",()=>{let a=window.translations.search_index_not_available;Pe(s,a)}),i.addEventListener("load",()=>{Se(o,s)}),Se(o,s),ot({trigger:t,searchEl:e,results:r,field:n,status:s},o)}function ot(t,e){let{field:n,results:r,searchEl:i,status:s,trigger:o}=t;xe(i,{closeOnClick:!0});function a(){Ee(i),n.setSelectionRange(0,n.value.length)}o.addEventListener("click",a),n.addEventListener("input",fe(()=>{at(r,n,s,e)},200)),n.addEventListener("keydown",l=>{if(r.childElementCount===0||l.ctrlKey||l.metaKey||l.altKey)return;let d=n.getAttribute("aria-activedescendant"),f=d?document.getElementById(d):null;if(f){let p=!1,v=!1;switch(l.key){case"Home":case"End":case"ArrowLeft":case"ArrowRight":v=!0;break;case"ArrowDown":case"ArrowUp":p=l.shiftKey;break}(p||v)&&ke(n)}if(!l.shiftKey)switch(l.key){case"Enter":f?.querySelector("a")?.click();break;case"ArrowUp":Te(r,n,f,-1),l.preventDefault();break;case"ArrowDown":Te(r,n,f,1),l.preventDefault();break}});function c(){ke(n)}n.addEventListener("change",c),n.addEventListener("blur",c),n.addEventListener("click",c),document.body.addEventListener("keydown",l=>{if(l.altKey||l.metaKey||l.shiftKey)return;let d=l.ctrlKey&&l.key==="k",f=!l.ctrlKey&&!ut()&&l.key==="/";(d||f)&&(l.preventDefault(),a())})}function at(t,e,n,r){if(!r.index||!r.data)return;t.innerHTML="",n.innerHTML="",Le+=1;let i=e.value.trim(),s;if(i){let a=i.split(" ").map(c=>c.length?`*${c}*`:"").join(" ");s=r.index.search(a).filter(({ref:c})=>{let l=r.data.rows[Number(c)].classes;return!l||!we(l)})}else s=[];if(s.length===0&&i){let a=window.translations.search_no_results_found_for_0.replace("{0}",` "${te(i)}" `);Pe(n,a);return}for(let a=0;ac.score-a.score);let o=Math.min(10,s.length);for(let a=0;a`,f=Ce(c.name,i);globalThis.DEBUG_SEARCH_WEIGHTS&&(f+=` (score: ${s[a].score.toFixed(2)})`),c.parent&&(f=` + ${Ce(c.parent,i)}.${f}`);let p=document.createElement("li");p.id=`tsd-search:${Le}-${a}`,p.role="option",p.ariaSelected="false",p.classList.value=c.classes??"";let v=document.createElement("a");v.tabIndex=-1,v.href=r.base+c.url,v.innerHTML=d+`${f}`,p.append(v),t.appendChild(p)}}function Te(t,e,n,r){let i;if(r===1?i=n?.nextElementSibling||t.firstElementChild:i=n?.previousElementSibling||t.lastElementChild,i!==n){if(!i||i.role!=="option"){console.error("Option missing");return}i.ariaSelected="true",i.scrollIntoView({behavior:"smooth",block:"nearest"}),e.setAttribute("aria-activedescendant",i.id),n?.setAttribute("aria-selected","false")}}function ke(t){let e=t.getAttribute("aria-activedescendant");(e?document.getElementById(e):null)?.setAttribute("aria-selected","false"),t.setAttribute("aria-activedescendant","")}function Ce(t,e){if(e==="")return t;let n=t.toLocaleLowerCase(),r=e.toLocaleLowerCase(),i=[],s=0,o=n.indexOf(r);for(;o!=-1;)i.push(te(t.substring(s,o)),`${te(t.substring(o,o+r.length))}`),s=o+r.length,o=n.indexOf(r,s);return i.push(te(t.substring(s))),i.join("")}var lt={"&":"&","<":"<",">":">","'":"'",'"':"""};function te(t){return t.replace(/[&<>"'"]/g,e=>lt[e])}function Pe(t,e){t.innerHTML=e?`
${e}
`:""}var ct=["button","checkbox","file","hidden","image","radio","range","reset","submit"];function ut(){let t=document.activeElement;return t?t.isContentEditable||t.tagName==="TEXTAREA"||t.tagName==="SEARCH"?!0:t.tagName==="INPUT"&&!ct.includes(t.type):!1}var D="mousedown",Me="mousemove",$="mouseup",ne={x:0,y:0},Qe=!1,ce=!1,dt=!1,F=!1,Oe=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);document.documentElement.classList.add(Oe?"is-mobile":"not-mobile");Oe&&"ontouchstart"in document.documentElement&&(dt=!0,D="touchstart",Me="touchmove",$="touchend");document.addEventListener(D,t=>{ce=!0,F=!1;let e=D=="touchstart"?t.targetTouches[0]:t;ne.y=e.pageY||0,ne.x=e.pageX||0});document.addEventListener(Me,t=>{if(ce&&!F){let e=D=="touchstart"?t.targetTouches[0]:t,n=ne.x-(e.pageX||0),r=ne.y-(e.pageY||0);F=Math.sqrt(n*n+r*r)>10}});document.addEventListener($,()=>{ce=!1});document.addEventListener("click",t=>{Qe&&(t.preventDefault(),t.stopImmediatePropagation(),Qe=!1)});var re=class extends I{active;className;constructor(e){super(e),this.className=this.el.dataset.toggle||"",this.el.addEventListener($,n=>this.onPointerUp(n)),this.el.addEventListener("click",n=>n.preventDefault()),document.addEventListener(D,n=>this.onDocumentPointerDown(n)),document.addEventListener($,n=>this.onDocumentPointerUp(n))}setActive(e){if(this.active==e)return;this.active=e,document.documentElement.classList.toggle("has-"+this.className,e),this.el.classList.toggle("active",e);let n=(this.active?"to-has-":"from-has-")+this.className;document.documentElement.classList.add(n),setTimeout(()=>document.documentElement.classList.remove(n),500)}onPointerUp(e){F||(this.setActive(!0),e.preventDefault())}onDocumentPointerDown(e){if(this.active){if(e.target.closest(".col-sidebar, .tsd-filter-group"))return;this.setActive(!1)}}onDocumentPointerUp(e){if(!F&&this.active&&e.target.closest(".col-sidebar")){let n=e.target.closest("a");if(n){let r=window.location.href;r.indexOf("#")!=-1&&(r=r.substring(0,r.indexOf("#"))),n.href.substring(0,r.length)==r&&setTimeout(()=>this.setActive(!1),250)}}}};var ue=new Map,de=class{open;accordions=[];key;constructor(e,n){this.key=e,this.open=n}add(e){this.accordions.push(e),e.open=this.open,e.addEventListener("toggle",()=>{this.toggle(e.open)})}toggle(e){for(let n of this.accordions)n.open=e;S.setItem(this.key,e.toString())}},ie=class extends I{constructor(e){super(e);let n=this.el.querySelector("summary"),r=n.querySelector("a");r&&r.addEventListener("click",()=>{location.assign(r.href)});let i=`tsd-accordion-${n.dataset.key??n.textContent.trim().replace(/\s+/g,"-").toLowerCase()}`,s;if(ue.has(i))s=ue.get(i);else{let o=S.getItem(i),a=o?o==="true":this.el.open;s=new de(i,a),ue.set(i,s)}s.add(this.el)}};function He(t){let e=S.getItem("tsd-theme")||"os";t.value=e,Ae(e),t.addEventListener("change",()=>{S.setItem("tsd-theme",t.value),Ae(t.value)})}function Ae(t){document.documentElement.dataset.theme=t}var se;function Ne(){let t=document.getElementById("tsd-nav-script");t&&(t.addEventListener("load",Re),Re())}async function Re(){let t=document.getElementById("tsd-nav-container");if(!t||!window.navigationData)return;let e=await R(window.navigationData);se=document.documentElement.dataset.base,se.endsWith("/")||(se+="/"),t.innerHTML="";for(let n of e)Be(n,t,[]);window.app.createComponents(t),window.app.showPage(),window.app.ensureActivePageVisible()}function Be(t,e,n){let r=e.appendChild(document.createElement("li"));if(t.children){let i=[...n,t.text],s=r.appendChild(document.createElement("details"));s.className=t.class?`${t.class} tsd-accordion`:"tsd-accordion";let o=s.appendChild(document.createElement("summary"));o.className="tsd-accordion-summary",o.dataset.key=i.join("$"),o.innerHTML='',De(t,o);let a=s.appendChild(document.createElement("div"));a.className="tsd-accordion-details";let c=a.appendChild(document.createElement("ul"));c.className="tsd-nested-navigation";for(let l of t.children)Be(l,c,i)}else De(t,r,t.class)}function De(t,e,n){if(t.path){let r=e.appendChild(document.createElement("a"));if(r.href=se+t.path,n&&(r.className=n),location.pathname===r.pathname&&!r.href.includes("#")&&(r.classList.add("current"),r.ariaCurrent="page"),t.kind){let i=window.translations[`kind_${t.kind}`].replaceAll('"',""");r.innerHTML=``}r.appendChild(Fe(t.text,document.createElement("span")))}else{let r=e.appendChild(document.createElement("span")),i=window.translations.folder.replaceAll('"',""");r.innerHTML=``,r.appendChild(Fe(t.text,document.createElement("span")))}}function Fe(t,e){let n=t.split(/(?<=[^A-Z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[_-])(?=[^_-])/);for(let r=0;r{let i=r.target;for(;i.parentElement&&i.parentElement.tagName!="LI";)i=i.parentElement;i.dataset.dropdown&&(i.dataset.dropdown=String(i.dataset.dropdown!=="true"))});let t=new Map,e=new Set;for(let r of document.querySelectorAll(".tsd-full-hierarchy [data-refl]")){let i=r.querySelector("ul");t.has(r.dataset.refl)?e.add(r.dataset.refl):i&&t.set(r.dataset.refl,i)}for(let r of e)n(r);function n(r){let i=t.get(r).cloneNode(!0);i.querySelectorAll("[id]").forEach(s=>{s.removeAttribute("id")}),i.querySelectorAll("[data-dropdown]").forEach(s=>{s.dataset.dropdown="false"});for(let s of document.querySelectorAll(`[data-refl="${r}"]`)){let o=gt(),a=s.querySelector("ul");s.insertBefore(o,a),o.dataset.dropdown=String(!!a),a||s.appendChild(i.cloneNode(!0))}}}function pt(){let t=document.getElementById("tsd-hierarchy-script");t&&(t.addEventListener("load",Ve),Ve())}async function Ve(){let t=document.querySelector(".tsd-panel.tsd-hierarchy:has(h4 a)");if(!t||!window.hierarchyData)return;let e=+t.dataset.refl,n=await R(window.hierarchyData),r=t.querySelector("ul"),i=document.createElement("ul");if(i.classList.add("tsd-hierarchy"),ft(i,n,e),r.querySelectorAll("li").length==i.querySelectorAll("li").length)return;let s=document.createElement("span");s.classList.add("tsd-hierarchy-toggle"),s.textContent=window.translations.hierarchy_expand,t.querySelector("h4 a")?.insertAdjacentElement("afterend",s),s.insertAdjacentText("beforebegin",", "),s.addEventListener("click",()=>{s.textContent===window.translations.hierarchy_expand?(r.insertAdjacentElement("afterend",i),r.remove(),s.textContent=window.translations.hierarchy_collapse):(i.insertAdjacentElement("afterend",r),i.remove(),s.textContent=window.translations.hierarchy_expand)})}function ft(t,e,n){let r=e.roots.filter(i=>mt(e,i,n));for(let i of r)t.appendChild(je(e,i,n))}function je(t,e,n,r=new Set){if(r.has(e))return;r.add(e);let i=t.reflections[e],s=document.createElement("li");if(s.classList.add("tsd-hierarchy-item"),e===n){let o=s.appendChild(document.createElement("span"));o.textContent=i.name,o.classList.add("tsd-hierarchy-target")}else{for(let a of i.uniqueNameParents||[]){let c=t.reflections[a],l=s.appendChild(document.createElement("a"));l.textContent=c.name,l.href=oe+c.url,l.className=c.class+" tsd-signature-type",s.append(document.createTextNode("."))}let o=s.appendChild(document.createElement("a"));o.textContent=t.reflections[e].name,o.href=oe+i.url,o.className=i.class+" tsd-signature-type"}if(i.children){let o=s.appendChild(document.createElement("ul"));o.classList.add("tsd-hierarchy");for(let a of i.children){let c=je(t,a,n,r);c&&o.appendChild(c)}}return r.delete(e),s}function mt(t,e,n){if(e===n)return!0;let r=new Set,i=[t.reflections[e]];for(;i.length;){let s=i.pop();if(!r.has(s)){r.add(s);for(let o of s.children||[]){if(o===n)return!0;i.push(t.reflections[o])}}}return!1}function gt(){let t=document.createElementNS("http://www.w3.org/2000/svg","svg");return t.setAttribute("width","20"),t.setAttribute("height","20"),t.setAttribute("viewBox","0 0 24 24"),t.setAttribute("fill","none"),t.innerHTML='',t}X(re,"a[data-toggle]");X(ie,".tsd-accordion");X(ee,".tsd-filter-item input[type=checkbox]");var qe=document.getElementById("tsd-theme");qe&&He(qe);var yt=new Z;Object.defineProperty(window,"app",{value:yt});_e();Ne();$e();"virtualKeyboard"in navigator&&(navigator.virtualKeyboard.overlaysContent=!0);})(); +/*! Bundled license information: + +lunr/lunr.js: + (** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 + * Copyright (C) 2020 Oliver Nightingale + * @license MIT + *) + (*! + * lunr.utils + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Set + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.tokenizer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Pipeline + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Vector + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.stemmer + * Copyright (C) 2020 Oliver Nightingale + * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt + *) + (*! + * lunr.stopWordFilter + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.trimmer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.TokenSet + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Index + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Builder + * Copyright (C) 2020 Oliver Nightingale + *) +*/ diff --git a/docs/typedoc/assets/navigation.js b/docs/typedoc/assets/navigation.js new file mode 100644 index 00000000..a758c742 --- /dev/null +++ b/docs/typedoc/assets/navigation.js @@ -0,0 +1 @@ +window.navigationData = "eJylmE2P2zYQhv+L9rqImtQ13L3VQYtF2sUGbbI9BHugJMqibZmGKK/tFv3vhfihJaWZIZMgl3jfdx6QoxlypC//Zj2/9NldVsqOZ7fZkfWN/fWm6dt9dpvtxKHK7t7dZmUj9lXHD9ndlzGs4qWs+G+C7ysg+sbItZUNafH258WPPyz+u/Uh3fXYP3Cl2AZaxY11tKMDQ/EDuR4jx9bDD7H1WEd8PUI9sb2oCJRQL4MjGfXE9icK9GJ1DKNNrOfEkpwlvqa/6/eyo1Dnevh/CujXrpPde1khGD7IpZFxyIPafLoeEUSrNr0RccDHTvaylHu9GhhztBZuLTjsiXdKyAOMeRlFDLAXRQZ33ZCHMsTme1Hk+u9JfTs8OIKRWz1krcKehRvNg3ieELRMalmP5HkIEt4hHso3zVjP/rqG56sAjhES04yUtQfKPROeb7I0Q1pgDIlv3/nMkvVlw6sI0HeRCWuhY0CTrJKYMrCJfU4+eqh8oSfTBOX5qGRRl1RADI104acRQ2O8AeLE0EgQydsiQE6cZKXYsw9qLicl1gp4xAagfDRNi+VZ//PaYUiy9FD6d9JCyo6znv/C1e/8CgJujINxtdMO7Mw3tvuWlRFS07KSRtk6hCGvIh7eiReOrsLI9Aps3cKAVxELb5hq4FiroIG7qkYCjYIGtqxEAo1CzEHa/MD7RlYw4lybX63zfMO9L44N76Z4c7lpKfXuJxbr0fLQSQ4D0IP2UdZCHDVowYUYa4qfqxTGWshDqpRtG5wsHkVLScn+h3dSKE5gcmshFwP1gmYMQtJCSAKAWcY6ygveVTUVDHSVF9yyktz6jl8VGD4I3306j6TcdxG7IU/oKc3agP2FV0/LhFdpw6/UWWl6L4+xwwtPwmvXA+/ZPWeVf6yEjJ41Tv+KVx2fkfSqk8/GxwHxLeNjjJOPHmrew1MzYznbZIE/LSdP+dSL/Stq+JW0pWK5+CTXy8UJiL0ploteFkbEsjvImoAATpoQBdzzCw5otEgDHostDpBapAF/9Z04bHCGcjqN+bxiOOOkRQywFgfWXdenuvarwmNovXA6PubJ41r0CkIMWmE0dE7hF7wiGn6JVoQG4NnUiGgyNQTJpSbQqRRqzRSHi1KowmmxcDAHLp7KgVBIQQtFF7NQj8WWlz0cK52Gh+OJFyqa9Q9nsGq2Z6pgDkPzfux4LcAdH4b2PToZg8hii1edLLbRqlONqPs/eA1mTot7I5KAP8WmwQmdVVGEzi++D6PHt2IxSAk5Cl1IDoL0kIPQbXRaMXw3pxWLbkUDkH3oeHoTOhyvaE2IFjX+ymMO1JCsL2AjJF2g+KHtkXLfRX41A89un+QcxBSJtIJPGS0xDNAQM4720J87h0+ss4doPgRa6XumlQCUjyZib8jUMgMZUxQ0q3AIdM8vUdBsioFAj8U2CoKaBmIZXxQ3O0Ug1ucVo97U4LkiBI2mGCi+P88Wg0V25zzkx1Fo3ggxzpOAIXM0mkhQpCS1gQRAc8iUYTwkJv6gnIfAoCNGSPJsBAwZNULUaKIOSPS6D1m+LwEXeXKeLQEWKW3PRsCQMSBEjaYYKLI/54lh4oXl2cjbaTudejVme077ADSbmV10/mFKGO5683Xm+X+JavwM" \ No newline at end of file diff --git a/docs/typedoc/assets/search.js b/docs/typedoc/assets/search.js new file mode 100644 index 00000000..21853ac4 --- /dev/null +++ b/docs/typedoc/assets/search.js @@ -0,0 +1 @@ +window.searchData = "eJy1XV2T27aS/S/y61xbDX77zXYm106uP9Z2cnfLldrSSNQM7ZE0S1J2fF3571sASakBHFJNiXmzh0B3EzxodPcBhB+zcvetmj399GP2pdiuZk/V1Wy72OSzp7PlrsxnV7N9ed/+5/FdvbmfXc2W94uqyqvZ09nsr6uuX0hZGMzDQ+9Vvtyt8p+L/H7lC3nUPF23T5nAq9nDosy3dad/UEH5/aF+nVfV4hYY+qhtsDk0OEtNvh16j+bpRe+Rb0+8R9vgwvcoqt8X98WqX01RfdUNplHz++J+P6Dka/v4LBVGwqLO+1+la3Hhu/x7/WJXDqj5ttb/vljJdVnuyhe7FVaR66fL5umZCl5Xtx+/P2Dxm+q2bp6dKfxduat3y929eQuo4qFtkbctzlT0e15WxW4LVXw9PBMJt33ck/vi5oke4aUl+/jnQb+XWnDpF/KkfQyEOYYye45qKD7oef7qjUTHo5tieEDYC3amQ30/Pft4/fHV62uRUj3t6mIzDCix5usXMqVm1CfQ99v7Zx9fvZUN72pfLupToJNqfnn93yKld/mfk+j717OPIn33i3oafW/f/FOmcLe9nUTjbx9/TkUa9/U6PV9jfDriYYpZkzP9QHw6MmH6WJPL9fUu7Uwhb3OmRuCczcpR+Tqbv4vdM15omaQnrI3Q+tY0CMFnv318ef3m46sXPU6lR+mjxb6+y7d1sRT6l3YY+AtCe67fvHj/P+/G2dIGn5Pa8fPb96+R/+mzYb0rNxI/JNX/+vrjs5fXz366fi+3YZPXi7t8scqHI5hRdrx7//bj2xdv/yW3ogulprPh/fXP1++v37wAq3ufEWW+zst8uxSs8VIrPrz655tnH397P8KKqrjdLup9ebEVKhVFs7YxVrtzXUVE3Ndtq7rcL+uRah/ZHaUjYb8nG425Ojr85WKvZY0zp+szrSXDThsOy4mMSWZFzEejXt7lq2Fk8EbnwgIsgBuQg/IH4iUQpYJc0JNDE6H1nWV4AZTqebQQa3p8fAkcw4t1DldsxugEbqtHpwCTQp0/i3WuJ9P5SqyzmEznr2KdXybT+VqsczOZzndinQ+T6fwvsc7/m0wniLl6dArWM6HOD2KdgpVLqBPEtz06BZHtoE5ll6BwxdIxgDU729GLw5d+teOil+Mo8LdkC+c8PA5Fk2qPMubQZUo7mhR8lB2HLlPacZvXo4xo2k9pQVFdmxcD1YoBO4oqP/Sa1hpTqRhpy9e2z5SWVCO/TDX5l6l3L01dUW5DvRNVIkda8fbmc74cNxj1btd1mtaWD3VZmFLkGFuqrtO0tvyWLkYask9HRdQCKzpObZQhrNOUtqzL3eZ5sV2U30dZo7vddN2mtmfsDNJ9pp9DWuoZs0h3+1vmkRY8Fr26zyT4jUWbBSxz7HZnh0axiN63NNvtptA8wPhbmu12U2geYugt1U7Ds3WDkklLCYPyVfdEXDRBzLMl6cmhjfANDsbB1OEriVU9Mm2F2h4fXwUOncbf7qja/HfUnp9lmS/q/Fle/Zp/R3IeNQ0WefUlH/DBjR2n1LzcLJbDeu42i+VFilpvAFUcn50tvCy+5n1v0Dy9yPrWo0Dxx2dnCr9bVHdQcvvgXLFfVmsstnlwrtjNYonFNg/OFPvv9QvT5HVe3+1WUMG3dfO/TddEqMqdlg1VWjzc5aWjhz2Rb1TpN5uJe2I3POHbPDNxqfTFTy/HaXyUL1enEMXHwHk7XGz78OtIIx6qgfKi3AY/JBmwo21x/rjHp72Nra9tM4lG7H64vrbF+drwJNltNnyxdp8MThJm/3/ycldUeb+gJ20Luf2NZSfsB5718Hep7UMy+gRhm40krMN31UzHl9V6Ch2+32Y6NotTu+l6dcCR/5J/r5A2/XfpyA+EQAdZT3gj8RsY6wZ04njIVdq2Olfrcdw2i+I4yfR/RkWK/167ucFBhN6UemJLrdE9LLxevOx2ayD5JzdznFThJgVc/qntqI5we1Ah4es+GBxsmxFAA+3SEZL0y7NNzgScUCliAbwROL5Zz6YB/ZXFJrSNj7rravWPovqH3nGTL2tTfh5nyxAbccoawES09hTbu7wsLrbHYSVO2QMYiWnt4ezEKWNcZmJyS16PAc5tXnvYucwCnyc5ZQPmSKYdF5cvOW2Tz5VMa1E1AjMuZzK5JaMwU02OGZvBOaXfZ2+mHY9698sHtsn0tDmfqxOr5Vj9Tg38tAUnqt/n2OAwSKdtAOzR1N+F1+JPG+RW4ae1xmOUThkE2aRpbQLM0imrelil6e0aM8MRuzS9RaNmue4w8TwHbJfECjDXpx+bMTMNMV6XWxTFpzMiz6qu1dm5gBWCL/b17p/5Ni8X9alwxlKsj0DsblnHEYg5vGePTTf3u+WX5Z2bvp4wyOo1tTU/5Q/13WhrVm2vqa15s9/cjIJJY8626zahPcvddl2Um3HY4Z0mtKUNsUXptG1Q21NS1TjHqvZ8zqttUf+en867Pdva/sW2qL/m43NwuYW/5t9fbR/2J5x1n31f8u9F23tC65qzRYLUxjar6Ta8G+wse3ZlcVtsF/WufLZalXlVjbHq2Hlx6Py32PZuf8NLjaNMe9jfDNGaF1o2+kseu/4NX7PMl8VDkW/rMz7moe/f8i0Ph7hGD9ih598wXnW52FaLpZ7uLzmFITCLdR2koC+169V2dSoO7zWsaPv+PZZ9bI7dn2PY4In98+1a5sXX/KeizJe1WyqX2Ga6r1j3qe3bFJWu03/YL5cjJyfvXh26T2hfm+sWu+21ffhZYNyx7+Hg9GWWHav7+7q4P5ii/zOK9bmJw4+753G490U8uonDenfTPMPWGt2DwvdGOha+N9IvE84zcFf40F5OkfC3N597he/MswuEO7UoV/6JfcsiFTzhdeUPbec8Ibypxjzfr9csJ2HyzeOb7vFZKpbl7uF5UVdAvH500zw6S/Rd/mcv4u/yPy9DvBHe+2WN+Ms+rFGAv6uRfsFnLarniyqHk7WobrpHF4lGY97JPnvMiwo7gaK6wAEUlVPF4nJPVKZPiu4FSFFdho5fvqEZ8/nb2ZNlqx3suzJfF2iEt9rFPnRPz1Kwu/ncOxt3N58vm43VXbGu/5Wv0Vc0z+6bZ+cLf1/c3vVKL9uH54k3QOgdm+bxhcPTqsDTp9NwwSTqFGB/1Sm4wGXt00XvCO3TxWXDY4TjsTGyLxgYI7rXCxjp4xyBHQ6aAPLG5kycv4t3q/Qu9kzYE95ouEztmtcTa9/n21tWfz2p79Ghg0jtY+uteiiDxcNDvl2NsOHQYWobnn+v82q0ITdtr6mtcaeExJZTc+U8S1y/IrHklLcZa4kOR0cY0TafTn/+Z10unDhl2IRjj8mtGAeNttPU2GjFjgNH22lqdBTbKi/rsfO36TX9/G3kjvtITZ+pv1EjddwnavpM/YVwjDhsiSx2PMsSP6AUmHI60BxrS+0FPsNmnI6GxlswDh2n48jRfmTzUPfGT8iFtO2ns0BvQhhhQNt8Wv1jfZfZezO553I328jsmBqT7iYSmRUT4DI+URnjRnQNzo3D41P5M1d2aDGVtmH/d2xyrj6QKJm9AG4mZj2RnjTBxXRL1JNDG+EbHKzr0wgq7J7Gps2EGuFcdDS+zP+cUKNbi0ca3958nlAjyM+R0qbZhHqhh3GU/pYuJtGIi+G2xkOb6TSeHFrWajqtwwPbNZlEH6yl2/q6JpPqG/qOhzYTaRz2Aeb5RJpAKd5V1jSZSN9JeHZNJtHXV2O3VbJWk2jFhXdb56HNJBp7K9q2Ut5sUr3DeGWtJtU67HRYq0m04oK4rfPQZjqNw0PbNZlO38kJylqdrxXEip+/gcD787fhs8i8nO4ydJ2AJ79AIchSbUP/SbkP7Ee4B9ToQ3Knf6/besVfTuq1t5IO6h3eNyrVW1RabQ6K9JZOvSmobXaZvqp5z2f3t7uyqO9Acm7prdoxZs0n0S/5vtWU37fevdhtHmCZ2VKqf7C7a3epxp+bi06G1a2HbkMZoWt/f39aV9PoMl26ICAbS91yqtEEp3L6lQp2N0h1vlt8v98tTk1P3fLh0FKq9Y+rWbN58+mPWXe6/+lMPQ4eZ7Ormbn/TV+i15hzNdO/9KFFXc1Wu+Xe/POPtlmzRV83blo/mc+uPs2vovnjNIn/+OPqU9fZPDB/6GQc/2I60uzqE12F0eMkTq2O5HUkq6OaXX1SqKPyOiqrYzC7+hSgjoHXMbA6hrOrTyHqGHodQ6tjNLv6FKGOkdcxsjrGs6tPMeoYex1jq2Myu/qUoI6J1zGxOqazq08p6ph6HVOrYza7+pShjpnXMbMBoPFAcwgBHzzkoMfAB+MHAMhGEGlckLoK1eNwHtidfRCRjSLS2KAAQZ58IJGNJNL4oBB29sFENppIY4Qi2NkHFNmIIo0TimFnH1Rko4o0ViiBnX1gkY0s0nihFHb2wUU2ukhjhjLY2QcY2QhTGjMKeiflI0zZCFMaM4pgZx9hyvFRxkkp2Bm4KRthKuhzjcoHmLIBpsI+J6d8fCkbXyrq8zrKh5ey4aU0YBScFcqHl7LhpZI+H6J8dCkbXUrjRcEZpXx0KRtdSuNFwRmlfHQpG12BQRecUYGPrsBGV2DQBWdU4KMrsNEVGHTBGRX46AqcVVADRsEZFYCF0IZXYOAFZ1Tg4yuw8RVEvT478AEW2AALNGQCQj478AEW2AALNGYCOB0DH2GBjbBAYyaA0A58hAU2wgKNmQDCM/ARFtgICzVmAgjP0EdYaCMspN7lNfQRFtoICzVmAojt0EdYaCMsNIEWxHboIyx0Yi2DMIjtEIRbNsJCjZkAYjv0ERbaCAs1ZkKI7dBHWGgjLNSYCeFqEfoIC22EhRozIYRn6CMstBEWasyEEJ6hj7DQRlikMRNCeEY+wiIbYZHGTAjhGfkIi2yERRozIURY5CMsshEWacyEEGGRj7DIRlgU9oW6kQ+wyAnoo14vFIGY3gZYZACGNfsAi2yARQZgcJGMfIBFNsAiDZkI+oLIB1hkAyzSkImg2458gEU2wGINmQima7EPsNgGWKwhE8FoJvYBFtsAizVkohB9qtgHWGwDLNaQiWDqFvsAi22AaSrrU4TTNx9hsY2w2OSMCTTbR1jspI0aMxFEWAwyRxthscZMBBEW+wiLbYTFGjPxHJrtIyy2ERZrzMRwVsU+wmIbYYnGTAyTucRHWGIjLKG+YkLiAyyxAZaovrJA4uMrsfGVBH2VgcSHV2LDKwl7c3wfXYmNrsSE+HCxSHx0JTa6krg3bU58dCVOYULjJYYrTQJqEza6EoMuuNIkProSG12JQRecjomPrsRGV2rQBUc79dGV2uhKNWBiXFXx4ZXa8Eo1YmI4HVMfX6mNr1RDJoEOP/UBltoASw3AoMNPfYSlNsJSjZkETqnUR1hqIyzVmEmgw099hKU2wlJT+4ITMvURljrlL42ZBCIsBRUwG2Fp1q/ZR1hqIyzTmElgIJT5CMtshGUaMwkMhDIfYZmNsEz1wjPzEZbZCMuCXnhmPsIyG2FZ2AvPzEdYZiMsMwiDGULmIyyzEZYZhMEMIfMRltkIyzRmUpghZD7CMhthWdo7qzIfYZlTZM16Z1UG6qxuoXXeO62aZ3Z39re2v8ZNCpOb5pnb3ym3zlWvB26euf2dius86HXCzTO3v1N0nZuIH+ZXzTO3v1N3nWsMpT3jByqvc6f0OtcwSqF/aJ65/Z3q6zzpr1fPQf117hRg56a4jwvHc1CCnTs12Hk28P6gCjt38Gdq9zhrIlTq92r9Bn+4dg2r/Q7+TAUfJ16ECv5uxd8U8XHuRajm7xb9TR0fp1+Eyv5u3d+U8lNcfkeVf7f0b6r5OIMjVPx3q/+moI+TOEL1f5cAMDV9nMcRogBcDsCU9VPMICAWwKEByFT2cSpIgAgghwkgU9xPMQkBuAByyAAy9X2cTRKgA8jhA8jU+HFCSYASIIcTIFPnxzklAVqAHF6ATK0fp5UEqAFyuAEy5X6cWRJgB8ihB8iU/HFySYAhIIciIFP1z+C6TYAkIIclINWfnxLgCcghCijoT1EJUAXkcAVkyv896wdgC8ihC8gwABlevwFhQA5jQIYEyPD6CTgDckgDMjxA1kOZAvw5vAEZKiDDrClgDsihDsiwARle/wB5QA57QIYQyPD6A/gDcggEMpxAhv03oBDI4RDI0AIZ9n+ARSCHRiDDDGTYfwEigRwmgVoqAU8gQCaQwyaQIQhojhEICAVyGAUyJAHNMQQBqUAOq0CGKKA5xiAgFshhFsiQBTTvoe4BCB12gQxhQHOMQkAwkMMwkCENaI5hCEgGclgGMsQBzTEOAdFADtNAhjygOQYiIBvIYRsoarZ8YCQCwoEcxoGiZtsHRiIgHchhHcgQCUQYiYB4IId5IEMmEOFgCJAP5LAPZBgFIhwNAQaCHAqCooZGxcsBYCHIoSHIMAtEOB4BTAQ5VAQZdoEIBySAjSCHjiDDMBDhjA4wEuRQEmRYBiIckgBWghxaggzTQIRjEsBMkENNkGEbSOGgHrAT5NATFDebkHBUDhgKcigKMqwDKYxEwFKQQ1OQYR5IYSQCpoIcqoIM+0AKIxGwFeTQFRQ3FWWMRMBYkENZUNxkxhiJgLUgh7Ygw0SQwkgEzAU51AUZNkLvWocCABId+oIMI0EKIxEwGORQGGRoCQowEgGNQQ6PQYaboJ78HHAZ5JAZlDRb4nCACwgNchgNMiwFBRiJgNUgh9Ygw1RQgJEImA1yqA1quI0AIxGwG+TQG2QYCwowEgHDQQ7FQYa1oAAjEbAc5NAcZJgLCjASAdNBDtVBhr0gvH2CANtBDt1BKQ14ZcB4kEN5kGExCO/BIMB6kEN7kGEyCO/DIMB8kEN9UNps0MSBJmA/yKE/yDAahPdjEGBAyKFAyLAahPdkEGBByKFByDAbhPdlEGBCyKFCyLAbhPdmEGBDyKFDyDAchPdnEGBEyKFEyLAcFOJAE7Ai5NAiZJgOCnv2jQIkOtQIGbaDIoxEwI6QQ4+QYTwowkgEDAk5FAll4YBXBiwJOTQJZdGAVwZMCTlUCRn2o6dsBsgSctgSMgRIT9kJ8CXkECZkOBCK8FwEnAk5pAkZHoQiPBcBb0IOcaIMEUIR3l0KmBPlMCfKMCG4cqUAc6Ic5kQ1zAn8hAowJ8phTtQ86A9zFaBOlEOdqPkAChXgTpTDnaj5AAoVIE+UQ54oQ4ZQhDfpAvZEOeyJatgTnLEpQJ8ohz5R87Q/Y1OAP1EOf6LmWX/GpgCBohwCRTVHJXDGpgCDohwGRbXHJfDmbkChKIdCUc2RCZyxKcChKIdDUc2xCZwvKUCiKIdEUc3RCZwvKcCiKIdFUc3xCZwvKUCjKIdGUc0RCpwvKcCjKIdHUc0xCpwvKUCkKIdIUc1RCpwvKcCkKIdJUc1xCpwvKUClKPdEhZr350sKHapwT1Uo6k93FDpY4Z2sUP3pjoKHKxwkNscrcLKh0AkL94iFCvuTDYWOWbjnLFTUn2wodNbCPWyh4v5kQ6HzFu6BC5X0JxsKHbpwT12otD/ZUOjghXvyQjVrMwwyFTp84ZAqKpj3J94KsCrKYVWUYUkoglGqArSKcmgVFTQxIoxSFeBVlMOrqKCJEWGUqgCxohxiRRmihGJ8wgkwK8phVlRzJCPGp5wAtaIcakUFzbEyfNIJcCvK4VaU4UoIb0lUgFxRDrmiDFlCeGehAuyKctgVZdgSwrsLFaBXlEOvKEOXYHpQAXpFOfSKauiVGE8FQK90fzOner/mZZ2vmmtd9KHcxezqx+x/29O+weEOvR+zUM2e/vjrr+Pp3qc//mIHfPUzraX7Yd6jEJ1OH6TohFkupv1xOy5LcVnhCFnmB+q4pIBLikZIMj8yxyWFXFIsk7Sv7/JtXSwXzeUuR3GKSVOpVBi/LvAoK+MjH8g+ILuJhL0j8XGPgqavjnSFIrv7R7hI/gGisBMpe2V26wgXyb9EFHUisxEizV0jXGTERcatSJqPENn9vDiXGnOpSSeVRkh1UUgJF5l2IoUfvbAwaL+0VAK7iYTblXJZWdNdV+5EUtm9kwzWHIyBzKHwOyOZJI7BQOZOrOsemSiOvUA2aMtFvbzLV+aeIsvvMlmBDL3Lxb6yfWXARimQTdTmmnsmgg1PIJuY7PpJNjYcT4HMQy5326ou982NjMwk5tKCdjp2M52E03K5K63XnB9FygWYX8jQA7a04G4Bapyw7roqthYwaUo6bK20w32bbOzYZwhHvmn7sx+WeTEzLxEivswXdb7IK3MJIhPFHGLSOYm5zHk1Mu82i6UrlLnEdN4JFX4V8+PxfJXnGE6F8+lwfxJ3iBkTFM+7RUpq1veHeme9JLMrEc5RI6QBcPFwZ/uxhMlLx8vbbTZ2PJMw/59JQXKQ11xmeJSWsrmaST/CQdqX/Lv1LVIeH81lS5Ll2AKGsFD2BfUV7s2FgwwTPByQzXPX7/DVXzYuq9x19yH7VF2oQ8IIopFmfuPHsouZ1SJdOEy5+W4W0pl5aSstE1unpQGfyBZJ1XROhDjIy+Jr7nocNrfTVl4mfOF96eUCVuwkm4u2w2f9Q1l0ky9X1oRLGDQz2ZrR/jg7Dyd5wJYJX2TrwZMNbhczk3CRdS+hZrEJn3vCOKmR5oGdR0st2IWDvvXAnjBHl7bSslHS3LyS+XUliynx7dhs6LjTEcaH6EZrJpEDXhj5thLBp2VRdJteJjJ3cbi2ha//HCfChZHfusJF8XFLhW/JLk3hoviACefn2sIsm1ShbHlubv+2sMXGJhAawa8QZwDgwZEwRm3uYuDuhgfhmWx8zV0K7TVeR1ERMyfuXI4wEme3M3DbeHlnLn/Bw88T8qiBT2nhbDnc1cAWLeZq4rR7ya4oIQyLtNzPle1yMiaYhLUs/quIzEQWR8RdeqCyzsnKh/Hw24d8GBl6dW1UKsuZizFb5JIu2wgO8b3M89zm1ntHbAS7mo4+3iUUtclry8SUOx5hYcf+8VqO5ISjTzZs9i/ScmEpFyYbKzc3SBhI0tbhC4MN10NzLySbV+w6WyZH8RkQdxGwMGaxrrDlQnlQHatOqAwVx5truUQen8Vd7CIEyN2XlbWiJExY2lZmhE5Y5/CWKObI0y4hETrNwooE2HcIZe7buhiMr7gWNmSwZxd7ccxzcAgTGXYxF5fEESHMN473DHMg8JUz7mrywpSU3S7MRfK6aNzV5IU5alG1gbvlFNnbHgrKYhvdr8DjYr0/vLVP5jeO9xNziVY2fyjuy6bA8fez+ffNuHeUfl/kOvgC0K31eo+FTKIXrvHEritlkfjbGnEgbmdfpMVLIn1nI/Lr4n5vCWQfpPvAMt9mfoOejx8Pt7tARAlD3i+WR2IgDmUDdu8E3cx9CL9gd3Mrd2fcCaUyJ3S/s2HFFyQl84hWwM5J3VA28TYOJZTyFWwuWwq1jL5Secq9gpCT9KIt7geE3J6WcZcvVnZNlk9bYaRl3RfOEcyXry5MVcKvxm4J55QHh1DSBTlKhqVdWdwW20W9KxerVZlX9nybc9HC0Pwo8WF/49Tn9BlSJnCsiZ7/00dKmTyZl7J4Bb4NIZQtEQ/lTv/4/72FEOaYhOHzQ2U5pIRhTBjW/J/1Imydj2TuyMI451cimf4yXxYPRb6tIXR4KCMsfpb5Oi/z7dKuELMZI1x7D3IAYHg4JCy4WC/G61CR7EtXTlLJRqbLSUgY8FQgqeRrojDK8W/r4Pk4X9iEpHzVl6XyyoES0vLszlnu5rjrSNq0i7LDxhWhoewSWS6c+5Ek6IQftrAIDUdDwN2zdDRN5IhcPZ9USffqQkqnk+pG4Hy3AyXdKwtL551QNyXiXLc+bNcKlY2j9W14tTaSvSm7m4VDkXNNwl0Q3bUrXAzfFCAkLLobVbgYXogUFu+9b8ermV0lglSXCGTSd3QLhykPUoSxidm65aRiEQNBV9YgYWCCSi8RTyTCTp4UEw5Eef2sy4tJdVKF5HZdLrbVYqn5FLccpn8rgi01wm9xlNeIsQTygFaYtDCBHus959moMPA38pZ58TVfFWW+9BjTOV+NhAGVkbnRmfduW+2XSy+U4HAUxjb7dIH8Jx/BpCsNCAkFI9Jznhbxf8joZR/HSARlAs4bU7cdRwkr+Pt6nVrxE3d7srmnbyCy98zwtVcY9bgXE1svyL9oKvyi/iXGvETDuZi58INaV91xYZyREJK1X8mqeDDHnMjAYGLVRW1XYhgQutoYCRfRTh7Irhlm2zmQyFb7Vmax2/qb5fTPujCQyFaNb2t/C58VoYullOhF+Y6Vpm8klmi2LW3y+m5nZRAJMy9tR0/IIX9bm1Fz91WwudBNdRlivq3RK/MQcd6ta8I92FoiKoCk/JvMOyAKSyHf1pvqtv7+4Cw+XGLTU1hg+LbuMnBv467lp9rUSph0fFt3N6tZ5RoeCLVT5UCEyL7Sf/JyV1T2/OOfXOCr/riaPRQP+X2xzWdPP/3x11//DxBGwcM="; \ No newline at end of file diff --git a/docs/typedoc/assets/style.css b/docs/typedoc/assets/style.css new file mode 100644 index 00000000..44328e99 --- /dev/null +++ b/docs/typedoc/assets/style.css @@ -0,0 +1,1633 @@ +@layer typedoc { + :root { + --dim-toolbar-contents-height: 2.5rem; + --dim-toolbar-border-bottom-width: 1px; + --dim-header-height: calc( + var(--dim-toolbar-border-bottom-width) + + var(--dim-toolbar-contents-height) + ); + + /* 0rem For mobile; unit is required for calculation in `calc` */ + --dim-container-main-margin-y: 0rem; + + --dim-footer-height: 3.5rem; + + --modal-animation-duration: 0.2s; + } + + :root { + /* Light */ + --light-color-background: #f2f4f8; + --light-color-background-secondary: #eff0f1; + /* Not to be confused with [:active](https://developer.mozilla.org/en-US/docs/Web/CSS/:active) */ + --light-color-background-active: #d6d8da; + --light-color-background-warning: #e6e600; + --light-color-warning-text: #222; + --light-color-accent: #c5c7c9; + --light-color-active-menu-item: var(--light-color-background-active); + --light-color-text: #222; + --light-color-contrast-text: #000; + --light-color-text-aside: #5e5e5e; + + --light-color-icon-background: var(--light-color-background); + --light-color-icon-text: var(--light-color-text); + + --light-color-comment-tag-text: var(--light-color-text); + --light-color-comment-tag: var(--light-color-background); + + --light-color-link: #1f70c2; + --light-color-focus-outline: #3584e4; + + --light-color-ts-keyword: #056bd6; + --light-color-ts-project: #b111c9; + --light-color-ts-module: var(--light-color-ts-project); + --light-color-ts-namespace: var(--light-color-ts-project); + --light-color-ts-enum: #7e6f15; + --light-color-ts-enum-member: var(--light-color-ts-enum); + --light-color-ts-variable: #4760ec; + --light-color-ts-function: #572be7; + --light-color-ts-class: #1f70c2; + --light-color-ts-interface: #108024; + --light-color-ts-constructor: var(--light-color-ts-class); + --light-color-ts-property: #9f5f30; + --light-color-ts-method: #be3989; + --light-color-ts-reference: #ff4d82; + --light-color-ts-call-signature: var(--light-color-ts-method); + --light-color-ts-index-signature: var(--light-color-ts-property); + --light-color-ts-constructor-signature: var( + --light-color-ts-constructor + ); + --light-color-ts-parameter: var(--light-color-ts-variable); + /* type literal not included as links will never be generated to it */ + --light-color-ts-type-parameter: #a55c0e; + --light-color-ts-accessor: #c73c3c; + --light-color-ts-get-signature: var(--light-color-ts-accessor); + --light-color-ts-set-signature: var(--light-color-ts-accessor); + --light-color-ts-type-alias: #d51270; + /* reference not included as links will be colored with the kind that it points to */ + --light-color-document: #000000; + + --light-color-alert-note: #0969d9; + --light-color-alert-tip: #1a7f37; + --light-color-alert-important: #8250df; + --light-color-alert-warning: #9a6700; + --light-color-alert-caution: #cf222e; + + --light-external-icon: url("data:image/svg+xml;utf8,"); + --light-color-scheme: light; + } + + :root { + /* Dark */ + --dark-color-background: #2b2e33; + --dark-color-background-secondary: #1e2024; + /* Not to be confused with [:active](https://developer.mozilla.org/en-US/docs/Web/CSS/:active) */ + --dark-color-background-active: #5d5d6a; + --dark-color-background-warning: #bebe00; + --dark-color-warning-text: #222; + --dark-color-accent: #9096a2; + --dark-color-active-menu-item: var(--dark-color-background-active); + --dark-color-text: #f5f5f5; + --dark-color-contrast-text: #ffffff; + --dark-color-text-aside: #dddddd; + + --dark-color-icon-background: var(--dark-color-background-secondary); + --dark-color-icon-text: var(--dark-color-text); + + --dark-color-comment-tag-text: var(--dark-color-text); + --dark-color-comment-tag: var(--dark-color-background); + + --dark-color-link: #00aff4; + --dark-color-focus-outline: #4c97f2; + + --dark-color-ts-keyword: #3399ff; + --dark-color-ts-project: #e358ff; + --dark-color-ts-module: var(--dark-color-ts-project); + --dark-color-ts-namespace: var(--dark-color-ts-project); + --dark-color-ts-enum: #f4d93e; + --dark-color-ts-enum-member: var(--dark-color-ts-enum); + --dark-color-ts-variable: #798dff; + --dark-color-ts-function: #a280ff; + --dark-color-ts-class: #8ac4ff; + --dark-color-ts-interface: #6cff87; + --dark-color-ts-constructor: var(--dark-color-ts-class); + --dark-color-ts-property: #ff984d; + --dark-color-ts-method: #ff4db8; + --dark-color-ts-reference: #ff4d82; + --dark-color-ts-call-signature: var(--dark-color-ts-method); + --dark-color-ts-index-signature: var(--dark-color-ts-property); + --dark-color-ts-constructor-signature: var(--dark-color-ts-constructor); + --dark-color-ts-parameter: var(--dark-color-ts-variable); + /* type literal not included as links will never be generated to it */ + --dark-color-ts-type-parameter: #e07d13; + --dark-color-ts-accessor: #ff6060; + --dark-color-ts-get-signature: var(--dark-color-ts-accessor); + --dark-color-ts-set-signature: var(--dark-color-ts-accessor); + --dark-color-ts-type-alias: #ff6492; + /* reference not included as links will be colored with the kind that it points to */ + --dark-color-document: #ffffff; + + --dark-color-alert-note: #0969d9; + --dark-color-alert-tip: #1a7f37; + --dark-color-alert-important: #8250df; + --dark-color-alert-warning: #9a6700; + --dark-color-alert-caution: #cf222e; + + --dark-external-icon: url("data:image/svg+xml;utf8,"); + --dark-color-scheme: dark; + } + + @media (prefers-color-scheme: light) { + :root { + --color-background: var(--light-color-background); + --color-background-secondary: var( + --light-color-background-secondary + ); + --color-background-active: var(--light-color-background-active); + --color-background-warning: var(--light-color-background-warning); + --color-warning-text: var(--light-color-warning-text); + --color-accent: var(--light-color-accent); + --color-active-menu-item: var(--light-color-active-menu-item); + --color-text: var(--light-color-text); + --color-contrast-text: var(--light-color-contrast-text); + --color-text-aside: var(--light-color-text-aside); + + --color-icon-background: var(--light-color-icon-background); + --color-icon-text: var(--light-color-icon-text); + + --color-comment-tag-text: var(--light-color-text); + --color-comment-tag: var(--light-color-background); + + --color-link: var(--light-color-link); + --color-focus-outline: var(--light-color-focus-outline); + + --color-ts-keyword: var(--light-color-ts-keyword); + --color-ts-project: var(--light-color-ts-project); + --color-ts-module: var(--light-color-ts-module); + --color-ts-namespace: var(--light-color-ts-namespace); + --color-ts-enum: var(--light-color-ts-enum); + --color-ts-enum-member: var(--light-color-ts-enum-member); + --color-ts-variable: var(--light-color-ts-variable); + --color-ts-function: var(--light-color-ts-function); + --color-ts-class: var(--light-color-ts-class); + --color-ts-interface: var(--light-color-ts-interface); + --color-ts-constructor: var(--light-color-ts-constructor); + --color-ts-property: var(--light-color-ts-property); + --color-ts-method: var(--light-color-ts-method); + --color-ts-reference: var(--light-color-ts-reference); + --color-ts-call-signature: var(--light-color-ts-call-signature); + --color-ts-index-signature: var(--light-color-ts-index-signature); + --color-ts-constructor-signature: var( + --light-color-ts-constructor-signature + ); + --color-ts-parameter: var(--light-color-ts-parameter); + --color-ts-type-parameter: var(--light-color-ts-type-parameter); + --color-ts-accessor: var(--light-color-ts-accessor); + --color-ts-get-signature: var(--light-color-ts-get-signature); + --color-ts-set-signature: var(--light-color-ts-set-signature); + --color-ts-type-alias: var(--light-color-ts-type-alias); + --color-document: var(--light-color-document); + + --color-alert-note: var(--light-color-alert-note); + --color-alert-tip: var(--light-color-alert-tip); + --color-alert-important: var(--light-color-alert-important); + --color-alert-warning: var(--light-color-alert-warning); + --color-alert-caution: var(--light-color-alert-caution); + + --external-icon: var(--light-external-icon); + --color-scheme: var(--light-color-scheme); + } + } + + @media (prefers-color-scheme: dark) { + :root { + --color-background: var(--dark-color-background); + --color-background-secondary: var( + --dark-color-background-secondary + ); + --color-background-active: var(--dark-color-background-active); + --color-background-warning: var(--dark-color-background-warning); + --color-warning-text: var(--dark-color-warning-text); + --color-accent: var(--dark-color-accent); + --color-active-menu-item: var(--dark-color-active-menu-item); + --color-text: var(--dark-color-text); + --color-contrast-text: var(--dark-color-contrast-text); + --color-text-aside: var(--dark-color-text-aside); + + --color-icon-background: var(--dark-color-icon-background); + --color-icon-text: var(--dark-color-icon-text); + + --color-comment-tag-text: var(--dark-color-text); + --color-comment-tag: var(--dark-color-background); + + --color-link: var(--dark-color-link); + --color-focus-outline: var(--dark-color-focus-outline); + + --color-ts-keyword: var(--dark-color-ts-keyword); + --color-ts-project: var(--dark-color-ts-project); + --color-ts-module: var(--dark-color-ts-module); + --color-ts-namespace: var(--dark-color-ts-namespace); + --color-ts-enum: var(--dark-color-ts-enum); + --color-ts-enum-member: var(--dark-color-ts-enum-member); + --color-ts-variable: var(--dark-color-ts-variable); + --color-ts-function: var(--dark-color-ts-function); + --color-ts-class: var(--dark-color-ts-class); + --color-ts-interface: var(--dark-color-ts-interface); + --color-ts-constructor: var(--dark-color-ts-constructor); + --color-ts-property: var(--dark-color-ts-property); + --color-ts-method: var(--dark-color-ts-method); + --color-ts-reference: var(--dark-color-ts-reference); + --color-ts-call-signature: var(--dark-color-ts-call-signature); + --color-ts-index-signature: var(--dark-color-ts-index-signature); + --color-ts-constructor-signature: var( + --dark-color-ts-constructor-signature + ); + --color-ts-parameter: var(--dark-color-ts-parameter); + --color-ts-type-parameter: var(--dark-color-ts-type-parameter); + --color-ts-accessor: var(--dark-color-ts-accessor); + --color-ts-get-signature: var(--dark-color-ts-get-signature); + --color-ts-set-signature: var(--dark-color-ts-set-signature); + --color-ts-type-alias: var(--dark-color-ts-type-alias); + --color-document: var(--dark-color-document); + + --color-alert-note: var(--dark-color-alert-note); + --color-alert-tip: var(--dark-color-alert-tip); + --color-alert-important: var(--dark-color-alert-important); + --color-alert-warning: var(--dark-color-alert-warning); + --color-alert-caution: var(--dark-color-alert-caution); + + --external-icon: var(--dark-external-icon); + --color-scheme: var(--dark-color-scheme); + } + } + + :root[data-theme="light"] { + --color-background: var(--light-color-background); + --color-background-secondary: var(--light-color-background-secondary); + --color-background-active: var(--light-color-background-active); + --color-background-warning: var(--light-color-background-warning); + --color-warning-text: var(--light-color-warning-text); + --color-icon-background: var(--light-color-icon-background); + --color-accent: var(--light-color-accent); + --color-active-menu-item: var(--light-color-active-menu-item); + --color-text: var(--light-color-text); + --color-contrast-text: var(--light-color-contrast-text); + --color-text-aside: var(--light-color-text-aside); + --color-icon-text: var(--light-color-icon-text); + + --color-comment-tag-text: var(--light-color-text); + --color-comment-tag: var(--light-color-background); + + --color-link: var(--light-color-link); + --color-focus-outline: var(--light-color-focus-outline); + + --color-ts-keyword: var(--light-color-ts-keyword); + --color-ts-project: var(--light-color-ts-project); + --color-ts-module: var(--light-color-ts-module); + --color-ts-namespace: var(--light-color-ts-namespace); + --color-ts-enum: var(--light-color-ts-enum); + --color-ts-enum-member: var(--light-color-ts-enum-member); + --color-ts-variable: var(--light-color-ts-variable); + --color-ts-function: var(--light-color-ts-function); + --color-ts-class: var(--light-color-ts-class); + --color-ts-interface: var(--light-color-ts-interface); + --color-ts-constructor: var(--light-color-ts-constructor); + --color-ts-property: var(--light-color-ts-property); + --color-ts-method: var(--light-color-ts-method); + --color-ts-reference: var(--light-color-ts-reference); + --color-ts-call-signature: var(--light-color-ts-call-signature); + --color-ts-index-signature: var(--light-color-ts-index-signature); + --color-ts-constructor-signature: var( + --light-color-ts-constructor-signature + ); + --color-ts-parameter: var(--light-color-ts-parameter); + --color-ts-type-parameter: var(--light-color-ts-type-parameter); + --color-ts-accessor: var(--light-color-ts-accessor); + --color-ts-get-signature: var(--light-color-ts-get-signature); + --color-ts-set-signature: var(--light-color-ts-set-signature); + --color-ts-type-alias: var(--light-color-ts-type-alias); + --color-document: var(--light-color-document); + + --color-note: var(--light-color-note); + --color-tip: var(--light-color-tip); + --color-important: var(--light-color-important); + --color-warning: var(--light-color-warning); + --color-caution: var(--light-color-caution); + + --external-icon: var(--light-external-icon); + --color-scheme: var(--light-color-scheme); + } + + :root[data-theme="dark"] { + --color-background: var(--dark-color-background); + --color-background-secondary: var(--dark-color-background-secondary); + --color-background-active: var(--dark-color-background-active); + --color-background-warning: var(--dark-color-background-warning); + --color-warning-text: var(--dark-color-warning-text); + --color-icon-background: var(--dark-color-icon-background); + --color-accent: var(--dark-color-accent); + --color-active-menu-item: var(--dark-color-active-menu-item); + --color-text: var(--dark-color-text); + --color-contrast-text: var(--dark-color-contrast-text); + --color-text-aside: var(--dark-color-text-aside); + --color-icon-text: var(--dark-color-icon-text); + + --color-comment-tag-text: var(--dark-color-text); + --color-comment-tag: var(--dark-color-background); + + --color-link: var(--dark-color-link); + --color-focus-outline: var(--dark-color-focus-outline); + + --color-ts-keyword: var(--dark-color-ts-keyword); + --color-ts-project: var(--dark-color-ts-project); + --color-ts-module: var(--dark-color-ts-module); + --color-ts-namespace: var(--dark-color-ts-namespace); + --color-ts-enum: var(--dark-color-ts-enum); + --color-ts-enum-member: var(--dark-color-ts-enum-member); + --color-ts-variable: var(--dark-color-ts-variable); + --color-ts-function: var(--dark-color-ts-function); + --color-ts-class: var(--dark-color-ts-class); + --color-ts-interface: var(--dark-color-ts-interface); + --color-ts-constructor: var(--dark-color-ts-constructor); + --color-ts-property: var(--dark-color-ts-property); + --color-ts-method: var(--dark-color-ts-method); + --color-ts-reference: var(--dark-color-ts-reference); + --color-ts-call-signature: var(--dark-color-ts-call-signature); + --color-ts-index-signature: var(--dark-color-ts-index-signature); + --color-ts-constructor-signature: var( + --dark-color-ts-constructor-signature + ); + --color-ts-parameter: var(--dark-color-ts-parameter); + --color-ts-type-parameter: var(--dark-color-ts-type-parameter); + --color-ts-accessor: var(--dark-color-ts-accessor); + --color-ts-get-signature: var(--dark-color-ts-get-signature); + --color-ts-set-signature: var(--dark-color-ts-set-signature); + --color-ts-type-alias: var(--dark-color-ts-type-alias); + --color-document: var(--dark-color-document); + + --color-note: var(--dark-color-note); + --color-tip: var(--dark-color-tip); + --color-important: var(--dark-color-important); + --color-warning: var(--dark-color-warning); + --color-caution: var(--dark-color-caution); + + --external-icon: var(--dark-external-icon); + --color-scheme: var(--dark-color-scheme); + } + + html { + color-scheme: var(--color-scheme); + @media (prefers-reduced-motion: no-preference) { + scroll-behavior: smooth; + } + } + + *:focus-visible, + .tsd-accordion-summary:focus-visible svg { + outline: 2px solid var(--color-focus-outline); + } + + .always-visible, + .always-visible .tsd-signatures { + display: inherit !important; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + line-height: 1.2; + } + + h1 { + font-size: 1.875rem; + margin: 0.67rem 0; + } + + h2 { + font-size: 1.5rem; + margin: 0.83rem 0; + } + + h3 { + font-size: 1.25rem; + margin: 1rem 0; + } + + h4 { + font-size: 1.05rem; + margin: 1.33rem 0; + } + + h5 { + font-size: 1rem; + margin: 1.5rem 0; + } + + h6 { + font-size: 0.875rem; + margin: 2.33rem 0; + } + + dl, + menu, + ol, + ul { + margin: 1em 0; + } + + dd { + margin: 0 0 0 34px; + } + + .container { + max-width: 1700px; + padding: 0 2rem; + } + + /* Footer */ + footer { + border-top: 1px solid var(--color-accent); + padding-top: 1rem; + padding-bottom: 1rem; + max-height: var(--dim-footer-height); + } + footer > p { + margin: 0 1em; + } + + .container-main { + margin: var(--dim-container-main-margin-y) auto; + /* toolbar, footer, margin */ + min-height: calc( + 100svh - var(--dim-header-height) - var(--dim-footer-height) - + 2 * var(--dim-container-main-margin-y) + ); + } + + @keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + @keyframes fade-out { + from { + opacity: 1; + visibility: visible; + } + to { + opacity: 0; + } + } + @keyframes pop-in-from-right { + from { + transform: translate(100%, 0); + } + to { + transform: translate(0, 0); + } + } + @keyframes pop-out-to-right { + from { + transform: translate(0, 0); + visibility: visible; + } + to { + transform: translate(100%, 0); + } + } + body { + background: var(--color-background); + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", + Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + font-size: 16px; + color: var(--color-text); + margin: 0; + } + + a { + color: var(--color-link); + text-decoration: none; + } + a:hover { + text-decoration: underline; + } + a.external[target="_blank"] { + background-image: var(--external-icon); + background-position: top 3px right; + background-repeat: no-repeat; + padding-right: 13px; + } + a.tsd-anchor-link { + color: var(--color-text); + } + :target { + scroll-margin-block: calc(var(--dim-header-height) + 0.5rem); + } + + code, + pre { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + padding: 0.2em; + margin: 0; + font-size: 0.875rem; + border-radius: 0.8em; + } + + pre { + position: relative; + white-space: pre-wrap; + word-wrap: break-word; + padding: 10px; + border: 1px solid var(--color-accent); + margin-bottom: 8px; + } + pre code { + padding: 0; + font-size: 100%; + } + pre > button { + position: absolute; + top: 10px; + right: 10px; + opacity: 0; + transition: opacity 0.1s; + box-sizing: border-box; + } + pre:hover > button, + pre > button.visible, + pre > button:focus-visible { + opacity: 1; + } + + blockquote { + margin: 1em 0; + padding-left: 1em; + border-left: 4px solid gray; + } + + img { + max-width: 100%; + } + + * { + scrollbar-width: thin; + scrollbar-color: var(--color-accent) var(--color-icon-background); + } + + *::-webkit-scrollbar { + width: 0.75rem; + } + + *::-webkit-scrollbar-track { + background: var(--color-icon-background); + } + + *::-webkit-scrollbar-thumb { + background-color: var(--color-accent); + border-radius: 999rem; + border: 0.25rem solid var(--color-icon-background); + } + + dialog { + border: none; + outline: none; + padding: 0; + background-color: var(--color-background); + } + dialog::backdrop { + display: none; + } + #tsd-overlay { + background-color: rgba(0, 0, 0, 0.5); + position: fixed; + z-index: 9999; + top: 0; + left: 0; + right: 0; + bottom: 0; + animation: fade-in var(--modal-animation-duration) forwards; + } + #tsd-overlay.closing { + animation-name: fade-out; + } + + .tsd-typography { + line-height: 1.333em; + } + .tsd-typography ul { + list-style: square; + padding: 0 0 0 20px; + margin: 0; + } + .tsd-typography .tsd-index-panel h3, + .tsd-index-panel .tsd-typography h3, + .tsd-typography h4, + .tsd-typography h5, + .tsd-typography h6 { + font-size: 1em; + } + .tsd-typography h5, + .tsd-typography h6 { + font-weight: normal; + } + .tsd-typography p, + .tsd-typography ul, + .tsd-typography ol { + margin: 1em 0; + } + .tsd-typography table { + border-collapse: collapse; + border: none; + } + .tsd-typography td, + .tsd-typography th { + padding: 6px 13px; + border: 1px solid var(--color-accent); + } + .tsd-typography thead, + .tsd-typography tr:nth-child(even) { + background-color: var(--color-background-secondary); + } + + .tsd-alert { + padding: 8px 16px; + margin-bottom: 16px; + border-left: 0.25em solid var(--alert-color); + } + .tsd-alert blockquote > :last-child, + .tsd-alert > :last-child { + margin-bottom: 0; + } + .tsd-alert-title { + color: var(--alert-color); + display: inline-flex; + align-items: center; + } + .tsd-alert-title span { + margin-left: 4px; + } + + .tsd-alert-note { + --alert-color: var(--color-alert-note); + } + .tsd-alert-tip { + --alert-color: var(--color-alert-tip); + } + .tsd-alert-important { + --alert-color: var(--color-alert-important); + } + .tsd-alert-warning { + --alert-color: var(--color-alert-warning); + } + .tsd-alert-caution { + --alert-color: var(--color-alert-caution); + } + + .tsd-breadcrumb { + margin: 0; + margin-top: 1rem; + padding: 0; + color: var(--color-text-aside); + } + .tsd-breadcrumb a { + color: var(--color-text-aside); + text-decoration: none; + } + .tsd-breadcrumb a:hover { + text-decoration: underline; + } + .tsd-breadcrumb li { + display: inline; + } + .tsd-breadcrumb li:after { + content: " / "; + } + + .tsd-comment-tags { + display: flex; + flex-direction: column; + } + dl.tsd-comment-tag-group { + display: flex; + align-items: center; + overflow: hidden; + margin: 0.5em 0; + } + dl.tsd-comment-tag-group dt { + display: flex; + margin-right: 0.5em; + font-size: 0.875em; + font-weight: normal; + } + dl.tsd-comment-tag-group dd { + margin: 0; + } + code.tsd-tag { + padding: 0.25em 0.4em; + border: 0.1em solid var(--color-accent); + margin-right: 0.25em; + font-size: 70%; + } + h1 code.tsd-tag:first-of-type { + margin-left: 0.25em; + } + + dl.tsd-comment-tag-group dd:before, + dl.tsd-comment-tag-group dd:after { + content: " "; + } + dl.tsd-comment-tag-group dd pre, + dl.tsd-comment-tag-group dd:after { + clear: both; + } + dl.tsd-comment-tag-group p { + margin: 0; + } + + .tsd-panel.tsd-comment .lead { + font-size: 1.1em; + line-height: 1.333em; + margin-bottom: 2em; + } + .tsd-panel.tsd-comment .lead:last-child { + margin-bottom: 0; + } + + .tsd-filter-visibility h4 { + font-size: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.5rem; + margin: 0; + } + .tsd-filter-item:not(:last-child) { + margin-bottom: 0.5rem; + } + .tsd-filter-input { + display: flex; + width: -moz-fit-content; + width: fit-content; + align-items: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + } + .tsd-filter-input input[type="checkbox"] { + cursor: pointer; + position: absolute; + width: 1.5em; + height: 1.5em; + opacity: 0; + } + .tsd-filter-input input[type="checkbox"]:disabled { + pointer-events: none; + } + .tsd-filter-input svg { + cursor: pointer; + width: 1.5em; + height: 1.5em; + margin-right: 0.5em; + border-radius: 0.33em; + /* Leaving this at full opacity breaks event listeners on Firefox. + Don't remove unless you know what you're doing. */ + opacity: 0.99; + } + .tsd-filter-input input[type="checkbox"]:focus-visible + svg { + outline: 2px solid var(--color-focus-outline); + } + .tsd-checkbox-background { + fill: var(--color-accent); + } + input[type="checkbox"]:checked ~ svg .tsd-checkbox-checkmark { + stroke: var(--color-text); + } + .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-background { + fill: var(--color-background); + stroke: var(--color-accent); + stroke-width: 0.25rem; + } + .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-checkmark { + stroke: var(--color-accent); + } + + .settings-label { + font-weight: bold; + text-transform: uppercase; + display: inline-block; + } + + .tsd-filter-visibility .settings-label { + margin: 0.75rem 0 0.5rem 0; + } + + .tsd-theme-toggle .settings-label { + margin: 0.75rem 0.75rem 0 0; + } + + .tsd-hierarchy h4 label:hover span { + text-decoration: underline; + } + + .tsd-hierarchy { + list-style: square; + margin: 0; + } + .tsd-hierarchy-target { + font-weight: bold; + } + .tsd-hierarchy-toggle { + color: var(--color-link); + cursor: pointer; + } + + .tsd-full-hierarchy:not(:last-child) { + margin-bottom: 1em; + padding-bottom: 1em; + border-bottom: 1px solid var(--color-accent); + } + .tsd-full-hierarchy, + .tsd-full-hierarchy ul { + list-style: none; + margin: 0; + padding: 0; + } + .tsd-full-hierarchy ul { + padding-left: 1.5rem; + } + .tsd-full-hierarchy a { + padding: 0.25rem 0 !important; + font-size: 1rem; + display: inline-flex; + align-items: center; + color: var(--color-text); + } + .tsd-full-hierarchy svg[data-dropdown] { + cursor: pointer; + } + .tsd-full-hierarchy svg[data-dropdown="false"] { + transform: rotate(-90deg); + } + .tsd-full-hierarchy svg[data-dropdown="false"] ~ ul { + display: none; + } + + .tsd-panel-group.tsd-index-group { + margin-bottom: 0; + } + .tsd-index-panel .tsd-index-list { + list-style: none; + line-height: 1.333em; + margin: 0; + padding: 0.25rem 0 0 0; + overflow: hidden; + display: grid; + grid-template-columns: repeat(3, 1fr); + column-gap: 1rem; + grid-template-rows: auto; + } + @media (max-width: 1024px) { + .tsd-index-panel .tsd-index-list { + grid-template-columns: repeat(2, 1fr); + } + } + @media (max-width: 768px) { + .tsd-index-panel .tsd-index-list { + grid-template-columns: repeat(1, 1fr); + } + } + .tsd-index-panel .tsd-index-list li { + -webkit-page-break-inside: avoid; + -moz-page-break-inside: avoid; + -ms-page-break-inside: avoid; + -o-page-break-inside: avoid; + page-break-inside: avoid; + } + + .tsd-flag { + display: inline-block; + padding: 0.25em 0.4em; + border-radius: 4px; + color: var(--color-comment-tag-text); + background-color: var(--color-comment-tag); + text-indent: 0; + font-size: 75%; + line-height: 1; + font-weight: normal; + } + + .tsd-anchor { + position: relative; + top: -100px; + } + + .tsd-member { + position: relative; + } + .tsd-member .tsd-anchor + h3 { + display: flex; + align-items: center; + margin-top: 0; + margin-bottom: 0; + border-bottom: none; + } + + .tsd-navigation.settings { + margin: 0; + margin-bottom: 1rem; + } + .tsd-navigation > a, + .tsd-navigation .tsd-accordion-summary { + width: calc(100% - 0.25rem); + display: flex; + align-items: center; + } + .tsd-navigation a, + .tsd-navigation summary > span, + .tsd-page-navigation a { + display: flex; + width: calc(100% - 0.25rem); + align-items: center; + padding: 0.25rem; + color: var(--color-text); + text-decoration: none; + box-sizing: border-box; + } + .tsd-navigation a.current, + .tsd-page-navigation a.current { + background: var(--color-active-menu-item); + color: var(--color-contrast-text); + } + .tsd-navigation a:hover, + .tsd-page-navigation a:hover { + text-decoration: underline; + } + .tsd-navigation ul, + .tsd-page-navigation ul { + margin-top: 0; + margin-bottom: 0; + padding: 0; + list-style: none; + } + .tsd-navigation li, + .tsd-page-navigation li { + padding: 0; + max-width: 100%; + } + .tsd-navigation .tsd-nav-link { + display: none; + } + .tsd-nested-navigation { + margin-left: 3rem; + } + .tsd-nested-navigation > li > details { + margin-left: -1.5rem; + } + .tsd-small-nested-navigation { + margin-left: 1.5rem; + } + .tsd-small-nested-navigation > li > details { + margin-left: -1.5rem; + } + + .tsd-page-navigation-section > summary { + padding: 0.25rem; + } + .tsd-page-navigation-section > summary > svg { + margin-right: 0.25rem; + } + .tsd-page-navigation-section > div { + margin-left: 30px; + } + .tsd-page-navigation ul { + padding-left: 1.75rem; + } + + #tsd-sidebar-links a { + margin-top: 0; + margin-bottom: 0.5rem; + line-height: 1.25rem; + } + #tsd-sidebar-links a:last-of-type { + margin-bottom: 0; + } + + a.tsd-index-link { + padding: 0.25rem 0 !important; + font-size: 1rem; + line-height: 1.25rem; + display: inline-flex; + align-items: center; + color: var(--color-text); + } + .tsd-accordion-summary { + list-style-type: none; /* hide marker on non-safari */ + outline: none; /* broken on safari, so just hide it */ + display: flex; + align-items: center; + gap: 0.25rem; + box-sizing: border-box; + } + .tsd-accordion-summary::-webkit-details-marker { + display: none; /* hide marker on safari */ + } + .tsd-accordion-summary, + .tsd-accordion-summary a { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + + cursor: pointer; + } + .tsd-accordion-summary a { + width: calc(100% - 1.5rem); + } + .tsd-accordion-summary > * { + margin-top: 0; + margin-bottom: 0; + padding-top: 0; + padding-bottom: 0; + } + /* + * We need to be careful to target the arrow indicating whether the accordion + * is open, but not any other SVGs included in the details element. + */ + .tsd-accordion:not([open]) > .tsd-accordion-summary > svg:first-child { + transform: rotate(-90deg); + } + .tsd-index-content > :not(:first-child) { + margin-top: 0.75rem; + } + .tsd-index-summary { + margin-top: 1.5rem; + margin-bottom: 0.75rem; + display: flex; + align-content: center; + } + + .tsd-no-select { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + .tsd-kind-icon { + margin-right: 0.5rem; + width: 1.25rem; + height: 1.25rem; + min-width: 1.25rem; + min-height: 1.25rem; + } + .tsd-signature > .tsd-kind-icon { + margin-right: 0.8rem; + } + + .tsd-panel { + margin-bottom: 2.5rem; + } + .tsd-panel.tsd-member { + margin-bottom: 4rem; + } + .tsd-panel:empty { + display: none; + } + .tsd-panel > h1, + .tsd-panel > h2, + .tsd-panel > h3 { + margin: 1.5rem -1.5rem 0.75rem -1.5rem; + padding: 0 1.5rem 0.75rem 1.5rem; + } + .tsd-panel > h1.tsd-before-signature, + .tsd-panel > h2.tsd-before-signature, + .tsd-panel > h3.tsd-before-signature { + margin-bottom: 0; + border-bottom: none; + } + + .tsd-panel-group { + margin: 2rem 0; + } + .tsd-panel-group.tsd-index-group { + margin: 2rem 0; + } + .tsd-panel-group.tsd-index-group details { + margin: 2rem 0; + } + .tsd-panel-group > .tsd-accordion-summary { + margin-bottom: 1rem; + } + + #tsd-search[open] { + animation: fade-in var(--modal-animation-duration) ease-out forwards; + } + #tsd-search[open].closing { + animation-name: fade-out; + } + + /* Avoid setting `display` on closed dialog */ + #tsd-search[open] { + display: flex; + flex-direction: column; + padding: 1rem; + width: 32rem; + max-width: 90vw; + max-height: calc(100vh - env(keyboard-inset-height, 0px) - 25vh); + /* Anchor dialog to top */ + margin-top: 10vh; + border-radius: 6px; + will-change: max-height; + } + #tsd-search-input { + box-sizing: border-box; + width: 100%; + padding: 0 0.625rem; /* 10px */ + outline: 0; + border: 2px solid var(--color-accent); + background-color: transparent; + color: var(--color-text); + border-radius: 4px; + height: 2.5rem; + flex: 0 0 auto; + font-size: 0.875rem; + transition: border-color 0.2s, background-color 0.2s; + } + #tsd-search-input:focus-visible { + background-color: var(--color-background-active); + border-color: transparent; + color: var(--color-contrast-text); + } + #tsd-search-input::placeholder { + color: inherit; + opacity: 0.8; + } + #tsd-search-results { + margin: 0; + padding: 0; + list-style: none; + flex: 1 1 auto; + display: flex; + flex-direction: column; + overflow-y: auto; + } + #tsd-search-results:not(:empty) { + margin-top: 0.5rem; + } + #tsd-search-results > li { + background-color: var(--color-background); + line-height: 1.5; + box-sizing: border-box; + border-radius: 4px; + } + #tsd-search-results > li:nth-child(even) { + background-color: var(--color-background-secondary); + } + #tsd-search-results > li:is(:hover, [aria-selected="true"]) { + background-color: var(--color-background-active); + color: var(--color-contrast-text); + } + /* It's important that this takes full size of parent `li`, to capture a click on `li` */ + #tsd-search-results > li > a { + display: flex; + align-items: center; + padding: 0.5rem 0.25rem; + box-sizing: border-box; + width: 100%; + } + #tsd-search-results > li > a > .text { + flex: 1 1 auto; + min-width: 0; + overflow-wrap: anywhere; + } + #tsd-search-results > li > a .parent { + color: var(--color-text-aside); + } + #tsd-search-results > li > a mark { + color: inherit; + background-color: inherit; + font-weight: bold; + } + #tsd-search-status { + flex: 1; + display: grid; + place-content: center; + text-align: center; + overflow-wrap: anywhere; + } + #tsd-search-status:not(:empty) { + min-height: 6rem; + } + + .tsd-signature { + margin: 0 0 1rem 0; + padding: 1rem 0.5rem; + border: 1px solid var(--color-accent); + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + font-size: 14px; + overflow-x: auto; + } + + .tsd-signature-keyword { + color: var(--color-ts-keyword); + font-weight: normal; + } + + .tsd-signature-symbol { + color: var(--color-text-aside); + font-weight: normal; + } + + .tsd-signature-type { + font-style: italic; + font-weight: normal; + } + + .tsd-signatures { + padding: 0; + margin: 0 0 1em 0; + list-style-type: none; + } + .tsd-signatures .tsd-signature { + margin: 0; + border-color: var(--color-accent); + border-width: 1px 0; + transition: background-color 0.1s; + } + .tsd-signatures .tsd-index-signature:not(:last-child) { + margin-bottom: 1em; + } + .tsd-signatures .tsd-index-signature .tsd-signature { + border-width: 1px; + } + .tsd-description .tsd-signatures .tsd-signature { + border-width: 1px; + } + + ul.tsd-parameter-list, + ul.tsd-type-parameter-list { + list-style: square; + margin: 0; + padding-left: 20px; + } + ul.tsd-parameter-list > li.tsd-parameter-signature, + ul.tsd-type-parameter-list > li.tsd-parameter-signature { + list-style: none; + margin-left: -20px; + } + ul.tsd-parameter-list h5, + ul.tsd-type-parameter-list h5 { + font-size: 16px; + margin: 1em 0 0.5em 0; + } + .tsd-sources { + margin-top: 1rem; + font-size: 0.875em; + } + .tsd-sources a { + color: var(--color-text-aside); + text-decoration: underline; + } + .tsd-sources ul { + list-style: none; + padding: 0; + } + + .tsd-page-toolbar { + position: sticky; + z-index: 1; + top: 0; + left: 0; + width: 100%; + color: var(--color-text); + background: var(--color-background-secondary); + border-bottom: var(--dim-toolbar-border-bottom-width) + var(--color-accent) solid; + transition: transform 0.3s ease-in-out; + } + .tsd-page-toolbar a { + color: var(--color-text); + } + .tsd-toolbar-contents { + display: flex; + align-items: center; + height: var(--dim-toolbar-contents-height); + margin: 0 auto; + } + .tsd-toolbar-contents > .title { + font-weight: bold; + margin-right: auto; + } + #tsd-toolbar-links { + display: flex; + align-items: center; + gap: 1.5rem; + margin-right: 1rem; + } + + .tsd-widget { + box-sizing: border-box; + display: inline-block; + opacity: 0.8; + height: 2.5rem; + width: 2.5rem; + transition: opacity 0.1s, background-color 0.1s; + text-align: center; + cursor: pointer; + border: none; + background-color: transparent; + } + .tsd-widget:hover { + opacity: 0.9; + } + .tsd-widget:active { + opacity: 1; + background-color: var(--color-accent); + } + #tsd-toolbar-menu-trigger { + display: none; + } + + .tsd-member-summary-name { + display: inline-flex; + align-items: center; + padding: 0.25rem; + text-decoration: none; + } + + .tsd-anchor-icon { + display: inline-flex; + align-items: center; + margin-left: 0.5rem; + color: var(--color-text); + vertical-align: middle; + } + + .tsd-anchor-icon svg { + width: 1em; + height: 1em; + visibility: hidden; + } + + .tsd-member-summary-name:hover > .tsd-anchor-icon svg, + .tsd-anchor-link:hover > .tsd-anchor-icon svg, + .tsd-anchor-icon:focus-visible svg { + visibility: visible; + } + + .deprecated { + text-decoration: line-through !important; + } + + .warning { + padding: 1rem; + color: var(--color-warning-text); + background: var(--color-background-warning); + } + + .tsd-kind-project { + color: var(--color-ts-project); + } + .tsd-kind-module { + color: var(--color-ts-module); + } + .tsd-kind-namespace { + color: var(--color-ts-namespace); + } + .tsd-kind-enum { + color: var(--color-ts-enum); + } + .tsd-kind-enum-member { + color: var(--color-ts-enum-member); + } + .tsd-kind-variable { + color: var(--color-ts-variable); + } + .tsd-kind-function { + color: var(--color-ts-function); + } + .tsd-kind-class { + color: var(--color-ts-class); + } + .tsd-kind-interface { + color: var(--color-ts-interface); + } + .tsd-kind-constructor { + color: var(--color-ts-constructor); + } + .tsd-kind-property { + color: var(--color-ts-property); + } + .tsd-kind-method { + color: var(--color-ts-method); + } + .tsd-kind-reference { + color: var(--color-ts-reference); + } + .tsd-kind-call-signature { + color: var(--color-ts-call-signature); + } + .tsd-kind-index-signature { + color: var(--color-ts-index-signature); + } + .tsd-kind-constructor-signature { + color: var(--color-ts-constructor-signature); + } + .tsd-kind-parameter { + color: var(--color-ts-parameter); + } + .tsd-kind-type-parameter { + color: var(--color-ts-type-parameter); + } + .tsd-kind-accessor { + color: var(--color-ts-accessor); + } + .tsd-kind-get-signature { + color: var(--color-ts-get-signature); + } + .tsd-kind-set-signature { + color: var(--color-ts-set-signature); + } + .tsd-kind-type-alias { + color: var(--color-ts-type-alias); + } + + /* if we have a kind icon, don't color the text by kind */ + .tsd-kind-icon ~ span { + color: var(--color-text); + } + + /* mobile */ + @media (max-width: 769px) { + #tsd-toolbar-menu-trigger { + display: inline-block; + /* temporary fix to vertically align, for compatibility */ + line-height: 2.5; + } + #tsd-toolbar-links { + display: none; + } + + .container-main { + display: flex; + } + .col-content { + float: none; + max-width: 100%; + width: 100%; + } + .col-sidebar { + position: fixed !important; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + z-index: 1024; + top: 0 !important; + bottom: 0 !important; + left: auto !important; + right: 0 !important; + padding: 1.5rem 1.5rem 0 0; + width: 75vw; + visibility: hidden; + background-color: var(--color-background); + transform: translate(100%, 0); + } + .col-sidebar > *:last-child { + padding-bottom: 20px; + } + .overlay { + content: ""; + display: block; + position: fixed; + z-index: 1023; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.75); + visibility: hidden; + } + + .to-has-menu .overlay { + animation: fade-in 0.4s; + } + + .to-has-menu .col-sidebar { + animation: pop-in-from-right 0.4s; + } + + .from-has-menu .overlay { + animation: fade-out 0.4s; + } + + .from-has-menu .col-sidebar { + animation: pop-out-to-right 0.4s; + } + + .has-menu body { + overflow: hidden; + } + .has-menu .overlay { + visibility: visible; + } + .has-menu .col-sidebar { + visibility: visible; + transform: translate(0, 0); + display: flex; + flex-direction: column; + gap: 1.5rem; + max-height: 100vh; + padding: 1rem 2rem; + } + .has-menu .tsd-navigation { + max-height: 100%; + } + .tsd-navigation .tsd-nav-link { + display: flex; + } + } + + /* one sidebar */ + @media (min-width: 770px) { + .container-main { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); + grid-template-areas: "sidebar content"; + --dim-container-main-margin-y: 2rem; + } + + .tsd-breadcrumb { + margin-top: 0; + } + + .col-sidebar { + grid-area: sidebar; + } + .col-content { + grid-area: content; + padding: 0 1rem; + } + } + @media (min-width: 770px) and (max-width: 1399px) { + .col-sidebar { + max-height: calc( + 100vh - var(--dim-header-height) - var(--dim-footer-height) - + 2 * var(--dim-container-main-margin-y) + ); + overflow: auto; + position: sticky; + top: calc( + var(--dim-header-height) + var(--dim-container-main-margin-y) + ); + } + .site-menu { + margin-top: 1rem; + } + } + + /* two sidebars */ + @media (min-width: 1200px) { + .container-main { + grid-template-columns: + minmax(0, 1fr) minmax(0, 2.5fr) minmax( + 0, + 20rem + ); + grid-template-areas: "sidebar content toc"; + } + + .col-sidebar { + display: contents; + } + + .page-menu { + grid-area: toc; + padding-left: 1rem; + } + .site-menu { + grid-area: sidebar; + } + + .site-menu { + margin-top: 0rem; + } + + .page-menu, + .site-menu { + max-height: calc( + 100vh - var(--dim-header-height) - var(--dim-footer-height) - + 2 * var(--dim-container-main-margin-y) + ); + overflow: auto; + position: sticky; + top: calc( + var(--dim-header-height) + var(--dim-container-main-margin-y) + ); + } + } +} diff --git a/docs/typedoc/core.html b/docs/typedoc/core.html new file mode 100644 index 00000000..c67df8dc --- /dev/null +++ b/docs/typedoc/core.html @@ -0,0 +1 @@ +core | whiteflag-js
whiteflag-js
    Preparing search index...

    Module core

    Enumerations

    WfErrorCode → WfErrorCode
    WfMsgType → WfMsgType
    WfVersion → WfVersion

    Classes

    WfCoreMessage → WfCoreMessage
    WfProtocolError → WfProtocolError

    Functions

    decodeField → decodeField
    decryptMessage → decryptMessage
    encodeField → encodeField
    encryptMessage → encryptMessage
    isValidMessage → isValidMessage
    isValidValue → isValidValue
    validateMessage → validateMessage
    diff --git a/docs/typedoc/core/lib/codec.html b/docs/typedoc/core/lib/codec.html new file mode 100644 index 00000000..5c6186ca --- /dev/null +++ b/docs/typedoc/core/lib/codec.html @@ -0,0 +1 @@ +core/lib/codec | whiteflag-js
    whiteflag-js
      Preparing search index...

      Module core/lib/codec

      Enumerations

      WfCodec

      Functions

      decodeField
      encodeField
      isValidValue
      diff --git a/docs/typedoc/core/lib/codec/WfCodec.html b/docs/typedoc/core/lib/codec/WfCodec.html new file mode 100644 index 00000000..10bb3790 --- /dev/null +++ b/docs/typedoc/core/lib/codec/WfCodec.html @@ -0,0 +1,22 @@ +WfCodec | whiteflag-js
      whiteflag-js
        Preparing search index...

        Enumeration WfCodec

        Whiteflag field encodings, defining the encoding of Whiteflag +message fields as defined by the Whiteflag specification +WfCodec

        +

        v1-draft.7

        +

        4.1.2 Message Encoding

        +
        Index

        Enumeration Members

        Enumeration Members

        BIN: "binary"

        Binary field

        +
        DATETIME: "datetime"

        Datetime field

        +
        DEC: "decimal"

        Decimal field

        +
        DURATION: "duration"

        Duration field

        +
        HEX: "hexadecimal"

        HExadecimal field

        +
        LAT: "latitude"

        Latitude field

        +
        LONG: "longitude"

        Longitude field

        +
        UTF8: "utf-8"

        UTF-8 / ASCII text field

        +
        diff --git a/docs/typedoc/core/lib/codec/decodeField.html b/docs/typedoc/core/lib/codec/decodeField.html new file mode 100644 index 00000000..9ca8fa14 --- /dev/null +++ b/docs/typedoc/core/lib/codec/decodeField.html @@ -0,0 +1,9 @@ +decodeField | whiteflag-js
        whiteflag-js
          Preparing search index...

          Function decodeField

          • Function

            Decodes a Whiteflag message field +decodeField

            +

            Parameters

            • buffer: BinaryBuffer

              a binary buffer with the encoded field

              +
            • codec: WfCodec

              the message field encoding: 'utf-8', 'bin', 'dec', 'hex', 'datetime', 'duration', 'lat', 'long'

              +
            • version: v1 = WfVersion.v1

              the version of the Whiteflag specification

              +

            Returns string

            a string with the decoded field value

            +

            v1-draft.7

            +

            4.1.2 Message Encoding, 4.1.3 Message Compression

            +
          diff --git a/docs/typedoc/core/lib/codec/encodeField.html b/docs/typedoc/core/lib/codec/encodeField.html new file mode 100644 index 00000000..00904962 --- /dev/null +++ b/docs/typedoc/core/lib/codec/encodeField.html @@ -0,0 +1,9 @@ +encodeField | whiteflag-js
          whiteflag-js
            Preparing search index...

            Function encodeField

            • Function

              Encodes a Whiteflag message field +encodeField

              +

              Parameters

              • value: string

                the message field value

                +
              • codec: WfCodec

                the message field encoding: 'utf-8', 'bin', 'dec', 'hex', 'datetime', 'duration', 'lat', 'long'

                +
              • version: v1 = WfVersion.v1

                the version of the Whiteflag specification

                +

              Returns BinaryBuffer

              a binary buffer with the compressed encoded field

              +

              v1-draft.7

              +

              4.1.2 Message Encoding, 4.1.3 Message Compression

              +
            diff --git a/docs/typedoc/core/lib/codec/isValidValue.html b/docs/typedoc/core/lib/codec/isValidValue.html new file mode 100644 index 00000000..c6f6748f --- /dev/null +++ b/docs/typedoc/core/lib/codec/isValidValue.html @@ -0,0 +1,7 @@ +isValidValue | whiteflag-js
            whiteflag-js
              Preparing search index...

              Function isValidValue

              • Function

                Checks if the field value is valid +isValidValue

                +

                Parameters

                • value: string

                  the field value

                  +
                • codec: WfCodec

                  the field encoding

                  +
                • version: v1 = WfVersion.v1

                  the Whiteflag protocol version

                  +

                Returns boolean

                true if valid, else false

                +
              diff --git a/docs/typedoc/core/lib/errors.html b/docs/typedoc/core/lib/errors.html new file mode 100644 index 00000000..da71f436 --- /dev/null +++ b/docs/typedoc/core/lib/errors.html @@ -0,0 +1 @@ +core/lib/errors | whiteflag-js
              whiteflag-js
                Preparing search index...

                Module core/lib/errors

                Enumerations

                WfErrorCode

                Classes

                WfProtocolError

                Functions

                catchedError
                diff --git a/docs/typedoc/core/lib/errors/WfErrorCode.html b/docs/typedoc/core/lib/errors/WfErrorCode.html new file mode 100644 index 00000000..1fdb4030 --- /dev/null +++ b/docs/typedoc/core/lib/errors/WfErrorCode.html @@ -0,0 +1,17 @@ +WfErrorCode | whiteflag-js
                whiteflag-js
                  Preparing search index...

                  Enumeration WfErrorCode

                  Defines Whiteflag protocol errors +WfErrorCode

                  +
                  Index

                  Enumeration Members

                  AUTHENTICATION: "WF_AUTH_ERROR"

                  Whiteflag message authentication error

                  +
                  ENCRYPTION: "WF_ENCRYPTION_ERROR"

                  Whiteflag encryption error

                  +
                  FORMAT: "WF_FORMAT_ERROR"

                  Whiteflag message format error

                  +
                  METAHEADER: "WF_METAHEADER_ERROR"

                  Incorrect or missingWhiteflag message meta data

                  +
                  PROTOCOL: "WF_PROTOCOL_ERROR"

                  Generic Whiteflag protocol error

                  +
                  REFERENCE: "WF_REFERENCE_ERROR"

                  Whiteflag message reference error

                  +
                  SIGNATURE: "WF_SIGN_ERROR"

                  Whiteflag signature error

                  +
                  diff --git a/docs/typedoc/core/lib/errors/WfProtocolError.html b/docs/typedoc/core/lib/errors/WfProtocolError.html new file mode 100644 index 00000000..cb0e87ee --- /dev/null +++ b/docs/typedoc/core/lib/errors/WfProtocolError.html @@ -0,0 +1,43 @@ +WfProtocolError | whiteflag-js
                  whiteflag-js
                    Preparing search index...

                    Class WfProtocolError

                    Error class for Whiteflag protocol and message errors +ProtocolError

                    +

                    Hierarchy

                    • Error
                      • WfProtocolError
                    Index

                    Constructors

                    • Constructor for protocol errors

                      +

                      Parameters

                      • message: string

                        a human readable error message

                        +
                      • causes: any

                        underlying errors causing this error

                        +
                      • code: WfErrorCode = WfErrorCode.PROTOCOL

                        constant identifying the error

                        +

                      Returns WfProtocolError

                    Properties

                    cause?: unknown
                    causes: string[]

                    Underlying causes of the error

                    +
                    code: string

                    The Whiteflag protocol error code

                    +
                    message: string
                    name: string
                    stack?: string
                    stackTraceLimit: number

                    The Error.stackTraceLimit property specifies the number of stack frames +collected by a stack trace (whether generated by new Error().stack or +Error.captureStackTrace(obj)).

                    +

                    The default value is 10 but may be set to any valid JavaScript number. Changes +will affect any stack trace captured after the value has been changed.

                    +

                    If set to a non-number value, or set to a negative number, stack traces will +not capture any frames.

                    +

                    Methods

                    • Creates a .stack property on targetObject, which when accessed returns +a string representing the location in the code at which +Error.captureStackTrace() was called.

                      +
                      const myObject = {};
                      Error.captureStackTrace(myObject);
                      myObject.stack; // Similar to `new Error().stack` +
                      + +

                      The first line of the trace will be prefixed with +${myObject.name}: ${myObject.message}.

                      +

                      The optional constructorOpt argument accepts a function. If given, all frames +above constructorOpt, including constructorOpt, will be omitted from the +generated stack trace.

                      +

                      The constructorOpt argument is useful for hiding implementation +details of error generation from the user. For instance:

                      +
                      function a() {
                      b();
                      }

                      function b() {
                      c();
                      }

                      function c() {
                      // Create an error without stack trace to avoid calculating the stack trace twice.
                      const { stackTraceLimit } = Error;
                      Error.stackTraceLimit = 0;
                      const error = new Error();
                      Error.stackTraceLimit = stackTraceLimit;

                      // Capture the stack trace above function b
                      Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
                      throw error;
                      }

                      a(); +
                      + +

                      Parameters

                      • targetObject: object
                      • OptionalconstructorOpt: Function

                      Returns void

                    diff --git a/docs/typedoc/core/lib/errors/catchedError.html b/docs/typedoc/core/lib/errors/catchedError.html new file mode 100644 index 00000000..54220f55 --- /dev/null +++ b/docs/typedoc/core/lib/errors/catchedError.html @@ -0,0 +1,5 @@ +catchedError | whiteflag-js
                    whiteflag-js
                      Preparing search index...

                      Function catchedError

                      • Processes a catched error in a type safe manner

                        +

                        Parameters

                        • msg: string = 'Unspecified error'

                          a generic message to use if no specific error message

                          +
                        • err: any = ...

                          the error to handle

                          +

                        Returns WfProtocolError

                        a new error object

                        +
                      diff --git a/docs/typedoc/core/lib/message.html b/docs/typedoc/core/lib/message.html new file mode 100644 index 00000000..c7848a7d --- /dev/null +++ b/docs/typedoc/core/lib/message.html @@ -0,0 +1 @@ +core/lib/message | whiteflag-js
                      whiteflag-js
                        Preparing search index...
                        diff --git a/docs/typedoc/core/lib/message/WfCoreMessage.html b/docs/typedoc/core/lib/message/WfCoreMessage.html new file mode 100644 index 00000000..b245591d --- /dev/null +++ b/docs/typedoc/core/lib/message/WfCoreMessage.html @@ -0,0 +1,95 @@ +WfCoreMessage | whiteflag-js
                        whiteflag-js
                          Preparing search index...

                          A core Whiteflag message as defined by the Whiteflag specification +WfCoreMessage

                          +

                          v1-draft.7

                          +

                          4 Message Format

                          +

                          Ths class represents a core Whiteflag message as +defined by the Whiteflag specification. It has a message header and +a message body which contain the message fields as specified for the +message type. It performs the encoding/encryption and decoding/decryption +to and from binary messages. Since the processing of Whiteflag messges +in accordance with the protocol requires additional metadata, the extrended +WfMessage class of the @whitelag/protol package should normallly be +used instead of this class.

                          +

                          Hierarchy (View Summary)

                          Index

                          Constructors

                          Methods

                          • Function

                            Encodes the message, making the contents final +encode

                            +

                            Parameters

                            • Optionalikm: Uint8Array<ArrayBufferLike>

                              the input key material to derive the encryption key, if the message is to be encrypted

                              +
                            • Optionaladdress: Uint8Array<ArrayBufferLike>

                              the binary encoded originator address, if the message is to be encrypted

                              +
                            • Optionaliv: Uint8Array<ArrayBufferLike>

                              the initialisation vector, if required for the encryption method

                              +

                            Returns Promise<WfCoreMessage>

                            this Whitedlag message object with the encoded message

                            +
                          • Function

                            Returns the value of the specified message field +get

                            +

                            Parameters

                            • fieldName: string

                              the name of the message field

                              +

                            Returns string | null

                            the value of the message field

                            +
                          • Function

                            Indicates if the message has already been encoded +isEncoded

                            +

                            Returns boolean

                            true if message has been encoded, else false

                            +
                          • Function

                            Indicates if the message is valid, i.e. if all fields contain valid values +isValid

                            +

                            Returns boolean

                            true if message is valid, else false

                            +
                          • Function

                            Sets the value of the specified message field, if the message has not been encoded +set

                            +

                            Parameters

                            • fieldName: string

                              the name of the message field

                              +
                            • value: string

                              the value to set

                              +

                            Returns boolean

                            true if succesful, else false

                            +
                          • Function

                            Returns the Whiteflag message encoded as a hexadecimal string +toHex

                            +

                            Returns string

                            a hexadecimal string with the encoded message

                            +
                          • Function

                            Returns the Whiteflag message as a plain object +toObject

                            +

                            Returns Object

                            the message as a plain object

                            +
                          • Function

                            Returns the Whiteflag message as a string +toString

                            +

                            Returns string

                            a concatinated string of field values

                            +
                          • Function

                            Returns the encoded Whiteflag message as a UInt8array +toU8a

                            +

                            Returns Uint8Array

                            a UInt8array with the encoded message

                            +
                          • Function

                            Returns message validation errors +validate

                            +

                            Returns string[]

                            an array of validation errors

                            +
                          • Function

                            Creates new Whiteflag message from a binary buffer +fromBinary

                            +

                            Parameters

                            • message: BinaryBuffer

                              a binary buffer with the encoded message

                              +
                            • Optionalikm: Uint8Array<ArrayBufferLike>

                              the input key material to derive the encryption key, if the message is encrypted

                              +
                            • Optionaladdress: Uint8Array<ArrayBufferLike>

                              the binary encoded originator address, if the message is encrypted

                              +
                            • Optionaliv: Uint8Array<ArrayBufferLike>

                              the initialisation vector, if required for the encryption method

                              +

                            Returns Promise<WfCoreMessage>

                            a new Whiteflag message object with the decoded message

                            +
                          • Creates new Whiteflag message from a hexadecimal encoded string

                            +

                            Parameters

                            • message: string

                              atring with the hexadecimal encoded message

                              +
                            • Optionalikm: string

                              the hexadecimalinput key material to derive the encryption key, if the message is encrypted

                              +
                            • Optionaladdress: string

                              the hexadecimal encoded originator address, if the message is encrypted

                              +
                            • Optionaliv: string

                              the hexadecimal initialisation vector, if required for the encryption method

                              +

                            Returns Promise<WfCoreMessage>

                            a new Whiteflag message object with the decoded message

                            +
                          • Function

                            Creates new Whiteflag message from a plain object +fromObject

                            +

                            Parameters

                            • message: any

                              a plain JavaScript object with message header and body

                              +

                            Returns Promise<WfCoreMessage>

                            a new Whiteflag message object

                            +
                          • Creates new Whiteflag message from a binary encoded message

                            +

                            Parameters

                            • message: Uint8Array

                              a Uint8Array with the binary encoded message

                              +
                            • Optionalikm: Uint8Array<ArrayBufferLike>

                              the input key material to derive the encryption key, if the message is encrypted

                              +
                            • Optionaladdress: Uint8Array<ArrayBufferLike>

                              the binary encoded originator address, if the message is encrypted

                              +
                            • Optionaliv: Uint8Array<ArrayBufferLike>

                              the initialisation vector, if required for the encryption method

                              +

                            Returns Promise<WfCoreMessage>

                            a new Whiteflag message object with the decoded message

                            +
                          diff --git a/docs/typedoc/core/lib/message/WfMsgType.html b/docs/typedoc/core/lib/message/WfMsgType.html new file mode 100644 index 00000000..5517e1af --- /dev/null +++ b/docs/typedoc/core/lib/message/WfMsgType.html @@ -0,0 +1,30 @@ +WfMsgType | whiteflag-js
                          whiteflag-js
                            Preparing search index...

                            Enumeration WfMsgType

                            Whiteflag message types, defining the types of Whiteflag message +as specified by the Whiteflag standard +WfFieldType

                            +

                            v1-draft.7

                            +

                            2.4.2.1 Functional Messages, 2.4.2.2 Management Messages

                            +
                            Index

                            Enumeration Members

                            A +D +E +F +I +K +M +P +Q +R +S +T +

                            Enumeration Members

                            A: "A"

                            Authentication message

                            +
                            D: "D"

                            Protection sign

                            +
                            E: "E"

                            Emergency signal

                            +
                            F: "F"

                            Free text message

                            +
                            I: "I"

                            Infrstructure sign

                            +
                            K: "K"

                            Cryptographic support message

                            +
                            M: "M"

                            Mission signal

                            +
                            P: "P"

                            Protection sign

                            +
                            Q: "Q"

                            Request signal

                            +
                            R: "R"

                            Reference message

                            +
                            S: "S"

                            Status signal

                            +
                            T: "T"

                            Test message

                            +
                            diff --git a/docs/typedoc/core/lib/message/decryptMessage.html b/docs/typedoc/core/lib/message/decryptMessage.html new file mode 100644 index 00000000..60d97bbf --- /dev/null +++ b/docs/typedoc/core/lib/message/decryptMessage.html @@ -0,0 +1,10 @@ +decryptMessage | whiteflag-js
                            whiteflag-js
                              Preparing search index...

                              Function decryptMessage

                              • Function

                                Decrypts an encrypted binary message +decryptMessage

                                +

                                Parameters

                                • message: BinaryBuffer

                                  a binary buffer with the encrypted message

                                  +
                                • method: WfCryptoMethod

                                  the Whiteflag encryption method

                                  +
                                • ikm: Uint8Array

                                  the input key material to derive the encryption key

                                  +
                                • address: Uint8Array

                                  the binary encoded originator address

                                  +
                                • Optionaliv: Uint8Array<ArrayBufferLike>

                                  the initialisation vector, if required for the encryption method

                                  +
                                • version: v1 = WfVersion.v1

                                  the Whiteflag protocol version

                                  +

                                Returns Promise<BinaryBuffer>

                                the decrypted binary encoded message

                                +
                              diff --git a/docs/typedoc/core/lib/message/encryptMessage.html b/docs/typedoc/core/lib/message/encryptMessage.html new file mode 100644 index 00000000..c15ecf35 --- /dev/null +++ b/docs/typedoc/core/lib/message/encryptMessage.html @@ -0,0 +1,10 @@ +encryptMessage | whiteflag-js
                              whiteflag-js
                                Preparing search index...

                                Function encryptMessage

                                • Function

                                  Encrypts a binary encoded message +encryptMessage

                                  +

                                  Parameters

                                  • message: BinaryBuffer

                                    a binary buffer with the binary encoded message

                                    +
                                  • method: WfCryptoMethod

                                    the Whiteflag encryption method

                                    +
                                  • ikm: Uint8Array

                                    the input key material to derive the encryption key

                                    +
                                  • address: Uint8Array

                                    the binary encoded originator address

                                    +
                                  • Optionaliv: Uint8Array<ArrayBufferLike>

                                    the initialisation vector, if required for the encryption method

                                    +
                                  • version: v1 = WfVersion.v1

                                    the Whiteflag protocol version

                                    +

                                  Returns Promise<BinaryBuffer>

                                  the encrypted message

                                  +
                                diff --git a/docs/typedoc/core/lib/message/isValidMessage.html b/docs/typedoc/core/lib/message/isValidMessage.html new file mode 100644 index 00000000..64126d0d --- /dev/null +++ b/docs/typedoc/core/lib/message/isValidMessage.html @@ -0,0 +1,5 @@ +isValidMessage | whiteflag-js
                                whiteflag-js
                                  Preparing search index...

                                  Function isValidMessage

                                  • Function

                                    Checks if an object is a valid Whiteflag message +isValidMessage

                                    +

                                    Parameters

                                    • message: any

                                      the message object to validate

                                      +

                                    Returns boolean

                                    true if message is valid, else false

                                    +
                                  diff --git a/docs/typedoc/core/lib/message/validateMessage.html b/docs/typedoc/core/lib/message/validateMessage.html new file mode 100644 index 00000000..65ece762 --- /dev/null +++ b/docs/typedoc/core/lib/message/validateMessage.html @@ -0,0 +1,5 @@ +validateMessage | whiteflag-js
                                  whiteflag-js
                                    Preparing search index...

                                    Function validateMessage

                                    • Function

                                      Checks a message object for validation errors +validateMessage

                                      +

                                      Parameters

                                      • message: any

                                        the message object to validate

                                        +

                                      Returns string[]

                                      an array of validation errors

                                      +
                                    diff --git a/docs/typedoc/core/lib/versions.html b/docs/typedoc/core/lib/versions.html new file mode 100644 index 00000000..bda189e5 --- /dev/null +++ b/docs/typedoc/core/lib/versions.html @@ -0,0 +1 @@ +core/lib/versions | whiteflag-js
                                    whiteflag-js
                                      Preparing search index...

                                      Module core/lib/versions

                                      Enumerations

                                      WfVersion
                                      diff --git a/docs/typedoc/core/lib/versions/WfVersion.html b/docs/typedoc/core/lib/versions/WfVersion.html new file mode 100644 index 00000000..1c512390 --- /dev/null +++ b/docs/typedoc/core/lib/versions/WfVersion.html @@ -0,0 +1,5 @@ +WfVersion | whiteflag-js
                                      whiteflag-js
                                        Preparing search index...

                                        Enumeration WfVersion

                                        Defines Whiteflag versions +WfVersion

                                        +
                                        Index

                                        Enumeration Members

                                        v1 +

                                        Enumeration Members

                                        v1: "1"

                                        Whiteflag version 1

                                        +
                                        diff --git a/docs/typedoc/crypto.html b/docs/typedoc/crypto.html new file mode 100644 index 00000000..879bd76b --- /dev/null +++ b/docs/typedoc/crypto.html @@ -0,0 +1 @@ +crypto | whiteflag-js
                                        whiteflag-js
                                          Preparing search index...

                                          Module crypto

                                          Enumerations

                                          WfCryptoMethod → WfCryptoMethod

                                          Functions

                                          createAesKey → createAesKey
                                          createHmacKey → createHmacKey
                                          decrypt → decrypt
                                          deriveKey → deriveKey
                                          encrypt → encrypt
                                          hash → hash
                                          hkdf → hkdf
                                          hmac → hmac
                                          diff --git a/docs/typedoc/crypto/lib/cipher.html b/docs/typedoc/crypto/lib/cipher.html new file mode 100644 index 00000000..4d481138 --- /dev/null +++ b/docs/typedoc/crypto/lib/cipher.html @@ -0,0 +1 @@ +crypto/lib/cipher | whiteflag-js
                                          whiteflag-js
                                            Preparing search index...

                                            Module crypto/lib/cipher

                                            Enumerations

                                            WfCryptoMethod

                                            Functions

                                            decrypt
                                            deriveKey
                                            encrypt
                                            diff --git a/docs/typedoc/crypto/lib/cipher/WfCryptoMethod.html b/docs/typedoc/crypto/lib/cipher/WfCryptoMethod.html new file mode 100644 index 00000000..afc03dc6 --- /dev/null +++ b/docs/typedoc/crypto/lib/cipher/WfCryptoMethod.html @@ -0,0 +1,10 @@ +WfCryptoMethod | whiteflag-js
                                            whiteflag-js
                                              Preparing search index...

                                              Enumeration WfCryptoMethod

                                              Whiteflag encryption methods, defining the encryption methods +for Whiteflag messages as specified by the Whiteflag standard +WfCryptoMethod

                                              +

                                              v1-draft.7

                                              +

                                              5.2.4 Message Encryption

                                              +
                                              Index

                                              Enumeration Members

                                              Enumeration Members

                                              ECDH: "1"

                                              Whiteflag encryption method 1: negotiated key

                                              +
                                              PSK: "2"

                                              Whiteflag encryption method 2: pre-shared key

                                              +
                                              diff --git a/docs/typedoc/crypto/lib/cipher/decrypt.html b/docs/typedoc/crypto/lib/cipher/decrypt.html new file mode 100644 index 00000000..160be003 --- /dev/null +++ b/docs/typedoc/crypto/lib/cipher/decrypt.html @@ -0,0 +1,10 @@ +decrypt | whiteflag-js
                                              whiteflag-js
                                                Preparing search index...

                                                Function decrypt

                                                • Function

                                                  Decrypts a message based on the specified encryption method +decrypt

                                                  +

                                                  Parameters

                                                  • message: Uint8Array<ArrayBuffer>

                                                    the message to be decyrpted

                                                    +
                                                  • method: WfCryptoMethod

                                                    the Whiteflag encryption method

                                                    +
                                                  • key: CryptoKey

                                                    the encryption key

                                                    +
                                                  • Optionaliv: Uint8Array<ArrayBuffer>

                                                    the initialisation vector, if required for the method

                                                    +
                                                  • version: v1 = WfVersion.v1

                                                    the Whiteflag protocol version

                                                    +

                                                  Returns Promise<Uint8Array<ArrayBufferLike>>

                                                  v1-draft.7

                                                  +

                                                  5.2.4 Message Encryption

                                                  +
                                                diff --git a/docs/typedoc/crypto/lib/cipher/deriveKey.html b/docs/typedoc/crypto/lib/cipher/deriveKey.html new file mode 100644 index 00000000..914baa3b --- /dev/null +++ b/docs/typedoc/crypto/lib/cipher/deriveKey.html @@ -0,0 +1,10 @@ +deriveKey | whiteflag-js
                                                whiteflag-js
                                                  Preparing search index...

                                                  Function deriveKey

                                                  • Function

                                                    Derives the encryption key based on the Whiteflag encryption method +deriveKey

                                                    +

                                                    Parameters

                                                    • ikm: Uint8Array<ArrayBuffer>

                                                      the raw input key material

                                                      +
                                                    • method: WfCryptoMethod

                                                      the Whiteflag encryption method

                                                      +
                                                    • info: Uint8Array<ArrayBuffer>

                                                      information to bind the key, e.g. the blockchain address of the originator

                                                      +
                                                    • version: v1 = WfVersion.v1

                                                      the Whiteflag protocol version

                                                      +

                                                    Returns Promise<CryptoKey>

                                                    the encryption key

                                                    +

                                                    v1-draft.7

                                                    +

                                                    5.2.3 Encryption Key and Authentication Token Derivation

                                                    +
                                                  diff --git a/docs/typedoc/crypto/lib/cipher/encrypt.html b/docs/typedoc/crypto/lib/cipher/encrypt.html new file mode 100644 index 00000000..edc333c2 --- /dev/null +++ b/docs/typedoc/crypto/lib/cipher/encrypt.html @@ -0,0 +1,10 @@ +encrypt | whiteflag-js
                                                  whiteflag-js
                                                    Preparing search index...

                                                    Function encrypt

                                                    • Function

                                                      Encrypts a message based on the specified encryption method +encrypt

                                                      +

                                                      Parameters

                                                      • message: Uint8Array<ArrayBuffer>

                                                        the message to be encrypted

                                                        +
                                                      • method: WfCryptoMethod

                                                        the Whiteflag encryption method

                                                        +
                                                      • key: CryptoKey

                                                        the input key material for the encryption key

                                                        +
                                                      • Optionaliv: Uint8Array<ArrayBuffer>

                                                        the initialisation vector, if required for the method

                                                        +
                                                      • version: v1 = WfVersion.v1

                                                        the Whiteflag protocol version

                                                        +

                                                      Returns Promise<Uint8Array<ArrayBufferLike>>

                                                      v1-draft.7

                                                      +

                                                      5.2.4 Message Encryption

                                                      +
                                                    diff --git a/docs/typedoc/crypto/lib/common.html b/docs/typedoc/crypto/lib/common.html new file mode 100644 index 00000000..ff0f8ab9 --- /dev/null +++ b/docs/typedoc/crypto/lib/common.html @@ -0,0 +1 @@ +crypto/lib/common | whiteflag-js
                                                    whiteflag-js
                                                      Preparing search index...

                                                      Module crypto/lib/common

                                                      Functions

                                                      zeroise
                                                      diff --git a/docs/typedoc/crypto/lib/common/zeroise.html b/docs/typedoc/crypto/lib/common/zeroise.html new file mode 100644 index 00000000..34a072c7 --- /dev/null +++ b/docs/typedoc/crypto/lib/common/zeroise.html @@ -0,0 +1,5 @@ +zeroise | whiteflag-js
                                                      whiteflag-js
                                                        Preparing search index...

                                                        Function zeroise

                                                        • Function

                                                          Basic zeroisation function +zeroise

                                                          +

                                                          Parameters

                                                          • u8array: Uint8Array

                                                            typed array to zeroise

                                                            +

                                                          Returns Uint8Array

                                                          the zeroised typed array

                                                          +
                                                        diff --git a/docs/typedoc/crypto/lib/hash.html b/docs/typedoc/crypto/lib/hash.html new file mode 100644 index 00000000..850d93a0 --- /dev/null +++ b/docs/typedoc/crypto/lib/hash.html @@ -0,0 +1 @@ +crypto/lib/hash | whiteflag-js
                                                        whiteflag-js
                                                          Preparing search index...

                                                          Module crypto/lib/hash

                                                          Functions

                                                          hash
                                                          hkdf
                                                          hmac
                                                          diff --git a/docs/typedoc/crypto/lib/hash/hash.html b/docs/typedoc/crypto/lib/hash/hash.html new file mode 100644 index 00000000..cd278e21 --- /dev/null +++ b/docs/typedoc/crypto/lib/hash/hash.html @@ -0,0 +1,7 @@ +hash | whiteflag-js
                                                          whiteflag-js
                                                            Preparing search index...

                                                            Function hash

                                                            • Function

                                                              Basic hashing function +hash

                                                              +

                                                              Parameters

                                                              • data: Uint8Array<ArrayBuffer>

                                                                data to hash

                                                                +
                                                              • length: number = HASHLEN

                                                                the required output length in octets; default is 32

                                                                +
                                                              • algorithm: string = HASHALG

                                                                the hash algorithm to be used; default is SHA-256

                                                                +

                                                              Returns Promise<Uint8Array<ArrayBuffer>>

                                                              the hash value

                                                              +
                                                            diff --git a/docs/typedoc/crypto/lib/hash/hkdf.html b/docs/typedoc/crypto/lib/hash/hkdf.html new file mode 100644 index 00000000..8e855feb --- /dev/null +++ b/docs/typedoc/crypto/lib/hash/hkdf.html @@ -0,0 +1,8 @@ +hkdf | whiteflag-js
                                                            whiteflag-js
                                                              Preparing search index...

                                                              Function hkdf

                                                              • Function

                                                                Hash-based Key Derivation Function using SHA-256 i.a.w. RFC 5869 +hkdf

                                                                +

                                                                Parameters

                                                                • ikm: Uint8Array<ArrayBuffer>

                                                                  input key material

                                                                  +
                                                                • salt: Uint8Array<ArrayBuffer>

                                                                  salt

                                                                  +
                                                                • info: Uint8Array<ArrayBuffer>

                                                                  info

                                                                  +
                                                                • keylen: number

                                                                  output key length in octets

                                                                  +

                                                                Returns Promise<Uint8Array<ArrayBuffer>>

                                                                generated key

                                                                +
                                                              diff --git a/docs/typedoc/crypto/lib/hash/hmac.html b/docs/typedoc/crypto/lib/hash/hmac.html new file mode 100644 index 00000000..d5dfd859 --- /dev/null +++ b/docs/typedoc/crypto/lib/hash/hmac.html @@ -0,0 +1,7 @@ +hmac | whiteflag-js
                                                              whiteflag-js
                                                                Preparing search index...

                                                                Function hmac

                                                                • Function

                                                                  Hash-Based Message Authentication Code function +hmac

                                                                  +

                                                                  Parameters

                                                                  • rawKey: Uint8Array<ArrayBuffer>

                                                                    the raw HMAC key

                                                                    +
                                                                  • message: Uint8Array<ArrayBuffer>

                                                                    the message to authenticate

                                                                    +
                                                                  • algorithm: string = HASHALG

                                                                    the hash algorithm to be used; default is SHA-256

                                                                    +

                                                                  Returns Promise<Uint8Array<ArrayBuffer>>

                                                                  the message authentication code

                                                                  +
                                                                diff --git a/docs/typedoc/crypto/lib/keys.html b/docs/typedoc/crypto/lib/keys.html new file mode 100644 index 00000000..8c82f40c --- /dev/null +++ b/docs/typedoc/crypto/lib/keys.html @@ -0,0 +1 @@ +crypto/lib/keys | whiteflag-js
                                                                whiteflag-js
                                                                  Preparing search index...

                                                                  Module crypto/lib/keys

                                                                  Functions

                                                                  createAesKey
                                                                  createHmacKey
                                                                  diff --git a/docs/typedoc/crypto/lib/keys/createAesKey.html b/docs/typedoc/crypto/lib/keys/createAesKey.html new file mode 100644 index 00000000..ea398995 --- /dev/null +++ b/docs/typedoc/crypto/lib/keys/createAesKey.html @@ -0,0 +1,6 @@ +createAesKey | whiteflag-js
                                                                  whiteflag-js
                                                                    Preparing search index...

                                                                    Function createAesKey

                                                                    • Function

                                                                      Creates an AES encryption and decryption key +createAesKey

                                                                      +

                                                                      Parameters

                                                                      • rawKey: Uint8Array<ArrayBuffer>

                                                                        the raw key

                                                                        +
                                                                      • algorithm: string = DEFAULT_ENCRYPTALG

                                                                        the AES mode to use the key for, default is CTR mode

                                                                        +

                                                                      Returns Promise<CryptoKey>

                                                                      the AES enrcyption key

                                                                      +
                                                                    diff --git a/docs/typedoc/crypto/lib/keys/createHmacKey.html b/docs/typedoc/crypto/lib/keys/createHmacKey.html new file mode 100644 index 00000000..de62255d --- /dev/null +++ b/docs/typedoc/crypto/lib/keys/createHmacKey.html @@ -0,0 +1,6 @@ +createHmacKey | whiteflag-js
                                                                    whiteflag-js
                                                                      Preparing search index...

                                                                      Function createHmacKey

                                                                      • Function

                                                                        Creates an HMAC signing key +createHmacKey

                                                                        +

                                                                        Parameters

                                                                        • rawKey: Uint8Array<ArrayBuffer>

                                                                          the raw key

                                                                          +
                                                                        • algorithm: string = DEFAULT_HASHALG

                                                                          the hashing algorithm, default is SHA-256

                                                                          +

                                                                        Returns Promise<CryptoKey>

                                                                        the HMAC signing key

                                                                        +
                                                                      diff --git a/docs/typedoc/hierarchy.html b/docs/typedoc/hierarchy.html new file mode 100644 index 00000000..97010ea4 --- /dev/null +++ b/docs/typedoc/hierarchy.html @@ -0,0 +1 @@ +whiteflag-js
                                                                      whiteflag-js
                                                                        Preparing search index...

                                                                        whiteflag-js

                                                                        Hierarchy Summary

                                                                        diff --git a/docs/typedoc/index.html b/docs/typedoc/index.html new file mode 100644 index 00000000..df395d22 --- /dev/null +++ b/docs/typedoc/index.html @@ -0,0 +1,37 @@ +whiteflag-js
                                                                        whiteflag-js
                                                                          Preparing search index...

                                                                          whiteflag-js

                                                                          Whiteflag JavaScript Library

                                                                          Whiteflag is a fully neutral and +secure communications protocol based on blockchain technology. It enables +near real-time communication in armed conflicts and disasters to exchange +early warning and status information to create shared situational awareness. +The Whiteflag Protocol specification +is an open standard.

                                                                          +

                                                                          The Whiteflag JavaScript Library (WFJSL) will be the reference implementation +of the Whiteflag Protocol written in TypeScript, +and compiled to JavaScript, +to support the development of Whiteflag-enabled applications in JavaScript.

                                                                          +

                                                                          The WFJSL is currently in development in order to separate the Whiteflag +core protocol code from the current reference implementation, i.e. the +Whiteflag API. This makes the code +better to maintain and test in support of future Whiteflag protocol +development, while ensuring reusability for other implementations.

                                                                          +

                                                                          The WFJSL is to include all Whiteflag protocol features, but with minimal +dependencies. Therefore, the library is independent of any specific +blockchain, database solution, user interface, etc.

                                                                          +

                                                                          Version 1 of the WFJSL corresponds with version 1 of the protocol, and +more specifically with v1-draft.7 of the standard. The planned WFJSL +functionality and corresponding 1.x subversions are defined as +milestones.

                                                                          +

                                                                          Detailed documentation of the WFJSL programming interface is available +at js.whiteflagprotocol.org. +The documentation is also found in this repository in the docs/ directory.

                                                                          +

                                                                          Testing of the software is done with the Mocha +test framework. To do a full test and run all the test scripts, use the +following NPM command in the project root:

                                                                          +
                                                                          npm test
                                                                          +
                                                                          + +

                                                                          The WFJSL software is dedicated to the public domain under the +Creative Commons CC0-1.0 Universal Public Domain Dedication +statement. See LICENSE.md for details.

                                                                          +

                                                                          The library may require third party software packages, which are not part of +this distribution and may be licensed differently.

                                                                          +
                                                                          diff --git a/docs/typedoc/main.html b/docs/typedoc/main.html new file mode 100644 index 00000000..26673264 --- /dev/null +++ b/docs/typedoc/main.html @@ -0,0 +1 @@ +main | whiteflag-js
                                                                          whiteflag-js
                                                                            Preparing search index...

                                                                            Module main

                                                                            Enumerations

                                                                            WfVersion → WfVersion

                                                                            Classes

                                                                            WfMessage → WfMessage

                                                                            Interfaces

                                                                            WfMetaHeader → WfMetaHeader
                                                                            diff --git a/docs/typedoc/main/lib/message.html b/docs/typedoc/main/lib/message.html new file mode 100644 index 00000000..dedc9151 --- /dev/null +++ b/docs/typedoc/main/lib/message.html @@ -0,0 +1 @@ +main/lib/message | whiteflag-js
                                                                            whiteflag-js
                                                                              Preparing search index...

                                                                              Module main/lib/message

                                                                              Classes

                                                                              WfMessage

                                                                              Interfaces

                                                                              WfMetaHeader
                                                                              diff --git a/docs/typedoc/main/lib/message/WfMessage.html b/docs/typedoc/main/lib/message/WfMessage.html new file mode 100644 index 00000000..d134facb --- /dev/null +++ b/docs/typedoc/main/lib/message/WfMessage.html @@ -0,0 +1,114 @@ +WfMessage | whiteflag-js
                                                                              whiteflag-js
                                                                                Preparing search index...

                                                                                A Whiteflag message as defined by the Whiteflag specification +WfMessage

                                                                                +

                                                                                v1-draft.7

                                                                                +

                                                                                4 Message Format

                                                                                +

                                                                                This class extends the core Whiteflag message class by +adding metadata to the message, additional data conversions (such as to and +from JSON), and specific Whiteflag protocol features. This allows the class +to be used and integrated in larger functional applications in accordance +with the Whiteflag specification.

                                                                                +

                                                                                Hierarchy (View Summary)

                                                                                Index

                                                                                Constructors

                                                                                Properties

                                                                                meta: WfMetaHeader = {}

                                                                                The message metadata required for processing the message

                                                                                +

                                                                                Methods

                                                                                • Function

                                                                                  Encodes the message, making the contents final +encode

                                                                                  +

                                                                                  Parameters

                                                                                  • Optionalikm: Uint8Array<ArrayBufferLike>

                                                                                    the input key material to derive the encryption key, if the message is to be encrypted

                                                                                    +
                                                                                  • Optionaladdress: Uint8Array<ArrayBufferLike>

                                                                                    the binary encoded originator address, if the message is to be encrypted

                                                                                    +
                                                                                  • Optionaliv: Uint8Array<ArrayBufferLike>

                                                                                    the initialisation vector, if required for the encryption method

                                                                                    +

                                                                                  Returns Promise<WfCoreMessage>

                                                                                  this Whitedlag message object with the encoded message

                                                                                  +
                                                                                • Function

                                                                                  Returns the value of the specified message field +get

                                                                                  +

                                                                                  Parameters

                                                                                  • fieldName: string

                                                                                    the name of the message field

                                                                                    +

                                                                                  Returns string | null

                                                                                  the value of the message field

                                                                                  +
                                                                                • Function

                                                                                  Returns the value of the metaheader field +getMeta

                                                                                  +

                                                                                  Parameters

                                                                                  • fieldName: string

                                                                                    the name of the metaheader field

                                                                                    +

                                                                                  Returns string | null

                                                                                  the value of the metaheader field

                                                                                  +
                                                                                • Function

                                                                                  Indicates if the message is valid, i.e. if all fields contain valid values +isValid

                                                                                  +

                                                                                  Returns boolean

                                                                                  true if message is valid, else false

                                                                                  +
                                                                                • Function

                                                                                  Sets the value of the specified message field, if the message has not been encoded +set

                                                                                  +

                                                                                  Parameters

                                                                                  • fieldName: string

                                                                                    the name of the message field

                                                                                    +
                                                                                  • value: string

                                                                                    the value to set

                                                                                    +

                                                                                  Returns boolean

                                                                                  true if succesful, else false

                                                                                  +
                                                                                • Function

                                                                                  Sets the value of the specified metaheader field +setMeta

                                                                                  +

                                                                                  Parameters

                                                                                  • fieldName: string

                                                                                    the name of the metaheader field

                                                                                    +
                                                                                  • value: string

                                                                                    the value to set

                                                                                    +

                                                                                  Returns boolean

                                                                                  true if succesful, else false

                                                                                  +
                                                                                • Function

                                                                                  Returns the Whiteflag message encoded as a hexadecimal string +toHex

                                                                                  +

                                                                                  Returns string

                                                                                  a hexadecimal string with the encoded message

                                                                                  +
                                                                                • Function

                                                                                  Returns the Whiteflag message as a plain object +toObject

                                                                                  +

                                                                                  Returns string

                                                                                  the message as a plain object

                                                                                  +
                                                                                • Function

                                                                                  Creates new Whiteflag message from a binary buffer +fromBinary

                                                                                  +

                                                                                  Parameters

                                                                                  • message: BinaryBuffer

                                                                                    a binary buffer with the encoded message

                                                                                    +
                                                                                  • Optionalikm: Uint8Array<ArrayBufferLike>

                                                                                    the input key material to derive the encryption key, if the message is encrypted

                                                                                    +
                                                                                  • Optionaladdress: Uint8Array<ArrayBufferLike>

                                                                                    the binary encoded originator address, if the message is encrypted

                                                                                    +
                                                                                  • Optionaliv: Uint8Array<ArrayBufferLike>

                                                                                    the initialisation vector, if required for the encryption method

                                                                                    +

                                                                                  Returns Promise<WfCoreMessage>

                                                                                  a new Whiteflag message object with the decoded message

                                                                                  +
                                                                                • Creates new Whiteflag message from a hexadecimal encoded string

                                                                                  +

                                                                                  Parameters

                                                                                  • message: string

                                                                                    atring with the hexadecimal encoded message

                                                                                    +
                                                                                  • Optionalikm: string

                                                                                    the hexadecimalinput key material to derive the encryption key, if the message is encrypted

                                                                                    +
                                                                                  • Optionaladdress: string

                                                                                    the hexadecimal encoded originator address, if the message is encrypted

                                                                                    +
                                                                                  • Optionaliv: string

                                                                                    the hexadecimal initialisation vector, if required for the encryption method

                                                                                    +

                                                                                  Returns Promise<WfCoreMessage>

                                                                                  a new Whiteflag message object with the decoded message

                                                                                  +
                                                                                • Function

                                                                                  Creates new Whiteflag message from a plain object +fromObject

                                                                                  +

                                                                                  Parameters

                                                                                  • message: string

                                                                                    a plain JavaScript object with message header and body

                                                                                    +

                                                                                  Returns Promise<WfMessage>

                                                                                  a new Whiteflag message

                                                                                  +

                                                                                  if message could not be created

                                                                                  +
                                                                                • Creates new Whiteflag message from a binary encoded message

                                                                                  +

                                                                                  Parameters

                                                                                  • message: Uint8Array

                                                                                    a Uint8Array with the binary encoded message

                                                                                    +
                                                                                  • Optionalikm: Uint8Array<ArrayBufferLike>

                                                                                    the input key material to derive the encryption key, if the message is encrypted

                                                                                    +
                                                                                  • Optionaladdress: Uint8Array<ArrayBufferLike>

                                                                                    the binary encoded originator address, if the message is encrypted

                                                                                    +
                                                                                  • Optionaliv: Uint8Array<ArrayBufferLike>

                                                                                    the initialisation vector, if required for the encryption method

                                                                                    +

                                                                                  Returns Promise<WfCoreMessage>

                                                                                  a new Whiteflag message object with the decoded message

                                                                                  +
                                                                                diff --git a/docs/typedoc/main/lib/message/WfMetaHeader.html b/docs/typedoc/main/lib/message/WfMetaHeader.html new file mode 100644 index 00000000..a1baf00e --- /dev/null +++ b/docs/typedoc/main/lib/message/WfMetaHeader.html @@ -0,0 +1,23 @@ +WfMetaHeader | whiteflag-js
                                                                                whiteflag-js
                                                                                  Preparing search index...

                                                                                  Interface WfMetaHeader

                                                                                  Defines a Whiteflag message header object +WfMetaHeader

                                                                                  +
                                                                                  interface WfMetaHeader {
                                                                                      autoGenerated?: string;
                                                                                      blockchain?: string;
                                                                                      blockDepth?: number;
                                                                                      blockNumber?: number;
                                                                                      confirmed?: boolean;
                                                                                      encodedMessage?: string;
                                                                                      encryptionInitVector?: string;
                                                                                      encryptionKeyInput?: string;
                                                                                      formatValid?: boolean;
                                                                                      originatorAddress?: string;
                                                                                      originatorPubKey?: string;
                                                                                      originatorValid?: boolean;
                                                                                      recipientAddress?: string;
                                                                                      referenceValid?: boolean;
                                                                                      transactionHash?: string;
                                                                                      transactionIndex?: number;
                                                                                      transactionTime?: string;
                                                                                      transceiveDirection?: string;
                                                                                      transmissionSuccess?: boolean;
                                                                                      validationErrors?: string[];
                                                                                      [key: string]: any;
                                                                                  }

                                                                                  Indexable

                                                                                  • [key: string]: any
                                                                                  Index

                                                                                  Properties

                                                                                  autoGenerated?: string
                                                                                  blockchain?: string
                                                                                  blockDepth?: number
                                                                                  blockNumber?: number
                                                                                  confirmed?: boolean
                                                                                  encodedMessage?: string
                                                                                  encryptionInitVector?: string
                                                                                  encryptionKeyInput?: string
                                                                                  formatValid?: boolean
                                                                                  originatorAddress?: string
                                                                                  originatorPubKey?: string
                                                                                  originatorValid?: boolean
                                                                                  recipientAddress?: string
                                                                                  referenceValid?: boolean
                                                                                  transactionHash?: string
                                                                                  transactionIndex?: number
                                                                                  transactionTime?: string
                                                                                  transceiveDirection?: string
                                                                                  transmissionSuccess?: boolean
                                                                                  validationErrors?: string[]
                                                                                  diff --git a/docs/typedoc/modules.html b/docs/typedoc/modules.html new file mode 100644 index 00000000..8fff09e1 --- /dev/null +++ b/docs/typedoc/modules.html @@ -0,0 +1 @@ +whiteflag-js
                                                                                  whiteflag-js
                                                                                    Preparing search index...
                                                                                    diff --git a/docs/typedoc/util.html b/docs/typedoc/util.html new file mode 100644 index 00000000..c2b4ec3d --- /dev/null +++ b/docs/typedoc/util.html @@ -0,0 +1 @@ +util | whiteflag-js
                                                                                    whiteflag-js
                                                                                      Preparing search index...

                                                                                      Module util

                                                                                      Classes

                                                                                      BinaryBuffer → BinaryBuffer
                                                                                      Jws → Jws

                                                                                      Functions

                                                                                      b64ToB64u → b64ToB64u
                                                                                      b64uToB64 → b64uToB64
                                                                                      b64uToHex → b64uToHex
                                                                                      b64uToObj → b64uToObj
                                                                                      b64uToString → b64uToString
                                                                                      b64uToU8a → b64uToU8a
                                                                                      cropBits → cropBits
                                                                                      hexToB64u → hexToB64u
                                                                                      hexToString → hexToString
                                                                                      hexToU8a → hexToU8a
                                                                                      isBase64 → isBase64
                                                                                      isBase64u → isBase64u
                                                                                      isHex → isHex
                                                                                      isObject → isObject
                                                                                      isString → isString
                                                                                      noHexPrefix → noHexPrefix
                                                                                      objToB64u → objToB64u
                                                                                      shiftLeft → shiftLeft
                                                                                      shiftRight → shiftRight
                                                                                      stringToB64u → stringToB64u
                                                                                      stringToHex → stringToHex
                                                                                      stringToU8a → stringToU8a
                                                                                      u8aToB64u → u8aToB64u
                                                                                      u8aToHex → u8aToHex
                                                                                      u8aToString → u8aToString
                                                                                      diff --git a/docs/typedoc/util/lib/binary.html b/docs/typedoc/util/lib/binary.html new file mode 100644 index 00000000..6f0c30dc --- /dev/null +++ b/docs/typedoc/util/lib/binary.html @@ -0,0 +1 @@ +util/lib/binary | whiteflag-js
                                                                                      whiteflag-js
                                                                                        Preparing search index...

                                                                                        Module util/lib/binary

                                                                                        Classes

                                                                                        BinaryBuffer

                                                                                        Functions

                                                                                        cropBits
                                                                                        shiftLeft
                                                                                        shiftRight
                                                                                        diff --git a/docs/typedoc/util/lib/binary/BinaryBuffer.html b/docs/typedoc/util/lib/binary/BinaryBuffer.html new file mode 100644 index 00000000..b46f70ac --- /dev/null +++ b/docs/typedoc/util/lib/binary/BinaryBuffer.html @@ -0,0 +1,116 @@ +BinaryBuffer | whiteflag-js
                                                                                        whiteflag-js
                                                                                          Preparing search index...

                                                                                          Class BinaryBuffer

                                                                                          A class representing a binary buffer +BinaryBuffer

                                                                                          +

                                                                                          Objects of this class represent a binary encoded piece of data, +e.g. a Whiteflag message, that can be manipulated at bit level. This class +provides the basic (not Whiteflag-specific) functionality for other +Whiteflag packages to encode and decode binary Whiteflag messages.

                                                                                          +
                                                                                          Index

                                                                                          Properties

                                                                                          length: number

                                                                                          The number of used bits in the buffer

                                                                                          +

                                                                                          Methods

                                                                                          • Function

                                                                                            Appends bytes from a number array to the binary buffer +appendBytes

                                                                                            +

                                                                                            Parameters

                                                                                            • byteArray: number[]

                                                                                              an array of numbers representing bytes

                                                                                              +
                                                                                            • nBits: number = 0

                                                                                              the number of used bits to append

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            the updated binary buffer

                                                                                            +
                                                                                          • Function

                                                                                            Appends a hexadecimal string to the binary buffer +appendHex

                                                                                            +

                                                                                            Parameters

                                                                                            • hexString: string

                                                                                              a hexadecimal string

                                                                                              +
                                                                                            • nBits: number = 0

                                                                                              the number of used bits to append

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            the updated binary buffer

                                                                                            +
                                                                                          • Function

                                                                                            Appends a Uint8Array to the binary buffer +appendU8a

                                                                                            +

                                                                                            Parameters

                                                                                            • u8array: Uint8Array

                                                                                              an array of 8-bit unsigned integers

                                                                                              +
                                                                                            • nBits: number = 0

                                                                                              the number of used bits to append

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            the updated binary buffer

                                                                                            +
                                                                                          • Function

                                                                                            Shortens the binary buffer to the length of the specified bits +crop

                                                                                            +

                                                                                            Parameters

                                                                                            • nBits: number

                                                                                              the number of used bits, or, if negative, the number of bits to remove

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            the updated binary buffer

                                                                                            +
                                                                                          • Extracts the specified bits from the binary buffer

                                                                                            +

                                                                                            Parameters

                                                                                            • startBit: number

                                                                                              the first bit to extract (inclusive)

                                                                                              +
                                                                                            • endBit: number = -1

                                                                                              the final bit of the extraction (exclusive), negative means until end of buffer

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            a new binary buffer with the extracted bits

                                                                                            +
                                                                                          • Extracts the specified bits from the binary buffer to a hexadecimal string

                                                                                            +

                                                                                            Parameters

                                                                                            • startBit: number

                                                                                              the first bit to extract (inclusive)

                                                                                              +
                                                                                            • endBit: number = -1

                                                                                              the final bit of the extraction (exclusive), negative means until end of buffer

                                                                                              +

                                                                                            Returns string

                                                                                            a hexadecimal string with the extracted data

                                                                                            +
                                                                                          • Function

                                                                                            Extracts the specified bits from the binary buffer to a Uint8Array +extractU8a

                                                                                            +

                                                                                            Parameters

                                                                                            • startBit: number

                                                                                              the first bit to extract (inclusive)

                                                                                              +
                                                                                            • endBit: number = -1

                                                                                              the final bit of the extraction (exclusive), negative means until end of buffer

                                                                                              +

                                                                                            Returns Uint8Array

                                                                                            an array of 8-bit unsigned integers with the extracted data

                                                                                            +
                                                                                          • Function

                                                                                            Inserts bytes from a number array at the start of the binary buffer +insertBytes

                                                                                            +

                                                                                            Parameters

                                                                                            • byteArray: number[]

                                                                                              an array of numbers representing bytes

                                                                                              +
                                                                                            • nBits: number = 0

                                                                                              the number of used bits to insert

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            the updated binary buffer

                                                                                            +
                                                                                          • Function

                                                                                            Inserts a hexadecimal string at the start of the binary buffer +insertHex

                                                                                            +

                                                                                            Parameters

                                                                                            • hexString: string

                                                                                              a hexadecimal string

                                                                                              +
                                                                                            • nBits: number = 0

                                                                                              the number of used bits to insert

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            the updated binary buffer

                                                                                            +
                                                                                          • Function

                                                                                            Inserts a Uint8Array at the start of the binary buffer +insertU8a

                                                                                            +

                                                                                            Parameters

                                                                                            • u8array: Uint8Array

                                                                                              an array of 8-bit unsigned integers

                                                                                              +
                                                                                            • nBits: number = 0

                                                                                              the number of used bits to insert

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            the updated binary buffer

                                                                                            +
                                                                                          • Function

                                                                                            Shifts bits in the buffer to the left, shrinking the buffer +shiftLeft

                                                                                            +

                                                                                            Parameters

                                                                                            • shift: number

                                                                                              the number of bits to shift to the left

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            the shifted binary buffer

                                                                                            +
                                                                                          • Function

                                                                                            Shifts bits in the buffer to the right, enlarging the buffer +shiftRight

                                                                                            +

                                                                                            Parameters

                                                                                            • shift: number

                                                                                              the number of bits to shift to the right

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            the shifted binary buffer

                                                                                            +
                                                                                          • Function

                                                                                            Gives the value of the binary buffer as a hexadecimal string +toHex

                                                                                            +

                                                                                            Returns string

                                                                                            a hexadecimal string

                                                                                            +
                                                                                          • Function

                                                                                            Gives the value of the binary buffer as a Uint8Array +toU8a

                                                                                            +

                                                                                            Returns Uint8Array

                                                                                            an array of 8-bit unsigned integers

                                                                                            +
                                                                                          • Function

                                                                                            Creates a binary buffer from bytes in a number array +fromBytes

                                                                                            +

                                                                                            Parameters

                                                                                            • byteArray: number[]

                                                                                              an array of numbers representing bytes

                                                                                              +
                                                                                            • nBits: number = 0

                                                                                              the number of used bits

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            a new binary buffer

                                                                                            +
                                                                                          • Function

                                                                                            Creates a binary buffer from a hexadecimal string +fromHex

                                                                                            +

                                                                                            Parameters

                                                                                            • hexString: string

                                                                                              a hexadecimal string

                                                                                              +
                                                                                            • nBits: number = 0

                                                                                              the number of used bits

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            a new binary buffer

                                                                                            +
                                                                                          • Function

                                                                                            Creates a binary buffer from a Uint8Array +fromU8a

                                                                                            +

                                                                                            Parameters

                                                                                            • u8array: Uint8Array

                                                                                              an array of 8-bit unsigned integers

                                                                                              +
                                                                                            • nBits: number = 0

                                                                                              the number of used bits

                                                                                              +

                                                                                            Returns BinaryBuffer

                                                                                            a new binary buffer

                                                                                            +
                                                                                          diff --git a/docs/typedoc/util/lib/binary/cropBits.html b/docs/typedoc/util/lib/binary/cropBits.html new file mode 100644 index 00000000..d0efbc45 --- /dev/null +++ b/docs/typedoc/util/lib/binary/cropBits.html @@ -0,0 +1,6 @@ +cropBits | whiteflag-js
                                                                                          whiteflag-js
                                                                                            Preparing search index...

                                                                                            Function cropBits

                                                                                            • Function

                                                                                              Shortens a Uint8Array to the length of the specified bits +cropBits

                                                                                              +

                                                                                              Parameters

                                                                                              • u8array: Uint8Array

                                                                                                the Uint8Array containing the bitset

                                                                                                +
                                                                                              • nBits: number

                                                                                                the number of used bits, or, if negative, the number of bits to remove

                                                                                                +

                                                                                              Returns Uint8Array

                                                                                              a new Uint8Array with the unused bits cleared

                                                                                              +
                                                                                            diff --git a/docs/typedoc/util/lib/binary/shiftLeft.html b/docs/typedoc/util/lib/binary/shiftLeft.html new file mode 100644 index 00000000..cf8f7279 --- /dev/null +++ b/docs/typedoc/util/lib/binary/shiftLeft.html @@ -0,0 +1,6 @@ +shiftLeft | whiteflag-js
                                                                                            whiteflag-js
                                                                                              Preparing search index...

                                                                                              Function shiftLeft

                                                                                              • Function

                                                                                                Shifts bits in a Uint8Array to the left modulo 8 +shiftLeft

                                                                                                +

                                                                                                Parameters

                                                                                                • u8array: Uint8Array

                                                                                                  the Uint8Array to be left shifted

                                                                                                  +
                                                                                                • shift: number

                                                                                                  the nummber of bits to be left shifted by modulo 8 bits

                                                                                                  +

                                                                                                Returns Uint8Array

                                                                                                a new Uint8Array with the left shifted bits

                                                                                                +
                                                                                              diff --git a/docs/typedoc/util/lib/binary/shiftRight.html b/docs/typedoc/util/lib/binary/shiftRight.html new file mode 100644 index 00000000..a00eadee --- /dev/null +++ b/docs/typedoc/util/lib/binary/shiftRight.html @@ -0,0 +1,6 @@ +shiftRight | whiteflag-js
                                                                                              whiteflag-js
                                                                                                Preparing search index...

                                                                                                Function shiftRight

                                                                                                • Function

                                                                                                  Shifts bits in a Uint8Array to the right modulo 8 +shiftRight

                                                                                                  +

                                                                                                  Parameters

                                                                                                  • u8array: Uint8Array

                                                                                                    the Uint8Array to be right shifted

                                                                                                    +
                                                                                                  • shift: number

                                                                                                    the nummber of bits to be right shifted by modulo 8 bits

                                                                                                    +

                                                                                                  Returns Uint8Array

                                                                                                  a new Uint8Array with the right shifted bits

                                                                                                  +
                                                                                                diff --git a/docs/typedoc/util/lib/encoding.html b/docs/typedoc/util/lib/encoding.html new file mode 100644 index 00000000..0a9318d3 --- /dev/null +++ b/docs/typedoc/util/lib/encoding.html @@ -0,0 +1 @@ +util/lib/encoding | whiteflag-js
                                                                                                whiteflag-js
                                                                                                  Preparing search index...
                                                                                                  diff --git a/docs/typedoc/util/lib/encoding/b64ToB64u.html b/docs/typedoc/util/lib/encoding/b64ToB64u.html new file mode 100644 index 00000000..faf75583 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/b64ToB64u.html @@ -0,0 +1,5 @@ +b64ToB64u | whiteflag-js
                                                                                                  whiteflag-js
                                                                                                    Preparing search index...

                                                                                                    Function b64ToB64u

                                                                                                    • Function

                                                                                                      Convert base64 to base64url +base64

                                                                                                      +

                                                                                                      Parameters

                                                                                                      • base64: string

                                                                                                        a base64 encoded string

                                                                                                        +

                                                                                                      Returns string

                                                                                                      a base64url encoded string

                                                                                                      +
                                                                                                    diff --git a/docs/typedoc/util/lib/encoding/b64uToB64.html b/docs/typedoc/util/lib/encoding/b64uToB64.html new file mode 100644 index 00000000..88906d40 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/b64uToB64.html @@ -0,0 +1,5 @@ +b64uToB64 | whiteflag-js
                                                                                                    whiteflag-js
                                                                                                      Preparing search index...

                                                                                                      Function b64uToB64

                                                                                                      • Function

                                                                                                        Convert base64url to base64 +base64u

                                                                                                        +

                                                                                                        Parameters

                                                                                                        • base64u: string

                                                                                                          a base64url encoded string

                                                                                                          +

                                                                                                        Returns string

                                                                                                        a base64 encoded string

                                                                                                        +
                                                                                                      diff --git a/docs/typedoc/util/lib/encoding/b64uToHex.html b/docs/typedoc/util/lib/encoding/b64uToHex.html new file mode 100644 index 00000000..c462a391 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/b64uToHex.html @@ -0,0 +1,5 @@ +b64uToHex | whiteflag-js
                                                                                                      whiteflag-js
                                                                                                        Preparing search index...

                                                                                                        Function b64uToHex

                                                                                                        • Function

                                                                                                          Creates hexadecimal string from a base64url encoded string +b64uToHex

                                                                                                          +

                                                                                                          Parameters

                                                                                                          • b64uString: string

                                                                                                            a base64url encoded string

                                                                                                            +

                                                                                                          Returns string

                                                                                                          a hexadecimal encoded string

                                                                                                          +
                                                                                                        diff --git a/docs/typedoc/util/lib/encoding/b64uToObj.html b/docs/typedoc/util/lib/encoding/b64uToObj.html new file mode 100644 index 00000000..041618a9 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/b64uToObj.html @@ -0,0 +1,5 @@ +b64uToObj | whiteflag-js
                                                                                                        whiteflag-js
                                                                                                          Preparing search index...

                                                                                                          Function b64uToObj

                                                                                                          • Function

                                                                                                            Creates an object from a base64URL encoded JSON string +b64uToObj

                                                                                                            +

                                                                                                            Parameters

                                                                                                            • base64u: string

                                                                                                              a base64URL encoded JSON string

                                                                                                              +

                                                                                                            Returns Object

                                                                                                            an object with the data from the JSON object

                                                                                                            +
                                                                                                          diff --git a/docs/typedoc/util/lib/encoding/b64uToString.html b/docs/typedoc/util/lib/encoding/b64uToString.html new file mode 100644 index 00000000..a1bbea55 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/b64uToString.html @@ -0,0 +1,5 @@ +b64uToString | whiteflag-js
                                                                                                          whiteflag-js
                                                                                                            Preparing search index...

                                                                                                            Function b64uToString

                                                                                                            • Function

                                                                                                              Creates a standard string from a base64url encoded string +b64uToString

                                                                                                              +

                                                                                                              Parameters

                                                                                                              • b64uString: string

                                                                                                                a base64url encoded string

                                                                                                                +

                                                                                                              Returns string

                                                                                                              a standard string

                                                                                                              +
                                                                                                            diff --git a/docs/typedoc/util/lib/encoding/b64uToU8a.html b/docs/typedoc/util/lib/encoding/b64uToU8a.html new file mode 100644 index 00000000..8be7515d --- /dev/null +++ b/docs/typedoc/util/lib/encoding/b64uToU8a.html @@ -0,0 +1,5 @@ +b64uToU8a | whiteflag-js
                                                                                                            whiteflag-js
                                                                                                              Preparing search index...

                                                                                                              Function b64uToU8a

                                                                                                              • Function

                                                                                                                Creates a UInt8 typed array from a base64url encoded string +b64uToU8a

                                                                                                                +

                                                                                                                Parameters

                                                                                                                • b64uString: string

                                                                                                                  a base64url encoded string

                                                                                                                  +

                                                                                                                Returns Uint8Array

                                                                                                                a UInt8 typed array

                                                                                                                +
                                                                                                              diff --git a/docs/typedoc/util/lib/encoding/hexToB64u.html b/docs/typedoc/util/lib/encoding/hexToB64u.html new file mode 100644 index 00000000..364a8359 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/hexToB64u.html @@ -0,0 +1,5 @@ +hexToB64u | whiteflag-js
                                                                                                              whiteflag-js
                                                                                                                Preparing search index...

                                                                                                                Function hexToB64u

                                                                                                                • Function

                                                                                                                  Creates a base64url encoded string from a hexadecimal string +hexToB64u

                                                                                                                  +

                                                                                                                  Parameters

                                                                                                                  • hexString: string

                                                                                                                    a hexadecimal string

                                                                                                                    +

                                                                                                                  Returns string

                                                                                                                  a base64url encoded string

                                                                                                                  +
                                                                                                                diff --git a/docs/typedoc/util/lib/encoding/hexToString.html b/docs/typedoc/util/lib/encoding/hexToString.html new file mode 100644 index 00000000..f1108079 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/hexToString.html @@ -0,0 +1,5 @@ +hexToString | whiteflag-js
                                                                                                                whiteflag-js
                                                                                                                  Preparing search index...

                                                                                                                  Function hexToString

                                                                                                                  • Function

                                                                                                                    Creates a regular string from a hexadecimal string +hexToString

                                                                                                                    +

                                                                                                                    Parameters

                                                                                                                    • hexString: string

                                                                                                                      a hexadecimal string

                                                                                                                      +

                                                                                                                    Returns string

                                                                                                                    a regular string

                                                                                                                    +
                                                                                                                  diff --git a/docs/typedoc/util/lib/encoding/hexToU8a.html b/docs/typedoc/util/lib/encoding/hexToU8a.html new file mode 100644 index 00000000..bbbd4f87 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/hexToU8a.html @@ -0,0 +1,5 @@ +hexToU8a | whiteflag-js
                                                                                                                  whiteflag-js
                                                                                                                    Preparing search index...

                                                                                                                    Function hexToU8a

                                                                                                                    • Function

                                                                                                                      Creates a UInt8 typed array from a hexadecimal string +hexToU8a

                                                                                                                      +

                                                                                                                      Parameters

                                                                                                                      • hexString: string

                                                                                                                        a hexadecimal string

                                                                                                                        +

                                                                                                                      Returns Uint8Array<ArrayBuffer>

                                                                                                                      an array of 8-bit unsigned integers

                                                                                                                      +
                                                                                                                    diff --git a/docs/typedoc/util/lib/encoding/isBase64.html b/docs/typedoc/util/lib/encoding/isBase64.html new file mode 100644 index 00000000..1699da10 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/isBase64.html @@ -0,0 +1,5 @@ +isBase64 | whiteflag-js
                                                                                                                    whiteflag-js
                                                                                                                      Preparing search index...

                                                                                                                      Function isBase64

                                                                                                                      • Function

                                                                                                                        Checks if a string is base64 encoded +isBase64

                                                                                                                        +

                                                                                                                        Parameters

                                                                                                                        • base64: string

                                                                                                                          a string that might be base64 encoded

                                                                                                                          +

                                                                                                                        Returns boolean

                                                                                                                        true if base64 encoded, else false

                                                                                                                        +
                                                                                                                      diff --git a/docs/typedoc/util/lib/encoding/isBase64u.html b/docs/typedoc/util/lib/encoding/isBase64u.html new file mode 100644 index 00000000..09bb6262 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/isBase64u.html @@ -0,0 +1,5 @@ +isBase64u | whiteflag-js
                                                                                                                      whiteflag-js
                                                                                                                        Preparing search index...

                                                                                                                        Function isBase64u

                                                                                                                        • Function

                                                                                                                          Checks if a string is base64url encoded +isBase64u

                                                                                                                          +

                                                                                                                          Parameters

                                                                                                                          • base64u: string

                                                                                                                            a string that might be base64url encoded

                                                                                                                            +

                                                                                                                          Returns boolean

                                                                                                                          true if base64url encoded, else false

                                                                                                                          +
                                                                                                                        diff --git a/docs/typedoc/util/lib/encoding/isHex.html b/docs/typedoc/util/lib/encoding/isHex.html new file mode 100644 index 00000000..0022c61d --- /dev/null +++ b/docs/typedoc/util/lib/encoding/isHex.html @@ -0,0 +1,5 @@ +isHex | whiteflag-js
                                                                                                                        whiteflag-js
                                                                                                                          Preparing search index...
                                                                                                                          • Function

                                                                                                                            Checks if a string is hexadecimal encoded +hexString

                                                                                                                            +

                                                                                                                            Parameters

                                                                                                                            • hexString: string

                                                                                                                              a string that might be hexadecimal encoded

                                                                                                                              +

                                                                                                                            Returns boolean

                                                                                                                            true if hexadecimal encoded, else false

                                                                                                                            +
                                                                                                                          diff --git a/docs/typedoc/util/lib/encoding/isObject.html b/docs/typedoc/util/lib/encoding/isObject.html new file mode 100644 index 00000000..b6da9cc9 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/isObject.html @@ -0,0 +1,5 @@ +isObject | whiteflag-js
                                                                                                                          whiteflag-js
                                                                                                                            Preparing search index...

                                                                                                                            Function isObject

                                                                                                                            • Function

                                                                                                                              Checks if something is an object +isObject

                                                                                                                              +

                                                                                                                              Parameters

                                                                                                                              • obj: any

                                                                                                                                something that might be an object

                                                                                                                                +

                                                                                                                              Returns boolean

                                                                                                                              true if object, else false

                                                                                                                              +
                                                                                                                            diff --git a/docs/typedoc/util/lib/encoding/isString.html b/docs/typedoc/util/lib/encoding/isString.html new file mode 100644 index 00000000..2519bef7 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/isString.html @@ -0,0 +1,5 @@ +isString | whiteflag-js
                                                                                                                            whiteflag-js
                                                                                                                              Preparing search index...

                                                                                                                              Function isString

                                                                                                                              • Function

                                                                                                                                Checks if something is a string +isString

                                                                                                                                +

                                                                                                                                Parameters

                                                                                                                                • charString: any

                                                                                                                                  something that might be a string

                                                                                                                                  +

                                                                                                                                Returns boolean

                                                                                                                                true if string, else false

                                                                                                                                +
                                                                                                                              diff --git a/docs/typedoc/util/lib/encoding/noHexPrefix.html b/docs/typedoc/util/lib/encoding/noHexPrefix.html new file mode 100644 index 00000000..a50f7b0f --- /dev/null +++ b/docs/typedoc/util/lib/encoding/noHexPrefix.html @@ -0,0 +1,5 @@ +noHexPrefix | whiteflag-js
                                                                                                                              whiteflag-js
                                                                                                                                Preparing search index...

                                                                                                                                Function noHexPrefix

                                                                                                                                • Function

                                                                                                                                  Removes the '0x' hex prefix if present +hexString

                                                                                                                                  +

                                                                                                                                  Parameters

                                                                                                                                  • hexString: string

                                                                                                                                    a hexadecimal encoded string

                                                                                                                                    +

                                                                                                                                  Returns string

                                                                                                                                  the the string without the hex prefix

                                                                                                                                  +
                                                                                                                                diff --git a/docs/typedoc/util/lib/encoding/objToB64u.html b/docs/typedoc/util/lib/encoding/objToB64u.html new file mode 100644 index 00000000..fded2772 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/objToB64u.html @@ -0,0 +1,5 @@ +objToB64u | whiteflag-js
                                                                                                                                whiteflag-js
                                                                                                                                  Preparing search index...

                                                                                                                                  Function objToB64u

                                                                                                                                  • Function

                                                                                                                                    Creates a base64URL encoded JSON string from an object +objToB64u

                                                                                                                                    +

                                                                                                                                    Parameters

                                                                                                                                    • obj: Object

                                                                                                                                      the object to be encoded

                                                                                                                                      +

                                                                                                                                    Returns string

                                                                                                                                    a base64URL encoded JSON string

                                                                                                                                    +
                                                                                                                                  diff --git a/docs/typedoc/util/lib/encoding/stringToB64u.html b/docs/typedoc/util/lib/encoding/stringToB64u.html new file mode 100644 index 00000000..1b24c646 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/stringToB64u.html @@ -0,0 +1,5 @@ +stringToB64u | whiteflag-js
                                                                                                                                  whiteflag-js
                                                                                                                                    Preparing search index...

                                                                                                                                    Function stringToB64u

                                                                                                                                    • Function

                                                                                                                                      Creates a base64url encoded string from a regular string +stringToB64u

                                                                                                                                      +

                                                                                                                                      Parameters

                                                                                                                                      • charString: string

                                                                                                                                        a regular character string

                                                                                                                                        +

                                                                                                                                      Returns string

                                                                                                                                      a base64url encoded string

                                                                                                                                      +
                                                                                                                                    diff --git a/docs/typedoc/util/lib/encoding/stringToHex.html b/docs/typedoc/util/lib/encoding/stringToHex.html new file mode 100644 index 00000000..2efb6f5a --- /dev/null +++ b/docs/typedoc/util/lib/encoding/stringToHex.html @@ -0,0 +1,5 @@ +stringToHex | whiteflag-js
                                                                                                                                    whiteflag-js
                                                                                                                                      Preparing search index...

                                                                                                                                      Function stringToHex

                                                                                                                                      • Function

                                                                                                                                        Creates a hexadecimal string from a regular string +stringToHex

                                                                                                                                        +

                                                                                                                                        Parameters

                                                                                                                                        • charString: string

                                                                                                                                          a regular character string

                                                                                                                                          +

                                                                                                                                        Returns string

                                                                                                                                        a hexadecimal string

                                                                                                                                        +
                                                                                                                                      diff --git a/docs/typedoc/util/lib/encoding/stringToU8a.html b/docs/typedoc/util/lib/encoding/stringToU8a.html new file mode 100644 index 00000000..a09f93e5 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/stringToU8a.html @@ -0,0 +1,5 @@ +stringToU8a | whiteflag-js
                                                                                                                                      whiteflag-js
                                                                                                                                        Preparing search index...

                                                                                                                                        Function stringToU8a

                                                                                                                                        • Function

                                                                                                                                          Creates a UInt8 typed array from a regular string +stringToU8a

                                                                                                                                          +

                                                                                                                                          Parameters

                                                                                                                                          • charString: string

                                                                                                                                            a regular character string

                                                                                                                                            +

                                                                                                                                          Returns Uint8Array<ArrayBuffer>

                                                                                                                                          an array of 8-bit unsigned integers

                                                                                                                                          +
                                                                                                                                        diff --git a/docs/typedoc/util/lib/encoding/u8aToB64u.html b/docs/typedoc/util/lib/encoding/u8aToB64u.html new file mode 100644 index 00000000..55c4fb2a --- /dev/null +++ b/docs/typedoc/util/lib/encoding/u8aToB64u.html @@ -0,0 +1,5 @@ +u8aToB64u | whiteflag-js
                                                                                                                                        whiteflag-js
                                                                                                                                          Preparing search index...

                                                                                                                                          Function u8aToB64u

                                                                                                                                          • Function

                                                                                                                                            Creates a base64url encoded string from a UInt8 typed array +u8aToB64u

                                                                                                                                            +

                                                                                                                                            Parameters

                                                                                                                                            • u8array: Uint8Array

                                                                                                                                              an array of 8-bit unsigned integers

                                                                                                                                              +

                                                                                                                                            Returns string

                                                                                                                                            a base64url encoded string

                                                                                                                                            +
                                                                                                                                          diff --git a/docs/typedoc/util/lib/encoding/u8aToHex.html b/docs/typedoc/util/lib/encoding/u8aToHex.html new file mode 100644 index 00000000..675ef538 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/u8aToHex.html @@ -0,0 +1,5 @@ +u8aToHex | whiteflag-js
                                                                                                                                          whiteflag-js
                                                                                                                                            Preparing search index...

                                                                                                                                            Function u8aToHex

                                                                                                                                            • Function

                                                                                                                                              Creates a hexadecimal string from an Uint8Array +u8aToHex

                                                                                                                                              +

                                                                                                                                              Parameters

                                                                                                                                              • u8array: Uint8Array

                                                                                                                                                an array of 8-bit unsigned integers

                                                                                                                                                +

                                                                                                                                              Returns string

                                                                                                                                              a hexadecimal string

                                                                                                                                              +
                                                                                                                                            diff --git a/docs/typedoc/util/lib/encoding/u8aToString.html b/docs/typedoc/util/lib/encoding/u8aToString.html new file mode 100644 index 00000000..bcd26597 --- /dev/null +++ b/docs/typedoc/util/lib/encoding/u8aToString.html @@ -0,0 +1,5 @@ +u8aToString | whiteflag-js
                                                                                                                                            whiteflag-js
                                                                                                                                              Preparing search index...

                                                                                                                                              Function u8aToString

                                                                                                                                              • Function

                                                                                                                                                Creates a standard string from a UInt8 typed array +u8aToString

                                                                                                                                                +

                                                                                                                                                Parameters

                                                                                                                                                • u8array: Uint8Array

                                                                                                                                                  a UInt8 typed array

                                                                                                                                                  +

                                                                                                                                                Returns string

                                                                                                                                                a standard string

                                                                                                                                                +
                                                                                                                                              diff --git a/docs/typedoc/util/lib/jws.html b/docs/typedoc/util/lib/jws.html new file mode 100644 index 00000000..a79e38ea --- /dev/null +++ b/docs/typedoc/util/lib/jws.html @@ -0,0 +1 @@ +util/lib/jws | whiteflag-js
                                                                                                                                              whiteflag-js
                                                                                                                                                Preparing search index...

                                                                                                                                                Module util/lib/jws

                                                                                                                                                Classes

                                                                                                                                                Jws
                                                                                                                                                diff --git a/docs/typedoc/util/lib/jws/Jws.html b/docs/typedoc/util/lib/jws/Jws.html new file mode 100644 index 00000000..9c51fc7c --- /dev/null +++ b/docs/typedoc/util/lib/jws/Jws.html @@ -0,0 +1,56 @@ +Jws | whiteflag-js
                                                                                                                                                whiteflag-js
                                                                                                                                                  Preparing search index...

                                                                                                                                                  A class representing a JSON Web Token (JWS) +Jws

                                                                                                                                                  +

                                                                                                                                                  Whiteflag uses JSON Web Signatures (JWS) for one of its +authentication methods. This class provides the basic (not Whiteflag- +specific) functionality to create, sign and convert JWSs for other +Whiteflag packages.

                                                                                                                                                  +
                                                                                                                                                  Index

                                                                                                                                                  Methods

                                                                                                                                                  • Function

                                                                                                                                                    Returns the JWS signature +getSignature

                                                                                                                                                    +

                                                                                                                                                    Returns string

                                                                                                                                                    a string with the the JWS signature

                                                                                                                                                    +
                                                                                                                                                  • Function

                                                                                                                                                    Returns the JWS signature input +getSignInput

                                                                                                                                                    +

                                                                                                                                                    Returns string

                                                                                                                                                    a string with the input to be signed by the signing algorithm

                                                                                                                                                    +
                                                                                                                                                  • Function

                                                                                                                                                    Indicates if the JWS has been signed +isSigned

                                                                                                                                                    +

                                                                                                                                                    Returns boolean

                                                                                                                                                    true if signed, else false

                                                                                                                                                    +
                                                                                                                                                  • Function

                                                                                                                                                    Sets the identifier of the signing algorithm, if not yet signed +setSignAlgorithm

                                                                                                                                                    +

                                                                                                                                                    Parameters

                                                                                                                                                    • algorithm: string

                                                                                                                                                      the identifier of the algorithm used to sign the payload

                                                                                                                                                      +

                                                                                                                                                    Returns boolean

                                                                                                                                                    true if identifier could be set, false if already signed

                                                                                                                                                    +
                                                                                                                                                  • Function

                                                                                                                                                    Sets the signature, if not yet signed +setSignature

                                                                                                                                                    +

                                                                                                                                                    Parameters

                                                                                                                                                    • signature: string

                                                                                                                                                      the base64url encoded signature

                                                                                                                                                      +

                                                                                                                                                    Returns boolean

                                                                                                                                                    true if signature could be added, false if already signed

                                                                                                                                                    +
                                                                                                                                                  • Function

                                                                                                                                                    Return a compact serialised JWS as a compact serialized string +toCompact

                                                                                                                                                    +

                                                                                                                                                    Returns string

                                                                                                                                                    the JWS as a compact serialized JWS string

                                                                                                                                                    +
                                                                                                                                                  • Function

                                                                                                                                                    Returns a flattened JWS +toFlat

                                                                                                                                                    +

                                                                                                                                                    Returns Object

                                                                                                                                                    the JWS as a flattened JWS plain JavaScript object

                                                                                                                                                    +
                                                                                                                                                  • Function

                                                                                                                                                    Returns a full JWS +toFull

                                                                                                                                                    +

                                                                                                                                                    Returns Object

                                                                                                                                                    the JWS as a full JWS plain JavaScript object

                                                                                                                                                    +
                                                                                                                                                  • Function

                                                                                                                                                    Creates a new JWS object from a compact serialised JWS string +fromCompact

                                                                                                                                                    +

                                                                                                                                                    Parameters

                                                                                                                                                    • jws: string

                                                                                                                                                      a compact serialised JWS string

                                                                                                                                                      +

                                                                                                                                                    Returns Jws

                                                                                                                                                    a new JWS object

                                                                                                                                                    +
                                                                                                                                                  • Function

                                                                                                                                                    Creates a new JWS object from a plain javaScript object +fromObject

                                                                                                                                                    +

                                                                                                                                                    Parameters

                                                                                                                                                    • jws: any

                                                                                                                                                      a plain object

                                                                                                                                                      +

                                                                                                                                                    Returns Jws

                                                                                                                                                    a new JWS object

                                                                                                                                                    +
                                                                                                                                                  • Function

                                                                                                                                                    Creates a new JWS from a payload +fromPayload

                                                                                                                                                    +

                                                                                                                                                    Parameters

                                                                                                                                                    • payload: Object

                                                                                                                                                      the JWS payload

                                                                                                                                                      +

                                                                                                                                                    Returns Jws

                                                                                                                                                    a new Binary Array

                                                                                                                                                    +
                                                                                                                                                  diff --git a/package.json b/package.json index 739b309e..0376ab56 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,26 @@ { - "name": "@whiteflag/js", - "version": "0.1.0-dev", + "name": "whiteflag-js", + "version": "1.0.0-dev", "description": "JavaScript implementation of the Whiteflag protocol", "license": "CC0-1.0", "private": true, - "workspaces": [ - "dist" - ], "keywords": [ "whiteflag", "protocol", "blockchain" ], + "workspaces": [ + "./dist/core", + "./dist/crypto", + "./dist/main", + "./dist/util" + ], + "directories": { + "dist": "dist", + "dos": "docs", + "src": "src", + "test": "test" + }, "homepage": "https://github.com/WhiteflagProtocol/whiteflag-js#readme", "repository": { "type": "git", @@ -21,28 +30,32 @@ "url": "https://github.com/WhiteflagProtocol/whiteflag-js/issues" }, "type": "module", - "main": "dist/protocol/index.js", - "types": "dist/protocol/index.d.ts", "dependencies": { - "@whiteflag/protocol": "file:./dist/protocol", - "@whiteflag/core": "file:./dist/core", - "@whiteflag/crypto": "file:./dist/crypto", - "@whiteflag/util": "file:./dist/util" + "@whiteflagprotocol/core": "file:./dist/core", + "@whiteflagprotocol/crypto": "file:./dist/crypto", + "@whiteflagprotocol/main": "file:./dist/main", + "@whiteflagprotocol/util": "file:./dist/util" }, "devDependencies": { "@types/node": "latest", - "typedoc": "latest", + "copyfiles": "^2.4.1", "mocha": "^11.7.5", "npm-run-all": "^4.1.5", - "rimraf": "latest" + "rimraf": "^6.1.2", + "typedoc": "latest", + "typescript": "^5.9.3" }, "scripts": { "test": "tsc && mocha --recursive", - "doc:src": "rm -rf docs/typedoc/* && typedoc", - "build:cleanup": "rimraf -g ./dist/**/*.js ./dist/**/**/*.js ./dist/**/*.ts ./dist/**/**/*.ts", - "build:compile": "tsc", + "docs:cleanup": "rimraf -g ./docs/typedoc/**/*", + "docs:src": "typedoc --groupReferencesByType --router structure", + "docs": "npm-run-all docs:*", + "build:cleanup": "rimraf -g ./dist/**/*.md ./dist/**/*.json ./dist/**/**/*.json ./dist/**/*.js ./dist/**/**/*.js ./dist/**/*.ts ./dist/**/**/*.ts", + "build:compile": "tsc --declaration false", + "build:declaration": "tsc --emitDeclarationOnly --declaration true --removeComments false", + "build:packages": "copyfiles -u 1 src/**/package.json src/**/*.md dist", "build:test": "mocha --recursive --reporter dot", - "build:docs": "npm-run-all doc:*", + "build:docs": "npm run docs", "build": "npm-run-all build:*" } } diff --git a/src/core/LICENSE.md b/src/core/LICENSE.md new file mode 100644 index 00000000..66129f4e --- /dev/null +++ b/src/core/LICENSE.md @@ -0,0 +1,53 @@ +# Whiteflag Software Dedication to the Public Domain + +## Preamble + +All persons and organisations that contributed to the initial development +of the Whiteflag Protocol and the Whiteflag JavaScript Library software did +so disinterestedly. + +## Whiteflag JavaScript Library CC0-1.0 Statement + +Excluding third party software that may be part of this distribution, the +Whiteflag JavaScript Library software and associated documentation is dedicated +to the public domain under the [Creative Commons CC0-1.0 Universal Public Domain Dedication](http://creativecommons.org/publicdomain/zero/1.0/) +statement, meaning that to the extent possible under law, the authors +and their organisations have waived all copyright and related or neighbouring +rights to this work, allowing anyone to copy, modify, distribute and implement +the work, even for commercial purposes, all without asking permission. + +In no way are the patent or trademark rights of any person affected by this +dedication to the public domain, nor are the rights that other persons may +have in the work or in how the work is used, such as publicity or privacy +rights. Unless expressly stated otherwise, the authors and their organisations +make no warranties about the work, and disclaim liability for all uses of the +work, to the fullest extent permitted by applicable law. When using or citing +the work, you should not imply endorsement by the authors or the affirmer. + +## Third Party Software Dependencies + +Where the Whiteflag JavaScript Library software requires third party +software packages, which are not part of this distribution. These third party +software packages need to be obtained through their respective official +distribution channels, and may be licensed. + +## Legal Restrictions for the Usage of the Whiteflag software + +WARNING: THE USAGE OF SIGNS AND SIGNALS WITH THIS SOFTWARE IS SUBJECT +TO LOCAL AND/OR INTERNATIONAL LAWS. Please take note of the following when +using this software: + +- This software only provides an additional means of communication for + certain signs and signals, and it does not replace any existing + standard, regulation or means of communication, whether mandatory or + not, including but not limited to physical signs, radio communications, + official registers, etc. + +- The usage of protective signs is subject to International Humanitarian + Law. Misuse of protective signs is a punishable violation under local + and international laws. + +- The usage of emergency signals may be subject to various regulations and + standards, depending on location and context. The misuse of emergency + signals, including misuse of the duress functionality, may be a + punishable violation under local and international laws. diff --git a/src/core/README.md b/src/core/README.md new file mode 100644 index 00000000..85abfafb --- /dev/null +++ b/src/core/README.md @@ -0,0 +1,104 @@ +# WFJSL Core Implementation + +## Overview + +The Whiteflag JavaScript Library (WFJSL) is an implementation of the Whiteflag +Protocol written in [TypeScript](https://developer.mozilla.org/en-US/docs/Glossary/TypeScript), +and compiled to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), +to support the development of Whiteflag-enabled applications in JavaScript. + +The `@whiteflagprotocol/core` package provides the modules that define +Whiteflag core protocol features as specified in the Whiteflag standard. +As such, this package is not a fully functional implementation of the +protocol, but separates core protocol functions from implementation-specific +design decisions. Therefore, this package is not intended to be used directly +by implementations of Whiteflag; instead, the `@whiteflagprotocol/main` should +be used. + +This description provides a generic overview of the WFJSL core package. +Please see the [WFJSL TypeDoc documentation](https://js.whiteflagprotocol.org/typedoc) +for a detailed description of all classes and functions. + +## Whiteflag Message class + +The Whiteflag message class `WfCoreMessage` defined in the `message` module +represents a Whiteflag message. The class contains the methods to create, +set field values, encode and encrypt a Whiteflag message. Please note that +there normally is no need to use the `WfCoreMessage` directly. Instead, the +`WfMessage` child class of the `@whiteflagprotocol/main` package is the main +class to use for Whiteflag message, as this extended class provides methods to +process the metadata required for full protocol functionality. + +A new message may be created using the constructor, or by using a static +factory method. For example, creating a new FreeText message (message +code `F`) and set the `Text` field, may be done as follows: + +```{javascript} +let wfMessage = new WfCoreMessage('F'); +WfCoreMessage.set('Text', 'Example text to be sent with the FreeText message'); +``` + +The `encode()` method encodes the message. The `WfCoreMessage` class +automatically verifies the fields and values when encoding and decoding. +Encoding and decoding are asynchronous, meaning the functions return +[Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). +Once encoded, the methods `toHex()` and `toU8a()` may be used to obtain the +encoded message as a hexadecimal string or a UInt8array, respectively. + +```{javascript} +await wfMessage.encode(); +const hexMessage = wfMessage.toHex(); +``` + +If a message is encoded, or decoded, the message is "final", meaning its +content cannot be changed. Decoding a message is done using a one of the +static factory methods such as `fromHex(...)` or `fromU8a(...)`, since the +message type is probably not known before decoding. + +```{javascript} +wfMessage = await WfCoreMessage.fromHex(hexMessage); +``` + +Encryption and decryption is automatically performed upon encoding and +decoding, based on the value of the `EncryptionIndicator` field in the message +header. Since the `WfCoreMessage` class does not hold any metadata, all +encryption and decryption parameters must be provided to the respective method +when encoding or decoding a message. + +The `message` module, also provides the following functions. These functions +are used by the `WfCoreMessage` class, but may also be used for alternative +processing of Whiteflag messages. + +| Function | Purpose | +|-------------------|--------------------------------------------------| +| `isValidMessage` | Checks if an object is a valid Whiteflag message | +| `validateMessage` | Checks a message object for validation errors | +| `encryptMessage` | Encrypts a binary encoded message | +| `decryptMessage` | Decrypts an encrypted binary message | + +Both validation functions may be used for both plain JavaScript objects and +objects of the `WfCoreMessage` class. The encryption and decryption functions +work only on binary encoded messages provided as a `BinaryBuffer`. + +## Whiteflag message field encoding + +The `codec` module provides the encoding and decoding for each field in +a Whiteflag message i.a.w. the Whiteflag specification. The available field +encodings are defined with the `WfCodec` enum. For the encoding, decoding, and +verification of field values, the module provides the following functions. + +| Function | Purpose | +|----------------|------------------------------------| +| `encodeField` | Encodes a Whiteflag message field | +| `decodeField` | Decodes a Whiteflag message field | +| `isValidValue` | Checks if the field value is valid | + +These functions are used by the `WfCoreMessage` class when encoding and +decoding a message. + +## Whiteflag versions + +Currently, only one version of the Whiteflag protocol has been developed. For +ease of implementation of future Whiteflag versions, WFJSL functions and +classes take the Whiteflag version into account. The `versions` module defines +the available Whiteflag versions with the `WfVersions` enum. diff --git a/src/core/index.ts b/src/core/index.ts index 7bf5702a..0ef74f4a 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,7 +1,27 @@ +'use strict'; /** * @module core * @summary Whiteflag JS core protocol functionality * @document docs/md/modules.md + * @primaryExport */ -export { WfMessage } from './lib/message'; -export { WfProtocolError } from './lib/errors'; +export { + encodeField, + decodeField, + isValidValue +} from './lib/codec.ts'; +export { + WfProtocolError, + WfErrorCode +} from './lib/errors.ts'; +export { + WfMsgType, + WfCoreMessage, + isValidMessage, + validateMessage, + encryptMessage, + decryptMessage +} from './lib/message.ts'; +export { + WfVersion +} from './lib/versions.ts'; diff --git a/src/core/lib/codec.ts b/src/core/lib/codec.ts new file mode 100644 index 00000000..2d69c90c --- /dev/null +++ b/src/core/lib/codec.ts @@ -0,0 +1,449 @@ +'use strict'; +/** + * @module core/codec + * @summary Whiteflag JS message field encoding and decoding module + */ +export { + WfCodec, + encodeField, + decodeField, + isValidValue +}; + +/* Dependencies */ +import { BinaryBuffer } from '@whiteflagprotocol/util'; + +/* Module imports */ +import { WfVersion } from './versions.ts'; + +/* Whiteflag specification */ +import fieldSpec_v1 from '../static/v1/wf-field-encoding.json' with { type: 'json' }; + +/* Constants */ +const NOCHAR = ''; +const HEXRADIX = 16; +const BYTELENGTH = 8; +const QUADBIT = 4; + +/* MODULE DECLARATIONS */ +/** + * Whiteflag field encodings, defining the encoding of Whiteflag + * message fields as defined by the Whiteflag specification + * @enum WfCodec + * @wfversion v1-draft.7 + * @wfreference 4.1.2 Message Encoding + */ +enum WfCodec { + /** Binary field */ + BIN = 'binary', + /** Decimal field */ + DEC = 'decimal', + /** HExadecimal field */ + HEX = 'hexadecimal', + /** UTF-8 / ASCII text field */ + UTF8 = 'utf-8', + /** Datetime field */ + DATETIME = 'datetime', + /** Duration field */ + DURATION = 'duration', + /** Latitude field */ + LAT = 'latitude', + /** Longitude field */ + LONG = 'longitude' +} +/** + * Whiteflag field specification + */ +const FIELDS = compileFieldCodecs(); + +/* MODULE FUNCTIONS */ +/** + * Encodes a Whiteflag message field + * @function encodeField + * @wfversion v1-draft.7 + * @wfreference 4.1.2 Message Encoding, 4.1.3 Message Compression + * @param value the message field value + * @param codec the message field encoding: 'utf-8', 'bin', 'dec', 'hex', 'datetime', 'duration', 'lat', 'long' + * @param version the version of the Whiteflag specification + * @returns a binary buffer with the compressed encoded field + */ +function encodeField(value: string, codec: WfCodec, version = WfVersion.v1): BinaryBuffer { + /* Check field value */ + if (!isValidValue(value, codec, version)) { + throw new Error(`Value of ${codec} field does not match ${FIELDS[codec][version].pattern} pattern`); + } + /* Choose codec based on field encoding */ + switch (codec) { + case WfCodec.BIN: { + return encodeBin(value); + } + case WfCodec.DEC: + case WfCodec.HEX: { + return encodeBDX(value); + } + case WfCodec.UTF8: { + return encodeUTF(value); + } + case WfCodec.DATETIME: { + return encodeDatum(value); + } + case WfCodec.DURATION: { + return encodeDatum(value); + } + case WfCodec.LAT: { + return encodeLatLong(value); + } + case WfCodec.LONG: { + return encodeLatLong(value); + } + default: { + throw new Error(`Invalid message field encoding: ${codec}`); + } + } +} +/** + * Decodes a Whiteflag message field + * @function decodeField + * @wfversion v1-draft.7 + * @wfreference 4.1.2 Message Encoding, 4.1.3 Message Compression + * @param buffer a binary buffer with the encoded field + * @param codec the message field encoding: 'utf-8', 'bin', 'dec', 'hex', 'datetime', 'duration', 'lat', 'long' + * @param version the version of the Whiteflag specification + * @returns a string with the decoded field value + */ +function decodeField(buffer: BinaryBuffer, codec: WfCodec, version = WfVersion.v1): string { + /* Check binary encoding */ + if (FIELDS[codec][version].length > 0 + && buffer.length !== FIELDS[codec][version].length) { + throw new Error(`Invalid ${codec} binary field length: ${buffer.length} bits`); + } + /* Choose decoding based on field encoding */ + switch (codec) { + case WfCodec.BIN: { + return decodeBin(buffer); + } + case WfCodec.DEC: + case WfCodec.HEX: { + return decodeBDX(buffer); + } + case WfCodec.UTF8: { + return decodeUTF(buffer); + } + case WfCodec.DATETIME: { + return decodeDatetime(buffer); + } + case WfCodec.DURATION: { + return decodeDuration(buffer); + } + case WfCodec.LAT: { + return decodeLat(buffer); + } + case WfCodec.LONG: { + return decodeLong(buffer); + } + default: { + throw new Error(`Invalid message field encoding: ${codec}`); + } + } +} +/** + * Checks if the field value is valid + * @function isValidValue + * @param value the field value + * @param codec the field encoding + * @param version the Whiteflag protocol version + * @returns true if valid, else false + */ +function isValidValue(value: string, codec: WfCodec, version = WfVersion.v1): boolean { + return FIELDS[codec][version].regex.test(value); +} + +/* PRIVATE MODULE DECLARATIONS */ +/** + * Defines an object with field encoding definitions + * @private + * @interface WfFieldEncoding + */ +interface WfFieldEncoding { + [key: string]: { // Field type + [key: string]: { // Whiteflag version + length: number, // Field length, or 0 if variable + pattern: string, // Regular expresssion pattern + regex: RegExp // Regular expression for field value verification + } + } +} + +/* PRIVATE MODULE FUNCTIONS */ +/** + * Compiles an object with all valid field encoding definitions + * @private + * @returns an object with field encoding definitions + */ +function compileFieldCodecs(): WfFieldEncoding { + const codec: WfFieldEncoding = {}; + for (const type of Object.values(WfCodec)) { + codec[type] = {}; + + /* Whiteflag version 1 */ { + const version = WfVersion.v1; + codec[type][version] = fieldSpec_v1[type] as any; + codec[type][version].regex = new RegExp(codec[type][version].pattern); + } + } + return codec; +} +/** + * Encodes a binary field to a binary buffer + * @private + * @param binStr representation of the binary encoding + * @returns a binary buffer with the encoded field + */ +function encodeBin(binStr: string): BinaryBuffer { + /* Number of bytes required */ + const bitLength = binStr.length; + const byteLength = Math.ceil(bitLength / BYTELENGTH); + + /* Add bits one by one */ + let buffer = new Uint8Array(byteLength); + for (let bitIndex = 0; bitIndex < bitLength; bitIndex++) { + if (binStr.substring(bitIndex, bitIndex + 1) === '1') { + const byteCursor = Math.floor(bitIndex / BYTELENGTH); + const bitPosition = bitIndex % BYTELENGTH; + buffer[byteCursor] |= (0x80 >>> bitPosition); + } + } + /* Return the resulting binary buffer */ + return BinaryBuffer.fromU8a(buffer, bitLength); +} +/** + * Decodes a binary buffer with the compressed encoded field + * to a field string with the binary value + * @private + * @param buffer a binary buffer with the encoded field + * @return a string with the decoded binary field value + */ +function decodeBin(buffer: BinaryBuffer): string { + const bitLength = buffer.length; + const byteArray = buffer.toU8a(); + let binStr: string = ''; + + /* Loop strough bits of binary buffer */ + for (let bitIndex = 0; bitIndex < bitLength; bitIndex++) { + const byteCursor = Math.floor(bitIndex / BYTELENGTH); + const bitPosition = bitIndex % BYTELENGTH; + if ((byteArray[byteCursor] >>> (BYTELENGTH - bitPosition - 1) & 1) == 1) { + binStr += '1'; + } else { + binStr += '0'; + } + } + return binStr.toLowerCase(); +} +/** + * Encodes a field string with a (hexa)decimal value + * to a binary buffer with the compressed encoded field + * @private + * @param bdxString an unencoded/uncompressed (hexa)decimal field value + * @returns a binary buffer with the encoded field + */ +function encodeBDX(bdxString: string): BinaryBuffer { + /* Each digit needs 4 bits */ + const bitLength = bdxString.length * QUADBIT; + const buffer = new Uint8Array(Math.ceil(bitLength / BYTELENGTH)); + + /* Add pairs of 4-bits to the buffer */ + for (let i = 0; i < buffer.length; i++) { + const d = i * 2; + buffer[i] |= parseInt(bdxString.substring(d, d + 1) + '0', HEXRADIX); + buffer[i] |= parseInt('0' + bdxString.substring(d + 1, d + 2), HEXRADIX); + } + /* Return the resulting binary buffer */ + return BinaryBuffer.fromU8a(buffer, bitLength); +} +/** + * Decodes a binary buffer with the compressed encoded field + * to a field string with the (hexa)decimal value + * @private + * @param buffer a binary buffer with the encoded field + * @return a string with the decoded (hexa)decimal field value + */ +function decodeBDX(buffer: BinaryBuffer): string { + const bitLength = buffer.length - (buffer.length % QUADBIT); + const byteArray = buffer.extractU8a(0, bitLength); + let bdxString: string = ''; + + /* Loop through the bits in the binary buffer */ + for (let bitIndex = 0; bitIndex < bitLength; bitIndex += BYTELENGTH) { + const byteCursor = Math.floor(bitIndex / BYTELENGTH); + + /* Add first 4 bits of the byte to the string */ + const byte = (byteArray[byteCursor] >> QUADBIT) & 0xF; + bdxString += byte.toString(HEXRADIX); + + /* Add second 4 bits of byte to the string */ + if ((bitIndex + QUADBIT) < bitLength) { + const byte = byteArray[byteCursor] & 0xF; + bdxString += byte.toString(HEXRADIX); + } + } + /* Return the resulting string */ + return bdxString.toLowerCase(); +} +/** + * Encodes a field with 1-byte UTF8-8 characters + * to a binary buffer with the compressed encoded field + * @private + * @param utfString an unencoded/uncompressed UTF8 text field + * @returns a binary buffer with the encoded field + */ +function encodeUTF(utfString: string): BinaryBuffer { + /* Each character need a byte */ + const bitLength = utfString.length * BYTELENGTH; + const buffer = new Uint8Array(utfString.length); + + /* Add the character code per byte */ + for (let i = 0; i < buffer.length; i++) { + buffer[i] = utfString.charCodeAt(i); + } + /* Return the resulting binary buffer */ + return BinaryBuffer.fromU8a(buffer, bitLength); +} +/** + * Decodes a binary buffer with the compressed encoded field + * to a field string with UTF8 text + * @private + * @param buffer a binary buffer with the encoded field + * @return a string with the decoded UTF8 text field value + */ +function decodeUTF(buffer: BinaryBuffer): string { + const bitLength = buffer.length - (buffer.length % BYTELENGTH); + return String.fromCharCode(...buffer.extractU8a(0, bitLength)); +} +/** + * Encodes a field with a datetime, time periode and latlong coordinates + * to a binary buffer with the compressed encoded field + * @private + * @param datumStr an unencoded/uncompressed datum field + * @returns a binary buffer with the encoded field + */ +function encodeDatum(datumStr: string): BinaryBuffer { + /* Encode field as BDX without fixed characters */ + return encodeBDX(datumStr.replace(/[-+:.A-Z]/g, NOCHAR)); +} +/** + * Decodes a binary buffer with the compressed encoded field + * to a field string with a datetime, time periode and latlong values + * without the fixed characters + * @private + * @param buffer a binary buffer with the encoded datum field + * @returns a string with the decoded datum field value + */ +function decodeDatum(buffer: BinaryBuffer): string { + /* Decode field as BDX without fixed characters */ + return decodeBDX(buffer); +} +/** + * Decodes a binary buffer with the compressed encoded datetime field + * to a field string with a datetime value with fixed characters + * @private + * @param buffer a binary buffer with the encoded datetime field + * @returns a string with the decoded datetime field value + */ +function decodeDatetime(buffer: BinaryBuffer): string { + const value = decodeDatum(buffer); + return [ + value.slice(0, 4), '-', + value.slice(4, 6), '-', + value.slice(6, 8), 'T', + value.slice(8, 10), ':', + value.slice(10, 12), ':', + value.slice(12), 'Z' + ].join(NOCHAR); +} +/** + * Decodes a binary buffer with the compressed encoded duration field + * to a field string with a duration value with fixed characters + * @private + * @param buffer a binary buffer with the encoded duration field + * @returns a string with the decoded duration field value + */ +function decodeDuration(buffer: BinaryBuffer): string { + const value = decodeDatum(buffer); + return [ + 'P', + value.slice(0, 2), 'D', + value.slice(2, 4), 'H', + value.slice(4), 'M' + ].join(NOCHAR); +} +/** + * Encodes a field with latlong coordinates + * to a binary buffer with the compressed encoded field + * @private + * @param latlongStr an unencoded/uncompressed latlong field + * @returns a binary buffer with the encoded field + */ +function encodeLatLong(latlongStr: string): BinaryBuffer { + /* Encode field as BDX without fixed characters */ + let buffer = encodeDatum(latlongStr); + + /* Sign of latlong coordinates */ + if (latlongStr.startsWith('-')) { + buffer.insertBytes([0x00], 1); + }; + if (latlongStr.startsWith('+')) { + buffer.insertBytes([0x80], 1); + } + /* Return the resulting binary buffer */ + return buffer; +} +/** + * Decodes a binary buffer with the compressed encoded latlong field + * to a field string with a latlong value without the fixed characters + * @private + * @param buffer a binary buffer with the encoded latlong field + * @returns a string with the decoded latlong value + */ +function decodeLatLong(buffer: BinaryBuffer): string { + /* Decode field as BDX without first bit */ + const latlongStr = decodeDatum(buffer.extract(1, buffer.length)); + + /* Sign of latlong coordinates */ + if (buffer.extractU8a(0,1)[0] === 0x80) { + return '+' + latlongStr; + } + if (buffer.extractU8a(0,1)[0] === 0x00) { + return '-' + latlongStr; + } + throw new SyntaxError('Invalid latlong encoding'); +} +/** + * Decodes a binary buffer with the compressed encoded latitude field + * to a field string with a latitude value with the fixed characters + * @private + * @param buffer a binary buffer with the encoded latitude field + * @returns a string with the decoded latitude value + */ + +function decodeLat(buffer: BinaryBuffer): string { + const value = decodeLatLong(buffer); + return [ + value.slice(0, 3), '.', + value.slice(3) + ].join(NOCHAR); +} +/** + * Decodes a binary buffer with the compressed encoded longitude field + * to a field string with a longitude value with the fixed characters + * @private + * @param buffer a binary buffer with the encoded longitude field + * @returns a string with the decoded longitude value + */ +function decodeLong(buffer: BinaryBuffer): string { + const value = decodeLatLong(buffer); + return [ + value.slice(0, 4), '.', + value.slice(4) + ].join(NOCHAR); +} diff --git a/src/core/lib/errors.ts b/src/core/lib/errors.ts index c77b8de7..887d0375 100644 --- a/src/core/lib/errors.ts +++ b/src/core/lib/errors.ts @@ -1,75 +1,84 @@ +'use strict'; /** * @module core/errors * @summary Whiteflag JS error classes * @document docs/md/errors.md */ -export { WfProtocolError }; +export { + WfProtocolError, + WfErrorCode, + catchedError +}; +/* MODULE DECLARATIONS */ /** - * for Whiteflag protocol and message errors + * Error class for Whiteflag protocol and message errors * @class ProtocolError * @extends {DomainError} */ class WfProtocolError extends Error { - /** - * @property {string} code The Whiteflag protocol error code - */ - code: string; - - /** - * @property {Array} causes Underlying causes of the error - */ - causes: string[]; + /* CLASS PROPERTIES */ + + /** The Whiteflag protocol error code */ + public code: string; + /** Underlying causes of the error */ + public causes: string[]; /** * Constructor for protocol errors - * @param {string} message a human readable error message - * @param {Array} causes underlying errors causing this error - * @param {WfErrorCode} code constant identifying the error + * @param message a human readable error message + * @param causes underlying errors causing this error + * @param code constant identifying the error */ - constructor(message: string, causes: string[], code: WfErrorCode = WfErrorCode.PROTOCOL) { + constructor(message: string, causes: any, code: WfErrorCode = WfErrorCode.PROTOCOL) { super(message); - this.name = 'ProtocolError'; + this.name = 'WfProtocolError'; this.code = code; - if (Array.isArray(causes)) { - this.causes = causes; - } else { - this.causes = [ causes ]; - } + + /* Process causes */ + this.causes = []; + if (Array.isArray(causes)) this.causes = causes; + if (causes instanceof Error) this.causes = [ causes.message ]; + if (typeof causes === 'string') this.causes = [ causes ]; } } - /** * Defines Whiteflag protocol errors * @enum WfErrorCode */ enum WfErrorCode { - /** - * Generic Whiteflag protocol error - */ + /** Generic Whiteflag protocol error */ PROTOCOL = 'WF_PROTOCOL_ERROR', - /** - * Incorrect or missingWhiteflag message meta data - */ + /** Incorrect or missingWhiteflag message meta data */ METAHEADER = 'WF_METAHEADER_ERROR', - /** - * Whiteflag message format error - */ + /** Whiteflag message format error */ FORMAT = 'WF_FORMAT_ERROR', - /** - * Whiteflag message reference error - */ + /** Whiteflag message reference error */ REFERENCE = 'WF_REFERENCE_ERROR', - /** - * Whiteflag message authentication error - */ + /** Whiteflag message authentication error */ AUTHENTICATION = 'WF_AUTH_ERROR', - /** - * Whiteflag signature error - */ + /** Whiteflag signature error */ SIGNATURE = 'WF_SIGN_ERROR', - /** - * Whiteflag encryption error - */ + /** Whiteflag encryption error */ ENCRYPTION = 'WF_ENCRYPTION_ERROR' -} \ No newline at end of file +} + +/* MODULE FUNCTIONS */ +/** + * Processes a catched error in a type safe manner + * @param msg a generic message to use if no specific error message + * @param err the error to handle + * @returns a new error object + */ +function catchedError(msg: string = 'Unspecified error', err: any = new Error('Unspecified error')) { + let code: WfErrorCode = WfErrorCode.PROTOCOL; + let message: string = msg; + let causes: string[] = []; + if (err instanceof Error) causes = [ err.message ]; + if (err instanceof WfProtocolError) { + msg = err.message; + causes = err.causes; + code = err.code as WfErrorCode; + } + return new WfProtocolError(message, causes, code); +} diff --git a/src/core/lib/message.ts b/src/core/lib/message.ts index 8f2f14b6..781f5e62 100644 --- a/src/core/lib/message.ts +++ b/src/core/lib/message.ts @@ -1,35 +1,937 @@ +'use strict'; /** * @module core/message * @summary Whiteflag JS message class */ -export { WfMessage }; +export { + WfMsgType, + WfCoreMessage, + isValidMessage, + validateMessage, + encryptMessage, + decryptMessage +}; + +/* Dependencies */ +import { WfCryptoMethod, encrypt, decrypt, deriveKey } from '@whiteflagprotocol/crypto'; +import { BinaryBuffer, hexToU8a, isString } from '@whiteflagprotocol/util'; + +/* Module imports */ +import { WfVersion } from './versions.ts'; +import { WfProtocolError, WfErrorCode } from './errors.ts'; +import { WfCodec, decodeField, encodeField, isValidValue } from './codec.ts'; + +/* Whiteflag specification */ +import msgSpec_v1 from '../static/v1/wf-msg-structure.json' with { type: 'json' }; + +/* Constants */ +const EMPTYPSTRING = ''; +const MSG_PREFIX = 'WF'; +const MSG_NOENCRYPT = '0'; + +/* MODULE DECLARATIONS */ +/** + * Whiteflag message types, defining the types of Whiteflag message + * as specified by the Whiteflag standard + * @enum WfFieldType + * @wfversion v1-draft.7 + * @wfreference 2.4.2.1 Functional Messages, 2.4.2.2 Management Messages + * + */ +enum WfMsgType { + /** Authentication message */ + A = 'A', + /** Cryptographic support message */ + K = 'K', + /** Test message */ + T = 'T', + /** Protection sign */ + P = 'P', + /** Protection sign */ + D = 'D', + /** Status signal */ + S = 'S', + /** Emergency signal */ + E = 'E', + /** Infrstructure sign */ + I = 'I', + /** Mission signal */ + M = 'M', + /** Request signal */ + Q = 'Q', + /** Reference message */ + R = 'R', + /** Free text message */ + F = 'F' +} +/** + * Whiteflag message specification each message type + */ +const MSGSPEC = compileMsgSpec(); /** - * Whiteflag Message - * @class WfMessage + * A core Whiteflag message as defined by the Whiteflag specification + * @class WfCoreMessage + * @wfversion v1-draft.7 + * @wfreference 4 Message Format + * @remarks Ths class represents a core Whiteflag message as + * defined by the Whiteflag specification. It has a message header and + * a message body which contain the message fields as specified for the + * message type. It performs the encoding/encryption and decoding/decryption + * to and from binary messages. Since the processing of Whiteflag messges + * in accordance with the protocol requires additional metadata, the extrended + * `WfMessage` class of the `@whitelag/protol` package should normallly be + * used instead of this class. */ -class WfMessage { +class WfCoreMessage { + /* CLASS PROPERTIES */ + + /** The message type */ + private type: WfMsgType; + /** The Whiteflag protocol version */ + private version: WfVersion = WfVersion.v1; + /** The message header containing the generic header fields */ + private header: WfMsgHeader = {}; + /** The message body containing the message type specific body fields */ + private body: WfMsgBody = {}; + /** The binary encoded message */ + private binary: BinaryBuffer = BinaryBuffer.empty(); + /** Indicates if message is final and cannot be altered */ + private final: boolean = false; + + /* CONSTRUCTOR */ /** - * @property {Object} header The message header + * Constructor for a Whiteflag message + * @param type the Whiteflag message type + * @param version the Whiteflag protocol version + * @param binary the binary encoded message, if available at creation */ - MetaHeader: Object; + constructor(type: WfMsgType, version = WfVersion.v1, binary?: BinaryBuffer) { + this.type = type; + this.version = version; + this.header = this.generateHeader(); + this.body = this.generateBody(); + if (binary instanceof BinaryBuffer) this.binary = binary; + } + + /* STATIC FACTORY METHODS */ /** - * @property {Object} header The message header + * Creates new Whiteflag message from a binary buffer + * @function fromBinary + * @param message a binary buffer with the encoded message + * @param ikm the input key material to derive the encryption key, if the message is encrypted + * @param address the binary encoded originator address, if the message is encrypted + * @param iv the initialisation vector, if required for the encryption method + * @returns a new Whiteflag message object with the decoded message */ - MessageHeader: Object; + public static async fromBinary(message: BinaryBuffer, ikm?: Uint8Array, address?: Uint8Array, iv?: Uint8Array): Promise { + let buffer = message; + + /* Decode and check unencrypted header */ + const { prefix, version, encryption } = extractUnencryptedHeader(buffer); + if (!checkPrefix(prefix)) { + throw new WfProtocolError(`Message has no ${MSG_PREFIX} prefix`, null, WfErrorCode.FORMAT); + } + if (!checkVersion(version)) { + throw new WfProtocolError(`Undefined protocol version: ${version}`, null, WfErrorCode.FORMAT); + } + if (!checkEncryption(encryption)) { + throw new WfProtocolError(`Undefined encryption method: ${encryption}`, null, WfErrorCode.ENCRYPTION); + } + /* Decrypt binary message if necessary */ + if (encryption !== MSG_NOENCRYPT) { + if (!ikm) throw new Error('Missing encryption key'); + if (!address) throw new Error('Missing orginator address'); + buffer = await decryptMessage(message, encryption as WfCryptoMethod, ikm, address, iv, version as WfVersion); + } + /* Decode message type */ + let type = extractHeaderField(buffer, 'MessageCode') as WfMsgType; + if (!checkType(type)) { + throw new WfProtocolError(`Undefined message type: ${type}`, null, WfErrorCode.FORMAT); + } + /* Create and decode new message object */ + const wfMessage = new this(type, version as WfVersion, message); + return wfMessage.decode(buffer); + } /** - * @property {Array} causes Underlying causes of the error + * Creates new Whiteflag message from a plain object + * @function fromObject + * @param message a plain JavaScript object with message header and body + * @returns a new Whiteflag message object */ - MessageBody: Object; + public static async fromObject(message: any): Promise { + /* Check object */ + const errors = validateMessage(message); + if (errors.length > 0) throw new WfProtocolError('Invalid message', errors, WfErrorCode.FORMAT); + + /* Create new WfCoreMessage object */ + const header = message.MessageHeader; + const body = message.MessageBody; + const wfMessage = new this(header['MessageCode'], header['Version']); + /* Set header fields */ + for (const field of Object.keys(header)) { + if (!wfMessage.set(field, header[field])) { + throw new WfProtocolError(`Header field ${field} could not be set`, null, WfErrorCode.FORMAT); + } + } + /* Set body fields */ + for (const field of Object.keys(body)) { + if (!wfMessage.set(field, body[field])) { + throw new WfProtocolError(`Body field ${field} could not be set`, null, WfErrorCode.FORMAT); + } + } + return wfMessage; + } /** - * Constructor for a Whiteflag message - * @param version the version of the used Whiteflag specification + * Creates new Whiteflag message from a hexadecimal encoded string + * @param message atring with the hexadecimal encoded message + * @param ikm the hexadecimalinput key material to derive the encryption key, if the message is encrypted + * @param address the hexadecimal encoded originator address, if the message is encrypted + * @param iv the hexadecimal initialisation vector, if required for the encryption method + * @returns a new Whiteflag message object with the decoded message */ - constructor(version: number) { - this.MetaHeader = {}; - this.MessageHeader = {}; - this.MessageBody = {}; + public static async fromHex(message: string, ikm?: string, address?: string, iv?: string): Promise { + /* Convert hexadecimal encryption paramters if present */ + if (ikm && address && iv) { + return this.fromBinary( + BinaryBuffer.fromHex(message), + hexToU8a(ikm), + hexToU8a(address), + hexToU8a(iv) + ); + } + /* No encryption paramters */ + return this.fromBinary( + BinaryBuffer.fromHex(message), + ); + } + /** + * Creates new Whiteflag message from a binary encoded message + * @param message a Uint8Array with the binary encoded message + * @param ikm the input key material to derive the encryption key, if the message is encrypted + * @param address the binary encoded originator address, if the message is encrypted + * @param iv the initialisation vector, if required for the encryption method + * @returns a new Whiteflag message object with the decoded message + */ + public static async fromU8a(message: Uint8Array, ikm?: Uint8Array, address?: Uint8Array, iv?: Uint8Array): Promise { + return this.fromBinary(BinaryBuffer.fromU8a(message), ikm, address, iv); + } + + /* PUBLIC CLASS METHODS */ + /** + * Indicates if the message has already been encoded + * @function isEncoded + * @returns true if message has been encoded, else false + */ + public isEncoded(): boolean { + if (this.final) return true; + return false; + } + /** + * Indicates if the message is valid, i.e. if all fields contain valid values + * @function isValid + * @returns true if message is valid, else false + */ + public isValid(): boolean { + if (this.validate().length > 0) return false; + return true; + } + /** + * Returns message validation errors + * @function validate + * @returns an array of validation errors + */ + public validate(): string[] { + return checkMsgSegments(this.header, this.body); + } + /** + * Decodes an unencrypted binary encoded message + * @param message a binary encoded message + * @returns a new Whiteflag message object + */ + public async decode(message: BinaryBuffer): Promise { + if (!this.final) { + /* Get message specification */ + let errors: string[] = []; + let msgSpec = MSGSPEC[this.type][this.version]; + + /* Decode and set header fields */ + for (const field of Object.keys(msgSpec.header)) { + if (!this.set(field, this.decodeHeaderField(message, field))) { + errors.push(`Header field ${field} could not be set`); + } + } + /* If test message, add pseudo message body */ + let type = this.type; + let offset = 0; + if (type === WfMsgType.T) { + const field = 'PseudoMessageCode'; + if (this.set(field, this.decodeBodyField(message, field))) { + const fieldSpec = MSGSPEC[type][this.version].body[field]; + offset = fieldSpec.endBit - fieldSpec.startBit; + type = this.body[field] as WfMsgType; + } else { + errors.push(`Body field ${field} could not be set`); + } + } + /* Decode and set body fields */ + msgSpec = MSGSPEC[type][this.version]; + for (const field of Object.keys(msgSpec.body)) { + if (!this.set(field, this.decodeBodyField(message, field, type, offset))) { + errors.push(`Body field ${field} could not be set`); + } + } + /* Final validation check */ + if (errors.length === 0) errors = this.validate(); + if (errors.length > 0) { + throw new WfProtocolError(`Cannot decode ${this.type} message`, errors, WfErrorCode.FORMAT); + } + this.final = true; + } + return this; + } + /** + * Encodes the message, making the contents final + * @function encode + * @param ikm the input key material to derive the encryption key, if the message is to be encrypted + * @param address the binary encoded originator address, if the message is to be encrypted + * @param iv the initialisation vector, if required for the encryption method + * @returns this Whitedlag message object with the encoded message + */ + public async encode(ikm?: Uint8Array, address?: Uint8Array, iv?: Uint8Array): Promise { + if (!this.final) { + /* Validate message before encoding */ + const errors = this.validate(); + if (errors.length > 0) { + throw new WfProtocolError('Cannot encode message', errors, WfErrorCode.FORMAT); + } + /* Encode message header */ + for (const field of Object.keys(this.header)) { + const encoding = MSGSPEC[this.type][this.version].header[field].encoding as WfCodec; + this.binary.append(encodeField(this.header[field], encoding, this.version)); + } + /* Encode message body */ + let type = this.type; + for (const field of Object.keys(this.body)) { + const encoding = MSGSPEC[type][this.version].body[field].encoding as WfCodec; + this.binary.append(encodeField(this.body[field], encoding, this.version)); + + /* If pseudo message code, treat rest of body as pseudo message type */ + if (field === 'PseudoMessageCode') type = this.body[field] as WfMsgType; + } + /* Encrypt message if encryption indicator is set */ + if (this.header['EncryptionIndicator'] !== MSG_NOENCRYPT) { + if (!ikm) throw new Error('Missing encryption key'); + if (!address) throw new Error('Missing orginator address'); + this.binary = await encryptMessage( + this.binary, + this.header['EncryptionIndicator'] as WfCryptoMethod, + ikm as Uint8Array, + address as Uint8Array, + iv as Uint8Array, + this.header['Version'] as WfVersion + ); + } + this.final = true; + } + return this; + } + /** + * Returns the value of the specified message field + * @function get + * @param fieldName the name of the message field + * @returns the value of the message field + */ + public get(fieldName: string): string | null { + /* Look for field in message header */ + for (const field of Object.keys(this.header)) { + if (field === fieldName) return this.header[field]; + } + /* Look for field in message body */ + for (const field of Object.keys(this.body)) { + if (field === fieldName) return this.body[field]; + } + /* Specified field not found */ + return null; + } + /** + * Sets the value of the specified message field, if the message has not been encoded + * @function set + * @param fieldName the name of the message field + * @param value the value to set + * @return true if succesful, else false + */ + public set(fieldName: string, value: string): boolean { + /* Cannot change data if already encoded */ + if (this.final) return false; + + /* Look for field to set value in message header */ + for (const field of Object.keys(this.header)) { + if (field === fieldName) { + /* Cannot change message prefix, version and type */ + if (field === 'Prefix' && value !== MSG_PREFIX) return false; + if (field === 'Version' && value !== this.header[field]) return false; + if (field === 'MessageCode' && value !== this.header[field]) return false; + + /* Set field value */ + this.header[field] = value; + return true; + } + } + /* Look for field to set value in message body */ + for (const field of Object.keys(this.body)) { + if (field === fieldName) { + if (field === 'PseudoMessageCode') { + /* Create new pseudo message body */ + this.body = this.generateBody(value as WfMsgType); + } else { + /* Set field value */ + this.body[field] = value; + } + return true; + } + } + /* Specified field not found */ + return false; + } + /** + * Returns the Whiteflag message as a plain object + * @function toObject + * @returns the message as a plain object + */ + public toObject(): Object { + return { + MessageHeader: this.header, + MessageBody: this.body + } + } + /** + * Returns the Whiteflag message as a string + * @function toString + * @returns a concatinated string of field values + */ + public toString(): string { + let messageStr: string = EMPTYPSTRING; + if (this.isValid()) { + /* Serialise message header */ + for (const field of Object.keys(this.header)) { + messageStr += this.header[field]; + } + /* Serialise message body */ + for (const field of Object.keys(this.body)) { + messageStr += this.body[field]; + } + } + return messageStr; + } + /** + * Returns the Whiteflag message encoded as a hexadecimal string + * @function toHex + * @returns a hexadecimal string with the encoded message + */ + public toHex(): string { + if (this.final) return this.binary.toHex(); + return EMPTYPSTRING; + } + /** + * Returns the encoded Whiteflag message as a UInt8array + * @function toU8a + * @returns a UInt8array with the encoded message + */ + public toU8a(): Uint8Array { + if (this.final) return this.binary.toU8a(); + return new Uint8Array(0); + } + + /* PRIVATE CLASS METHODS */ + /** + * Generates message header and sets known values + * @private + * @returns a Whiteflag message header object + */ + private generateHeader(): WfMsgHeader { + let header: WfMsgHeader = {}; + for (const field of Object.keys(MSGSPEC[this.type][this.version].header)) { + header[field] = EMPTYPSTRING; + } + header['Prefix'] = MSG_PREFIX; + header['Version'] = this.version as string; + header['MessageCode'] = this.type as string; + return header; + } + /** + * Decodes a field from a binary encoded message header + * @param message the binary encoded message + * @param field the header field to decode + * @returns the field value + */ + private decodeHeaderField(message: BinaryBuffer, field: string): string { + const msgSpec = MSGSPEC[this.type][this.version]; + return decodeField( + message.extract( + msgSpec.header[field]?.startBit, + msgSpec.header[field]?.endBit + ), + msgSpec.header[field]?.encoding as WfCodec); + } + /** + * Generates message body, or if specifed pseudo message body + * @private + * @@param type the pseudo message type, if test message body + * @returns a Whiteflag message body object + */ + private generateBody(pseudoType?: WfMsgType): WfMsgBody { + let body: WfMsgBody = {}; + let type = this.type; + if (checkType(pseudoType)) { + body['PseudoMessageCode'] = pseudoType as string; + type = pseudoType as WfMsgType; + } + for (const field of Object.keys(MSGSPEC[type][this.version].body)) { + body[field] = EMPTYPSTRING; + } + return body; + } + /** + * Decodes a field from a binary encoded message body + * @param message the binary encoded message + * @param field the body field to decode + * @param type the message type to override, e.g. for pseudo message body + * @param bitOffset the bit offset for dynamic fields + * @returns the field value + */ + private decodeBodyField(message: BinaryBuffer, field: string, type: WfMsgType = this.type, bitOffset: number = 0): string { + const msgSpec = MSGSPEC[type][this.version]; + return decodeField(message.extract( + msgSpec.body[field]?.startBit + bitOffset, + msgSpec.body[field]?.endBit + bitOffset + ), msgSpec.body[field]?.encoding as WfCodec); + } +} + +/* MODULE FUNCTIONS */ +/** + * Checks if an object is a valid Whiteflag message + * @function isValidMessage + * @param message the message object to validate + * @returns true if message is valid, else false + */ +function isValidMessage(message: any): boolean { + if (validateMessage(message).length > 0) return false; + return true; +} +/** + * Checks a message object for validation errors + * @function validateMessage + * @param message the message object to validate + * @returns an array of validation errors + */ +function validateMessage(message: any): string[] { + /* Check object */ + if (!(message instanceof Object)) throw new TypeError('Not an object'); + if (message instanceof WfCoreMessage) return message.validate(); + + /* Check if message header and body exist */ + let errors: string[] = []; + if (!message?.MessageHeader || !(message?.MessageHeader instanceof Object)) { + errors.push('Missing or invalid message header'); + } + if (!message?.MessageBody || !(message?.MessageBody instanceof Object)) { + errors.push('Missing or invalid message body'); } + if (errors.length > 0) return errors; + + /* Check message header and body fields */ + errors.push(...checkMsgSegments(message.MessageHeader, message.MessageBody)); + return errors; +} +/** + * Encrypts a binary encoded message + * @function encryptMessage + * @param message a binary buffer with the binary encoded message + * @param method the Whiteflag encryption method + * @param ikm the input key material to derive the encryption key + * @param address the binary encoded originator address + * @param iv the initialisation vector, if required for the encryption method + * @param version the Whiteflag protocol version + * @returns the encrypted message + */ +async function encryptMessage(message: BinaryBuffer, + method: WfCryptoMethod, + ikm: Uint8Array, + address: Uint8Array, + iv?: Uint8Array, + version = WfVersion.v1 + ): Promise { + /* Split message in unencrypted and encrypted part */ + const { unencrypted, encrypted: decrypted } = splitEncryptedMsg(message); + + /* Derive encryption key */ + const key = await deriveKey( + ikm as Uint8Array, + method as WfCryptoMethod, + address as Uint8Array, + version as WfVersion + ); + /* Encrypt the message part */ + const encrypted = await encrypt( + decrypted as Uint8Array, + method as WfCryptoMethod, + key as CryptoKey, + iv as Uint8Array, + version as WfVersion + ); + /* Merge parts and return result */ + return mergeEncryptedMsg(unencrypted, encrypted); +} +/** + * Decrypts an encrypted binary message + * @function decryptMessage + * @param message a binary buffer with the encrypted message + * @param method the Whiteflag encryption method + * @param ikm the input key material to derive the encryption key + * @param address the binary encoded originator address + * @param iv the initialisation vector, if required for the encryption method + * @param version the Whiteflag protocol version + * @returns the decrypted binary encoded message + */ +async function decryptMessage(message: BinaryBuffer, + method: WfCryptoMethod, + ikm: Uint8Array, + address: Uint8Array, + iv?: Uint8Array, + version = WfVersion.v1 + ): Promise { + /* Split message in unencrypted and encrypted part */ + const { unencrypted, encrypted } = splitEncryptedMsg(message); + /* Derive encryption key */ + const key = await deriveKey( + ikm as Uint8Array, + method as WfCryptoMethod, + address as Uint8Array, + version as WfVersion + ); + /* Decrypt encrypted message part */ + const decrypted = await decrypt( + encrypted as Uint8Array, + method as WfCryptoMethod, + key as CryptoKey, + iv as Uint8Array, + version as WfVersion + ); + /* Merge parts and return result */ + return mergeEncryptedMsg(unencrypted, decrypted); +} + +/* PRIVATE MODULE DECLARATIONS */ +/** + * Defines a Whiteflag message header object + * @private + * @interface WfMsgHeader + */ +interface WfMsgHeader { + /* Required for dynamic creation */ + [key: string]: any, + /* Defined header fields + * for all versions */ + Prefix?: string, + Version?: string, + EncryptionIndicator?: string, + DuressIndicator?: string, + MessageCode?: string, + ReferenceIndicator?: string, + ReferencedMessage?: string +} +/** + * Defines a Whiteflag message header object + * @private + * @interface WfMsgHeader + */ +interface WfMsgBody { + /* Required for dynamic creation */ + [key: string]: any, + /* Defined body fields + * for all versions and message types */ + VerificationMethod?: string, + VerificationData?: string, + CryptoDataType?: string, + CryptoData?: string, + PseudoMessageCode?: string, + SubjectCode?: string, + DateTime?: string, + Duration?: string, + ObjectType?: string, + ObjectLatitude?: string, + ObjectLongitude?: string, + ObjectSizeDim1?: string, + ObjectSizeDim2?: string, + ObjectOrientation?: string, + ReferenceMethod?: string, + ReferenceData?: string, + Text?: string +} +/** + * Defines an object with field type definitions + * @private + * @interface WfMsgSpec + */ +interface WfMsgSpec { + [key: string]: { // Message type + [key: string]: { // Whiteflag version + header: { + [key: string]: { // Message header field with start bit + encoding: string, + startBit: number, + endBit: number + } + }, + body: { + [key: string]: { // Message body field with start bit + encoding: string, + startBit: number, + endBit: number + } + } + } + } +} + +/* PRIVATE MODULE FUNCTIONS */ +/** + * Compiles an object with all valid field type definitions + * @private + * @returns an object with field type definitions + */ +function compileMsgSpec(): WfMsgSpec { + const SIGNSIGNALTYPE = '$signsignal'; + const msgSpec: WfMsgSpec = {}; + for (const type of Object.values(WfMsgType)) { + msgSpec[type] = {}; + + /* Whiteflag version 1 */ { + const version = WfVersion.v1; + const headerSpec_v1 = compileMsgSpecRegex(msgSpec_v1.header); + const signsignalSpec_v1 = compileMsgSpecRegex(msgSpec_v1.body[SIGNSIGNALTYPE]); + msgSpec[type][version] = { header: {}, body: {} }; + msgSpec[type][version].header = headerSpec_v1; + if (SIGNSIGNALTYPE in msgSpec_v1.body[type]) { + msgSpec[type][version].body = signsignalSpec_v1; + } else { + msgSpec[type][version].body = compileMsgSpecRegex(msgSpec_v1.body[type]); + } + } + } + /* All done */ + return msgSpec; +} +/** + * Compiles regular expression if pattern is defined for a field + * @private + * @param segSpec the message segment specification + * @returns the message segment specification with compiled regex + */ +function compileMsgSpecRegex(segSpec: any): any { + for (const field of Object.keys(segSpec)) { + if (segSpec[field]?.pattern) { + segSpec[field].regex = new RegExp(segSpec[field].pattern); + } + } + return segSpec; +} +/** + * Checks the message header and body + * @private + * @param header the message header + * @param body the message body + * @returns an array of validation errors + */ +function checkMsgSegments(header: WfMsgHeader, body: WfMsgBody): string[] { + /* Check message version and type */ + let errors: string[] = []; + if (!('Version'in header)) errors.push('Missing protocol version'); + if (!('MessageCode'in header)) errors.push('Missing message type code'); + if (errors.length > 0) return errors; + + /* Validate message header and body */ + errors.push(...checkMsgHeader( + header, header['MessageCode'] as WfMsgType, header['Version'] as WfVersion + )); + errors.push(...checkMsgBody( + body, header['MessageCode'] as WfMsgType, header['Version'] as WfVersion + )); + return errors; +} +/** + * Checks the message header + * @private + * @param header the message header + * @param type the message type + * @param version the Whiteflag protocol version + * @returns an array of validation errors + */ +function checkMsgHeader(header: WfMsgHeader, type: WfMsgType, version = WfVersion.v1): string[] { + return checkFields(header, MSGSPEC[type][version].header, version); +} +/** + * Checks the message body + * @private + * @param body the message body + * @param type the message type + * @param version the Whiteflag protocol version + * @returns an array of validation errors + */ +function checkMsgBody(body: WfMsgBody, type: WfMsgType, version = WfVersion.v1): string[] { + return checkFields(body, MSGSPEC[type][version].body, version); +} +/** + * Checks the fields of a message segment (header or body) + * @private + * @param segment the message header or body + * @param segSpec the message segment specification + * @param version the Whiteflag protocol version + * @returns an array of validation errors + */ +function checkFields(segment: (WfMsgHeader | WfMsgBody), segSpec: any, version = WfVersion.v1): string[] { + let errors: string[] = []; + for (const field of Object.keys(segSpec)) { + /* Check if field exists */ + if (!Object.hasOwn(segment, field)) { + errors.push(`Missing ${field} field`); + continue; + } + /* Check field value */ + if (segSpec[field].encoding === EMPTYPSTRING) { + continue; + } + if (segment[field] === EMPTYPSTRING) { + errors.push(`${field} field has no value`); + continue; + } + /* Specific pattern for field defined in message specification */ + if (segSpec[field]?.regex instanceof RegExp) { + if (!segSpec[field].regex.test(segment[field])) { + errors.push(`Value of ${field} field does not match ${segSpec[field].pattern} pattern`) + } + continue; + } + /* Generic pattern for field based on field type */ + const encoding = segSpec[field].encoding as WfCodec; + if (!isValidValue(segment[field], encoding, version)) { + errors.push(`Value of ${field} field is not valid for ${encoding} encoding`); + } + } + return errors; +} +/** + * Check if encryption method is valid + * @private + * @param method the encryption method to check + * @returns true if encryption method is valid, else false + */ +function checkEncryption(method: WfCryptoMethod | string | undefined): boolean { + if (method === undefined) return false; + if (isString(method)) { + if (method === MSG_NOENCRYPT) return true; + if (Object.keys(WfCryptoMethod).includes(method as string)) return true; + } + if (Object.values(WfCryptoMethod).includes(method as WfCryptoMethod)) return true; + return false; +} +/** + * Checks if message prefix is valid + * @private + * @param encryption the message prefix to check + * @returns true if prefix is valid, else false + */ +function checkPrefix(prefix: string | undefined): boolean { + if (prefix === undefined) return false; + if (prefix === MSG_PREFIX) return true; + return false; +} +/** + * Check if mesage type is valid + * @private + * @param type the message type to check + * @returns true if message type is valid, else false + */ +function checkType(type: WfMsgType | string | undefined): boolean { + if (type === undefined) return false; + if (isString(type) + && Object.keys(WfMsgType).includes(type as string)) return true; + if (Object.values(WfMsgType).includes(type as WfMsgType)) return true; + return false; +} +/** + * Check if protocol version is valid + * @private + * @param version the protocol version to check + * @returns true if protocol version is valid, else false + */ +function checkVersion(version: WfVersion | string | undefined): boolean { + if (version === undefined) return false; + if (isString(version) + && Object.keys(WfVersion).includes(version as string)) return true; + if (Object.values(WfVersion).includes(version as WfVersion)) return true; + return false; +} +/** + * Extracts the unecrypted header field values from an encoded message + * @private + * @param message the binary encoded message + * @returns a plain object with the prefix, version and encryption values + */ +function extractUnencryptedHeader(message: BinaryBuffer): { prefix: string, version: string, encryption: string } { + return { + prefix: extractHeaderField(message, 'Prefix'), + version: extractHeaderField(message, 'Version'), + encryption: extractHeaderField(message, 'EncryptionIndicator') + }; +} +/** + * Extracts a header field value from an encoded unknown message type + * @private + * @param message the binary encoded message + * @param field the header field to decode + * @returns the field value + */ +function extractHeaderField(message: BinaryBuffer, field: string): string { + /* Use version 1 of an A message for generic header field specification */ + const fieldSpec = MSGSPEC[WfMsgType.A][WfVersion.v1].header[field]; + + /* Decode header field */ + return decodeField( + message.extract( + fieldSpec.startBit, + fieldSpec.endBit + ), + fieldSpec.encoding as WfCodec + ); +} +/** + * Splits a binary encoded message in the unencrypted and encrypted parts + * @param message the full binary encoded messsage + * @returns the unencrypted and encrypted message parts + */ +function splitEncryptedMsg(message: BinaryBuffer): { unencrypted: Uint8Array, encrypted: Uint8Array } { + /* Use version 1 of an A message for generic header unencrypted split */ + const split = MSGSPEC[WfMsgType.A][WfVersion.v1].header['EncryptionIndicator'].endBit; + return { + unencrypted: message.extract(0, split).toU8a(), + encrypted: message.extract(split, message.length).toU8a() + } +} +/** + * Merges the unencrypted and encrypted parts of a binary encoded message + * @param unenecrypted the unencrypted message part + * @param encrypted the encrypted message part + * @returns the full binary encoded messsage + */ +function mergeEncryptedMsg(unenecrypted: Uint8Array, encrypted: Uint8Array): BinaryBuffer { + /* Use version 1 of an A message for generic header unencrypted split */ + const split = MSGSPEC[WfMsgType.A][WfVersion.v1].header['EncryptionIndicator'].endBit; + return BinaryBuffer + .fromU8a(unenecrypted, split) + .appendU8a(encrypted); } diff --git a/src/core/lib/versions.ts b/src/core/lib/versions.ts new file mode 100644 index 00000000..0bc3dbc5 --- /dev/null +++ b/src/core/lib/versions.ts @@ -0,0 +1,18 @@ +'use strict'; +/** + * @module core/versions + * @summary Whiteflag JS protocol versions + */ +export { + WfVersion +}; + +/* MODULE DECLARATIONS */ +/** + * Defines Whiteflag versions + * @enum WfVersion + */ +enum WfVersion { + /** Whiteflag version 1 */ + v1 = '1' +} diff --git a/src/core/package.json b/src/core/package.json new file mode 100644 index 00000000..392d87e2 --- /dev/null +++ b/src/core/package.json @@ -0,0 +1,31 @@ +{ + "name": "@whiteflagprotocol/core", + "version": "1.0.0", + "description": "Whiteflag JS core protocol functionality", + "license": "CC0-1.0", + "keywords": [ + "whiteflag", + "protocol", + "core", + "blockchain" + ], + "directories": { + "lib": "lib" + }, + "dependencies": { + "@whiteflagprotocol/crypto": "^1.0.0", + "@whiteflagprotocol/util": "^1.0.0" + }, + "homepage": "https://github.com/WhiteflagProtocol/whiteflag-js#readme", + "repository": { + "type": "git", + "directory": "dist/core", + "url": "https://github.com/WhiteflagProtocol/whiteflag-js.git" + }, + "bugs": { + "url": "https://github.com/WhiteflagProtocol/whiteflag-js/issues" + }, + "type": "module", + "main": "index.js", + "types": "index.d.ts" +} diff --git a/src/core/static/v1/wf-field-encoding.json b/src/core/static/v1/wf-field-encoding.json new file mode 100644 index 00000000..a15b9d1f --- /dev/null +++ b/src/core/static/v1/wf-field-encoding.json @@ -0,0 +1,35 @@ +{ + "$description": "Whiteflag protocol version 1 field encoding", + "binary": { + "pattern": "^[0-1]*$", + "length": -1 + }, + "decimal": { + "pattern": "^[0-9]*$", + "length": -1 + }, + "hexadecimal": { + "pattern": "^[a-fA-F0-9]*$", + "length": -1 + }, + "utf-8": { + "pattern": "^[\\u0000-\\u007F]*$", + "length": -1 + }, + "datetime": { + "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$", + "length": 56 + }, + "duration": { + "pattern": "^P[0-9]{2}D[0-9]{2}H[0-9]{2}M$", + "length": 24 + }, + "latitude": { + "pattern": "^[+-][0-9]{2}.[0-9]{5}$", + "length": 29 + }, + "longitude": { + "pattern": "^[+-][0-9]{3}.[0-9]{5}$", + "length": 33 + } +} \ No newline at end of file diff --git a/src/core/static/v1/wf-msg-structure.json b/src/core/static/v1/wf-msg-structure.json new file mode 100644 index 00000000..b0514511 --- /dev/null +++ b/src/core/static/v1/wf-msg-structure.json @@ -0,0 +1,153 @@ +{ + "$description": "Whiteflag protocol version 1 message structure", + "header": { + "Prefix": { + "encoding": "utf-8", + "pattern": "^WF$", + "startBit": 0, + "endBit": 16 + }, + "Version": { + "encoding": "utf-8", + "pattern": "^[A-Z0-9]{1}$", + "startBit": 16, + "endBit": 24 + }, + "EncryptionIndicator": { + "encoding": "utf-8", + "pattern": "^[A-Z0-9]{1}$", + "startBit": 24, + "endBit": 32 + }, + "DuressIndicator": { + "encoding": "binary", + "pattern": "^[0-1]{1}$", + "startBit": 32, + "endBit": 33 + }, + "MessageCode": { + "encoding": "utf-8", + "pattern": "^[A-Z]{1}$", + "startBit": 33, + "endBit": 41 + }, + "ReferenceIndicator": { + "encoding": "hexadecimal", + "pattern": "^[a-fA-F0-9]{1}$", + "startBit": 41, + "endBit": 45 + }, + "ReferencedMessage": { + "encoding": "hexadecimal", + "pattern": "^[a-fA-F0-9]{64}$", + "startBit": 45, + "endBit": 301 + } + }, + "body": { + "A": { + "VerificationMethod": { + "encoding": "hexadecimal", + "startBit": 301, + "endBit": 305 + }, + "VerificationData": { + "encoding": "utf-8", + "startBit": 305, + "endBit": -1 + } + }, + "K": { + "CryptoDataType": { + "encoding": "hexadecimal", + "startBit": 301, + "endBit": 305 + }, + "CryptoData": { + "encoding": "hexadecimal", + "startBit": 305, + "endBit": -1 + } + }, + "T": { + "PseudoMessageCode": { + "encoding": "utf-8", + "startBit": 301, + "endBit": 309 + } + }, + "R": { + "ReferenceMethod": { + "encoding": "hexadecimal", + "startBit": 301, + "endBit": 305 + }, + "ReferenceData": { + "encoding": "utf-8", + "startBit": 305, + "endBit": -1 + } + }, + "F": { + "Text": { + "encoding": "utf-8", + "startBit": 301, + "endBit": -1 + } + }, + "P": { "$signsignal": true }, + "D": { "$signsignal": true }, + "S": { "$signsignal": true }, + "E": { "$signsignal": true }, + "I": { "$signsignal": true }, + "M": { "$signsignal": true }, + "Q": { "$signsignal": true }, + "$signsignal": { + "SubjectCode": { + "encoding": "hexadecimal", + "startBit": 301, + "endBit": 309 + }, + "DateTime": { + "encoding": "datetime", + "startBit": 309, + "endBit": 365 + }, + "Duration": { + "encoding": "duration", + "startBit": 365, + "endBit": 389 + }, + "ObjectType": { + "encoding": "hexadecimal", + "startBit": 389, + "endBit": 397 + }, + "ObjectLatitude": { + "encoding": "latitude", + "startBit": 397, + "endBit": 426 + }, + "ObjectLongitude": { + "encoding": "longitude", + "startBit": 426, + "endBit": 459 + }, + "ObjectSizeDim1": { + "encoding": "decimal", + "startBit": 459, + "endBit": 475 + }, + "ObjectSizeDim2": { + "encoding": "decimal", + "startBit": 475, + "endBit": 491 + }, + "ObjectOrientation": { + "encoding": "decimal", + "startBit": 491, + "endBit": 503 + } + } + } +} \ No newline at end of file diff --git a/src/crypto/LICENSE.md b/src/crypto/LICENSE.md new file mode 100644 index 00000000..66129f4e --- /dev/null +++ b/src/crypto/LICENSE.md @@ -0,0 +1,53 @@ +# Whiteflag Software Dedication to the Public Domain + +## Preamble + +All persons and organisations that contributed to the initial development +of the Whiteflag Protocol and the Whiteflag JavaScript Library software did +so disinterestedly. + +## Whiteflag JavaScript Library CC0-1.0 Statement + +Excluding third party software that may be part of this distribution, the +Whiteflag JavaScript Library software and associated documentation is dedicated +to the public domain under the [Creative Commons CC0-1.0 Universal Public Domain Dedication](http://creativecommons.org/publicdomain/zero/1.0/) +statement, meaning that to the extent possible under law, the authors +and their organisations have waived all copyright and related or neighbouring +rights to this work, allowing anyone to copy, modify, distribute and implement +the work, even for commercial purposes, all without asking permission. + +In no way are the patent or trademark rights of any person affected by this +dedication to the public domain, nor are the rights that other persons may +have in the work or in how the work is used, such as publicity or privacy +rights. Unless expressly stated otherwise, the authors and their organisations +make no warranties about the work, and disclaim liability for all uses of the +work, to the fullest extent permitted by applicable law. When using or citing +the work, you should not imply endorsement by the authors or the affirmer. + +## Third Party Software Dependencies + +Where the Whiteflag JavaScript Library software requires third party +software packages, which are not part of this distribution. These third party +software packages need to be obtained through their respective official +distribution channels, and may be licensed. + +## Legal Restrictions for the Usage of the Whiteflag software + +WARNING: THE USAGE OF SIGNS AND SIGNALS WITH THIS SOFTWARE IS SUBJECT +TO LOCAL AND/OR INTERNATIONAL LAWS. Please take note of the following when +using this software: + +- This software only provides an additional means of communication for + certain signs and signals, and it does not replace any existing + standard, regulation or means of communication, whether mandatory or + not, including but not limited to physical signs, radio communications, + official registers, etc. + +- The usage of protective signs is subject to International Humanitarian + Law. Misuse of protective signs is a punishable violation under local + and international laws. + +- The usage of emergency signals may be subject to various regulations and + standards, depending on location and context. The misuse of emergency + signals, including misuse of the duress functionality, may be a + punishable violation under local and international laws. diff --git a/src/crypto/README.md b/src/crypto/README.md new file mode 100644 index 00000000..94597c02 --- /dev/null +++ b/src/crypto/README.md @@ -0,0 +1,79 @@ +# WFJSL Cryptographic Functions + +## Overview + +The Whiteflag JavaScript Library (WFJSL) is an implementation of the Whiteflag +Protocol written in [TypeScript](https://developer.mozilla.org/en-US/docs/Glossary/TypeScript), +and compiled to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), +to support the development of Whiteflag-enabled applications in JavaScript. + +The `@whiteflagprotocol/crypto` package provides cryptographic functions for +other Whiteflag packages. Therefore, it should normally not be necessary to +add this package as a dependency. + +The WFJSL uses the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) +for the basic implementation of Whiteflag cryptographic functions, such as +secret negotiation, encryption, and authentication. Putting these critical +security functions in a separate package makes them better inspectable, +testable and maintainable. + +Most cryptography functions are asynchronous and return a +[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). + +This description provides a generic overview of the WFJSL cryptography +package. Please see the [WFJSL TypeDoc documentation](https://js.whiteflagprotocol.org/typedoc) +for a detailed description of all classes and functions. + +## Hashing + +The Whiteflag cryptography package provides three hashing functions +with the `hash` module: + +| Function | Purpose | +|----------|---------------------------------------------------------------------------| +| `hkdf` | Hash-based Key Derivation Function using SHA-256 i.a.w. RFC 5869 | +| `hash` | Basic hashing function, using SHA-256 as default | +| `hmac` | Hash-Based Message Authentication Code function, using SHA-256 as default | + +The `hkdf` function is used by Whiteflag to derive encryption keys and +authentication tokens and bind them to the blockchain address of a specific +originator. The `hash` and `hmac` functions are wrappers easy common access +to the underlying algorithms through the Web Crypto API; they are used by +the `hkdf` function, but may also be used for other functionality as required. + +## Encryption + +The Whiteflag cryptography package provides the following functions for +message encryption with the `cipher` module: + +| Function | Purpose | +|-------------|---------------------------------------------------------------------------------------| +| `encrypt` | Encrypts a binary encoded Whiteflag message, based on the Whiteflag encryption method | +| `decrypt` | Decrypts a binary encoded Whiteflag message, based on the Whiteflag encryption method | +| `deriveKey` | Derives the encryption key based on the Whiteflag encryption method | + +The `encrypt` and `decrypt` functions take a binary encoded Whiteflag message, +along with a number of encryption parameters such as the encryption key, to +perform the encryption and decryption of messages. + +The `deriveKey` function uses the `hkdf` function with the input key material, +information parameter, salt, and key length for the encryption method, to +generate the Web Crypto API encryption key to be used with the `encrypt` and +`decrypt` functions i.a.w. the Whiteflag standard. + +The Whiteflag encryption methods are defined by the `WfCryptoMethod` enum. + +## Cryptographic Keys + +The Whiteflag cryptography package provides the following functions for +key generation with the `keys` module: + +| Function | Purpose | +|-----------------|----------------------------------------------| +| `createAesKey` | Creates an AES encryption and decryption key | +| `createHmacKey` | Creates an HMAC signing key | + +All key generation functions create a Web Crypto API `CryptoKey` object, +typically from a raw key generated or provided elsewhere. These keys are +primarily intended to provide the correct Web Crypto API keys to other +functions of the cryptography package. diff --git a/src/crypto/index.ts b/src/crypto/index.ts index 9b1b2feb..2d6e7a73 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -1,5 +1,22 @@ +'use strict'; /** - * @module + * @module crypto * @summary Whiteflag JS cryptographic classes and functions * @document docs/md/modules.md + * @primaryExport */ +export { + WfCryptoMethod, + encrypt, + decrypt, + deriveKey +} from './lib/cipher.ts'; +export { + hkdf, + hash, + hmac +} from './lib/hash.ts'; +export { + createAesKey, + createHmacKey +} from './lib/keys.ts'; diff --git a/src/crypto/lib/cipher.ts b/src/crypto/lib/cipher.ts new file mode 100644 index 00000000..99c0a65c --- /dev/null +++ b/src/crypto/lib/cipher.ts @@ -0,0 +1,242 @@ +'use strict'; +/** + * @module crypto/cipher + * @summary Whiteflag JS encryption and decryption functions + */ +export { + WfCryptoMethod, + encrypt, + decrypt, + deriveKey +}; + +/* Dependencies */ +import { WfVersion } from '@whiteflagprotocol/core'; +import { hexToU8a } from '@whiteflagprotocol/util'; + +/* Module imports */ +import { zeroise } from './common.ts'; +import { hkdf } from './hash.ts'; +import { createAesKey } from './keys.ts'; + +/* Whiteflag specification */ +import cryptoSpec_v1 from '../static/v1/wf-crypto-params.json' with { type: 'json' }; + +/* Constants */ +const BYTELENGTH = 8; + +/* MODULE DECLARATIONS */ +/** + * Whiteflag encryption methods, defining the encryption methods + * for Whiteflag messages as specified by the Whiteflag standard + * @enum WfCryptoMethod + * @wfversion v1-draft.7 + * @wfreference 5.2.4 Message Encryption + */ +enum WfCryptoMethod { + /** Whiteflag encryption method 1: negotiated key */ + ECDH = '1', + /** Whiteflag encryption method 2: pre-shared key */ + PSK = '2' +} +/** + * Whiteflag encryption parameters for each method + */ +const PARAMS = compileCryptoParams(); + +/* MODULE FUNCTIONS */ +/** + * Encrypts a message based on the specified encryption method + * @function encrypt + * @wfversion v1-draft.7 + * @wfreference 5.2.4 Message Encryption + * @param message the message to be encrypted + * @param method the Whiteflag encryption method + * @param key the input key material for the encryption key + * @param iv the initialisation vector, if required for the method + * @param version the Whiteflag protocol version + */ +async function encrypt(message: Uint8Array, + method: WfCryptoMethod, + key: CryptoKey, + iv?: Uint8Array, + version = WfVersion.v1 + ): Promise { + /* Choose encryption based on encryption method */ + switch (method) { + case WfCryptoMethod.ECDH: + case WfCryptoMethod.PSK: { + const parameters = getAesParameters(method, iv, version); + return encryptAes(message, key, parameters); + } + default: { + throw new Error(`Invalid encryption method: ${method}`); + } + } +} +/** + * Decrypts a message based on the specified encryption method + * @function decrypt + * @wfversion v1-draft.7 + * @wfreference 5.2.4 Message Encryption + * @param message the message to be decyrpted + * @param method the Whiteflag encryption method + * @param key the encryption key + * @param iv the initialisation vector, if required for the method + * @param version the Whiteflag protocol version + */ +async function decrypt(message: Uint8Array, + method: WfCryptoMethod, + key: CryptoKey, + iv?: Uint8Array, + version = WfVersion.v1 + ): Promise { + /* Choose decryption based on encryption method */ + switch (method) { + case WfCryptoMethod.ECDH: + case WfCryptoMethod.PSK: { + const parameters = getAesParameters(method, iv, version); + return decryptAes(message, key, parameters); + } + default: { + throw new Error(`Invalid encryption method: ${method}`); + } + } +} +/** + * Derives the encryption key based on the Whiteflag encryption method + * @function deriveKey + * @wfversion v1-draft.7 + * @wfreference 5.2.3 Encryption Key and Authentication Token Derivation + * @param ikm the raw input key material + * @param info information to bind the key, e.g. the blockchain address of the originator + * @param method the Whiteflag encryption method + * @param version the Whiteflag protocol version + * @returns the encryption key + */ +async function deriveKey(ikm: Uint8Array, + method: WfCryptoMethod, + info: Uint8Array, + version = WfVersion.v1 + ): Promise { + /* Derive raw key with HKDF */ + const salt = hexToU8a(PARAMS[method][version].salt); + const keyLength = PARAMS[method][version].keyLength; + const rawKey = await hkdf(ikm, salt, info, keyLength); + zeroise(ikm); + + /* Create encryption key object based on encryption method */ + switch (method) { + case WfCryptoMethod.ECDH: + case WfCryptoMethod.PSK: { + return createAesKey(rawKey, PARAMS[method][version].algorithm); + } + default: { + throw new Error(`Invalid encryption method: ${method}`); + } + } +} + +/* PRIVATE MODULE DECLARATIONS */ +/** + * Defines an object with field encoding definitions + * @private + * @interface WfFieldEncoding + */ +interface WfCryptoParams { + [key: string]: { // Encryption method + [key: string]: { // Whiteflag version + algorithm: string, // Encryption algorithm + keyLength: number, // Byte length of the encryption key + ivLength: number, // Byte length of the initialisation vector + ctrLength: number // Byte length of the counter block part used as counter + salt: string // Salt for HKDF key generation + } + } +} + +/* PRIVATE MODULE FUNCTIONS */ +/** + * Compiles an object with all valid field type definitions + * @private + * @returns an object with field type definitions + */ +function compileCryptoParams(): WfCryptoParams { + const params: WfCryptoParams = {}; + for (const method of Object.values(WfCryptoMethod)) { + params[method] = {}; + + /* Whiteflag version 1 */ { + const version = WfVersion.v1; + params[method][version] = cryptoSpec_v1[method]; + } + } + return params; +} +/** + * Encrypts data using AES + * @private + * @param data the data to be encrypted + * @param key the encryption key + * @param parameters the AES encryption parameters + * @returns the encrypted binary data + */ +async function encryptAes(data: Uint8Array, + key: CryptoKey, + parameters: AesCtrParams | AesCbcParams | AesGcmParams + ): Promise { + const encrypted = await crypto.subtle.encrypt( + parameters, key, data + ); + return new Uint8Array(encrypted); +} +/** + * Decrypts data using AES + * @private + * @param data the data to be encrypted + * @param key the encryption key + * @param parameters the AES encryption parameters + * @returns the decrypted binary data + */ +async function decryptAes(data: Uint8Array, + key: CryptoKey, + parameters: AesCtrParams | AesCbcParams | AesGcmParams + ): Promise { + const decrypted = await crypto.subtle.decrypt( + parameters, key, data + ); + return new Uint8Array(decrypted); +} +/** + * Creates the algortihm parameter object for AES based on the Whiteflag encryption method + * @private + * @param method the Whiteflag encryption method + * @param iv the initialisation vector, if required for the method + * @param version the Whiteflag protocol version + * @returns the AES algortihm parameter object + */ +function getAesParameters(method: WfCryptoMethod, + iv?: Uint8Array, + version = WfVersion.v1 + ): AesCtrParams | AesCbcParams | AesGcmParams { + /* Compile encryption parameters based on encryption method */ + switch (method) { + case WfCryptoMethod.ECDH: + case WfCryptoMethod.PSK: { + if (!iv) { + throw new Error(`Encryption method ${method} requires an initialisation vector`); + } + if (iv.length !== PARAMS[method][version].ivLength) { + throw new Error(`Invalid initialisation vector length for encryption method ${method}`); + } + return { + name: PARAMS[method][version].algorithm, + counter: iv, + length: PARAMS[method][version].ctrLength * BYTELENGTH + }; + } + default: { + throw new Error(`Invalid encryption method: ${method}`); + } + } +} diff --git a/src/crypto/lib/common.ts b/src/crypto/lib/common.ts new file mode 100644 index 00000000..6460445b --- /dev/null +++ b/src/crypto/lib/common.ts @@ -0,0 +1,19 @@ +'use strict'; +/** + * @module crypto/common + * @summary Whiteflag JS common crypto functions + */ +export { + zeroise +}; + +/* MODULE FUNCTIONS */ +/** + * Basic zeroisation function + * @function zeroise + * @param u8array typed array to zeroise + * @returns the zeroised typed array + */ +function zeroise(u8array: Uint8Array): Uint8Array { + return u8array.fill(0); +} diff --git a/src/crypto/lib/hash.ts b/src/crypto/lib/hash.ts new file mode 100644 index 00000000..a2d6d101 --- /dev/null +++ b/src/crypto/lib/hash.ts @@ -0,0 +1,97 @@ +'use strict'; +/** + * @module crypto/hash + * @summary Whiteflag JS hashing functions + */ +export { + hkdf, + hash, + hmac +}; + +/* Module imports */ +import { zeroise } from './common.ts'; +import { createHmacKey } from './keys.ts'; + +/* Constants */ +const HMAC = 'HMAC'; +const HASHALG = 'SHA-256'; +const HASHLEN = 32; + +/* MODULE FUNCTIONS */ +/** + * Hash-based Key Derivation Function using SHA-256 i.a.w. RFC 5869 + * @function hkdf + * @param ikm input key material + * @param salt salt + * @param info info + * @param keylen output key length in octets + * @returns generated key + */ +async function hkdf(ikm: Uint8Array, + salt: Uint8Array, + info: Uint8Array, + keylen: number + ): Promise> { + /* Step 1. HKDF-Extract(salt, IKM) -> PRK */ + const prk = await hmac(salt, ikm); + zeroise(ikm); + + /* Step 2. HKDF-Expand(PRK, info, L) -> OKM */ + let okm = new Uint8Array(keylen); + let t = new Uint8Array(HASHLEN); + let offset = 0; + + const N = Math.ceil(keylen / HASHLEN); + for (let i = 1; i <= N; i++) { + /* Concatinate previous hash t, info and counter i */ + let block = new Uint8Array(offset + info.length + 1); + block.set(t.slice(0, block.length)); + block.set(info.slice(0, info.length), offset); + block[offset + info.length] = i; + + /* Get hash and add to okm buffer */ + let hash = await hmac(prk, block); + t.set(hash.slice(0, t.length)) + offset = offset * (i - 1); + if (offset < okm.length) { + okm.set(hash.slice(0, (okm.length-offset)), offset); + } + /* Block contains t after after first interation */ + offset = HASHLEN; + } + /* Return output key material */ + return okm; +} +/** + * Basic hashing function + * @function hash + * @param data data to hash + * @param length the required output length in octets; default is 32 + * @param algorithm the hash algorithm to be used; default is SHA-256 + * @returns the hash value + */ +async function hash(data: Uint8Array, + length = HASHLEN, + algorithm = HASHALG + ): Promise> { + /* Create hash */ + const hash = await crypto.subtle.digest(algorithm, data); + return new Uint8Array(hash, 0, length); +} +/** + * Hash-Based Message Authentication Code function + * @function hmac + * @param rawKey the raw HMAC key + * @param message the message to authenticate + * @param algorithm the hash algorithm to be used; default is SHA-256 + * @returns the message authentication code + */ +async function hmac(rawKey: Uint8Array, + message: Uint8Array, + algorithm = HASHALG + ): Promise> { + const key = await createHmacKey(rawKey, algorithm); + const mac = await crypto.subtle.sign(HMAC, key, message.buffer); + return new Uint8Array(mac); +} diff --git a/src/crypto/lib/keys.ts b/src/crypto/lib/keys.ts new file mode 100644 index 00000000..841b2726 --- /dev/null +++ b/src/crypto/lib/keys.ts @@ -0,0 +1,62 @@ +'use strict'; +/** + * @module crypto/keys + * @summary Whiteflag JS cryptographic key generation functions + */ +export { + createAesKey, + createHmacKey +}; + +/* Constants */ +const BYTELENGTH = 8; +const RAWKEY = 'raw'; +const NOTEXTRACTABLE = false; +const DEFAULT_HASHALG = 'SHA-256'; +const DEFAULT_ENCRYPTALG = 'AES-CTR'; + +/* MODULE FUNCTIONS */ +/** + * Creates an AES encryption and decryption key + * @function createAesKey + * @param rawKey the raw key + * @param algorithm the AES mode to use the key for, default is CTR mode + * @returns the AES enrcyption key + */ +async function createAesKey(rawKey: Uint8Array, + algorithm: string = DEFAULT_ENCRYPTALG + ): Promise { + const aesAlgorithm: AesKeyAlgorithm = { + name: algorithm, + length: rawKey.length * BYTELENGTH + }; + return crypto.subtle.importKey( + RAWKEY, + rawKey.buffer, + aesAlgorithm, + NOTEXTRACTABLE, + ['encrypt', 'decrypt'] + ); +} +/** + * Creates an HMAC signing key + * @function createHmacKey + * @param rawKey the raw key + * @param algorithm the hashing algorithm, default is SHA-256 + * @returns the HMAC signing key + */ +async function createHmacKey(rawKey: Uint8Array, + algorithm: string = DEFAULT_HASHALG + ): Promise { + const hmacAlgorithm: HmacImportParams = { + name: 'HMAC', + hash: { name: algorithm } + }; + return crypto.subtle.importKey( + RAWKEY, + rawKey.buffer, + hmacAlgorithm, + NOTEXTRACTABLE, + ['sign'] + ); +} diff --git a/src/crypto/package.json b/src/crypto/package.json new file mode 100644 index 00000000..c4b8cd9d --- /dev/null +++ b/src/crypto/package.json @@ -0,0 +1,31 @@ +{ + "name": "@whiteflagprotocol/crypto", + "version": "1.0.0", + "description": "Whiteflag JS cryptographic classes and functions", + "license": "CC0-1.0", + "keywords": [ + "whiteflag", + "protocol", + "cryptography", + "blockchain" + ], + "directories": { + "lib": "lib" + }, + "dependencies": { + "@whiteflagprotocol/core": "^1.0.0", + "@whiteflagprotocol/util": "^1.0.0" + }, + "homepage": "https://github.com/WhiteflagProtocol/whiteflag-js#readme", + "repository": { + "type": "git", + "directory": "dist/crypto", + "url": "https://github.com/WhiteflagProtocol/whiteflag-js.git" + }, + "bugs": { + "url": "https://github.com/WhiteflagProtocol/whiteflag-js/issues" + }, + "type": "module", + "main": "index.js", + "types": "index.d.ts" +} diff --git a/src/crypto/static/v1/wf-auth-params.json b/src/crypto/static/v1/wf-auth-params.json new file mode 100644 index 00000000..6fd5e7b0 --- /dev/null +++ b/src/crypto/static/v1/wf-auth-params.json @@ -0,0 +1,8 @@ +{ + "$description": "Whiteflag protocol version 1 authentication methods", + "2": { + "$description": "Whiteflag authentication method 1: pre-shared secret", + "tokenLength": 32, + "salt": "420abc48f5d69328c457d61725d3fd7af2883cad8460976167e375b9f2c14081" + } +} \ No newline at end of file diff --git a/src/crypto/static/v1/wf-crypto-params.json b/src/crypto/static/v1/wf-crypto-params.json new file mode 100644 index 00000000..c56da825 --- /dev/null +++ b/src/crypto/static/v1/wf-crypto-params.json @@ -0,0 +1,19 @@ +{ + "$description": "Whiteflag protocol version 1 encryption methods", + "1": { + "$description": "Whiteflag encryption method 1: AES-256 in counter mode (CTR) with ECDH negotiated secret key", + "algorithm": "AES-CTR", + "keyLength": 32, + "ivLength": 16, + "ctrLength": 8, + "salt": "8ddb03085a2c15e69c35c224bce2952dca7878770724741cbce5a135328be0c0" + }, + "2": { + "$description": "Whiteflag encryption method 2: AES-256 in counter mode (CTR) with pre-shared secret key", + "algorithm": "AES-CTR", + "keyLength": 32, + "ivLength": 16, + "ctrLength": 8, + "salt": "c4d028bd45c876135e80ef7889835822a6f19a31835557d5854d1334e8497b56" + } +} \ No newline at end of file diff --git a/src/main/LICENSE.md b/src/main/LICENSE.md new file mode 100644 index 00000000..66129f4e --- /dev/null +++ b/src/main/LICENSE.md @@ -0,0 +1,53 @@ +# Whiteflag Software Dedication to the Public Domain + +## Preamble + +All persons and organisations that contributed to the initial development +of the Whiteflag Protocol and the Whiteflag JavaScript Library software did +so disinterestedly. + +## Whiteflag JavaScript Library CC0-1.0 Statement + +Excluding third party software that may be part of this distribution, the +Whiteflag JavaScript Library software and associated documentation is dedicated +to the public domain under the [Creative Commons CC0-1.0 Universal Public Domain Dedication](http://creativecommons.org/publicdomain/zero/1.0/) +statement, meaning that to the extent possible under law, the authors +and their organisations have waived all copyright and related or neighbouring +rights to this work, allowing anyone to copy, modify, distribute and implement +the work, even for commercial purposes, all without asking permission. + +In no way are the patent or trademark rights of any person affected by this +dedication to the public domain, nor are the rights that other persons may +have in the work or in how the work is used, such as publicity or privacy +rights. Unless expressly stated otherwise, the authors and their organisations +make no warranties about the work, and disclaim liability for all uses of the +work, to the fullest extent permitted by applicable law. When using or citing +the work, you should not imply endorsement by the authors or the affirmer. + +## Third Party Software Dependencies + +Where the Whiteflag JavaScript Library software requires third party +software packages, which are not part of this distribution. These third party +software packages need to be obtained through their respective official +distribution channels, and may be licensed. + +## Legal Restrictions for the Usage of the Whiteflag software + +WARNING: THE USAGE OF SIGNS AND SIGNALS WITH THIS SOFTWARE IS SUBJECT +TO LOCAL AND/OR INTERNATIONAL LAWS. Please take note of the following when +using this software: + +- This software only provides an additional means of communication for + certain signs and signals, and it does not replace any existing + standard, regulation or means of communication, whether mandatory or + not, including but not limited to physical signs, radio communications, + official registers, etc. + +- The usage of protective signs is subject to International Humanitarian + Law. Misuse of protective signs is a punishable violation under local + and international laws. + +- The usage of emergency signals may be subject to various regulations and + standards, depending on location and context. The misuse of emergency + signals, including misuse of the duress functionality, may be a + punishable violation under local and international laws. diff --git a/src/main/README.md b/src/main/README.md new file mode 100644 index 00000000..6ba56d48 --- /dev/null +++ b/src/main/README.md @@ -0,0 +1,67 @@ +# WFJSL Main Interface + +## Overview + +The Whiteflag JavaScript Library (WFJSL) is an implementation of the Whiteflag +Protocol written in [TypeScript](https://developer.mozilla.org/en-US/docs/Glossary/TypeScript), +and compiled to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), +to support the development of Whiteflag-enabled applications in JavaScript. + +The `@whiteflagprotocol/main` package provides all classes and functions +required to implement the Whiteflag protocol. Normally this should be +the only dependency for projects implementing Whiteflag. + +This description provides a generic overview of the Whiteflag main package. +Please refer to the [WFJSL TypeDoc documentation](https://js.whiteflagprotocol.org/typedoc) +for a detailed description of all classes and functions. + +## Whiteflag message class + +The Whiteflag message class `WfMessage` defined in the `main` module +represents a Whiteflag message. + +This class extends the core Whiteflag message class `WfCoreMessage` by adding +metadata to the message, additional data conversions (such as to and from +JSON), and specific Whiteflag protocol features. This allows the class to be +used and integrated in larger functional applications in accordance with the +Whiteflag protocol specification. + +A new message may be created using the constructor, or by using a static +factory method. For example, creating a new FreeText message (message +code `F`) and set the `Text` field, may be done as follows: + +```{javascript} +let wfMessage = new WfCoreMessage('F'); +WfCoreMessage.set('Text', 'Example text to be sent with the FreeText message'); +``` + +The `encode()` method encodes the message. It automatically verifies the fields +and values when encoding and decoding. Encoding and decoding are asynchronous, +meaning the functions return [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). +Once encoded, the methods `toHex()` and `toU8a()` may be used to obtain the +encoded message as a hexadecimal string or a UInt8array, respectively. + +```{javascript} +await wfMessage.encode(); +const hexMessage = wfMessage.toHex(); +``` + +If a message is encoded, or decoded, the message is "final", meaning its +content cannot be changed. Decoding a message is done using a one of the +static factory methods such as `fromHex(...)` or `fromU8a(...)`, since the +message type is probably not known before decoding. + +```{javascript} +wfMessage = await WfCoreMessage.fromHex(hexMessage); +``` + +Encryption and decryption is automatically performed upon encoding and +decoding, based on the value of the `EncryptionIndicator` field in the message +header. + +## Whiteflag versions + +Currently, only one version of the Whiteflag protocol has been developed. For +ease of implementation of future Whiteflag versions, WFJSL functions and +classes take the Whiteflag version into account. The available Whiteflag +versions are defined with the `WfVersions` enum. diff --git a/src/main/index.ts b/src/main/index.ts new file mode 100644 index 00000000..30f613fb --- /dev/null +++ b/src/main/index.ts @@ -0,0 +1,14 @@ +'use strict'; +/** + * @module main + * @summary Whiteflag JS main package + * @document docs/md/modules.md + * @primaryExport + */ +export { + WfMessage, + WfMetaHeader +} from './lib/message.ts'; +export { + WfVersion +} from '@whiteflagprotocol/core'; diff --git a/src/main/lib/message.ts b/src/main/lib/message.ts new file mode 100644 index 00000000..6c0da9f6 --- /dev/null +++ b/src/main/lib/message.ts @@ -0,0 +1,133 @@ +'use strict'; +/** + * @module main/message + * @summary Whiteflag JS message class + */ +export { + WfMessage, + WfMetaHeader +}; + +/* Module imports */ +import { + WfVersion, + WfMsgType, + WfCoreMessage +} from '@whiteflagprotocol/core'; + +/** + * A Whiteflag message as defined by the Whiteflag specification + * @class WfMessage + * @wfversion v1-draft.7 + * @wfreference 4 Message Format + * @remarks This class extends the core Whiteflag message class by + * adding metadata to the message, additional data conversions (such as to and + * from JSON), and specific Whiteflag protocol features. This allows the class + * to be used and integrated in larger functional applications in accordance + * with the Whiteflag specification. + */ +class WfMessage extends WfCoreMessage { + /* CLASS PROPERTIES */ + + /** The message metadata required for processing the message */ + protected meta: WfMetaHeader = {}; + + /* CONSTRUCTOR */ + /** + * Constructor for a Whiteflag message + * @param type the Whiteflag message type + * @param version the Whiteflag protocol version + */ + constructor(type: string, version: string = '1') { + super(type as WfMsgType, version as WfVersion); + } + + /* STATIC FACTORY METHODS */ + /** + * Creates new Whiteflag message from a plain object + * @function fromObject + * @param message a plain JavaScript object with message header and body + * @returns a new Whiteflag message + * @throws {WfProtocolError} if message could not be created + */ + public static async fromJSON(message: string): Promise { + const wfMessage = await this.fromObject(JSON.parse(message)); + return wfMessage as WfMessage; + } + + /* PUBLIC CLASS METHODS */ + /** + * Returns the value of the metaheader field + * @function getMeta + * @param fieldName the name of the metaheader field + * @returns the value of the metaheader field + */ + public getMeta(fieldName: string): string | null { + /* Look for field in metaheader */ + for (const field of Object.keys(this.meta)) { + if (field === fieldName) return this.meta[field]; + } + /* Specified field not found */ + return null; + } + /** + * Sets the value of the specified metaheader field + * @function setMeta + * @param fieldName the name of the metaheader field + * @param value the value to set + * @return true if succesful, else false + */ + public setMeta(fieldName: string, value: string): boolean { + this.meta[fieldName] = value; + return true; + } + /** + * Returns the Whiteflag message as a plain object + * @function toObject + * @returns the message as a plain object + */ + public toObject(): Object { + let message = super.toObject() as any; + message.MetaHeader = this.meta; + return message; + } + /** + * Returns the Whiteflag message as a plain object + * @function toObject + * @returns the message as a plain object + */ + public toJSON(): string { + return JSON.stringify(this.toObject()); + } +} + +/* MODULE DECLARATIONS */ +/** + * Defines a Whiteflag message header object + * @interface WfMetaHeader + */ +interface WfMetaHeader { + /* Metaheader may include custom properties */ + [key: string]: any, + /* Fields used by the Whiteflag software */ + autoGenerated?: string, + blockchain?: string + transceiveDirection?: string, + transmissionSuccess?: boolean, + transactionHash?: string, + transactionTime?: string, + transactionIndex?: number, + blockNumber?: number, + blockDepth?: number, + confirmed?: boolean, + recipientAddress?: string, + originatorAddress?: string, + originatorPubKey?: string, + originatorValid?: boolean, + referenceValid?: boolean, + formatValid?: boolean, + validationErrors?: string[], + encodedMessage?: string, + encryptionInitVector?: string, + encryptionKeyInput?: string +} diff --git a/src/main/package.json b/src/main/package.json new file mode 100644 index 00000000..e15a1c4e --- /dev/null +++ b/src/main/package.json @@ -0,0 +1,30 @@ +{ + "name": "@whiteflagprotocol/main", + "version": "1.0.0", + "description": "Whiteflag JS main package", + "license": "CC0-1.0", + "keywords": [ + "whiteflag", + "protocol", + "blockchain" + ], + "directories": { + "lib": "lib" + }, + "dependencies": { + "@whiteflagprotocol/crypto": "^1.0.0", + "@whiteflagprotocol/util": "^1.0.0" + }, + "homepage": "https://github.com/WhiteflagProtocol/whiteflag-js#readme", + "repository": { + "type": "git", + "directory": "dist/protocol", + "url": "https://github.com/WhiteflagProtocol/whiteflag-js.git" + }, + "bugs": { + "url": "https://github.com/WhiteflagProtocol/whiteflag-js/issues" + }, + "type": "module", + "main": "index.js", + "types": "index.d.ts" +} diff --git a/src/protocol/index.ts b/src/protocol/index.ts deleted file mode 100644 index 9a5692dd..00000000 --- a/src/protocol/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @module - * @summary Whiteflag JS main package - * @document docs/md/modules.md - */ diff --git a/src/util/LICENSE.md b/src/util/LICENSE.md new file mode 100644 index 00000000..66129f4e --- /dev/null +++ b/src/util/LICENSE.md @@ -0,0 +1,53 @@ +# Whiteflag Software Dedication to the Public Domain + +## Preamble + +All persons and organisations that contributed to the initial development +of the Whiteflag Protocol and the Whiteflag JavaScript Library software did +so disinterestedly. + +## Whiteflag JavaScript Library CC0-1.0 Statement + +Excluding third party software that may be part of this distribution, the +Whiteflag JavaScript Library software and associated documentation is dedicated +to the public domain under the [Creative Commons CC0-1.0 Universal Public Domain Dedication](http://creativecommons.org/publicdomain/zero/1.0/) +statement, meaning that to the extent possible under law, the authors +and their organisations have waived all copyright and related or neighbouring +rights to this work, allowing anyone to copy, modify, distribute and implement +the work, even for commercial purposes, all without asking permission. + +In no way are the patent or trademark rights of any person affected by this +dedication to the public domain, nor are the rights that other persons may +have in the work or in how the work is used, such as publicity or privacy +rights. Unless expressly stated otherwise, the authors and their organisations +make no warranties about the work, and disclaim liability for all uses of the +work, to the fullest extent permitted by applicable law. When using or citing +the work, you should not imply endorsement by the authors or the affirmer. + +## Third Party Software Dependencies + +Where the Whiteflag JavaScript Library software requires third party +software packages, which are not part of this distribution. These third party +software packages need to be obtained through their respective official +distribution channels, and may be licensed. + +## Legal Restrictions for the Usage of the Whiteflag software + +WARNING: THE USAGE OF SIGNS AND SIGNALS WITH THIS SOFTWARE IS SUBJECT +TO LOCAL AND/OR INTERNATIONAL LAWS. Please take note of the following when +using this software: + +- This software only provides an additional means of communication for + certain signs and signals, and it does not replace any existing + standard, regulation or means of communication, whether mandatory or + not, including but not limited to physical signs, radio communications, + official registers, etc. + +- The usage of protective signs is subject to International Humanitarian + Law. Misuse of protective signs is a punishable violation under local + and international laws. + +- The usage of emergency signals may be subject to various regulations and + standards, depending on location and context. The misuse of emergency + signals, including misuse of the duress functionality, may be a + punishable violation under local and international laws. diff --git a/src/util/README.md b/src/util/README.md new file mode 100644 index 00000000..91b73752 --- /dev/null +++ b/src/util/README.md @@ -0,0 +1,74 @@ +# WFJSL Utility Classes and Functions + +## Overview + +The Whiteflag JavaScript Library (WFJSL) is an implementation of the Whiteflag +Protocol written in [TypeScript](https://developer.mozilla.org/en-US/docs/Glossary/TypeScript), +and compiled to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), +to support the development of Whiteflag-enabled applications in JavaScript. + +The `@whiteflagprotocol/util` package provides common utilities for other +Whiteflag packages. These utilities include common data conversions, generic +helper functions, etc. for other Whiteflag packages. It should normally not be +necessary to add this package as a dependency, but its functionality might be +useful for other purposes. + +This description provides a generic overview of the WFJSL utility package. +Please see the [WFJSL TypeDoc documentation](https://js.whiteflagprotocol.org/typedoc) +for a detailed description of all classes and functions. + +## BinaryBuffer class + +The `binary` module of the Whiteflag utility package provides the +`BinaryBuffer` class. Objects of this class represent a binary encoded piece +of data, e.g. a Whiteflag message, that can be manipulated at bit level. + +Static methods to create a binary buffer: + +* `BinaryBuffer.empty()`: creates an empty binary buffer +* `BinaryBuffer.from(...)`: creates a binary buffer from another binary buffer +* `BinaryBuffer.fromBytes(...)`: creates a binary buffer from bytes in a number array +* `BinaryBuffer.fromHex(...)`: creates a binary buffer from a hexadecimal string +* `BinaryBuffer.fromU8a(...)`: creates a binary buffer from a Uint8Array + +Public methods to manipulate a binary buffer: + +* `BinaryBuffer.append(...)`: appends another binary buffer to the end of the binary buffer +* `BinaryBuffer.crop(...)`: shortens the binary buffer to the length of the specified bits +* `BinaryBuffer.extract(...)`: extracts the specified bits from the binary buffer +* `BinaryBuffer.insert(...)`: inserts another binary buffer at the start of the binary buffer +* `BinaryBuffer.shiftLeft(...)`: shifts bits in the buffer to the left, shrinking the buffer +* `BinaryBuffer.shiftRight(...)`: shifts bits in the buffer to the right, enlarging the buffer + +Some of these functions have an equivalent that allow to use a different +binary representation, e.g. `appendHex(...)` or `insertU8a(...)`. + +## Data conversions + +The `encoding` module provides generic functions to convert data +from one encoding to another. + +| Encoding | Description | Converts to | +|-------------|---------------------------------------------------------------|-----------------------------------------| +| Base64 | a string with a 64-character binary-to-text encoding | Base64url | +| Base64url | a string with a URL-safe 64-character binary-to-text encoding | Base64, Hexadecimal, String, UInt8Array | +| Hexadecimal | a string with a hexadecimal representation of a binary | Base64url, String, UInt8Array | +| Object | a plain JavaScript object | Base64url | +| Text | a string with UTF-8 characters | Base64url, Hexadecimal, UInt8Array | +| UInt8Array | an array of bytes representing a binary encoding | Base64url, Hexadecimal, String | + +For example `hexToB64u(...)` creates a base64url encoded string from a +hexadecimal string. The module also provides some additional helper functions +for different data encodings: + +* `isObject(...)` checks if something is an object +* `isString(...)` checks if something is a string +* `isBase64(...)` checks if a string is base64 encoded +* `isBase64u(...)` checks if a string is base64url encoded +* `isHex(...)` checks if a string is hexadecimal encoded +* `noHexPrefix(...)` removes the '0x' hex prefix if present + +## JSON Web Signature (JWS) + +Whiteflag uses JSON Web Signatures (JWS). To create, sign and convert JWSs +the `jws` module provides a common `Jws` class to other Whiteflag packages. diff --git a/src/util/index.ts b/src/util/index.ts index 93015e5b..948a3fdf 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -1,7 +1,10 @@ +'use strict'; /** * @module util * @summary Whiteflag JS common utility and helper functions * @document docs/md/modules.md + * @primaryExport */ -export { BinaryArray } from './lib/binary'; -export * from './lib/convert'; +export * from './lib/binary.ts'; +export * from './lib/encoding.ts'; +export * from './lib/jws.ts'; diff --git a/src/util/lib/binary.ts b/src/util/lib/binary.ts index e8691c79..2dad12e2 100644 --- a/src/util/lib/binary.ts +++ b/src/util/lib/binary.ts @@ -1,44 +1,460 @@ +'use strict'; /** * @module util/binary - * @summary Whiteflag JS binary array class + * @summary Whiteflag JS binary buffer class */ -export { BinaryArray }; +export { + BinaryBuffer, + cropBits, + shiftRight, + shiftLeft +}; -/* Dependencies */ -import { hexToU8a, u8aToHex } from "./convert"; +/* Module imports */ +import { + isHex, + hexToU8a, + u8aToHex +} from './encoding.ts'; +/* Constants */ +const BYTELENGTH = 8; + +/* MODULE DECLARATIONS */ /** - * Binary Array - * @class BinArray + * A class representing a binary buffer + * @class BinaryBuffer + * @remarks Objects of this class represent a binary encoded piece of data, + * e.g. a Whiteflag message, that can be manipulated at bit level. This class + * provides the basic (not Whiteflag-specific) functionality for other + * Whiteflag packages to encode and decode binary Whiteflag messages. */ -class BinaryArray { +class BinaryBuffer { + /* CLASS PROPERTIES */ + + /** The Uint8Array that holds the binary content */ + private buffer: Uint8Array; + /** The number of used bits in the buffer */ + public length: number; + + /* CONSTRUCTOR */ /** - * @property buffer the binary content + * Constructor for a binary buffer + * @param buffer the Uint8Array typed array to create the binary buffer from + * @param nBits the bit length of the buffer */ - private buffer: Uint8Array; + private constructor(buffer: Uint8Array = new Uint8Array(0), nBits: number = 0) { + if (buffer.length > 0) { + this.length = this.calcBitLength(buffer.byteLength, nBits); + this.buffer = cropBits(buffer, this.length); + } else { + this.length = 0; + this.buffer = buffer; + } + } + /* STATIC FACTORY METHODS */ + /** + * Creates an empty binary buffer + * @function from + * @returns a new binary buffer + */ + public static empty(): BinaryBuffer { + return new BinaryBuffer(); + } + /** + * Creates a binary buffer from another binary buffer + * @function from + * @param binBuffer a binary buffer + * @returns a new binary buffer + */ + public static from(binBuffer: BinaryBuffer): BinaryBuffer { + return new BinaryBuffer(binBuffer.toU8a(), binBuffer.length); + } /** - * Constructor for a Binary Array - * @param buffer the Uint8Array typed array to create the Binary Array from + * Creates a binary buffer from bytes in a number array + * @function fromBytes + * @param byteArray an array of numbers representing bytes + * @param nBits the number of used bits + * @returns a new binary buffer */ - private constructor(buffer: Uint8Array) { - this.buffer = buffer; + public static fromBytes(byteArray: Array, nBits: number = 0): BinaryBuffer { + return new BinaryBuffer(new Uint8Array(byteArray), nBits); + } + /** + * Creates a binary buffer from a hexadecimal string + * @function fromHex + * @param hexString a hexadecimal string + * @param nBits the number of used bits + * @returns a new binary buffer + */ + public static fromHex(hexString: string, nBits: number = 0): BinaryBuffer { + if (!isHex(hexString)) throw new TypeError('Invalid hexadecimal string'); + return new BinaryBuffer(hexToU8a(hexString), nBits); + } + /** + * Creates a binary buffer from a Uint8Array + * @function fromU8a + * @param u8array an array of 8-bit unsigned integers + * @param nBits the number of used bits + * @returns a new binary buffer + */ + public static fromU8a(u8array: Uint8Array, nBits: number = 0): BinaryBuffer { + return new BinaryBuffer(u8array, nBits); } + /* PUBLIC CLASS METHODS */ + /** + * Appends another binary buffer to the binary buffer + * @function append + * @param binBuffer a binary buffer + * @returns the updated binary buffer + */ + public append(binBuffer: BinaryBuffer): BinaryBuffer { + return this.appendU8a(binBuffer.toU8a(), binBuffer.length); + } + /** + * Appends bytes from a number array to the binary buffer + * @function appendBytes + * @param byteArray an array of numbers representing bytes + * @param nBits the number of used bits to append + * @returns the updated binary buffer + */ + public appendBytes(byteArray: Array, nBits: number = 0): BinaryBuffer { + return this.appendU8a(new Uint8Array(byteArray), nBits); + } + /** + * Appends a hexadecimal string to the binary buffer + * @function appendHex + * @param hexString a hexadecimal string + * @param nBits the number of used bits to append + * @returns the updated binary buffer + */ + public appendHex(hexString: string, nBits: number = 0): BinaryBuffer { + if (!isHex(hexString)) throw new TypeError('Invalid hexadecimal string'); + return this.appendU8a(hexToU8a(hexString), nBits); + } + /** + * Appends a Uint8Array to the binary buffer + * @function appendU8a + * @param u8array an array of 8-bit unsigned integers + * @param nBits the number of used bits to append + * @returns the updated binary buffer + */ + public appendU8a(u8array: Uint8Array, nBits: number = 0): BinaryBuffer { + const bitLength = this.length; + this.buffer = this.concatinate(this.buffer, bitLength, u8array, nBits); + this.length = bitLength + this.calcBitLength(u8array.byteLength, nBits); + return this; + } + /** + * Shortens the binary buffer to the length of the specified bits + * @function crop + * @param nBits the number of used bits, or, if negative, the number of bits to remove + * @return the updated binary buffer + */ + public crop(nBits: number) { + if (nBits === 0) return this; + + /* Determine resulting buffer length */ + let length = nBits; + if (nBits > this.length) length = this.length; + if (nBits < 0) length = this.length + nBits; + if (length < 0) length = 0; + + /* Crop buffer and set new length */ + this.buffer = cropBits(this.buffer, nBits); + this.length = length; + return this; + } + /** + * Extracts the specified bits from the binary buffer + * @param startBit the first bit to extract (inclusive) + * @param endBit the final bit of the extraction (exclusive), negative means until end of buffer + * @returns a new binary buffer with the extracted bits + */ + public extract(startBit: number, endBit: number = -1): BinaryBuffer { + const lastBit = endBit < 0 ? this.length : endBit; + const buffer = this.extractU8a(startBit, lastBit); + const bitLength = lastBit - startBit; + return new BinaryBuffer(buffer, bitLength); + } + /** + * Extracts the specified bits from the binary buffer to a hexadecimal string + * @param startBit the first bit to extract (inclusive) + * @param endBit the final bit of the extraction (exclusive), negative means until end of buffer + * @returns a hexadecimal string with the extracted data + */ + public extractHex(startBit: number, endBit: number = -1): string { + return u8aToHex(this.extractU8a(startBit, endBit)); + } + /** + * Extracts the specified bits from the binary buffer to a Uint8Array + * @function extractU8a + * @param startBit the first bit to extract (inclusive) + * @param endBit the final bit of the extraction (exclusive), negative means until end of buffer + * @returns an array of 8-bit unsigned integers with the extracted data + */ + public extractU8a(startBit: number, endBit: number = -1): Uint8Array { + /* Check range */ + const lastBit = endBit < 0 ? this.length : endBit; + if (startBit < 0) throw RangeError('Starting bit cannot be less than 0'); + if (startBit >= this.length) throw RangeError('Starting bit is larger than binary buffer length'); + if (startBit > lastBit) throw RangeError('Starting bit is larger than ending bit'); + + /* Calculate parameters */ + let bitLength = lastBit - startBit; + if (lastBit > this.length) bitLength = this.length - startBit; + const startByte = Math.floor(startBit / BYTELENGTH); + const byteLength = this.calcByteLength(bitLength); + const shift = startBit % BYTELENGTH; + + /* Create and return new byte array */ + const buffer = new Uint8Array(this.buffer.slice( + startByte, startByte + byteLength + (shift > 0 ? 1 : 0) + )); + return cropBits(shiftLeft(buffer, shift), bitLength); + } + /** + * Inserts bytes from a number array at the start of the binary buffer + * @function insertBytes + * @param byteArray an array of numbers representing bytes + * @param nBits the number of used bits to insert + * @returns the updated binary buffer + */ + public insertBytes(byteArray: Array, nBits: number = 0): BinaryBuffer { + return this.insertU8a(new Uint8Array(byteArray), nBits); + } + /** + * Inserts a hexadecimal string at the start of the binary buffer + * @function insertHex + * @param hexString a hexadecimal string + * @param nBits the number of used bits to insert + * @returns the updated binary buffer + */ + public insertHex(hexString: string, nBits: number = 0): BinaryBuffer { + if (!isHex(hexString)) throw new TypeError('Invalid hexadecimal string'); + return this.insertU8a(hexToU8a(hexString), nBits); + } + /** + * Inserts a Uint8Array at the start of the binary buffer + * @function insertU8a + * @param u8array an array of 8-bit unsigned integers + * @param nBits the number of used bits to insert + * @returns the updated binary buffer + */ + public insertU8a(u8array: Uint8Array, nBits: number = 0): BinaryBuffer { + const bitLength = this.length; + this.buffer = this.concatinate(u8array, nBits, this.buffer, bitLength); + this.length = bitLength + this.calcBitLength(u8array.byteLength, nBits); + return this; + } /** - * Creates a Binary Array from a hexadecimal string - * @param hexString hexadecimal string - * @returns a new Binary Array + * Shifts bits in the buffer to the left, shrinking the buffer + * @function shiftLeft + * @param shift the number of bits to shift to the left + * @returns the shifted binary buffer */ - public static fromHex(hexString: string): BinaryArray { - return new BinaryArray(hexToU8a(hexString)); + public shiftLeft(shift: number): BinaryBuffer { + if (shift < 0) return this.shiftRight(-shift); + + /* Left shift larger than lentgh gives empty buffer */ + if (shift >= this.length) { + this.buffer = new Uint8Array(0); + this.length = 0; + return this; + } + /* Create new smaller buffer */ + const bitLength = this.length - shift; + const byteShift = Math.floor(shift / BYTELENGTH); + const buffer = new Uint8Array(this.calcByteLength(bitLength) + 1); + for (let i = 0; i < buffer.length; i++) { + buffer[i] = this.buffer[i + byteShift]; + } + this.buffer = cropBits(shiftLeft(buffer, shift), bitLength); + this.length = bitLength; + return this; } + /** + * Shifts bits in the buffer to the right, enlarging the buffer + * @function shiftRight + * @param shift the number of bits to shift to the right + * @returns the shifted binary buffer + */ + public shiftRight(shift: number): BinaryBuffer { + if (shift < 0) return this.shiftLeft(-shift); + /* Create new larger buffer */ + const byteShift = Math.ceil(shift / BYTELENGTH); + const padding = new Uint8Array(byteShift); + this.buffer = this.concatinate(padding, shift, this.buffer, this.length); + this.length = this.length + shift; + return this; + } /** - * Gives the value of the Binary Array as a hexadecimal string + * Gives the value of the binary buffer as a Uint8Array + * @function toU8a + * @returns an array of 8-bit unsigned integers + */ + public toU8a(): Uint8Array { + return new Uint8Array(this.buffer); + } + /** + * Gives the value of the binary buffer as a hexadecimal string + * @function toHex * @returns a hexadecimal string */ - toHex(): string { + public toHex(): string { return u8aToHex(this.buffer); } -} \ No newline at end of file + + /* PRIVATE CLASS METHODS */ + /** + * Calculates the number of bits to be stored in the buffer + * @private + * @param byteLength the actual byte length of the buffer containing the bitset + * @param nBits the specified bit length of the buffer, or, if negative, the number of bits to remove + * @returns the calculated bit length + */ + private calcBitLength(byteLength: number, nBits: number): number { + const bitLength = byteLength * BYTELENGTH; + if (nBits < 1) return Math.max(bitLength + nBits, 0); + if (nBits > bitLength) return bitLength; + return nBits; + } + /** + * Calculates the number of bytes required to hold a given number of bits + * @private + * @param nBits the number of used bits in the binary buffer + * @returns the required byte length of the binary buffer + */ + private calcByteLength(nBits: number): number { + return Math.ceil(nBits / BYTELENGTH); + } + /** + * Concatinates two bitsets + * @private + * @param u8array1 Uint8Array containing the first bitset + * @param nBits1 number of bits in the first bitset, i.e. which bits to take from the first Uint8Array + * @param u8array2 Uint8Array containing the second bitset + * @param nBits2 number of bits in the second bitset, i.e. which bits to take from the second Uint8Array + */ + private concatinate(u8array1: Uint8Array, nBits1: number, u8array2: Uint8Array, nBits2: number): Uint8Array { + /* Calculate paramters */ + const bitLength1 = this.calcBitLength(u8array1.byteLength, nBits1); + const bitLength2 = this.calcBitLength(u8array2.byteLength, nBits2); + const bitLength = bitLength1 + bitLength2; + const byteLength = this.calcByteLength(bitLength); + const shift = bitLength1 % BYTELENGTH; + + /* Prepare byte arrays */ + const bArray1 = cropBits(u8array1, bitLength1); + const bArray2 = shiftRight(cropBits(u8array2, bitLength2), shift); + const buffer = new Uint8Array(byteLength); + + /* Add byte arrays to buffer */ + buffer.set(bArray1); + if (shift === 0) { + /* No overlapping byte */ + const offset = bArray1.byteLength; + const slice = bArray2.slice(0, buffer.byteLength - offset); + buffer.set(slice, offset); + } else { + /* Overlapping byte */ + const offset = bArray1.byteLength; + const slice = bArray2.slice(1, buffer.byteLength - offset + 1); + buffer.set(slice, offset); + buffer[bArray1.byteLength - 1] |= bArray2[0]; + } + return buffer; + } +} + +/* MODULE FUNCTIONS */ +/** + * Shortens a Uint8Array to the length of the specified bits + * @function cropBits + * @param u8array the Uint8Array containing the bitset + * @param nBits the number of used bits, or, if negative, the number of bits to remove + * @return a new Uint8Array with the unused bits cleared + */ +function cropBits(u8array: Uint8Array, nBits: number): Uint8Array { + if (nBits === 0) return new Uint8Array(u8array); + + /* Determine resulting byte array length and bits to clear */ + let byteLength: number; + let clearBits: number; + if (nBits > 0) { + byteLength = Math.ceil(nBits / BYTELENGTH); + clearBits = BYTELENGTH - (nBits % BYTELENGTH); + } else { + byteLength = u8array.byteLength - Math.floor(-nBits / BYTELENGTH); + clearBits = -nBits % BYTELENGTH; + } + /* Return the full buffer if byte length is larger than buffer length */ + if (byteLength > u8array.byteLength) return new Uint8Array(u8array); + + /* Return empty buffer if byte lentgh is zero */ + if (byteLength < 1) return new Uint8Array(0); + + /* Create new buffer of byte length, and clear unused bits in last byte */ + let buffer = new Uint8Array(u8array.slice(0, byteLength)); + if (clearBits < BYTELENGTH) buffer[byteLength - 1] &= (0xFF << clearBits); + return buffer; +} +/** + * Shifts bits in a Uint8Array to the right modulo 8 + * @function shiftRight + * @param u8array the Uint8Array to be right shifted + * @param shift the nummber of bits to be right shifted by modulo 8 bits + * @returns a new Uint8Array with the right shifted bits + */ +function shiftRight(u8array: Uint8Array, shift: number): Uint8Array { + /* Check negative value */ + if (shift < 0) return shiftLeft(u8array, -shift); + + /* Calculate shift parameters */ + const byteLength = u8array.byteLength + 1; + const mod = shift % BYTELENGTH; + const mask = (0xFF >>> (BYTELENGTH - mod)); + + /* Create new byte array */ + if (mod === 0) return new Uint8Array(u8array); + let buffer = new Uint8Array(byteLength); + + /* Fill bytes of new Uint8Array, starting at the end, and return result */ + for (let byteIndex = (byteLength - 1); byteIndex > 0; byteIndex--) { + buffer[byteIndex] |= ((0xFF & u8array[byteIndex - 1] & mask) << (BYTELENGTH - mod)); + buffer[byteIndex - 1] = ((0xFF & u8array[byteIndex - 1]) >>> mod); + } + return buffer; +} +/** + * Shifts bits in a Uint8Array to the left modulo 8 + * @function shiftLeft + * @param u8array the Uint8Array to be left shifted + * @param shift the nummber of bits to be left shifted by modulo 8 bits + * @returns a new Uint8Array with the left shifted bits + */ +function shiftLeft(u8array: Uint8Array, shift: number): Uint8Array { + /* Check negative value */ + if (shift < 0) return shiftRight(u8array, -shift); + + /* Calculate shift parameters */ + const byteLength = u8array.byteLength; + const mod = shift % BYTELENGTH; + const mask = (0xFF << (BYTELENGTH - mod)); + + /* Create new byte array */ + if (mod === 0) return new Uint8Array(u8array); + let buffer = new Uint8Array(u8array.byteLength); + + /* Fill bytes of new byte array in two passes and return result */ + for (let byteIndex = 0; byteIndex < byteLength; byteIndex++) { + buffer[byteIndex] = ((0xFF & u8array[byteIndex]) << mod); + } + for (let byteIndex = 0; byteIndex < (byteLength - 1); byteIndex++) { + buffer[byteIndex] |= ((0xFF & u8array[byteIndex + 1] & mask) >>> (BYTELENGTH - mod)); + } + return cropBits(buffer, -(shift % BYTELENGTH)); +} diff --git a/src/util/lib/convert.ts b/src/util/lib/encoding.ts similarity index 61% rename from src/util/lib/convert.ts rename to src/util/lib/encoding.ts index eca43163..56ee0186 100644 --- a/src/util/lib/convert.ts +++ b/src/util/lib/encoding.ts @@ -1,12 +1,19 @@ +'use strict'; /** - * @module util/convert - * @summary Whiteflag JS common data conversions + * @module util/encoding + * @summary Whiteflag JS common encodings and data conversions */ export { isObject, isString, - toBase64u, - toBase64, + isBase64, + isBase64u, + isHex, + noHexPrefix, + objToB64u, + b64uToObj, + b64ToB64u, + b64uToB64, b64uToHex, b64uToString, b64uToU8a, @@ -21,12 +28,16 @@ export { u8aToString, }; -/* Module constants */ +/* Constants */ const NOSEPARATOR = ''; const HEXBYTELENGTH = 2; const HEXRADIX = 16; +const HEXPREFIX = '0x'; +const REGEX_BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; +const REGEX_BASE64U = /^(?:[A-Za-z0-9_-]+)$/; +const REGEX_HEXSTRING = /^(0x|0X)?(?:[a-fA-F0-9]{2})+$/ -/* MAIN MODULE FUNCTIONS */ +/* MODULE FUNCTIONS */ /** * Checks if something is an object * @function isObject @@ -47,12 +58,76 @@ function isString(charString: any): boolean { return (typeof charString === 'string' || charString instanceof String); } +/** + * Checks if a string is base64 encoded + * @function isBase64 + * @param base64 a string that might be base64 encoded + * @returns true if base64 encoded, else false + */ +function isBase64(base64: string): boolean { + return REGEX_BASE64.test(base64); +} + +/** + * Checks if a string is base64url encoded + * @function isBase64u + * @param base64u a string that might be base64url encoded + * @returns true if base64url encoded, else false + */ +function isBase64u(base64u: string): boolean { + return REGEX_BASE64U.test(base64u); +} + +/** + * Checks if a string is hexadecimal encoded + * @function hexString + * @param hexString a string that might be hexadecimal encoded + * @returns true if hexadecimal encoded, else false + */ +function isHex(hexString: string): boolean { + return REGEX_HEXSTRING.test(hexString); +} + +/** + * Removes the '0x' hex prefix if present + * @function hexString + * @param hexString a hexadecimal encoded string + * @returns the the string without the hex prefix + */ +function noHexPrefix(hexString: string): string { + if (hexString.startsWith(HEXPREFIX)) { + return hexString.substring(2).toLowerCase(); + } + return hexString.toLowerCase(); +} + +/** + * Creates a base64URL encoded JSON string from an object + * @function objToB64u + * @param obj the object to be encoded + * @returns a base64URL encoded JSON string + */ +function objToB64u(obj: Object): string { + return stringToB64u(JSON.stringify(obj)); +} + +/** + * Creates an object from a base64URL encoded JSON string + * @function b64uToObj + * @param base64u a base64URL encoded JSON string + * @returns an object with the data from the JSON object + */ +function b64uToObj(base64u: string): Object { + return JSON.parse(b64uToString(base64u)); +} + /** * Convert base64 to base64url + * @function base64 * @param base64 a base64 encoded string * @returns a base64url encoded string */ -function toBase64u(base64: string): string { +function b64ToB64u(base64: string): string { return base64 .replace(/=/g, '') .replace(/\+/g, '-') @@ -61,11 +136,12 @@ function toBase64u(base64: string): string { /** * Convert base64url to base64 - * @param b64uString a base64url encoded string + * @function base64u + * @param base64u a base64url encoded string * @returns a base64 encoded string */ -function toBase64(b64uString: string): string { - let base64: string = b64uString +function b64uToB64(base64u: string): string { + let base64: string = base64u .replace(/\-/g, '+') .replace(/_/g, '/'); switch (base64.length % 4) { @@ -92,7 +168,7 @@ function b64uToHex(b64uString: string): string { * @returns a standard string */ function b64uToString(b64uString: string): string { - return atob(toBase64(b64uString)); + return atob(b64uToB64(b64uString)); } /** @@ -131,10 +207,11 @@ function hexToString(hexString: string): string { * @param hexString a hexadecimal string * @returns an array of 8-bit unsigned integers */ -function hexToU8a(hexString: string): Uint8Array { - const u8array = new Uint8Array(hexString.length / HEXBYTELENGTH); - for (let i = 0; i < hexString.length; i += HEXBYTELENGTH) { - u8array[i / HEXBYTELENGTH] = parseInt(hexString.slice(i, i + HEXBYTELENGTH), HEXRADIX); +function hexToU8a(hexString: string): Uint8Array { + const hex = noHexPrefix(hexString); + const u8array = new Uint8Array(hex.length / HEXBYTELENGTH); + for (let i = 0; i < hex.length; i += HEXBYTELENGTH) { + u8array[i / HEXBYTELENGTH] = parseInt(hex.slice(i, i + HEXBYTELENGTH), HEXRADIX); } return u8array; } @@ -146,7 +223,7 @@ function hexToU8a(hexString: string): Uint8Array { * @returns a base64url encoded string */ function stringToB64u(charString: string): string { - return toBase64u(btoa(charString)); + return b64ToB64u(btoa(charString)); } /** @@ -162,7 +239,7 @@ function stringToHex(charString: string): string { .charCodeAt(i).toString(HEXRADIX) .padStart(HEXBYTELENGTH, '0'); } - return hexString; + return hexString.toLowerCase(); } /** @@ -171,7 +248,7 @@ function stringToHex(charString: string): string { * @param charString a regular character string * @returns an array of 8-bit unsigned integers */ -function stringToU8a(charString: string): Uint8Array { +function stringToU8a(charString: string): Uint8Array { return Uint8Array.from(charString, char => char.charCodeAt(0)); } @@ -199,7 +276,7 @@ function u8aToHex(u8array: Uint8Array): string { .padStart(HEXBYTELENGTH, '0') ); } - return hexArray.join(NOSEPARATOR); + return hexArray.join(NOSEPARATOR).toLowerCase(); } /** diff --git a/src/util/lib/jws.ts b/src/util/lib/jws.ts new file mode 100644 index 00000000..c60e0d62 --- /dev/null +++ b/src/util/lib/jws.ts @@ -0,0 +1,248 @@ +'use strict'; +/** + * @module util/jws + * @summary Whiteflag JS JSON Web Signature class and functions + */ +export { + Jws +}; + +/* Module imports */ +import { + isObject, + isString, + isBase64u, + objToB64u, + b64uToObj +} from './encoding.ts'; + +/* Constants */ +/** + * Defines JWS formats i.a.w. RFC 7515 + * @enum JwsFormat + */ +enum JwsFormat { + /** JWS Compact Serialization */ + COMPACT = 'JWS_COMPACT', + /** flattened JWS JSON Serialization */ + FLAT = 'JWS_FLATTENED', + /** full JWS JSON object */ + FULL = 'JWS_FULL', +} +/** + * Separator in compact serialised JWS + * @Constant + */ +const JWSSEPARATOR = '.'; +const REGEX_FLAT = /e[yw][A-Za-z0-9-_]+/; +const REGEX_COMPACT = /e[yw][A-Za-z0-9-_]+\.(e[yw][A-Za-z0-9-_]+\.)?[A-Za-z0-9-_]+/; + +/* MODULE DECLARATIONS */ +/** + * A class representing a JSON Web Token (JWS) + * @class Jws + * @remarks Whiteflag uses JSON Web Signatures (JWS) for one of its + * authentication methods. This class provides the basic (not Whiteflag- + * specific) functionality to create, sign and convert JWSs for other + * Whiteflag packages. + */ +class Jws { + /* CLASS PROPERTIES */ + + /** The protected the JWS protected header */ + private protected = { alg: '' } + /** The JWS payload */ + private payload = { iat: 0 } + /** The JWS signature */ + private signature: string = ''; + + /* CONSTRUCTOR */ + /** + * Constructor for a Whiteflag message + * @private + * @param header the JWS header, which will automatically be protected + * @param payload the JWS payload + * @param signature the JWS signature + */ + private constructor(header: any, payload: any, signature: string = '') { + this.protected = header; + this.payload = payload; + this.signature = signature; + } + + /* STATIC FACTORY METHODS */ + /** + * Creates a new JWS from a payload + * @function fromPayload + * @param payload the JWS payload + * @returns a new Binary Array + */ + public static fromPayload(payload: Object): Jws { + return new Jws({}, payload, ''); + } + /** + * Creates a new JWS object from a plain javaScript object + * @function fromObject + * @param jws a plain object + * @returns a new JWS object + */ + public static fromObject(jws: any): Jws { + switch (jwsType(jws)) { + case JwsFormat.FULL: { + return new Jws( + jws?.protected, + jws?.payload, + jws?.signature + ); + } + case JwsFormat.FLAT: { + return new Jws( + b64uToObj(jws.protected), + b64uToObj(jws.payload), + jws?.signature + ); + } + case JwsFormat.COMPACT: { + return this.fromCompact(jws); + } + } + } + /** + * Creates a new JWS object from a compact serialised JWS string + * @function fromCompact + * @param jws a compact serialised JWS string + * @returns a new JWS object + */ + public static fromCompact(jws: string): Jws { + if (jwsType(jws) !== JwsFormat.COMPACT) { + throw new TypeError('Invalid compact serialised JWS string'); + } + const jwsArray = jws.split(JWSSEPARATOR); + let header = {}; + if (jwsArray.length > 0) header = b64uToObj(jwsArray[0]); + let payload = {}; + if (jwsArray.length > 1) payload = b64uToObj(jwsArray[1]); + let signature = ''; + if (jwsArray.length > 2) signature = jwsArray[2]; + return new Jws(header, payload, signature); + } + + /* PUBLIC CLASS METHODS */ + /** + * Indicates if the JWS has been signed + * @function isSigned + * @returns true if signed, else false + */ + public isSigned(): boolean { + return (this.signature.length > 0); + } + /** + * Returns the JWS signature input + * @function getSignInput + * @returns a string with the input to be signed by the signing algorithm + */ + public getSignInput(): string { + /* Add timestamp if not (yet) signed */ + if (!this.isSigned()) { + this.payload.iat = Math.floor(Date.now()/1000); + } + return objToB64u(this.protected) + JWSSEPARATOR + objToB64u(this.payload); + } + /** + * Sets the identifier of the signing algorithm, if not yet signed + * @function setSignAlgorithm + * @param algorithm the identifier of the algorithm used to sign the payload + * @returns true if identifier could be set, false if already signed + */ + public setSignAlgorithm(algorithm: string): boolean { + if (this.isSigned()) return false; + this.protected.alg = algorithm; + return true; + } + /** + * Sets the signature, if not yet signed + * @function setSignature + * @param signature the base64url encoded signature + * @returns true if signature could be added, false if already signed + */ + public setSignature(signature: string): boolean { + if (this.isSigned()) return false; + if (!isBase64u(signature)) { + throw new TypeError('Signature is not base64url encoded'); + } + this.signature = signature; + return true; + } + /** + * Returns the JWS signature + * @function getSignature + * @returns a string with the the JWS signature + */ + public getSignature(): string { + return this.signature; + } + /** + * Returns a full JWS + * @function toFull + * @returns the JWS as a full JWS plain JavaScript object + */ + public toFull(): Object { + return { + protected: this.protected, + payload: this.payload, + signature: this.signature + } + } + /** + * Returns a flattened JWS + * @function toFlat + * @returns the JWS as a flattened JWS plain JavaScript object + */ + public toFlat(): Object { + return { + protected: objToB64u(this.protected), + payload: objToB64u(this.payload), + signature: this.signature + } + } + /** + * Return a compact serialised JWS as a compact serialized string + * @function toCompact + * @returns the JWS as a compact serialized JWS string + */ + public toCompact(): string { + let compactJws = objToB64u(this.protected) + + JWSSEPARATOR + + objToB64u(this.payload); + if (this.isSigned()) { + compactJws = compactJws + + JWSSEPARATOR + + this.signature + } + return compactJws; + } +} + +/* PRIVATE FUNCTIONS */ +/** + * Return the type of the provided JWS + * @private + * @param jws a JSON Web Signature + * @returns the JWS format + * @throws if invalid JWS + */ +function jwsType(jws: any): JwsFormat { + if (isString(jws) && REGEX_COMPACT.test(jws)) { + return JwsFormat.COMPACT; + } + if (isObject(jws)) { + if (isObject(jws.protected) && isObject(jws.payload)) { + return JwsFormat.FULL; + } + if (isString(jws.protected) && REGEX_FLAT.test(jws.protected) + && isString(jws.payload) && REGEX_FLAT.test(jws.payload)) { + return JwsFormat.FLAT; + } + } + throw new TypeError('Invalid JWS representation or encoding'); +} diff --git a/src/util/package.json b/src/util/package.json new file mode 100644 index 00000000..07573bec --- /dev/null +++ b/src/util/package.json @@ -0,0 +1,27 @@ +{ + "name": "@whiteflagprotocol/util", + "version": "1.0.0", + "description": "Whiteflag JS common utility and helper functions", + "license": "CC0-1.0", + "keywords": [ + "whiteflag", + "protocol", + "utility", + "blockchain" + ], + "directories": { + "lib": "lib" + }, + "homepage": "https://github.com/WhiteflagProtocol/whiteflag-js#readme", + "repository": { + "type": "git", + "directory": "dist/util", + "url": "https://github.com/WhiteflagProtocol/whiteflag-js.git" + }, + "bugs": { + "url": "https://github.com/WhiteflagProtocol/whiteflag-js/issues" + }, + "type": "module", + "main": "index.js", + "types": "index.d.ts" +} diff --git a/test/core/codec.js b/test/core/codec.js new file mode 100644 index 00000000..04418888 --- /dev/null +++ b/test/core/codec.js @@ -0,0 +1,97 @@ +'use strict'; +/** + * @module test/core/codec + * @summary Whiteflag JS message field encoding and decoding tests + */ + +/* Test framework */ +import { describe as testCase } from 'mocha'; +import { it as assertion } from 'mocha'; +import { deepStrictEqual } from 'assert'; + +/* Functions required for test */ +import { BinaryBuffer } from '@whiteflagprotocol/util'; + +/* Functions to test */ +import { encodeField, decodeField } from '@whiteflagprotocol/core'; + +/* Test data */ +import testVector from './codec.json' with { type: 'json' }; + +/* TEST SCRIPT */ +testCase('Core codec module', function() { + testCase('Field encoding', function() { + assertion(' 1. should correctly encode (hexa)decimal fields', function(done) { + const BDX = encodeField(testVector['bdx'].string, 'hexadecimal'); + deepStrictEqual(BDX.toU8a(), new Uint8Array(testVector['bdx'].buffer)); + return done(); + }); + assertion(' 2. should correctly encode text fields', function(done) { + const UTF = encodeField(testVector['utf-8'].string, 'utf-8'); + deepStrictEqual(UTF.toU8a(), new Uint8Array(testVector['utf-8'].buffer)); + return done(); + }); + assertion(' 3. should correctly encode datum fields', function(done) { + const DATETIME = encodeField(testVector['datetime'].string, 'datetime'); + deepStrictEqual(DATETIME.toHex(), testVector['datetime'].hex); + const PERIOD = encodeField(testVector['duration'].string, 'duration'); + deepStrictEqual(PERIOD.toHex(), testVector['duration'].hex); + const LAT = encodeField(testVector['latitude'].string, 'latitude'); + deepStrictEqual(LAT.toHex(), testVector['latitude'].hex); + const LONG = encodeField(testVector['longitude'].string, 'longitude'); + deepStrictEqual(LONG.toHex(), testVector['longitude'].hex); + return done(); + }); + assertion(' 4. should correctly encode binary fields', function(done) { + const B1 = encodeField(testVector['binary'].string1, 'binary'); + deepStrictEqual(B1.toHex(), testVector['binary'].hex1); + const B2 = encodeField(testVector['binary'].string2, 'binary'); + deepStrictEqual(B2.toHex(), testVector['binary'].hex2); + return done(); + }); + }); + testCase('Field decoding', function() { + assertion(' 1. should correctly decode (hexa)decimal fields', function(done) { + const BDX = encodeField(testVector['bdx'].string, 'hexadecimal'); + const bdxString = decodeField(BDX, 'hexadecimal'); + deepStrictEqual(bdxString, testVector['bdx'].string); + return done(); + }); + assertion(' 2. should correctly decode text fields', function(done) { + const UTF = encodeField(testVector['utf-8'].string, 'utf-8'); + const utfString = decodeField(UTF, 'utf-8'); + deepStrictEqual(utfString, testVector['utf-8'].string); + return done(); + }); + assertion(' 3. should correctly decode datum fields', function(done) { + const DATETIME = encodeField(testVector['datetime'].string, 'datetime'); + const dtString = decodeField(DATETIME, 'datetime'); + deepStrictEqual(dtString, testVector['datetime'].string); + const PERIOD = encodeField(testVector['duration'].string, 'duration'); + const pString = decodeField(PERIOD, 'duration'); + deepStrictEqual(pString, testVector['duration'].string); + const LAT = encodeField(testVector['latitude'].string, 'latitude'); + const latString = decodeField(LAT, 'latitude'); + deepStrictEqual(latString, testVector['latitude'].string); + const LONG = encodeField(testVector['longitude'].string, 'longitude'); + const longString = decodeField(LONG, 'longitude'); + deepStrictEqual(longString, testVector['longitude'].string); + return done(); + }); + assertion(' 4. should correctly decode binary fields', function(done) { + const B1 = encodeField(testVector['binary'].string1, 'binary'); + const binString1 = decodeField(B1, 'binary'); + deepStrictEqual(binString1, testVector['binary'].string1); + const B2 = encodeField(testVector['binary'].string2, 'binary'); + const binString2 = decodeField(B2, 'binary'); + deepStrictEqual(binString2, testVector['binary'].string2); + return done(); + }); + assertion(' 5. should correctly decode exctracted text fields', function(done) { + const buf = BinaryBuffer.fromBytes([ 0b00010101, 0b11010001, 0b10000000 ]); + const str = decodeField(buf.extract(2, 18), 'utf-8'); + deepStrictEqual(str, 'WF'); + return done(); + }); + }); +}); diff --git a/test/core/codec.json b/test/core/codec.json new file mode 100644 index 00000000..8f5b301b --- /dev/null +++ b/test/core/codec.json @@ -0,0 +1,32 @@ +{ + "bdx": { + "string": "1580bcd", + "buffer": [ 21, 128, 188, 208 ] + }, + "utf-8": { + "string": "Whiteflag:5746/test-01?!?", + "buffer": [ 87, 104, 105, 116, 101, 102, 108, 97, 103, 58, 53, 55, 52, 54, 47, 116, 101, 115, 116, 45, 48, 49, 63, 33, 63 ] + }, + "datetime": { + "string": "2020-07-01T21:42:23Z", + "hex": "20200701214223" + }, + "duration": { + "string": "P24D11H30M", + "hex": "241130" + }, + "latitude": { + "string": "+23.34244", + "hex": "919a1220" + }, + "longitude": { + "string": "-163.34245", + "hex": "0b19a12280" + }, + "binary": { + "string1": "1", + "hex1": "80", + "string2": "10011", + "hex2": "98" + } +} \ No newline at end of file diff --git a/test/core/message.js b/test/core/message.js new file mode 100644 index 00000000..0a66e4a5 --- /dev/null +++ b/test/core/message.js @@ -0,0 +1,139 @@ +'use strict'; +/** + * @module test/core/codec + * @summary Whiteflag JS message field encoding and decoding tests + */ + +/* Test framework */ +import { describe as testCase } from 'mocha'; +import { it as assertion } from 'mocha'; +import { strictEqual, deepStrictEqual } from 'assert'; + +/* Functions required for test */ +import { BinaryBuffer, hexToU8a } from '@whiteflagprotocol/util'; + +/* Functions to test */ +import { WfCoreMessage, encryptMessage, decryptMessage } from '@whiteflagprotocol/core'; + +/* Test data */ +import testVector from './message.json' with { type: 'json' }; + +/* TEST SCRIPT */ +testCase('Core message module', function() { + testCase('Message creation', function() { + assertion(' 1. should create new message obejct', function(done) { + const MSG = new WfCoreMessage('A'); + strictEqual(MSG.get('Version'), '1'); + strictEqual(MSG.get('MessageCode'), 'A'); + strictEqual(MSG.isValid(), false); + return done(); + }); + assertion(' 2. should create message from object', async function() { + const MSG = await WfCoreMessage.fromObject(testVector['1'].wfMessage); + strictEqual(MSG.get('MessageCode'), testVector['1'].wfMessage.MessageHeader.MessageCode); + return strictEqual(MSG.isValid(), true); + }); + }); + testCase('Message serialisation', function() { + assertion(' 3a. should correctly serialise (test vector 1)', async function() { + const MSG = await WfCoreMessage.fromObject(testVector['1'].wfMessage); + return strictEqual(MSG.toString(), testVector['1'].concatinatedMessage); + }); + assertion(' 3b. should correctly serialise (test vector 2)', async function() { + const MSG = await WfCoreMessage.fromObject(testVector['2'].wfMessage); + return strictEqual(MSG.toString(), testVector['2'].concatinatedMessage); + }); + assertion(' 3c. should correctly serialise (test vector 3)', async function() { + const MSG = await WfCoreMessage.fromObject(testVector['3'].wfMessage); + return strictEqual(MSG.toString(), testVector['3'].concatinatedMessage); + }); + assertion(' 3d. should correctly serialise (test vector 4)', async function() { + const MSG = await WfCoreMessage.fromObject(testVector['4'].wfMessage); + return strictEqual(MSG.toString(), testVector['4'].concatinatedMessage); + }); + }); + testCase('Message encoding', function() { + assertion(' 4a. should correctly encode to binary (test vector 1)', async function() { + const MSG = await WfCoreMessage.fromObject(testVector['1'].wfMessage); + await MSG.encode(); + return strictEqual(MSG.toHex(), testVector['1'].encodedMessage); + }); + assertion(' 4b. should correctly encode to binary (test vector 2)', async function() { + const MSG = await WfCoreMessage.fromObject(testVector['2'].wfMessage); + await MSG.encode(); + return strictEqual(MSG.toHex(), testVector['2'].encodedMessage); + }); + assertion(' 4c. should correctly encode to binary (test vector 3)', async function() { + const MSG = await WfCoreMessage.fromObject(testVector['3'].wfMessage); + await MSG.encode(); + return strictEqual(MSG.toHex(), testVector['3'].encodedMessage); + }); + assertion(' 4d. should correctly encode to binary (test vector 4)', async function() { + const MSG = await WfCoreMessage.fromObject(testVector['4'].wfMessage); + await MSG.encode(); + return strictEqual(MSG.toHex(), testVector['4'].encodedMessage); + }); + }); + testCase('Message decoding', function() { + assertion(' 5a. should correctly decode (test vector 1)', async function() { + const MSG = await WfCoreMessage.fromHex(testVector['1'].encodedMessage); + return strictEqual(MSG.toString(), testVector['1'].concatinatedMessage); + }); + assertion(' 5b. should correctly decode (test vector 2)', async function() { + const MSG = await WfCoreMessage.fromHex(testVector['2'].encodedMessage); + return strictEqual(MSG.toString(), testVector['2'].concatinatedMessage); + }); + assertion(' 5c. should correctly decode (test vector 3)', async function() { + const MSG = await WfCoreMessage.fromHex(testVector['3'].encodedMessage); + return strictEqual(MSG.toString(), testVector['3'].concatinatedMessage); + }); + assertion(' 5d. should correctly decode (test vector 4)', async function() { + const MSG = await WfCoreMessage.fromHex(testVector['4'].encodedMessage); + return strictEqual(MSG.toString(), testVector['4'].concatinatedMessage); + }); + }); + testCase('Message encryption', function() { + assertion(' 5a. should correctly encrypt plain binary message', async function() { + const MSG_UNENCRYPTED = BinaryBuffer.fromHex(testVector['5'].unencryptedMessage); + const MSG_ENCRYPTED = await encryptMessage( + MSG_UNENCRYPTED, + testVector['5'].encryptionIndicator, + hexToU8a(testVector['5'].encryptionKeyInput), + hexToU8a(testVector['5'].originatorAddress), + hexToU8a(testVector['5'].encryptionInitVector) + ); + return strictEqual(MSG_ENCRYPTED.toHex(), testVector['5'].encodedMessage); + }); + assertion(' 5b. should correctly encrypt plain message object', async function() { + const MSG = await WfCoreMessage.fromObject(testVector['5'].wfMessage); + await MSG.encode( + hexToU8a(testVector['5'].encryptionKeyInput), + hexToU8a(testVector['5'].originatorAddress), + hexToU8a(testVector['5'].encryptionInitVector) + ); + return strictEqual(MSG.toHex(), testVector['5'].encodedMessage); + }); + }); + testCase('Message decryption', function() { + assertion(' 6a. should correctly decrypt plain binary message', async function() { + const MSG_ENCRYPTED = BinaryBuffer.fromHex(testVector['5'].encodedMessage); + const MSG_UNENCRYPTED = await decryptMessage( + MSG_ENCRYPTED, + testVector['5'].encryptionIndicator, + hexToU8a(testVector['5'].encryptionKeyInput), + hexToU8a(testVector['5'].originatorAddress), + hexToU8a(testVector['5'].encryptionInitVector) + ); + return strictEqual(MSG_UNENCRYPTED.toHex(), testVector['5'].unencryptedMessage); + }); + assertion(' 6b. should correctly create message from plain encrypted message', async function() { + const MSG = await WfCoreMessage.fromHex( + testVector['5'].encodedMessage, + testVector['5'].encryptionKeyInput, + testVector['5'].originatorAddress, + testVector['5'].encryptionInitVector + ); + return strictEqual(MSG.get('Text'), testVector['5'].wfMessage.MessageBody['Text']); + }); + }); +}); diff --git a/test/core/message.json b/test/core/message.json new file mode 100644 index 00000000..bd1c72fb --- /dev/null +++ b/test/core/message.json @@ -0,0 +1,130 @@ +{ + "1": { + "encodedMessage": "5746313020800000000000000000000000000000000000000000000000000000000000000000b43a3a38399d1797b7b933b0b734b9b0ba34b7b71734b73a17bbb434ba32b33630b380", + "concatinatedMessage": "WF100A000000000000000000000000000000000000000000000000000000000000000001https://organisation.int/whiteflag", + "wfMessage": { + "MetaHeader": { + "test": true, + "originatorAddress": "1C8KSK68SJjfDSBx9BpSx3qB3bePf23r77" + }, + "MessageHeader": { + "Prefix": "WF", + "Version": "1", + "EncryptionIndicator": "0", + "DuressIndicator": "0", + "MessageCode": "A", + "ReferenceIndicator": "0", + "ReferencedMessage": "0000000000000000000000000000000000000000000000000000000000000000" + }, + "MessageBody": { + "VerificationMethod": "1", + "VerificationData": "https://organisation.int/whiteflag" + } + } + }, + "2": { + "encodedMessage": "57463130a6a1f7da7067d41891592131a12a60c9053b4eb0aefe6263385da9f5b789421e1d7401009841882148a800000114c1e596006f04c050eca6420084", + "concatinatedMessage": "WF101M43efb4e0cfa83122b242634254c1920a769d615dfcc4c670bb53eb6f12843c3ae802013-08-31T04:29:15ZP00D00H00M22+30.79658-037.8260287653210042", + "wfMessage": { + "MetaHeader": { + "test": true, + "originatorAddress": "1C8KSK68SJjfDSBx9BpSx3qB3bePf23r77" + }, + "MessageHeader": { + "Prefix": "WF", + "Version": "1", + "EncryptionIndicator": "0", + "DuressIndicator": "1", + "MessageCode": "M", + "ReferenceIndicator": "4", + "ReferencedMessage": "3efb4e0cfa83122b242634254c1920a769d615dfcc4c670bb53eb6f12843c3ae" + }, + "MessageBody": { + "SubjectCode": "80", + "DateTime": "2013-08-31T04:29:15Z", + "Duration": "P00D00H00M", + "ObjectType": "22", + "ObjectLatitude": "+30.79658", + "ObjectLongitude": "-037.82602", + "ObjectSizeDim1": "8765", + "ObjectSizeDim2": "3210", + "ObjectOrientation": "042" + } + } + }, + "3": { + "encodedMessage": "57463130232fb60f0f6c4a8589bddcf076e790ac9eb1601d3fd9ced67eaaa62c9fb9644a16fabb434ba32b33630b3903a32b9ba1036b2b9b9b0b3b2908", + "concatinatedMessage": "WF100F5f6c1e1ed8950b137bb9e0edcf21593d62c03a7fb39dacfd554c593f72c8942dfWhiteflag test message!", + "wfMessage": { + "MetaHeader": { + "test": true, + "originatorAddress": "1C8KSK68SJjfDSBx9BpSx3qB3bePf23r77" + }, + "MessageHeader": { + "Prefix": "WF", + "Version": "1", + "EncryptionIndicator": "0", + "DuressIndicator": "0", + "MessageCode": "F", + "ReferenceIndicator": "5", + "ReferencedMessage": "f6c1e1ed8950b137bb9e0edcf21593d62c03a7fb39dacfd554c593f72c8942df" + }, + "MessageBody": { + "Text": "Whiteflag test message!" + } + } + }, + "4": { + "encodedMessage": "574631302a4fe8927067d41891592131a12a60c9053b4eb0aefe6263385da9f5b789421e1cca6c01009841882148a800000114c1e58a006f02c050e8a646208e", + "concatinatedMessage": "WF100T9fd124e0cfa83122b242634254c1920a769d615dfcc4c670bb53eb6f12843c399M802013-08-31T04:29:15ZP00D00H00M22+30.79628-037.8160287453231047", + "wfMessage": { + "MetaHeader": { + "test": true, + "originatorAddress": "1C8KSK68SJjfDSBx9BpSx3qB3bePf23r77" + }, + "MessageHeader": { + "Prefix": "WF", + "Version": "1", + "EncryptionIndicator": "0", + "DuressIndicator": "0", + "MessageCode": "T", + "ReferenceIndicator": "9", + "ReferencedMessage": "fd124e0cfa83122b242634254c1920a769d615dfcc4c670bb53eb6f12843c399" + }, + "MessageBody": { + "PseudoMessageCode": "M", + "SubjectCode": "80", + "DateTime": "2013-08-31T04:29:15Z", + "Duration": "P00D00H00M", + "ObjectType": "22", + "ObjectLatitude": "+30.79628", + "ObjectLongitude": "-037.81602", + "ObjectSizeDim1": "8745", + "ObjectSizeDim2": "3231", + "ObjectOrientation": "047" + } + } + }, + "5": { + "originatorAddress": "007a0baf6f84f0fa7402ea972686e56d50b707c9b67b108866", + "unencryptedMessage": "5746313223000000000088888889111111119999999a22222222aaaaaaab33333333bbbbbbbb0983098309830983118b118b118b118b1993199319931993219b219b219b219b29a329a329a329a331ab31ab31ab31a9b1b9b1b9b1b9b1b9c1c9c1c9c1c9c1c8", + "encryptionIndicator": "2", + "encryptionKeyInput": "32676187ba7badda85ea63a69870a7133909f1999774abb2eed251073616a6e7", + "encryptionInitVector": "40aa85015d24e4601448c1ba8d7bf1aa", + "encodedMessage": "574631326d7658e7d17479677a0de95076989fcd7825b709349b143f2b17644e5cb2c8ded5c7f18d77447cf9dc2115e0c1c81d717b57fadaeedf27bfef8926448ff666d3d9a65168827c94b393974ebbe6b7f0599e184bfd1ace3569117c23ae17c5640f2f2d", + "wfMessage": { + "MessageHeader": { + "Prefix": "WF", + "Version": "1", + "EncryptionIndicator": "2", + "MessageCode": "F", + "DuressIndicator": "0", + "ReferenceIndicator": "0", + "ReferencedMessage": "0000000011111111222222223333333344444444555555556666666677777777" + }, + "MessageBody": { + "Text": "a0a0a0a0b1b1b1b1c2c2c2c2d3d3d3d3e4e4e4e4f5f5f5f56767676789898989" + } + } + } +} \ No newline at end of file diff --git a/test/crypto/cipher.js b/test/crypto/cipher.js new file mode 100644 index 00000000..7557dd2c --- /dev/null +++ b/test/crypto/cipher.js @@ -0,0 +1,60 @@ +'use strict'; +/** + * @module test/crypto/cipher + * @summary Whiteflag JS encryption and decryption functions tests + */ + +/* Test framework */ +import { describe as testCase } from 'mocha'; +import { it as assertion } from 'mocha'; +import { deepStrictEqual } from 'assert'; + +/* Functions required for test */ +import { hexToU8a, u8aToHex } from '@whiteflagprotocol/util'; + +/* Functions to test */ +import { encrypt, decrypt, createAesKey } from '@whiteflagprotocol/crypto'; + +/* Test data */ +import testVector from './cipher.json' with { type: 'json' }; +const AES256CTR_PSK = '2'; // pre-shared key + +/* TEST SCRIPT */ +testCase('Crypto cipher module', function() { + testCase('AES 256 counter mode', function() { + assertion(' 1. should pass NIST SP 800-38A F.5.5 CTR-AES256 encryption', async function() { + const plaintext = hexToU8a( + testVector['1'].plaintext1 + + testVector['1'].plaintext2 + + testVector['1'].plaintext3 + + testVector['1'].plaintext4 + ); + const cyphertext = + testVector['1'].ciphertext1 + + testVector['1'].ciphertext2 + + testVector['1'].ciphertext3 + + testVector['1'].ciphertext4; + const key = await createAesKey(hexToU8a(testVector['1'].key), testVector['1'].algorithm); + const iv = hexToU8a(testVector['1'].initCounter); + const encrypted = await encrypt(plaintext, AES256CTR_PSK, key, iv); + return deepStrictEqual(u8aToHex(encrypted), cyphertext); + }); + assertion(' 2. should pass NIST SP 800-38A F.5.6 CTR-AES256 decryption', async function() { + const ciphertext = hexToU8a( + testVector['2'].ciphertext1 + + testVector['2'].ciphertext2 + + testVector['2'].ciphertext3 + + testVector['2'].ciphertext4 + ); + const plaintext = + testVector['2'].plaintext1 + + testVector['2'].plaintext2 + + testVector['2'].plaintext3 + + testVector['2'].plaintext4; + const key = await createAesKey(hexToU8a(testVector['2'].key), testVector['2'].algorithm); + const iv = hexToU8a(testVector['2'].initCounter); + const decrypted = await decrypt(ciphertext, AES256CTR_PSK, key, iv); + return deepStrictEqual(u8aToHex(decrypted), plaintext); + }); + }); +}); diff --git a/test/crypto/cipher.json b/test/crypto/cipher.json new file mode 100644 index 00000000..97b98720 --- /dev/null +++ b/test/crypto/cipher.json @@ -0,0 +1,30 @@ +{ + "1": { + "$description": "NIST SP 800-38A F.5.5 CTR-AES256.Encrypt", + "algorithm": "AES-CTR", + "key": "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", + "initCounter": "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "plaintext1": "6bc1bee22e409f96e93d7e117393172a", + "ciphertext1": "601ec313775789a5b7a7f504bbf3d228", + "plaintext2": "ae2d8a571e03ac9c9eb76fac45af8e51", + "ciphertext2": "f443e3ca4d62b59aca84e990cacaf5c5", + "plaintext3": "30c81c46a35ce411e5fbc1191a0a52ef", + "ciphertext3": "2b0930daa23de94ce87017ba2d84988d", + "plaintext4": "f69f2445df4f9b17ad2b417be66c3710", + "ciphertext4": "dfc9c58db67aada613c2dd08457941a6" + }, + "2": { + "$description": "NIST SP 800-38A F.5.6 CTR-AES256.Decrypt", + "algorithm": "AES-CTR", + "key": "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", + "initCounter": "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "ciphertext1": "601ec313775789a5b7a7f504bbf3d228", + "plaintext1": "6bc1bee22e409f96e93d7e117393172a", + "ciphertext2": "f443e3ca4d62b59aca84e990cacaf5c5", + "plaintext2": "ae2d8a571e03ac9c9eb76fac45af8e51", + "ciphertext3": "2b0930daa23de94ce87017ba2d84988d", + "plaintext3": "30c81c46a35ce411e5fbc1191a0a52ef", + "ciphertext4": "dfc9c58db67aada613c2dd08457941a6", + "plaintext4": "f69f2445df4f9b17ad2b417be66c3710" + } +} \ No newline at end of file diff --git a/test/crypto/hash.js b/test/crypto/hash.js new file mode 100644 index 00000000..f5690913 --- /dev/null +++ b/test/crypto/hash.js @@ -0,0 +1,84 @@ +'use strict'; +/** + * @module test/crypto/hash + * @summary Whiteflag JS hashing functions tests + */ + +/* Test framework */ +import { describe as testCase } from 'mocha'; +import { it as assertion } from 'mocha'; +import { strictEqual, rejects } from 'assert'; + +/* Functions required for test */ +import { hexToU8a, u8aToHex } from '@whiteflagprotocol/util'; + +/* Functions to test */ +import { hkdf, hmac, hash } from '@whiteflagprotocol/crypto'; + +/* Test data */ +import testVector from './hash.json' with { type: 'json' }; + +/* TEST SCRIPT */ +testCase('Crypto hashing module', function() { + testCase('Basic hash function using SHA-256', function() { + assertion(' 1. should pass FIPS 180-4 SHA-256 32 bit message test', async function() { + const msg = hexToU8a(testVector['1'].Msg); + const digest = u8aToHex(await hash(msg)); + return strictEqual(digest, testVector['1'].MD); + }); + assertion(' 2. should pass FIPS 180-4 SHA-256 128 bit message test', async function() { + const msg = hexToU8a(testVector['2'].Msg); + const digest = u8aToHex(await hash(msg)); + return strictEqual(digest, testVector['2'].MD); + }); + assertion(' 3. should pass FIPS 180-4 SHA-256 512 bit message test', async function() { + const msg = hexToU8a(testVector['3'].Msg); + const digest = u8aToHex(await hash(msg)); + return strictEqual(digest, testVector['3'].MD); + }); + }); + testCase('Hash-based Message Authentication Code using SHA-256', function() { + assertion(' 4. should pass FIPS 198-1 SHA-256 HMAC Test Vector 132', async function() { + const key = hexToU8a(testVector['4'].Key); + const msg = hexToU8a(testVector['4'].Msg); + const mac = u8aToHex(await hmac(key, msg)); + return strictEqual(mac, testVector['4'].Mac); + }); + assertion(' 5. should pass FIPS 198-1 SHA-256 HMAC Test Vector 211', async function() { + const key = hexToU8a(testVector['5'].Key); + const msg = hexToU8a(testVector['5'].Msg); + const mac = u8aToHex(await hmac(key, msg)); + return strictEqual(mac, testVector['5'].Mac); + }); + }); + testCase('Hash-based Key Derivation Function using SHA-256', function() { + assertion(' 6. should pass RFC 5869 Test Case 1', async function() { + const ikm = hexToU8a(testVector['6'].IKM); + const salt = hexToU8a(testVector['6'].salt); + const info = hexToU8a(testVector['6'].info); + const keylen = testVector['6'].L; + const okm = u8aToHex(await hkdf(ikm, salt, info, keylen)); + return strictEqual(okm, testVector['6'].OKM); + }); + assertion(' 7. should pass RFC 5869 Test Case 2', async function() { + const ikm = hexToU8a(testVector['7'].IKM); + const salt = hexToU8a(testVector['7'].salt); + const info = hexToU8a(testVector['7'].info); + const keylen = testVector['7'].L; + const okm = u8aToHex(await hkdf(ikm, salt, info, keylen)); + return strictEqual(okm, testVector['7'].OKM); + }); + assertion(' 8. should give zero-length key error for RFC 5869 Test Case 3', async function() { + const ikm = hexToU8a(testVector['8'].IKM); + const salt = hexToU8a(testVector['8'].salt); + const info = hexToU8a(testVector['8'].info); + const keylen = testVector['8'].L; + + /* Test should result in zero-length key error for empty salt */ + await rejects(async () => { + const okm = u8aToHex(await hkdf(ikm, salt, info, keylen)); + return strictEqual(okm, testVector['6'].OKM); + }, DOMException); + }); + }); +}); diff --git a/test/crypto/hash.json b/test/crypto/hash.json new file mode 100644 index 00000000..f519fd2c --- /dev/null +++ b/test/crypto/hash.json @@ -0,0 +1,65 @@ +{ + "1": { + "$description": "FIPS 180-4 SHA-256 Short Message tests for BYTE oriented implementations", + "L": 32, + "Msg": "74ba2521", + "MD": "b16aa56be3880d18cd41e68384cf1ec8c17680c45a02b1575dc1518923ae8b0e" + }, + "2": { + "$description": "FIPS 180-4 SHA-256 Short Message tests for BYTE oriented implementations", + "L": 128, + "Msg": "0a27847cdc98bd6f62220b046edd762b", + "MD": "80c25ec1600587e7f28b18b1b18e3cdc89928e39cab3bc25e4d4a4c139bcedc4" + }, + "3": { + "$description": "FIPS 180-4 SHA-256 Short Message tests for BYTE oriented implementations", + "L": 512, + "Msg": "5a86b737eaea8ee976a0a24da63e7ed7eefad18a101c1211e2b3650c5187c2a8a650547208251f6d4237e661c7bf4c77f335390394c37fa1a9f9be836ac28509", + "MD": "42e61e174fbb3897d6dd6cef3dd2802fe67b331953b06114a65c772859dfc1aa" + }, + "4": { + "$description": "FIPS 198-1 SHA-256 HMAC Test Vector 132", + "Count": 132, + "Klen": 64, + "Tlen": 32, + "Key": "fd013d615c6ca959030a520e148808a07e27d38a215634d53486ae8be43a856f3e5dc6eb4fd9874a8a6570276a9e7b25585af7e1ce39d325bd7d195f2c1bb951", + "Msg": "91ea78334108ce6261ddee5d98045bb307a6e8f3d0ee65c1d9bc7d28cd9edf3264fc9cb6e592d072e9238559616cd42eda584d5200729adb619f5ee5740d632dda67f5dce34b89a054fda301685df6f31416cca78f19a8a7124a2a22dd7834847a934b4a451940152cd20ffdb4bd07273c4a2b9a86c9d94e7323a9860ec89860", + "Mac": "eb5aaa4ee702ff7b5324bc72c98fe87df6d9cc342b053ebce6cbf27fdea0eabf" + }, + "5": { + "$description": "FIPS 198-1 SHA-256 HMAC Test Vector 211", + "Count": 211, + "Klen": 74, + "Tlen": 32, + "Key": "44f71c2317cde52151c84260d1d3c04a28cc15ce5b3802b2e5357e2bfcaf10ab15d77dfaaad1a3883bada502939948234c559dcd95e7e158338fa12ac6fd21874ec2ffabed051416ef77", + "Msg": "2ac0bb0524c22b902de34ce64e6172d1b2074e159f517ab1abd152622cd10669f03aed8e2eb51c65bd0f38d084e288c532724e512fd558ddd257d2b1d41c5eb6040767803ddbb18b95a035c5d8492d4d35936b7b3630ee20f625b70f8e71d9dcd0efd0e3387d138c1f5eedce32dd88f223334b9a9eab65017f04aa8442179f62", + "Mac": "ca0053d51f6cf6f9998ff1e0db00b90e82c7b18cb5377acc8ebe9afe20da1c3d" + }, + "6": { + "$description": "RFC 5869 A.1 Test Case 1", + "IKM": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "salt": "000102030405060708090a0b0c", + "info": "f0f1f2f3f4f5f6f7f8f9", + "L": 42, + "PKM": "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5", + "OKM": "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865" + }, + "7": { + "$description": "RFC 5869 A.2 Test Case 2", + "IKM": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f", + "salt": "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf", + "info": "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "L": 82, + "PKM": "06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244", + "OKM": "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87" + }, + "8": { + "$description": "RFC 5869 A.3 Test Case 3", + "IKM": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "salt": "", + "info": "", + "L": 42, + "PKM": "19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04", + "OKM": "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8" + } +} \ No newline at end of file diff --git a/test/util/binary.js b/test/util/binary.js new file mode 100644 index 00000000..25d9511d --- /dev/null +++ b/test/util/binary.js @@ -0,0 +1,123 @@ +'use strict'; +/** + * @module test/util/binary + * @summary Whiteflag JS binary buffer class tests + */ + +/* Test framework */ +import { describe as testCase } from 'mocha'; +import { it as assertion } from 'mocha'; +import { deepStrictEqual } from 'assert'; + +/* Functions required for test */ +import { hexToU8a, u8aToHex } from '@whiteflagprotocol/util'; + +/* Functions to test */ +import { + BinaryBuffer, + cropBits, + shiftRight, + shiftLeft +} from '@whiteflagprotocol/util'; + +/* Test data */ +import testVector from './binary.json' with { type: 'json' }; + +/* TEST SCRIPT */ +testCase('Util binary buffer module', function() { + testCase('Binary manupulation functions', function() { + assertion(' 1. should correctly crop bits of UInt8Array', function(done) { + const C1 = cropBits(hexToU8a(testVector['1'].hex), testVector['1'].bitLength); + deepStrictEqual(u8aToHex(C1), testVector['1'].crop.hex); + const C2 = cropBits(hexToU8a(testVector['1'].hex), testVector['1'].crop.nBits); + deepStrictEqual(u8aToHex(C1), u8aToHex(C2)); + return done(); + }); + assertion(' 2. should correctly right shift bits of UInt8Array', function(done) { + const R = shiftRight(hexToU8a(testVector['1'].hex), testVector['1'].shift.nBits); + deepStrictEqual(u8aToHex(R), testVector['1'].shift.right.hex); + const L = shiftLeft(hexToU8a(testVector['1'].hex), -(testVector['1'].shift.nBits)); + deepStrictEqual(L, R); + return done(); + }); + assertion(' 3. should correctly left shift bits of UInt8Array', function(done) { + const L = shiftLeft(hexToU8a(testVector['1'].hex), testVector['1'].shift.nBits); + deepStrictEqual(u8aToHex(L), testVector['1'].shift.left.hex); + const R = shiftRight(hexToU8a(testVector['1'].hex), -(testVector['1'].shift.nBits)); + deepStrictEqual(R, L); + return done(); + }); + }); + testCase('Binary buffer class', function() { + assertion(' 2a. should correctly create binary buffer from UInt8Array', function(done) { + const buffer = BinaryBuffer.fromU8a(hexToU8a(testVector['1'].hex), testVector['1'].crop.nBits); + deepStrictEqual(buffer.toHex(), testVector['1'].crop.hex); + deepStrictEqual(buffer.length, testVector['1'].bitLength); + return done(); + }); + assertion(' 2b. should correctly create binary buffer from hexadecimal string', function(done) { + const buffer = BinaryBuffer.fromHex(testVector['2'].hex); + deepStrictEqual(buffer.toHex(), testVector['2'].hex); + deepStrictEqual(buffer.length, testVector['2'].bitLength); + return done(); + }); + assertion(' 3a. should correctly append, insert and shift data', function(done) { + /* Append data */ + const A = BinaryBuffer.fromHex(testVector['3'].hex1, testVector['3'].bitLength1); + deepStrictEqual(A.length, testVector['3'].bitLength1); + A.appendHex(testVector['3'].hex2, testVector['3'].bitLength2); + deepStrictEqual(A.toHex(), testVector['3'].append.hex); + deepStrictEqual(A.length, testVector['3'].append.bitLength); + + /* Insert data */ + const I = BinaryBuffer.fromHex(testVector['3'].hex1, testVector['3'].bitLength1); + I.insertHex(testVector['3'].hex2, testVector['3'].bitLength2); + deepStrictEqual(I.toHex(), testVector['3'].insert.hex); + deepStrictEqual(I.length, testVector['3'].insert.bitLength); + + /* Shift */ + deepStrictEqual(I.shiftRight(testVector['3'].shift.nBits).toHex(), testVector['3'].shift.right.hex); + deepStrictEqual(I.shiftRight(-testVector['3'].shift.nBits).toHex(), testVector['3'].insert.hex); + deepStrictEqual(I.shiftLeft(testVector['3'].shift.nBits).toHex(), testVector['3'].shift.left.hex); + + return done(); + }); + assertion(' 3b. should correctly append and insert data (test vector 4)', function(done) { + const A = BinaryBuffer.fromHex(testVector['4'].hex1); + deepStrictEqual(A.length, testVector['4'].bitLength1); + A.appendHex(testVector['4'].hex2); + deepStrictEqual(A.toHex(), testVector['4'].append.hex); + deepStrictEqual(A.length, testVector['4'].append.bitLength); + return done(); + }); + assertion(' 3c. should correctly append and insert data (test vector 5)', function(done) { + const A = BinaryBuffer.fromHex(testVector['5'].hex1, testVector['5'].bitLength1); + deepStrictEqual(A.length, testVector['5'].bitLength1); + A.appendHex(testVector['5'].hex2); + deepStrictEqual(A.toHex(), testVector['5'].append.hex); + deepStrictEqual(A.length, testVector['5'].append.bitLength); + return done(); + }); + assertion(' 4a. should correctly extract bits (test vector 3)', function(done) { + const E = BinaryBuffer.fromU8a(hexToU8a(testVector['3'].hex1), testVector['3'].bitLength1); + E.appendU8a(hexToU8a(testVector['3'].hex2), testVector['3'].bitLength2); + const extract = E.extractHex(testVector['3'].extract.startBit, testVector['3'].extract.endBit); + deepStrictEqual(extract, testVector['3'].extract.hex); + return done(); + }); + assertion(' 4b. should correctly extract bits (test vector 4)', function(done) { + const E = BinaryBuffer.fromHex(testVector['4'].hex1); + E.appendHex(testVector['4'].hex2); + const extract = E.extractHex(testVector['4'].extract.startBit, testVector['4'].extract.endBit); + deepStrictEqual(extract, testVector['4'].extract.hex); + return done(); + }); + assertion(' 4c. should correctly extract bits (test vector 5)', function(done) { + const E = BinaryBuffer.fromHex(testVector['5'].hex1, testVector['5'].bitLength1); + E.appendHex(testVector['5'].hex2); + const extract = E.extractHex(testVector['5'].extract.startBit, testVector['5'].extract.endBit); + deepStrictEqual(extract, testVector['5'].extract.hex); + return done(); + }); + }); +}) diff --git a/test/util/binary.json b/test/util/binary.json new file mode 100644 index 00000000..6376d6c1 --- /dev/null +++ b/test/util/binary.json @@ -0,0 +1,73 @@ +{ + "1": { + "hex": "f00fff", + "bitLength": 14, + "crop": { + "nBits": -10, + "hex": "f00c" + }, + "shift": { + "nBits": 3, + "left": { "hex": "807ff8" }, + "right": { "hex": "1e01ffe0" } + } + }, + "2": { + "hex": "57463130a6a1f7da7067d41891592131a12a60c9053b4eb0aefe6263385da9f5b789421e1d7401009841882148a800000114c1e596006f04c050eca6420084", + "bitLength": 504 + }, + "3": { + "hex1": "ffffff", + "bitLength1": 21, + "hex2": "5555", + "bitLength2": 10, + "append": { + "hex": "fffffaaa", + "bitLength": 31 + }, + "extract": { + "startBit": 14, + "endBit": 27, + "hex": "fea8" + }, + "insert": { + "hex": "557ffffe", + "bitLength": 31 + }, + "shift": { + "nBits": 5, + "left": { "hex": "afffffc0" }, + "right": { "hex": "02abfffff0" } + } + }, + "4": { + "hex1": "afff", + "bitLength1": 16, + "hex2": "6a", + "bitLength2": 8, + "append": { + "hex": "afff6a", + "bitLength": 24 + }, + "extract": { + "startBit": 12, + "endBit": 20, + "hex": "f6" + } + }, + "5": { + "hex1": "bfff", + "bitLength1": 14, + "hex2": "6affff", + "bitLength2": 24, + "append": { + "hex": "bffdabfffc", + "bitLength": 38 + }, + "extract": { + "startBit": 10, + "endBit": 20, + "hex": "f680" + } + } +} \ No newline at end of file diff --git a/test/util/convert.js b/test/util/convert.js deleted file mode 100644 index 722f1d5a..00000000 --- a/test/util/convert.js +++ /dev/null @@ -1,107 +0,0 @@ -'use strict'; -/** - * @module test/util/convert - * @summary Whiteflag JS common data conversions tests - */ - -/* Node.js core and external modules */ -import { describe as testCase } from 'mocha'; -import { it as assertion } from 'mocha'; -import { deepStrictEqual } from 'assert'; -import { readFileSync } from 'fs'; - -/* Functions to test */ -import { - b64uToHex, - b64uToString, - b64uToU8a, - hexToB64u, - hexToString, - hexToU8a, - stringToB64u, - stringToHex, - stringToU8a, - u8aToB64u, - u8aToHex, - u8aToString -} from '@whiteflag/util'; - -/* Constants */ -/** - * @constant {Object} testVector - * @description Defines the common array functions test data - */ -const testVector = JSON.parse(readFileSync('./test/util/convert.json')); - -/* TEST SCRIPT */ -testCase('Util encoding module', function() { - const testVectorU8a = Uint8Array.from(testVector.Uint8Array.array); - testCase('Character string encoding', function() { - assertion(' 1. should correctly convert to UInt8Array', function(done) { - const u8array = stringToU8a(testVector.char.string); - deepStrictEqual(u8array, testVectorU8a); - return done(); - }); - assertion(' 2. should correctly convert to base4url string', function(done) { - const b64uString = stringToB64u(testVector.char.string); - deepStrictEqual(b64uString, testVector.base64url.string); - return done(); - }); - assertion(' 3. should correctly convert to hexadecimal string', function(done) { - const hexString = stringToHex(testVector.char.string); - deepStrictEqual(hexString, testVector.hex.string); - return done(); - }); - }); - testCase('Base64URL encoding', function() { - assertion(' 4. should correctly convert to UInt8Array', function(done) { - const u8array = b64uToU8a(testVector.base64url.string); - deepStrictEqual(u8array, testVectorU8a); - return done(); - }); - assertion(' 5. should correctly convert to standard string', function(done) { - const charString = b64uToString(testVector.base64url.string); - deepStrictEqual(charString, testVector.char.string); - return done(); - }); - assertion(' 6. should correctly convert to hexadecimal string', function(done) { - const hexString = b64uToHex(testVector.base64url.string); - deepStrictEqual(hexString, testVector.hex.string); - return done(); - }); - }); - testCase('Hexadecimal encoding', function() { - assertion(' 7. should correctly transform to UInt8Array', function(done) { - const u8array = hexToU8a(testVector.hex.string); - deepStrictEqual(u8array, testVectorU8a); - return done(); - }); - assertion(' 8. should correctly convert to standard string', function(done) { - const charString = hexToString(testVector.hex.string); - deepStrictEqual(charString, testVector.char.string); - return done(); - }); - assertion(' 9. should correctly convert to base4url string', function(done) { - const b64uString = hexToB64u(testVector.hex.string); - deepStrictEqual(b64uString, testVector.base64url.string); - return done(); - }); - }); - testCase('Binary encoding', function() { - assertion('10. should correctly convert to hexadecimal string', function(done) { - const hexString = u8aToHex(testVectorU8a); - deepStrictEqual(hexString, testVector.hex.string); - return done(); - }); - assertion('11. should correctly convert to standard string', function(done) { - const charString = u8aToString(testVectorU8a); - deepStrictEqual(charString, testVector.char.string); - return done(); - }); - assertion('12. should correctly convert to base4url string', function(done) { - const b64uString = u8aToB64u(testVectorU8a); - deepStrictEqual(b64uString, testVector.base64url.string); - return done(); - }); - }); -}); diff --git a/test/util/convert.json b/test/util/convert.json deleted file mode 100644 index 2c479dae..00000000 --- a/test/util/convert.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "hex": { - "string": "5768697465666c61673a353734362f746573742d3031" - }, - "char": { - "string": "Whiteflag:5746/test-01" - }, - "base64url": { - "string": "V2hpdGVmbGFnOjU3NDYvdGVzdC0wMQ" - }, - "Uint8Array": { - "array": [ 87, 104, 105, 116, 101, 102, 108, 97, 103, 58, 53, 55, 52, 54, 47, 116, 101, 115, 116, 45, 48, 49 ] - } -} \ No newline at end of file diff --git a/test/util/encoding.js b/test/util/encoding.js new file mode 100644 index 00000000..69206657 --- /dev/null +++ b/test/util/encoding.js @@ -0,0 +1,162 @@ +'use strict'; +/** + * @module test/util/encoding + * @summary Whiteflag JS common encoding and data conversion tests + */ + +/* Test framework */ +import { describe as testCase } from 'mocha'; +import { it as assertion } from 'mocha'; +import { strictEqual, deepStrictEqual } from 'assert'; + +/* Functions to test */ +import { + isBase64, + isBase64u, + isHex, + objToB64u, + b64uToObj, + b64ToB64u, + b64uToB64, + b64uToHex, + b64uToString, + b64uToU8a, + hexToB64u, + hexToString, + hexToU8a, + stringToB64u, + stringToHex, + stringToU8a, + u8aToB64u, + u8aToHex, + u8aToString +} from '@whiteflagprotocol/util'; + +/* Test data */ +import testVector from './encoding.json' with { type: 'json' }; + +/* TEST SCRIPT */ +testCase('Util encoding module', function() { + const testVectorU8a = Uint8Array.from(testVector['0'].Uint8Array); + testCase('Character string encoding', function() { + assertion(' 1a. should correctly convert to UInt8Array', function(done) { + const u8array = stringToU8a(testVector['0'].string); + deepStrictEqual(u8array, testVectorU8a); + return done(); + }); + assertion(' 1b. should correctly convert to base4url string', function(done) { + const b64uString = stringToB64u(testVector['0'].string); + deepStrictEqual(b64uString, testVector['0'].base64url); + return done(); + }); + assertion(' 1c. should correctly convert to hexadecimal string', function(done) { + const hexString = stringToHex(testVector['0'].string); + deepStrictEqual(hexString, testVector['0'].hex); + return done(); + }); + }); + testCase('Base64 encoding', function() { + assertion(' 2a. should correctly identify non-base64 string', function(done) { + strictEqual(isBase64(testVector['0'].string), false); + return done(); + }); + assertion(' 2b. should correctly identify base64 string', function(done) { + strictEqual(isBase64(testVector['0'].base64), true); + return done(); + }); + assertion(' 2c. should correctly convert to base64url string', function(done) { + const b64uString = b64ToB64u(testVector['0'].base64); + deepStrictEqual(b64uString, testVector['0'].base64url); + return done(); + }); + }); + testCase('Base64URL encoding', function() { + assertion(' 3a. should correctly identify non-base64url string', function(done) { + strictEqual(isBase64u(testVector['0'].base64), false); + return done(); + }); + assertion(' 3b. should correctly identify base64url string', function(done) { + strictEqual(isBase64u(testVector['0'].base64url), true); + return done(); + }); + assertion(' 4a. should correctly convert to UInt8Array', function(done) { + const u8array = b64uToU8a(testVector['0'].base64url); + deepStrictEqual(u8array, testVectorU8a); + return done(); + }); + assertion(' 4b. should correctly convert to standard string', function(done) { + const charString = b64uToString(testVector['0'].base64url); + deepStrictEqual(charString, testVector['0'].string); + return done(); + }); + assertion(' 4c. should correctly convert to hexadecimal string', function(done) { + const hexString = b64uToHex(testVector['0'].base64url); + deepStrictEqual(hexString, testVector['0'].hex); + return done(); + }); + assertion(' 4c. should correctly convert to base64 string', function(done) { + const b64String = b64uToB64(testVector['0'].base64url); + deepStrictEqual(b64String, testVector['0'].base64); + return done(); + }); + }); + testCase('Hexadecimal encoding', function() { + assertion(' 5a. should correctly identify non-hexadecimal string', function(done) { + strictEqual(isHex(testVector['0'].string), false); + strictEqual(isHex(testVector['0'].base64), false); + return done(); + }); + assertion(' 5b. should correctly identify hexadecimal string', function(done) { + strictEqual(isHex(testVector['0'].hex), true); + strictEqual(isHex(testVector['0'].hexp), true); + return done(); + }); + assertion(' 5b. should correctly transform to UInt8Array', function(done) { + const u8array = hexToU8a(testVector['0'].hexp); + deepStrictEqual(u8array, testVectorU8a); + return done(); + }); + assertion(' 5c. should correctly convert to standard string', function(done) { + const charString = hexToString(testVector['0'].hex); + deepStrictEqual(charString, testVector['0'].string); + return done(); + }); + assertion(' 5c. should correctly convert to base4url string', function(done) { + const b64uString = hexToB64u(testVector['0'].hex); + deepStrictEqual(b64uString, testVector['0'].base64url); + return done(); + }); + }); + testCase('Binary encoding', function() { + assertion(' 6a. should correctly convert to hexadecimal string', function(done) { + const hexString = u8aToHex(testVectorU8a); + deepStrictEqual(hexString, testVector['0'].hex); + return done(); + }); + assertion(' 6b. should correctly convert to standard string', function(done) { + const charString = u8aToString(testVectorU8a); + deepStrictEqual(charString, testVector['0'].string); + return done(); + }); + assertion(' 6c. should correctly convert to base4url string', function(done) { + const b64uString = u8aToB64u(testVectorU8a); + deepStrictEqual(b64uString, testVector['0'].base64url); + return done(); + }); + }); + testCase('JSON Base64URL encoding', function() { + assertion(' 7. should correctly convert random object to base64URL and back', function(done) { + const obj = { prop1: 'string', prop2: { key: 'value'}, prop3: [ "one", "two"] }; + const str = objToB64u(obj); + strictEqual(isBase64u(str), true); + deepStrictEqual(obj, b64uToObj(str)); + return done(); + }); + assertion(' 8. should correctly convert RFC 7515 Annex A.1.2 header example to base64URL', function(done) { + const obj = testVector['0'].object.plain.protected; + const encoded = testVector['0'].object.base64url; + deepStrictEqual(objToB64u(obj), encoded); + return done(); + }); + }); +}); diff --git a/test/util/encoding.json b/test/util/encoding.json new file mode 100644 index 00000000..0ae1d0ef --- /dev/null +++ b/test/util/encoding.json @@ -0,0 +1,19 @@ +{ + "0": { + "hex": "5768697465666c61673a353734362f746573742d30313f3f3f", + "hexp": "0x5768697465666c61673a353734362f746573742d30313f3f3f", + "string": "Whiteflag:5746/test-01???", + "base64": "V2hpdGVmbGFnOjU3NDYvdGVzdC0wMT8/Pw==", + "base64url": "V2hpdGVmbGFnOjU3NDYvdGVzdC0wMT8_Pw", + "Uint8Array": [ 87, 104, 105, 116, 101, 102, 108, 97, 103, 58, 53, 55, 52, 54, 47, 116, 101, 115, 116, 45, 48, 49, 63, 63, 63 ], + "object": { + "$description": "RFC 7515 A.2.1 header encoding example", + "base64url": "eyJhbGciOiJSUzI1NiJ9", + "plain": { + "protected": { + "alg":"RS256" + } + } + } + } +} \ No newline at end of file diff --git a/test/util/jws.js b/test/util/jws.js new file mode 100644 index 00000000..f8f0c967 --- /dev/null +++ b/test/util/jws.js @@ -0,0 +1,89 @@ +'use strict'; +/** + * @module test/util/jws + * @summary Whiteflag JS JSON Web Signature functions tests + */ + +/* Test framework */ +import { describe as testCase } from 'mocha'; +import { it as assertion } from 'mocha'; +import { strictEqual, deepStrictEqual, throws } from 'assert'; + +/* Functions to test */ +import { Jws } from '@whiteflagprotocol/util'; + +/* Test data */ +import testVector from './jws.json' with { type: 'json' }; + +/* TEST SCRIPT */ +testCase('Util JWS module', function() { + testCase('JWS variants i.a.w. Whiteflag Standard Annex C example', function() { + assertion(' 3a. should correctly transform flattened JWS to compact JWS', function(done) { + const jws = Jws.fromObject(testVector['1'].flat); + deepStrictEqual(jws.toCompact(), testVector['1'].compact); + return done(); + }); + assertion(' 3b. should correctly transform full JWS to compact JWS', function(done) { + const jws = Jws.fromObject(testVector['1'].full); + deepStrictEqual(jws.toCompact(), testVector['1'].compact); + return done(); + }); + assertion(' 4a. should correctly transform compact JWS to flattened JWS', function(done) { + const jws = Jws.fromCompact(testVector['1'].compact); + deepStrictEqual(jws.toFlat(), testVector['1'].flat); + return done(); + }); + assertion(' 4b. should correctly transform full JWS to flattened JWS', function(done) { + const jws = Jws.fromObject(testVector['1'].full); + deepStrictEqual(jws.toFlat(), testVector['1'].flat); + return done(); + }); + assertion(' 5a. should correctly transform compact JWS to full JWS', function(done) { + const jws = Jws.fromCompact(testVector['1'].compact); + deepStrictEqual(jws.toFull(), testVector['1'].full); + return done(); + }); + assertion(' 5b. should correctly transform flattened JWS to full JWS', function(done) { + const jws = Jws.fromObject(testVector['1'].flat); + deepStrictEqual(jws.toFull(), testVector['1'].full); + return done(); + }); + }); + testCase('JWS class functions', function() { + assertion(' 6a. should correctly get signature input from signed JWS', function(done) { + const jws = Jws.fromObject(testVector['1'].flat); + deepStrictEqual(jws.getSignInput(), testVector['1'].signInput); + return done(); + }); + assertion(' 6b. should correctly get signature from signed JWS', function(done) { + const jws = Jws.fromCompact(testVector['1'].compact); + deepStrictEqual(jws.getSignature(), testVector['1'].full.signature); + return done(); + }); + assertion(' 7a. should not be able to change signed JWS', function(done) { + const jws = Jws.fromCompact(testVector['1'].compact); + strictEqual(jws.setSignature('dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'), false); + return done(); + }); + assertion(' 7b. should not be able to set algorithm for signed JWS', function(done) { + const jws = Jws.fromCompact(testVector['1'].compact); + strictEqual(jws.setSignAlgorithm('sr25519'), false); + return done(); + }); + assertion(' 8a. should be able to set algorithm for unsigned JWS', function(done) { + const jws = Jws.fromObject(testVector['2'].flat); + strictEqual(jws.setSignAlgorithm('sr25519'), true); + return done(); + }); + assertion(' 8b. should be not able to sign unsigned JWS with invalid signature encoding', function(done) { + const jws = Jws.fromObject(testVector['2'].full); + throws(() => jws.setSignature(testVector['2'].falseSignature), TypeError); + return done(); + }); + assertion(' 8c. should be able to sign unsigned JWS', function(done) { + const jws = Jws.fromObject(testVector['2'].full); + strictEqual(jws.setSignature('XWBRA1TrCxs8tpep1lLPcmpp9JlO_A0TJB5ULOROvadje3SgAsfkFEjE2DoHGpWJ_zNGlEPBtdUQo9MEypIp2Q'), true); + return done(); + }); + }); +}); diff --git a/test/util/jws.json b/test/util/jws.json new file mode 100644 index 00000000..24dfc55c --- /dev/null +++ b/test/util/jws.json @@ -0,0 +1,42 @@ +{ + "1": { + "$description": "Whiteflag Standard Annex C example", + "signInput": "eyJhbGciOiJFUzI1NiJ9.eyJhZGRyIjoiMUM4S1NLNjhTSmpmRFNCeDlCcFN4M3FCM2JlUGYyM3I3NyIsIm9yZ25hbWUiOiJPcmdhbmlzYXRpb24gTmFtZSIsInVybCI6Imh0dHBzOi8vb3JnYW5pc2F0aW9uLmludC93aGl0ZWZsYWcifQ", + "compact": "eyJhbGciOiJFUzI1NiJ9.eyJhZGRyIjoiMUM4S1NLNjhTSmpmRFNCeDlCcFN4M3FCM2JlUGYyM3I3NyIsIm9yZ25hbWUiOiJPcmdhbmlzYXRpb24gTmFtZSIsInVybCI6Imh0dHBzOi8vb3JnYW5pc2F0aW9uLmludC93aGl0ZWZsYWcifQ.XWBRA1TrCxs8tpep1lLPcmpp9JlO_A0TJB5ULOROvadje3SgAsfkFEjE2DoHGpWJ_zNGlEPBtdUQo9MEypIp2Q", + "flat": { + "protected": "eyJhbGciOiJFUzI1NiJ9", + "payload": "eyJhZGRyIjoiMUM4S1NLNjhTSmpmRFNCeDlCcFN4M3FCM2JlUGYyM3I3NyIsIm9yZ25hbWUiOiJPcmdhbmlzYXRpb24gTmFtZSIsInVybCI6Imh0dHBzOi8vb3JnYW5pc2F0aW9uLmludC93aGl0ZWZsYWcifQ", + "signature": "XWBRA1TrCxs8tpep1lLPcmpp9JlO_A0TJB5ULOROvadje3SgAsfkFEjE2DoHGpWJ_zNGlEPBtdUQo9MEypIp2Q" + }, + "full": { + "protected": { + "alg": "ES256" + }, + "payload": { + "addr": "1C8KSK68SJjfDSBx9BpSx3qB3bePf23r77", + "orgname": "Organisation Name", + "url": "https://organisation.int/whiteflag" + }, + "signature": "XWBRA1TrCxs8tpep1lLPcmpp9JlO_A0TJB5ULOROvadje3SgAsfkFEjE2DoHGpWJ_zNGlEPBtdUQo9MEypIp2Q" + } + }, + "2": { + "$description": "Whiteflag Standard Annex C unsigned example", + "signInput": "eyJhbGciOiJFUzI1NiJ9.eyJhZGRyIjoiMUM4S1NLNjhTSmpmRFNCeDlCcFN4M3FCM2JlUGYyM3I3NyIsIm9yZ25hbWUiOiJPcmdhbmlzYXRpb24gTmFtZSIsInVybCI6Imh0dHBzOi8vb3JnYW5pc2F0aW9uLmludC93aGl0ZWZsYWcifQ", + "falseSignature": "V2hpdGVmbGFnOjU3NDYvdGVzdC0wMT8/Pw==", + "flat": { + "protected": "eyJhbGciOiJFUzI1NiJ9", + "payload": "eyJhZGRyIjoiMUM4S1NLNjhTSmpmRFNCeDlCcFN4M3FCM2JlUGYyM3I3NyIsIm9yZ25hbWUiOiJPcmdhbmlzYXRpb24gTmFtZSIsInVybCI6Imh0dHBzOi8vb3JnYW5pc2F0aW9uLmludC93aGl0ZWZsYWcifQ" + }, + "full": { + "protected": { + "alg": "ES256" + }, + "payload": { + "addr": "1C8KSK68SJjfDSBx9BpSx3qB3bePf23r77", + "orgname": "Organisation Name", + "url": "https://organisation.int/whiteflag" + } + } + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 9024c9a6..831dd2c0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,24 @@ { "compilerOptions": { "target": "es2022", - "module": "commonjs", + "module": "preserve", + "esModuleInterop": true, + "resolveJsonModule": true, + "allowImportingTsExtensions": true, + "rewriteRelativeImportExtensions": true, "strict": true, - "declaration": true, + "noImplicitUseStrict": false, + "declaration": false, "removeComments": true, "rootDir": "./src", "outDir": "./dist", "paths": { - "@my-packages/protocol": ["./dist/protocol"], - "@my-packages/core": ["./dist/core"], - "@my-packages/crypto": ["./dist/crypto"], - "@my-packages/util": ["./dist/util"] - } + "@whiteflagprotocol/main": ["./src/main"], + "@whiteflagprotocol/core": ["./src/core"], + "@whiteflagprotocol/crypto": ["./src/crypto"], + "@whiteflagprotocol/util": ["./src/util"] + }, + "noImplicitThis": true, }, "include": ["./src/**/*"], "typedocOptions": { diff --git a/tsdoc.json b/tsdoc.json new file mode 100644 index 00000000..d35dfc8c --- /dev/null +++ b/tsdoc.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": [ "typedoc/tsdoc.json" ], + "noStandardTags": false, + "tagDefinitions": [ + { "tagName": "@wfreference", "syntaxKind": "block" }, + { "tagName": "@wfversion", "syntaxKind": "block" } + ] +} \ No newline at end of file