Options
All
  • Public
  • Public/Protected
  • All
Menu

@ndn/packet

This package is part of NDNts, Named Data Networking libraries for the modern web.

This package implements Name, Interest, and Data types as specified in NDN Packet Format v0.3.

import { TT, Name, Component, ImplicitDigest, AltUri, Interest, Data, digestSigning } from "@ndn/packet";

// other imports for examples
import { Decoder, Encoder, fromUtf8, toUtf8 } from "@ndn/tlv";
import { strict as assert } from "assert";
(async () => {

Name Component

// Name components are immutable. Once it's created, you can never change it.
// Construct a Component from its TLV-TYPE and TLV-VALUE.
const compA = new Component(TT.GenericNameComponent, Uint8Array.of(0x41));
// Create a Component from URI representation.
const compB = Component.from("B");
// Typed components are supported, too.
const compMetadata = Component.from("32=metadata");

// We can retrieve TLV-TYPE, TLV-LENGTH, and TLV-VALUE.
assert.equal(compA.type, TT.GenericNameComponent);
assert.equal(compB.type, TT.GenericNameComponent);
assert.equal(compMetadata.type, 0x20);

assert.equal(compA.length, 1);
assert.equal(compB.length, 1);
assert.equal(compMetadata.length, 8);

assert.deepEqual(compA.value, Uint8Array.of(0x41));
assert.deepEqual(compB.value, Uint8Array.of(0x42));
assert.deepEqual(compMetadata.value, Uint8Array.of(0x6D, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61));

// For convenience, we can retrieve TLV-VALUE as text string, too.
assert.equal(compA.text, "A");
assert.equal(compB.text, "B");
assert.equal(compMetadata.text, "metadata");

// Components are comparable.
assert.equal(compA.compare(compA), Component.CompareResult.EQUAL);
assert.equal(compA.compare(compB), Component.CompareResult.LT);
assert.equal(compB.compare(compA), Component.CompareResult.GT);
assert.equal(compA.equals(compA), true);
assert.equal(compA.equals(compB), false);

Name

// Names, like components, are immutable.
// Construct from URI.
const name1 = new Name("/localhost/2020=NDNts/rocks");
// Construct from a list of components, or strings to create components.
const name2 = new Name([compA, compB, "C", compMetadata]);
// Name parsing functions expect URI in canonical format. They DO NOT recognize alternate/pretty
// URI syntax other than allow omitting "8=" prefix of GenericNameComponent.

// You can always convert a name back to its URI in canonical format.
assert.equal(name1.toString(), "/8=localhost/2020=NDNts/8=rocks");
assert.equal(name2.toString(), "/8=A/8=B/8=C/32=metadata");

// AltUri.ofName() function allows printing a name as alternate/pretty URI syntax.
assert.equal(AltUri.ofName(name1), "/localhost/2020=NDNts/rocks");
assert.equal(AltUri.ofName(name2), "/A/B/C/32=metadata");
// AltUri.ofName() from this package only recognizes 0x01, 0x02, and 0x08 types. If you are using
// naming conventions from @ndn/naming-convention2 package, use the AltUri from that package.
// This feature isn't in the regular name.toString(), so that it does not unnecessarily increase
// browser bundle size in applications that do not need it.

// It's crucial to know how many name components you have.
assert.equal(name1.length, 3);
assert.equal(name2.length, 4);

// You can get an individual name component.
const name1comp1 = name1.get(1);
// It would return 'undefined' if the component does not exist, so we have to check.
if (typeof name1comp1 === "undefined") {
  assert.fail(); // This isn't supposed to happen for this name, though.
} else {
  assert.equal(name1comp1.text, "NDNts");
}

// To save the 'undefined' check, use at(i). It throws if the component does not exist.
assert.throws(() => name1.at(5));
assert.equal(name1.at(1).text, "NDNts");

// Slice the name to obtain part of it.
const name1sliced = name1.slice(1, 3);
assert.equal(name1sliced.toString(), "/2020=NDNts/8=rocks");

// Or, get the prefix.
const name2prefix = name2.getPrefix(3);
assert.equal(name2prefix.toString(), "/8=A/8=B/8=C");

// Indexing from the back is supported, too.
assert.equal(name1.at(-1).text, "rocks");
assert.equal(name1.slice(-2).toString(), "/2020=NDNts/8=rocks");
assert.equal(name2.getPrefix(-1).toString(), "/8=A/8=B/8=C");

// Names are comparable.
const nameAB = new Name("/A/B");
const nameABB = new Name("/A/B/B");
const nameABC = new Name("/A/B/C");
const nameABD = new Name("/A/B/D");
const nameABCD = new Name("/A/B/C/D");
assert.equal(nameABC.equals(nameABC), true);
assert.equal(nameABC.equals(nameABD), false);
assert.equal(nameABC.compare(nameABC), Name.CompareResult.EQUAL);
assert.equal(nameABC.compare(nameABB), Name.CompareResult.GT);
assert.equal(nameABC.compare(nameABD), Name.CompareResult.LT);
assert.equal(nameABC.compare(nameABCD), Name.CompareResult.LPREFIX);
assert.equal(nameABC.compare(nameAB), Name.CompareResult.RPREFIX);

// LPREFIX means the first name is a strict prefix of the second name.
// It implies the first name is less than the second name.
// If you only care about the order, check if the result is less than zero.
assert(nameABC.compare(nameABCD) < 0);

// RPREFIX means the second name is a strict prefix of the first name.
// It implies the first name is greater than the second name.
// If you only care about the order, check if the result is greater than zero.
assert(nameABC.compare(nameAB) > 0);

// If you want to know whether a name is a prefix of another, it's EQUAL or LPREFIX.
// But we got a faster way:
assert.equal(nameABC.isPrefixOf(nameABC), true);
assert.equal(nameABC.isPrefixOf(nameABCD), true);
assert.equal(nameABC.isPrefixOf(nameAB), false);

// I said names are immutable, but you can surely modify them to get a new Name.
const name1modified = name1.getPrefix(-1).append("is", "awesome");
assert(name1modified.toString(), "/8=localhost/2020=NDNts/8=rocks/8=is/8=awesome");
assert(name1.toString(), "/8=localhost/2020=NDNts/8=rocks"); // unchanged

Layer-3 Packet Types: Interest and Data

// We have an Interest type, of course.
// You can set fields via constructor or setters.
const interest = new Interest(new Name("/A"), Interest.CanBePrefix, Interest.MustBeFresh);
interest.canBePrefix = false;
interest.lifetime = 2000;

// Encode and decode the Interest.
const interestWire = Encoder.encode(interest);
const interest2 = new Decoder(interestWire).decode(Interest);
assert.equal(interest2.name.toString(), "/8=A");

// We got a Data type, too.
// You can set fields via constructor or setters.
const data = new Data(interest.name, Data.FreshnessPeriod(5000));
data.content = toUtf8("hello NDNts");

Signing and Verification

// Every NDN Data must be signed.
// This package provides the low-level API, and an implementation of SHA256 digest signing.
// Other signature types are in @ndn/keychain package.

// Sign the Data. The API is asynchronous as required by WebCrypto.
await digestSigning.sign(data);

// After signing, we can encode the Data.
const dataWire = Encoder.encode(data);

// And then decode it.
const data2 = new Decoder(dataWire).decode(Data);

// Data signature should be verified.
// If the verify() function does not throw, it means the signature is good.
try {
  await digestSigning.verify(data);
} catch (err: unknown) {
  console.log(err);
  return;
}
// It's very important that you do not modify the Data if you need to verify its signature.
// Otherwise, you'll get errors or incorrect results.

// After verifying, we can access the Content.
assert.equal(fromUtf8(data2.content), "hello NDNts");

Implicit Digest

// To obtain implicit digest, we'll have to await, because it internally uses WebCrypto, which is async.
const digest = await data.computeImplicitDigest();
assert.equal(digest.length, 32);

// Full names are available, too.
const fullName = await data2.computeFullName();
assert.equal(fullName.length - 1, data2.name.length);
assert(fullName.at(-1).is(ImplicitDigest));

// After computation, implicit digest is cached on the Data instance,
// so we can get them without await:
const digest2 = data.getImplicitDigest();
const fullName2 = data.getFullName();
assert.equal(digest2, digest);
assert(typeof fullName2 !== "undefined");
assert.equal(fullName2.toString(), fullName.toString());

// Note that you cannot modify the Data after encoding or decoding,
// or you'll get incorrect implicit digest results.

Interest-Data Matching

// data.canSatisfy(interest) determines whether a Data satisfy an Interest.
// This is an async function because it potentially involves computing the implicit digest.

assert.equal(await data.canSatisfy(interest), true);
const interest3 = new Interest("/B");
assert.equal(await data.canSatisfy(interest3), false);
const data3 = new Decoder(dataWire).decode(Data);
const interestWithFullName = new Interest(fullName);
assert.equal(await data.canSatisfy(interestWithFullName), true);
})();

