// Name components are immutable. Once it's created, you can never change it. // Construct a Component from its TLV-TYPE and TLV-VALUE. constcompA = newComponent(TT.GenericNameComponent, Uint8Array.of(0x41)); // Create a Component from URI representation. constcompB = Component.from("B"); // Typed components are supported, too. constcompMetadata = 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);
// 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");
// Names, like components, are immutable. // Construct from URI. constname1 = newName("/localhost/2020=NDNts/rocks"); // Construct from a list of components, or strings to create components. constname2 = newName([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. constname1comp1 = name1.get(1); // It would return 'undefined' if the component does not exist, so we have to check. if (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. constname1sliced = name1.slice(1, 3); assert.equal(name1sliced.toString(), "/2020=NDNts/8=rocks");
// Or, get the prefix. constname2prefix = 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");
// 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. constname1modified = 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
// We have an Interest type, of course. // You can set fields via constructor or setters. constinterest = newInterest(newName("/A"), Interest.CanBePrefix, Interest.MustBeFresh); interest.canBePrefix = false; interest.lifetime = 2000;
// Encode and decode the Interest. constinterestWire = Encoder.encode(interest); constinterest2 = newDecoder(interestWire).decode(Interest); assert.equal(interest2.name.toString(), "/8=A");
// We got a Data type, too. // You can set fields via constructor or setters. constdata = newData(interest.name, Data.FreshnessPeriod(5000)); data.content = toUtf8("hello NDNts");
// 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. awaitdigestSigning.sign(data);
// After signing, we can encode the Data. constdataWire = Encoder.encode(data);
// And then decode it. constdata2 = newDecoder(dataWire).decode(Data);
// Data signature should be verified. // If the verify() function does not throw, it means the signature is good. awaitdigestSigning.verify(data);
// 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");
// To obtain implicit digest, we'll have to await, because it internally uses WebCrypto, which is async. constdigest = awaitdata.computeImplicitDigest(); assert.equal(digest.length, 32);
// Full names are available, too. constfullName = awaitdata2.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: constdigest2 = data.getImplicitDigest(); constfullName2 = data.getFullName(); assert.equal(digest2, digest); assert(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.
// data.canSatisfy(interest) determines whether a Data satisfy an Interest. // This is an async function because it potentially involves computing the implicit digest.
Low level verification function.
It takes a buffer of signed portion and the signature value, and returns a Promise
that is resolved upon good signature or rejected upon bad signature.
@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.
Name Component
Name
Layer-3 Packet Types: Interest and Data
Signing and Verification
Implicit Digest
Interest-Data Matching