Index

References

AltUri

Re-exports AltUri

AltUri

Re-exports AltUri

AltUriConverter

Re-exports AltUriConverter

AltUriConverter

Re-exports AltUriConverter

Component

Re-exports Component

Component

Re-exports Component

ComponentLike

Re-exports ComponentLike

ComponentLike

Re-exports ComponentLike

Data

Re-exports Data

Decrypter

Re-exports Decrypter

Encrypter

Re-exports Encrypter

FwHint

Re-exports FwHint

ImplicitDigest

Re-exports ImplicitDigest

ImplicitDigest

Re-exports ImplicitDigest

Interest

Re-exports Interest

KeyLocator

Re-exports KeyLocator

LLDecrypt

Re-exports LLDecrypt

LLEncrypt

Re-exports LLEncrypt

LLSign

Re-exports LLSign

LLVerify

Re-exports LLVerify

Nack

Re-exports Nack

NackHeader

Re-exports NackHeader

NackReason

Re-exports NackReason

Name

Re-exports Name

Name

Re-exports Name

NameLike

Re-exports NameLike

NameLike

Re-exports NameLike

NamingConvention

Re-exports NamingConvention

NamingConvention

Re-exports NamingConvention

ParamsDigest

Re-exports ParamsDigest

ParamsDigest

Re-exports ParamsDigest

SigInfo

Re-exports SigInfo

SigType

Re-exports SigType

SignedInterestPolicy

Re-exports SignedInterestPolicy

Signer

Re-exports Signer

TT

Re-exports TT

Verifier

Re-exports Verifier

digestSigning

Re-exports digestSigning

noopEncryption

Re-exports noopEncryption

noopSigning

Re-exports noopSigning

nullSigner

Re-exports nullSigner

Type aliases

ComponentLike

ComponentLike: Component | string

NameLike

NameLike: Name | string

Variables

Const AltUri

AltUri: AltUriConverter = new AltUriConverter([new Generic(),ImplicitDigest,ParamsDigest,])

Print Generic, ImplicitDigest, ParamsDigest in alternate URI syntax.

Const CHARCODE_PERCENT

CHARCODE_PERCENT: number = "%".charCodeAt(0)

Const CHARCODE_PERIOD

CHARCODE_PERIOD: number = ".".charCodeAt(0)

Const CHAR_ENCODE

CHAR_ENCODE: string[] = (() => {const UNESCAPED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";const a = new Array<string>(256);for (let ch = 0x00; ch <= 0xFF; ++ch) {const s = String.fromCharCode(ch);a[ch] = UNESCAPED.includes(s) ? s : `%${ch.toString(16).padStart(2, "0").toUpperCase()}`;}return a;})()

Const DIGEST_LENGTH

DIGEST_LENGTH: 32 = 32

Const EVD

EVD: EvDecoder<Delegation> = new EvDecoder<FwHint.Delegation>("Delegation", TT.Delegation).add(TT.Preference, (t, { nni }) => t.preference = nni).add(TT.Name, (t, { decoder }) => t.name = decoder.decode(Name))

Const EVD

EVD: EvDecoder<KeyLocator> = new EvDecoder<KeyLocator>("KeyLocator", TT.KeyLocator).add(TT.Name, (t, { value }) => t.name = new Name(value)).add(TT.KeyDigest, (t, { value }) => t.digest = value)

Const EVD

EVD: EvDecoder<SigInfo> = new EvDecoder<SigInfo>("SigInfo", [TT.ISigInfo, TT.DSigInfo]).add(TT.SigType, (t, { nni }) => t.type = nni, { required: true }).add(TT.KeyLocator, (t, { decoder }) => t.keyLocator = decoder.decode(KeyLocator)).add(TT.SigNonce, (t, { value }) => t.nonce = value).add(TT.SigTime, (t, { nni }) => t.time = nni).add(TT.SigSeqNum, (t, { nni }) => t.seqNum = nni).setUnknown(EXTENSIONS.decodeUnknown)

Const EVD

EVD: EvDecoder<Fields> = new EvDecoder<Fields>("Interest", TT.Interest).add(TT.Name, (t, { decoder }) => t.name = decoder.decode(Name), { required: true }).add(TT.CanBePrefix, (t) => t.canBePrefix = true).add(TT.MustBeFresh, (t) => t.mustBeFresh = true).add(TT.ForwardingHint, (t, { value }) => t.fwHint = FwHint.decodeValue(value)).add(TT.Nonce, (t, { value }) => t.nonce = NNI.decode(value, { len: 4 })).add(TT.InterestLifetime, (t, { nni }) => t.lifetime = nni).add(TT.HopLimit, (t, { value }) => t.hopLimit = NNI.decode(value, { len: 1 })).add(TT.AppParameters, (t, { value, tlv, after }) => {if (ParamsDigest.findIn(t.name, false) < 0) {throw new Error("ParamsDigest missing in parameterized Interest");}t.appParameters = value;t.paramsPortion = new Uint8Array(tlv.buffer, tlv.byteOffset,tlv.byteLength + after.byteLength);}).add(TT.ISigInfo, (t, { decoder }) => t.sigInfo = decoder.decode(SigInfo)).add(TT.ISigValue, (t, { value, tlv }) => {if (!t.name.at(-1).is(ParamsDigest)) {throw new Error("ParamsDigest missing or out of place in signed Interest");}if (!t.paramsPortion) {throw new Error("AppParameters missing in signed Interest");}if (typeof t.sigInfo === "undefined") {throw new Error("ISigInfo missing in signed Interest");}assert(tlv.buffer === t.paramsPortion.buffer);t.sigValue = value;const signedPart0 = t.name.getPrefix(-1).value;const signedPart1 = new Uint8Array(tlv.buffer, t.paramsPortion.byteOffset,tlv.byteOffset - t.paramsPortion.byteOffset);t.signedPortion = new Uint8Array(signedPart0.byteLength + signedPart1.byteLength);t.signedPortion.set(signedPart0, 0);t.signedPortion.set(signedPart1, signedPart0.byteLength);})

Const EVD

EVD: EvDecoder<Fields> = new EvDecoder<Fields>("Data", TT.Data).setTop((t, { tlv }) => t.topTlv = tlv).add(TT.Name, (t, { decoder }) => t.name = decoder.decode(Name), { required: true }).add(TT.MetaInfo,new EvDecoder<Fields>("MetaInfo").add(TT.ContentType, (t, { nni }) => t.contentType = nni).add(TT.FreshnessPeriod, (t, { nni }) => t.freshnessPeriod = nni).add(TT.FinalBlock, (t, { vd }) => t.finalBlockId = vd.decode(Component)),).add(TT.Content, (t, { value }) => t.content = value).add(TT.DSigInfo, (t, { decoder }) => {t.sigInfo = decoder.decode(SigInfo);}, { required: true }).add(TT.DSigValue, (t, { value, before }) => {t.sigValue = value;t.signedPortion = before;}, { required: true })

Const EVD

EVD: EvDecoder<NackHeader> = new EvDecoder<NackHeader>("NackHeader", TT.Nack).add(TT.NackReason, (t, { nni }) => t.reason = nni)

Const EXTENSIONS

EXTENSIONS: ExtensionRegistry<SigInfo> = new ExtensionRegistry<SigInfo>()

Const FIELDS

FIELDS: unique symbol = Symbol("Interest.FIELDS")

Const FIELDS

FIELDS: unique symbol = Symbol("Data.FIELDS")

Const FIELD_LIST

FIELD_LIST: Array<keyof Fields> = ["name", "canBePrefix", "mustBeFresh", "fwHint", "nonce", "lifetime", "hopLimit", "appParameters", "sigInfo", "sigValue"]

Const FIELD_LIST

FIELD_LIST: Array<keyof Fields> = ["name", "contentType", "freshnessPeriod", "finalBlockId", "isFinalBlock", "content", "sigInfo", "sigValue"]

Const FIELD_PARAMS_LIST

FIELD_PARAMS_LIST: Set<"name" | "canBePrefix" | "mustBeFresh" | "fwHint" | "nonce" | "lifetime" | "hopLimit" | "appParameters" | "sigInfo" | "sigValue" | "signedPortion" | "paramsPortion"> = new Set<keyof Fields>(["appParameters", "sigInfo", "sigValue"])

Const FIELD_SIGNED_LIST

FIELD_SIGNED_LIST: Set<"name" | "canBePrefix" | "mustBeFresh" | "fwHint" | "nonce" | "lifetime" | "hopLimit" | "appParameters" | "sigInfo" | "sigValue" | "signedPortion" | "paramsPortion"> = new Set<keyof Fields>(["name", "appParameters", "sigInfo"])

Const HOPLIMIT_MAX

HOPLIMIT_MAX: 255 = 255

Const ImplicitDigest

ImplicitDigest: ImplicitDigestComp = new ImplicitDigestComp()

ImplicitSha256DigestComponent

Const PARAMS_PLACEHOLDER_TAG

PARAMS_PLACEHOLDER_TAG: unique symbol = Symbol("ParametersSha256DigestComponent.placeholder")

Const ParamsDigest

ParamsDigest: ParamsDigestComp = new ParamsDigestComp()

ParametersSha256DigestComponent

Const crypto

crypto: Crypto = globalThis.crypto

Const ctorAssign

ctorAssign: unique symbol = Symbol("SigInfo.ctorAssign")

Const ctorAssign

ctorAssign: unique symbol = Symbol("Interest.ctorAssign")

Const ctorAssign

ctorAssign: unique symbol = Symbol("Interest.ctorAssign")

Functions

assertType

  • assertType(t: number): void

checkType

  • checkType(t: number): boolean

evict

  • evict<K>(capacity: number, container: Set<K> | Map<K, unknown>): void

randBytes

  • randBytes(size: number): Uint8Array

randBytes

  • randBytes(size: number): Uint8Array

sha256

  • sha256(input: Uint8Array): Promise<Uint8Array>

sha256

  • sha256(input: Uint8Array): Promise<Uint8Array>

timingSafeEqual

  • timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean

timingSafeEqual

  • timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean

Object literals

Const NackReason

NackReason: object

Congestion

Congestion: number = 50

Duplicate

Duplicate: number = 100

NoRoute

NoRoute: number = 150

Const SigType

SigType: object

HmacWithSha256

HmacWithSha256: number = 4

Null

Null: number = 200

Sha256

Sha256: number = 0

Sha256WithEcdsa

Sha256WithEcdsa: number = 3

Sha256WithRsa

Sha256WithRsa: number = 1

Const TT

TT: object

AppParameters

AppParameters: number = 36

CanBePrefix

CanBePrefix: number = 33

Content

Content: number = 21

ContentType

ContentType: number = 24

DSigInfo

DSigInfo: number = 22

DSigValue

DSigValue: number = 23

Data

Data: number = 6

Delegation

Delegation: number = 31

FinalBlock

FinalBlock: number = 26

ForwardingHint

ForwardingHint: number = 30

FreshnessPeriod

FreshnessPeriod: number = 25

GenericNameComponent

GenericNameComponent: number = 8

HopLimit

HopLimit: number = 34

ISigInfo

ISigInfo: number = 44

ISigValue

ISigValue: number = 46

ImplicitSha256DigestComponent

ImplicitSha256DigestComponent: number = 1

Interest

Interest: number = 5

InterestLifetime

InterestLifetime: number = 12

KeyDigest

KeyDigest: number = 29

KeyLocator

KeyLocator: number = 28

MetaInfo

MetaInfo: number = 20

MustBeFresh

MustBeFresh: number = 18

Nack

Nack: number = 800

NackReason

NackReason: number = 801

Name

Name: number = 7

Nonce

Nonce: number = 10

ParametersSha256DigestComponent

ParametersSha256DigestComponent: number = 2

Preference

Preference: number = 30

SigNonce

SigNonce: number = 38

SigSeqNum

SigSeqNum: number = 42

SigTime

SigTime: number = 40

SigType

SigType: number = 27

Const digestSigning

digestSigning: object

Signer and Verifier for SigType.Sha256 digest.

sign

verify

Const noopEncryption

noopEncryption: object

Encrypter and decrypter that do nothing.

decrypt

  • decrypt(): Promise<void>

encrypt

  • encrypt(): Promise<void>

Const noopSigning

noopSigning: object

Signer and Verifier that do nothing.

sign

  • sign(): Promise<void>

verify

  • verify(): Promise<void>

Const nullSigner

nullSigner: object

Signer for SigType.Null, a packet that is not signed.

see

https://redmine.named-data.net/projects/ndn-tlv/wiki/NullSignature

sign

Legend

  • Namespace
  • Object literal
  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Interface
  • Interface with type parameter
  • Class
  • Class with type parameter
  • Enumeration

Generated using TypeDoc