gui/js: Update to latest NDN.JS
Change-Id: Ica3a4e20fe36ad3c4dedeee729dcc899684d6b77
diff --git a/gui/html/chronoshare-helpers.js b/gui/html/chronoshare-helpers.js
index f1e9aa1..b727c6c 100644
--- a/gui/html/chronoshare-helpers.js
+++ b/gui/html/chronoshare-helpers.js
@@ -99,7 +99,7 @@
openHistoryForItem = function(fileName) {
- url = new HistoryClosure(null).base_no_item_url("fileHistory")
+ url = new HistoryDisplay(null).base_no_item_url("fileHistory")
url += "&item=" + encodeURIComponent(encodeURIComponent(fileName));
document.location = url;
};
@@ -161,4 +161,4 @@
}
}
});
-}
\ No newline at end of file
+}
diff --git a/gui/html/chronoshare-navigation.js b/gui/html/chronoshare-navigation.js
index 8fdd12b..81b07cd 100644
--- a/gui/html/chronoshare-navigation.js
+++ b/gui/html/chronoshare-navigation.js
@@ -26,7 +26,7 @@
// URIPARAMS = aurl[1];
// if (CHRONOSHARE) {
- // CHRONOSHARE.run ();
+ // CHRONOSHARE.run();
// }
// }
// else if (aurl[1] != URIPARAMS)
@@ -35,7 +35,7 @@
// URIPARAMS = aurl[1];
// if (CHRONOSHARE) {
- // CHRONOSHARE.run ();
+ // CHRONOSHARE.run();
// }
// }
diff --git a/gui/html/chronoshare.js b/gui/html/chronoshare.js
index 38743ad..916b092 100644
--- a/gui/html/chronoshare.js
+++ b/gui/html/chronoshare.js
@@ -1,10 +1,22 @@
+info_actions_collection = [];
+info_actions_counter = 0;
+info_files_collection = [];
+info_files_counter = 0;
+lastSegmentRequested = -1;
+file_data = "";
+file_segments = 0;
+file_callback = file_onData;
+restore_callback = file_onData;
+
+face = null;
+
$.Class("ChronoShare", {}, {
init: function(username, foldername) {
$("#folder-name").text(foldername);
$("#user-name").text(username);
this.username = new Name(username);
- this.files = new Name("/localhost")
+ this.files = new Name("/localhop")
.add(this.username)
.add("chronoshare")
.add(foldername)
@@ -12,14 +24,14 @@
.add("files")
.add("folder");
- this.actions = new Name("/localhost")
+ this.actions = new Name("/localhop")
.add(this.username)
.add("chronoshare")
.add(foldername)
.add("info")
.add("actions");
- this.restore = new Name("/localhost")
+ this.restore = new Name("/localhop")
.add(this.username)
.add("chronoshare")
.add(foldername)
@@ -27,8 +39,15 @@
.add("restore")
.add("file");
- this.ndn = new NDN({host: "127.0.0.1"});
- this.ndn.verify = false; // disable content verification, works WAAAAY faster
+
+ $("#json").empty();
+
+ if (face == null) {
+ var host = "localhost";
+ // Connect to the forwarder with a WebSocket.
+ face = new Face({host: host});
+ console.log("Init Face OK!");
+ }
},
run: function() {
@@ -38,30 +57,20 @@
cmd = {};
if (PAGE == "fileList") {
+ info_files_collection = [];
+ info_files_counter = 0;
cmd = this.info_files(PARAMS.item);
}
else if (PAGE == "folderHistory") {
+ info_actions_collection = [];
+ info_actions_counter = 0;
cmd = this.info_actions("folder", PARAMS.item);
}
else if (PAGE == "fileHistory") {
+ info_actions_collection = [];
+ info_actions_counter = 0;
cmd = this.info_actions("file", PARAMS.item);
}
-
- if (cmd.request && cmd.callback) {
- console.log(cmd.request.to_uri());
- this.ndn.expressInterest(cmd.request, cmd.callback);
- }
- else {
- $("#loader").fadeOut(500); // ("hidden");
- $("#content").empty();
- if (cmd.error) {
- $("#error").html(cmd.error);
- }
- else {
- $("#error").html("Unknown error with " + PAGE);
- }
- $("#error").removeClass("hidden");
- }
},
info_files: function(folder) {
@@ -69,7 +78,8 @@
.add(this.files)
./*add (folder_in_question).*/ addSegment(
PARAMS.offset ? PARAMS.offset : 0);
- return {request: request, callback: new FilesClosure(this)};
+ face.expressInterest(request, info_files_onData, on_Timeout);
+ console.log("Express OK: " + request.to_uri());
},
info_actions: function(type /*"file" or "folder"*/,
@@ -83,7 +93,9 @@
request.add(fileOrFolder);
}
request.addSegment(PARAMS.offset ? PARAMS.offset : 0);
- return {request: request, callback: new HistoryClosure(this)};
+
+ face.expressInterest(request, info_actions_onData, on_Timeout);
+ console.log("Express OK: " + request.to_uri());
},
cmd_restore_file:
@@ -92,8 +104,9 @@
request =
new Name().add(this.restore).add(filename).addSegment(version).add(hash);
console.log(request.to_uri());
- this.ndn.expressInterest(request,
- new CmdRestoreFileClosure(this, callback));
+
+ restore_callback = callback;
+ face.expressInterest(request, restore_onData, on_Timeout);
},
get_file:
@@ -101,10 +114,144 @@
callback /*function (bool <- data received, data <- returned data)*/) {
baseName = new Name(modifiedBy).add("chronoshare").add("file").add(hash);
- new FileGetter(this.ndn, baseName, segments, callback).start();
- }
+ lastSegmentRequested = 0;
+ file_data = "";
+ file_segments = segments;
+ file_callback = callback;
+
+ request = new Name().add(baseName).addSegment(lastSegmentRequested);
+ console.log("Express get_file Name: ", request.to_uri());
+ console.log("Total Segments: ", file_segments);
+ face.expressInterest(request, file_onData, on_Timeout);
+
+ },
+
});
+function restore_onData(interest, upcallInfo)
+{
+ convertedData = upcallInfo.getContent().buf().toString('binary');
+ console.log("restore_onData");
+ console.log(convertedData);
+ restore_callback(true, convertedData);
+};
+
+function file_onData(interest, upcallInfo)
+{
+ convertedData = upcallInfo.getContent().buf().toString('binary');
+ console.log("file_onData");
+ console.log(convertedData);
+ file_data += convertedData;
+ if (lastSegmentRequested + 1 == file_segments) {
+ file_callback(true, file_data);
+ }
+ else {
+ lastSegmentRequested++;
+ nextSegment =
+ interest.getName().getPrefix(-1).addSegment(lastSegmentRequested);
+ face.expressInterest(nextSegment, file_onData, on_Timeout);
+ }
+};
+
+function info_actions_onData(interest, upcallInfo)
+{
+ convertedData = upcallInfo.getContent().buf().toString(
+ 'binary'); //DataUtils.toString (upcallInfo.contentObject.content);
+ console.log(convertedData);
+ moreName = "more";
+ collectionName = "actions";
+
+ if (PARAMS.debug) {
+ $("#json").append($(document.createTextNode(convertedData)));
+ $("#json").removeClass("hidden");
+ }
+
+ console.log("Prefix");
+ console.log(interest.getName().getPrefix(-1).to_uri());
+
+ data = JSON.parse(convertedData);
+ console.log(data);
+ console.log(data[collectionName]);
+ var collection = data[collectionName];
+ info_actions_collection = info_actions_collection.concat(data[collectionName]);
+
+ console.log(info_actions_collection);
+
+ if (data[moreName] !== undefined) {
+ nextSegment = interest.getName().getPrefix(-1).addSegment(data[moreName]);
+ info_actions_counter++;
+
+ if (info_actions_counter < 5) {
+ console.log("MORE: " + nextSegment.to_uri());
+ face.expressInterest(nextSegment, info_actions_onData, on_Timeout);
+ }
+ else {
+ $("#loader").fadeOut(500); // ("hidden");
+ var HD = new HistoryDisplay(CHRONOSHARE);
+ HD.onData(info_actions_collection, data[moreName]);
+ }
+ }
+ else {
+ $("#loader").fadeOut(500); // ("hidden");
+ console.log("I'm triggered more undefined~");
+ var HD = new HistoryDisplay(CHRONOSHARE);
+ HD.onData(info_actions_collection, undefined);
+ }
+};
+
+function info_files_onData(interest, upcallInfo)
+{
+ convertedData = upcallInfo.getContent().buf().toString(
+ 'binary'); //DataUtils.toString (upcallInfo.contentObject.content);
+ console.log(convertedData);
+ moreName = "more";
+ collectionName = "files";
+
+ if (PARAMS.debug) {
+ $("#json").append($(document.createTextNode(convertedData)));
+ $("#json").removeClass("hidden");
+ }
+
+ console.log("Prefix");
+ console.log(interest.getName().getPrefix(-1).to_uri());
+
+ data = JSON.parse(convertedData);
+ console.log(data);
+ console.log(data[collectionName]);
+ var collection = data[collectionName];
+ info_files_collection = info_files_collection.concat(data[collectionName]);
+
+ console.log(info_files_collection);
+
+ if (data[moreName] !== undefined) {
+ nextSegment = interest.getName().getPrefix(-1).addSegment(data[moreName]);
+ info_files_counter++;
+
+ if (info_files_counter < 5) {
+ console.log("MORE: " + nextSegment.to_uri());
+ face.expressInterest(nextSegment, info_files_onData, on_Timeout);
+ }
+ else {
+ $("#loader").fadeOut(500); // ("hidden");
+ var HD = new FilesDisplay(CHRONOSHARE);
+ HD.onData(info_files_collection, data[moreName]);
+ }
+ }
+ else {
+ $("#loader").fadeOut(500); // ("hidden");
+ console.log("I'm triggered more undefined~");
+ var HD = new FilesDisplay(CHRONOSHARE);
+ HD.onData(info_files_collection, undefined);
+ }
+};
+
+
+function on_Timeout(interest)
+{
+ $("#error").html("Ha Ha Interest time out");
+ $("#error").removeClass("hidden");
+}
+
$.Class("CmdRestoreFileClosure", {}, {
init: function(chronoshare, callback) {
this.chronoshare = chronoshare;
@@ -112,12 +259,12 @@
},
upcall: function(kind, upcallInfo) {
if (kind == Closure.UPCALL_CONTENT ||
- kind == Closure.UPCALL_CONTENT_UNVERIFIED) { // disable content verification
+ kind == Closure.UPCALL_CONTENT_UNVERIFIED) { //disable content verification
convertedData = DataUtils.toString(upcallInfo.contentObject.content);
this.callback(true, convertedData);
}
else if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
- this.callback(false, "Interest timed out");
+ this.callback(false, "Interest timed out 99");
}
else {
this.callback(false, "Unknown error happened");
@@ -125,230 +272,9 @@
}
});
-$.Class("FileGetter", {}, {
- init: function(ndn, baseName, segments, callback) {
- this.ndn = ndn;
- this.callback = callback;
- this.baseName = baseName;
- this.segments = segments;
- this.lastSegmentRequested = -1;
- this.data = "";
- },
-
- start: function() {
- this.lastSegmentRequested++;
-
- request =
- new Name().add(this.baseName).addSegment(this.lastSegmentRequested);
-
- console.log(request.to_uri());
- this.ndn.expressInterest(request, this);
- },
-
- upcall: function(kind, upcallInfo) {
- if (kind == Closure.UPCALL_CONTENT ||
- kind == Closure.UPCALL_CONTENT_UNVERIFIED) { // disable content verification
- convertedData = DataUtils.toString(upcallInfo.contentObject.content);
-
- this.data += convertedData;
-
- if (this.lastSegmentRequested + 1 == this.segments) {
- this.callback(true, this.data);
- }
- else {
- this.start();
- }
- }
- else {
- this.callback(false, "Interest timed out");
- }
- }
-});
-
-$.Class("RestPipelineClosure", {}, {
- init: function(collectionName, moreName) {
- this.collectionName = collectionName;
- this.moreName = moreName;
- $("#json").empty();
-
- this.collection = [];
- this.counter = 0;
- },
-
- upcall: function(kind, upcallInfo) {
- if (kind == Closure.UPCALL_CONTENT ||
- kind == Closure.UPCALL_CONTENT_UNVERIFIED) { // disable content verification
-
- convertedData = DataUtils.toString(upcallInfo.contentObject.content);
- if (PARAMS.debug) {
- $("#json").append($(document.createTextNode(convertedData)));
- $("#json").removeClass("hidden");
- }
- data = JSON.parse(convertedData);
-
- this.collection = this.collection.concat(data[this.collectionName]);
- if (data[this.moreName] !== undefined) {
- nextSegment =
- upcallInfo.interest.name.cut(1).addSegment(data[this.moreName]);
- this.counter++;
-
- if (this.counter < 5) {
- console.log("MORE: " + nextSegment.to_uri());
- CHRONOSHARE.ndn.expressInterest(nextSegment, this);
- }
- else {
- $("#loader").fadeOut(500); // ("hidden");
- this.onData(this.collection, data[this.moreName]);
- }
- }
- else {
- $("#loader").fadeOut(500); // ("hidden");
- this.onData(this.collection, undefined);
- }
- }
- else if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
- $("#loader").fadeOut(500); // ("hidden");
- this.onTimeout(upcallInfo.interest);
- }
- else {
- $("#loader").fadeOut(500); // ("hidden");
- this.onUnknownError(upcallInfo.interest);
- }
-
- return Closure.RESULT_OK; // make sure we never re-express the interest
- },
-
- onData: function(data, more) {},
-
- onTimeout: function() {
- $("#error").html("Interest timed out");
- $("#error").removeClass("hidden");
- },
-
- onUnknownError: function() {
- $("#error").html("Unknown error happened");
- $("#error").removeClass("hidden");
- }
-});
-
-// $.Class ("FilesClosure", {}, {
-RestPipelineClosure("FilesClosure", {}, {
+$.Class("HistoryDisplay", {}, {
init: function(chronoshare) {
- this._super("files", "more");
- this.chronoshare = chronoshare;
- },
-
- onData: function(data, more) {
- tbody = $("<tbody />", {"id": "file-list-files"});
-
- /// @todo Eventually set title for other pages
- $("title").text("ChronoShare - List of files" +
- (PARAMS.item ? " - " + PARAMS.item : ""));
-
- // error handling?
- newcontent =
- $("<div />", {"id": "content"})
- .append($("<h2 />").append($(document.createTextNode("List of files ")),
- $("<green />").text(PARAMS.item)),
- $("<table />", {"class": "item-list"})
- .append(
- $("<thead />")
- .append($("<tr />")
- .append($("<th />", {
- "class": "filename border-left",
- "scope": "col"
- }).text("Filename"))
- .append($("<th />",
- {"class": "version", "scope": "col"})
- .text("Version"))
- .append($("<th />",
- {"class": "size", "scope": "col"})
- .text("Size"))
- .append($("<th />", {
- "class": "modified",
- "scope": "col"
- }).text("Modified"))
- .append($("<th />", {
- "class": "modified-by border-right",
- "scope": "col"
- }).text("Modified By"))))
- .append(tbody)
- .append($("<tfoot />").append($("<tr />").append($("<td />", {
- "colspan": "5",
- "class": "border-right border-left"
- })))));
- newcontent.hide();
-
- for (var i = 0; i < data.length; i++) {
- file = data[i];
-
- row = $("<tr />", {"class": "with-context-menu"});
- if (i % 2) {
- row.addClass("odd");
- }
-
- row.bind('mouseenter mouseleave', function() {
- $(this).toggleClass('highlighted');
- });
-
- row.attr("filename",
- file.filename); // encodeURIComponent(encodeURIComponent(file.filename)));
- row.bind('click', function(e) {
- openHistoryForItem($(this).attr("filename"))
- });
-
- row.append(
- $("<td />", {"class": "filename border-left"})
- .text(file.filename)
- .prepend(
- $("<img />", {"src": imgFullPath(fileExtension(file.filename))})));
- row.append($("<td />", {"class": "version"}).text(file.version));
- row.append(
- $("<td />", {"class": "size"}).text(SegNumToFileSize(file.segNum)));
- row.append($("<td />", {
- "class": "modified"
- }).text(new Date(file.timestamp + "+00:00"))); // convert from UTC
- row.append($("<td />", {"class": "modified-by border-right"})
- .append($("<userName />").text(file.owner.userName))
- .append($("<seqNo> /").text(file.owner.seqNo)));
-
- tbody = tbody.append(row);
- }
-
- displayContent(newcontent, more, this.base_url());
-
- $.contextMenu('destroy', ".with-context-menu"); // cleanup
- $.contextMenu({
- selector: ".with-context-menu",
- items: {
- "info": {name: "x", type: "html", html: "<b>File operations</b>"},
- "sep1": "---------",
- history: {
- name: "View file history",
- icon: "quit", // need a better icon
- callback: function(key, opt) {
- openHistoryForItem(opt.$trigger.attr("filename"));
- }
- },
- }
- });
- },
-
- base_url: function() {
- url = "#fileList" +
- "&user=" + encodeURIComponent(encodeURIComponent(PARAMS.user)) +
- "&folder=" + encodeURIComponent(encodeURIComponent(PARAMS.folder));
- if (PARAMS.item !== undefined) {
- url += "&item=" + encodeURIComponent(encodeURIComponent(PARAMS.item));
- }
- return url;
- }
-});
-
-RestPipelineClosure("HistoryClosure", {}, {
- init: function(chronoshare) {
- this._super("actions", "more");
this.chronoshare = chronoshare;
},
@@ -356,7 +282,8 @@
if (fileExtension(file.attr("filename")) == "txt") {
CHRONOSHARE.get_file(file.attr("file_modified_by"),
DataUtils.toNumbers(file.attr("file_hash")),
- file.attr("file_seg_num"), function(status, data) {
+ file.attr("file_seg_num"),
+ function(status, data) {
$("<div />", {
"title": "Preview of " + file.attr("filename") +
" version " + file.attr("file_version")
@@ -462,13 +389,11 @@
$("<td />", {
"class": "size"
}).text(action.update ? SegNumToFileSize(action.update.segNum) : ""));
- row.append($("<td />", {
- "class": "timestamp"
- }).text(new Date(action.timestamp + "+00:00"))); // conversion
- // from UTC
- // timezone (we
- // store action
- // time in UTC)
+ row.append(
+ $("<td />", {"class": "timestamp"})
+ .text(new Date(
+ action.timestamp +
+ "+00:00"))); // conversion from UTC timezone (we store action time in UTC)
row.append($("<td />", {"class": "modified-by border-right"})
.append($("<userName />").text(action.id.userName))
.append($("<seqNo> /").text(action.id.seqNo)));
@@ -529,6 +454,10 @@
response != "OK") {
custom_alert(response);
}
+
+ custom_alert(
+ "Restore Succeed!");
+
console.log(response);
self.dialog("close");
@@ -568,3 +497,115 @@
return url;
}
});
+
+$.Class("FilesDisplay", {}, {
+ init: function(chronoshare) {
+ this.chronoshare = chronoshare;
+ },
+
+ onData: function(data, more) {
+ tbody = $("<tbody />", {"id": "file-list-files"});
+
+ /// @todo Eventually set title for other pages
+ $("title").text("ChronoShare - List of files" +
+ (PARAMS.item ? " - " + PARAMS.item : ""));
+
+ // error handling?
+ newcontent =
+ $("<div />", {"id": "content"})
+ .append($("<h2 />").append($(document.createTextNode("List of files ")),
+ $("<green />").text(PARAMS.item)),
+ $("<table />", {"class": "item-list"})
+ .append(
+ $("<thead />")
+ .append($("<tr />")
+ .append($("<th />", {
+ "class": "filename border-left",
+ "scope": "col"
+ }).text("Filename"))
+ .append($("<th />",
+ {"class": "version", "scope": "col"})
+ .text("Version"))
+ .append($("<th />",
+ {"class": "size", "scope": "col"})
+ .text("Size"))
+ .append($("<th />", {
+ "class": "modified",
+ "scope": "col"
+ }).text("Modified"))
+ .append($("<th />", {
+ "class": "modified-by border-right",
+ "scope": "col"
+ }).text("Modified By"))))
+ .append(tbody)
+ .append($("<tfoot />").append($("<tr />").append($("<td />", {
+ "colspan": "5",
+ "class": "border-right border-left"
+ })))));
+ newcontent.hide();
+
+ for (var i = 0; i < data.length; i++) {
+ file = data[i];
+
+ row = $("<tr />", {"class": "with-context-menu"});
+ if (i % 2) {
+ row.addClass("odd");
+ }
+
+ row.bind('mouseenter mouseleave', function() {
+ $(this).toggleClass('highlighted');
+ });
+
+ row.attr("filename",
+ file.filename); //encodeURIComponent(encodeURIComponent(file.filename)));
+ row.bind('click', function(e) {
+ openHistoryForItem($(this).attr("filename"))
+ });
+
+ row.append(
+ $("<td />", {"class": "filename border-left"})
+ .text(file.filename)
+ .prepend(
+ $("<img />", {"src": imgFullPath(fileExtension(file.filename))})));
+ row.append($("<td />", {"class": "version"}).text(file.version));
+ row.append(
+ $("<td />", {"class": "size"}).text(SegNumToFileSize(file.segNum)));
+ row.append($("<td />", {
+ "class": "modified"
+ }).text(new Date(file.timestamp + "+00:00"))); // convert from UTC
+ row.append($("<td />", {"class": "modified-by border-right"})
+ .append($("<userName />").text(file.owner.userName))
+ .append($("<seqNo> /").text(file.owner.seqNo)));
+
+ tbody = tbody.append(row);
+ }
+
+ displayContent(newcontent, more, this.base_url());
+
+ $.contextMenu('destroy', ".with-context-menu"); // cleanup
+ $.contextMenu({
+ selector: ".with-context-menu",
+ items: {
+ "info": {name: "x", type: "html", html: "<b>File operations</b>"},
+ "sep1": "---------",
+ history: {
+ name: "View file history",
+ icon: "quit", // need a better icon
+ callback: function(key, opt) {
+ openHistoryForItem(opt.$trigger.attr("filename"));
+ }
+ },
+ }
+ });
+ },
+
+ base_url: function() {
+ url = "#fileList" +
+ "&user=" + encodeURIComponent(encodeURIComponent(PARAMS.user)) +
+ "&folder=" + encodeURIComponent(encodeURIComponent(PARAMS.folder));
+ if (PARAMS.item !== undefined) {
+ url += "&item=" + encodeURIComponent(encodeURIComponent(PARAMS.item));
+ }
+ return url;
+ }
+});
diff --git a/gui/html/js/ndn-js.js b/gui/html/js/ndn-js.js
index 63f1fef..9408f0c 100644
--- a/gui/html/js/ndn-js.js
+++ b/gui/html/js/ndn-js.js
@@ -1,5017 +1,1679 @@
-/**
- * @author: Jeff Thompson
- * See COPYING for copyright and distribution information.
- * Provide the callback closure for the async communication methods in the NDN class.
- * This is a port of Closure.py from PyNDN, written by:
- * Derek Kulinski <takeda@takeda.tk>
- * Jeff Burke <jburke@ucla.edu>
- */
-
-/*
- * Create a subclass of Closure and pass an object to async calls.
- */
-var Closure = function Closure() {
- // I don't think storing NDN's closure is needed
- // and it creates a reference loop, as of now both
- // of those variables are never set -- Derek
- //
- // Use instance variables to return data to callback
- this.ndn_data = null; // this holds the ndn_closure
- this.ndn_data_dirty = false;
-
-};
-
-// Upcall result
-Closure.RESULT_ERR = -1; // upcall detected an error
-Closure.RESULT_OK = 0; // normal upcall return
-Closure.RESULT_REEXPRESS = 1; // reexpress the same interest again
-Closure.RESULT_INTEREST_CONSUMED = 2; // upcall claims to consume interest
-Closure.RESULT_VERIFY = 3; // force an unverified result to be verified
-Closure.RESULT_FETCHKEY = 4; // get the key in the key locator and re-call the interest
- // with the key available in the local storage
-
-// Upcall kind
-Closure.UPCALL_FINAL = 0; // handler is about to be deregistered
-Closure.UPCALL_INTEREST = 1; // incoming interest
-Closure.UPCALL_CONSUMED_INTEREST = 2; // incoming interest, someone has answered
-Closure.UPCALL_CONTENT = 3; // incoming verified content
-Closure.UPCALL_INTEREST_TIMED_OUT = 4; // interest timed out
-Closure.UPCALL_CONTENT_UNVERIFIED = 5; // content that has not been verified
-Closure.UPCALL_CONTENT_BAD = 6; // verification failed
-
-/*
- * Override this in your subclass.
- * If you're getting strange errors in upcall()
- * check your code whether you're returning a value.
- */
-Closure.prototype.upcall = function(kind, upcallInfo) {
- //dump('upcall ' + this + " " + kind + " " + upcallInfo + "\n");
- return Closure.RESULT_OK;
-};
-
-var UpcallInfo = function UpcallInfo(ndn, interest, matchedComps, contentObject) {
- this.ndn = ndn; // NDN object (not used)
- this.interest = interest; // Interest object
- this.matchedComps = matchedComps; // int
- this.contentObject = contentObject; // Content object
-};
-
-UpcallInfo.prototype.toString = function() {
- var ret = "ndn = " + this.ndn;
- ret += "\nInterest = " + this.interest;
- ret += "\nmatchedComps = " + this.matchedComps;
- ret += "\nContentObject: " + this.contentObject;
- return ret;
-}
-/**
- * @author: Wentao Shang
- * See COPYING for copyright and distribution information.
- */
-
-var WebSocketTransport = function WebSocketTransport() {
- if (!WebSocket)
- throw new Error("WebSocket support is not available on this platform.");
-
- this.ws = null;
- this.connectedHost = null; // Read by NDN.
- this.connectedPort = null; // Read by NDN.
- this.elementReader = null;
- this.defaultGetHostAndPort = NDN.makeShuffledGetHostAndPort
- (["A.ws.ndn.ucla.edu", "B.ws.ndn.ucla.edu", "C.ws.ndn.ucla.edu", "D.ws.ndn.ucla.edu",
- "E.ws.ndn.ucla.edu"],
- 9696);
-};
-
-/*
- * Connect to the host and port in ndn. This replaces a previous connection and sets connectedHost
- * and connectedPort. Once connected, call onopenCallback().
- * Listen on the port to read an entire binary XML encoded element and call
- * ndn.onReceivedElement(element).
- */
-WebSocketTransport.prototype.connect = function(ndn, onopenCallback) {
- if (this.ws != null)
- delete this.ws;
-
- this.ws = new WebSocket('ws://' + ndn.host + ':' + ndn.port);
- if (LOG > 0) console.log('ws connection created.');
- this.connectedHost = ndn.host;
- this.connectedPort = ndn.port;
-
- this.ws.binaryType = "arraybuffer";
-
- this.elementReader = new BinaryXmlElementReader(ndn);
- var self = this;
- this.ws.onmessage = function(ev) {
- var result = ev.data;
- //console.log('RecvHandle called.');
-
- if(result == null || result == undefined || result == "" ) {
- console.log('INVALID ANSWER');
- } else if (result instanceof ArrayBuffer) {
- var bytearray = new Uint8Array(result);
-
- if (LOG>3) console.log('BINARY RESPONSE IS ' + DataUtils.toHex(bytearray));
-
- try {
- // Find the end of the binary XML element and call ndn.onReceivedElement.
- self.elementReader.onReceivedData(bytearray);
- } catch (ex) {
- console.log("NDN.ws.onmessage exception: " + ex);
- return;
- }
- }
- }
-
- this.ws.onopen = function(ev) {
- if (LOG > 3) console.log(ev);
- if (LOG > 3) console.log('ws.onopen: WebSocket connection opened.');
- if (LOG > 3) console.log('ws.onopen: ReadyState: ' + this.readyState);
- // NDN.registerPrefix will fetch the ndndid when needed.
-
- onopenCallback();
- }
-
- this.ws.onerror = function(ev) {
- console.log('ws.onerror: ReadyState: ' + this.readyState);
- console.log(ev);
- console.log('ws.onerror: WebSocket error: ' + ev.data);
- }
-
- this.ws.onclose = function(ev) {
- console.log('ws.onclose: WebSocket connection closed.');
- self.ws = null;
-
- // Close NDN when WebSocket is closed
- ndn.readyStatus = NDN.CLOSED;
- ndn.onclose();
- //console.log("NDN.onclose event fired.");
- }
-};
-
-/*
- * Send the Uint8Array data.
- */
-WebSocketTransport.prototype.send = function(data) {
- if (this.ws != null) {
- // If we directly use data.buffer to feed ws.send(),
- // WebSocket may end up sending a packet with 10000 bytes of data.
- // That is, WebSocket will flush the entire buffer
- // regardless of the offset of the Uint8Array. So we have to create
- // a new Uint8Array buffer with just the right size and copy the
- // content from binaryInterest to the new buffer.
- // ---Wentao
- var bytearray = new Uint8Array(data.length);
- bytearray.set(data);
- this.ws.send(bytearray.buffer);
- if (LOG > 3) console.log('ws.send() returned.');
- }
- else
- console.log('WebSocket connection is not established.');
-}
-/**
- * @author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- * This class contains all NDNx tags
- */
-
-
-var NDNProtocolDTags = {
-
- /**
- * Note if you add one of these, add it to the reverse string map as well.
- * Emphasize getting the work done at compile time over trying to make something
- * flexible and developer error-proof.
- */
-
- Any : 13,
- Name : 14,
- Component : 15,
- Certificate : 16,
- Collection : 17,
- CompleteName : 18,
- Content : 19,
- SignedInfo : 20,
- ContentDigest : 21,
- ContentHash : 22,
- Count : 24,
- Header : 25,
- Interest : 26, /* 20090915 */
- Key : 27,
- KeyLocator : 28,
- KeyName : 29,
- Length : 30,
- Link : 31,
- LinkAuthenticator : 32,
- NameComponentCount : 33, /* DeprecatedInInterest */
- RootDigest : 36,
- Signature : 37,
- Start : 38,
- Timestamp : 39,
- Type : 40,
- Nonce : 41,
- Scope : 42,
- Exclude : 43,
- Bloom : 44,
- BloomSeed : 45,
- AnswerOriginKind : 47,
- InterestLifetime : 48,
- Witness : 53,
- SignatureBits : 54,
- DigestAlgorithm : 55,
- BlockSize : 56,
- FreshnessSeconds : 58,
- FinalBlockID : 59,
- PublisherPublicKeyDigest : 60,
- PublisherCertificateDigest : 61,
- PublisherIssuerKeyDigest : 62,
- PublisherIssuerCertificateDigest : 63,
- ContentObject : 64, /* 20090915 */
- WrappedKey : 65,
- WrappingKeyIdentifier : 66,
- WrapAlgorithm : 67,
- KeyAlgorithm : 68,
- Label : 69,
- EncryptedKey : 70,
- EncryptedNonceKey : 71,
- WrappingKeyName : 72,
- Action : 73,
- FaceID : 74,
- IPProto : 75,
- Host : 76,
- Port : 77,
- MulticastInterface : 78,
- ForwardingFlags : 79,
- FaceInstance : 80,
- ForwardingEntry : 81,
- MulticastTTL : 82,
- MinSuffixComponents : 83,
- MaxSuffixComponents : 84,
- ChildSelector : 85,
- RepositoryInfo : 86,
- Version : 87,
- RepositoryVersion : 88,
- GlobalPrefix : 89,
- LocalName : 90,
- Policy : 91,
- Namespace : 92,
- GlobalPrefixName : 93,
- PolicyVersion : 94,
- KeyValueSet : 95,
- KeyValuePair : 96,
- IntegerValue : 97,
- DecimalValue : 98,
- StringValue : 99,
- BinaryValue : 100,
- NameValue : 101,
- Entry : 102,
- ACL : 103,
- ParameterizedName : 104,
- Prefix : 105,
- Suffix : 106,
- Root : 107,
- ProfileName : 108,
- Parameters : 109,
- InfoString : 110,
- // 111 unallocated
- StatusResponse : 112,
- StatusCode : 113,
- StatusText : 114,
-
- // Sync protocol
- SyncNode : 115,
- SyncNodeKind : 116,
- SyncNodeElement : 117,
- SyncVersion : 118,
- SyncNodeElements : 119,
- SyncContentHash : 120,
- SyncLeafCount : 121,
- SyncTreeDepth : 122,
- SyncByteCount : 123,
- ConfigSlice : 124,
- ConfigSliceList : 125,
- ConfigSliceOp : 126,
-
- // Remember to keep in sync with schema/tagnames.csvsdict
- NDNProtocolDataUnit : 17702112,
- NDNPROTOCOL_DATA_UNIT : "NDNProtocolDataUnit"
-};
-
-var NDNProtocolDTagsStrings = [
- null, null, null, null, null, null, null, null, null, null, null,
- null, null,
- "Any", "Name", "Component", "Certificate", "Collection", "CompleteName",
- "Content", "SignedInfo", "ContentDigest", "ContentHash", null, "Count", "Header",
- "Interest", "Key", "KeyLocator", "KeyName", "Length", "Link", "LinkAuthenticator",
- "NameComponentCount", null, null, "RootDigest", "Signature", "Start", "Timestamp", "Type",
- "Nonce", "Scope", "Exclude", "Bloom", "BloomSeed", null, "AnswerOriginKind",
- "InterestLifetime", null, null, null, null, "Witness", "SignatureBits", "DigestAlgorithm", "BlockSize",
- null, "FreshnessSeconds", "FinalBlockID", "PublisherPublicKeyDigest", "PublisherCertificateDigest",
- "PublisherIssuerKeyDigest", "PublisherIssuerCertificateDigest", "ContentObject",
- "WrappedKey", "WrappingKeyIdentifier", "WrapAlgorithm", "KeyAlgorithm", "Label",
- "EncryptedKey", "EncryptedNonceKey", "WrappingKeyName", "Action", "FaceID", "IPProto",
- "Host", "Port", "MulticastInterface", "ForwardingFlags", "FaceInstance",
- "ForwardingEntry", "MulticastTTL", "MinSuffixComponents", "MaxSuffixComponents", "ChildSelector",
- "RepositoryInfo", "Version", "RepositoryVersion", "GlobalPrefix", "LocalName",
- "Policy", "Namespace", "GlobalPrefixName", "PolicyVersion", "KeyValueSet", "KeyValuePair",
- "IntegerValue", "DecimalValue", "StringValue", "BinaryValue", "NameValue", "Entry",
- "ACL", "ParameterizedName", "Prefix", "Suffix", "Root", "ProfileName", "Parameters",
- "InfoString", null,
- "StatusResponse", "StatusCode", "StatusText", "SyncNode", "SyncNodeKind", "SyncNodeElement",
- "SyncVersion", "SyncNodeElements", "SyncContentHash", "SyncLeafCount", "SyncTreeDepth", "SyncByteCount",
- "ConfigSlice", "ConfigSliceList", "ConfigSliceOp" ];
-
-
-//TESTING
-//console.log(exports.NDNProtocolDTagsStrings[17]);
-
-/**
- * @author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- * This class represents NDNTime Objects
- */
-
-var NDNTime = function NDNTime(
-
- input) {
-
-
-
-
- this.NANOS_MAX = 999877929;
-
- /*if(typeof input =='object'){
- this.longDate = DataUtils.byteArrayToUnsignedLong(input);
- this.binaryDate = input;
- }*/
- if(typeof input =='number'){
- this.msec = input;
- //this.binaryDate = DataUtils.unsignedLongToByteArray(input);
-
- }
- else{
- if(LOG>1) console.log('UNRECOGNIZED TYPE FOR TIME');
- }
-};
-
-
-NDNTime.prototype.getJavascriptDate = function(){
- var d = new Date();
- d.setTime( this.msec );
- return d
-};
-
- /**
- * Create a NDNTime
- * @param timestamp source timestamp to initialize from, some precision will be lost
- */
-
-
- /**
- * Create a NDNTime from its binary encoding
- * @param binaryTime12 the binary representation of a NDNTime
- */
-/*NDNTime.prototype.setDateBinary = function(
- //byte []
- binaryTime12) {
-
-
- if ((null == binaryTime12) || (binaryTime12.length == 0)) {
- throw new IllegalArgumentException("Invalid binary time!");
- }
-
-
- value = 0;
- for(i = 0; i < binaryTime12.length; i++) {
- value = value << 8;
- b = (binaryTime12[i]) & 0xFF;
- value |= b;
- }
-
- //this.date = new Date(value);
-
-};
-
-//byte[]
-NDNTime.prototype.toBinaryTime = function() {
-
- return this.msec; //unsignedLongToByteArray(this.date.getTime());
-
-}*/
-/*
-unsignedLongToByteArray= function( value) {
- if( 0 == value )
- return [0];
-
- if( 0 <= value && value <= 0x00FF ) {
- //byte []
- bb = new Array[1];
- bb[0] = (value & 0x00FF);
- return bb;
- }
-
-
- //byte []
- out = null;
- //int
- offset = -1;
- for(var i = 7; i >=0; --i) {
- //byte
- b = ((value >> (i * 8)) & 0xFF);
- if( out == null && b != 0 ) {
- out = new Array(i+1);//byte[i+1];
- offset = i;
- }
- if( out != null )
- out[ offset - i ] = b;
- }
- return out;
-}*/
-
-/**
- * @author: Jeff Thompson
- * See COPYING for copyright and distribution information.
- * This is the closure class for use in expressInterest to re express with exponential falloff.
- */
-
-/*
- * Create a new ExponentialReExpressClosure where upcall responds to UPCALL_INTEREST_TIMED_OUT
- * by expressing the interest again with double the interestLifetime. If the interesLifetime goes
- * over maxInterestLifetime, then call callerClosure.upcall with UPCALL_INTEREST_TIMED_OUT.
- * When upcall is not UPCALL_INTEREST_TIMED_OUT, just call callerClosure.upcall.
- *
- * settings is an associative array with the following defaults:
- * {
- * maxInterestLifetime: 16000 // milliseconds
- * }
- */
-var ExponentialReExpressClosure = function ExponentialReExpressClosure
- (callerClosure, settings) {
- // Inherit from Closure.
- Closure.call(this);
-
- this.callerClosure = callerClosure;
- settings = (settings || {});
- this.maxInterestLifetime = (settings.maxInterestLifetime || 16000);
-};
-
-ExponentialReExpressClosure.prototype.upcall = function(kind, upcallInfo) {
- try {
- if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
- var interestLifetime = upcallInfo.interest.interestLifetime;
- if (interestLifetime == null)
- return this.callerClosure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, upcallInfo);
-
- var nextInterestLifetime = interestLifetime * 2;
- if (nextInterestLifetime > this.maxInterestLifetime)
- return this.callerClosure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, upcallInfo);
-
- var nextInterest = upcallInfo.interest.clone();
- nextInterest.interestLifetime = nextInterestLifetime;
- upcallInfo.ndn.expressInterest(nextInterest.name, this, nextInterest);
- return Closure.RESULT_OK;
- }
- else
- return this.callerClosure.upcall(kind, upcallInfo);
- } catch (ex) {
- console.log("ExponentialReExpressClosure.upcall exception: " + ex);
- return Closure.RESULT_ERR;
+// Domain Public by Eric Wendelin http://www.eriwen.com/ (2008)
+// Luke Smith http://lucassmith.name/ (2008)
+// Loic Dachary <loic@dachary.org> (2008)
+// Johan Euphrosine <proppy@aminche.com> (2008)
+// Oyvind Sean Kinsey http://kinsey.no/blog (2010)
+// Victor Homyakov <victor-homyakov@users.sourceforge.net> (2010)
+/*global module, exports, define, ActiveXObject*/
+(function(global, factory) {
+ if (typeof exports === 'object') {
+ // Node
+ module.exports.printStackTrace = factory();
+ } else if (typeof define === 'function' && define.amd) {
+ // AMD
+ define(factory);
+ } else {
+ // Browser globals
+ global.printStackTrace = factory();
}
-};
-/**
- * @author: Meki Cheraoui, Jeff Thompson
- * See COPYING for copyright and distribution information.
- * This class represents a Name as an array of components where each is a byte array.
- */
-
-/*
- * Create a new Name from _components.
- * If _components is a string, parse it as a URI.
- * If _components is a Name, add a deep copy of its components.
- * Otherwise it is an array of components where each is a string, byte array, ArrayBuffer, Uint8Array
- * or Name.
- * Convert and store as an array of Uint8Array.
- * If a component is a string, encode as utf8.
- */
-var Name = function Name(_components){
- if( typeof _components == 'string') {
- if(LOG>3)console.log('Content Name String '+_components);
- this.components = Name.createNameArray(_components);
- }
- else if(typeof _components === 'object'){
- this.components = [];
- if (_components instanceof Name)
- this.add(_components);
- else {
- for (var i = 0; i < _components.length; ++i)
- this.add(_components[i]);
- }
- }
- else if(_components==null)
- this.components =[];
- else
- if(LOG>1)console.log("NO CONTENT NAME GIVEN");
-};
-
-Name.prototype.getName = function() {
- return this.to_uri();
-};
-
-/* Parse name as a URI and return an array of Uint8Array components.
- *
- */
-Name.createNameArray = function(name) {
- name = name.trim();
- if (name.length <= 0)
- return [];
-
- var iColon = name.indexOf(':');
- if (iColon >= 0) {
- // Make sure the colon came before a '/'.
- var iFirstSlash = name.indexOf('/');
- if (iFirstSlash < 0 || iColon < iFirstSlash)
- // Omit the leading protocol such as ndn:
- name = name.substr(iColon + 1, name.length - iColon - 1).trim();
- }
-
- if (name[0] == '/') {
- if (name.length >= 2 && name[1] == '/') {
- // Strip the authority following "//".
- var iAfterAuthority = name.indexOf('/', 2);
- if (iAfterAuthority < 0)
- // Unusual case: there was only an authority.
- return [];
- else
- name = name.substr(iAfterAuthority + 1, name.length - iAfterAuthority - 1).trim();
- }
- else
- name = name.substr(1, name.length - 1).trim();
- }
-
- var array = name.split('/');
-
- // Unescape the components.
- for (var i = 0; i < array.length; ++i) {
- var component = Name.fromEscapedString(array[i]);
-
- if (component == null) {
- // Ignore the illegal componenent. This also gets rid of a trailing '/'.
- array.splice(i, 1);
- --i;
- continue;
- }
- else
- array[i] = component;
- }
-
- return array;
-}
-
-
-Name.prototype.from_ndnb = function(/*XMLDecoder*/ decoder) {
- decoder.readStartElement(this.getElementLabel());
-
-
- this.components = new Array(); //new ArrayList<byte []>();
-
- while (decoder.peekStartElement(NDNProtocolDTags.Component)) {
- this.add(decoder.readBinaryElement(NDNProtocolDTags.Component));
- }
-
- decoder.readEndElement();
-};
-
-Name.prototype.to_ndnb = function(/*XMLEncoder*/ encoder) {
-
- if( this.components ==null )
- throw new Error("CANNOT ENCODE EMPTY CONTENT NAME");
-
- encoder.writeStartElement(this.getElementLabel());
- var count = this.components.length;
- for (var i=0; i < count; i++) {
- encoder.writeElement(NDNProtocolDTags.Component, this.components[i]);
- }
- encoder.writeEndElement();
-};
-
-Name.prototype.getElementLabel = function(){
- return NDNProtocolDTags.Name;
-};
-
-/*
- * component is a string, byte array, ArrayBuffer, Uint8Array or Name.
- * Convert to Uint8Array and add to this Name.
- * If a component is a string, encode as utf8.
- * Return this Name object to allow chaining calls to add.
- */
-Name.prototype.add = function(component){
- var result;
- if(typeof component == 'string')
- result = DataUtils.stringToUtf8Array(component);
- else if(typeof component == 'object' && component instanceof Uint8Array)
- result = new Uint8Array(component);
- else if(typeof component == 'object' && component instanceof ArrayBuffer) {
- // Make a copy. Don't use ArrayBuffer.slice since it isn't always supported.
- result = new Uint8Array(new ArrayBuffer(component.byteLength));
- result.set(new Uint8Array(component));
- }
- else if (typeof component == 'object' && component instanceof Name) {
- var components;
- if (component == this)
- // special case, when we need to create a copy
- components = this.components.slice(0, this.components.length);
- else
- components = component.components;
-
- for (var i = 0; i < components.length; ++i)
- this.components.push(new Uint8Array(components[i]));
- return this;
- }
- else if(typeof component == 'object')
- // Assume component is a byte array. We can't check instanceof Array because
- // this doesn't work in JavaScript if the array comes from a different module.
- result = new Uint8Array(component);
- else
- throw new Error("Cannot add Name element at index " + this.components.length +
- ": Invalid type");
-
- this.components.push(result);
- return this;
-};
-
-/**
- * @brief Add component that represents a segment number
- *
- * @param number Segment number (integer is expected)
- *
- * This component has a special format handling:
- * - if number is zero, then %00 is added
- * - if number is between 1 and 255, %00%01 .. %00%FF is added
- * - ...
- */
-Name.prototype.addSegment = function(number) {
- // step 1: figure out how many bytes will be needed
- var bytes = 1; // at least 1 byte
- var test_number = number;
- while (test_number > 0) {
- bytes ++;
- test_number >>= 8;
- }
-
- var result = new Uint8Array (bytes);
- var index = 0;
- result[index] = 0;
- index ++;
- while (number > 0) {
- result[index] = number & 0xFF;
- number >>= 8;
- index ++;
- }
-
- this.components.push(result);
- return this;
-}
-
-// Return the escaped name string according to "NDNx URI Scheme".
-Name.prototype.to_uri = function() {
- if (this.components.length == 0)
- return "/";
-
- var result = "";
-
- for(var i = 0; i < this.components.length; ++i)
- result += "/"+ Name.toEscapedString(this.components[i]);
-
- return result;
-};
-
-/**
-* @brief Add component that represents a segment number
-*
-* @param number Segment number (integer is expected)
-*
-* This component has a special format handling:
-* - if number is zero, then %00 is added
-* - if number is between 1 and 255, %00%01 .. %00%FF is added
-* - ...
-*/
-Name.prototype.addSegment = function(number) {
- var segmentNumberBigEndian = DataUtils.nonNegativeIntToBigEndian(number);
- // Put a 0 byte in front.
- var segmentNumberComponent = new Uint8Array(segmentNumberBigEndian.length + 1);
- segmentNumberComponent.set(segmentNumberBigEndian, 1);
-
- this.components.push(segmentNumberComponent);
- return this;
-};
-
-/*
- * Return a new Name with the first nComponents components of this Name.
- */
-Name.prototype.getPrefix = function(nComponents) {
- return new Name(this.components.slice(0, nComponents));
-}
-
-/**
- * @brief Get prefix of the name, containing less minusComponents right components
- * @param minusComponents number of components to cut from the back
- */
-Name.prototype.cut = function (minusComponents) {
- return new Name(this.components.slice(0, this.components.length-1));
-}
-
-/*
- * Return a new ArrayBuffer of the component at i.
- */
-Name.prototype.getComponent = function(i) {
- var result = new ArrayBuffer(this.components[i].length);
- new Uint8Array(result).set(this.components[i]);
- return result;
-}
-
-/*
- * The "file name" in a name is the last component that isn't blank and doesn't start with one of the
- * special marker octets (for version, etc.). Return the index in this.components of
- * the file name, or -1 if not found.
- */
-Name.prototype.indexOfFileName = function() {
- for (var i = this.components.length - 1; i >= 0; --i) {
- var component = this.components[i];
- if (component.length <= 0)
- continue;
-
- if (component[0] == 0 || component[0] == 0xC0 || component[0] == 0xC1 ||
- (component[0] >= 0xF5 && component[0] <= 0xFF))
- continue;
-
- return i;
- }
-
- return -1;
-}
-
-/*
- * Return true if this Name has the same components as name.
- */
-Name.prototype.equalsName = function(name) {
- if (this.components.length != name.components.length)
- return false;
-
- // Start from the last component because they are more likely to differ.
- for (var i = this.components.length - 1; i >= 0; --i) {
- if (!DataUtils.arraysEqual(this.components[i], name.components[i]))
- return false;
- }
-
- return true;
-}
-
-/*
- * Find the last component in name that has a ContentDigest and return the digest value as Uint8Array,
- * or null if not found. See Name.getComponentContentDigestValue.
- */
-Name.prototype.getContentDigestValue = function() {
- for (var i = this.components.length - 1; i >= 0; --i) {
- var digestValue = Name.getComponentContentDigestValue(this.components[i]);
- if (digestValue != null)
- return digestValue;
- }
-
- return null;
-}
-
-/*
- * If component is a ContentDigest, return the digest value as a Uint8Array subarray (don't modify!).
- * If not a ContentDigest, return null.
- * A ContentDigest component is Name.ContentDigestPrefix + 32 bytes + Name.ContentDigestSuffix.
- */
-Name.getComponentContentDigestValue = function(component) {
- var digestComponentLength = Name.ContentDigestPrefix.length + 32 + Name.ContentDigestSuffix.length;
- // Check for the correct length and equal ContentDigestPrefix and ContentDigestSuffix.
- if (component.length == digestComponentLength &&
- DataUtils.arraysEqual(component.subarray(0, Name.ContentDigestPrefix.length),
- Name.ContentDigestPrefix) &&
- DataUtils.arraysEqual(component.subarray
- (component.length - Name.ContentDigestSuffix.length, component.length),
- Name.ContentDigestSuffix))
- return component.subarray(Name.ContentDigestPrefix.length, Name.ContentDigestPrefix.length + 32);
- else
- return null;
-}
-
-// Meta GUID "%C1.M.G%C1" + ContentDigest with a 32 byte BLOB.
-Name.ContentDigestPrefix = new Uint8Array([0xc1, 0x2e, 0x4d, 0x2e, 0x47, 0xc1, 0x01, 0xaa, 0x02, 0x85]);
-Name.ContentDigestSuffix = new Uint8Array([0x00]);
-
-/*
- * Return component as an escaped string according to "NDNx URI Scheme".
- * We can't use encodeURIComponent because that doesn't encode all the characters we want to.
- */
-Name.toEscapedString = function(component) {
- var result = "";
- var gotNonDot = false;
- for (var i = 0; i < component.length; ++i) {
- if (component[i] != 0x2e) {
- gotNonDot = true;
- break;
- }
- }
- if (!gotNonDot) {
- // Special case for component of zero or more periods. Add 3 periods.
- result = "...";
- for (var i = 0; i < component.length; ++i)
- result += ".";
- }
- else {
- for (var i = 0; i < component.length; ++i) {
- var value = component[i];
- // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
- if (value >= 0x30 && value <= 0x39 || value >= 0x41 && value <= 0x5a ||
- value >= 0x61 && value <= 0x7a || value == 0x2b || value == 0x2d ||
- value == 0x2e || value == 0x5f)
- result += String.fromCharCode(value);
- else
- result += "%" + (value < 16 ? "0" : "") + value.toString(16).toUpperCase();
- }
- }
- return result;
-};
-
-/*
- * Return component as a Uint8Array by decoding the escapedString according to "NDNx URI Scheme".
- * If escapedString is "", "." or ".." then return null, which means to skip the component in the name.
- */
-Name.fromEscapedString = function(escapedString) {
- var component = unescape(escapedString.trim());
-
- if (component.match(/[^.]/) == null) {
- // Special case for component of only periods.
- if (component.length <= 2)
- // Zero, one or two periods is illegal. Ignore this componenent to be
- // consistent with the C implementation.
- return null;
- else
- // Remove 3 periods.
- return DataUtils.toNumbersFromString(component.substr(3, component.length - 3));
- }
- else
- return DataUtils.toNumbersFromString(component);
-}
-
-Name.prototype.match = function(/*Name*/ name) {
- var i_name = this.components;
- var o_name = name.components;
-
- // The intrest name is longer than the name we are checking it against.
- if (i_name.length > o_name.length)
- return false;
-
- // Check if at least one of given components doesn't match.
- for (var i = 0; i < i_name.length; ++i) {
- if (!DataUtils.arraysEqual(i_name[i], o_name[i]))
- return false;
- }
-
- return true;
-};
-/**
- * @author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- * This class represents ContentObject Objects
- */
-var ContentObject = function ContentObject(_name,_signedInfo,_content,_signature){
-
-
- if (typeof _name == 'string') {
- this.name = new Name(_name);
- }
- else{
- //TODO Check the class of _name
- this.name = _name;
- }
- this.signedInfo = _signedInfo;
-
- if (typeof _content == 'string') {
- this.content = DataUtils.toNumbersFromString(_content);
- } else {
- this.content = _content;
- }
-
- this.signature = _signature;
-
-
- this.startSIG = null;
- this.endSIG = null;
-
- //this.startSignedInfo = null;
- this.endContent = null;
-
- this.rawSignatureData = null;
-};
-
-ContentObject.prototype.sign = function(){
-
- var n1 = this.encodeObject(this.name);
- var n2 = this.encodeObject(this.signedInfo);
- var n3 = this.encodeContent();
- /*console.log('sign: ');
- console.log(n1);
- console.log(n2);
- console.log(n3);*/
-
- //var n = n1.concat(n2,n3);
- var tempBuf = new ArrayBuffer(n1.length + n2.length + n3.length);
- var n = new Uint8Array(tempBuf);
- //console.log(n);
- n.set(n1, 0);
- //console.log(n);
- n.set(n2, n1.length);
- //console.log(n);
- n.set(n3, n1.length + n2.length);
- //console.log(n);
-
- if(LOG>4)console.log('Signature Data is (binary) '+n);
-
- if(LOG>4)console.log('Signature Data is (RawString)');
-
- if(LOG>4)console.log( DataUtils.toString(n) );
-
- //var sig = DataUtils.toString(n);
-
-
- var rsa = new RSAKey();
-
- rsa.readPrivateKeyFromPEMString(globalKeyManager.privateKey);
-
- //var hSig = rsa.signString(sig, "sha256");
-
- var hSig = rsa.signByteArrayWithSHA256(n);
-
-
- if(LOG>4)console.log('SIGNATURE SAVED IS');
-
- if(LOG>4)console.log(hSig);
-
- if(LOG>4)console.log( DataUtils.toNumbers(hSig.trim()));
-
- this.signature.signature = DataUtils.toNumbers(hSig.trim());
-
-
-};
-
-ContentObject.prototype.encodeObject = function encodeObject(obj){
- var enc = new BinaryXMLEncoder();
-
- obj.to_ndnb(enc);
-
- var num = enc.getReducedOstream();
-
- return num;
-
-
-};
-
-ContentObject.prototype.encodeContent = function encodeContent(obj){
- var enc = new BinaryXMLEncoder();
-
- enc.writeElement(NDNProtocolDTags.Content, this.content);
-
- var num = enc.getReducedOstream();
-
- return num;
-
-
-};
-
-ContentObject.prototype.saveRawData = function(bytes){
-
- var sigBits = bytes.subarray(this.startSIG, this.endSIG);
-
- this.rawSignatureData = sigBits;
-};
-
-ContentObject.prototype.from_ndnb = function(/*XMLDecoder*/ decoder) {
-
- // TODO VALIDATE THAT ALL FIELDS EXCEPT SIGNATURE ARE PRESENT
-
- decoder.readStartElement(this.getElementLabel());
-
-
- if( decoder.peekStartElement(NDNProtocolDTags.Signature) ){
- this.signature = new Signature();
- this.signature.from_ndnb(decoder);
- }
-
- //this.endSIG = decoder.offset;
-
- this.startSIG = decoder.offset;
-
- this.name = new Name();
- this.name.from_ndnb(decoder);
-
- //this.startSignedInfo = decoder.offset;
-
-
- if( decoder.peekStartElement(NDNProtocolDTags.SignedInfo) ){
- this.signedInfo = new SignedInfo();
- this.signedInfo.from_ndnb(decoder);
- }
-
- this.content = decoder.readBinaryElement(NDNProtocolDTags.Content);
-
-
- //this.endContent = decoder.offset;
- this.endSIG = decoder.offset;
-
-
- decoder.readEndElement();
-
- this.saveRawData(decoder.istream);
-};
-
-ContentObject.prototype.to_ndnb = function(/*XMLEncoder*/ encoder) {
-
- //TODO verify name, SignedInfo and Signature is present
-
-
- encoder.writeStartElement(this.getElementLabel());
-
-
-
-
- if(null!=this.signature) this.signature.to_ndnb(encoder);
-
-
- this.startSIG = encoder.offset;
-
-
- if(null!=this.name) this.name.to_ndnb(encoder);
-
- //this.endSIG = encoder.offset;
- //this.startSignedInfo = encoder.offset;
-
-
- if(null!=this.signedInfo) this.signedInfo.to_ndnb(encoder);
-
- encoder.writeElement(NDNProtocolDTags.Content, this.content);
-
-
- this.endSIG = encoder.offset;
-
- //this.endContent = encoder.offset;
-
-
- encoder.writeEndElement();
-
- this.saveRawData(encoder.ostream);
-
-};
-
-ContentObject.prototype.getElementLabel= function(){return NDNProtocolDTags.ContentObject;};
-
-/**
- * Signature
- */
-var Signature = function Signature(_witness,_signature,_digestAlgorithm) {
-
- this.Witness = _witness;//byte [] _witness;
- this.signature = _signature;//byte [] _signature;
- this.digestAlgorithm = _digestAlgorithm//String _digestAlgorithm;
-};
-
-Signature.prototype.from_ndnb =function( decoder) {
- decoder.readStartElement(this.getElementLabel());
-
- if(LOG>4)console.log('STARTED DECODING SIGNATURE');
-
- if (decoder.peekStartElement(NDNProtocolDTags.DigestAlgorithm)) {
- if(LOG>4)console.log('DIGIEST ALGORITHM FOUND');
- this.digestAlgorithm = decoder.readUTF8Element(NDNProtocolDTags.DigestAlgorithm);
- }
- if (decoder.peekStartElement(NDNProtocolDTags.Witness)) {
- if(LOG>4)console.log('WITNESS FOUND');
- this.Witness = decoder.readBinaryElement(NDNProtocolDTags.Witness);
- }
-
- //FORCE TO READ A SIGNATURE
-
- if(LOG>4)console.log('SIGNATURE FOUND');
- this.signature = decoder.readBinaryElement(NDNProtocolDTags.SignatureBits);
-
- decoder.readEndElement();
-
-};
-
-
-Signature.prototype.to_ndnb= function( encoder){
-
- if (!this.validate()) {
- throw new Error("Cannot encode: field values missing.");
- }
-
- encoder.writeStartElement(this.getElementLabel());
-
- if ((null != this.digestAlgorithm) && (!this.digestAlgorithm.equals(NDNDigestHelper.DEFAULT_DIGEST_ALGORITHM))) {
- encoder.writeElement(NDNProtocolDTags.DigestAlgorithm, OIDLookup.getDigestOID(this.DigestAlgorithm));
- }
-
- if (null != this.Witness) {
- // needs to handle null witness
- encoder.writeElement(NDNProtocolDTags.Witness, this.Witness);
- }
-
- encoder.writeElement(NDNProtocolDTags.SignatureBits, this.signature);
-
- encoder.writeEndElement();
-};
-
-Signature.prototype.getElementLabel = function() { return NDNProtocolDTags.Signature; };
-
-
-Signature.prototype.validate = function() {
- return null != this.signature;
-};
-
-
-/**
- * SignedInfo
- */
-var ContentType = {DATA:0, ENCR:1, GONE:2, KEY:3, LINK:4, NACK:5};
-var ContentTypeValue = {0:0x0C04C0, 1:0x10D091,2:0x18E344,3:0x28463F,4:0x2C834A,5:0x34008A};
-var ContentTypeValueReverse = {0x0C04C0:0, 0x10D091:1,0x18E344:2,0x28463F:3,0x2C834A:4,0x34008A:5};
-
-var SignedInfo = function SignedInfo(_publisher,_timestamp,_type,_locator,_freshnessSeconds,_finalBlockID){
-
- //TODO, Check types
-
- this.publisher = _publisher; //publisherPublicKeyDigest
- this.timestamp=_timestamp; // NDN Time
- this.type=_type; // ContentType
- this.locator =_locator;//KeyLocator
- this.freshnessSeconds =_freshnessSeconds; // Integer
- this.finalBlockID=_finalBlockID; //byte array
-
- // SWT: merge setFields() method into constructor
- this.setFields();
-
-};
-
-SignedInfo.prototype.setFields = function(){
- //BASE64 -> RAW STRING
-
- //this.locator = new KeyLocator( DataUtils.toNumbersFromString(stringCertificate) ,KeyLocatorType.CERTIFICATE );
-
- var publicKeyHex = globalKeyManager.publicKey;
-
- if(LOG>4)console.log('PUBLIC KEY TO WRITE TO CONTENT OBJECT IS ');
- if(LOG>4)console.log(publicKeyHex);
-
- var publicKeyBytes = DataUtils.toNumbers(globalKeyManager.publicKey) ;
-
-
-
- //var stringCertificate = DataUtils.base64toString(globalKeyManager.certificate);
-
- //if(LOG>3)console.log('string Certificate is '+stringCertificate);
-
- //HEX -> BYTE ARRAY
- //var publisherkey = DataUtils.toNumbers(hex_sha256(stringCertificate));
-
- //if(LOG>3)console.log('publisher key is ');
- //if(LOG>3)console.log(publisherkey);
-
- var publisherKeyDigest = hex_sha256_from_bytes(publicKeyBytes);
-
- this.publisher = new PublisherPublicKeyDigest( DataUtils.toNumbers( publisherKeyDigest ) );
-
- //this.publisher = new PublisherPublicKeyDigest(publisherkey);
-
- var d = new Date();
-
- var time = d.getTime();
-
-
- this.timestamp = new NDNTime( time );
-
- if(LOG>4)console.log('TIME msec is');
-
- if(LOG>4)console.log(this.timestamp.msec);
-
- //DATA
- this.type = 0;//0x0C04C0;//ContentTypeValue[ContentType.DATA];
-
- //if(LOG>4)console.log('toNumbersFromString(stringCertificate) '+DataUtils.toNumbersFromString(stringCertificate));
-
- if(LOG>4)console.log('PUBLIC KEY TO WRITE TO CONTENT OBJECT IS ');
- if(LOG>4)console.log(publicKeyBytes);
-
- this.locator = new KeyLocator( publicKeyBytes ,KeyLocatorType.KEY );
-
- //this.locator = new KeyLocator( DataUtils.toNumbersFromString(stringCertificate) ,KeyLocatorType.CERTIFICATE );
-
-};
-
-SignedInfo.prototype.from_ndnb = function( decoder){
-
- decoder.readStartElement( this.getElementLabel() );
-
- if (decoder.peekStartElement(NDNProtocolDTags.PublisherPublicKeyDigest)) {
- if(LOG>4)console.log('DECODING PUBLISHER KEY');
- this.publisher = new PublisherPublicKeyDigest();
- this.publisher.from_ndnb(decoder);
- }
-
- if (decoder.peekStartElement(NDNProtocolDTags.Timestamp)) {
- if(LOG>4)console.log('DECODING TIMESTAMP');
- this.timestamp = decoder.readDateTime(NDNProtocolDTags.Timestamp);
- }
-
- if (decoder.peekStartElement(NDNProtocolDTags.Type)) {
- var binType = decoder.readBinaryElement(NDNProtocolDTags.Type);//byte []
-
-
- //TODO Implement type of Key Reading
-
- if(LOG>4)console.log('Binary Type of of Signed Info is '+binType);
-
- this.type = binType;
-
-
- //TODO Implement type of Key Reading
-
-
- if (null == this.type) {
- throw new Error("Cannot parse signedInfo type: bytes.");
- }
-
- } else {
- this.type = ContentType.DATA; // default
- }
-
- if (decoder.peekStartElement(NDNProtocolDTags.FreshnessSeconds)) {
- this.freshnessSeconds = decoder.readIntegerElement(NDNProtocolDTags.FreshnessSeconds);
- if(LOG>4)console.log('FRESHNESS IN SECONDS IS '+ this.freshnessSeconds);
- }
-
- if (decoder.peekStartElement(NDNProtocolDTags.FinalBlockID)) {
- if(LOG>4)console.log('DECODING FINAL BLOCKID');
- this.finalBlockID = decoder.readBinaryElement(NDNProtocolDTags.FinalBlockID);
- }
-
- if (decoder.peekStartElement(NDNProtocolDTags.KeyLocator)) {
- if(LOG>4)console.log('DECODING KEY LOCATOR');
- this.locator = new KeyLocator();
- this.locator.from_ndnb(decoder);
- }
-
- decoder.readEndElement();
-};
-
-SignedInfo.prototype.to_ndnb = function( encoder) {
- if (!this.validate()) {
- throw new Error("Cannot encode : field values missing.");
- }
- encoder.writeStartElement(this.getElementLabel());
-
- if (null!=this.publisher) {
- if(LOG>3) console.log('ENCODING PUBLISHER KEY' + this.publisher.publisherPublicKeyDigest);
-
- this.publisher.to_ndnb(encoder);
- }
-
- if (null!=this.timestamp) {
- encoder.writeDateTime(NDNProtocolDTags.Timestamp, this.timestamp );
- }
-
- if (null!=this.type && this.type !=0) {
-
- encoder.writeElement(NDNProtocolDTags.type, this.type);
- }
-
- if (null!=this.freshnessSeconds) {
- encoder.writeElement(NDNProtocolDTags.FreshnessSeconds, this.freshnessSeconds);
- }
-
- if (null!=this.finalBlockID) {
- encoder.writeElement(NDNProtocolDTags.FinalBlockID, this.finalBlockID);
- }
-
- if (null!=this.locator) {
- this.locator.to_ndnb(encoder);
- }
-
- encoder.writeEndElement();
-};
-
-SignedInfo.prototype.valueToType = function(){
- //for (Entry<byte [], ContentType> entry : ContentValueTypes.entrySet()) {
- //if (Arrays.equals(value, entry.getKey()))
- //return entry.getValue();
- //}
- return null;
-
-};
-
-SignedInfo.prototype.getElementLabel = function() {
- return NDNProtocolDTags.SignedInfo;
-};
-
-SignedInfo.prototype.validate = function() {
- // We don't do partial matches any more, even though encoder/decoder
- // is still pretty generous.
- if (null ==this.publisher || null==this.timestamp ||null== this.locator)
- return false;
- return true;
-};
-/*
- * Date Format 1.2.3
- * (c) 2007-2009 Steven Levithan <stevenlevithan.com>
- * MIT license
- *
- * Includes enhancements by Scott Trenda <scott.trenda.net>
- * and Kris Kowal <cixar.com/~kris.kowal/>
- *
- * Accepts a date, a mask, or a date and a mask.
- * Returns a formatted version of the given date.
- * The date defaults to the current date/time.
- * The mask defaults to dateFormat.masks.default.
- */
-
-var DateFormat = function () {
- var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
- timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
- timezoneClip = /[^-+\dA-Z]/g,
- pad = function (val, len) {
- val = String(val);
- len = len || 2;
- while (val.length < len) val = "0" + val;
- return val;
- };
-
- // Regexes and supporting functions are cached through closure
- return function (date, mask, utc) {
- var dF = dateFormat;
-
- // You can't provide utc if you skip other args (use the "UTC:" mask prefix)
- if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
- mask = date;
- date = undefined;
- }
-
- // Passing date through Date applies Date.parse, if necessary
- date = date ? new Date(date) : new Date;
- if (isNaN(date)) throw SyntaxError("invalid date");
-
- mask = String(dF.masks[mask] || mask || dF.masks["default"]);
-
- // Allow setting the utc argument via the mask
- if (mask.slice(0, 4) == "UTC:") {
- mask = mask.slice(4);
- utc = true;
- }
-
- var _ = utc ? "getUTC" : "get",
- d = date[_ + "Date"](),
- D = date[_ + "Day"](),
- m = date[_ + "Month"](),
- y = date[_ + "FullYear"](),
- H = date[_ + "Hours"](),
- M = date[_ + "Minutes"](),
- s = date[_ + "Seconds"](),
- L = date[_ + "Milliseconds"](),
- o = utc ? 0 : date.getTimezoneOffset(),
- flags = {
- d: d,
- dd: pad(d),
- ddd: dF.i18n.dayNames[D],
- dddd: dF.i18n.dayNames[D + 7],
- m: m + 1,
- mm: pad(m + 1),
- mmm: dF.i18n.monthNames[m],
- mmmm: dF.i18n.monthNames[m + 12],
- yy: String(y).slice(2),
- yyyy: y,
- h: H % 12 || 12,
- hh: pad(H % 12 || 12),
- H: H,
- HH: pad(H),
- M: M,
- MM: pad(M),
- s: s,
- ss: pad(s),
- l: pad(L, 3),
- L: pad(L > 99 ? Math.round(L / 10) : L),
- t: H < 12 ? "a" : "p",
- tt: H < 12 ? "am" : "pm",
- T: H < 12 ? "A" : "P",
- TT: H < 12 ? "AM" : "PM",
- Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
- o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
- S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
- };
-
- return mask.replace(token, function ($0) {
- return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
- });
- };
-}();
-
-// Some common format strings
-DateFormat.masks = {
- "default": "ddd mmm dd yyyy HH:MM:ss",
- shortDate: "m/d/yy",
- mediumDate: "mmm d, yyyy",
- longDate: "mmmm d, yyyy",
- fullDate: "dddd, mmmm d, yyyy",
- shortTime: "h:MM TT",
- mediumTime: "h:MM:ss TT",
- longTime: "h:MM:ss TT Z",
- isoDate: "yyyy-mm-dd",
- isoTime: "HH:MM:ss",
- isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
- isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
-};
-
-// Internationalization strings
-DateFormat.i18n = {
- dayNames: [
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
- "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
- ],
- monthNames: [
- "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
- "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
- ]
-};
-
-// For convenience...
-Date.prototype.format = function (mask, utc) {
- return dateFormat(this, mask, utc);
-};
-/**
- * @author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- * This class represents Interest Objects
- */
-
-// _interestLifetime is in milliseconds.
-var Interest = function Interest
- (_name, _faceInstance, _minSuffixComponents, _maxSuffixComponents, _publisherPublicKeyDigest, _exclude,
- _childSelector, _answerOriginKind, _scope, _interestLifetime, _nonce) {
-
- this.name = _name;
- this.faceInstance = _faceInstance;
- this.maxSuffixComponents = _maxSuffixComponents;
- this.minSuffixComponents = _minSuffixComponents;
-
- this.publisherPublicKeyDigest = _publisherPublicKeyDigest;
- this.exclude = _exclude;
- this.childSelector = _childSelector;
- this.answerOriginKind = _answerOriginKind;
- this.scope = _scope;
- this.interestLifetime = _interestLifetime; // milli seconds
- this.nonce = _nonce;
-};
-
-Interest.RECURSIVE_POSTFIX = "*";
-
-Interest.CHILD_SELECTOR_LEFT = 0;
-Interest.CHILD_SELECTOR_RIGHT = 1;
-Interest.ANSWER_CONTENT_STORE = 1;
-Interest.ANSWER_GENERATED = 2;
-Interest.ANSWER_STALE = 4; // Stale answer OK
-Interest.MARK_STALE = 16; // Must have scope 0. Michael calls this a "hack"
-
-Interest.DEFAULT_ANSWER_ORIGIN_KIND = Interest.ANSWER_CONTENT_STORE | Interest.ANSWER_GENERATED;
-
-
-Interest.prototype.from_ndnb = function(/*XMLDecoder*/ decoder) {
-
- decoder.readStartElement(NDNProtocolDTags.Interest);
-
- this.name = new Name();
- this.name.from_ndnb(decoder);
-
- if (decoder.peekStartElement(NDNProtocolDTags.MinSuffixComponents))
- this.minSuffixComponents = decoder.readIntegerElement(NDNProtocolDTags.MinSuffixComponents);
-
- if (decoder.peekStartElement(NDNProtocolDTags.MaxSuffixComponents))
- this.maxSuffixComponents = decoder.readIntegerElement(NDNProtocolDTags.MaxSuffixComponents);
-
- if (decoder.peekStartElement(NDNProtocolDTags.PublisherPublicKeyDigest)) {
- this.publisherPublicKeyDigest = new PublisherPublicKeyDigest();
- this.publisherPublicKeyDigest.from_ndnb(decoder);
- }
-
- if (decoder.peekStartElement(NDNProtocolDTags.Exclude)) {
- this.exclude = new Exclude();
- this.exclude.from_ndnb(decoder);
- }
-
- if (decoder.peekStartElement(NDNProtocolDTags.ChildSelector))
- this.childSelector = decoder.readIntegerElement(NDNProtocolDTags.ChildSelector);
-
- if (decoder.peekStartElement(NDNProtocolDTags.AnswerOriginKind))
- this.answerOriginKind = decoder.readIntegerElement(NDNProtocolDTags.AnswerOriginKind);
-
- if (decoder.peekStartElement(NDNProtocolDTags.Scope))
- this.scope = decoder.readIntegerElement(NDNProtocolDTags.Scope);
-
- if (decoder.peekStartElement(NDNProtocolDTags.InterestLifetime))
- this.interestLifetime = 1000.0 * DataUtils.bigEndianToUnsignedInt
- (decoder.readBinaryElement(NDNProtocolDTags.InterestLifetime)) / 4096;
-
- if (decoder.peekStartElement(NDNProtocolDTags.Nonce))
- this.nonce = decoder.readBinaryElement(NDNProtocolDTags.Nonce);
-
- decoder.readEndElement();
-};
-
-Interest.prototype.to_ndnb = function(/*XMLEncoder*/ encoder){
- //Could check if name is present
-
- encoder.writeStartElement(NDNProtocolDTags.Interest);
-
- this.name.to_ndnb(encoder);
-
- if (null != this.minSuffixComponents)
- encoder.writeElement(NDNProtocolDTags.MinSuffixComponents, this.minSuffixComponents);
-
- if (null != this.maxSuffixComponents)
- encoder.writeElement(NDNProtocolDTags.MaxSuffixComponents, this.maxSuffixComponents);
-
- if (null != this.publisherPublicKeyDigest)
- this.publisherPublicKeyDigest.to_ndnb(encoder);
-
- if (null != this.exclude)
- this.exclude.to_ndnb(encoder);
-
- if (null != this.childSelector)
- encoder.writeElement(NDNProtocolDTags.ChildSelector, this.childSelector);
-
- if (this.DEFAULT_ANSWER_ORIGIN_KIND != this.answerOriginKind && this.answerOriginKind!=null)
- encoder.writeElement(NDNProtocolDTags.AnswerOriginKind, this.answerOriginKind);
-
- if (null != this.scope)
- encoder.writeElement(NDNProtocolDTags.Scope, this.scope);
-
- if (null != this.interestLifetime)
- encoder.writeElement(NDNProtocolDTags.InterestLifetime,
- DataUtils.nonNegativeIntToBigEndian((this.interestLifetime / 1000.0) * 4096));
-
- if (null != this.nonce)
- encoder.writeElement(NDNProtocolDTags.Nonce, this.nonce);
-
- encoder.writeEndElement();
-
-};
-
-/*
- * Return true if this.name.match(name) and the name conforms to the interest selectors.
- */
-Interest.prototype.matches_name = function(/*Name*/ name) {
- if (!this.name.match(name))
- return false;
-
- if (this.minSuffixComponents != null &&
- // Add 1 for the implicit digest.
- !(name.components.length + 1 - this.name.components.length >= this.minSuffixComponents))
- return false;
- if (this.maxSuffixComponents != null &&
- // Add 1 for the implicit digest.
- !(name.components.length + 1 - this.name.components.length <= this.maxSuffixComponents))
- return false;
- if (this.exclude != null && name.components.length > this.name.components.length &&
- this.exclude.matches(name.components[this.name.components.length]))
- return false;
-
- return true;
-};
-
-/*
- * Return a new Interest with the same fields as this Interest.
- * Note: This does NOT make a deep clone of the name, exclue or other objects.
- */
-Interest.prototype.clone = function() {
- return new Interest
- (this.name, this.faceInstance, this.minSuffixComponents, this.maxSuffixComponents,
- this.publisherPublicKeyDigest, this.exclude, this.childSelector, this.answerOriginKind,
- this.scope, this.interestLifetime, this.nonce);
-};
-
-/*
- * Handle the interest Exclude element.
- * _values is an array where each element is either Uint8Array component or Exclude.ANY.
- */
-var Exclude = function Exclude(_values) {
- this.values = (_values || []);
-}
-
-Exclude.ANY = "*";
-
-Exclude.prototype.from_ndnb = function(/*XMLDecoder*/ decoder) {
- decoder.readStartElement(NDNProtocolDTags.Exclude);
-
- while (true) {
- if (decoder.peekStartElement(NDNProtocolDTags.Component))
- this.values.push(decoder.readBinaryElement(NDNProtocolDTags.Component));
- else if (decoder.peekStartElement(NDNProtocolDTags.Any)) {
- decoder.readStartElement(NDNProtocolDTags.Any);
- decoder.readEndElement();
- this.values.push(Exclude.ANY);
- }
- else if (decoder.peekStartElement(NDNProtocolDTags.Bloom)) {
- // Skip the Bloom and treat it as Any.
- decoder.readBinaryElement(NDNProtocolDTags.Bloom);
- this.values.push(Exclude.ANY);
- }
- else
- break;
- }
-
- decoder.readEndElement();
-};
-
-Exclude.prototype.to_ndnb = function(/*XMLEncoder*/ encoder) {
- if (this.values == null || this.values.length == 0)
- return;
-
- encoder.writeStartElement(NDNProtocolDTags.Exclude);
-
- // TODO: Do we want to order the components (except for ANY)?
- for (var i = 0; i < this.values.length; ++i) {
- if (this.values[i] == Exclude.ANY) {
- encoder.writeStartElement(NDNProtocolDTags.Any);
- encoder.writeEndElement();
- }
- else
- encoder.writeElement(NDNProtocolDTags.Component, this.values[i]);
- }
-
- encoder.writeEndElement();
-};
-
-/*
- * Return a string with elements separated by "," and Exclude.ANY shown as "*".
- */
-Exclude.prototype.to_uri = function() {
- if (this.values == null || this.values.length == 0)
- return "";
-
- var result = "";
- for (var i = 0; i < this.values.length; ++i) {
- if (i > 0)
- result += ",";
-
- if (this.values[i] == Exclude.ANY)
- result += "*";
- else
- result += Name.toEscapedString(this.values[i]);
- }
- return result;
-};
-
-/*
- * Return true if the component matches any of the exclude criteria.
- */
-Exclude.prototype.matches = function(/*Uint8Array*/ component) {
- for (var i = 0; i < this.values.length; ++i) {
- if (this.values[i] == Exclude.ANY) {
- var lowerBound = null;
- if (i > 0)
- lowerBound = this.values[i - 1];
-
- // Find the upper bound, possibly skipping over multiple ANY in a row.
- var iUpperBound;
- var upperBound = null;
- for (iUpperBound = i + 1; iUpperBound < this.values.length; ++iUpperBound) {
- if (this.values[iUpperBound] != Exclude.ANY) {
- upperBound = this.values[iUpperBound];
- break;
- }
- }
-
- // If lowerBound != null, we already checked component equals lowerBound on the last pass.
- // If upperBound != null, we will check component equals upperBound on the next pass.
- if (upperBound != null) {
- if (lowerBound != null) {
- if (Exclude.compareComponents(component, lowerBound) > 0 &&
- Exclude.compareComponents(component, upperBound) < 0)
- return true;
- }
- else {
- if (Exclude.compareComponents(component, upperBound) < 0)
- return true;
- }
-
- // Make i equal iUpperBound on the next pass.
- i = iUpperBound - 1;
- }
- else {
- if (lowerBound != null) {
- if (Exclude.compareComponents(component, lowerBound) > 0)
- return true;
- }
- else
- // this.values has only ANY.
- return true;
- }
- }
- else {
- if (DataUtils.arraysEqual(component, this.values[i]))
- return true;
- }
- }
-
- return false;
-};
-
-/*
- * Return -1 if component1 is less than component2, 1 if greater or 0 if equal.
- * A component is less if it is shorter, otherwise if equal length do a byte comparison.
- */
-Exclude.compareComponents = function(/*Uint8Array*/ component1, /*Uint8Array*/ component2) {
- if (component1.length < component2.length)
- return -1;
- if (component1.length > component2.length)
- return 1;
-
- for (var i = 0; i < component1.length; ++i) {
- if (component1[i] < component2[i])
- return -1;
- if (component1[i] > component2[i])
- return 1;
- }
-
- return 0;
-};
-/**
- * @author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- * This class represents Key Objects
- */
-
-var Key = function Key(){
- /* TODO: Port from PyNDN:
- generateRSA()
- privateToDER()
- publicToDER()
- privateToPEM()
- publicToPEM()
- fromDER()
- fromPEM()
+}(this, function() {
+ /**
+ * Main function giving a function stack trace with a forced or passed in Error
+ *
+ * @cfg {Error} e The error to create a stacktrace from (optional)
+ * @cfg {Boolean} guess If we should try to resolve the names of anonymous functions
+ * @return {Array} of Strings with functions, lines, files, and arguments where possible
*/
-}
-
-/**
- * KeyLocator
- */
-var KeyLocatorType = {
- KEY:1,
- CERTIFICATE:2,
- KEYNAME:3
-};
-
-var KeyLocator = function KeyLocator(_input,_type){
-
- this.type = _type;
-
- if (_type == KeyLocatorType.KEYNAME){
- if (LOG>3) console.log('KeyLocator: SET KEYNAME');
- this.keyName = _input;
- }
- else if (_type == KeyLocatorType.KEY){
- if (LOG>3) console.log('KeyLocator: SET KEY');
- this.publicKey = _input;
- }
- else if (_type == KeyLocatorType.CERTIFICATE){
- if (LOG>3) console.log('KeyLocator: SET CERTIFICATE');
- this.certificate = _input;
+ function printStackTrace(options) {
+ options = options || {guess: true};
+ var ex = options.e || null, guess = !!options.guess, mode = options.mode || null;
+ var p = new printStackTrace.implementation(), result = p.run(ex, mode);
+ return (guess) ? p.guessAnonymousFunctions(result) : result;
}
-};
+ printStackTrace.implementation = function() {
+ };
-KeyLocator.prototype.from_ndnb = function(decoder) {
+ printStackTrace.implementation.prototype = {
+ /**
+ * @param {Error} [ex] The error to create a stacktrace from (optional)
+ * @param {String} [mode] Forced mode (optional, mostly for unit tests)
+ */
+ run: function(ex, mode) {
+ ex = ex || this.createException();
+ mode = mode || this.mode(ex);
+ if (mode === 'other') {
+ return this.other(arguments.callee);
+ } else {
+ return this[mode](ex);
+ }
+ },
- decoder.readStartElement(this.getElementLabel());
+ createException: function() {
+ try {
+ this.undef();
+ } catch (e) {
+ return e;
+ }
+ },
- if (decoder.peekStartElement(NDNProtocolDTags.Key)) {
- try {
- var encodedKey = decoder.readBinaryElement(NDNProtocolDTags.Key);
- // This is a DER-encoded SubjectPublicKeyInfo.
-
- //TODO FIX THIS, This should create a Key Object instead of keeping bytes
+ /**
+ * Mode could differ for different exception, e.g.
+ * exceptions in Chrome may or may not have arguments or stack.
+ *
+ * @return {String} mode of operation for the exception
+ */
+ mode: function(e) {
+ if (typeof window !== 'undefined' && window.navigator.userAgent.indexOf('PhantomJS') > -1) {
+ return 'phantomjs';
+ }
- this.publicKey = encodedKey;//CryptoUtil.getPublicKey(encodedKey);
- this.type = KeyLocatorType.KEY;
-
+ if (e['arguments'] && e.stack) {
+ return 'chrome';
+ }
- if(LOG>4) console.log('PUBLIC KEY FOUND: '+ this.publicKey);
- //this.publicKey = encodedKey;
-
-
- } catch (e) {
- throw new Error("Cannot parse key: ", e);
- }
+ if (e.stack && e.sourceURL) {
+ return 'safari';
+ }
- if (null == this.publicKey) {
- throw new Error("Cannot parse key: ");
- }
+ if (e.stack && e.number) {
+ return 'ie';
+ }
- } else if ( decoder.peekStartElement(NDNProtocolDTags.Certificate)) {
- try {
- var encodedCert = decoder.readBinaryElement(NDNProtocolDTags.Certificate);
-
- /*
- * Certificates not yet working
- */
-
- //CertificateFactory factory = CertificateFactory.getInstance("X.509");
- //this.certificate = (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(encodedCert));
-
+ if (e.stack && e.fileName) {
+ return 'firefox';
+ }
- this.certificate = encodedCert;
- this.type = KeyLocatorType.CERTIFICATE;
-
- if(LOG>4) console.log('CERTIFICATE FOUND: '+ this.certificate);
-
- } catch ( e) {
- throw new Error("Cannot decode certificate: " + e);
- }
- if (null == this.certificate) {
- throw new Error("Cannot parse certificate! ");
- }
- } else {
- this.type = KeyLocatorType.KEYNAME;
-
- this.keyName = new KeyName();
- this.keyName.from_ndnb(decoder);
- }
- decoder.readEndElement();
-};
-
-
-KeyLocator.prototype.to_ndnb = function( encoder) {
-
- if(LOG>4) console.log('type is is ' + this.type);
- //TODO Check if Name is missing
- if (!this.validate()) {
- throw new ContentEncodingException("Cannot encode " + this.getClass().getName() + ": field values missing.");
- }
-
-
- //TODO FIX THIS TOO
- encoder.writeStartElement(this.getElementLabel());
-
- if (this.type == KeyLocatorType.KEY) {
- if(LOG>5)console.log('About to encode a public key' +this.publicKey);
- encoder.writeElement(NDNProtocolDTags.Key, this.publicKey);
-
- } else if (this.type == KeyLocatorType.CERTIFICATE) {
-
- try {
- encoder.writeElement(NDNProtocolDTags.Certificate, this.certificate);
- } catch ( e) {
- throw new Error("CertificateEncodingException attempting to write key locator: " + e);
- }
-
- } else if (this.type == KeyLocatorType.KEYNAME) {
-
- this.keyName.to_ndnb(encoder);
- }
- encoder.writeEndElement();
-
-};
-
-KeyLocator.prototype.getElementLabel = function() {
- return NDNProtocolDTags.KeyLocator;
-};
-
-KeyLocator.prototype.validate = function() {
- return ( (null != this.keyName) || (null != this.publicKey) || (null != this.certificate) );
-};
-
-/**
- * KeyName is only used by KeyLocator.
- */
-var KeyName = function KeyName() {
- this.contentName = this.contentName; //contentName
- this.publisherID = this.publisherID; //publisherID
-
-};
-
-KeyName.prototype.from_ndnb=function( decoder){
-
-
- decoder.readStartElement(this.getElementLabel());
-
- this.contentName = new Name();
- this.contentName.from_ndnb(decoder);
-
- if(LOG>4) console.log('KEY NAME FOUND: ');
-
- if ( PublisherID.peek(decoder) ) {
- this.publisherID = new PublisherID();
- this.publisherID.from_ndnb(decoder);
- }
-
- decoder.readEndElement();
-};
-
-KeyName.prototype.to_ndnb = function( encoder) {
- if (!this.validate()) {
- throw new Error("Cannot encode : field values missing.");
- }
-
- encoder.writeStartElement(this.getElementLabel());
-
- this.contentName.to_ndnb(encoder);
- if (null != this.publisherID)
- this.publisherID.to_ndnb(encoder);
-
- encoder.writeEndElement();
-};
-
-KeyName.prototype.getElementLabel = function() { return NDNProtocolDTags.KeyName; };
-
-KeyName.prototype.validate = function() {
- // DKS -- do we do recursive validation?
- // null signedInfo ok
- return (null != this.contentName);
-};
-
-/**
- * @author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- * This class represents Publisher and PublisherType Objects
- */
-
-
-var PublisherType = function PublisherType(_tag){
- this.KEY =(NDNProtocolDTags.PublisherPublicKeyDigest);
- this.CERTIFICATE= (NDNProtocolDTags.PublisherCertificateDigest);
- this.ISSUER_KEY= (NDNProtocolDTags.PublisherIssuerKeyDigest);
- this.ISSUER_CERTIFICATE =(NDNProtocolDTags.PublisherIssuerCertificateDigest);
-
- this.Tag = _tag;
-};
-
-var isTypeTagVal = function(tagVal) {
- if ((tagVal == NDNProtocolDTags.PublisherPublicKeyDigest) ||
- (tagVal == NDNProtocolDTags.PublisherCertificateDigest) ||
- (tagVal == NDNProtocolDTags.PublisherIssuerKeyDigest) ||
- (tagVal == NDNProtocolDTags.PublisherIssuerCertificateDigest)) {
- return true;
- }
- return false;
-};
-
-
-
-
-var PublisherID = function PublisherID() {
-
- this.PUBLISHER_ID_DIGEST_ALGORITHM = "SHA-256";
- this.PUBLISHER_ID_LEN = 256/8;
-
- //TODO, implement publisherID creation and key creation
-
- //TODO implement generatePublicKeyDigest
- this.publisherID =null;//= generatePublicKeyDigest(key);//ByteArray
-
- //TODO implement generate key
- //CryptoUtil.generateKeyID(PUBLISHER_ID_DIGEST_ALGORITHM, key);
- this.publisherType = null;//isIssuer ? PublisherType.ISSUER_KEY : PublisherType.KEY;//publisher Type
-
-};
-
-
-PublisherID.prototype.from_ndnb = function(decoder) {
-
- // We have a choice here of one of 4 binary element types.
- var nextTag = decoder.peekStartElementAsLong();
-
- if (null == nextTag) {
- throw new Error("Cannot parse publisher ID.");
- }
-
- this.publisherType = new PublisherType(nextTag);
-
- if (!isTypeTagVal(nextTag)) {
- throw new Error("Invalid publisher ID, got unexpected type: " + nextTag);
- }
- this.publisherID = decoder.readBinaryElement(nextTag);
- if (null == this.publisherID) {
- throw new ContentDecodingException(new Error("Cannot parse publisher ID of type : " + nextTag + "."));
- }
-};
-
-PublisherID.prototype.to_ndnb = function(encoder) {
- if (!this.validate()) {
- throw new Error("Cannot encode " + this.getClass().getName() + ": field values missing.");
- }
-
- encoder.writeElement(this.getElementLabel(), this.publisherID);
-};
-
-PublisherID.peek = function(/* XMLDecoder */ decoder) {
-
- //Long
- var nextTag = decoder.peekStartElementAsLong();
-
- if (null == nextTag) {
- // on end element
- return false;
- }
- return (isTypeTagVal(nextTag));
- };
-
-PublisherID.prototype.getElementLabel = function() {
- return this.publisherType.Tag;
-};
-
-PublisherID.prototype.validate = function(){
- return ((null != id() && (null != type())));
-};
-
-
-
-/**
- * @author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- * This class represents PublisherPublicKeyDigest Objects
- */
-var PublisherPublicKeyDigest = function PublisherPublicKeyDigest(_pkd){
-
- //this.PUBLISHER_ID_LEN = 256/8;
- this.PUBLISHER_ID_LEN = 512/8;
-
-
- this.publisherPublicKeyDigest = _pkd;
- //if( typeof _pkd == "object") this.publisherPublicKeyDigest = _pkd; // Byte Array
- //else if( typeof _pkd == "PublicKey") ;//TODO...
-
-};
-
-PublisherPublicKeyDigest.prototype.from_ndnb = function( decoder) {
-
- this.publisherPublicKeyDigest = decoder.readBinaryElement(this.getElementLabel());
-
- if(LOG>4)console.log('Publisher public key digest is ' + this.publisherPublicKeyDigest);
-
- if (null == this.publisherPublicKeyDigest) {
- throw new Error("Cannot parse publisher key digest.");
- }
-
- //TODO check if the length of the PublisherPublicKeyDigest is correct ( Security reason)
-
- if (this.publisherPublicKeyDigest.length != this.PUBLISHER_ID_LEN) {
- if (LOG > 0)
- console.log('LENGTH OF PUBLISHER ID IS WRONG! Expected ' + this.PUBLISHER_ID_LEN + ", got " + this.publisherPublicKeyDigest.length);
-
- //this.publisherPublicKeyDigest = new PublisherPublicKeyDigest(this.PublisherPublicKeyDigest).PublisherKeyDigest;
- }
- };
-
-PublisherPublicKeyDigest.prototype.to_ndnb= function( encoder) {
- //TODO Check that the ByteArray for the key is present
- if (!this.validate()) {
- throw new Error("Cannot encode : field values missing.");
- }
- if(LOG>3) console.log('PUBLISHER KEY DIGEST IS'+this.publisherPublicKeyDigest);
- encoder.writeElement(this.getElementLabel(), this.publisherPublicKeyDigest);
-};
-
-PublisherPublicKeyDigest.prototype.getElementLabel = function() { return NDNProtocolDTags.PublisherPublicKeyDigest; };
-
-PublisherPublicKeyDigest.prototype.validate =function() {
- return (null != this.publisherPublicKeyDigest);
-};
-/**
- * @author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- * This class represents Face Instances
- */
-
-var NetworkProtocol = { TCP:6, UDP:17};
-
-var FaceInstance = function FaceInstance(
- _action,
- _publisherPublicKeyDigest,
- _faceID,
- _ipProto,
- _host,
- _port,
- _multicastInterface,
- _multicastTTL,
- _freshnessSeconds){
-
-
- this.action = _action;
- this.publisherPublicKeyDigest = _publisherPublicKeyDigest;
- this.faceID = _faceID;
- this.ipProto = _ipProto;
- this.host = _host;
- this.Port = _port;
- this.multicastInterface =_multicastInterface;
- this.multicastTTL =_multicastTTL;
- this.freshnessSeconds = _freshnessSeconds;
-
- //action ::= ("newface" | "destroyface" | "queryface")
- //publisherPublicKeyDigest ::= SHA-256 digest
- //faceID ::= nonNegativeInteger
- //ipProto ::= nonNegativeInteger [IANA protocol number, 6=TCP, 17=UDP]
- //Host ::= textual representation of numeric IPv4 or IPv6 address
- //Port ::= nonNegativeInteger [1..65535]
- //MulticastInterface ::= textual representation of numeric IPv4 or IPv6 address
- //MulticastTTL ::= nonNegativeInteger [1..255]
- //freshnessSeconds ::= nonNegativeInteger
-
-};
-
-/**
- * Used by NetworkObject to decode the object from a network stream.
- */
-FaceInstance.prototype.from_ndnb = function(//XMLDecoder
- decoder) {
-
- decoder.readStartElement(this.getElementLabel());
-
- if (decoder.peekStartElement(NDNProtocolDTags.Action)) {
-
- this.action = decoder.readUTF8Element(NDNProtocolDTags.Action);
-
- }
- if (decoder.peekStartElement(NDNProtocolDTags.PublisherPublicKeyDigest)) {
-
- this.publisherPublicKeyDigest = new PublisherPublicKeyDigest();
- this.publisherPublicKeyDigest.from_ndnb(decoder);
-
- }
- if (decoder.peekStartElement(NDNProtocolDTags.FaceID)) {
-
- this.faceID = decoder.readIntegerElement(NDNProtocolDTags.FaceID);
-
- }
- if (decoder.peekStartElement(NDNProtocolDTags.IPProto)) {
-
- //int
- var pI = decoder.readIntegerElement(NDNProtocolDTags.IPProto);
-
- this.ipProto = null;
-
- if (NetworkProtocol.TCP == pI) {
-
- this.ipProto = NetworkProtocol.TCP;
-
- } else if (NetworkProtocol.UDP == pI) {
-
- this.ipProto = NetworkProtocol.UDP;
-
- } else {
-
- throw new Error("FaceInstance.decoder. Invalid " +
- NDNProtocolDTags.tagToString(NDNProtocolDTags.IPProto) + " field: " + pI);
-
- }
- }
-
- if (decoder.peekStartElement(NDNProtocolDTags.Host)) {
-
- this.host = decoder.readUTF8Element(NDNProtocolDTags.Host);
-
- }
-
- if (decoder.peekStartElement(NDNProtocolDTags.Port)) {
- this.Port = decoder.readIntegerElement(NDNProtocolDTags.Port);
- }
-
- if (decoder.peekStartElement(NDNProtocolDTags.MulticastInterface)) {
- this.multicastInterface = decoder.readUTF8Element(NDNProtocolDTags.MulticastInterface);
- }
-
- if (decoder.peekStartElement(NDNProtocolDTags.MulticastTTL)) {
- this.multicastTTL = decoder.readIntegerElement(NDNProtocolDTags.MulticastTTL);
- }
-
- if (decoder.peekStartElement(NDNProtocolDTags.FreshnessSeconds)) {
- this.freshnessSeconds = decoder.readIntegerElement(NDNProtocolDTags.FreshnessSeconds);
- }
- decoder.readEndElement();
-}
-
-/**
- * Used by NetworkObject to encode the object to a network stream.
- */
-FaceInstance.prototype.to_ndnb = function(//XMLEncoder
- encoder){
-
- //if (!this.validate()) {
- //throw new Error("Cannot encode : field values missing.");
- //throw new Error("")
- //}
- encoder.writeStartElement(this.getElementLabel());
-
- if (null != this.action && this.action.length != 0)
- encoder.writeElement(NDNProtocolDTags.Action, this.action);
-
- if (null != this.publisherPublicKeyDigest) {
- this.publisherPublicKeyDigest.to_ndnb(encoder);
- }
- if (null != this.faceID) {
- encoder.writeElement(NDNProtocolDTags.FaceID, this.faceID);
- }
- if (null != this.ipProto) {
- //encoder.writeElement(NDNProtocolDTags.IPProto, this.IpProto.value());
- encoder.writeElement(NDNProtocolDTags.IPProto, this.ipProto);
- }
- if (null != this.host && this.host.length != 0) {
- encoder.writeElement(NDNProtocolDTags.Host, this.host);
- }
- if (null != this.Port) {
- encoder.writeElement(NDNProtocolDTags.Port, this.Port);
- }
- if (null != this.multicastInterface && this.multicastInterface.length != 0) {
- encoder.writeElement(NDNProtocolDTags.MulticastInterface, this.multicastInterface);
- }
- if (null != this.multicastTTL) {
- encoder.writeElement(NDNProtocolDTags.MulticastTTL, this.multicastTTL);
- }
- if (null != this.freshnessSeconds) {
- encoder.writeElement(NDNProtocolDTags.FreshnessSeconds, this.freshnessSeconds);
- }
- encoder.writeEndElement();
-}
-
-
-FaceInstance.prototype.getElementLabel= function(){return NDNProtocolDTags.FaceInstance;};
-
-/**
- * @author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- * This class represents Forwarding Entries
- */
-
-var ForwardingEntry = function ForwardingEntry(
- //ActionType
- _action,
- //Name
- _prefixName,
- //PublisherPublicKeyDigest
- _ndndId,
- //Integer
- _faceID,
- //Integer
- _flags,
- //Integer
- _lifetime){
-
-
-
- //String
- this.action = _action;
- //Name\
- this.prefixName = _prefixName;
- //PublisherPublicKeyDigest
- this.ndndID = _ndndId;
- //Integer
- this.faceID = _faceID;
- //Integer
- this.flags = _flags;
- //Integer
- this.lifetime = _lifetime; // in seconds
-
-};
-
-ForwardingEntry.prototype.from_ndnb =function(
- //XMLDecoder
- decoder)
- //throws ContentDecodingException
- {
- decoder.readStartElement(this.getElementLabel());
- if (decoder.peekStartElement(NDNProtocolDTags.Action)) {
- this.action = decoder.readUTF8Element(NDNProtocolDTags.Action);
- }
- if (decoder.peekStartElement(NDNProtocolDTags.Name)) {
- this.prefixName = new Name();
- this.prefixName.from_ndnb(decoder) ;
- }
- if (decoder.peekStartElement(NDNProtocolDTags.PublisherPublicKeyDigest)) {
- this.NdndId = new PublisherPublicKeyDigest();
- this.NdndId.from_ndnb(decoder);
- }
- if (decoder.peekStartElement(NDNProtocolDTags.FaceID)) {
- this.faceID = decoder.readIntegerElement(NDNProtocolDTags.FaceID);
- }
- if (decoder.peekStartElement(NDNProtocolDTags.ForwardingFlags)) {
- this.flags = decoder.readIntegerElement(NDNProtocolDTags.ForwardingFlags);
- }
- if (decoder.peekStartElement(NDNProtocolDTags.FreshnessSeconds)) {
- this.lifetime = decoder.readIntegerElement(NDNProtocolDTags.FreshnessSeconds);
- }
- decoder.readEndElement();
- };
-
- /**
- * Used by NetworkObject to encode the object to a network stream.
- */
-ForwardingEntry.prototype.to_ndnb =function(
- //XMLEncoder
-encoder)
-{
-
-
- //if (!validate()) {
- //throw new ContentEncodingException("Cannot encode " + this.getClass().getName() + ": field values missing.");
- //}
- encoder.writeStartElement(this.getElementLabel());
- if (null != this.action && this.action.length != 0)
- encoder.writeElement(NDNProtocolDTags.Action, this.action);
- if (null != this.prefixName) {
- this.prefixName.to_ndnb(encoder);
- }
- if (null != this.NdndId) {
- this.NdndId.to_ndnb(encoder);
- }
- if (null != this.faceID) {
- encoder.writeElement(NDNProtocolDTags.FaceID, this.faceID);
- }
- if (null != this.flags) {
- encoder.writeElement(NDNProtocolDTags.ForwardingFlags, this.flags);
- }
- if (null != this.lifetime) {
- encoder.writeElement(NDNProtocolDTags.FreshnessSeconds, this.lifetime);
- }
- encoder.writeEndElement();
- };
-
-ForwardingEntry.prototype.getElementLabel = function() { return NDNProtocolDTags.ForwardingEntry; }
-/**
- * @author: Jeff Thompson
- * See COPYING for copyright and distribution information.
- * Encapsulate an Uint8Array and support dynamic reallocation.
- */
-
-/*
- * Create a DynamicUint8Array where this.array is a Uint8Array of size length.
- * If length is not supplied, use a default initial length.
- * The methods will update this.length.
- * To access the array, use this.array or call subarray.
- */
-var DynamicUint8Array = function DynamicUint8Array(length) {
- if (!length)
- length = 16;
-
- this.array = new Uint8Array(length);
- this.length = length;
-};
-
-/*
- * Ensure that this.array has the length, reallocate and copy if necessary.
- * Update this.length which may be greater than length.
- */
-DynamicUint8Array.prototype.ensureLength = function(length) {
- if (this.array.length >= length)
- return;
-
- // See if double is enough.
- var newLength = this.array.length * 2;
- if (length > newLength)
- // The needed length is much greater, so use it.
- newLength = length;
-
- var newArray = new Uint8Array(newLength);
- newArray.set(this.array);
- this.array = newArray;
- this.length = newLength;
-};
-
-/*
- * Call this.array.set(value, offset), reallocating if necessary.
- */
-DynamicUint8Array.prototype.set = function(value, offset) {
- this.ensureLength(value.length + offset);
- this.array.set(value, offset);
-};
-
-/*
- * Return this.array.subarray(begin, end);
- */
-DynamicUint8Array.prototype.subarray = function(begin, end) {
- return this.array.subarray(begin, end);
-}
-/**
- * This class is used to encode ndnb binary elements (blob, type/value pairs).
- *
- * @author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- */
-
-var XML_EXT = 0x00;
-
-var XML_TAG = 0x01;
-
-var XML_DTAG = 0x02;
-
-var XML_ATTR = 0x03;
-
-var XML_DATTR = 0x04;
-
-var XML_BLOB = 0x05;
-
-var XML_UDATA = 0x06;
-
-var XML_CLOSE = 0x0;
-
-var XML_SUBTYPE_PROCESSING_INSTRUCTIONS = 16;
-
-
-var XML_TT_BITS = 3;
-var XML_TT_MASK = ((1 << XML_TT_BITS) - 1);
-var XML_TT_VAL_BITS = XML_TT_BITS + 1;
-var XML_TT_VAL_MASK = ((1 << (XML_TT_VAL_BITS)) - 1);
-var XML_REG_VAL_BITS = 7;
-var XML_REG_VAL_MASK = ((1 << XML_REG_VAL_BITS) - 1);
-var XML_TT_NO_MORE = (1 << XML_REG_VAL_BITS); // 0x80
-var BYTE_MASK = 0xFF;
-var LONG_BYTES = 8;
-var LONG_BITS = 64;
-
-var bits_11 = 0x0000007FF;
-var bits_18 = 0x00003FFFF;
-var bits_32 = 0x0FFFFFFFF;
-
-
-var BinaryXMLEncoder = function BinaryXMLEncoder(){
- this.ostream = new DynamicUint8Array(100);
- this.offset =0;
- this.CODEC_NAME = "Binary";
-};
-
-/*
- * Encode utf8Content as utf8.
- */
-BinaryXMLEncoder.prototype.writeUString = function(/*String*/ utf8Content) {
- this.encodeUString(utf8Content, XML_UDATA);
-};
-
-
-BinaryXMLEncoder.prototype.writeBlob = function(
- /*Uint8Array*/ binaryContent
- ) {
-
- if(LOG >3) console.log(binaryContent);
-
- this.encodeBlob(binaryContent, binaryContent.length);
-};
-
-
-BinaryXMLEncoder.prototype.writeStartElement = function(
- /*String*/ tag,
- /*TreeMap<String,String>*/ attributes
- ) {
-
- /*Long*/ var dictionaryVal = tag; //stringToTag(tag);
-
- if (null == dictionaryVal) {
- this.encodeUString(tag, XML_TAG);
- } else {
- this.encodeTypeAndVal(XML_DTAG, dictionaryVal);
- }
-
- if (null != attributes) {
- this.writeAttributes(attributes);
- }
-};
-
-
-BinaryXMLEncoder.prototype.writeEndElement = function() {
- this.ostream.ensureLength(this.offset + 1);
- this.ostream.array[this.offset] = XML_CLOSE;
- this.offset += 1;
-}
-
-
-BinaryXMLEncoder.prototype.writeAttributes = function(/*TreeMap<String,String>*/ attributes) {
- if (null == attributes) {
- return;
- }
-
- // the keySet of a TreeMap is sorted.
-
- for(var i=0; i<attributes.length;i++){
- var strAttr = attributes[i].k;
- var strValue = attributes[i].v;
-
- var dictionaryAttr = stringToTag(strAttr);
- if (null == dictionaryAttr) {
- // not in dictionary, encode as attr
- // compressed format wants length of tag represented as length-1
- // to save that extra bit, as tag cannot be 0 length.
- // encodeUString knows to do that.
- this.encodeUString(strAttr, XML_ATTR);
- } else {
- this.encodeTypeAndVal(XML_DATTR, dictionaryAttr);
- }
- // Write value
- this.encodeUString(strValue);
-
- }
-}
-
-
-//returns a string
-stringToTag = function(/*long*/ tagVal) {
- if ((tagVal >= 0) && (tagVal < NDNProtocolDTagsStrings.length)) {
- return NDNProtocolDTagsStrings[tagVal];
- } else if (tagVal == NDNProtocolDTags.NDNProtocolDataUnit) {
- return NDNProtocolDTags.NDNPROTOCOL_DATA_UNIT;
- }
- return null;
-};
-
-//returns a Long
-tagToString = function(/*String*/ tagName) {
- // the slow way, but right now we don't care.... want a static lookup for the forward direction
- for (var i=0; i < NDNProtocolDTagsStrings.length; ++i) {
- if ((null != NDNProtocolDTagsStrings[i]) && (NDNProtocolDTagsStrings[i] == tagName)) {
- return i;
- }
- }
- if (NDNProtocolDTags.NDNPROTOCOL_DATA_UNIT == tagName) {
- return NDNProtocolDTags.NDNProtocolDataUnit;
- }
- return null;
-};
-
-/*
- * If Content is a string, then encode as utf8 and write UDATA.
- */
-BinaryXMLEncoder.prototype.writeElement = function(
- //long
- tag,
- //byte[]
- Content,
- //TreeMap<String, String>
- attributes
- ) {
- this.writeStartElement(tag, attributes);
- // Will omit if 0-length
-
- if(typeof Content === 'number') {
- if(LOG>4) console.log('GOING TO WRITE THE NUMBER .charCodeAt(0) ' + Content.toString().charCodeAt(0) );
- if(LOG>4) console.log('GOING TO WRITE THE NUMBER ' + Content.toString() );
- if(LOG>4) console.log('type of number is ' + typeof Content.toString() );
-
- this.writeUString(Content.toString());
- //whatever
- }
- else if(typeof Content === 'string'){
- if(LOG>4) console.log('GOING TO WRITE THE STRING ' + Content );
- if(LOG>4) console.log('type of STRING is ' + typeof Content );
-
- this.writeUString(Content);
- }
- else{
- if(LOG>4) console.log('GOING TO WRITE A BLOB ' + Content );
-
- this.writeBlob(Content);
- }
-
- this.writeEndElement();
-}
-
-
-
-var TypeAndVal = function TypeAndVal(_type,_val) {
- this.type = _type;
- this.val = _val;
-
-};
-
-
-BinaryXMLEncoder.prototype.encodeTypeAndVal = function(
- //int
- type,
- //long
- val
- ) {
-
- if(LOG>4) console.log('Encoding type '+ type+ ' and value '+ val);
-
- if(LOG>4) console.log('OFFSET IS ' + this.offset);
-
- if ((type > XML_UDATA) || (type < 0) || (val < 0)) {
- throw new Error("Tag and value must be positive, and tag valid.");
- }
-
- // Encode backwards. Calculate how many bytes we need:
- var numEncodingBytes = this.numEncodingBytes(val);
- this.ostream.ensureLength(this.offset + numEncodingBytes);
-
- // Bottom 4 bits of val go in last byte with tag.
- this.ostream.array[this.offset + numEncodingBytes - 1] =
- //(byte)
- (BYTE_MASK &
- (((XML_TT_MASK & type) |
- ((XML_TT_VAL_MASK & val) << XML_TT_BITS))) |
- XML_TT_NO_MORE); // set top bit for last byte
- val = val >>> XML_TT_VAL_BITS;
-
- // Rest of val goes into preceding bytes, 7 bits per byte, top bit
- // is "more" flag.
- var i = this.offset + numEncodingBytes - 2;
- while ((0 != val) && (i >= this.offset)) {
- this.ostream.array[i] = //(byte)
- (BYTE_MASK & (val & XML_REG_VAL_MASK)); // leave top bit unset
- val = val >>> XML_REG_VAL_BITS;
- --i;
- }
- if (val != 0) {
- throw new Error( "This should not happen: miscalculated encoding");
- //Log.warning(Log.FAC_ENCODING, "This should not happen: miscalculated encoding length, have " + val + " left.");
- }
- this.offset+= numEncodingBytes;
-
- return numEncodingBytes;
-};
-
-/*
- * Encode ustring as utf8.
- */
-BinaryXMLEncoder.prototype.encodeUString = function(
- //String
- ustring,
- //byte
- type) {
-
- if (null == ustring)
- return;
- if (type == XML_TAG || type == XML_ATTR && ustring.length == 0)
- return;
-
- if(LOG>3) console.log("The string to write is ");
- if(LOG>3) console.log(ustring);
-
- var strBytes = DataUtils.stringToUtf8Array(ustring);
-
- this.encodeTypeAndVal(type,
- (((type == XML_TAG) || (type == XML_ATTR)) ?
- (strBytes.length-1) :
- strBytes.length));
-
- if(LOG>3) console.log("THE string to write is ");
-
- if(LOG>3) console.log(strBytes);
-
- this.writeString(strBytes);
- this.offset+= strBytes.length;
-};
-
-
-
-BinaryXMLEncoder.prototype.encodeBlob = function(
- //Uint8Array
- blob,
- //int
- length) {
-
-
- if (null == blob)
- return;
-
- if(LOG>4) console.log('LENGTH OF XML_BLOB IS '+length);
-
- /*blobCopy = new Array(blob.Length);
-
- for (i = 0; i < blob.length; i++) //in InStr.ToCharArray())
- {
- blobCopy[i] = blob[i];
- }*/
-
- this.encodeTypeAndVal(XML_BLOB, length);
-
- this.writeBlobArray(blob);
- this.offset += length;
-};
-
-var ENCODING_LIMIT_1_BYTE = ((1 << (XML_TT_VAL_BITS)) - 1);
-var ENCODING_LIMIT_2_BYTES = ((1 << (XML_TT_VAL_BITS + XML_REG_VAL_BITS)) - 1);
-var ENCODING_LIMIT_3_BYTES = ((1 << (XML_TT_VAL_BITS + 2 * XML_REG_VAL_BITS)) - 1);
-
-BinaryXMLEncoder.prototype.numEncodingBytes = function(
- //long
- x) {
- if (x <= ENCODING_LIMIT_1_BYTE) return (1);
- if (x <= ENCODING_LIMIT_2_BYTES) return (2);
- if (x <= ENCODING_LIMIT_3_BYTES) return (3);
-
- var numbytes = 1;
-
- // Last byte gives you XML_TT_VAL_BITS
- // Remainder each give you XML_REG_VAL_BITS
- x = x >>> XML_TT_VAL_BITS;
- while (x != 0) {
- numbytes++;
- x = x >>> XML_REG_VAL_BITS;
- }
- return (numbytes);
-};
-
-BinaryXMLEncoder.prototype.writeDateTime = function(
- //String
- tag,
- //NDNTime
- dateTime) {
-
- if(LOG>4)console.log('ENCODING DATE with LONG VALUE');
- if(LOG>4)console.log(dateTime.msec);
-
- //var binarydate = DataUtils.unsignedLongToByteArray( Math.round((dateTime.msec/1000) * 4096) );
-
-
- //parse to hex
- var binarydate = Math.round((dateTime.msec/1000) * 4096).toString(16) ;
-
- //HACK
- var binarydate = DataUtils.toNumbers( '0'.concat(binarydate,'0')) ;
-
-
- if(LOG>4)console.log('ENCODING DATE with BINARY VALUE');
- if(LOG>4)console.log(binarydate);
- if(LOG>4)console.log('ENCODING DATE with BINARY VALUE(HEX)');
- if(LOG>4)console.log(DataUtils.toHex(binarydate));
-
- this.writeElement(tag, binarydate);
-};
-
-// This does not update this.offset.
-BinaryXMLEncoder.prototype.writeString = function(input) {
-
- if(typeof input === 'string'){
- //console.log('went here');
- if(LOG>4) console.log('GOING TO WRITE A STRING');
- if(LOG>4) console.log(input);
-
- this.ostream.ensureLength(this.offset + input.length);
- for (var i = 0; i < input.length; i++) {
- if(LOG>4) console.log('input.charCodeAt(i)=' + input.charCodeAt(i));
- this.ostream.array[this.offset + i] = (input.charCodeAt(i));
- }
- }
- else{
- if(LOG>4) console.log('GOING TO WRITE A STRING IN BINARY FORM');
- if(LOG>4) console.log(input);
-
- this.writeBlobArray(input);
- }
- /*
- else if(typeof input === 'object'){
-
- }
- */
-};
-
-
-BinaryXMLEncoder.prototype.writeBlobArray = function(
- //Uint8Array
- blob) {
-
- if(LOG>4) console.log('GOING TO WRITE A BLOB');
-
- this.ostream.set(blob, this.offset);
-};
-
-
-BinaryXMLEncoder.prototype.getReducedOstream = function() {
- return this.ostream.subarray(0, this.offset);
-};
-
-/**
- * This class is used to decode ndnb binary elements (blob, type/value pairs).
- *
- * @author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- */
-
-var XML_EXT = 0x00;
-
-var XML_TAG = 0x01;
-
-var XML_DTAG = 0x02;
-
-var XML_ATTR = 0x03;
-
-var XML_DATTR = 0x04;
-
-var XML_BLOB = 0x05;
-
-var XML_UDATA = 0x06;
-
-var XML_CLOSE = 0x0;
-
-var XML_SUBTYPE_PROCESSING_INSTRUCTIONS = 16;
-
-
-var XML_TT_BITS = 3;
-var XML_TT_MASK = ((1 << XML_TT_BITS) - 1);
-var XML_TT_VAL_BITS = XML_TT_BITS + 1;
-var XML_TT_VAL_MASK = ((1 << (XML_TT_VAL_BITS)) - 1);
-var XML_REG_VAL_BITS = 7;
-var XML_REG_VAL_MASK = ((1 << XML_REG_VAL_BITS) - 1);
-var XML_TT_NO_MORE = (1 << XML_REG_VAL_BITS); // 0x80
-var BYTE_MASK = 0xFF;
-var LONG_BYTES = 8;
-var LONG_BITS = 64;
-
-var bits_11 = 0x0000007FF;
-var bits_18 = 0x00003FFFF;
-var bits_32 = 0x0FFFFFFFF;
-
-
-
-//returns a string
-tagToString = function(/*long*/ tagVal) {
- if ((tagVal >= 0) && (tagVal < NDNProtocolDTagsStrings.length)) {
- return NDNProtocolDTagsStrings[tagVal];
- } else if (tagVal == NDNProtocolDTags.NDNProtocolDataUnit) {
- return NDNProtocolDTags.NDNPROTOCOL_DATA_UNIT;
- }
- return null;
-};
-
-//returns a Long
-stringToTag = function(/*String*/ tagName) {
- // the slow way, but right now we don't care.... want a static lookup for the forward direction
- for (var i=0; i < NDNProtocolDTagsStrings.length; ++i) {
- if ((null != NDNProtocolDTagsStrings[i]) && (NDNProtocolDTagsStrings[i] == tagName)) {
- return i;
- }
- }
- if (NDNProtocolDTags.NDNPROTOCOL_DATA_UNIT == tagName) {
- return NDNProtocolDTags.NDNProtocolDataUnit;
- }
- return null;
-};
-
-//console.log(stringToTag(64));
-var BinaryXMLDecoder = function BinaryXMLDecoder(istream){
- var MARK_LEN=512;
- var DEBUG_MAX_LEN = 32768;
-
- this.istream = istream;
- this.offset = 0;
-};
-
-BinaryXMLDecoder.prototype.initializeDecoding = function() {
- //if (!this.istream.markSupported()) {
- //throw new IllegalArgumentException(this.getClass().getName() + ": input stream must support marking!");
- //}
-}
-
-BinaryXMLDecoder.prototype.readStartDocument = function(){
- // Currently no start document in binary encoding.
- }
-
-BinaryXMLDecoder.prototype.readEndDocument = function() {
- // Currently no end document in binary encoding.
- };
-
-BinaryXMLDecoder.prototype.readStartElement = function(
- //String
- startTag,
- //TreeMap<String, String>
- attributes) {
-
-
- //NOT SURE
- //if(typeof startTag == 'number')
- //startTag = tagToString(startTag);
-
- //TypeAndVal
- var tv = this.decodeTypeAndVal();
-
- if (null == tv) {
- throw new ContentDecodingException(new Error("Expected start element: " + startTag + " got something not a tag."));
- }
-
- //String
- var decodedTag = null;
- //console.log(tv);
- //console.log(typeof tv);
-
- //console.log(XML_TAG);
- if (tv.type() == XML_TAG) {
- //console.log('got here');
- //Log.info(Log.FAC_ENCODING, "Unexpected: got tag in readStartElement; looking for tag " + startTag + " got length: " + (int)tv.val()+1);
- // Tag value represents length-1 as tags can never be empty.
- var valval ;
- if(typeof tv.val() == 'string'){
- valval = (parseInt(tv.val())) + 1;
- }
- else
- valval = (tv.val())+ 1;
-
- //console.log('valval is ' +valval);
-
- decodedTag = this.decodeUString(valval);
-
- } else if (tv.type() == XML_DTAG) {
- //console.log('gothere');
- //console.log(tv.val());
- //decodedTag = tagToString(tv.val());
- //console.log()
- decodedTag = tv.val();
- }
-
- //console.log(decodedTag);
- //console.log('startTag is '+startTag);
-
-
- if ((null == decodedTag) || decodedTag != startTag ) {
- console.log('expecting '+ startTag + ' but got '+ decodedTag);
- throw new ContentDecodingException(new Error("Expected start element: " + startTag + " got: " + decodedTag + "(" + tv.val() + ")"));
- }
-
- // DKS: does not read attributes out of stream if caller doesn't
- // ask for them. Should possibly peek and skip over them regardless.
- // TODO: fix this
- if (null != attributes) {
- readAttributes(attributes);
- }
- }
-
-
-BinaryXMLDecoder.prototype.readAttributes = function(
- // array of [attributeName, attributeValue]
- attributes) {
-
- if (null == attributes) {
- return;
- }
-
- try {
- // Now need to get attributes.
- //TypeAndVal
- var nextTV = this.peekTypeAndVal();
-
- while ((null != nextTV) && ((XML_ATTR == nextTV.type()) ||
- (XML_DATTR == nextTV.type()))) {
-
- // Decode this attribute. First, really read the type and value.
- //this.TypeAndVal
- var thisTV = this.decodeTypeAndVal();
-
- //String
- var attributeName = null;
- if (XML_ATTR == thisTV.type()) {
- // Tag value represents length-1 as attribute names cannot be empty.
- var valval ;
- if(typeof thisTV.val() == 'string'){
- valval = (parseInt(thisTV.val())) + 1;
- }
- else
- valval = (thisTV.val())+ 1;
-
- attributeName = this.decodeUString(valval);
-
- } else if (XML_DATTR == thisTV.type()) {
- // DKS TODO are attributes same or different dictionary?
- attributeName = tagToString(thisTV.val());
- if (null == attributeName) {
- throw new ContentDecodingException(new Error("Unknown DATTR value" + thisTV.val()));
- }
- }
- // Attribute values are always UDATA
- //String
- var attributeValue = this.decodeUString();
-
- //
- attributes.push([attributeName, attributeValue]);
-
- nextTV = this.peekTypeAndVal();
- }
- } catch ( e) {
- throw new ContentDecodingException(new Error("readStartElement", e));
- }
-};
-
-//returns a string
-BinaryXMLDecoder.prototype.peekStartElementAsString = function() {
- //this.istream.mark(MARK_LEN);
-
- //String
- var decodedTag = null;
- var previousOffset = this.offset;
- try {
- // Have to distinguish genuine errors from wrong tags. Could either use
- // a special exception subtype, or redo the work here.
- //this.TypeAndVal
- var tv = this.decodeTypeAndVal();
-
- if (null != tv) {
-
- if (tv.type() == XML_TAG) {
- /*if (tv.val()+1 > DEBUG_MAX_LEN) {
- throw new ContentDecodingException(new Error("Decoding error: length " + tv.val()+1 + " longer than expected maximum length!")(;
- }*/
-
- // Tag value represents length-1 as tags can never be empty.
- var valval ;
- if(typeof tv.val() == 'string'){
- valval = (parseInt(tv.val())) + 1;
- }
- else
- valval = (tv.val())+ 1;
-
- decodedTag = this.decodeUString(valval);
-
- //Log.info(Log.FAC_ENCODING, "Unexpected: got text tag in peekStartElement; length: " + valval + " decoded tag = " + decodedTag);
-
- } else if (tv.type() == XML_DTAG) {
- decodedTag = tagToString(tv.val());
- }
-
- } // else, not a type and val, probably an end element. rewind and return false.
-
- } catch ( e) {
-
- } finally {
- try {
- this.offset = previousOffset;
- } catch ( e) {
- Log.logStackTrace(Log.FAC_ENCODING, Level.WARNING, e);
- throw new ContentDecodingException(new Error("Cannot reset stream! " + e.getMessage(), e));
- }
- }
- return decodedTag;
-};
-
-BinaryXMLDecoder.prototype.peekStartElement = function(
- //String
- startTag) {
- //String
- if(typeof startTag == 'string'){
- var decodedTag = this.peekStartElementAsString();
-
- if ((null != decodedTag) && decodedTag == startTag) {
- return true;
- }
- return false;
- }
- else if(typeof startTag == 'number'){
- var decodedTag = this.peekStartElementAsLong();
- if ((null != decodedTag) && decodedTag == startTag) {
- return true;
- }
- return false;
- }
- else{
- throw new ContentDecodingException(new Error("SHOULD BE STRING OR NUMBER"));
- }
-}
-//returns Long
-BinaryXMLDecoder.prototype.peekStartElementAsLong = function() {
- //this.istream.mark(MARK_LEN);
-
- //Long
- var decodedTag = null;
-
- var previousOffset = this.offset;
-
- try {
- // Have to distinguish genuine errors from wrong tags. Could either use
- // a special exception subtype, or redo the work here.
- //this.TypeAndVal
- var tv = this.decodeTypeAndVal();
-
- if (null != tv) {
-
- if (tv.type() == XML_TAG) {
- if (tv.val()+1 > DEBUG_MAX_LEN) {
- throw new ContentDecodingException(new Error("Decoding error: length " + tv.val()+1 + " longer than expected maximum length!"));
- }
-
- var valval ;
- if(typeof tv.val() == 'string'){
- valval = (parseInt(tv.val())) + 1;
- }
- else
- valval = (tv.val())+ 1;
-
- // Tag value represents length-1 as tags can never be empty.
- //String
- var strTag = this.decodeUString(valval);
-
- decodedTag = stringToTag(strTag);
-
- //Log.info(Log.FAC_ENCODING, "Unexpected: got text tag in peekStartElement; length: " + valval + " decoded tag = " + decodedTag);
-
- } else if (tv.type() == XML_DTAG) {
- decodedTag = tv.val();
- }
-
- } // else, not a type and val, probably an end element. rewind and return false.
-
- } catch ( e) {
-
- } finally {
- try {
- //this.istream.reset();
- this.offset = previousOffset;
- } catch ( e) {
- Log.logStackTrace(Log.FAC_ENCODING, Level.WARNING, e);
- throw new Error("Cannot reset stream! " + e.getMessage(), e);
- }
- }
- return decodedTag;
- };
-
-
-// returns a byte[]
-BinaryXMLDecoder.prototype.readBinaryElement = function(
- //long
- startTag,
- //TreeMap<String, String>
- attributes){
- //byte []
- var blob = null;
-
- this.readStartElement(startTag, attributes);
- blob = this.readBlob();
-
- return blob;
-};
-
-
-BinaryXMLDecoder.prototype.readEndElement = function(){
- if(LOG>4)console.log('this.offset is '+this.offset);
-
- var next = this.istream[this.offset];
-
- this.offset++;
- //read();
-
- if(LOG>4)console.log('XML_CLOSE IS '+XML_CLOSE);
- if(LOG>4)console.log('next is '+next);
-
- if (next != XML_CLOSE) {
- console.log("Expected end element, got: " + next);
- throw new ContentDecodingException(new Error("Expected end element, got: " + next));
- }
- };
-
-
-//String
-BinaryXMLDecoder.prototype.readUString = function(){
- //String
- var ustring = this.decodeUString();
- this.readEndElement();
- return ustring;
-
- };
-
-
-//returns a uint8array
-BinaryXMLDecoder.prototype.readBlob = function() {
- //uint8array
-
- var blob = this.decodeBlob();
- this.readEndElement();
- return blob;
-
- };
-
-
-//NDNTime
-BinaryXMLDecoder.prototype.readDateTime = function(
- //long
- startTag) {
- //byte []
-
- var byteTimestamp = this.readBinaryElement(startTag);
-
- //var lontimestamp = DataUtils.byteArrayToUnsignedLong(byteTimestamp);
-
- byteTimestamp = DataUtils.toHex(byteTimestamp);
-
-
- byteTimestamp = parseInt(byteTimestamp, 16);
-
- var lontimestamp = (byteTimestamp/ 4096) * 1000;
-
- //if(lontimestamp<0) lontimestamp = - lontimestamp;
-
- if(LOG>3) console.log('DECODED DATE WITH VALUE');
- if(LOG>3) console.log(lontimestamp);
-
-
- //NDNTime
- var timestamp = new NDNTime(lontimestamp);
- //timestamp.setDateBinary(byteTimestamp);
-
- if (null == timestamp) {
- throw new ContentDecodingException(new Error("Cannot parse timestamp: " + DataUtils.printHexBytes(byteTimestamp)));
- }
- return timestamp;
-};
-
-BinaryXMLDecoder.prototype.decodeTypeAndVal = function() {
-
- /*int*/var type = -1;
- /*long*/var val = 0;
- /*boolean*/var more = true;
-
- do {
-
- var next = this.istream[this.offset ];
-
-
- if (next < 0) {
- return null;
- }
-
- if ((0 == next) && (0 == val)) {
- return null;
- }
-
- more = (0 == (next & XML_TT_NO_MORE));
-
- if (more) {
- val = val << XML_REG_VAL_BITS;
- val |= (next & XML_REG_VAL_MASK);
- } else {
-
- type = next & XML_TT_MASK;
- val = val << XML_TT_VAL_BITS;
- val |= ((next >>> XML_TT_BITS) & XML_TT_VAL_MASK);
- }
-
- this.offset++;
-
- } while (more);
-
- if(LOG>3)console.log('TYPE is '+ type + ' VAL is '+ val);
-
- return new TypeAndVal(type, val);
-};
-
-
-
-//TypeAndVal
-BinaryXMLDecoder.peekTypeAndVal = function() {
- //TypeAndVal
- var tv = null;
-
- //this.istream.mark(LONG_BYTES*2);
-
- var previousOffset = this.offset;
-
- try {
- tv = this.decodeTypeAndVal();
- } finally {
- //this.istream.reset();
- this.offset = previousOffset;
- }
-
- return tv;
-};
-
-
-//Uint8Array
-BinaryXMLDecoder.prototype.decodeBlob = function(
- //int
- blobLength) {
-
- if(null == blobLength){
- //TypeAndVal
- var tv = this.decodeTypeAndVal();
-
- var valval ;
-
- if(typeof tv.val() == 'string'){
- valval = (parseInt(tv.val()));
- }
- else
- valval = (tv.val());
-
- //console.log('valval here is ' + valval);
- return this.decodeBlob(valval);
- }
-
- //
- //Uint8Array
- var bytes = this.istream.subarray(this.offset, this.offset+ blobLength);
- this.offset += blobLength;
-
- return bytes;
-};
-
-//String
-BinaryXMLDecoder.prototype.decodeUString = function(
- //int
- byteLength) {
- if(null == byteLength ){
- var tempStreamPosition = this.offset;
-
- //TypeAndVal
- var tv = this.decodeTypeAndVal();
-
- if(LOG>3)console.log('TV is '+tv);
- if(LOG>3)console.log(tv);
-
- if(LOG>3)console.log('Type of TV is '+typeof tv);
-
- if ((null == tv) || (XML_UDATA != tv.type())) { // if we just have closers left, will get back null
- //if (Log.isLoggable(Log.FAC_ENCODING, Level.FINEST))
- //Log.finest(Log.FAC_ENCODING, "Expected UDATA, got " + ((null == tv) ? " not a tag " : tv.type()) + ", assuming elided 0-length blob.");
-
- this.offset = tempStreamPosition;
-
- return "";
- }
-
- return this.decodeUString(tv.val());
- }
- else{
- //uint8array
- var stringBytes = this.decodeBlob(byteLength);
-
- //return DataUtils.getUTF8StringFromBytes(stringBytes);
- return DataUtils.toString(stringBytes);
-
- }
-};
-
-//OBject containg a pair of type and value
-var TypeAndVal = function TypeAndVal(_type,_val) {
- this.t = _type;
- this.v = _val;
-};
-
-TypeAndVal.prototype.type = function(){
- return this.t;
-};
-
-TypeAndVal.prototype.val = function(){
- return this.v;
-};
-
-
-
-
-BinaryXMLDecoder.prototype.readIntegerElement =function(
- //String
- startTag) {
-
- //String
- if(LOG>4) console.log('READING INTEGER '+ startTag);
- if(LOG>4) console.log('TYPE OF '+ typeof startTag);
-
- var strVal = this.readUTF8Element(startTag);
-
- return parseInt(strVal);
-};
-
-
-BinaryXMLDecoder.prototype.readUTF8Element =function(
- //String
- startTag,
- //TreeMap<String, String>
- attributes) {
- //throws Error where name == "ContentDecodingException"
-
- this.readStartElement(startTag, attributes); // can't use getElementText, can't get attributes
- //String
- var strElementText = this.readUString();
- return strElementText;
-};
-
-
-/*
- * Set the offset into the input, used for the next read.
- */
-BinaryXMLDecoder.prototype.seek = function(
- //int
- offset) {
- this.offset = offset;
-}
-
-/*
- * Call with: throw new ContentDecodingException(new Error("message")).
- */
-function ContentDecodingException(error) {
- this.message = error.message;
- // Copy lineNumber, etc. from where new Error was called.
- for (var prop in error)
- this[prop] = error[prop];
-}
-ContentDecodingException.prototype = new Error();
-ContentDecodingException.prototype.name = "ContentDecodingException";
-
-/**
- * This class uses BinaryXMLDecoder to follow the structure of a ndnb binary element to
- * determine its end.
- *
- * @author: Jeff Thompson
- * See COPYING for copyright and distribution information.
- */
-
-var BinaryXMLStructureDecoder = function BinaryXMLDecoder() {
- this.gotElementEnd = false;
- this.offset = 0;
- this.level = 0;
- this.state = BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE;
- this.headerLength = 0;
- this.useHeaderBuffer = false;
- this.headerBuffer = new DynamicUint8Array(5);
- this.nBytesToRead = 0;
-};
-
-BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE = 0;
-BinaryXMLStructureDecoder.READ_BYTES = 1;
-
-/*
- * Continue scanning input starting from this.offset. If found the end of the element
- * which started at offset 0 then return true, else false.
- * If this returns false, you should read more into input and call again.
- * You have to pass in input each time because the array could be reallocated.
- * This throws an exception for badly formed ndnb.
- */
-BinaryXMLStructureDecoder.prototype.findElementEnd = function(
- // Uint8Array
- input)
-{
- if (this.gotElementEnd)
- // Someone is calling when we already got the end.
- return true;
-
- var decoder = new BinaryXMLDecoder(input);
-
- while (true) {
- if (this.offset >= input.length)
- // All the cases assume we have some input.
- return false;
-
- switch (this.state) {
- case BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE:
- // First check for XML_CLOSE.
- if (this.headerLength == 0 && input[this.offset] == XML_CLOSE) {
- ++this.offset;
- // Close the level.
- --this.level;
- if (this.level == 0)
- // Finished.
- return true;
- if (this.level < 0)
- throw new Error("BinaryXMLStructureDecoder: Unexepected close tag at offset " +
- (this.offset - 1));
-
- // Get ready for the next header.
- this.startHeader();
- break;
+ if (e.message && e['opera#sourceloc']) {
+ // e.message.indexOf("Backtrace:") > -1 -> opera9
+ // 'opera#sourceloc' in e -> opera9, opera10a
+ // !e.stacktrace -> opera9
+ if (!e.stacktrace) {
+ return 'opera9'; // use e.message
}
-
- var startingHeaderLength = this.headerLength;
- while (true) {
- if (this.offset >= input.length) {
- // We can't get all of the header bytes from this input. Save in headerBuffer.
- this.useHeaderBuffer = true;
- var nNewBytes = this.headerLength - startingHeaderLength;
- this.headerBuffer.set
- (input.subarray(this.offset - nNewBytes, nNewBytes), startingHeaderLength);
-
- return false;
- }
- var headerByte = input[this.offset++];
- ++this.headerLength;
- if (headerByte & XML_TT_NO_MORE)
- // Break and read the header.
- break;
+ if (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length) {
+ // e.message may have more stack entries than e.stacktrace
+ return 'opera9'; // use e.message
}
-
- var typeAndVal;
- if (this.useHeaderBuffer) {
- // Copy the remaining bytes into headerBuffer.
- nNewBytes = this.headerLength - startingHeaderLength;
- this.headerBuffer.set
- (input.subarray(this.offset - nNewBytes, nNewBytes), startingHeaderLength);
+ return 'opera10a'; // use e.stacktrace
+ }
- typeAndVal = new BinaryXMLDecoder(this.headerBuffer.array).decodeTypeAndVal();
+ if (e.message && e.stack && e.stacktrace) {
+ // e.stacktrace && e.stack -> opera10b
+ if (e.stacktrace.indexOf("called from line") < 0) {
+ return 'opera10b'; // use e.stacktrace, format differs from 'opera10a'
+ }
+ // e.stacktrace && e.stack -> opera11
+ return 'opera11'; // use e.stacktrace, format differs from 'opera10a', 'opera10b'
+ }
+
+ if (e.stack && !e.fileName) {
+ // Chrome 27 does not have e.arguments as earlier versions,
+ // but still does not have e.fileName as Firefox
+ return 'chrome';
+ }
+
+ return 'other';
+ },
+
+ /**
+ * Given a context, function name, and callback function, overwrite it so that it calls
+ * printStackTrace() first with a callback and then runs the rest of the body.
+ *
+ * @param {Object} context of execution (e.g. window)
+ * @param {String} functionName to instrument
+ * @param {Function} callback function to call with a stack trace on invocation
+ */
+ instrumentFunction: function(context, functionName, callback) {
+ context = context || window;
+ var original = context[functionName];
+ context[functionName] = function instrumented() {
+ callback.call(this, printStackTrace().slice(4));
+ return context[functionName]._instrumented.apply(this, arguments);
+ };
+ context[functionName]._instrumented = original;
+ },
+
+ /**
+ * Given a context and function name of a function that has been
+ * instrumented, revert the function to it's original (non-instrumented)
+ * state.
+ *
+ * @param {Object} context of execution (e.g. window)
+ * @param {String} functionName to de-instrument
+ */
+ deinstrumentFunction: function(context, functionName) {
+ if (context[functionName].constructor === Function &&
+ context[functionName]._instrumented &&
+ context[functionName]._instrumented.constructor === Function) {
+ context[functionName] = context[functionName]._instrumented;
+ }
+ },
+
+ /**
+ * Given an Error object, return a formatted Array based on Chrome's stack string.
+ *
+ * @param e - Error object to inspect
+ * @return Array<String> of function calls, files and line numbers
+ */
+ chrome: function(e) {
+ return (e.stack + '\n')
+ .replace(/^[\s\S]+?\s+at\s+/, ' at ') // remove message
+ .replace(/^\s+(at eval )?at\s+/gm, '') // remove 'at' and indentation
+ .replace(/^([^\(]+?)([\n$])/gm, '{anonymous}() ($1)$2')
+ .replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, '{anonymous}() ($1)')
+ .replace(/^(.+) \((.+)\)$/gm, '$1@$2')
+ .split('\n')
+ .slice(0, -1);
+ },
+
+ /**
+ * Given an Error object, return a formatted Array based on Safari's stack string.
+ *
+ * @param e - Error object to inspect
+ * @return Array<String> of function calls, files and line numbers
+ */
+ safari: function(e) {
+ return e.stack.replace(/\[native code\]\n/m, '')
+ .replace(/^(?=\w+Error\:).*$\n/m, '')
+ .replace(/^@/gm, '{anonymous}()@')
+ .split('\n');
+ },
+
+ /**
+ * Given an Error object, return a formatted Array based on IE's stack string.
+ *
+ * @param e - Error object to inspect
+ * @return Array<String> of function calls, files and line numbers
+ */
+ ie: function(e) {
+ return e.stack
+ .replace(/^\s*at\s+(.*)$/gm, '$1')
+ .replace(/^Anonymous function\s+/gm, '{anonymous}() ')
+ .replace(/^(.+)\s+\((.+)\)$/gm, '$1@$2')
+ .split('\n')
+ .slice(1);
+ },
+
+ /**
+ * Given an Error object, return a formatted Array based on Firefox's stack string.
+ *
+ * @param e - Error object to inspect
+ * @return Array<String> of function calls, files and line numbers
+ */
+ firefox: function(e) {
+ return e.stack.replace(/(?:\n@:0)?\s+$/m, '')
+ .replace(/^(?:\((\S*)\))?@/gm, '{anonymous}($1)@')
+ .split('\n');
+ },
+
+ opera11: function(e) {
+ var ANON = '{anonymous}', lineRE = /^.*line (\d+), column (\d+)(?: in (.+))? in (\S+):$/;
+ var lines = e.stacktrace.split('\n'), result = [];
+
+ for (var i = 0, len = lines.length; i < len; i += 2) {
+ var match = lineRE.exec(lines[i]);
+ if (match) {
+ var location = match[4] + ':' + match[1] + ':' + match[2];
+ var fnName = match[3] || "global code";
+ fnName = fnName.replace(/<anonymous function: (\S+)>/, "$1").replace(/<anonymous function>/, ANON);
+ result.push(fnName + '@' + location + ' -- ' + lines[i + 1].replace(/^\s+/, ''));
+ }
+ }
+
+ return result;
+ },
+
+ opera10b: function(e) {
+ // "<anonymous function: run>([arguments not available])@file://localhost/G:/js/stacktrace.js:27\n" +
+ // "printStackTrace([arguments not available])@file://localhost/G:/js/stacktrace.js:18\n" +
+ // "@file://localhost/G:/js/test/functional/testcase1.html:15"
+ var lineRE = /^(.*)@(.+):(\d+)$/;
+ var lines = e.stacktrace.split('\n'), result = [];
+
+ for (var i = 0, len = lines.length; i < len; i++) {
+ var match = lineRE.exec(lines[i]);
+ if (match) {
+ var fnName = match[1] ? (match[1] + '()') : "global code";
+ result.push(fnName + '@' + match[2] + ':' + match[3]);
+ }
+ }
+
+ return result;
+ },
+
+ /**
+ * Given an Error object, return a formatted Array based on Opera 10's stacktrace string.
+ *
+ * @param e - Error object to inspect
+ * @return Array<String> of function calls, files and line numbers
+ */
+ opera10a: function(e) {
+ // " Line 27 of linked script file://localhost/G:/js/stacktrace.js\n"
+ // " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function foo\n"
+ var ANON = '{anonymous}', lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;
+ var lines = e.stacktrace.split('\n'), result = [];
+
+ for (var i = 0, len = lines.length; i < len; i += 2) {
+ var match = lineRE.exec(lines[i]);
+ if (match) {
+ var fnName = match[3] || ANON;
+ result.push(fnName + '()@' + match[2] + ':' + match[1] + ' -- ' + lines[i + 1].replace(/^\s+/, ''));
+ }
+ }
+
+ return result;
+ },
+
+ // Opera 7.x-9.2x only!
+ opera9: function(e) {
+ // " Line 43 of linked script file://localhost/G:/js/stacktrace.js\n"
+ // " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n"
+ var ANON = '{anonymous}', lineRE = /Line (\d+).*script (?:in )?(\S+)/i;
+ var lines = e.message.split('\n'), result = [];
+
+ for (var i = 2, len = lines.length; i < len; i += 2) {
+ var match = lineRE.exec(lines[i]);
+ if (match) {
+ result.push(ANON + '()@' + match[2] + ':' + match[1] + ' -- ' + lines[i + 1].replace(/^\s+/, ''));
+ }
+ }
+
+ return result;
+ },
+
+ phantomjs: function(e) {
+ var ANON = '{anonymous}', lineRE = /(\S+) \((\S+)\)/i;
+ var lines = e.stack.split('\n'), result = [];
+
+ for (var i = 1, len = lines.length; i < len; i++) {
+ lines[i] = lines[i].replace(/^\s+at\s+/gm, '');
+ var match = lineRE.exec(lines[i]);
+ if (match) {
+ result.push(match[1] + '()@' + match[2]);
}
else {
- // We didn't have to use the headerBuffer.
- decoder.seek(this.offset - this.headerLength);
- typeAndVal = decoder.decodeTypeAndVal();
+ result.push(ANON + '()@' + lines[i]);
}
-
- if (typeAndVal == null)
- throw new Error("BinaryXMLStructureDecoder: Can't read header starting at offset " +
- (this.offset - this.headerLength));
-
- // Set the next state based on the type.
- var type = typeAndVal.t;
- if (type == XML_DATTR)
- // We already consumed the item. READ_HEADER_OR_CLOSE again.
- // ndnb has rules about what must follow an attribute, but we are just scanning.
- this.startHeader();
- else if (type == XML_DTAG || type == XML_EXT) {
- // Start a new level and READ_HEADER_OR_CLOSE again.
- ++this.level;
- this.startHeader();
+ }
+
+ return result;
+ },
+
+ // Safari 5-, IE 9-, and others
+ other: function(curr) {
+ var ANON = '{anonymous}', fnRE = /function(?:\s+([\w$]+))?\s*\(/, stack = [], fn, args, maxStackSize = 10;
+ var slice = Array.prototype.slice;
+ while (curr && stack.length < maxStackSize) {
+ fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
+ try {
+ args = slice.call(curr['arguments'] || []);
+ } catch (e) {
+ args = ['Cannot access arguments: ' + e];
}
- else if (type == XML_TAG || type == XML_ATTR) {
- if (type == XML_TAG)
- // Start a new level and read the tag.
- ++this.level;
- // Minimum tag or attribute length is 1.
- this.nBytesToRead = typeAndVal.v + 1;
- this.state = BinaryXMLStructureDecoder.READ_BYTES;
- // ndnb has rules about what must follow an attribute, but we are just scanning.
+ stack[stack.length] = fn + '(' + this.stringifyArguments(args) + ')';
+ try {
+ curr = curr.caller;
+ } catch (e) {
+ stack[stack.length] = 'Cannot access caller: ' + e;
+ break;
}
- else if (type == XML_BLOB || type == XML_UDATA) {
- this.nBytesToRead = typeAndVal.v;
- this.state = BinaryXMLStructureDecoder.READ_BYTES;
+ }
+ return stack;
+ },
+
+ /**
+ * Given arguments array as a String, substituting type names for non-string types.
+ *
+ * @param {Arguments,Array} args
+ * @return {String} stringified arguments
+ */
+ stringifyArguments: function(args) {
+ var result = [];
+ var slice = Array.prototype.slice;
+ for (var i = 0; i < args.length; ++i) {
+ var arg = args[i];
+ if (arg === undefined) {
+ result[i] = 'undefined';
+ } else if (arg === null) {
+ result[i] = 'null';
+ } else if (arg.constructor) {
+ // TODO constructor comparison does not work for iframes
+ if (arg.constructor === Array) {
+ if (arg.length < 3) {
+ result[i] = '[' + this.stringifyArguments(arg) + ']';
+ } else {
+ result[i] = '[' + this.stringifyArguments(slice.call(arg, 0, 1)) + '...' + this.stringifyArguments(slice.call(arg, -1)) + ']';
+ }
+ } else if (arg.constructor === Object) {
+ result[i] = '#object';
+ } else if (arg.constructor === Function) {
+ result[i] = '#function';
+ } else if (arg.constructor === String) {
+ result[i] = '"' + arg + '"';
+ } else if (arg.constructor === Number) {
+ result[i] = arg;
+ } else {
+ result[i] = '?';
+ }
}
- else
- throw new Error("BinaryXMLStructureDecoder: Unrecognized header type " + type);
- break;
-
- case BinaryXMLStructureDecoder.READ_BYTES:
- var nRemainingBytes = input.length - this.offset;
- if (nRemainingBytes < this.nBytesToRead) {
- // Need more.
- this.offset += nRemainingBytes;
- this.nBytesToRead -= nRemainingBytes;
- return false;
+ }
+ return result.join(',');
+ },
+
+ sourceCache: {},
+
+ /**
+ * @return {String} the text from a given URL
+ */
+ ajax: function(url) {
+ var req = this.createXMLHTTPObject();
+ if (req) {
+ try {
+ req.open('GET', url, false);
+ //req.overrideMimeType('text/plain');
+ //req.overrideMimeType('text/javascript');
+ req.send(null);
+ //return req.status == 200 ? req.responseText : '';
+ return req.responseText;
+ } catch (e) {
}
- // Got the bytes. Read a new header or close.
- this.offset += this.nBytesToRead;
- this.startHeader();
- break;
-
- default:
- // We don't expect this to happen.
- throw new Error("BinaryXMLStructureDecoder: Unrecognized state " + this.state);
+ }
+ return '';
+ },
+
+ /**
+ * Try XHR methods in order and store XHR factory.
+ *
+ * @return {XMLHttpRequest} XHR function or equivalent
+ */
+ createXMLHTTPObject: function() {
+ var xmlhttp, XMLHttpFactories = [
+ function() {
+ return new XMLHttpRequest();
+ }, function() {
+ return new ActiveXObject('Msxml2.XMLHTTP');
+ }, function() {
+ return new ActiveXObject('Msxml3.XMLHTTP');
+ }, function() {
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ }
+ ];
+ for (var i = 0; i < XMLHttpFactories.length; i++) {
+ try {
+ xmlhttp = XMLHttpFactories[i]();
+ // Use memoization to cache the factory
+ this.createXMLHTTPObject = XMLHttpFactories[i];
+ return xmlhttp;
+ } catch (e) {
+ }
+ }
+ },
+
+ /**
+ * Given a URL, check if it is in the same domain (so we can get the source
+ * via Ajax).
+ *
+ * @param url {String} source url
+ * @return {Boolean} False if we need a cross-domain request
+ */
+ isSameDomain: function(url) {
+ return typeof location !== "undefined" && url.indexOf(location.hostname) !== -1; // location may not be defined, e.g. when running from nodejs.
+ },
+
+ /**
+ * Get source code from given URL if in the same domain.
+ *
+ * @param url {String} JS source URL
+ * @return {Array} Array of source code lines
+ */
+ getSource: function(url) {
+ // TODO reuse source from script tags?
+ if (!(url in this.sourceCache)) {
+ this.sourceCache[url] = this.ajax(url).split('\n');
+ }
+ return this.sourceCache[url];
+ },
+
+ guessAnonymousFunctions: function(stack) {
+ for (var i = 0; i < stack.length; ++i) {
+ var reStack = /\{anonymous\}\(.*\)@(.*)/,
+ reRef = /^(.*?)(?::(\d+))(?::(\d+))?(?: -- .+)?$/,
+ frame = stack[i], ref = reStack.exec(frame);
+
+ if (ref) {
+ var m = reRef.exec(ref[1]);
+ if (m) { // If falsey, we did not get any file/line information
+ var file = m[1], lineno = m[2], charno = m[3] || 0;
+ if (file && this.isSameDomain(file) && lineno) {
+ var functionName = this.guessAnonymousFunction(file, lineno, charno);
+ stack[i] = frame.replace('{anonymous}', functionName);
+ }
+ }
+ }
+ }
+ return stack;
+ },
+
+ guessAnonymousFunction: function(url, lineNo, charNo) {
+ var ret;
+ try {
+ ret = this.findFunctionName(this.getSource(url), lineNo);
+ } catch (e) {
+ ret = 'getSource failed with url: ' + url + ', exception: ' + e.toString();
+ }
+ return ret;
+ },
+
+ findFunctionName: function(source, lineNo) {
+ // FIXME findFunctionName fails for compressed source
+ // (more than one function on the same line)
+ // function {name}({args}) m[1]=name m[2]=args
+ var reFunctionDeclaration = /function\s+([^(]*?)\s*\(([^)]*)\)/;
+ // {name} = function ({args}) TODO args capture
+ // /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*function(?:[^(]*)/
+ var reFunctionExpression = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/;
+ // {name} = eval()
+ var reFunctionEvaluation = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/;
+ // Walk backwards in the source lines until we find
+ // the line which matches one of the patterns above
+ var code = "", line, maxLines = Math.min(lineNo, 20), m, commentPos;
+ for (var i = 0; i < maxLines; ++i) {
+ // lineNo is 1-based, source[] is 0-based
+ line = source[lineNo - i - 1];
+ commentPos = line.indexOf('//');
+ if (commentPos >= 0) {
+ line = line.substr(0, commentPos);
+ }
+ // TODO check other types of comments? Commented code may lead to false positive
+ if (line) {
+ code = line + code;
+ m = reFunctionExpression.exec(code);
+ if (m && m[1]) {
+ return m[1];
+ }
+ m = reFunctionDeclaration.exec(code);
+ if (m && m[1]) {
+ //return m[1] + "(" + (m[2] || "") + ")";
+ return m[1];
+ }
+ m = reFunctionEvaluation.exec(code);
+ if (m && m[1]) {
+ return m[1];
+ }
+ }
+ }
+ return '(?)';
}
- }
-};
+ };
-/*
- * Set the state to READ_HEADER_OR_CLOSE and set up to start reading the header
- */
-BinaryXMLStructureDecoder.prototype.startHeader = function() {
- this.headerLength = 0;
- this.useHeaderBuffer = false;
- this.state = BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE;
-}
-
-/*
- * Set the offset into the input, used for the next read.
- */
-BinaryXMLStructureDecoder.prototype.seek = function(
- //int
- offset) {
- this.offset = offset;
-}
+ return printStackTrace;
+}));
/**
- * This class contains utilities to help parse the data
- * author: Meki Cheraoui, Jeff Thompson
- * See COPYING for copyright and distribution information.
- */
-
-var DataUtils = function DataUtils(){
-
-
-};
-
-
-/*
- * NOTE THIS IS CURRENTLY NOT BEHING USED
- *
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Ryan Bennett
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
*/
-DataUtils.keyStr = "ABCDEFGHIJKLMNOP" +
- "QRSTUVWXYZabcdef" +
- "ghijklmnopqrstuv" +
- "wxyz0123456789+/" +
- "=";
+// define a shim require function so that a node/browserify require calls dont cause errors when ndn-js is used via <script> tag
-
+/** @ignore */
+var ndn = ndn || {}
+/** @ignore */
+var exports = ndn;
+
+/** @ignore */
+var module = {}
+/** @ignore */
+function require(){return ndn;}
/**
- * Raw String to Base 64
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
*/
-DataUtils.stringtoBase64=function stringtoBase64(input) {
- input = escape(input);
- var output = "";
- var chr1, chr2, chr3 = "";
- var enc1, enc2, enc3, enc4 = "";
- var i = 0;
- do {
- chr1 = input.charCodeAt(i++);
- chr2 = input.charCodeAt(i++);
- chr3 = input.charCodeAt(i++);
+// This is included after stacktrace.js and browserify-require.js so that
+// require().printStackTrace works.
- enc1 = chr1 >> 2;
- enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
- enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
- enc4 = chr3 & 63;
+exports.printStackTrace = printStackTrace;
+/**
+ * This module checks for the availability of various crypto.subtle api's at runtime,
+ * exporting a function that returns the known availability of necessary NDN crypto apis
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Ryan Bennett <nomad.ry@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
- if (isNaN(chr2)) {
- enc3 = enc4 = 64;
- } else if (isNaN(chr3)) {
- enc4 = 64;
- }
-
- output = output +
- DataUtils.keyStr.charAt(enc1) +
- DataUtils.keyStr.charAt(enc2) +
- DataUtils.keyStr.charAt(enc3) +
- DataUtils.keyStr.charAt(enc4);
- chr1 = chr2 = chr3 = "";
- enc1 = enc2 = enc3 = enc4 = "";
- } while (i < input.length);
-
- return output;
+function DetectSubtleCrypto(){
+ var use = false;
+ var baselineSupport = (
+ (typeof crypto !== 'undefined' && crypto && crypto.subtle)
+ && (
+ (location.protocol === "https:" || "chrome-extension:" || "chrome:")
+ || (location.hostname === "localhost" || location.hostname === "127.0.0.1")
+ )
+ ) ? true : false ;
+ if (baselineSupport) {
+ var algo = { name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, hash:{name:"SHA-256"}, publicExponent: new Uint8Array([0x01, 0x00, 0x01])};
+ var keypair;
+ //try to perform every RSA crypto operation we need, if everything works, set use = true
+ crypto.subtle.generateKey(
+ algo,
+ true, //exportable;
+ ["sign", "verify"]).then(function(key){
+ keypair = key;
+ return crypto.subtle.sign(algo, key.privateKey, new Uint8Array([1,2,3,4,5]));
+ }).then(function(signature){
+ return crypto.subtle.verify(algo, keypair.publicKey, signature, new Uint8Array([1,2,3,4,5]));
+ }).then(function(verified){
+ return crypto.subtle.exportKey("pkcs8",keypair.privateKey);
+ }).then(function(pkcs8){
+ return crypto.subtle.importKey("pkcs8", pkcs8, algo, true, ["sign"]);
+ }).then(function(importedKey){
+ return crypto.subtle.exportKey("spki", keypair.publicKey);
+ }).then(function(spki){
+ return crypto.subtle.importKey("spki", spki, algo, true, ["verify"]);
+ }).then(function(importedKey){
+ var testDigest = new Uint8Array([1,2,3,4,5]);
+ return crypto.subtle.digest({name:"SHA-256"}, testDigest.buffer);
+ }).then(function(result){
+ use = true;
+ }, function(err){
+ console.log("DetectSubtleCrypto encountered error, not using crypto.subtle: ", err)
+ });
}
-
-/**
- * Base 64 to Raw String
- */
-DataUtils.base64toString = function base64toString(input) {
- var output = "";
- var chr1, chr2, chr3 = "";
- var enc1, enc2, enc3, enc4 = "";
- var i = 0;
-
- // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
- var base64test = /[^A-Za-z0-9\+\/\=]/g;
- /* Test for invalid characters. */
- if (base64test.exec(input)) {
- alert("There were invalid base64 characters in the input text.\n" +
- "Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" +
- "Expect errors in decoding.");
- }
-
- input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
-
- do {
- enc1 = DataUtils.keyStr.indexOf(input.charAt(i++));
- enc2 = DataUtils.keyStr.indexOf(input.charAt(i++));
- enc3 = DataUtils.keyStr.indexOf(input.charAt(i++));
- enc4 = DataUtils.keyStr.indexOf(input.charAt(i++));
-
- chr1 = (enc1 << 2) | (enc2 >> 4);
- chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
- chr3 = ((enc3 & 3) << 6) | enc4;
-
- output = output + String.fromCharCode(chr1);
-
- if (enc3 != 64) {
- output = output + String.fromCharCode(chr2);
- }
- if (enc4 != 64) {
- output = output + String.fromCharCode(chr3);
- }
-
- chr1 = chr2 = chr3 = "";
- enc1 = enc2 = enc3 = enc4 = "";
-
- } while (i < input.length);
-
- return unescape(output);
- };
-
-//byte []
-
-/**
- * NOT WORKING!!!!!
- *
- * Unsiged Long Number to Byte Array
- */
-
- /*
-DataUtils.unsignedLongToByteArray= function( value) {
-
- if(LOG>4)console.log('INPUT IS '+value);
-
- if( 0 == value )
- return [0];
-
- if( 0 <= value && value <= 0x00FF ) {
- //byte []
- var bb = new Array(1);
- bb[0] = (value & 0x00FF);
- return bb;
- }
-
- if(LOG>4) console.log('type of value is '+typeof value);
- if(LOG>4) console.log('value is '+value);
- //byte []
- var out = null;
- //int
- var offset = -1;
- for(var i = 7; i >=0; --i) {
- //byte
- console.log(i);
- console.log('value is '+value);
- console.log('(value >> (i * 8)) '+ (value >> (i * 8)) );
- console.log(' ((value >> (i * 8)) & 0xFF) '+ ((value >> (i * 8)) & 0xFF) );
-
- var b = ((value >> (i * 8)) & 0xFF) ;
-
- if(LOG>4) console.log('b is '+b);
-
- if( out == null && b != 0 ) {
- //out = new byte[i+1];
- out = new Array(i+1);
- offset = i;
- }
-
- if( out != null )
- out[ offset - i ] = b;
- }
- if(LOG>4)console.log('OUTPUT IS ');
- if(LOG>4)console.log(out);
- return out;
+ return function useSubtleCrypto(){
+ return use;
+ }
}
+
+var UseSubtleCrypto = DetectSubtleCrypto();
+
+exports.UseSubtleCrypto = UseSubtleCrypto;
+/*! CryptoJS v3.1.2 core-fix.js
+ * code.google.com/p/crypto-js
+ * (c) 2009-2013 by Jeff Mott. All rights reserved.
+ * code.google.com/p/crypto-js/wiki/License
+ * THIS IS FIX of 'core.js' to fix Hmac issue.
+ * https://code.google.com/p/crypto-js/issues/detail?id=84
+ * https://crypto-js.googlecode.com/svn-history/r667/branches/3.x/src/core.js
+ */
+/**
+ * CryptoJS core components.
+ */
+var CryptoJS = CryptoJS || (function (Math, undefined) {
+ /**
+ * CryptoJS namespace.
+ */
+ var C = {};
+
+ /**
+ * Library namespace.
+ */
+ var C_lib = C.lib = {};
+
+ /**
+ * Base object for prototypal inheritance.
+ */
+ var Base = C_lib.Base = (function () {
+ function F() {}
+
+ return {
+ /**
+ * Creates a new object that inherits from this object.
+ *
+ * @param {Object} overrides Properties to copy into the new object.
+ *
+ * @return {Object} The new object.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var MyType = CryptoJS.lib.Base.extend({
+ * field: 'value',
+ *
+ * method: function () {
+ * }
+ * });
+ */
+ extend: function (overrides) {
+ // Spawn
+ F.prototype = this;
+ var subtype = new F();
+
+ // Augment
+ if (overrides) {
+ subtype.mixIn(overrides);
+ }
+
+ // Create default initializer
+ if (!subtype.hasOwnProperty('init')) {
+ subtype.init = function () {
+ subtype.$super.init.apply(this, arguments);
+ };
+ }
+
+ // Initializer's prototype is the subtype object
+ subtype.init.prototype = subtype;
+
+ // Reference supertype
+ subtype.$super = this;
+
+ return subtype;
+ },
+
+ /**
+ * Extends this object and runs the init method.
+ * Arguments to create() will be passed to init().
+ *
+ * @return {Object} The new object.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var instance = MyType.create();
+ */
+ create: function () {
+ var instance = this.extend();
+ instance.init.apply(instance, arguments);
+
+ return instance;
+ },
+
+ /**
+ * Initializes a newly created object.
+ * Override this method to add some logic when your objects are created.
+ *
+ * @example
+ *
+ * var MyType = CryptoJS.lib.Base.extend({
+ * init: function () {
+ * // ...
+ * }
+ * });
+ */
+ init: function () {
+ },
+
+ /**
+ * Copies properties into this object.
+ *
+ * @param {Object} properties The properties to mix in.
+ *
+ * @example
+ *
+ * MyType.mixIn({
+ * field: 'value'
+ * });
+ */
+ mixIn: function (properties) {
+ for (var propertyName in properties) {
+ if (properties.hasOwnProperty(propertyName)) {
+ this[propertyName] = properties[propertyName];
+ }
+ }
+
+ // IE won't copy toString using the loop above
+ if (properties.hasOwnProperty('toString')) {
+ this.toString = properties.toString;
+ }
+ },
+
+ /**
+ * Creates a copy of this object.
+ *
+ * @return {Object} The clone.
+ *
+ * @example
+ *
+ * var clone = instance.clone();
+ */
+ clone: function () {
+ return this.init.prototype.extend(this);
+ }
+ };
+ }());
+
+ /**
+ * An array of 32-bit words.
+ *
+ * @property {Array} words The array of 32-bit words.
+ * @property {number} sigBytes The number of significant bytes in this word array.
+ */
+ var WordArray = C_lib.WordArray = Base.extend({
+ /**
+ * Initializes a newly created word array.
+ *
+ * @param {Array} words (Optional) An array of 32-bit words.
+ * @param {number} sigBytes (Optional) The number of significant bytes in the words.
+ *
+ * @example
+ *
+ * var wordArray = CryptoJS.lib.WordArray.create();
+ * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]);
+ * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6);
+ */
+ init: function (words, sigBytes) {
+ words = this.words = words || [];
+
+ if (sigBytes != undefined) {
+ this.sigBytes = sigBytes;
+ } else {
+ this.sigBytes = words.length * 4;
+ }
+ },
+
+ /**
+ * Converts this word array to a string.
+ *
+ * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex
+ *
+ * @return {string} The stringified word array.
+ *
+ * @example
+ *
+ * var string = wordArray + '';
+ * var string = wordArray.toString();
+ * var string = wordArray.toString(CryptoJS.enc.Utf8);
+ */
+ toString: function (encoder) {
+ return (encoder || Hex).stringify(this);
+ },
+
+ /**
+ * Concatenates a word array to this word array.
+ *
+ * @param {WordArray} wordArray The word array to append.
+ *
+ * @return {WordArray} This word array.
+ *
+ * @example
+ *
+ * wordArray1.concat(wordArray2);
+ */
+ concat: function (wordArray) {
+ // Shortcuts
+ var thisWords = this.words;
+ var thatWords = wordArray.words;
+ var thisSigBytes = this.sigBytes;
+ var thatSigBytes = wordArray.sigBytes;
+
+ // Clamp excess bits
+ this.clamp();
+
+ // Concat
+ if (thisSigBytes % 4) {
+ // Copy one byte at a time
+ for (var i = 0; i < thatSigBytes; i++) {
+ var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
+ thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8);
+ }
+ } else {
+ // Copy one word at a time
+ for (var i = 0; i < thatSigBytes; i += 4) {
+ thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2];
+ }
+ }
+ this.sigBytes += thatSigBytes;
+
+ // Chainable
+ return this;
+ },
+
+ /**
+ * Removes insignificant bits.
+ *
+ * @example
+ *
+ * wordArray.clamp();
+ */
+ clamp: function () {
+ // Shortcuts
+ var words = this.words;
+ var sigBytes = this.sigBytes;
+
+ // Clamp
+ words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8);
+ words.length = Math.ceil(sigBytes / 4);
+ },
+
+ /**
+ * Creates a copy of this word array.
+ *
+ * @return {WordArray} The clone.
+ *
+ * @example
+ *
+ * var clone = wordArray.clone();
+ */
+ clone: function () {
+ var clone = Base.clone.call(this);
+ clone.words = this.words.slice(0);
+
+ return clone;
+ },
+
+ /**
+ * Creates a word array filled with random bytes.
+ *
+ * @param {number} nBytes The number of random bytes to generate.
+ *
+ * @return {WordArray} The random word array.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var wordArray = CryptoJS.lib.WordArray.random(16);
+ */
+ random: function (nBytes) {
+ var words = [];
+ for (var i = 0; i < nBytes; i += 4) {
+ words.push((Math.random() * 0x100000000) | 0);
+ }
+
+ return new WordArray.init(words, nBytes);
+ }
+ });
+
+ /**
+ * Encoder namespace.
+ */
+ var C_enc = C.enc = {};
+
+ /**
+ * Hex encoding strategy.
+ */
+ var Hex = C_enc.Hex = {
+ /**
+ * Converts a word array to a hex string.
+ *
+ * @param {WordArray} wordArray The word array.
+ *
+ * @return {string} The hex string.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var hexString = CryptoJS.enc.Hex.stringify(wordArray);
+ */
+ stringify: function (wordArray) {
+ // Shortcuts
+ var words = wordArray.words;
+ var sigBytes = wordArray.sigBytes;
+
+ // Convert
+ var hexChars = [];
+ for (var i = 0; i < sigBytes; i++) {
+ var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
+ hexChars.push((bite >>> 4).toString(16));
+ hexChars.push((bite & 0x0f).toString(16));
+ }
+
+ return hexChars.join('');
+ },
+
+ /**
+ * Converts a hex string to a word array.
+ *
+ * @param {string} hexStr The hex string.
+ *
+ * @return {WordArray} The word array.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var wordArray = CryptoJS.enc.Hex.parse(hexString);
+ */
+ parse: function (hexStr) {
+ // Shortcut
+ var hexStrLength = hexStr.length;
+
+ // Convert
+ var words = [];
+ for (var i = 0; i < hexStrLength; i += 2) {
+ words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
+ }
+
+ return new WordArray.init(words, hexStrLength / 2);
+ }
+ };
+
+ /**
+ * Latin1 encoding strategy.
+ */
+ var Latin1 = C_enc.Latin1 = {
+ /**
+ * Converts a word array to a Latin1 string.
+ *
+ * @param {WordArray} wordArray The word array.
+ *
+ * @return {string} The Latin1 string.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray);
+ */
+ stringify: function (wordArray) {
+ // Shortcuts
+ var words = wordArray.words;
+ var sigBytes = wordArray.sigBytes;
+
+ // Convert
+ var latin1Chars = [];
+ for (var i = 0; i < sigBytes; i++) {
+ var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
+ latin1Chars.push(String.fromCharCode(bite));
+ }
+
+ return latin1Chars.join('');
+ },
+
+ /**
+ * Converts a Latin1 string to a word array.
+ *
+ * @param {string} latin1Str The Latin1 string.
+ *
+ * @return {WordArray} The word array.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var wordArray = CryptoJS.enc.Latin1.parse(latin1String);
+ */
+ parse: function (latin1Str) {
+ // Shortcut
+ var latin1StrLength = latin1Str.length;
+
+ // Convert
+ var words = [];
+ for (var i = 0; i < latin1StrLength; i++) {
+ words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8);
+ }
+
+ return new WordArray.init(words, latin1StrLength);
+ }
+ };
+
+ /**
+ * UTF-8 encoding strategy.
+ */
+ var Utf8 = C_enc.Utf8 = {
+ /**
+ * Converts a word array to a UTF-8 string.
+ *
+ * @param {WordArray} wordArray The word array.
+ *
+ * @return {string} The UTF-8 string.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray);
+ */
+ stringify: function (wordArray) {
+ try {
+ return decodeURIComponent(escape(Latin1.stringify(wordArray)));
+ } catch (e) {
+ throw new Error('Malformed UTF-8 data');
+ }
+ },
+
+ /**
+ * Converts a UTF-8 string to a word array.
+ *
+ * @param {string} utf8Str The UTF-8 string.
+ *
+ * @return {WordArray} The word array.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var wordArray = CryptoJS.enc.Utf8.parse(utf8String);
+ */
+ parse: function (utf8Str) {
+ return Latin1.parse(unescape(encodeURIComponent(utf8Str)));
+ }
+ };
+
+ /**
+ * Abstract buffered block algorithm template.
+ *
+ * The property blockSize must be implemented in a concrete subtype.
+ *
+ * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0
+ */
+ var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({
+ /**
+ * Resets this block algorithm's data buffer to its initial state.
+ *
+ * @example
+ *
+ * bufferedBlockAlgorithm.reset();
+ */
+ reset: function () {
+ // Initial values
+ this._data = new WordArray.init();
+ this._nDataBytes = 0;
+ },
+
+ /**
+ * Adds new data to this block algorithm's buffer.
+ *
+ * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8.
+ *
+ * @example
+ *
+ * bufferedBlockAlgorithm._append('data');
+ * bufferedBlockAlgorithm._append(wordArray);
+ */
+ _append: function (data) {
+ // Convert string to WordArray, else assume WordArray already
+ if (typeof data == 'string') {
+ data = Utf8.parse(data);
+ }
+
+ // Append
+ this._data.concat(data);
+ this._nDataBytes += data.sigBytes;
+ },
+
+ /**
+ * Processes available data blocks.
+ *
+ * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype.
+ *
+ * @param {boolean} doFlush Whether all blocks and partial blocks should be processed.
+ *
+ * @return {WordArray} The processed data.
+ *
+ * @example
+ *
+ * var processedData = bufferedBlockAlgorithm._process();
+ * var processedData = bufferedBlockAlgorithm._process(!!'flush');
+ */
+ _process: function (doFlush) {
+ // Shortcuts
+ var data = this._data;
+ var dataWords = data.words;
+ var dataSigBytes = data.sigBytes;
+ var blockSize = this.blockSize;
+ var blockSizeBytes = blockSize * 4;
+
+ // Count blocks ready
+ var nBlocksReady = dataSigBytes / blockSizeBytes;
+ if (doFlush) {
+ // Round up to include partial blocks
+ nBlocksReady = Math.ceil(nBlocksReady);
+ } else {
+ // Round down to include only full blocks,
+ // less the number of blocks that must remain in the buffer
+ nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0);
+ }
+
+ // Count words ready
+ var nWordsReady = nBlocksReady * blockSize;
+
+ // Count bytes ready
+ var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes);
+
+ // Process blocks
+ if (nWordsReady) {
+ for (var offset = 0; offset < nWordsReady; offset += blockSize) {
+ // Perform concrete-algorithm logic
+ this._doProcessBlock(dataWords, offset);
+ }
+
+ // Remove processed words
+ var processedWords = dataWords.splice(0, nWordsReady);
+ data.sigBytes -= nBytesReady;
+ }
+
+ // Return processed words
+ return new WordArray.init(processedWords, nBytesReady);
+ },
+
+ /**
+ * Creates a copy of this object.
+ *
+ * @return {Object} The clone.
+ *
+ * @example
+ *
+ * var clone = bufferedBlockAlgorithm.clone();
+ */
+ clone: function () {
+ var clone = Base.clone.call(this);
+ clone._data = this._data.clone();
+
+ return clone;
+ },
+
+ _minBufferSize: 0
+ });
+
+ /**
+ * Abstract hasher template.
+ *
+ * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits)
+ */
+ var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({
+ /**
+ * Configuration options.
+ */
+ cfg: Base.extend(),
+
+ /**
+ * Initializes a newly created hasher.
+ *
+ * @param {Object} cfg (Optional) The configuration options to use for this hash computation.
+ *
+ * @example
+ *
+ * var hasher = CryptoJS.algo.SHA256.create();
+ */
+ init: function (cfg) {
+ // Apply config defaults
+ this.cfg = this.cfg.extend(cfg);
+
+ // Set initial values
+ this.reset();
+ },
+
+ /**
+ * Resets this hasher to its initial state.
+ *
+ * @example
+ *
+ * hasher.reset();
+ */
+ reset: function () {
+ // Reset data buffer
+ BufferedBlockAlgorithm.reset.call(this);
+
+ // Perform concrete-hasher logic
+ this._doReset();
+ },
+
+ /**
+ * Updates this hasher with a message.
+ *
+ * @param {WordArray|string} messageUpdate The message to append.
+ *
+ * @return {Hasher} This hasher.
+ *
+ * @example
+ *
+ * hasher.update('message');
+ * hasher.update(wordArray);
+ */
+ update: function (messageUpdate) {
+ // Append
+ this._append(messageUpdate);
+
+ // Update the hash
+ this._process();
+
+ // Chainable
+ return this;
+ },
+
+ /**
+ * Finalizes the hash computation.
+ * Note that the finalize operation is effectively a destructive, read-once operation.
+ *
+ * @param {WordArray|string} messageUpdate (Optional) A final message update.
+ *
+ * @return {WordArray} The hash.
+ *
+ * @example
+ *
+ * var hash = hasher.finalize();
+ * var hash = hasher.finalize('message');
+ * var hash = hasher.finalize(wordArray);
+ */
+ finalize: function (messageUpdate) {
+ // Final message update
+ if (messageUpdate) {
+ this._append(messageUpdate);
+ }
+
+ // Perform concrete-hasher logic
+ var hash = this._doFinalize();
+
+ return hash;
+ },
+
+ blockSize: 512/32,
+
+ /**
+ * Creates a shortcut function to a hasher's object interface.
+ *
+ * @param {Hasher} hasher The hasher to create a helper for.
+ *
+ * @return {Function} The shortcut function.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256);
+ */
+ _createHelper: function (hasher) {
+ return function (message, cfg) {
+ return new hasher.init(cfg).finalize(message);
+ };
+ },
+
+ /**
+ * Creates a shortcut function to the HMAC's object interface.
+ *
+ * @param {Hasher} hasher The hasher to use in this HMAC helper.
+ *
+ * @return {Function} The shortcut function.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256);
+ */
+ _createHmacHelper: function (hasher) {
+ return function (message, key) {
+ return new C_algo.HMAC.init(hasher, key).finalize(message);
+ };
+ }
+ });
+
+ /**
+ * Algorithm namespace.
+ */
+ var C_algo = C.algo = {};
+
+ return C;
+}(Math));
+
+exports.CryptoJS = CryptoJS;
+module.exports = exports;
+/*
+CryptoJS v3.1.2
+code.google.com/p/crypto-js
+(c) 2009-2013 by Jeff Mott. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+code.google.com/p/crypto-js/wiki/License
+*/
+
+var C = require('./core.js').CryptoJS;
+(function (Math) {
+ // Shortcuts
+ var C_lib = C.lib;
+ var WordArray = C_lib.WordArray;
+ var Hasher = C_lib.Hasher;
+ var C_algo = C.algo;
+
+ // Initialization and round constants tables
+ var H = [];
+ var K = [];
+
+ // Compute constants
+ (function () {
+ function isPrime(n) {
+ var sqrtN = Math.sqrt(n);
+ for (var factor = 2; factor <= sqrtN; factor++) {
+ if (!(n % factor)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function getFractionalBits(n) {
+ return ((n - (n | 0)) * 0x100000000) | 0;
+ }
+
+ var n = 2;
+ var nPrime = 0;
+ while (nPrime < 64) {
+ if (isPrime(n)) {
+ if (nPrime < 8) {
+ H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2));
+ }
+ K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));
+
+ nPrime++;
+ }
+
+ n++;
+ }
+ }());
+
+ // Reusable object
+ var W = [];
+
+ /**
+ * SHA-256 hash algorithm.
+ */
+ var SHA256 = C_algo.SHA256 = Hasher.extend({
+ _doReset: function () {
+ this._hash = new WordArray.init(H.slice(0));
+ },
+
+ _doProcessBlock: function (M, offset) {
+ // Shortcut
+ var H = this._hash.words;
+
+ // Working variables
+ var a = H[0];
+ var b = H[1];
+ var c = H[2];
+ var d = H[3];
+ var e = H[4];
+ var f = H[5];
+ var g = H[6];
+ var h = H[7];
+
+ // Computation
+ for (var i = 0; i < 64; i++) {
+ if (i < 16) {
+ W[i] = M[offset + i] | 0;
+ } else {
+ var gamma0x = W[i - 15];
+ var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^
+ ((gamma0x << 14) | (gamma0x >>> 18)) ^
+ (gamma0x >>> 3);
+
+ var gamma1x = W[i - 2];
+ var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
+ ((gamma1x << 13) | (gamma1x >>> 19)) ^
+ (gamma1x >>> 10);
+
+ W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];
+ }
+
+ var ch = (e & f) ^ (~e & g);
+ var maj = (a & b) ^ (a & c) ^ (b & c);
+
+ var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22));
+ var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25));
+
+ var t1 = h + sigma1 + ch + K[i] + W[i];
+ var t2 = sigma0 + maj;
+
+ h = g;
+ g = f;
+ f = e;
+ e = (d + t1) | 0;
+ d = c;
+ c = b;
+ b = a;
+ a = (t1 + t2) | 0;
+ }
+
+ // Intermediate hash value
+ H[0] = (H[0] + a) | 0;
+ H[1] = (H[1] + b) | 0;
+ H[2] = (H[2] + c) | 0;
+ H[3] = (H[3] + d) | 0;
+ H[4] = (H[4] + e) | 0;
+ H[5] = (H[5] + f) | 0;
+ H[6] = (H[6] + g) | 0;
+ H[7] = (H[7] + h) | 0;
+ },
+
+ _doFinalize: function () {
+ // Shortcuts
+ var data = this._data;
+ var dataWords = data.words;
+
+ var nBitsTotal = this._nDataBytes * 8;
+ var nBitsLeft = data.sigBytes * 8;
+
+ // Add padding
+ dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
+ dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);
+ dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;
+ data.sigBytes = dataWords.length * 4;
+
+ // Hash final blocks
+ this._process();
+
+ // Return final computed hash
+ return this._hash;
+ },
+
+ clone: function () {
+ var clone = Hasher.clone.call(this);
+ clone._hash = this._hash.clone();
+
+ return clone;
+ }
+ });
+
+ /**
+ * Shortcut function to the hasher's object interface.
+ *
+ * @param {WordArray|string} message The message to hash.
+ *
+ * @return {WordArray} The hash.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var hash = CryptoJS.SHA256('message');
+ * var hash = CryptoJS.SHA256(wordArray);
+ */
+ C.SHA256 = Hasher._createHelper(SHA256);
+
+ /**
+ * Shortcut function to the HMAC's object interface.
+ *
+ * @param {WordArray|string} message The message to hash.
+ * @param {WordArray|string} key The secret key.
+ *
+ * @return {WordArray} The HMAC.
+ *
+ * @static
+ *
+ * @example
+ *
+ * var hmac = CryptoJS.HmacSHA256(message, key);
+ */
+ C.HmacSHA256 = Hasher._createHmacHelper(SHA256);
+}(Math));
+
+exports.CryptoJS = C;
+module.exports = exports;
+/*
+CryptoJS v3.1.2
+code.google.com/p/crypto-js
+(c) 2009-2013 by Jeff Mott. All rights reserved.
+code.google.com/p/crypto-js/wiki/License
*/
-
-/**
- * NOT WORKING!!!!!
- *
- * Unsiged Long Number to Byte Array
- *//*
-DataUtils.byteArrayToUnsignedLong = function(//final byte []
- src) {
- if(LOG>4) console.log('INPUT IS ');
- if(LOG>4) console.log(src);
-
- var value = 0;
- for(var i = 0; i < src.length; i++) {
- value = value << 8;
- // Java will assume the byte is signed, so extend it and trim it.
-
-
- var b = ((src[i]) & 0xFF );
- value |= b;
- }
-
- if(LOG>4) console.log('OUTPUT IS ');
-
- if(LOG>4) console.log(value);
-
- return value;
- }*/
-
-
-/**
- * Hex String to Byte Array
+(function () {
+ // Shortcuts
+ var C = CryptoJS;
+ var C_lib = C.lib;
+ var Base = C_lib.Base;
+ var C_enc = C.enc;
+ var Utf8 = C_enc.Utf8;
+ var C_algo = C.algo;
+
+ /**
+ * HMAC algorithm.
+ */
+ var HMAC = C_algo.HMAC = Base.extend({
+ /**
+ * Initializes a newly created HMAC.
+ *
+ * @param {Hasher} hasher The hash algorithm to use.
+ * @param {WordArray|string} key The secret key.
+ *
+ * @example
+ *
+ * var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
+ */
+ init: function (hasher, key) {
+ // Init hasher
+ hasher = this._hasher = new hasher.init();
+
+ // Convert string to WordArray, else assume WordArray already
+ if (typeof key == 'string') {
+ key = Utf8.parse(key);
+ }
+
+ // Shortcuts
+ var hasherBlockSize = hasher.blockSize;
+ var hasherBlockSizeBytes = hasherBlockSize * 4;
+
+ // Allow arbitrary length keys
+ if (key.sigBytes > hasherBlockSizeBytes) {
+ key = hasher.finalize(key);
+ }
+
+ // Clamp excess bits
+ key.clamp();
+
+ // Clone key for inner and outer pads
+ var oKey = this._oKey = key.clone();
+ var iKey = this._iKey = key.clone();
+
+ // Shortcuts
+ var oKeyWords = oKey.words;
+ var iKeyWords = iKey.words;
+
+ // XOR keys with pad constants
+ for (var i = 0; i < hasherBlockSize; i++) {
+ oKeyWords[i] ^= 0x5c5c5c5c;
+ iKeyWords[i] ^= 0x36363636;
+ }
+ oKey.sigBytes = iKey.sigBytes = hasherBlockSizeBytes;
+
+ // Set initial values
+ this.reset();
+ },
+
+ /**
+ * Resets this HMAC to its initial state.
+ *
+ * @example
+ *
+ * hmacHasher.reset();
+ */
+ reset: function () {
+ // Shortcut
+ var hasher = this._hasher;
+
+ // Reset
+ hasher.reset();
+ hasher.update(this._iKey);
+ },
+
+ /**
+ * Updates this HMAC with a message.
+ *
+ * @param {WordArray|string} messageUpdate The message to append.
+ *
+ * @return {HMAC} This HMAC instance.
+ *
+ * @example
+ *
+ * hmacHasher.update('message');
+ * hmacHasher.update(wordArray);
+ */
+ update: function (messageUpdate) {
+ this._hasher.update(messageUpdate);
+
+ // Chainable
+ return this;
+ },
+
+ /**
+ * Finalizes the HMAC computation.
+ * Note that the finalize operation is effectively a destructive, read-once operation.
+ *
+ * @param {WordArray|string} messageUpdate (Optional) A final message update.
+ *
+ * @return {WordArray} The HMAC.
+ *
+ * @example
+ *
+ * var hmac = hmacHasher.finalize();
+ * var hmac = hmacHasher.finalize('message');
+ * var hmac = hmacHasher.finalize(wordArray);
+ */
+ finalize: function (messageUpdate) {
+ // Shortcut
+ var hasher = this._hasher;
+
+ // Compute HMAC
+ var innerHash = hasher.finalize(messageUpdate);
+ hasher.reset();
+ var hmac = hasher.finalize(this._oKey.clone().concat(innerHash));
+
+ return hmac;
+ }
+ });
+}());
+/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
*/
- //THIS IS NOT WORKING
-/*
-DataUtils.HexStringtoByteArray = function(str) {
- var byteArray = [];
- for (var i = 0; i < str.length; i++)
- if (str.charCodeAt(i) <= 0x7F)
- byteArray.push(str.charCodeAt(i));
- else {
- var h = encodeURIComponent(str.charAt(i)).substr(1).split('%');
- for (var j = 0; j < h.length; j++)
- byteArray.push(parseInt(h[j], 16));
- }
- return byteArray;
-};
-*/
-
-/**
- * Uint8Array to Hex String
- */
-//http://ejohn.org/blog/numbers-hex-and-colors/
-DataUtils.toHex = function(args){
- if (LOG>4) console.log('ABOUT TO CONVERT '+ args);
- //console.log(args);
- var ret = "";
- for ( var i = 0; i < args.length; i++ )
- ret += (args[i] < 16 ? "0" : "") + args[i].toString(16);
- if (LOG>4) console.log('Converted to: ' + ret);
- return ret; //.toUpperCase();
-}
-
-/**
- * Raw string to hex string.
- */
-DataUtils.stringToHex = function(args){
- var ret = "";
- for (var i = 0; i < args.length; ++i) {
- var value = args.charCodeAt(i);
- ret += (value < 16 ? "0" : "") + value.toString(16);
- }
- return ret;
-}
-
-/**
- * Uint8Array to raw string.
- */
-DataUtils.toString = function(args){
- //console.log(arguments);
- var ret = "";
- for ( var i = 0; i < args.length; i++ )
- ret += String.fromCharCode(args[i]);
- return ret;
-}
-
-/**
- * Hex String to Uint8Array.
- */
-DataUtils.toNumbers = function(str) {
- if (typeof str == 'string') {
- var ret = new Uint8Array(Math.floor(str.length / 2));
- var i = 0;
- str.replace(/(..)/g, function(str) {
- ret[i++] = parseInt(str, 16);
- });
- return ret;
- }
-}
-
-/**
- * Hex String to raw string.
- */
-DataUtils.hexToRawString = function(str) {
- if(typeof str =='string') {
- var ret = "";
- str.replace(/(..)/g, function(s) {
- ret += String.fromCharCode(parseInt(s, 16));
- });
- return ret;
- }
-}
-
-/**
- * Raw String to Uint8Array.
- */
-DataUtils.toNumbersFromString = function(str) {
- var bytes = new Uint8Array(str.length);
- for(var i=0;i<str.length;i++)
- bytes[i] = str.charCodeAt(i);
- return bytes;
-}
-
-/*
- * Encode str as utf8 and return as Uint8Array.
- * TODO: Use TextEncoder when available.
- */
-DataUtils.stringToUtf8Array = function(str) {
- return DataUtils.toNumbersFromString(str2rstr_utf8(str));
-}
-
-/*
- * arrays is an array of Uint8Array. Return a new Uint8Array which is the concatenation of all.
- */
-DataUtils.concatArrays = function(arrays) {
- var totalLength = 0;
- for (var i = 0; i < arrays.length; ++i)
- totalLength += arrays[i].length;
-
- var result = new Uint8Array(totalLength);
- var offset = 0;
- for (var i = 0; i < arrays.length; ++i) {
- result.set(arrays[i], offset);
- offset += arrays[i].length;
- }
- return result;
-
-}
-
-// TODO: Take Uint8Array and use TextDecoder when available.
-DataUtils.decodeUtf8 = function (utftext) {
- var string = "";
- var i = 0;
- var c = 0;
- var c1 = 0;
- var c2 = 0;
-
- while ( i < utftext.length ) {
-
- c = utftext.charCodeAt(i);
-
- if (c < 128) {
- string += String.fromCharCode(c);
- i++;
- }
- else if((c > 191) && (c < 224)) {
- c2 = utftext.charCodeAt(i+1);
- string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
- i += 2;
- }
- else {
- c2 = utftext.charCodeAt(i+1);
- var c3 = utftext.charCodeAt(i+2);
- string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
- i += 3;
- }
-
- }
-
- return string;
- };
-
-//NOT WORKING
-/*
-DataUtils.getUTF8StringFromBytes = function(bytes) {
-
- bytes = toString(bytes);
-
- var ix = 0;
-
- if( bytes.slice(0,3) == "\xEF\xBB\xBF") {
- ix = 3;
- }
-
- var string = "";
- for( ; ix < bytes.length; ix++ ) {
- var byte1 = bytes[ix].charCodeAt(0);
- if( byte1 < 0x80 ) {
- string += String.fromCharCode(byte1);
- } else if( byte1 >= 0xC2 && byte1 < 0xE0 ) {
- var byte2 = bytes[++ix].charCodeAt(0);
- string += String.fromCharCode(((byte1&0x1F)<<6) + (byte2&0x3F));
- } else if( byte1 >= 0xE0 && byte1 < 0xF0 ) {
- var byte2 = bytes[++ix].charCodeAt(0);
- var byte3 = bytes[++ix].charCodeAt(0);
- string += String.fromCharCode(((byte1&0xFF)<<12) + ((byte2&0x3F)<<6) + (byte3&0x3F));
- } else if( byte1 >= 0xF0 && byte1 < 0xF5) {
- var byte2 = bytes[++ix].charCodeAt(0);
- var byte3 = bytes[++ix].charCodeAt(0);
- var byte4 = bytes[++ix].charCodeAt(0);
- var codepoint = ((byte1&0x07)<<18) + ((byte2&0x3F)<<12)+ ((byte3&0x3F)<<6) + (byte4&0x3F);
- codepoint -= 0x10000;
- string += String.fromCharCode(
- (codepoint>>10) + 0xD800,
- (codepoint&0x3FF) + 0xDC00
- );
- }
- }
-
- return string;
-}*/
-
-/**
- * Return true if a1 and a2 are the same length with equal elements.
- */
-DataUtils.arraysEqual = function(a1, a2){
- if (a1.length != a2.length)
- return false;
-
- for (var i = 0; i < a1.length; ++i) {
- if (a1[i] != a2[i])
- return false;
- }
-
- return true;
-};
-
-/*
- * Convert the big endian Uint8Array to an unsigned int.
- * Don't check for overflow.
- */
-DataUtils.bigEndianToUnsignedInt = function(bytes) {
- var result = 0;
- for (var i = 0; i < bytes.length; ++i) {
- result <<= 8;
- result += bytes[i];
- }
- return result;
-};
-
-/*
- * Convert the int value to a new big endian Uint8Array and return.
- * If value is 0 or negative, return Uint8Array(0).
- */
-DataUtils.nonNegativeIntToBigEndian = function(value) {
- value = Math.round(value);
- if (value <= 0)
- return new Uint8Array(0);
-
- // Assume value is not over 64 bits.
- var size = 8;
- var result = new Uint8Array(size);
- var i = 0;
- while (value != 0) {
- ++i;
- result[size - i] = value & 0xff;
- value >>= 8;
- }
- return result.subarray(size - i, size);
-};
-
-/*
- * Modify array to randomly shuffle the elements.
- */
-DataUtils.shuffle = function(array) {
- for (var i = array.length - 1; i >= 1; --i) {
- // j is from 0 to i.
- var j = Math.floor(Math.random() * (i + 1));
- var temp = array[i];
- array[i] = array[j];
- array[j] = temp;
- }
-}
-/**
- * This file contains utilities to help encode and decode NDN objects.
- * author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- */
-
-function encodeToHexInterest(interest){
- return DataUtils.toHex(encodeToBinaryInterest(interest));
-}
-
-
-function encodeToBinaryInterest(interest) {
- var enc = new BinaryXMLEncoder();
- interest.to_ndnb(enc);
-
- return enc.getReducedOstream();
-}
-
-
-function encodeToHexContentObject(co){
- return DataUtils.toHex(encodeToBinaryContentObject(co));
-}
-
-function encodeToBinaryContentObject(co){
- var enc = new BinaryXMLEncoder();
- co.to_ndnb(enc);
-
- return enc.getReducedOstream();
-}
-
-function encodeForwardingEntry(co){
- var enc = new BinaryXMLEncoder();
-
- co.to_ndnb(enc);
-
- var bytes = enc.getReducedOstream();
-
- return bytes;
-
-
-}
-
-
-
-function decodeHexFaceInstance(result){
-
- var numbers = DataUtils.toNumbers(result);
-
-
- var decoder = new BinaryXMLDecoder(numbers);
-
- if(LOG>3)console.log('DECODING HEX FACE INSTANCE \n'+numbers);
-
- var faceInstance = new FaceInstance();
-
- faceInstance.from_ndnb(decoder);
-
- return faceInstance;
-
-}
-
-
-
-function decodeHexInterest(result){
- var numbers = DataUtils.toNumbers(result);
-
- var decoder = new BinaryXMLDecoder(numbers);
-
- if(LOG>3)console.log('DECODING HEX INTERST \n'+numbers);
-
- var interest = new Interest();
-
- interest.from_ndnb(decoder);
-
- return interest;
-
-}
-
-
-
-function decodeHexContentObject(result){
- var numbers = DataUtils.toNumbers(result);
-
- var decoder = new BinaryXMLDecoder(numbers);
-
- if(LOG>3)console.log('DECODED HEX CONTENT OBJECT \n'+numbers);
-
- var co = new ContentObject();
-
- co.from_ndnb(decoder);
-
- return co;
-
-}
-
-
-
-function decodeHexForwardingEntry(result){
- var numbers = DataUtils.toNumbers(result);
-
- var decoder = new BinaryXMLDecoder(numbers);
-
- if(LOG>3)console.log('DECODED HEX FORWARDING ENTRY \n'+numbers);
-
- var forwardingEntry = new ForwardingEntry();
-
- forwardingEntry.from_ndnb(decoder);
-
- return forwardingEntry;
-
-}
-
-/*
- * Decode the Uint8Array which holds SubjectPublicKeyInfo and return an RSAKey.
- */
-function decodeSubjectPublicKeyInfo(array) {
- var hex = DataUtils.toHex(array).toLowerCase();
- var a = _x509_getPublicKeyHexArrayFromCertHex(hex, _x509_getSubjectPublicKeyPosFromCertHex(hex, 0));
- var rsaKey = new RSAKey();
- rsaKey.setPublic(a[0], a[1]);
- return rsaKey;
-}
-
-/* Return a user friendly HTML string with the contents of co.
- This also outputs to console.log.
- */
-function contentObjectToHtml(/* ContentObject */ co) {
- var output ="";
-
- if(co==-1)
- output+= "NO CONTENT FOUND"
- else if (co==-2)
- output+= "CONTENT NAME IS EMPTY"
- else{
- if(co.name!=null && co.name.components!=null){
- output+= "NAME: " + co.name.to_uri();
-
- output+= "<br />";
- output+= "<br />";
- }
-
- if(co.content !=null){
- output += "CONTENT(ASCII): "+ DataUtils.toString(co.content);
-
- output+= "<br />";
- output+= "<br />";
- }
- if(co.content !=null){
- output += "CONTENT(hex): "+ DataUtils.toHex(co.content);
-
- output+= "<br />";
- output+= "<br />";
- }
- if(co.signature !=null && co.signature.signature!=null){
- output += "SIGNATURE(hex): "+ DataUtils.toHex(co.signature.signature);
-
- output+= "<br />";
- output+= "<br />";
- }
- if(co.signedInfo !=null && co.signedInfo.publisher!=null && co.signedInfo.publisher.publisherPublicKeyDigest!=null){
- output += "Publisher Public Key Digest(hex): "+ DataUtils.toHex(co.signedInfo.publisher.publisherPublicKeyDigest);
-
- output+= "<br />";
- output+= "<br />";
- }
- if(co.signedInfo !=null && co.signedInfo.timestamp!=null){
- var d = new Date();
- d.setTime( co.signedInfo.timestamp.msec );
-
- var bytes = [217, 185, 12, 225, 217, 185, 12, 225];
-
- output += "TimeStamp: "+d;
- output+= "<br />";
- output += "TimeStamp(number): "+ co.signedInfo.timestamp.msec;
-
- output+= "<br />";
- }
- if(co.signedInfo !=null && co.signedInfo.finalBlockID!=null){
- output += "FinalBlockID: "+ DataUtils.toHex(co.signedInfo.finalBlockID);
- output+= "<br />";
- }
- if(co.signedInfo!=null && co.signedInfo.locator!=null && co.signedInfo.locator.certificate!=null){
- var certificateHex = DataUtils.toHex(co.signedInfo.locator.certificate).toLowerCase();
- var signature = DataUtils.toHex(co.signature.signature).toLowerCase();
- var input = DataUtils.toString(co.rawSignatureData);
-
- output += "Hex Certificate: "+ certificateHex ;
-
- output+= "<br />";
- output+= "<br />";
-
- var x509 = new X509();
- x509.readCertHex(certificateHex);
- output += "Public key (hex) modulus: " + x509.subjectPublicKeyRSA.n.toString(16) + "<br/>";
- output += "exponent: " + x509.subjectPublicKeyRSA.e.toString(16) + "<br/>";
- output += "<br/>";
-
- var result = x509.subjectPublicKeyRSA.verifyByteArray(co.rawSignatureData, null, signature);
- if(LOG>2) console.log('result is '+result);
-
- var n = x509.subjectPublicKeyRSA.n;
- var e = x509.subjectPublicKeyRSA.e;
-
- if(LOG>2) console.log('PUBLIC KEY n after is ');
- if(LOG>2) console.log(n);
-
- if(LOG>2) console.log('EXPONENT e after is ');
- if(LOG>2) console.log(e);
-
- if(result)
- output += 'SIGNATURE VALID';
- else
- output += 'SIGNATURE INVALID';
-
- //output += "VALID: "+ toHex(co.signedInfo.locator.publicKey);
-
- output+= "<br />";
- output+= "<br />";
-
- //if(LOG>4) console.log('str'[1]);
- }
- if(co.signedInfo!=null && co.signedInfo.locator!=null && co.signedInfo.locator.publicKey!=null){
- var publickeyHex = DataUtils.toHex(co.signedInfo.locator.publicKey).toLowerCase();
- var publickeyString = DataUtils.toString(co.signedInfo.locator.publicKey);
- var signature = DataUtils.toHex(co.signature.signature).toLowerCase();
- var input = DataUtils.toString(co.rawSignatureData);
-
- var wit = null;
- var witHex = "";
- if (co.signature.Witness != null) {
- wit = new Witness();
- wit.decode(co.signature.Witness);
- witHex = DataUtils.toHex(co.signature.Witness);
- }
-
- output += "Public key: " + publickeyHex;
-
- output+= "<br />";
- output+= "<br />";
-
- if(LOG>2) console.log(" ContentName + SignedInfo + Content = "+input);
- if(LOG>2) console.log(" PublicKeyHex = "+publickeyHex );
- if(LOG>2) console.log(" PublicKeyString = "+publickeyString );
-
- if(LOG>2) console.log(" Signature "+signature );
- if(LOG>2) console.log(" Witness "+witHex );
-
- if(LOG>2) console.log(" Signature NOW IS" );
-
- if(LOG>2) console.log(co.signature.signature);
-
- var rsakey = decodeSubjectPublicKeyInfo(co.signedInfo.locator.publicKey);
-
- output += "Public key (hex) modulus: " + rsakey.n.toString(16) + "<br/>";
- output += "exponent: " + rsakey.e.toString(16) + "<br/>";
- output += "<br/>";
-
- var result = rsakey.verifyByteArray(co.rawSignatureData, wit, signature);
- // var result = rsakey.verifyString(input, signature);
-
- if(LOG>2) console.log('PUBLIC KEY n after is ');
- if(LOG>2) console.log(rsakey.n);
-
- if(LOG>2) console.log('EXPONENT e after is ');
- if(LOG>2) console.log(rsakey.e);
-
- if(result)
- output += 'SIGNATURE VALID';
- else
- output += 'SIGNATURE INVALID';
-
- //output += "VALID: "+ toHex(co.signedInfo.locator.publicKey);
-
- output+= "<br />";
- output+= "<br />";
-
- //if(LOG>4) console.log('str'[1]);
- }
- }
-
- return output;
-}
-
-
-/**
- * @author: Meki Cheraoui
- * See COPYING for copyright and distribution information.
- */
-
-var KeyManager = function KeyManager(){
-
-
-//Certificate
-
-this.certificate = 'MIIBmzCCAQQCCQC32FyQa61S7jANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQDEwd'+
-
-'heGVsY2R2MB4XDTEyMDQyODIzNDQzN1oXDTEyMDUyODIzNDQzN1owEjEQMA4GA1'+
-
-'UEAxMHYXhlbGNkdjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4X0wp9goq'+
-
-'xuECxdULcr2IHr9Ih4Iaypg0Wy39URIup8/CLzQmdsh3RYqd55hqonu5VTTpH3i'+
-
-'MLx6xZDVJAZ8OJi7pvXcQ2C4Re2kjL2c8SanI0RfDhlS1zJadfr1VhRPmpivcYa'+
-
-'wJ4aFuOLAi+qHFxtN7lhcGCgpW1OV60oXd58CAwEAATANBgkqhkiG9w0BAQUFAA'+
-
-'OBgQDLOrA1fXzSrpftUB5Ro6DigX1Bjkf7F5Bkd69hSVp+jYeJFBBlsILQAfSxU'+
-
-'ZPQtD+2Yc3iCmSYNyxqu9PcufDRJlnvB7PG29+L3y9lR37tetzUV9eTscJ7rdp8'+
-
-'Wt6AzpW32IJ/54yKNfP7S6ZIoIG+LP6EIxq6s8K1MXRt8uBJKw==';
-
-
-//this.publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDhfTCn2CirG4QLF1QtyvYgev0iHghrKmDRbLf1REi6nz8IvNCZ2yHdFip3nmGqie7lVNOkfeIwvHrFkNUkBnw4mLum9dxDYLhF7aSMvZzxJqcjRF8OGVLXMlp1+vVWFE+amK9xhrAnhoW44sCL6ocXG03uWFwYKClbU5XrShd3nwIDAQAB';
-this.publicKey ='30819F300D06092A864886F70D010101050003818D0030818902818100E17D30A7D828AB1B840B17542DCAF6207AFD221E086B2A60D16CB7F54448BA9F3F08BCD099DB21DD162A779E61AA89EEE554D3A47DE230BC7AC590D524067C3898BBA6F5DC4360B845EDA48CBD9CF126A723445F0E1952D7325A75FAF556144F9A98AF7186B0278685B8E2C08BEA87171B4DEE585C1828295B5395EB4A17779F0203010001';
-//Private Key
-
-this.privateKey ='MIICXQIBAAKBgQDhfTCn2CirG4QLF1QtyvYgev0iHghrKmDRbLf1REi6nz8IvNCZ2yHdFip3nmGqie7lVNOkfeIwvHrFkNUkBnw4mLum9dxDYLhF7aSMvZzxJqcjRF8OGVLXMlp1+vVWFE+amK9xhrAnhoW44sCL6ocXG03uWFwYKClbU5XrShd3nwIDAQABAoGAGkv6T6jC3WmhFZYL6CdCWvlc6gysmKrhjarrLTxgavtFY6R5g2ft5BXAsCCVbUkWxkIFSKqxpVNl0gKZCNGEzPDN6mHJOQI/h0rlxNIHAuGfoAbCzALnqmyZivhJAPGijAyKuU9tczsst5+Kpn+bn7ehzHQuj7iwJonS5WbojqECQQD851K8TpW2GrRizNgG4dx6orZxAaon/Jnl8lS7soXhllQty7qG+oDfzznmdMsiznCqEABzHUUKOVGE9RWPN3aRAkEA5D/w9N55d0ibnChFJlc8cUAoaqH+w+U3oQP2Lb6AZHJpLptN4y4b/uf5d4wYU5/i/gC7SSBH3wFhh9bjRLUDLwJAVOx8vN0Kqt7myfKNbCo19jxjVSlA8TKCn1Oznl/BU1I+rC4oUaEW25DjmX6IpAR8kq7S59ThVSCQPjxqY/A08QJBAIRaF2zGPITQk3r/VumemCvLWiRK/yG0noc9dtibqHOWbCtcXtOm/xDWjq+lis2i3ssOvYrvrv0/HcDY+Dv1An0CQQCLJtMsfSg4kvG/FRY5UMhtMuwo8ovYcMXt4Xv/LWaMhndD67b2UGawQCRqr5ghRTABWdDD/HuuMBjrkPsX0861';
-
-
-/*
- this.certificate =
- 'MIIBvTCCASYCCQD55fNzc0WF7TANBgkqhkiG9w0BAQUFADAjMQswCQYDVQQGEwJK'+
- 'UDEUMBIGA1UEChMLMDAtVEVTVC1SU0EwHhcNMTAwNTI4MDIwODUxWhcNMjAwNTI1'+
- 'MDIwODUxWjAjMQswCQYDVQQGEwJKUDEUMBIGA1UEChMLMDAtVEVTVC1SU0EwgZ8w'+
- 'DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANGEYXtfgDRlWUSDn3haY4NVVQiKI9Cz'+
- 'Thoua9+DxJuiseyzmBBe7Roh1RPqdvmtOHmEPbJ+kXZYhbozzPRbFGHCJyBfCLzQ'+
- 'fVos9/qUQ88u83b0SFA2MGmQWQAlRtLy66EkR4rDRwTj2DzR4EEXgEKpIvo8VBs/'+
- '3+sHLF3ESgAhAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAEZ6mXFFq3AzfaqWHmCy1'+
- 'ARjlauYAa8ZmUFnLm0emg9dkVBJ63aEqARhtok6bDQDzSJxiLpCEF6G4b/Nv/M/M'+
- 'LyhP+OoOTmETMegAVQMq71choVJyOFE5BtQa6M/lCHEOya5QUfoRF2HF9EjRF44K'+
- '3OK+u3ivTSj3zwjtpudY5Xo=';
-
- this.privateKey =
- 'MIICWwIBAAKBgQDRhGF7X4A0ZVlEg594WmODVVUIiiPQs04aLmvfg8SborHss5gQ'+
- 'Xu0aIdUT6nb5rTh5hD2yfpF2WIW6M8z0WxRhwicgXwi80H1aLPf6lEPPLvN29EhQ'+
- 'NjBpkFkAJUbS8uuhJEeKw0cE49g80eBBF4BCqSL6PFQbP9/rByxdxEoAIQIDAQAB'+
- 'AoGAA9/q3Zk6ib2GFRpKDLO/O2KMnAfR+b4XJ6zMGeoZ7Lbpi3MW0Nawk9ckVaX0'+
- 'ZVGqxbSIX5Cvp/yjHHpww+QbUFrw/gCjLiiYjM9E8C3uAF5AKJ0r4GBPl4u8K4bp'+
- 'bXeSxSB60/wPQFiQAJVcA5xhZVzqNuF3EjuKdHsw+dk+dPECQQDubX/lVGFgD/xY'+
- 'uchz56Yc7VHX+58BUkNSewSzwJRbcueqknXRWwj97SXqpnYfKqZq78dnEF10SWsr'+
- '/NMKi+7XAkEA4PVqDv/OZAbWr4syXZNv/Mpl4r5suzYMMUD9U8B2JIRnrhmGZPzL'+
- 'x23N9J4hEJ+Xh8tSKVc80jOkrvGlSv+BxwJAaTOtjA3YTV+gU7Hdza53sCnSw/8F'+
- 'YLrgc6NOJtYhX9xqdevbyn1lkU0zPr8mPYg/F84m6MXixm2iuSz8HZoyzwJARi2p'+
- 'aYZ5/5B2lwroqnKdZBJMGKFpUDn7Mb5hiSgocxnvMkv6NjT66Xsi3iYakJII9q8C'+
- 'Ma1qZvT/cigmdbAh7wJAQNXyoizuGEltiSaBXx4H29EdXNYWDJ9SS5f070BRbAIl'+
- 'dqRh3rcNvpY6BKJqFapda1DjdcncZECMizT/GMrc1w==';
-
- */
-};
-
-
-KeyManager.prototype.verify = function verify(message,signature){
-
- var input = message;
-
- var _PEM_X509CERT_STRING_ = this.certificate;
-
- var x509 = new X509();
-
- x509.readCertPEM(_PEM_X509CERT_STRING_);
-
- var result = x509.subjectPublicKeyRSA.verifyString(input, signature);
-
- return result;
-};
-
-KeyManager.prototype.sign= function sign(message){
-
- var input = message;
-
- var _PEM_PRIVATE_KEY_STRING_ = this.privateKey;
-
- var rsa = new RSAKey();
-
- rsa.readPrivateKeyFromPEMString(_PEM_PRIVATE_KEY_STRING_);
-
- var hSig = rsa.signString(input, "sha256");
-
- return hSig;
-
-};
-
-
-
-var globalKeyManager = new KeyManager();
-//var KeyPair = { "public" : "PUBLIC KEY" , "private" : "PRIVATE KEY" };
-
-
-/**
- * @author: Wentao Shang
- * See COPYING for copyright and distribution information.
- */
-
-var MerklePath = function MerkelPath() {
- this.index = null; // int
- this.digestList = []; // array of hex string
-};
-
-var Witness = function Witness() {
- this.oid = null; // string
- this.path = new MerklePath(); // MerklePath
-};
-
-function parseOID(bytes, start, end) {
- var s, n = 0, bits = 0;
- for (var i = start; i < end; ++i) {
- var v = bytes[i];
- n = (n << 7) | (v & 0x7F);
- bits += 7;
- if (!(v & 0x80)) { // finished
- if (s == undefined)
- s = parseInt(n / 40) + "." + (n % 40);
- else
- s += "." + ((bits >= 31) ? "bigint" : n);
- n = bits = 0;
- }
- s += String.fromCharCode();
- }
- return s;
-}
-
-function parseInteger(bytes, start, end) {
- var n = 0;
- for (var i = start; i < end; ++i)
- n = (n << 8) | bytes[i];
- return n;
-}
-
-Witness.prototype.decode = function(/* Uint8Array */ witness) {
- /* The asn1.js decoder has some bug and
- * cannot decode certain kind of witness.
- * So we use an alternative (and dirty) hack
- * to read witness from byte streams
- * ------Wentao
- */
- /*
- var wit = DataUtils.toHex(witness).toLowerCase();
- try {
- var der = Hex.decode(wit);
- var asn1 = ASN1.decode(der);
- }
- catch (e) {
- console.log(e);
- console.log(wit);
- }
- //console.log(asn1.toPrettyString());
-
- this.oid = asn1.sub[0].sub[0].content(); // OID
- //console.log(this.oid);
- this.path.index = asn1.sub[1].sub[0].sub[0].content(); // index
- //console.log(this.path.index);
- for (i = 0; i < asn1.sub[1].sub[0].sub[1].sub.length; i++) {
- pos = asn1.sub[1].sub[0].sub[1].sub[i].stream.pos;
- str = wit.substring(2 * pos + 4, 2 * pos + 68);
- this.path.digestList.push(str); // digest hex string
- //console.log(str);
- }
- */
-
- // FIXME: Need to be fixed to support arbitrary ASN1 encoding,
- // But do we really nned that???? -------Wentao
-
- // The structure of Witness is fixed as follows:
- // SEQUENCE (2 elem)
- // SEQUENCE (1 elem)
- // OBJECT IDENTIFIER 1.2.840.113550.11.1.2.2
- // OCTET STRING (1 elem)
- // SEQUENCE (2 elem)
- // INTEGER index
- // SEQUENCE (n elem)
- // OCTET STRING(32 byte) 345FB4B5E9A1D2FF450ECA87EB87601683027A1A...
- // OCTET STRING(32 byte) DBCEE5B7A6C2B851B029324197DDBD9A655723DC...
- // OCTET STRING(32 byte) 4C79B2D256E4CD657A27F01DCB51AC3C56A24E71...
- // OCTET STRING(32 byte) 7F7FB169604A87EAC94378F0BDB4FC5D5899AB88...
- // ......
- // Hence we can follow this structure to extract witness fields at fixed level
- // Tag numbers for ASN1:
- // SEQUENCE 0x10
- // OCT STRING 0x04
- // INTEGER 0x02
- // OBJECT IDENTIFIER 0x06
- var i = 0;
- var step = 0; // count of sequence tag
- while (i < witness.length) {
- var len = 0;
-
- if (witness[i] == 0x30) {
- // Sequence (constructed)
- // There is no primitive sequence in Witness
- if ((witness[i + 1] & 0x80) != 0) {
- len = witness[i+1] & 0x7F;
- }
- step++;
- } else if (witness[i] == 0x06) {
- // Decode OID
- len = witness[i+1]; // XXX: OID will not be longer than 127 bytes
- this.oid = parseOID(witness, i + 2, i + 2 + len);
- //console.log(this.oid);
- } else if (witness[i] == 0x02) {
- // Decode node index
- len = witness[i+1]; // XXX: index will not be longer than 127 bytes
- this.path.index = parseInteger(witness, i + 2, i + 2 + len);
- //console.log(this.path.index);
- } else if (witness[i] == 0x04) {
- if ((witness[i + 1] & 0x80) != 0) {
- len = witness[i+1] & 0x7F;
- }
- if (step == 4) {
- // Start to decode digest hex string
- len = witness[i+1]; // XXX: digest hex should always be 32 bytes
- var str = DataUtils.toHex(witness.subarray(i + 2, i + 2 + len));
- this.path.digestList.push(str); // digest hex string
- //console.log(str);
- }
- }
- i = i + 2 + len;
- }
-};
-/*
- * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined
- * in FIPS 180-2
- * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
- * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
- * Distributed under the BSD License
- * See http://pajhome.org.uk/crypt/md5 for details.
- * Also http://anmar.eu.org/projects/jssha2/
- */
-
-/*
- * Configurable variables. You may need to tweak these to be compatible with
- * the server-side, but the defaults work in most cases.
- */
-var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
-var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
-
-/*
- * These are the functions you'll usually want to call
- * They take string arguments and return either hex or base-64 encoded strings
- */
-
-//@author axelcdv
-/**
- * Computes the Sha-256 hash of the given byte array
- * @param {byte[]}
- * @return the hex string corresponding to the Sha-256 hash of the byte array
- */
-function hex_sha256_from_bytes(byteArray){
- return rstr2hex(binb2rstr(binb_sha256( byteArray2binb(byteArray), byteArray.length * 8)));
-}
-
-function hex_sha256(s) { return rstr2hex(rstr_sha256(str2rstr_utf8(s))); }
-function b64_sha256(s) { return rstr2b64(rstr_sha256(str2rstr_utf8(s))); }
-function any_sha256(s, e) { return rstr2any(rstr_sha256(str2rstr_utf8(s)), e); }
-function hex_hmac_sha256(k, d)
- { return rstr2hex(rstr_hmac_sha256(str2rstr_utf8(k), str2rstr_utf8(d))); }
-function b64_hmac_sha256(k, d)
- { return rstr2b64(rstr_hmac_sha256(str2rstr_utf8(k), str2rstr_utf8(d))); }
-function any_hmac_sha256(k, d, e)
- { return rstr2any(rstr_hmac_sha256(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
-
-
-/*
- function hex_sha256(s) { return rstr2hex(rstr_sha256(s)); }
-function b64_sha256(s) { return rstr2b64(rstr_sha256(s)); }
-function any_sha256(s, e) { return rstr2any(rstr_sha256(s), e); }
-function hex_hmac_sha256(k, d)
- { return rstr2hex(rstr_hmac_sha256(str2rstr_utf8(k), d)); }
-function b64_hmac_sha256(k, d)
- { return rstr2b64(rstr_hmac_sha256(str2rstr_utf8(k), d)); }
-function any_hmac_sha256(k, d, e)
- { return rstr2any(rstr_hmac_sha256(str2rstr_utf8(k), d), e); }
-*/
-
-/*
- * Perform a simple self-test to see if the VM is working
- */
-function sha256_vm_test()
-{
- return hex_sha256("abc").toLowerCase() ==
- "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
-}
-
-/**
- * Calculate the sha256 of a raw string
- * @param s: the raw string
- */
-function rstr_sha256(s)
-{
- return binb2rstr(binb_sha256(rstr2binb(s), s.length * 8));
-}
-
-/**
- * Calculate the HMAC-sha256 of a key and some data (raw strings)
- */
-function rstr_hmac_sha256(key, data)
-{
- var bkey = rstr2binb(key);
- if(bkey.length > 16) bkey = binb_sha256(bkey, key.length * 8);
-
- var ipad = Array(16), opad = Array(16);
- for(var i = 0; i < 16; i++)
- {
- ipad[i] = bkey[i] ^ 0x36363636;
- opad[i] = bkey[i] ^ 0x5C5C5C5C;
- }
-
- var hash = binb_sha256(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
- return binb2rstr(binb_sha256(opad.concat(hash), 512 + 256));
-}
-
-/**
- * Convert a raw string to a hex string
- */
-function rstr2hex(input)
-{
- try { hexcase } catch(e) { hexcase=0; }
- var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
- var output = "";
- var x;
- for(var i = 0; i < input.length; i++)
- {
- x = input.charCodeAt(i);
- output += hex_tab.charAt((x >>> 4) & 0x0F)
- + hex_tab.charAt( x & 0x0F);
- }
- return output;
-}
-
-/*
- * Convert a raw string to a base-64 string
- */
-function rstr2b64(input)
-{
- try { b64pad } catch(e) { b64pad=''; }
- var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- var output = "";
- var len = input.length;
- for(var i = 0; i < len; i += 3)
- {
- var triplet = (input.charCodeAt(i) << 16)
- | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
- | (i + 2 < len ? input.charCodeAt(i+2) : 0);
- for(var j = 0; j < 4; j++)
- {
- if(i * 8 + j * 6 > input.length * 8) output += b64pad;
- else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
- }
- }
- return output;
-}
-
-/*
- * Convert a raw string to an arbitrary string encoding
- */
-function rstr2any(input, encoding)
-{
- var divisor = encoding.length;
- var remainders = Array();
- var i, q, x, quotient;
-
- /* Convert to an array of 16-bit big-endian values, forming the dividend */
- var dividend = Array(Math.ceil(input.length / 2));
- for(i = 0; i < dividend.length; i++)
- {
- dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
- }
-
- /*
- * Repeatedly perform a long division. The binary array forms the dividend,
- * the length of the encoding is the divisor. Once computed, the quotient
- * forms the dividend for the next step. We stop when the dividend is zero.
- * All remainders are stored for later use.
- */
- while(dividend.length > 0)
- {
- quotient = Array();
- x = 0;
- for(i = 0; i < dividend.length; i++)
- {
- x = (x << 16) + dividend[i];
- q = Math.floor(x / divisor);
- x -= q * divisor;
- if(quotient.length > 0 || q > 0)
- quotient[quotient.length] = q;
- }
- remainders[remainders.length] = x;
- dividend = quotient;
- }
-
- /* Convert the remainders to the output string */
- var output = "";
- for(i = remainders.length - 1; i >= 0; i--)
- output += encoding.charAt(remainders[i]);
-
- /* Append leading zero equivalents */
- var full_length = Math.ceil(input.length * 8 /
- (Math.log(encoding.length) / Math.log(2)))
- for(i = output.length; i < full_length; i++)
- output = encoding[0] + output;
-
- return output;
-}
-
-/*
- * Encode a string as utf-8.
- * For efficiency, this assumes the input is valid utf-16.
- */
-function str2rstr_utf8(input)
-{
- var output = "";
- var i = -1;
- var x, y;
-
- while(++i < input.length)
- {
- /* Decode utf-16 surrogate pairs */
- x = input.charCodeAt(i);
- y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
- if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
- {
- x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
- i++;
- }
-
- /* Encode output as utf-8 */
- if(x <= 0x7F)
- output += String.fromCharCode(x);
- else if(x <= 0x7FF)
- output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
- 0x80 | ( x & 0x3F));
- else if(x <= 0xFFFF)
- output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
- 0x80 | ((x >>> 6 ) & 0x3F),
- 0x80 | ( x & 0x3F));
- else if(x <= 0x1FFFFF)
- output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
- 0x80 | ((x >>> 12) & 0x3F),
- 0x80 | ((x >>> 6 ) & 0x3F),
- 0x80 | ( x & 0x3F));
- }
- return output;
-}
-
-/*
- * Encode a string as utf-16
- */
-function str2rstr_utf16le(input)
-{
- var output = "";
- for(var i = 0; i < input.length; i++)
- output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
- (input.charCodeAt(i) >>> 8) & 0xFF);
- return output;
-}
-
-function str2rstr_utf16be(input)
-{
- var output = "";
- for(var i = 0; i < input.length; i++)
- output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
- input.charCodeAt(i) & 0xFF);
- return output;
-}
-
-/**
- * Convert a raw string to an array of big-endian words
- * Characters >255 have their high-byte silently ignored.
- */
-function rstr2binb(input)
-{
- //console.log('Raw string comming is '+input);
- var output = Array(input.length >> 2);
- /* JavaScript automatically zeroizes a new array.
- for(var i = 0; i < output.length; i++)
- output[i] = 0;
- */
- for(var i = 0; i < input.length * 8; i += 8)
- output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
- return output;
-}
-
-/**
- * @author axelcdv
- * Convert a byte array to an array of big-endian words
- * @param {byte[]} input
- * @return the array of big-endian words
- */
-function byteArray2binb(input){
- //console.log("Byte array coming is " + input);
- var output = Array(input.length >> 2);
- /* JavaScript automatically zeroizes a new array.
- for(var i = 0; i < output.length; i++)
- output[i] = 0;
- */
- for(var i = 0; i < input.length * 8; i += 8)
- output[i>>5] |= (input[i / 8] & 0xFF) << (24 - i % 32);
- return output;
-}
-
-/*
- * Convert an array of big-endian words to a string
- */
-function binb2rstr(input)
-{
- var output = "";
- for(var i = 0; i < input.length * 32; i += 8)
- output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
- return output;
-}
-
-/*
- * Main sha256 function, with its support functions
- */
-function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));}
-function sha256_R (X, n) {return ( X >>> n );}
-function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
-function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
-function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));}
-function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));}
-function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));}
-function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));}
-function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));}
-function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));}
-function sha256_Gamma0512(x) {return (sha256_S(x, 1) ^ sha256_S(x, 8) ^ sha256_R(x, 7));}
-function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}
-
-var sha256_K = new Array
-(
- 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
- -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
- 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
- 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
- -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
- 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
- 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
- -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
- 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
- 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
- -1866530822, -1538233109, -1090935817, -965641998
-);
-
-function binb_sha256(m, l)
-{
- var HASH = new Array(1779033703, -1150833019, 1013904242, -1521486534,
- 1359893119, -1694144372, 528734635, 1541459225);
- var W = new Array(64);
-
- /* append padding */
- m[l >> 5] |= 0x80 << (24 - l % 32);
- m[((l + 64 >> 9) << 4) + 15] = l;
-
- for(var offset = 0; offset < m.length; offset += 16)
- processBlock_sha256(m, offset, HASH, W);
-
- return HASH;
-}
-
-/*
- * Process a block of 16 4-byte words in m starting at offset and update HASH.
- * offset must be a multiple of 16 and less than m.length. W is a scratchpad Array(64).
- */
-function processBlock_sha256(m, offset, HASH, W) {
- var a, b, c, d, e, f, g, h;
- var j, T1, T2;
-
- a = HASH[0];
- b = HASH[1];
- c = HASH[2];
- d = HASH[3];
- e = HASH[4];
- f = HASH[5];
- g = HASH[6];
- h = HASH[7];
-
- for(j = 0; j < 64; j++)
- {
- if (j < 16) W[j] = m[j + offset];
- else W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
- sha256_Gamma0256(W[j - 15])), W[j - 16]);
-
- T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
- sha256_K[j]), W[j]);
- T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
- h = g;
- g = f;
- f = e;
- e = safe_add(d, T1);
- d = c;
- c = b;
- b = a;
- a = safe_add(T1, T2);
- }
-
- HASH[0] = safe_add(a, HASH[0]);
- HASH[1] = safe_add(b, HASH[1]);
- HASH[2] = safe_add(c, HASH[2]);
- HASH[3] = safe_add(d, HASH[3]);
- HASH[4] = safe_add(e, HASH[4]);
- HASH[5] = safe_add(f, HASH[5]);
- HASH[6] = safe_add(g, HASH[6]);
- HASH[7] = safe_add(h, HASH[7]);
-}
-
-function safe_add (x, y)
-{
- var lsw = (x & 0xFFFF) + (y & 0xFFFF);
- var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
- return (msw << 16) | (lsw & 0xFFFF);
-}
-
-/*
- * Create a Sha256, call update(data) multiple times, then call finalize().
- */
-var Sha256 = function Sha256() {
- this.W = new Array(64);
- this.hash = new Array(1779033703, -1150833019, 1013904242, -1521486534,
- 1359893119, -1694144372, 528734635, 1541459225);
- this.nTotalBytes = 0;
- this.buffer = new Uint8Array(16 * 4);
- this.nBufferBytes = 0;
-}
-
-/*
- * Update the hash with data, which is Uint8Array.
- */
-Sha256.prototype.update = function(data) {
- this.nTotalBytes += data.length;
-
- if (this.nBufferBytes > 0) {
- // Fill up the buffer and process it first.
- var bytesNeeded = this.buffer.length - this.nBufferBytes;
- if (data.length < bytesNeeded) {
- this.buffer.set(data, this.nBufferBytes);
- this.nBufferBytes += data.length;
- return;
- }
- else {
- this.buffer.set(data.subarray(0, bytesNeeded), this.nBufferBytes);
- processBlock_sha256(byteArray2binb(this.buffer), 0, this.hash, this.W);
- this.nBufferBytes = 0;
- // Consume the bytes from data.
- data = data.subarray(bytesNeeded, data.length);
- if (data.length == 0)
- return;
- }
- }
-
- // 2^6 is 16 * 4.
- var nBlocks = data.length >> 6;
- if (nBlocks > 0) {
- var nBytes = nBlocks * 16 * 4;
- var m = byteArray2binb(data.subarray(0, nBytes));
- for(var offset = 0; offset < m.length; offset += 16)
- processBlock_sha256(m, offset, this.hash, this.W);
-
- data = data.subarray(nBytes, data.length);
- }
-
- if (data.length > 0) {
- // Save the remainder in the buffer.
- this.buffer.set(data);
- this.nBufferBytes = data.length;
- }
-}
-
-/*
- * Finalize the hash and return the result as Uint8Array.
- * Only call this once. Return values on subsequent calls are undefined.
- */
-Sha256.prototype.finalize = function() {
- var m = byteArray2binb(this.buffer.subarray(0, this.nBufferBytes));
- /* append padding */
- var l = this.nBufferBytes * 8;
- m[l >> 5] |= 0x80 << (24 - l % 32);
- m[((l + 64 >> 9) << 4) + 15] = this.nTotalBytes * 8;
-
- for(var offset = 0; offset < m.length; offset += 16)
- processBlock_sha256(m, offset, this.hash, this.W);
-
- return Sha256.binb2Uint8Array(this.hash);
-}
-
-/*
- * Convert an array of big-endian words to Uint8Array.
- */
-Sha256.binb2Uint8Array = function(input)
-{
- var output = new Uint8Array(input.length * 4);
- var iOutput = 0;
- for (var i = 0; i < input.length * 32; i += 8)
- output[iOutput++] = (input[i>>5] >>> (24 - i % 32)) & 0xFF;
- return output;
-}
var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64pad="=";
@@ -5031,7 +1693,7 @@
c = parseInt(h.substring(i,i+2),16);
ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
}
- while((ret.length & 3) > 0) ret += b64pad;
+ if (b64pad) while((ret.length & 3) > 0) ret += b64pad;
return ret;
}
@@ -5041,9 +1703,10 @@
var i;
var k = 0; // b64 state, 0-3
var slop;
+ var v;
for(i = 0; i < s.length; ++i) {
if(s.charAt(i) == b64pad) break;
- var v = b64map.indexOf(s.charAt(i));
+ v = b64map.indexOf(s.charAt(i));
if(v < 0) continue;
if(k == 0) {
ret += int2char(v >> 2);
@@ -5083,10 +1746,137 @@
}
return a;
}
+
+exports.b64tohex = b64tohex;
+exports.b64toBA = b64toBA;
+exports.hex2b64 = hex2b64;
+
+module.exports = exports;
+/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
+ */
+// prng4.js - uses Arcfour as a PRNG
+
+function Arcfour() {
+ this.i = 0;
+ this.j = 0;
+ this.S = new Array();
+}
+
+// Initialize arcfour context from key, an array of ints, each from [0..255]
+function ARC4init(key) {
+ var i, j, t;
+ for(i = 0; i < 256; ++i)
+ this.S[i] = i;
+ j = 0;
+ for(i = 0; i < 256; ++i) {
+ j = (j + this.S[i] + key[i % key.length]) & 255;
+ t = this.S[i];
+ this.S[i] = this.S[j];
+ this.S[j] = t;
+ }
+ this.i = 0;
+ this.j = 0;
+}
+
+function ARC4next() {
+ var t;
+ this.i = (this.i + 1) & 255;
+ this.j = (this.j + this.S[this.i]) & 255;
+ t = this.S[this.i];
+ this.S[this.i] = this.S[this.j];
+ this.S[this.j] = t;
+ return this.S[(t + this.S[this.i]) & 255];
+}
+
+Arcfour.prototype.init = ARC4init;
+Arcfour.prototype.next = ARC4next;
+
+// Plug in your RNG constructor here
+function prng_newstate() {
+ return new Arcfour();
+}
+
+// Pool size must be a multiple of 4 and greater than 32.
+// An array of bytes the size of the pool will be passed to init()
+var rng_psize = 256;
+/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
+ */
+// Random number generator - requires a PRNG backend, e.g. prng4.js
+
+// For best results, put code like
+// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'>
+// in your main HTML document.
+
+var rng_state;
+var rng_pool;
+var rng_pptr;
+
+// Mix in a 32-bit integer into the pool
+function rng_seed_int(x) {
+ rng_pool[rng_pptr++] ^= x & 255;
+ rng_pool[rng_pptr++] ^= (x >> 8) & 255;
+ rng_pool[rng_pptr++] ^= (x >> 16) & 255;
+ rng_pool[rng_pptr++] ^= (x >> 24) & 255;
+ if(rng_pptr >= rng_psize) rng_pptr -= rng_psize;
+}
+
+// Mix in the current time (w/milliseconds) into the pool
+function rng_seed_time() {
+ rng_seed_int(new Date().getTime());
+}
+
+// Initialize the pool with junk if needed.
+if(rng_pool == null) {
+ rng_pool = new Array();
+ rng_pptr = 0;
+ var t;
+ if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) {
+ // Extract entropy (256 bits) from NS4 RNG if available
+ var z = window.crypto.random(32);
+ for(t = 0; t < z.length; ++t)
+ rng_pool[rng_pptr++] = z.charCodeAt(t) & 255;
+ }
+ while(rng_pptr < rng_psize) { // extract some randomness from Math.random()
+ t = Math.floor(65536 * Math.random());
+ rng_pool[rng_pptr++] = t >>> 8;
+ rng_pool[rng_pptr++] = t & 255;
+ }
+ rng_pptr = 0;
+ rng_seed_time();
+ //rng_seed_int(window.screenX);
+ //rng_seed_int(window.screenY);
+}
+
+function rng_get_byte() {
+ if(rng_state == null) {
+ rng_seed_time();
+ rng_state = prng_newstate();
+ rng_state.init(rng_pool);
+ for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr)
+ rng_pool[rng_pptr] = 0;
+ rng_pptr = 0;
+ //rng_pool = null;
+ }
+ // TODO: allow reseeding after first request
+ return rng_state.next();
+}
+
+function rng_get_bytes(ba) {
+ var i;
+ for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte();
+}
+
+function SecureRandom() {}
+
+SecureRandom.prototype.nextBytes = rng_get_bytes;
+/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
+ */
// Depends on jsbn.js and rng.js
// Version 1.1: support utf-8 encoding in pkcs1pad2
+var intShim = require("jsbn");
+
// convert a (hex) string to a bignum object
function parseBigInt(str,r) {
return new BigInteger(str,r);
@@ -5109,11 +1899,7 @@
return b.toString(16);
}
-/**
- * PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
- * @param s: the string to encode
- * @param n: the size in byte
- */
+// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
function pkcs1pad2(s,n) {
if(n < s.length + 11) { // TODO: fix for utf-8
alert("Message too long for RSA");
@@ -5149,10 +1935,65 @@
return new BigInteger(ba);
}
-/**
- * "empty" RSA key constructor
- * @returns {RSAKey}
- */
+// PKCS#1 (OAEP) mask generation function
+function oaep_mgf1_arr(seed, len, hash)
+{
+ var mask = '', i = 0;
+
+ while (mask.length < len)
+ {
+ mask += hash(String.fromCharCode.apply(String, seed.concat([
+ (i & 0xff000000) >> 24,
+ (i & 0x00ff0000) >> 16,
+ (i & 0x0000ff00) >> 8,
+ i & 0x000000ff])));
+ i += 1;
+ }
+
+ return mask;
+}
+
+var SHA1_SIZE = 20;
+
+// PKCS#1 (OAEP) pad input string s to n bytes, and return a bigint
+function oaep_pad(s, n, hash)
+{
+ if (s.length + 2 * SHA1_SIZE + 2 > n)
+ {
+ throw "Message too long for RSA";
+ }
+
+ var PS = '', i;
+
+ for (i = 0; i < n - s.length - 2 * SHA1_SIZE - 2; i += 1)
+ {
+ PS += '\x00';
+ }
+
+ var DB = rstr_sha1('') + PS + '\x01' + s;
+ var seed = new Array(SHA1_SIZE);
+ new SecureRandom().nextBytes(seed);
+
+ var dbMask = oaep_mgf1_arr(seed, DB.length, hash || rstr_sha1);
+ var maskedDB = [];
+
+ for (i = 0; i < DB.length; i += 1)
+ {
+ maskedDB[i] = DB.charCodeAt(i) ^ dbMask.charCodeAt(i);
+ }
+
+ var seedMask = oaep_mgf1_arr(maskedDB, seed.length, rstr_sha1);
+ var maskedSeed = [0];
+
+ for (i = 0; i < seed.length; i += 1)
+ {
+ maskedSeed[i + 1] = seed[i] ^ seedMask.charCodeAt(i);
+ }
+
+ return new BigInteger(maskedSeed.concat(maskedDB));
+}
+
+// "empty" RSA key constructor
function RSAKey() {
this.n = null;
this.e = 0;
@@ -5164,14 +2005,15 @@
this.coeff = null;
}
-/**
- * Set the public key fields N and e from hex strings
- * @param N
- * @param E
- * @returns {RSASetPublic}
- */
+// Set the public key fields N and e from hex strings
function RSASetPublic(N,E) {
- if(N != null && E != null && N.length > 0 && E.length > 0) {
+ this.isPublic = true;
+ if (typeof N !== "string")
+ {
+ this.n = N;
+ this.e = E;
+ }
+ else if(N != null && E != null && N.length > 0 && E.length > 0) {
this.n = parseBigInt(N,16);
this.e = parseInt(E,16);
}
@@ -5179,18 +2021,12 @@
alert("Invalid RSA public key");
}
-/**
- * Perform raw public operation on "x": return x^e (mod n)
- * @param x
- * @returns x^e (mod n)
- */
+// Perform raw public operation on "x": return x^e (mod n)
function RSADoPublic(x) {
return x.modPowInt(this.e, this.n);
}
-/**
- * Return the PKCS#1 RSA encryption of "text" as an even-length hex string
- */
+// Return the PKCS#1 RSA encryption of "text" as an even-length hex string
function RSAEncrypt(text) {
var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
if(m == null) return null;
@@ -5200,6 +2036,16 @@
if((h.length & 1) == 0) return h; else return "0" + h;
}
+// Return the PKCS#1 OAEP RSA encryption of "text" as an even-length hex string
+function RSAEncryptOAEP(text, hash) {
+ var m = oaep_pad(text, (this.n.bitLength()+7)>>3, hash);
+ if(m == null) return null;
+ var c = this.doPublic(m);
+ if(c == null) return null;
+ var h = c.toString(16);
+ if((h.length & 1) == 0) return h; else return "0" + h;
+}
+
// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
//function RSAEncryptB64(text) {
// var h = this.encrypt(text);
@@ -5212,11 +2058,23 @@
// public
RSAKey.prototype.setPublic = RSASetPublic;
RSAKey.prototype.encrypt = RSAEncrypt;
+RSAKey.prototype.encryptOAEP = RSAEncryptOAEP;
//RSAKey.prototype.encrypt_b64 = RSAEncryptB64;
+
+RSAKey.prototype.type = "RSA";
+
+exports.RSAKey = RSAKey;
+module.exports = exports;
+/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
+ */
// Depends on rsa.js and jsbn2.js
// Version 1.1: support utf-8 decoding in pkcs1unpad2
+var intShim = require("jsbn");
+var BigInteger = intShim.BigInteger ? intShim.BigInteger : intShim ;
+var RSAKey = require('./rsa.js').RSAKey;
+
// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext
function pkcs1unpad2(d,n) {
var b = d.toByteArray();
@@ -5245,9 +2103,101 @@
return ret;
}
+// PKCS#1 (OAEP) mask generation function
+function oaep_mgf1_str(seed, len, hash)
+{
+ var mask = '', i = 0;
+
+ while (mask.length < len)
+ {
+ mask += hash(seed + String.fromCharCode.apply(String, [
+ (i & 0xff000000) >> 24,
+ (i & 0x00ff0000) >> 16,
+ (i & 0x0000ff00) >> 8,
+ i & 0x000000ff]));
+ i += 1;
+ }
+
+ return mask;
+}
+
+var SHA1_SIZE = 20;
+
+// Undo PKCS#1 (OAEP) padding and, if valid, return the plaintext
+function oaep_unpad(d, n, hash)
+{
+ d = d.toByteArray();
+
+ var i;
+
+ for (i = 0; i < d.length; i += 1)
+ {
+ d[i] &= 0xff;
+ }
+
+ while (d.length < n)
+ {
+ d.unshift(0);
+ }
+
+ d = String.fromCharCode.apply(String, d);
+
+ if (d.length < 2 * SHA1_SIZE + 2)
+ {
+ throw "Cipher too short";
+ }
+
+ var maskedSeed = d.substr(1, SHA1_SIZE)
+ var maskedDB = d.substr(SHA1_SIZE + 1);
+
+ var seedMask = oaep_mgf1_str(maskedDB, SHA1_SIZE, hash || rstr_sha1);
+ var seed = [], i;
+
+ for (i = 0; i < maskedSeed.length; i += 1)
+ {
+ seed[i] = maskedSeed.charCodeAt(i) ^ seedMask.charCodeAt(i);
+ }
+
+ var dbMask = oaep_mgf1_str(String.fromCharCode.apply(String, seed),
+ d.length - SHA1_SIZE, rstr_sha1);
+
+ var DB = [];
+
+ for (i = 0; i < maskedDB.length; i += 1)
+ {
+ DB[i] = maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i);
+ }
+
+ DB = String.fromCharCode.apply(String, DB);
+
+ if (DB.substr(0, SHA1_SIZE) !== rstr_sha1(''))
+ {
+ throw "Hash mismatch";
+ }
+
+ DB = DB.substr(SHA1_SIZE);
+
+ var first_one = DB.indexOf('\x01');
+ var last_zero = (first_one != -1) ? DB.substr(0, first_one).lastIndexOf('\x00') : -1;
+
+ if (last_zero + 1 != first_one)
+ {
+ throw "Malformed data";
+ }
+
+ return DB.substr(first_one + 1);
+}
+
// Set the private key fields N, e, and d from hex strings
function RSASetPrivate(N,E,D) {
- if(N != null && E != null && N.length > 0 && E.length > 0) {
+ this.isPrivate = true;
+ if (typeof N !== "string")
+ {
+ this.n = N;
+ this.e = E;
+ this.d = D;
+ }
+ else if(N != null && E != null && N.length > 0 && E.length > 0) {
this.n = parseBigInt(N,16);
this.e = parseInt(E,16);
this.d = parseBigInt(D,16);
@@ -5258,7 +2208,13 @@
// Set the private key fields N, e, d and CRT params from hex strings
function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) {
- if(N != null && E != null && N.length > 0 && E.length > 0) {
+ this.isPrivate = true;
+ if (N == null) throw "RSASetPrivateEx N == null";
+ if (E == null) throw "RSASetPrivateEx E == null";
+ if (N.length == 0) throw "RSASetPrivateEx N.length == 0";
+ if (E.length == 0) throw "RSASetPrivateEx E.length == 0";
+
+ if (N != null && E != null && N.length > 0 && E.length > 0) {
this.n = parseBigInt(N,16);
this.e = parseInt(E,16);
this.d = parseBigInt(D,16);
@@ -5267,14 +2223,12 @@
this.dmp1 = parseBigInt(DP,16);
this.dmq1 = parseBigInt(DQ,16);
this.coeff = parseBigInt(C,16);
+ } else {
+ alert("Invalid RSA private key in RSASetPrivateEx");
}
- else
- alert("Invalid RSA private key");
}
-/**
- * Generate a new random private key B bits long, using public expt E
- */
+// Generate a new random private key B bits long, using public expt E
function RSAGenerate(B,E) {
var rng = new SecureRandom();
var qs = B>>1;
@@ -5306,12 +2260,10 @@
break;
}
}
+ this.isPrivate = true;
}
-/**
- * Perform raw private operation on "x": return x^d (mod n)
- * @return x^d (mod n)
- */
+// Perform raw private operation on "x": return x^d (mod n)
function RSADoPrivate(x) {
if(this.p == null || this.q == null)
return x.modPow(this.d, this.n);
@@ -5338,6 +2290,15 @@
return pkcs1unpad2(m, (this.n.bitLength()+7)>>3);
}
+// Return the PKCS#1 OAEP RSA decryption of "ctext".
+// "ctext" is an even-length hex string and the output is a plain string.
+function RSADecryptOAEP(ctext, hash) {
+ var c = parseBigInt(ctext, 16);
+ var m = this.doPrivate(c);
+ if(m == null) return null;
+ return oaep_unpad(m, (this.n.bitLength()+7)>>3, hash);
+}
+
// Return the PKCS#1 RSA decryption of "ctext".
// "ctext" is a Base64-encoded string and the output is a plain string.
//function RSAB64Decrypt(ctext) {
@@ -5353,16 +2314,1256 @@
RSAKey.prototype.setPrivateEx = RSASetPrivateEx;
RSAKey.prototype.generate = RSAGenerate;
RSAKey.prototype.decrypt = RSADecrypt;
+RSAKey.prototype.decryptOAEP = RSADecryptOAEP;
//RSAKey.prototype.b64_decrypt = RSAB64Decrypt;
+
+exports.RSAKey = RSAKey;
+module.exports = exports;
+/*! crypto-1.1.7.js (c) 2013-2015 Kenji Urushima | kjur.github.com/jsrsasign/license
+ */
+/*
+ * crypto.js - Cryptographic Algorithm Provider class
+ *
+ * Copyright (c) 2013-2015 Kenji Urushima (kenji.urushima@gmail.com)
+ *
+ * This software is licensed under the terms of the MIT License.
+ * http://kjur.github.com/jsrsasign/license
+ *
+ * The above copyright and license notice shall be
+ * included in all copies or substantial portions of the Software.
+ */
+
+/**
+ * @fileOverview
+ * @name crypto-1.1.js
+ * @author Kenji Urushima kenji.urushima@gmail.com
+ * @version 1.1.7 (2015-Oct-11)
+ * @since jsrsasign 2.2
+ * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
+ */
+
+var CryptoJS = require('./sha256.js').CryptoJS
+ , intShim = require('jsbn');
+
+
+/**
+ * kjur's class library name space
+ * @name KJUR
+ * @namespace kjur's class library name space
+ */
+if (typeof KJUR == "undefined" || !KJUR) KJUR = {};
+/**
+ * kjur's cryptographic algorithm provider library name space
+ * <p>
+ * This namespace privides following crytpgrahic classes.
+ * <ul>
+ * <li>{@link KJUR.crypto.MessageDigest} - Java JCE(cryptograhic extension) style MessageDigest class</li>
+ * <li>{@link KJUR.crypto.Signature} - Java JCE(cryptograhic extension) style Signature class</li>
+ * <li>{@link KJUR.crypto.Util} - cryptographic utility functions and properties</li>
+ * </ul>
+ * NOTE: Please ignore method summary and document of this namespace. This caused by a bug of jsdoc2.
+ * </p>
+ * @name KJUR.crypto
+ * @namespace
+ */
+if (typeof KJUR.crypto == "undefined" || !KJUR.crypto) KJUR.crypto = {};
+
+/**
+ * static object for cryptographic function utilities
+ * @name KJUR.crypto.Util
+ * @class static object for cryptographic function utilities
+ * @property {Array} DIGESTINFOHEAD PKCS#1 DigestInfo heading hexadecimal bytes for each hash algorithms
+ * @property {Array} DEFAULTPROVIDER associative array of default provider name for each hash and signature algorithms
+ * @description
+ */
+KJUR.crypto.Util = new function() {
+ this.DIGESTINFOHEAD = {
+ 'sha1': "3021300906052b0e03021a05000414",
+ 'sha224': "302d300d06096086480165030402040500041c",
+ 'sha256': "3031300d060960864801650304020105000420",
+ 'sha384': "3041300d060960864801650304020205000430",
+ 'sha512': "3051300d060960864801650304020305000440",
+ 'md2': "3020300c06082a864886f70d020205000410",
+ 'md5': "3020300c06082a864886f70d020505000410",
+ 'ripemd160': "3021300906052b2403020105000414"
+ };
+
+ /*
+ * @since crypto 1.1.1
+ */
+ this.DEFAULTPROVIDER = {
+ 'md5': 'cryptojs',
+ 'sha1': 'cryptojs',
+ 'sha224': 'cryptojs',
+ 'sha256': 'cryptojs',
+ 'sha384': 'cryptojs',
+ 'sha512': 'cryptojs',
+ 'ripemd160': 'cryptojs',
+ 'hmacmd5': 'cryptojs',
+ 'hmacsha1': 'cryptojs',
+ 'hmacsha224': 'cryptojs',
+ 'hmacsha256': 'cryptojs',
+ 'hmacsha384': 'cryptojs',
+ 'hmacsha512': 'cryptojs',
+ 'hmacripemd160': 'cryptojs',
+
+ 'MD5withRSA': 'cryptojs/jsrsa',
+ 'SHA1withRSA': 'cryptojs/jsrsa',
+ 'SHA224withRSA': 'cryptojs/jsrsa',
+ 'SHA256withRSA': 'cryptojs/jsrsa',
+ 'SHA384withRSA': 'cryptojs/jsrsa',
+ 'SHA512withRSA': 'cryptojs/jsrsa',
+ 'RIPEMD160withRSA': 'cryptojs/jsrsa',
+
+ 'MD5withECDSA': 'cryptojs/jsrsa',
+ 'SHA1withECDSA': 'cryptojs/jsrsa',
+ 'SHA224withECDSA': 'cryptojs/jsrsa',
+ 'SHA256withECDSA': 'cryptojs/jsrsa',
+ 'SHA384withECDSA': 'cryptojs/jsrsa',
+ 'SHA512withECDSA': 'cryptojs/jsrsa',
+ 'RIPEMD160withECDSA': 'cryptojs/jsrsa',
+
+ 'SHA1withDSA': 'cryptojs/jsrsa',
+ 'SHA224withDSA': 'cryptojs/jsrsa',
+ 'SHA256withDSA': 'cryptojs/jsrsa',
+
+ 'MD5withRSAandMGF1': 'cryptojs/jsrsa',
+ 'SHA1withRSAandMGF1': 'cryptojs/jsrsa',
+ 'SHA224withRSAandMGF1': 'cryptojs/jsrsa',
+ 'SHA256withRSAandMGF1': 'cryptojs/jsrsa',
+ 'SHA384withRSAandMGF1': 'cryptojs/jsrsa',
+ 'SHA512withRSAandMGF1': 'cryptojs/jsrsa',
+ 'RIPEMD160withRSAandMGF1': 'cryptojs/jsrsa'
+ };
+
+ /*
+ * @since crypto 1.1.2
+ */
+ this.CRYPTOJSMESSAGEDIGESTNAME = {
+ 'md5': 'CryptoJS.algo.MD5',
+ 'sha1': 'CryptoJS.algo.SHA1',
+ 'sha224': 'CryptoJS.algo.SHA224',
+ 'sha256': 'CryptoJS.algo.SHA256',
+ 'sha384': 'CryptoJS.algo.SHA384',
+ 'sha512': 'CryptoJS.algo.SHA512',
+ 'ripemd160': 'CryptoJS.algo.RIPEMD160'
+ };
+
+ /**
+ * get hexadecimal DigestInfo
+ * @name getDigestInfoHex
+ * @memberOf KJUR.crypto.Util
+ * @function
+ * @param {String} hHash hexadecimal hash value
+ * @param {String} alg hash algorithm name (ex. 'sha1')
+ * @return {String} hexadecimal string DigestInfo ASN.1 structure
+ */
+ this.getDigestInfoHex = function(hHash, alg) {
+ if (typeof this.DIGESTINFOHEAD[alg] == "undefined")
+ throw "alg not supported in Util.DIGESTINFOHEAD: " + alg;
+ return this.DIGESTINFOHEAD[alg] + hHash;
+ };
+
+ /**
+ * get PKCS#1 padded hexadecimal DigestInfo
+ * @name getPaddedDigestInfoHex
+ * @memberOf KJUR.crypto.Util
+ * @function
+ * @param {String} hHash hexadecimal hash value of message to be signed
+ * @param {String} alg hash algorithm name (ex. 'sha1')
+ * @param {Integer} keySize key bit length (ex. 1024)
+ * @return {String} hexadecimal string of PKCS#1 padded DigestInfo
+ */
+ this.getPaddedDigestInfoHex = function(hHash, alg, keySize) {
+ var hDigestInfo = this.getDigestInfoHex(hHash, alg);
+ var pmStrLen = keySize / 4; // minimum PM length
+
+ if (hDigestInfo.length + 22 > pmStrLen) // len(0001+ff(*8)+00+hDigestInfo)=22
+ throw "key is too short for SigAlg: keylen=" + keySize + "," + alg;
+
+ var hHead = "0001";
+ var hTail = "00" + hDigestInfo;
+ var hMid = "";
+ var fLen = pmStrLen - hHead.length - hTail.length;
+ for (var i = 0; i < fLen; i += 2) {
+ hMid += "ff";
+ }
+ var hPaddedMessage = hHead + hMid + hTail;
+ return hPaddedMessage;
+ };
+
+ /**
+ * get hexadecimal hash of string with specified algorithm
+ * @name hashString
+ * @memberOf KJUR.crypto.Util
+ * @function
+ * @param {String} s input string to be hashed
+ * @param {String} alg hash algorithm name
+ * @return {String} hexadecimal string of hash value
+ * @since 1.1.1
+ */
+ this.hashString = function(s, alg) {
+ var md = new KJUR.crypto.MessageDigest({'alg': alg});
+ return md.digestString(s);
+ };
+
+ /**
+ * get hexadecimal hash of hexadecimal string with specified algorithm
+ * @name hashHex
+ * @memberOf KJUR.crypto.Util
+ * @function
+ * @param {String} sHex input hexadecimal string to be hashed
+ * @param {String} alg hash algorithm name
+ * @return {String} hexadecimal string of hash value
+ * @since 1.1.1
+ */
+ this.hashHex = function(sHex, alg) {
+ var md = new KJUR.crypto.MessageDigest({'alg': alg});
+ return md.digestHex(sHex);
+ };
+
+ /**
+ * get hexadecimal SHA1 hash of string
+ * @name sha1
+ * @memberOf KJUR.crypto.Util
+ * @function
+ * @param {String} s input string to be hashed
+ * @return {String} hexadecimal string of hash value
+ * @since 1.0.3
+ */
+ this.sha1 = function(s) {
+ var md = new KJUR.crypto.MessageDigest({'alg':'sha1', 'prov':'cryptojs'});
+ return md.digestString(s);
+ };
+
+ /**
+ * get hexadecimal SHA256 hash of string
+ * @name sha256
+ * @memberOf KJUR.crypto.Util
+ * @function
+ * @param {String} s input string to be hashed
+ * @return {String} hexadecimal string of hash value
+ * @since 1.0.3
+ */
+ this.sha256 = function(s) {
+ var md = new KJUR.crypto.MessageDigest({'alg':'sha256', 'prov':'cryptojs'});
+ return md.digestString(s);
+ };
+
+ this.sha256Hex = function(s) {
+ var md = new KJUR.crypto.MessageDigest({'alg':'sha256', 'prov':'cryptojs'});
+ return md.digestHex(s);
+ };
+
+ /**
+ * get hexadecimal SHA512 hash of string
+ * @name sha512
+ * @memberOf KJUR.crypto.Util
+ * @function
+ * @param {String} s input string to be hashed
+ * @return {String} hexadecimal string of hash value
+ * @since 1.0.3
+ */
+ this.sha512 = function(s) {
+ var md = new KJUR.crypto.MessageDigest({'alg':'sha512', 'prov':'cryptojs'});
+ return md.digestString(s);
+ };
+
+ this.sha512Hex = function(s) {
+ var md = new KJUR.crypto.MessageDigest({'alg':'sha512', 'prov':'cryptojs'});
+ return md.digestHex(s);
+ };
+
+ /**
+ * get hexadecimal MD5 hash of string
+ * @name md5
+ * @memberOf KJUR.crypto.Util
+ * @function
+ * @param {String} s input string to be hashed
+ * @return {String} hexadecimal string of hash value
+ * @since 1.0.3
+ */
+ this.md5 = function(s) {
+ var md = new KJUR.crypto.MessageDigest({'alg':'md5', 'prov':'cryptojs'});
+ return md.digestString(s);
+ };
+
+ /**
+ * get hexadecimal RIPEMD160 hash of string
+ * @name ripemd160
+ * @memberOf KJUR.crypto.Util
+ * @function
+ * @param {String} s input string to be hashed
+ * @return {String} hexadecimal string of hash value
+ * @since 1.0.3
+ */
+ this.ripemd160 = function(s) {
+ var md = new KJUR.crypto.MessageDigest({'alg':'ripemd160', 'prov':'cryptojs'});
+ return md.digestString(s);
+ };
+
+ /*
+ * @since 1.1.2
+ */
+ this.getCryptoJSMDByName = function(s) {
+
+ };
+};
+
+/**
+ * MessageDigest class which is very similar to java.security.MessageDigest class
+ * @name KJUR.crypto.MessageDigest
+ * @class MessageDigest class which is very similar to java.security.MessageDigest class
+ * @param {Array} params parameters for constructor
+ * @description
+ * <br/>
+ * Currently this supports following algorithm and providers combination:
+ * <ul>
+ * <li>md5 - cryptojs</li>
+ * <li>sha1 - cryptojs</li>
+ * <li>sha224 - cryptojs</li>
+ * <li>sha256 - cryptojs</li>
+ * <li>sha384 - cryptojs</li>
+ * <li>sha512 - cryptojs</li>
+ * <li>ripemd160 - cryptojs</li>
+ * <li>sha256 - sjcl (NEW from crypto.js 1.0.4)</li>
+ * </ul>
+ * @example
+ * // CryptoJS provider sample
+ * var md = new KJUR.crypto.MessageDigest({alg: "sha1", prov: "cryptojs"});
+ * md.updateString('aaa')
+ * var mdHex = md.digest()
+ *
+ * // SJCL(Stanford JavaScript Crypto Library) provider sample
+ * var md = new KJUR.crypto.MessageDigest({alg: "sha256", prov: "sjcl"}); // sjcl supports sha256 only
+ * md.updateString('aaa')
+ * var mdHex = md.digest()
+ */
+KJUR.crypto.MessageDigest = function(params) {
+ var md = null;
+ var algName = null;
+ var provName = null;
+
+ /**
+ * set hash algorithm and provider
+ * @name setAlgAndProvider
+ * @memberOf KJUR.crypto.MessageDigest
+ * @function
+ * @param {String} alg hash algorithm name
+ * @param {String} prov provider name
+ * @description
+ * @example
+ * // for SHA1
+ * md.setAlgAndProvider('sha1', 'cryptojs');
+ * // for RIPEMD160
+ * md.setAlgAndProvider('ripemd160', 'cryptojs');
+ */
+ this.setAlgAndProvider = function(alg, prov) {
+ if (alg != null && prov === undefined) prov = KJUR.crypto.Util.DEFAULTPROVIDER[alg];
+
+ // for cryptojs
+ if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(alg) != -1 &&
+ prov == 'cryptojs') {
+ try {
+ this.md = eval(KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[alg]).create();
+ } catch (ex) {
+ throw "setAlgAndProvider hash alg set fail alg=" + alg + "/" + ex;
+ }
+ this.updateString = function(str) {
+ this.md.update(str);
+ };
+ this.updateHex = function(hex) {
+ var wHex = CryptoJS.enc.Hex.parse(hex);
+ this.md.update(wHex);
+ };
+ this.digest = function() {
+ var hash = this.md.finalize();
+ return hash.toString(CryptoJS.enc.Hex);
+ };
+ this.digestString = function(str) {
+ this.updateString(str);
+ return this.digest();
+ };
+ this.digestHex = function(hex) {
+ this.updateHex(hex);
+ return this.digest();
+ };
+ }
+ if (':sha256:'.indexOf(alg) != -1 &&
+ prov == 'sjcl') {
+ try {
+ this.md = new sjcl.hash.sha256();
+ } catch (ex) {
+ throw "setAlgAndProvider hash alg set fail alg=" + alg + "/" + ex;
+ }
+ this.updateString = function(str) {
+ this.md.update(str);
+ };
+ this.updateHex = function(hex) {
+ var baHex = sjcl.codec.hex.toBits(hex);
+ this.md.update(baHex);
+ };
+ this.digest = function() {
+ var hash = this.md.finalize();
+ return sjcl.codec.hex.fromBits(hash);
+ };
+ this.digestString = function(str) {
+ this.updateString(str);
+ return this.digest();
+ };
+ this.digestHex = function(hex) {
+ this.updateHex(hex);
+ return this.digest();
+ };
+ }
+ };
+
+ /**
+ * update digest by specified string
+ * @name updateString
+ * @memberOf KJUR.crypto.MessageDigest
+ * @function
+ * @param {String} str string to update
+ * @description
+ * @example
+ * md.updateString('New York');
+ */
+ this.updateString = function(str) {
+ throw "updateString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName;
+ };
+
+ /**
+ * update digest by specified hexadecimal string
+ * @name updateHex
+ * @memberOf KJUR.crypto.MessageDigest
+ * @function
+ * @param {String} hex hexadecimal string to update
+ * @description
+ * @example
+ * md.updateHex('0afe36');
+ */
+ this.updateHex = function(hex) {
+ throw "updateHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName;
+ };
+
+ /**
+ * completes hash calculation and returns hash result
+ * @name digest
+ * @memberOf KJUR.crypto.MessageDigest
+ * @function
+ * @description
+ * @example
+ * md.digest()
+ */
+ this.digest = function() {
+ throw "digest() not supported for this alg/prov: " + this.algName + "/" + this.provName;
+ };
+
+ /**
+ * performs final update on the digest using string, then completes the digest computation
+ * @name digestString
+ * @memberOf KJUR.crypto.MessageDigest
+ * @function
+ * @param {String} str string to final update
+ * @description
+ * @example
+ * md.digestString('aaa')
+ */
+ this.digestString = function(str) {
+ throw "digestString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName;
+ };
+
+ /**
+ * performs final update on the digest using hexadecimal string, then completes the digest computation
+ * @name digestHex
+ * @memberOf KJUR.crypto.MessageDigest
+ * @function
+ * @param {String} hex hexadecimal string to final update
+ * @description
+ * @example
+ * md.digestHex('0f2abd')
+ */
+ this.digestHex = function(hex) {
+ throw "digestHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName;
+ };
+
+ if (params !== undefined) {
+ if (params['alg'] !== undefined) {
+ this.algName = params['alg'];
+ if (params['prov'] === undefined)
+ this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName];
+ this.setAlgAndProvider(this.algName, this.provName);
+ }
+ }
+};
+
+/**
+ * Mac(Message Authentication Code) class which is very similar to java.security.Mac class
+ * @name KJUR.crypto.Mac
+ * @class Mac class which is very similar to java.security.Mac class
+ * @param {Array} params parameters for constructor
+ * @description
+ * <br/>
+ * Currently this supports following algorithm and providers combination:
+ * <ul>
+ * <li>hmacmd5 - cryptojs</li>
+ * <li>hmacsha1 - cryptojs</li>
+ * <li>hmacsha224 - cryptojs</li>
+ * <li>hmacsha256 - cryptojs</li>
+ * <li>hmacsha384 - cryptojs</li>
+ * <li>hmacsha512 - cryptojs</li>
+ * </ul>
+ * NOTE: HmacSHA224 and HmacSHA384 issue was fixed since jsrsasign 4.1.4.
+ * Please use 'ext/cryptojs-312-core-fix*.js' instead of 'core.js' of original CryptoJS
+ * to avoid those issue.
+ * <br/>
+ * NOTE2: Hmac signature bug was fixed in jsrsasign 4.9.0 by providing CryptoJS
+ * bug workaround.
+ * <br/>
+ * Please see {@link KJUR.crypto.Mac.setPassword}, how to provide password
+ * in various ways in detail.
+ * @example
+ * var mac = new KJUR.crypto.Mac({alg: "HmacSHA1", "pass": "pass"});
+ * mac.updateString('aaa')
+ * var macHex = md.doFinal()
+ *
+ * // other password representation
+ * var mac = new KJUR.crypto.Mac({alg: "HmacSHA256", "pass": {"hex": "6161"}});
+ * var mac = new KJUR.crypto.Mac({alg: "HmacSHA256", "pass": {"utf8": "aa"}});
+ * var mac = new KJUR.crypto.Mac({alg: "HmacSHA256", "pass": {"rstr": "\x61\x61"}});
+ * var mac = new KJUR.crypto.Mac({alg: "HmacSHA256", "pass": {"b64": "Mi02/+...a=="}});
+ * var mac = new KJUR.crypto.Mac({alg: "HmacSHA256", "pass": {"b64u": "Mi02_-...a"}});
+ */
+KJUR.crypto.Mac = function(params) {
+ var mac = null;
+ var pass = null;
+ var algName = null;
+ var provName = null;
+ var algProv = null;
+
+ this.setAlgAndProvider = function(alg, prov) {
+ alg = alg.toLowerCase();
+
+ if (alg == null) alg = "hmacsha1";
+
+ alg = alg.toLowerCase();
+ if (alg.substr(0, 4) != "hmac") {
+ throw "setAlgAndProvider unsupported HMAC alg: " + alg;
+ }
+
+ if (prov === undefined) prov = KJUR.crypto.Util.DEFAULTPROVIDER[alg];
+ this.algProv = alg + "/" + prov;
+
+ var hashAlg = alg.substr(4);
+
+ // for cryptojs
+ if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(hashAlg) != -1 &&
+ prov == 'cryptojs') {
+ try {
+ var mdObj = eval(KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[hashAlg]);
+ this.mac = CryptoJS.algo.HMAC.create(mdObj, this.pass);
+ } catch (ex) {
+ throw "setAlgAndProvider hash alg set fail hashAlg=" + hashAlg + "/" + ex;
+ }
+ this.updateString = function(str) {
+ this.mac.update(str);
+ };
+ this.updateHex = function(hex) {
+ var wHex = CryptoJS.enc.Hex.parse(hex);
+ this.mac.update(wHex);
+ };
+ this.doFinal = function() {
+ var hash = this.mac.finalize();
+ return hash.toString(CryptoJS.enc.Hex);
+ };
+ this.doFinalString = function(str) {
+ this.updateString(str);
+ return this.doFinal();
+ };
+ this.doFinalHex = function(hex) {
+ this.updateHex(hex);
+ return this.doFinal();
+ };
+ }
+ };
+
+ /**
+ * update digest by specified string
+ * @name updateString
+ * @memberOf KJUR.crypto.Mac
+ * @function
+ * @param {String} str string to update
+ * @description
+ * @example
+ * md.updateString('New York');
+ */
+ this.updateString = function(str) {
+ throw "updateString(str) not supported for this alg/prov: " + this.algProv;
+ };
+
+ /**
+ * update digest by specified hexadecimal string
+ * @name updateHex
+ * @memberOf KJUR.crypto.Mac
+ * @function
+ * @param {String} hex hexadecimal string to update
+ * @description
+ * @example
+ * md.updateHex('0afe36');
+ */
+ this.updateHex = function(hex) {
+ throw "updateHex(hex) not supported for this alg/prov: " + this.algProv;
+ };
+
+ /**
+ * completes hash calculation and returns hash result
+ * @name doFinal
+ * @memberOf KJUR.crypto.Mac
+ * @function
+ * @description
+ * @example
+ * md.digest()
+ */
+ this.doFinal = function() {
+ throw "digest() not supported for this alg/prov: " + this.algProv;
+ };
+
+ /**
+ * performs final update on the digest using string, then completes the digest computation
+ * @name doFinalString
+ * @memberOf KJUR.crypto.Mac
+ * @function
+ * @param {String} str string to final update
+ * @description
+ * @example
+ * md.digestString('aaa')
+ */
+ this.doFinalString = function(str) {
+ throw "digestString(str) not supported for this alg/prov: " + this.algProv;
+ };
+
+ /**
+ * performs final update on the digest using hexadecimal string,
+ * then completes the digest computation
+ * @name doFinalHex
+ * @memberOf KJUR.crypto.Mac
+ * @function
+ * @param {String} hex hexadecimal string to final update
+ * @description
+ * @example
+ * md.digestHex('0f2abd')
+ */
+ this.doFinalHex = function(hex) {
+ throw "digestHex(hex) not supported for this alg/prov: " + this.algProv;
+ };
+
+ /**
+ * set password for Mac
+ * @name setPassword
+ * @memberOf KJUR.crypto.Mac
+ * @function
+ * @param {Object} pass password for Mac
+ * @since crypto 1.1.7 jsrsasign 4.9.0
+ * @description
+ * This method will set password for (H)Mac internally.
+ * Argument 'pass' can be specified as following:
+ * <ul>
+ * <li>even length string of 0..9, a..f or A-F: implicitly specified as hexadecimal string</li>
+ * <li>not above string: implicitly specified as raw string</li>
+ * <li>{rstr: "\x65\x70"}: explicitly specified as raw string</li>
+ * <li>{hex: "6570"}: explicitly specified as hexacedimal string</li>
+ * <li>{utf8: "秘密"}: explicitly specified as UTF8 string</li>
+ * <li>{b64: "Mi78..=="}: explicitly specified as Base64 string</li>
+ * <li>{b64u: "Mi7-_"}: explicitly specified as Base64URL string</li>
+ * </ul>
+ * It is *STRONGLY RECOMMENDED* that explicit representation of password argument
+ * to avoid ambiguity. For example string "6161" can mean a string "6161" or
+ * a hexadecimal string of "aa" (i.e. \x61\x61).
+ * @example
+ * mac = KJUR.crypto.Mac({'alg': 'hmacsha256'});
+ * // set password by implicit raw string
+ * mac.setPassword("\x65\x70\xb9\x0b");
+ * mac.setPassword("password");
+ * // set password by implicit hexadecimal string
+ * mac.setPassword("6570b90b");
+ * mac.setPassword("6570B90B");
+ * // set password by explicit raw string
+ * mac.setPassword({"rstr": "\x65\x70\xb9\x0b"});
+ * // set password by explicit hexadecimal string
+ * mac.setPassword({"hex": "6570b90b"});
+ * // set password by explicit utf8 string
+ * mac.setPassword({"utf8": "passwordパスワード");
+ * // set password by explicit Base64 string
+ * mac.setPassword({"b64": "Mb+c3f/=="});
+ * // set password by explicit Base64URL string
+ * mac.setPassword({"b64u": "Mb-c3f_"});
+ */
+ this.setPassword = function(pass) {
+ // internal this.pass shall be CryptoJS DWord Object for CryptoJS bug
+ // work around. CrytoJS HMac password can be passed by
+ // raw string as described in the manual however it doesn't
+ // work properly in some case. If password was passed
+ // by CryptoJS DWord which is not described in the manual
+ // it seems to work. (fixed since crypto 1.1.7)
+
+ if (typeof pass == 'string') {
+ var hPass = pass;
+ if (pass.length % 2 == 1 || ! pass.match(/^[0-9A-Fa-f]+$/)) { // raw str
+ hPass = rstrtohex(pass);
+ }
+ this.pass = CryptoJS.enc.Hex.parse(hPass);
+ return;
+ }
+
+ if (typeof pass != 'object')
+ throw "KJUR.crypto.Mac unsupported password type: " + pass;
+
+ var hPass = null;
+ if (pass.hex !== undefined) {
+ if (pass.hex.length % 2 != 0 || ! pass.hex.match(/^[0-9A-Fa-f]+$/))
+ throw "Mac: wrong hex password: " + pass.hex;
+ hPass = pass.hex;
+ }
+ if (pass.utf8 !== undefined) hPass = utf8tohex(pass.utf8);
+ if (pass.rstr !== undefined) hPass = rstrtohex(pass.rstr);
+ if (pass.b64 !== undefined) hPass = b64tohex(pass.b64);
+ if (pass.b64u !== undefined) hPass = b64utohex(pass.b64u);
+
+ if (hPass == null)
+ throw "KJUR.crypto.Mac unsupported password type: " + pass;
+
+ this.pass = CryptoJS.enc.Hex.parse(hPass);
+ };
+
+ if (params !== undefined) {
+ if (params.pass !== undefined) {
+ this.setPassword(params.pass);
+ }
+ if (params.alg !== undefined) {
+ this.algName = params.alg;
+ if (params['prov'] === undefined)
+ this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName];
+ this.setAlgAndProvider(this.algName, this.provName);
+ }
+ }
+};
+
+/**
+ * Signature class which is very similar to java.security.Signature class
+ * @name KJUR.crypto.Signature
+ * @class Signature class which is very similar to java.security.Signature class
+ * @param {Array} params parameters for constructor
+ * @property {String} state Current state of this signature object whether 'SIGN', 'VERIFY' or null
+ * @description
+ * <br/>
+ * As for params of constructor's argument, it can be specify following attributes:
+ * <ul>
+ * <li>alg - signature algorithm name (ex. {MD5,SHA1,SHA224,SHA256,SHA384,SHA512,RIPEMD160}with{RSA,ECDSA,DSA})</li>
+ * <li>provider - currently 'cryptojs/jsrsa' only</li>
+ * </ul>
+ * <h4>SUPPORTED ALGORITHMS AND PROVIDERS</h4>
+ * This Signature class supports following signature algorithm and provider names:
+ * <ul>
+ * <li>MD5withRSA - cryptojs/jsrsa</li>
+ * <li>SHA1withRSA - cryptojs/jsrsa</li>
+ * <li>SHA224withRSA - cryptojs/jsrsa</li>
+ * <li>SHA256withRSA - cryptojs/jsrsa</li>
+ * <li>SHA384withRSA - cryptojs/jsrsa</li>
+ * <li>SHA512withRSA - cryptojs/jsrsa</li>
+ * <li>RIPEMD160withRSA - cryptojs/jsrsa</li>
+ * <li>MD5withECDSA - cryptojs/jsrsa</li>
+ * <li>SHA1withECDSA - cryptojs/jsrsa</li>
+ * <li>SHA224withECDSA - cryptojs/jsrsa</li>
+ * <li>SHA256withECDSA - cryptojs/jsrsa</li>
+ * <li>SHA384withECDSA - cryptojs/jsrsa</li>
+ * <li>SHA512withECDSA - cryptojs/jsrsa</li>
+ * <li>RIPEMD160withECDSA - cryptojs/jsrsa</li>
+ * <li>MD5withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>SHA1withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>SHA224withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>SHA256withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>SHA384withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>SHA512withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>RIPEMD160withRSAandMGF1 - cryptojs/jsrsa</li>
+ * <li>SHA1withDSA - cryptojs/jsrsa</li>
+ * <li>SHA224withDSA - cryptojs/jsrsa</li>
+ * <li>SHA256withDSA - cryptojs/jsrsa</li>
+ * </ul>
+ * Here are supported elliptic cryptographic curve names and their aliases for ECDSA:
+ * <ul>
+ * <li>secp256k1</li>
+ * <li>secp256r1, NIST P-256, P-256, prime256v1</li>
+ * <li>secp384r1, NIST P-384, P-384</li>
+ * </ul>
+ * NOTE1: DSA signing algorithm is also supported since crypto 1.1.5.
+ * <h4>EXAMPLES</h4>
+ * @example
+ * // RSA signature generation
+ * var sig = new KJUR.crypto.Signature({"alg": "SHA1withRSA"});
+ * sig.init(prvKeyPEM);
+ * sig.updateString('aaa');
+ * var hSigVal = sig.sign();
+ *
+ * // DSA signature validation
+ * var sig2 = new KJUR.crypto.Signature({"alg": "SHA1withDSA"});
+ * sig2.init(certPEM);
+ * sig.updateString('aaa');
+ * var isValid = sig2.verify(hSigVal);
+ *
+ * // ECDSA signing
+ * var sig = new KJUR.crypto.Signature({'alg':'SHA1withECDSA'});
+ * sig.init(prvKeyPEM);
+ * sig.updateString('aaa');
+ * var sigValueHex = sig.sign();
+ *
+ * // ECDSA verifying
+ * var sig2 = new KJUR.crypto.Signature({'alg':'SHA1withECDSA'});
+ * sig.init(certPEM);
+ * sig.updateString('aaa');
+ * var isValid = sig.verify(sigValueHex);
+ */
+KJUR.crypto.Signature = function(params) {
+ var prvKey = null; // RSAKey/KJUR.crypto.{ECDSA,DSA} object for signing
+ var pubKey = null; // RSAKey/KJUR.crypto.{ECDSA,DSA} object for verifying
+
+ var md = null; // KJUR.crypto.MessageDigest object
+ var sig = null;
+ var algName = null;
+ var provName = null;
+ var algProvName = null;
+ var mdAlgName = null;
+ var pubkeyAlgName = null; // rsa,ecdsa,rsaandmgf1(=rsapss)
+ var state = null;
+ var pssSaltLen = -1;
+ var initParams = null;
+
+ var sHashHex = null; // hex hash value for hex
+ var hDigestInfo = null;
+ var hPaddedDigestInfo = null;
+ var hSign = null;
+
+ this._setAlgNames = function() {
+ if (this.algName.match(/^(.+)with(.+)$/)) {
+ this.mdAlgName = RegExp.$1.toLowerCase();
+ this.pubkeyAlgName = RegExp.$2.toLowerCase();
+ }
+ };
+
+ this._zeroPaddingOfSignature = function(hex, bitLength) {
+ var s = "";
+ var nZero = bitLength / 4 - hex.length;
+ for (var i = 0; i < nZero; i++) {
+ s = s + "0";
+ }
+ return s + hex;
+ };
+
+ /**
+ * set signature algorithm and provider
+ * @name setAlgAndProvider
+ * @memberOf KJUR.crypto.Signature
+ * @function
+ * @param {String} alg signature algorithm name
+ * @param {String} prov provider name
+ * @description
+ * @example
+ * md.setAlgAndProvider('SHA1withRSA', 'cryptojs/jsrsa');
+ */
+ this.setAlgAndProvider = function(alg, prov) {
+ this._setAlgNames();
+ if (prov != 'cryptojs/jsrsa')
+ throw "provider not supported: " + prov;
+
+ if (':md5:sha1:sha224:sha256:sha384:sha512:ripemd160:'.indexOf(this.mdAlgName) != -1) {
+ try {
+ this.md = new KJUR.crypto.MessageDigest({'alg':this.mdAlgName});
+ } catch (ex) {
+ throw "setAlgAndProvider hash alg set fail alg=" +
+ this.mdAlgName + "/" + ex;
+ }
+
+ this.init = function(keyparam, pass) {
+ var keyObj = null;
+ try {
+ if (pass === undefined) {
+ keyObj = KEYUTIL.getKey(keyparam);
+ } else {
+ keyObj = KEYUTIL.getKey(keyparam, pass);
+ }
+ } catch (ex) {
+ throw "init failed:" + ex;
+ }
+
+ if (keyObj.isPrivate === true) {
+ this.prvKey = keyObj;
+ this.state = "SIGN";
+ } else if (keyObj.isPublic === true) {
+ this.pubKey = keyObj;
+ this.state = "VERIFY";
+ } else {
+ throw "init failed.:" + keyObj;
+ }
+ };
+
+ this.initSign = function(params) {
+ if (typeof params['ecprvhex'] == 'string' &&
+ typeof params['eccurvename'] == 'string') {
+ this.ecprvhex = params['ecprvhex'];
+ this.eccurvename = params['eccurvename'];
+ } else {
+ this.prvKey = params;
+ }
+ this.state = "SIGN";
+ };
+
+ this.initVerifyByPublicKey = function(params) {
+ if (typeof params['ecpubhex'] == 'string' &&
+ typeof params['eccurvename'] == 'string') {
+ this.ecpubhex = params['ecpubhex'];
+ this.eccurvename = params['eccurvename'];
+ } else if (params instanceof KJUR.crypto.ECDSA) {
+ this.pubKey = params;
+ } else if (params instanceof RSAKey) {
+ this.pubKey = params;
+ }
+ this.state = "VERIFY";
+ };
+
+ this.initVerifyByCertificatePEM = function(certPEM) {
+ var x509 = new X509();
+ x509.readCertPEM(certPEM);
+ this.pubKey = x509.subjectPublicKeyRSA;
+ this.state = "VERIFY";
+ };
+
+ this.updateString = function(str) {
+ this.md.updateString(str);
+ };
+
+ this.updateHex = function(hex) {
+ this.md.updateHex(hex);
+ };
+
+ this.sign = function() {
+ this.sHashHex = this.md.digest();
+ if (typeof this.ecprvhex != "undefined" &&
+ typeof this.eccurvename != "undefined") {
+ var ec = new KJUR.crypto.ECDSA({'curve': this.eccurvename});
+ this.hSign = ec.signHex(this.sHashHex, this.ecprvhex);
+ } else if (this.prvKey instanceof RSAKey &&
+ this.pubkeyAlgName == "rsaandmgf1") {
+ this.hSign = this.prvKey.signWithMessageHashPSS(this.sHashHex,
+ this.mdAlgName,
+ this.pssSaltLen);
+ } else if (this.prvKey instanceof RSAKey &&
+ this.pubkeyAlgName == "rsa") {
+ this.hSign = this.prvKey.signWithMessageHash(this.sHashHex,
+ this.mdAlgName);
+ } else if (this.prvKey instanceof KJUR.crypto.ECDSA) {
+ this.hSign = this.prvKey.signWithMessageHash(this.sHashHex);
+ } else if (this.prvKey instanceof KJUR.crypto.DSA) {
+ this.hSign = this.prvKey.signWithMessageHash(this.sHashHex);
+ } else {
+ throw "Signature: unsupported public key alg: " + this.pubkeyAlgName;
+ }
+ return this.hSign;
+ };
+ this.signString = function(str) {
+ this.updateString(str);
+ return this.sign();
+ };
+ this.signHex = function(hex) {
+ this.updateHex(hex);
+ return this.sign();
+ };
+ this.verify = function(hSigVal) {
+ this.sHashHex = this.md.digest();
+ if (typeof this.ecpubhex != "undefined" &&
+ typeof this.eccurvename != "undefined") {
+ var ec = new KJUR.crypto.ECDSA({curve: this.eccurvename});
+ return ec.verifyHex(this.sHashHex, hSigVal, this.ecpubhex);
+ } else if (this.pubKey instanceof RSAKey &&
+ this.pubkeyAlgName == "rsaandmgf1") {
+ return this.pubKey.verifyWithMessageHashPSS(this.sHashHex, hSigVal,
+ this.mdAlgName,
+ this.pssSaltLen);
+ } else if (this.pubKey instanceof RSAKey &&
+ this.pubkeyAlgName == "rsa") {
+ return this.pubKey.verifyWithMessageHash(this.sHashHex, hSigVal);
+ } else if (this.pubKey instanceof KJUR.crypto.ECDSA) {
+ return this.pubKey.verifyWithMessageHash(this.sHashHex, hSigVal);
+ } else if (this.pubKey instanceof KJUR.crypto.DSA) {
+ return this.pubKey.verifyWithMessageHash(this.sHashHex, hSigVal);
+ } else {
+ throw "Signature: unsupported public key alg: " + this.pubkeyAlgName;
+ }
+ };
+ }
+ };
+
+ /**
+ * Initialize this object for signing or verifying depends on key
+ * @name init
+ * @memberOf KJUR.crypto.Signature
+ * @function
+ * @param {Object} key specifying public or private key as plain/encrypted PKCS#5/8 PEM file, certificate PEM or {@link RSAKey}, {@link KJUR.crypto.DSA} or {@link KJUR.crypto.ECDSA} object
+ * @param {String} pass (OPTION) passcode for encrypted private key
+ * @since crypto 1.1.3
+ * @description
+ * This method is very useful initialize method for Signature class since
+ * you just specify key then this method will automatically initialize it
+ * using {@link KEYUTIL.getKey} method.
+ * As for 'key', following argument type are supported:
+ * <h5>signing</h5>
+ * <ul>
+ * <li>PEM formatted PKCS#8 encrypted RSA/ECDSA private key concluding "BEGIN ENCRYPTED PRIVATE KEY"</li>
+ * <li>PEM formatted PKCS#5 encrypted RSA/DSA private key concluding "BEGIN RSA/DSA PRIVATE KEY" and ",ENCRYPTED"</li>
+ * <li>PEM formatted PKCS#8 plain RSA/ECDSA private key concluding "BEGIN PRIVATE KEY"</li>
+ * <li>PEM formatted PKCS#5 plain RSA/DSA private key concluding "BEGIN RSA/DSA PRIVATE KEY" without ",ENCRYPTED"</li>
+ * <li>RSAKey object of private key</li>
+ * <li>KJUR.crypto.ECDSA object of private key</li>
+ * <li>KJUR.crypto.DSA object of private key</li>
+ * </ul>
+ * <h5>verification</h5>
+ * <ul>
+ * <li>PEM formatted PKCS#8 RSA/EC/DSA public key concluding "BEGIN PUBLIC KEY"</li>
+ * <li>PEM formatted X.509 certificate with RSA/EC/DSA public key concluding
+ * "BEGIN CERTIFICATE", "BEGIN X509 CERTIFICATE" or "BEGIN TRUSTED CERTIFICATE".</li>
+ * <li>RSAKey object of public key</li>
+ * <li>KJUR.crypto.ECDSA object of public key</li>
+ * <li>KJUR.crypto.DSA object of public key</li>
+ * </ul>
+ * @example
+ * sig.init(sCertPEM)
+ */
+ this.init = function(key, pass) {
+ throw "init(key, pass) not supported for this alg:prov=" +
+ this.algProvName;
+ };
+
+ /**
+ * Initialize this object for verifying with a public key
+ * @name initVerifyByPublicKey
+ * @memberOf KJUR.crypto.Signature
+ * @function
+ * @param {Object} param RSAKey object of public key or associative array for ECDSA
+ * @since 1.0.2
+ * @deprecated from crypto 1.1.5. please use init() method instead.
+ * @description
+ * Public key information will be provided as 'param' parameter and the value will be
+ * following:
+ * <ul>
+ * <li>{@link RSAKey} object for RSA verification</li>
+ * <li>associative array for ECDSA verification
+ * (ex. <code>{'ecpubhex': '041f..', 'eccurvename': 'secp256r1'}</code>)
+ * </li>
+ * </ul>
+ * @example
+ * sig.initVerifyByPublicKey(rsaPrvKey)
+ */
+ this.initVerifyByPublicKey = function(rsaPubKey) {
+ throw "initVerifyByPublicKey(rsaPubKeyy) not supported for this alg:prov=" +
+ this.algProvName;
+ };
+
+ /**
+ * Initialize this object for verifying with a certficate
+ * @name initVerifyByCertificatePEM
+ * @memberOf KJUR.crypto.Signature
+ * @function
+ * @param {String} certPEM PEM formatted string of certificate
+ * @since 1.0.2
+ * @deprecated from crypto 1.1.5. please use init() method instead.
+ * @description
+ * @example
+ * sig.initVerifyByCertificatePEM(certPEM)
+ */
+ this.initVerifyByCertificatePEM = function(certPEM) {
+ throw "initVerifyByCertificatePEM(certPEM) not supported for this alg:prov=" +
+ this.algProvName;
+ };
+
+ /**
+ * Initialize this object for signing
+ * @name initSign
+ * @memberOf KJUR.crypto.Signature
+ * @function
+ * @param {Object} param RSAKey object of public key or associative array for ECDSA
+ * @deprecated from crypto 1.1.5. please use init() method instead.
+ * @description
+ * Private key information will be provided as 'param' parameter and the value will be
+ * following:
+ * <ul>
+ * <li>{@link RSAKey} object for RSA signing</li>
+ * <li>associative array for ECDSA signing
+ * (ex. <code>{'ecprvhex': '1d3f..', 'eccurvename': 'secp256r1'}</code>)</li>
+ * </ul>
+ * @example
+ * sig.initSign(prvKey)
+ */
+ this.initSign = function(prvKey) {
+ throw "initSign(prvKey) not supported for this alg:prov=" + this.algProvName;
+ };
+
+ /**
+ * Updates the data to be signed or verified by a string
+ * @name updateString
+ * @memberOf KJUR.crypto.Signature
+ * @function
+ * @param {String} str string to use for the update
+ * @description
+ * @example
+ * sig.updateString('aaa')
+ */
+ this.updateString = function(str) {
+ throw "updateString(str) not supported for this alg:prov=" + this.algProvName;
+ };
+
+ /**
+ * Updates the data to be signed or verified by a hexadecimal string
+ * @name updateHex
+ * @memberOf KJUR.crypto.Signature
+ * @function
+ * @param {String} hex hexadecimal string to use for the update
+ * @description
+ * @example
+ * sig.updateHex('1f2f3f')
+ */
+ this.updateHex = function(hex) {
+ throw "updateHex(hex) not supported for this alg:prov=" + this.algProvName;
+ };
+
+ /**
+ * Returns the signature bytes of all data updates as a hexadecimal string
+ * @name sign
+ * @memberOf KJUR.crypto.Signature
+ * @function
+ * @return the signature bytes as a hexadecimal string
+ * @description
+ * @example
+ * var hSigValue = sig.sign()
+ */
+ this.sign = function() {
+ throw "sign() not supported for this alg:prov=" + this.algProvName;
+ };
+
+ /**
+ * performs final update on the sign using string, then returns the signature bytes of all data updates as a hexadecimal string
+ * @name signString
+ * @memberOf KJUR.crypto.Signature
+ * @function
+ * @param {String} str string to final update
+ * @return the signature bytes of a hexadecimal string
+ * @description
+ * @example
+ * var hSigValue = sig.signString('aaa')
+ */
+ this.signString = function(str) {
+ throw "digestString(str) not supported for this alg:prov=" + this.algProvName;
+ };
+
+ /**
+ * performs final update on the sign using hexadecimal string, then returns the signature bytes of all data updates as a hexadecimal string
+ * @name signHex
+ * @memberOf KJUR.crypto.Signature
+ * @function
+ * @param {String} hex hexadecimal string to final update
+ * @return the signature bytes of a hexadecimal string
+ * @description
+ * @example
+ * var hSigValue = sig.signHex('1fdc33')
+ */
+ this.signHex = function(hex) {
+ throw "digestHex(hex) not supported for this alg:prov=" + this.algProvName;
+ };
+
+ /**
+ * verifies the passed-in signature.
+ * @name verify
+ * @memberOf KJUR.crypto.Signature
+ * @function
+ * @param {String} str string to final update
+ * @return {Boolean} true if the signature was verified, otherwise false
+ * @description
+ * @example
+ * var isValid = sig.verify('1fbcefdca4823a7(snip)')
+ */
+ this.verify = function(hSigVal) {
+ throw "verify(hSigVal) not supported for this alg:prov=" + this.algProvName;
+ };
+
+ this.initParams = params;
+
+ if (params !== undefined) {
+ if (params['alg'] !== undefined) {
+ this.algName = params['alg'];
+ if (params['prov'] === undefined) {
+ this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName];
+ } else {
+ this.provName = params['prov'];
+ }
+ this.algProvName = this.algName + ":" + this.provName;
+ this.setAlgAndProvider(this.algName, this.provName);
+ this._setAlgNames();
+ }
+
+ if (params['psssaltlen'] !== undefined) this.pssSaltLen = params['psssaltlen'];
+
+ if (params['prvkeypem'] !== undefined) {
+ if (params['prvkeypas'] !== undefined) {
+ throw "both prvkeypem and prvkeypas parameters not supported";
+ } else {
+ try {
+ var prvKey = new RSAKey();
+ prvKey.readPrivateKeyFromPEMString(params['prvkeypem']);
+ this.initSign(prvKey);
+ } catch (ex) {
+ throw "fatal error to load pem private key: " + ex;
+ }
+ }
+ }
+ }
+};
+
+/**
+ * static object for cryptographic function utilities
+ * @name KJUR.crypto.OID
+ * @class static object for cryptography related OIDs
+ * @property {Array} oidhex2name key value of hexadecimal OID and its name
+ * (ex. '2a8648ce3d030107' and 'secp256r1')
+ * @since crypto 1.1.3
+ * @description
+ */
+
+
+KJUR.crypto.OID = new function() {
+ this.oidhex2name = {
+ '2a864886f70d010101': 'rsaEncryption',
+ '2a8648ce3d0201': 'ecPublicKey',
+ '2a8648ce380401': 'dsa',
+ '2a8648ce3d030107': 'secp256r1',
+ '2b8104001f': 'secp192k1',
+ '2b81040021': 'secp224r1',
+ '2b8104000a': 'secp256k1',
+ '2b81040023': 'secp521r1',
+ '2b81040022': 'secp384r1',
+ '2a8648ce380403': 'SHA1withDSA', // 1.2.840.10040.4.3
+ '608648016503040301': 'SHA224withDSA', // 2.16.840.1.101.3.4.3.1
+ '608648016503040302': 'SHA256withDSA' // 2.16.840.1.101.3.4.3.2
+ };
+};
+
+exports.KJUR = KJUR;
+module.exports = exports;
/*! rsapem-1.1.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license
*/
//
// rsa-pem.js - adding function for reading/writing PKCS#1 PEM private key
// to RSAKey class.
//
-// version: 1.1 (2012-May-10)
+// version: 1.1.1 (2013-Apr-12)
//
-// Copyright (c) 2010-2012 Kenji Urushima (kenji.urushima@gmail.com)
+// Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com)
//
// This software is licensed under the terms of the MIT License.
// http://kjur.github.com/jsrsasign/license/
@@ -5381,6 +3582,18 @@
// new lines from PEM formatted RSA private key string.
//
+/**
+ * @fileOverview
+ * @name rsapem-1.1.js
+ * @author Kenji Urushima kenji.urushima@gmail.com
+ * @version 1.1
+ * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
+ */
+
+var ASN1HEX = require('./asn1hex-1.1.js').ASN1HEX;
+var b64tohex = require('./base64.js').b64tohex;
+var RSAKey = require('./rsa2.js').RSAKey;
+
function _rsapem_pemToBase64(sPEMPrivateKey) {
var s = sPEMPrivateKey;
s = s.replace("-----BEGIN RSA PRIVATE KEY-----", "");
@@ -5421,6 +3634,19 @@
}
/**
+ * read RSA private key from a ASN.1 hexadecimal string
+ * @name readPrivateKeyFromASN1HexString
+ * @memberOf RSAKey#
+ * @function
+ * @param {String} keyHex ASN.1 hexadecimal string of PKCS#1 private key.
+ * @since 1.1.1
+ */
+function _rsapem_readPrivateKeyFromASN1HexString(keyHex) {
+ var a = _rsapem_getHexValueArrayOfChildrenFromHex(keyHex);
+ this.setPrivateEx(a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]);
+}
+
+/**
* read PKCS#1 private key from a string
* @name readPrivateKeyFromPEMString
* @memberOf RSAKey#
@@ -5435,70 +3661,37 @@
}
RSAKey.prototype.readPrivateKeyFromPEMString = _rsapem_readPrivateKeyFromPEMString;
-/*! rsasign-1.2.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license
+RSAKey.prototype.readPrivateKeyFromASN1HexString = _rsapem_readPrivateKeyFromASN1HexString;
+
+exports.RSAKey = RSAKey;
+module.exports = exports;
+/*! rsasign-1.2.7.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license
*/
-//
-// rsa-sign.js - adding signing functions to RSAKey class.
-//
-//
-// version: 1.2.1 (08 May 2012)
-//
-// Copyright (c) 2010-2012 Kenji Urushima (kenji.urushima@gmail.com)
-//
-// This software is licensed under the terms of the MIT License.
-// http://kjur.github.com/jsrsasign/license/
-//
-// The above copyright and license notice shall be
-// included in all copies or substantial portions of the Software.
-
-//
-// Depends on:
-// function sha1.hex(s) of sha1.js
-// jsbn.js
-// jsbn2.js
-// rsa.js
-// rsa2.js
-//
-
-// keysize / pmstrlen
-// 512 / 128
-// 1024 / 256
-// 2048 / 512
-// 4096 / 1024
+/*
+ * rsa-sign.js - adding signing functions to RSAKey class.
+ *
+ * version: 1.2.7 (2013 Aug 25)
+ *
+ * Copyright (c) 2010-2013 Kenji Urushima (kenji.urushima@gmail.com)
+ *
+ * This software is licensed under the terms of the MIT License.
+ * http://kjur.github.com/jsrsasign/license/
+ *
+ * The above copyright and license notice shall be
+ * included in all copies or substantial portions of the Software.
+ */
/**
- * @property {Dictionary} _RSASIGN_DIHEAD
- * @description Array of head part of hexadecimal DigestInfo value for hash algorithms.
- * You can add any DigestInfo hash algorith for signing.
- * See PKCS#1 v2.1 spec (p38).
+ * @fileOverview
+ * @name rsasign-1.2.js
+ * @author Kenji Urushima kenji.urushima@gmail.com
+ * @version rsasign 1.2.7
+ * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
*/
-var _RSASIGN_DIHEAD = [];
-_RSASIGN_DIHEAD['sha1'] = "3021300906052b0e03021a05000414";
-_RSASIGN_DIHEAD['sha256'] = "3031300d060960864801650304020105000420";
-_RSASIGN_DIHEAD['sha384'] = "3041300d060960864801650304020205000430";
-_RSASIGN_DIHEAD['sha512'] = "3051300d060960864801650304020305000440";
-_RSASIGN_DIHEAD['md2'] = "3020300c06082a864886f70d020205000410";
-_RSASIGN_DIHEAD['md5'] = "3020300c06082a864886f70d020505000410";
-_RSASIGN_DIHEAD['ripemd160'] = "3021300906052b2403020105000414";
-/**
- * @property {Dictionary} _RSASIGN_HASHHEXFUNC
- * @description Array of functions which calculate hash and returns it as hexadecimal.
- * You can add any hash algorithm implementations.
- */
-var _RSASIGN_HASHHEXFUNC = [];
-_RSASIGN_HASHHEXFUNC['sha1'] = function(s){return hex_sha1(s);}; // http://pajhome.org.uk/crypt/md5/md5.html
-_RSASIGN_HASHHEXFUNC['sha256'] = function(s){return hex_sha256(s);} // http://pajhome.org.uk/crypt/md5/md5.html
-_RSASIGN_HASHHEXFUNC['sha512'] = function(s){return hex_sha512(s);} // http://pajhome.org.uk/crypt/md5/md5.html
-_RSASIGN_HASHHEXFUNC['md5'] = function(s){return hex_md5(s);}; // http://pajhome.org.uk/crypt/md5/md5.html
-_RSASIGN_HASHHEXFUNC['ripemd160'] = function(s){return hex_rmd160(s);}; // http://pajhome.org.uk/crypt/md5/md5.html
-
-//@author axelcdv
-var _RSASIGN_HASHBYTEFUNC = [];
-_RSASIGN_HASHBYTEFUNC['sha256'] = function(byteArray){return hex_sha256_from_bytes(byteArray);};
-
-//_RSASIGN_HASHHEXFUNC['sha1'] = function(s){return sha1.hex(s);} // http://user1.matsumoto.ne.jp/~goma/js/hash.html
-//_RSASIGN_HASHHEXFUNC['sha256'] = function(s){return sha256.hex;} // http://user1.matsumoto.ne.jp/~goma/js/hash.html
+var intShim = require('jsbn');
+var BigInteger = intShim.BigInteger ? intShim.BigInteger : intShim;
+var RSAKey = require('./rsapem-1.1.js').RSAKey
var _RE_HEXDECONLY = new RegExp("");
_RE_HEXDECONLY.compile("[^0-9a-f]", "gi");
@@ -5508,134 +3701,176 @@
// ========================================================================
function _rsasign_getHexPaddedDigestInfoForString(s, keySize, hashAlg) {
- var pmStrLen = keySize / 4;
- var hashFunc = _RSASIGN_HASHHEXFUNC[hashAlg];
- var sHashHex = hashFunc(s);
+ var hashFunc = function(s) { return KJUR.crypto.Util.hashString(s, hashAlg); };
+ var sHashHex = hashFunc(s);
- var sHead = "0001";
- var sTail = "00" + _RSASIGN_DIHEAD[hashAlg] + sHashHex;
- var sMid = "";
- var fLen = pmStrLen - sHead.length - sTail.length;
- for (var i = 0; i < fLen; i += 2) {
- sMid += "ff";
- }
- sPaddedMessageHex = sHead + sMid + sTail;
- return sPaddedMessageHex;
-}
-
-
-//@author: Meki Cheraoui
-function _rsasign_getHexPaddedDigestInfoForStringHEX(s, keySize, hashAlg) {
- var pmStrLen = keySize / 4;
- var hashFunc = _RSASIGN_HASHHEXFUNC[hashAlg];
- var sHashHex = hashFunc(s);
-
- var sHead = "0001";
- var sTail = "00" + _RSASIGN_DIHEAD[hashAlg] + sHashHex;
- var sMid = "";
- var fLen = pmStrLen - sHead.length - sTail.length;
- for (var i = 0; i < fLen; i += 2) {
- sMid += "ff";
- }
- sPaddedMessageHex = sHead + sMid + sTail;
- return sPaddedMessageHex;
-}
-
-/**
- * Apply padding, then computes the hash of the given byte array, according to the key size and with the hash algorithm
- * @param byteArray (byte[])
- * @param keySize (int)
- * @param hashAlg the hash algorithm to apply (string)
- * @return the hash of byteArray
- */
-function _rsasign_getHexPaddedDigestInfoForByteArray(byteArray, keySize, hashAlg){
- var pmStrLen = keySize / 4;
- var hashFunc = _RSASIGN_HASHBYTEFUNC[hashAlg];
- var sHashHex = hashFunc(byteArray); //returns hex hash
-
- var sHead = "0001";
- var sTail = "00" + _RSASIGN_DIHEAD[hashAlg] + sHashHex;
- var sMid = "";
- var fLen = pmStrLen - sHead.length - sTail.length;
- for (var i = 0; i < fLen; i += 2) {
- sMid += "ff";
- }
- sPaddedMessageHex = sHead + sMid + sTail;
- return sPaddedMessageHex;
+ return KJUR.crypto.Util.getPaddedDigestInfoHex(sHashHex, hashAlg, keySize);
}
function _zeroPaddingOfSignature(hex, bitLength) {
- var s = "";
- var nZero = bitLength / 4 - hex.length;
- for (var i = 0; i < nZero; i++) {
- s = s + "0";
- }
- return s + hex;
+ var s = "";
+ var nZero = bitLength / 4 - hex.length;
+ for (var i = 0; i < nZero; i++) {
+ s = s + "0";
+ }
+ return s + hex;
}
/**
* sign for a message string with RSA private key.<br/>
* @name signString
- * @memberOf RSAKey#
+ * @memberOf RSAKey
* @function
* @param {String} s message string to be signed.
* @param {String} hashAlg hash algorithm name for signing.<br/>
* @return returns hexadecimal string of signature value.
*/
function _rsasign_signString(s, hashAlg) {
- //alert("this.n.bitLength() = " + this.n.bitLength());
- var hPM = _rsasign_getHexPaddedDigestInfoForString(s, this.n.bitLength(), hashAlg);
- var biPaddedMessage = parseBigInt(hPM, 16);
- var biSign = this.doPrivate(biPaddedMessage);
- var hexSign = biSign.toString(16);
- return _zeroPaddingOfSignature(hexSign, this.n.bitLength());
-}
+ var hashFunc = function(s) { return KJUR.crypto.Util.hashString(s, hashAlg); };
+ var sHashHex = hashFunc(s);
-//@author: ucla-cs
-function _rsasign_signStringHEX(s, hashAlg) {
- //alert("this.n.bitLength() = " + this.n.bitLength());
- var hPM = _rsasign_getHexPaddedDigestInfoForString(s, this.n.bitLength(), hashAlg);
- var biPaddedMessage = parseBigInt(hPM, 16);
- var biSign = this.doPrivate(biPaddedMessage);
- var hexSign = biSign.toString(16);
- return _zeroPaddingOfSignature(hexSign, this.n.bitLength());
+ return this.signWithMessageHash(sHashHex, hashAlg);
}
-
/**
- * Sign a message byteArray with an RSA private key
- * @name signByteArray
- * @memberOf RSAKey#
+ * sign hash value of message to be signed with RSA private key.<br/>
+ * @name signWithMessageHash
+ * @memberOf RSAKey
* @function
- * @param {byte[]} byteArray
- * @param {Sring} hashAlg the hash algorithm to apply
- * @param {RSAKey} rsa key to sign with: hack because the context is lost here
- * @return hexadecimal string of signature value
+ * @param {String} sHashHex hexadecimal string of hash value of message to be signed.
+ * @param {String} hashAlg hash algorithm name for signing.<br/>
+ * @return returns hexadecimal string of signature value.
+ * @since rsasign 1.2.6
*/
-function _rsasign_signByteArray(byteArray, hashAlg, rsaKey) {
- var hPM = _rsasign_getHexPaddedDigestInfoForByteArray(byteArray, rsaKey.n.bitLength(), hashAlg); ///hack because the context is lost here
- var biPaddedMessage = parseBigInt(hPM, 16);
- var biSign = rsaKey.doPrivate(biPaddedMessage); //hack because the context is lost here
- var hexSign = biSign.toString(16);
- return _zeroPaddingOfSignature(hexSign, rsaKey.n.bitLength()); //hack because the context is lost here
+function _rsasign_signWithMessageHash(sHashHex, hashAlg) {
+ var hPM = KJUR.crypto.Util.getPaddedDigestInfoHex(sHashHex, hashAlg, this.n.bitLength());
+ var biPaddedMessage = parseBigInt(hPM, 16);
+ var biSign = this.doPrivate(biPaddedMessage);
+ var hexSign = biSign.toString(16);
+ return _zeroPaddingOfSignature(hexSign, this.n.bitLength());
}
-/**
- * Sign a byte array with the Sha-256 algorithm
- * @param {byte[]} byteArray
- * @return hexadecimal string of signature value
- */
-function _rsasign_signByteArrayWithSHA256(byteArray){
- return _rsasign_signByteArray(byteArray, 'sha256', this); //Hack because the context is lost in the next function
-}
-
-
function _rsasign_signStringWithSHA1(s) {
- return _rsasign_signString(s, 'sha1');
+ return _rsasign_signString.call(this, s, 'sha1');
}
function _rsasign_signStringWithSHA256(s) {
- return _rsasign_signString(s, 'sha256');
+ return _rsasign_signString.call(this, s, 'sha256');
+}
+
+// PKCS#1 (PSS) mask generation function
+function pss_mgf1_str(seed, len, hash) {
+ var mask = '', i = 0;
+
+ while (mask.length < len) {
+ mask += hextorstr(hash(rstrtohex(seed + String.fromCharCode.apply(String, [
+ (i & 0xff000000) >> 24,
+ (i & 0x00ff0000) >> 16,
+ (i & 0x0000ff00) >> 8,
+ i & 0x000000ff]))));
+ i += 1;
+ }
+
+ return mask;
+}
+
+/**
+ * sign for a message string with RSA private key by PKCS#1 PSS signing.<br/>
+ * @name signStringPSS
+ * @memberOf RSAKey
+ * @function
+ * @param {String} s message string to be signed.
+ * @param {String} hashAlg hash algorithm name for signing.
+ * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
+ * There are two special values:
+ * <ul>
+ * <li>-1: sets the salt length to the digest length</li>
+ * <li>-2: sets the salt length to maximum permissible value
+ * (i.e. keybytelen - hashbytelen - 2)</li>
+ * </ul>
+ * DEFAULT is -1. (NOTE: OpenSSL's default is -2.)
+ * @return returns hexadecimal string of signature value.
+ */
+function _rsasign_signStringPSS(s, hashAlg, sLen) {
+ var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); }
+ var hHash = hashFunc(rstrtohex(s));
+
+ if (sLen === undefined) sLen = -1;
+ return this.signWithMessageHashPSS(hHash, hashAlg, sLen);
+}
+
+/**
+ * sign hash value of message with RSA private key by PKCS#1 PSS signing.<br/>
+ * @name signWithMessageHashPSS
+ * @memberOf RSAKey
+ * @function
+ * @param {String} hHash hexadecimal hash value of message to be signed.
+ * @param {String} hashAlg hash algorithm name for signing.
+ * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
+ * There are two special values:
+ * <ul>
+ * <li>-1: sets the salt length to the digest length</li>
+ * <li>-2: sets the salt length to maximum permissible value
+ * (i.e. keybytelen - hashbytelen - 2)</li>
+ * </ul>
+ * DEFAULT is -1. (NOTE: OpenSSL's default is -2.)
+ * @return returns hexadecimal string of signature value.
+ * @since rsasign 1.2.6
+ */
+function _rsasign_signWithMessageHashPSS(hHash, hashAlg, sLen) {
+ var mHash = hextorstr(hHash);
+ var hLen = mHash.length;
+ var emBits = this.n.bitLength() - 1;
+ var emLen = Math.ceil(emBits / 8);
+ var i;
+ var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); }
+
+ if (sLen === -1 || sLen === undefined) {
+ sLen = hLen; // same as hash length
+ } else if (sLen === -2) {
+ sLen = emLen - hLen - 2; // maximum
+ } else if (sLen < -2) {
+ throw "invalid salt length";
+ }
+
+ if (emLen < (hLen + sLen + 2)) {
+ throw "data too long";
+ }
+
+ var salt = '';
+
+ if (sLen > 0) {
+ salt = new Array(sLen);
+ new SecureRandom().nextBytes(salt);
+ salt = String.fromCharCode.apply(String, salt);
+ }
+
+ var H = hextorstr(hashFunc(rstrtohex('\x00\x00\x00\x00\x00\x00\x00\x00' + mHash + salt)));
+ var PS = [];
+
+ for (i = 0; i < emLen - sLen - hLen - 2; i += 1) {
+ PS[i] = 0x00;
+ }
+
+ var DB = String.fromCharCode.apply(String, PS) + '\x01' + salt;
+ var dbMask = pss_mgf1_str(H, DB.length, hashFunc);
+ var maskedDB = [];
+
+ for (i = 0; i < DB.length; i += 1) {
+ maskedDB[i] = DB.charCodeAt(i) ^ dbMask.charCodeAt(i);
+ }
+
+ var mask = (0xff00 >> (8 * emLen - emBits)) & 0xff;
+ maskedDB[0] &= ~mask;
+
+ for (i = 0; i < hLen; i++) {
+ maskedDB.push(H.charCodeAt(i));
+ }
+
+ maskedDB.push(0xbc);
+
+ return _zeroPaddingOfSignature(this.doPrivate(new BigInteger(maskedDB)).toString(16),
+ this.n.bitLength());
}
// ========================================================================
@@ -5643,47 +3878,47 @@
// ========================================================================
function _rsasign_getDecryptSignatureBI(biSig, hN, hE) {
- var rsa = new RSAKey();
- rsa.setPublic(hN, hE);
- var biDecryptedSig = rsa.doPublic(biSig);
- return biDecryptedSig;
+ var rsa = new RSAKey();
+ rsa.setPublic(hN, hE);
+ var biDecryptedSig = rsa.doPublic(biSig);
+ return biDecryptedSig;
}
function _rsasign_getHexDigestInfoFromSig(biSig, hN, hE) {
- var biDecryptedSig = _rsasign_getDecryptSignatureBI(biSig, hN, hE);
- var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
- return hDigestInfo;
+ var biDecryptedSig = _rsasign_getDecryptSignatureBI(biSig, hN, hE);
+ var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
+ return hDigestInfo;
}
function _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo) {
- for (var algName in _RSASIGN_DIHEAD) {
- var head = _RSASIGN_DIHEAD[algName];
- var len = head.length;
- if (hDigestInfo.substring(0, len) == head) {
- var a = [algName, hDigestInfo.substring(len)];
- return a;
+ for (var algName in KJUR.crypto.Util.DIGESTINFOHEAD) {
+ var head = KJUR.crypto.Util.DIGESTINFOHEAD[algName];
+ var len = head.length;
+ if (hDigestInfo.substring(0, len) == head) {
+ var a = [algName, hDigestInfo.substring(len)];
+ return a;
+ }
}
- }
- return [];
+ return [];
}
function _rsasign_verifySignatureWithArgs(sMsg, biSig, hN, hE) {
- var hDigestInfo = _rsasign_getHexDigestInfoFromSig(biSig, hN, hE);
- var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
- if (digestInfoAry.length == 0) return false;
- var algName = digestInfoAry[0];
- var diHashValue = digestInfoAry[1];
- var ff = _RSASIGN_HASHHEXFUNC[algName];
- var msgHashValue = ff(sMsg);
- return (diHashValue == msgHashValue);
+ var hDigestInfo = _rsasign_getHexDigestInfoFromSig(biSig, hN, hE);
+ var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
+ if (digestInfoAry.length == 0) return false;
+ var algName = digestInfoAry[0];
+ var diHashValue = digestInfoAry[1];
+ var ff = function(s) { return KJUR.crypto.Util.hashString(s, algName); };
+ var msgHashValue = ff(sMsg);
+ return (diHashValue == msgHashValue);
}
function _rsasign_verifyHexSignatureForMessage(hSig, sMsg) {
- var biSig = parseBigInt(hSig, 16);
- var result = _rsasign_verifySignatureWithArgs(sMsg, biSig,
- this.n.toString(16),
- this.e.toString(16));
- return result;
+ var biSig = parseBigInt(hSig, 16);
+ var result = _rsasign_verifySignatureWithArgs(sMsg, biSig,
+ this.n.toString(16),
+ this.e.toString(16));
+ return result;
}
/**
@@ -5697,661 +3932,1410 @@
* @return returns 1 if valid, otherwise 0
*/
function _rsasign_verifyString(sMsg, hSig) {
- hSig = hSig.replace(_RE_HEXDECONLY, '');
+ hSig = hSig.replace(_RE_HEXDECONLY, '');
+ hSig = hSig.replace(/[ \n]+/g, "");
+ var biSig = parseBigInt(hSig, 16);
+ if (biSig.bitLength() > this.n.bitLength()) return 0;
+ var biDecryptedSig = this.doPublic(biSig);
+ var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
+ var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
- if(LOG>3)console.log('n is '+this.n);
- if(LOG>3)console.log('e is '+this.e);
-
- if (hSig.length != this.n.bitLength() / 4) return 0;
- hSig = hSig.replace(/[ \n]+/g, "");
- var biSig = parseBigInt(hSig, 16);
- var biDecryptedSig = this.doPublic(biSig);
- var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
- var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
-
- if (digestInfoAry.length == 0) return false;
- var algName = digestInfoAry[0];
- var diHashValue = digestInfoAry[1];
- var ff = _RSASIGN_HASHHEXFUNC[algName];
- var msgHashValue = ff(sMsg);
- return (diHashValue == msgHashValue);
+ if (digestInfoAry.length == 0) return false;
+ var algName = digestInfoAry[0];
+ var diHashValue = digestInfoAry[1];
+ var ff = function(s) { return KJUR.crypto.Util.hashString(s, algName); };
+ var msgHashValue = ff(sMsg);
+ return (diHashValue == msgHashValue);
}
/**
- * verifies a sigature for a message byte array with RSA public key.<br/>
- * @name verifyByteArray
- * @memberOf RSAKey#
+ * verifies a sigature for a message string with RSA public key.<br/>
+ * @name verifyWithMessageHash
+ * @memberOf RSAKey
* @function
- * @param {byte[]} byteArray message byte array to be verified.
- * @param {String} hSig hexadecimal string of signature.<br/>
+ * @param {String} sHashHex hexadecimal hash value of message to be verified.
+ * @param {String} hSig hexadecimal string of siganture.<br/>
* non-hexadecimal charactors including new lines will be ignored.
- * @return returns 1 if valid, otherwise 0
+ * @return returns 1 if valid, otherwise 0
+ * @since rsasign 1.2.6
*/
-function _rsasign_verifyByteArray(byteArray, witness, hSig) {
- hSig = hSig.replace(_RE_HEXDECONLY, '');
-
- if(LOG>3)console.log('n is '+this.n);
- if(LOG>3)console.log('e is '+this.e);
-
- if (hSig.length != this.n.bitLength() / 4) return 0;
- hSig = hSig.replace(/[ \n]+/g, "");
- var biSig = parseBigInt(hSig, 16);
- var biDecryptedSig = this.doPublic(biSig);
- var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
- var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
-
- if (digestInfoAry.length == 0) return false;
- var algName = digestInfoAry[0];
- var diHashValue = digestInfoAry[1];
- var msgHashValue = null;
-
- if (witness == null) {
- var ff = _RSASIGN_HASHBYTEFUNC[algName];
- msgHashValue = ff(byteArray);
- } else {
- // Compute merkle hash
- var h = hex_sha256_from_bytes(byteArray);
- var index = witness.path.index;
- for (var i = witness.path.digestList.length - 1; i >= 0; i--) {
- var str = "";
- if (index % 2 == 0) {
- str = h + witness.path.digestList[i];
- } else {
- str = witness.path.digestList[i] + h;
- }
- h = hex_sha256_from_bytes(DataUtils.toNumbers(str));
- index = Math.floor(index / 2);
- }
- msgHashValue = hex_sha256_from_bytes(DataUtils.toNumbers(h));
- }
- //console.log(diHashValue);
- //console.log(msgHashValue);
- return (diHashValue == msgHashValue);
+function _rsasign_verifyWithMessageHash(sHashHex, hSig) {
+ hSig = hSig.replace(_RE_HEXDECONLY, '');
+ hSig = hSig.replace(/[ \n]+/g, "");
+ var biSig = parseBigInt(hSig, 16);
+ if (biSig.bitLength() > this.n.bitLength()) return 0;
+ var biDecryptedSig = this.doPublic(biSig);
+ var hDigestInfo = biDecryptedSig.toString(16).replace(/^1f+00/, '');
+ var digestInfoAry = _rsasign_getAlgNameAndHashFromHexDisgestInfo(hDigestInfo);
+
+ if (digestInfoAry.length == 0) return false;
+ var algName = digestInfoAry[0];
+ var diHashValue = digestInfoAry[1];
+ return (diHashValue == sHashHex);
}
+/**
+ * verifies a sigature for a message string with RSA public key by PKCS#1 PSS sign.<br/>
+ * @name verifyStringPSS
+ * @memberOf RSAKey
+ * @function
+ * @param {String} sMsg message string to be verified.
+ * @param {String} hSig hexadecimal string of signature value
+ * @param {String} hashAlg hash algorithm name
+ * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
+ * There are two special values:
+ * <ul>
+ * <li>-1: sets the salt length to the digest length</li>
+ * <li>-2: sets the salt length to maximum permissible value
+ * (i.e. keybytelen - hashbytelen - 2)</li>
+ * </ul>
+ * DEFAULT is -1. (NOTE: OpenSSL's default is -2.)
+ * @return returns true if valid, otherwise false
+ */
+function _rsasign_verifyStringPSS(sMsg, hSig, hashAlg, sLen) {
+ var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); };
+ var hHash = hashFunc(rstrtohex(sMsg));
+ if (sLen === undefined) sLen = -1;
+ return this.verifyWithMessageHashPSS(hHash, hSig, hashAlg, sLen);
+}
+
+/**
+ * verifies a sigature for a hash value of message string with RSA public key by PKCS#1 PSS sign.<br/>
+ * @name verifyWithMessageHashPSS
+ * @memberOf RSAKey
+ * @function
+ * @param {String} hHash hexadecimal hash value of message string to be verified.
+ * @param {String} hSig hexadecimal string of signature value
+ * @param {String} hashAlg hash algorithm name
+ * @param {Integer} sLen salt byte length from 0 to (keybytelen - hashbytelen - 2).
+ * There are two special values:
+ * <ul>
+ * <li>-1: sets the salt length to the digest length</li>
+ * <li>-2: sets the salt length to maximum permissible value
+ * (i.e. keybytelen - hashbytelen - 2)</li>
+ * </ul>
+ * DEFAULT is -1 (NOTE: OpenSSL's default is -2.)
+ * @return returns true if valid, otherwise false
+ * @since rsasign 1.2.6
+ */
+function _rsasign_verifyWithMessageHashPSS(hHash, hSig, hashAlg, sLen) {
+ var biSig = new BigInteger(hSig, 16);
+
+ if (biSig.bitLength() > this.n.bitLength()) {
+ return false;
+ }
+
+ var hashFunc = function(sHex) { return KJUR.crypto.Util.hashHex(sHex, hashAlg); };
+ var mHash = hextorstr(hHash);
+ var hLen = mHash.length;
+ var emBits = this.n.bitLength() - 1;
+ var emLen = Math.ceil(emBits / 8);
+ var i;
+
+ if (sLen === -1 || sLen === undefined) {
+ sLen = hLen; // same as hash length
+ } else if (sLen === -2) {
+ sLen = emLen - hLen - 2; // recover
+ } else if (sLen < -2) {
+ throw "invalid salt length";
+ }
+
+ if (emLen < (hLen + sLen + 2)) {
+ throw "data too long";
+ }
+
+ var em = this.doPublic(biSig).toByteArray();
+
+ for (i = 0; i < em.length; i += 1) {
+ em[i] &= 0xff;
+ }
+
+ while (em.length < emLen) {
+ em.unshift(0);
+ }
+
+ if (em[emLen -1] !== 0xbc) {
+ throw "encoded message does not end in 0xbc";
+ }
+
+ em = String.fromCharCode.apply(String, em);
+
+ var maskedDB = em.substr(0, emLen - hLen - 1);
+ var H = em.substr(maskedDB.length, hLen);
+
+ var mask = (0xff00 >> (8 * emLen - emBits)) & 0xff;
+
+ if ((maskedDB.charCodeAt(0) & mask) !== 0) {
+ throw "bits beyond keysize not zero";
+ }
+
+ var dbMask = pss_mgf1_str(H, maskedDB.length, hashFunc);
+ var DB = [];
+
+ for (i = 0; i < maskedDB.length; i += 1) {
+ DB[i] = maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i);
+ }
+
+ DB[0] &= ~mask;
+
+ var checkLen = emLen - hLen - sLen - 2;
+
+ for (i = 0; i < checkLen; i += 1) {
+ if (DB[i] !== 0x00) {
+ throw "leftmost octets not zero";
+ }
+ }
+
+ if (DB[checkLen] !== 0x01) {
+ throw "0x01 marker not found";
+ }
+
+ return H === hextorstr(hashFunc(rstrtohex('\x00\x00\x00\x00\x00\x00\x00\x00' + mHash +
+ String.fromCharCode.apply(String, DB.slice(-sLen)))));
+}
+
+RSAKey.prototype.signWithMessageHash = _rsasign_signWithMessageHash;
RSAKey.prototype.signString = _rsasign_signString;
-
-RSAKey.prototype.signByteArray = _rsasign_signByteArray; //@author axelcdv
-RSAKey.prototype.signByteArrayWithSHA256 = _rsasign_signByteArrayWithSHA256; //@author axelcdv
-
RSAKey.prototype.signStringWithSHA1 = _rsasign_signStringWithSHA1;
RSAKey.prototype.signStringWithSHA256 = _rsasign_signStringWithSHA256;
RSAKey.prototype.sign = _rsasign_signString;
RSAKey.prototype.signWithSHA1 = _rsasign_signStringWithSHA1;
RSAKey.prototype.signWithSHA256 = _rsasign_signStringWithSHA256;
+RSAKey.prototype.signWithMessageHashPSS = _rsasign_signWithMessageHashPSS;
+RSAKey.prototype.signStringPSS = _rsasign_signStringPSS;
+RSAKey.prototype.signPSS = _rsasign_signStringPSS;
+RSAKey.SALT_LEN_HLEN = -1;
+RSAKey.SALT_LEN_MAX = -2;
-/*RSAKey.prototype.signStringHEX = _rsasign_signStringHEX;
-RSAKey.prototype.signStringWithSHA1HEX = _rsasign_signStringWithSHA1HEX;
-RSAKey.prototype.signStringWithSHA256HEX = _rsasign_signStringWithSHA256HEX;
-RSAKey.prototype.signHEX = _rsasign_signStringHEX;
-RSAKey.prototype.signWithSHA1HEX = _rsasign_signStringWithSHA1HEX;
-RSAKey.prototype.signWithSHA256HEX = _rsasign_signStringWithSHA256HEX;
-*/
-
-RSAKey.prototype.verifyByteArray = _rsasign_verifyByteArray;
+RSAKey.prototype.verifyWithMessageHash = _rsasign_verifyWithMessageHash;
RSAKey.prototype.verifyString = _rsasign_verifyString;
RSAKey.prototype.verifyHexSignatureForMessage = _rsasign_verifyHexSignatureForMessage;
RSAKey.prototype.verify = _rsasign_verifyString;
RSAKey.prototype.verifyHexSignatureForByteArrayMessage = _rsasign_verifyHexSignatureForMessage;
-/*
-RSAKey.prototype.verifyStringHEX = _rsasign_verifyStringHEX;
-RSAKey.prototype.verifyHexSignatureForMessageHEX = _rsasign_verifyHexSignatureForMessageHEX;
-RSAKey.prototype.verifyHEX = _rsasign_verifyStringHEX;
-RSAKey.prototype.verifyHexSignatureForByteArrayMessageHEX = _rsasign_verifyHexSignatureForMessageHEX;
-*/
-
-
+RSAKey.prototype.verifyWithMessageHashPSS = _rsasign_verifyWithMessageHashPSS;
+RSAKey.prototype.verifyStringPSS = _rsasign_verifyStringPSS;
+RSAKey.prototype.verifyPSS = _rsasign_verifyStringPSS;
+RSAKey.SALT_LEN_RECOVER = -2;
+
/**
* @name RSAKey
- * @class
+ * @class key of RSA public key algorithm
* @description Tom Wu's RSA Key class and extension
*/
-/*! asn1hex-1.1.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license
- */
-//
-// asn1hex.js - Hexadecimal represented ASN.1 string library
-//
-// version: 1.1 (09-May-2012)
-//
-// Copyright (c) 2010-2012 Kenji Urushima (kenji.urushima@gmail.com)
-//
-// This software is licensed under the terms of the MIT License.
-// http://kjur.github.com/jsrsasign/license/
-//
-// The above copyright and license notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// Depends on:
-//
-// MEMO:
-// f('3082025b02...', 2) ... 82025b ... 3bytes
-// f('020100', 2) ... 01 ... 1byte
-// f('0203001...', 2) ... 03 ... 1byte
-// f('02818003...', 2) ... 8180 ... 2bytes
-// f('3080....0000', 2) ... 80 ... -1
-//
-// Requirements:
-// - ASN.1 type octet length MUST be 1.
-// (i.e. ASN.1 primitives like SET, SEQUENCE, INTEGER, OCTETSTRING ...)
-// -
-/**
- * get byte length for ASN.1 L(length) bytes
- * @name getByteLengthOfL_AtObj
- * @memberOf ASN1HEX
- * @function
- * @param {String} s hexadecimal string of ASN.1 DER encoded data
- * @param {Number} pos string index
- * @return byte length for ASN.1 L(length) bytes
+exports.RSAKey = RSAKey;
+module.exports = exports;
+/*! ecdsa-modified-1.0.4.js (c) Stephan Thomas, Kenji Urushima | github.com/bitcoinjs/bitcoinjs-lib/blob/master/LICENSE
*/
-function _asnhex_getByteLengthOfL_AtObj(s, pos) {
- if (s.substring(pos + 2, pos + 3) != '8') return 1;
- var i = parseInt(s.substring(pos + 3, pos + 4));
- if (i == 0) return -1; // length octet '80' indefinite length
- if (0 < i && i < 10) return i + 1; // including '8?' octet;
- return -2; // malformed format
-}
-
+/*
+ * ecdsa-modified.js - modified Bitcoin.ECDSA class
+ *
+ * Copyright (c) 2013 Stefan Thomas (github.com/justmoon)
+ * Kenji Urushima (kenji.urushima@gmail.com)
+ * LICENSE
+ * https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/LICENSE
+ */
/**
- * get hexadecimal string for ASN.1 L(length) bytes
- * @name getHexOfL_AtObj
- * @memberOf ASN1HEX
- * @function
- * @param {String} s hexadecimal string of ASN.1 DER encoded data
- * @param {Number} pos string index
- * @return {String} hexadecimal string for ASN.1 L(length) bytes
+ * @fileOverview
+ * @name ecdsa-modified-1.0.js
+ * @author Stefan Thomas (github.com/justmoon) and Kenji Urushima (kenji.urushima@gmail.com)
+ * @version 1.0.4 (2013-Oct-06)
+ * @since jsrsasign 4.0
+ * @license <a href="https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/LICENSE">MIT License</a>
*/
-function _asnhex_getHexOfL_AtObj(s, pos) {
- var len = _asnhex_getByteLengthOfL_AtObj(s, pos);
- if (len < 1) return '';
- return s.substring(pos + 2, pos + 2 + len * 2);
-}
-//
-// getting ASN.1 length value at the position 'idx' of
-// hexa decimal string 's'.
-//
-// f('3082025b02...', 0) ... 82025b ... ???
-// f('020100', 0) ... 01 ... 1
-// f('0203001...', 0) ... 03 ... 3
-// f('02818003...', 0) ... 8180 ... 128
-/**
- * get integer value of ASN.1 length for ASN.1 data
- * @name getIntOfL_AtObj
- * @memberOf ASN1HEX
- * @function
- * @param {String} s hexadecimal string of ASN.1 DER encoded data
- * @param {Number} pos string index
- * @return ASN.1 L(length) integer value
- */
-function _asnhex_getIntOfL_AtObj(s, pos) {
- var hLength = _asnhex_getHexOfL_AtObj(s, pos);
- if (hLength == '') return -1;
- var bi;
- if (parseInt(hLength.substring(0, 1)) < 8) {
- bi = parseBigInt(hLength, 16);
- } else {
- bi = parseBigInt(hLength.substring(2), 16);
- }
- return bi.intValue();
-}
+if (typeof KJUR == "undefined" || !KJUR) KJUR = {};
+if (typeof KJUR.crypto == "undefined" || !KJUR.crypto) KJUR.crypto = {};
/**
- * get ASN.1 value starting string position for ASN.1 object refered by index 'idx'.
- * @name getStartPosOfV_AtObj
- * @memberOf ASN1HEX
- * @function
- * @param {String} s hexadecimal string of ASN.1 DER encoded data
- * @param {Number} pos string index
+ * class for EC key generation, ECDSA signing and verifcation
+ * @name KJUR.crypto.ECDSA
+ * @class class for EC key generation, ECDSA signing and verifcation
+ * @description
+ * <p>
+ * CAUTION: Most of the case, you don't need to use this class except
+ * for generating an EC key pair. Please use {@link KJUR.crypto.Signature} class instead.
+ * </p>
+ * <p>
+ * This class was originally developped by Stefan Thomas for Bitcoin JavaScript library.
+ * (See {@link https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/ecdsa.js})
+ * Currently this class supports following named curves and their aliases.
+ * <ul>
+ * <li>secp256r1, NIST P-256, P-256, prime256v1 (*)</li>
+ * <li>secp256k1 (*)</li>
+ * <li>secp384r1, NIST P-384, P-384 (*)</li>
+ * </ul>
+ * </p>
*/
-function _asnhex_getStartPosOfV_AtObj(s, pos) {
- var l_len = _asnhex_getByteLengthOfL_AtObj(s, pos);
- if (l_len < 0) return l_len;
- return pos + (l_len + 1) * 2;
-}
+KJUR.crypto.ECDSA = function(params) {
+ var curveName = "secp256r1"; // curve name default
+ var ecparams = null;
+ var prvKeyHex = null;
+ var pubKeyHex = null;
+
+ var rng = new SecureRandom();
+
+ var P_OVER_FOUR = null;
+
+ this.type = "EC";
+
+ function implShamirsTrick(P, k, Q, l) {
+ var m = Math.max(k.bitLength(), l.bitLength());
+ var Z = P.add2D(Q);
+ var R = P.curve.getInfinity();
+
+ for (var i = m - 1; i >= 0; --i) {
+ R = R.twice2D();
+
+ R.z = BigInteger.ONE;
+
+ if (k.testBit(i)) {
+ if (l.testBit(i)) {
+ R = R.add2D(Z);
+ } else {
+ R = R.add2D(P);
+ }
+ } else {
+ if (l.testBit(i)) {
+ R = R.add2D(Q);
+ }
+ }
+ }
+
+ return R;
+ };
+
+ //===========================
+ // PUBLIC METHODS
+ //===========================
+ this.getBigRandom = function (limit) {
+ return new BigInteger(limit.bitLength(), rng)
+ .mod(limit.subtract(BigInteger.ONE))
+ .add(BigInteger.ONE)
+ ;
+ };
+
+ this.setNamedCurve = function(curveName) {
+ this.ecparams = KJUR.crypto.ECParameterDB.getByName(curveName);
+ this.prvKeyHex = null;
+ this.pubKeyHex = null;
+ this.curveName = curveName;
+ }
+
+ this.setPrivateKeyHex = function(prvKeyHex) {
+ this.isPrivate = true;
+ this.prvKeyHex = prvKeyHex;
+ }
+
+ this.setPublicKeyHex = function(pubKeyHex) {
+ this.isPublic = true;
+ this.pubKeyHex = pubKeyHex;
+ }
+
+ /**
+ * generate a EC key pair
+ * @name generateKeyPairHex
+ * @memberOf KJUR.crypto.ECDSA
+ * @function
+ * @return {Array} associative array of hexadecimal string of private and public key
+ * @since ecdsa-modified 1.0.1
+ * @example
+ * var ec = new KJUR.crypto.ECDSA({'curve': 'secp256r1'});
+ * var keypair = ec.generateKeyPairHex();
+ * var pubhex = keypair.ecpubhex; // hexadecimal string of EC private key (=d)
+ * var prvhex = keypair.ecprvhex; // hexadecimal string of EC public key
+ */
+ this.generateKeyPairHex = function() {
+ var biN = this.ecparams['n'];
+ var biPrv = this.getBigRandom(biN);
+ var epPub = this.ecparams['G'].multiply(biPrv);
+ var biX = epPub.getX().toBigInteger();
+ var biY = epPub.getY().toBigInteger();
+
+ var charlen = this.ecparams['keylen'] / 4;
+ var hPrv = ("0000000000" + biPrv.toString(16)).slice(- charlen);
+ var hX = ("0000000000" + biX.toString(16)).slice(- charlen);
+ var hY = ("0000000000" + biY.toString(16)).slice(- charlen);
+ var hPub = "04" + hX + hY;
+
+ this.setPrivateKeyHex(hPrv);
+ this.setPublicKeyHex(hPub);
+ return {'ecprvhex': hPrv, 'ecpubhex': hPub};
+ };
+
+ this.signWithMessageHash = function(hashHex) {
+ return this.signHex(hashHex, this.prvKeyHex);
+ };
+
+ /**
+ * signing to message hash
+ * @name signHex
+ * @memberOf KJUR.crypto.ECDSA
+ * @function
+ * @param {String} hashHex hexadecimal string of hash value of signing message
+ * @param {String} privHex hexadecimal string of EC private key
+ * @return {String} hexadecimal string of ECDSA signature
+ * @since ecdsa-modified 1.0.1
+ * @example
+ * var ec = new KJUR.crypto.ECDSA({'curve': 'secp256r1'});
+ * var sigValue = ec.signHex(hash, prvKey);
+ */
+ this.signHex = function (hashHex, privHex) {
+ var d = new BigInteger(privHex, 16);
+ var n = this.ecparams['n'];
+ var e = new BigInteger(hashHex, 16);
+
+ do {
+ var k = this.getBigRandom(n);
+ var G = this.ecparams['G'];
+ var Q = G.multiply(k);
+ var r = Q.getX().toBigInteger().mod(n);
+ } while (r.compareTo(BigInteger.ZERO) <= 0);
+
+ var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
+
+ return KJUR.crypto.ECDSA.biRSSigToASN1Sig(r, s);
+ };
+
+ this.sign = function (hash, priv) {
+ var d = priv;
+ var n = this.ecparams['n'];
+ var e = BigInteger.fromByteArrayUnsigned(hash);
+
+ do {
+ var k = this.getBigRandom(n);
+ var G = this.ecparams['G'];
+ var Q = G.multiply(k);
+ var r = Q.getX().toBigInteger().mod(n);
+ } while (r.compareTo(BigInteger.ZERO) <= 0);
+
+ var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
+ return this.serializeSig(r, s);
+ };
+
+ this.verifyWithMessageHash = function(hashHex, sigHex) {
+ return this.verifyHex(hashHex, sigHex, this.pubKeyHex);
+ };
+
+ /**
+ * verifying signature with message hash and public key
+ * @name verifyHex
+ * @memberOf KJUR.crypto.ECDSA
+ * @function
+ * @param {String} hashHex hexadecimal string of hash value of signing message
+ * @param {String} sigHex hexadecimal string of signature value
+ * @param {String} pubkeyHex hexadecimal string of public key
+ * @return {Boolean} true if the signature is valid, otherwise false
+ * @since ecdsa-modified 1.0.1
+ * @example
+ * var ec = new KJUR.crypto.ECDSA({'curve': 'secp256r1'});
+ * var result = ec.verifyHex(msgHashHex, sigHex, pubkeyHex);
+ */
+ this.verifyHex = function(hashHex, sigHex, pubkeyHex) {
+ var r,s;
+
+ var obj = KJUR.crypto.ECDSA.parseSigHex(sigHex);
+ r = obj.r;
+ s = obj.s;
+
+ var Q;
+ Q = ECPointFp.decodeFromHex(this.ecparams['curve'], pubkeyHex);
+ var e = new BigInteger(hashHex, 16);
+
+ return this.verifyRaw(e, r, s, Q);
+ };
+
+ this.verify = function (hash, sig, pubkey) {
+ var r,s;
+ if (Bitcoin.Util.isArray(sig)) {
+ var obj = this.parseSig(sig);
+ r = obj.r;
+ s = obj.s;
+ } else if ("object" === typeof sig && sig.r && sig.s) {
+ r = sig.r;
+ s = sig.s;
+ } else {
+ throw "Invalid value for signature";
+ }
+
+ var Q;
+ if (pubkey instanceof ECPointFp) {
+ Q = pubkey;
+ } else if (Bitcoin.Util.isArray(pubkey)) {
+ Q = ECPointFp.decodeFrom(this.ecparams['curve'], pubkey);
+ } else {
+ throw "Invalid format for pubkey value, must be byte array or ECPointFp";
+ }
+ var e = BigInteger.fromByteArrayUnsigned(hash);
+
+ return this.verifyRaw(e, r, s, Q);
+ };
+
+ this.verifyRaw = function (e, r, s, Q) {
+ var n = this.ecparams['n'];
+ var G = this.ecparams['G'];
+
+ if (r.compareTo(BigInteger.ONE) < 0 ||
+ r.compareTo(n) >= 0)
+ return false;
+
+ if (s.compareTo(BigInteger.ONE) < 0 ||
+ s.compareTo(n) >= 0)
+ return false;
+
+ var c = s.modInverse(n);
+
+ var u1 = e.multiply(c).mod(n);
+ var u2 = r.multiply(c).mod(n);
+
+ // TODO(!!!): For some reason Shamir's trick isn't working with
+ // signed message verification!? Probably an implementation
+ // error!
+ //var point = implShamirsTrick(G, u1, Q, u2);
+ var point = G.multiply(u1).add(Q.multiply(u2));
+
+ var v = point.getX().toBigInteger().mod(n);
+
+ return v.equals(r);
+ };
+
+ /**
+ * Serialize a signature into DER format.
+ *
+ * Takes two BigIntegers representing r and s and returns a byte array.
+ */
+ this.serializeSig = function (r, s) {
+ var rBa = r.toByteArraySigned();
+ var sBa = s.toByteArraySigned();
+
+ var sequence = [];
+ sequence.push(0x02); // INTEGER
+ sequence.push(rBa.length);
+ sequence = sequence.concat(rBa);
+
+ sequence.push(0x02); // INTEGER
+ sequence.push(sBa.length);
+ sequence = sequence.concat(sBa);
+
+ sequence.unshift(sequence.length);
+ sequence.unshift(0x30); // SEQUENCE
+ return sequence;
+ };
+
+ /**
+ * Parses a byte array containing a DER-encoded signature.
+ *
+ * This function will return an object of the form:
+ *
+ * {
+ * r: BigInteger,
+ * s: BigInteger
+ * }
+ */
+ this.parseSig = function (sig) {
+ var cursor;
+ if (sig[0] != 0x30)
+ throw new Error("Signature not a valid DERSequence");
+
+ cursor = 2;
+ if (sig[cursor] != 0x02)
+ throw new Error("First element in signature must be a DERInteger");;
+ var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]);
+
+ cursor += 2+sig[cursor+1];
+ if (sig[cursor] != 0x02)
+ throw new Error("Second element in signature must be a DERInteger");
+ var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]);
+
+ cursor += 2+sig[cursor+1];
+
+ //if (cursor != sig.length)
+ // throw new Error("Extra bytes in signature");
+
+ var r = BigInteger.fromByteArrayUnsigned(rBa);
+ var s = BigInteger.fromByteArrayUnsigned(sBa);
+
+ return {r: r, s: s};
+ };
+
+ this.parseSigCompact = function (sig) {
+ if (sig.length !== 65) {
+ throw "Signature has the wrong length";
+ }
+
+ // Signature is prefixed with a type byte storing three bits of
+ // information.
+ var i = sig[0] - 27;
+ if (i < 0 || i > 7) {
+ throw "Invalid signature type";
+ }
+
+ var n = this.ecparams['n'];
+ var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n);
+ var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n);
+
+ return {r: r, s: s, i: i};
+ };
+
+ /*
+ * Recover a public key from a signature.
+ *
+ * See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public
+ * Key Recovery Operation".
+ *
+ * http://www.secg.org/download/aid-780/sec1-v2.pdf
+ */
+ /*
+ recoverPubKey: function (r, s, hash, i) {
+ // The recovery parameter i has two bits.
+ i = i & 3;
+
+ // The less significant bit specifies whether the y coordinate
+ // of the compressed point is even or not.
+ var isYEven = i & 1;
+
+ // The more significant bit specifies whether we should use the
+ // first or second candidate key.
+ var isSecondKey = i >> 1;
+
+ var n = this.ecparams['n'];
+ var G = this.ecparams['G'];
+ var curve = this.ecparams['curve'];
+ var p = curve.getQ();
+ var a = curve.getA().toBigInteger();
+ var b = curve.getB().toBigInteger();
+
+ // We precalculate (p + 1) / 4 where p is if the field order
+ if (!P_OVER_FOUR) {
+ P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4));
+ }
+
+ // 1.1 Compute x
+ var x = isSecondKey ? r.add(n) : r;
+
+ // 1.3 Convert x to point
+ var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p);
+ var beta = alpha.modPow(P_OVER_FOUR, p);
+
+ var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2);
+ // If beta is even, but y isn't or vice versa, then convert it,
+ // otherwise we're done and y == beta.
+ var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta);
+
+ // 1.4 Check that nR is at infinity
+ var R = new ECPointFp(curve,
+ curve.fromBigInteger(x),
+ curve.fromBigInteger(y));
+ R.validate();
+
+ // 1.5 Compute e from M
+ var e = BigInteger.fromByteArrayUnsigned(hash);
+ var eNeg = BigInteger.ZERO.subtract(e).mod(n);
+
+ // 1.6 Compute Q = r^-1 (sR - eG)
+ var rInv = r.modInverse(n);
+ var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv);
+
+ Q.validate();
+ if (!this.verifyRaw(e, r, s, Q)) {
+ throw "Pubkey recovery unsuccessful";
+ }
+
+ var pubKey = new Bitcoin.ECKey();
+ pubKey.pub = Q;
+ return pubKey;
+ },
+ */
+
+ /*
+ * Calculate pubkey extraction parameter.
+ *
+ * When extracting a pubkey from a signature, we have to
+ * distinguish four different cases. Rather than putting this
+ * burden on the verifier, Bitcoin includes a 2-bit value with the
+ * signature.
+ *
+ * This function simply tries all four cases and returns the value
+ * that resulted in a successful pubkey recovery.
+ */
+ /*
+ calcPubkeyRecoveryParam: function (address, r, s, hash) {
+ for (var i = 0; i < 4; i++) {
+ try {
+ var pubkey = Bitcoin.ECDSA.recoverPubKey(r, s, hash, i);
+ if (pubkey.getBitcoinAddress().toString() == address) {
+ return i;
+ }
+ } catch (e) {}
+ }
+ throw "Unable to find valid recovery factor";
+ }
+ */
+
+ if (params !== undefined) {
+ if (params['curve'] !== undefined) {
+ this.curveName = params['curve'];
+ }
+ }
+ if (this.curveName === undefined) this.curveName = curveName;
+ this.setNamedCurve(this.curveName);
+ if (params !== undefined) {
+ if (params['prv'] !== undefined) this.setPrivateKeyHex(params['prv']);
+ if (params['pub'] !== undefined) this.setPublicKeyHex(params['pub']);
+ }
+};
/**
- * get hexadecimal string of ASN.1 V(value)
- * @name getHexOfV_AtObj
- * @memberOf ASN1HEX
+ * parse ASN.1 DER encoded ECDSA signature
+ * @name parseSigHex
+ * @memberOf KJUR.crypto.ECDSA
* @function
- * @param {String} s hexadecimal string of ASN.1 DER encoded data
- * @param {Number} pos string index
- * @return {String} hexadecimal string of ASN.1 value.
+ * @static
+ * @param {String} sigHex hexadecimal string of ECDSA signature value
+ * @return {Array} associative array of signature field r and s of BigInteger
+ * @since ecdsa-modified 1.0.1
+ * @example
+ * var ec = new KJUR.crypto.ECDSA({'curve': 'secp256r1'});
+ * var sig = ec.parseSigHex('30...');
+ * var biR = sig.r; // BigInteger object for 'r' field of signature.
+ * var biS = sig.s; // BigInteger object for 's' field of signature.
*/
-function _asnhex_getHexOfV_AtObj(s, pos) {
- var pos1 = _asnhex_getStartPosOfV_AtObj(s, pos);
- var len = _asnhex_getIntOfL_AtObj(s, pos);
- return s.substring(pos1, pos1 + len * 2);
-}
+KJUR.crypto.ECDSA.parseSigHex = function(sigHex) {
+ var p = KJUR.crypto.ECDSA.parseSigHexInHexRS(sigHex);
+ var biR = new BigInteger(p.r, 16);
+ var biS = new BigInteger(p.s, 16);
+
+ return {'r': biR, 's': biS};
+};
/**
- * get hexadecimal string of ASN.1 TLV at
- * @name getHexOfTLV_AtObj
- * @memberOf ASN1HEX
+ * parse ASN.1 DER encoded ECDSA signature
+ * @name parseSigHexInHexRS
+ * @memberOf KJUR.crypto.ECDSA
* @function
- * @param {String} s hexadecimal string of ASN.1 DER encoded data
- * @param {Number} pos string index
- * @return {String} hexadecimal string of ASN.1 TLV.
- * @since 1.1
+ * @static
+ * @param {String} sigHex hexadecimal string of ECDSA signature value
+ * @return {Array} associative array of signature field r and s in hexadecimal
+ * @since ecdsa-modified 1.0.3
+ * @example
+ * var ec = new KJUR.crypto.ECDSA({'curve': 'secp256r1'});
+ * var sig = ec.parseSigHexInHexRS('30...');
+ * var hR = sig.r; // hexadecimal string for 'r' field of signature.
+ * var hS = sig.s; // hexadecimal string for 's' field of signature.
*/
-function _asnhex_getHexOfTLV_AtObj(s, pos) {
- var hT = s.substr(pos, 2);
- var hL = _asnhex_getHexOfL_AtObj(s, pos);
- var hV = _asnhex_getHexOfV_AtObj(s, pos);
- return hT + hL + hV;
-}
+KJUR.crypto.ECDSA.parseSigHexInHexRS = function(sigHex) {
+ // 1. ASN.1 Sequence Check
+ if (sigHex.substr(0, 2) != "30")
+ throw "signature is not a ASN.1 sequence";
+
+ // 2. Items of ASN.1 Sequence Check
+ var a = ASN1HEX.getPosArrayOfChildren_AtObj(sigHex, 0);
+ if (a.length != 2)
+ throw "number of signature ASN.1 sequence elements seem wrong";
+
+ // 3. Integer check
+ var iTLV1 = a[0];
+ var iTLV2 = a[1];
+ if (sigHex.substr(iTLV1, 2) != "02")
+ throw "1st item of sequene of signature is not ASN.1 integer";
+ if (sigHex.substr(iTLV2, 2) != "02")
+ throw "2nd item of sequene of signature is not ASN.1 integer";
+
+ // 4. getting value
+ var hR = ASN1HEX.getHexOfV_AtObj(sigHex, iTLV1);
+ var hS = ASN1HEX.getHexOfV_AtObj(sigHex, iTLV2);
+
+ return {'r': hR, 's': hS};
+};
/**
- * get next sibling starting index for ASN.1 object string
- * @name getPosOfNextSibling_AtObj
- * @memberOf ASN1HEX
+ * convert hexadecimal ASN.1 encoded signature to concatinated signature
+ * @name asn1SigToConcatSig
+ * @memberOf KJUR.crypto.ECDSA
* @function
- * @param {String} s hexadecimal string of ASN.1 DER encoded data
- * @param {Number} pos string index
- * @return next sibling starting index for ASN.1 object string
+ * @static
+ * @param {String} asn1Hex hexadecimal string of ASN.1 encoded ECDSA signature value
+ * @return {String} r-s concatinated format of ECDSA signature value
+ * @since ecdsa-modified 1.0.3
*/
-function _asnhex_getPosOfNextSibling_AtObj(s, pos) {
- var pos1 = _asnhex_getStartPosOfV_AtObj(s, pos);
- var len = _asnhex_getIntOfL_AtObj(s, pos);
- return pos1 + len * 2;
-}
+KJUR.crypto.ECDSA.asn1SigToConcatSig = function(asn1Sig) {
+ var pSig = KJUR.crypto.ECDSA.parseSigHexInHexRS(asn1Sig);
+ var hR = pSig.r;
+ var hS = pSig.s;
+
+ if (hR.substr(0, 2) == "00" && (((hR.length / 2) * 8) % (16 * 8)) == 8)
+ hR = hR.substr(2);
+
+ if (hS.substr(0, 2) == "00" && (((hS.length / 2) * 8) % (16 * 8)) == 8)
+ hS = hS.substr(2);
+
+ if ((((hR.length / 2) * 8) % (16 * 8)) != 0)
+ throw "unknown ECDSA sig r length error";
+
+ if ((((hS.length / 2) * 8) % (16 * 8)) != 0)
+ throw "unknown ECDSA sig s length error";
+
+ return hR + hS;
+};
/**
- * get array of indexes of child ASN.1 objects
- * @name getPosArrayOfChildren_AtObj
- * @memberOf ASN1HEX
+ * convert hexadecimal concatinated signature to ASN.1 encoded signature
+ * @name concatSigToASN1Sig
+ * @memberOf KJUR.crypto.ECDSA
* @function
- * @param {String} s hexadecimal string of ASN.1 DER encoded data
- * @param {Number} start string index of ASN.1 object
- * @return {Array of Number} array of indexes for childen of ASN.1 objects
+ * @static
+ * @param {String} concatSig r-s concatinated format of ECDSA signature value
+ * @return {String} hexadecimal string of ASN.1 encoded ECDSA signature value
+ * @since ecdsa-modified 1.0.3
*/
-function _asnhex_getPosArrayOfChildren_AtObj(h, pos) {
- var a = new Array();
- var p0 = _asnhex_getStartPosOfV_AtObj(h, pos);
- a.push(p0);
+KJUR.crypto.ECDSA.concatSigToASN1Sig = function(concatSig) {
+ if ((((concatSig.length / 2) * 8) % (16 * 8)) != 0)
+ throw "unknown ECDSA concatinated r-s sig length error";
- var len = _asnhex_getIntOfL_AtObj(h, pos);
- var p = p0;
- var k = 0;
- while (1) {
- var pNext = _asnhex_getPosOfNextSibling_AtObj(h, p);
- if (pNext == null || (pNext - p0 >= (len * 2))) break;
- if (k >= 200) break;
-
- a.push(pNext);
- p = pNext;
-
- k++;
- }
-
- return a;
-}
+ var hR = concatSig.substr(0, concatSig.length / 2);
+ var hS = concatSig.substr(concatSig.length / 2);
+ return KJUR.crypto.ECDSA.hexRSSigToASN1Sig(hR, hS);
+};
/**
- * get string index of nth child object of ASN.1 object refered by h, idx
- * @name getNthChildIndex_AtObj
- * @memberOf ASN1HEX
+ * convert hexadecimal R and S value of signature to ASN.1 encoded signature
+ * @name hexRSSigToASN1Sig
+ * @memberOf KJUR.crypto.ECDSA
* @function
- * @param {String} h hexadecimal string of ASN.1 DER encoded data
- * @param {Number} idx start string index of ASN.1 object
- * @param {Number} nth for child
- * @return {Number} string index of nth child.
- * @since 1.1
+ * @static
+ * @param {String} hR hexadecimal string of R field of ECDSA signature value
+ * @param {String} hS hexadecimal string of S field of ECDSA signature value
+ * @return {String} hexadecimal string of ASN.1 encoded ECDSA signature value
+ * @since ecdsa-modified 1.0.3
*/
-function _asnhex_getNthChildIndex_AtObj(h, idx, nth) {
- var a = _asnhex_getPosArrayOfChildren_AtObj(h, idx);
- return a[nth];
-}
-
-// ========== decendant methods ==============================
+KJUR.crypto.ECDSA.hexRSSigToASN1Sig = function(hR, hS) {
+ var biR = new BigInteger(hR, 16);
+ var biS = new BigInteger(hS, 16);
+ return KJUR.crypto.ECDSA.biRSSigToASN1Sig(biR, biS);
+};
/**
- * get string index of nth child object of ASN.1 object refered by h, idx
- * @name getDecendantIndexByNthList
- * @memberOf ASN1HEX
+ * convert R and S BigInteger object of signature to ASN.1 encoded signature
+ * @name biRSSigToASN1Sig
+ * @memberOf KJUR.crypto.ECDSA
* @function
- * @param {String} h hexadecimal string of ASN.1 DER encoded data
- * @param {Number} currentIndex start string index of ASN.1 object
- * @param {Array of Number} nthList array list of nth
- * @return {Number} string index refered by nthList
- * @since 1.1
+ * @static
+ * @param {BigInteger} biR BigInteger object of R field of ECDSA signature value
+ * @param {BigInteger} biS BIgInteger object of S field of ECDSA signature value
+ * @return {String} hexadecimal string of ASN.1 encoded ECDSA signature value
+ * @since ecdsa-modified 1.0.3
*/
-function _asnhex_getDecendantIndexByNthList(h, currentIndex, nthList) {
- if (nthList.length == 0) {
- return currentIndex;
- }
- var firstNth = nthList.shift();
- var a = _asnhex_getPosArrayOfChildren_AtObj(h, currentIndex);
- return _asnhex_getDecendantIndexByNthList(h, a[firstNth], nthList);
-}
+KJUR.crypto.ECDSA.biRSSigToASN1Sig = function(biR, biS) {
+ var derR = new KJUR.asn1.DERInteger({'bigint': biR});
+ var derS = new KJUR.asn1.DERInteger({'bigint': biS});
+ var derSeq = new KJUR.asn1.DERSequence({'array': [derR, derS]});
+ return derSeq.getEncodedHex();
+};
+
+/*! asn1hex-1.1.6.js (c) 2012-2015 Kenji Urushima | kjur.github.com/jsrsasign/license
+ */
+/*
+ * asn1hex.js - Hexadecimal represented ASN.1 string library
+ *
+ * Copyright (c) 2010-2015 Kenji Urushima (kenji.urushima@gmail.com)
+ *
+ * This software is licensed under the terms of the MIT License.
+ * http://kjur.github.com/jsrsasign/license/
+ *
+ * The above copyright and license notice shall be
+ * included in all copies or substantial portions of the Software.
+ */
/**
- * get hexadecimal string of ASN.1 TLV refered by current index and nth index list.
- * @name getDecendantHexTLVByNthList
- * @memberOf ASN1HEX
- * @function
- * @param {String} h hexadecimal string of ASN.1 DER encoded data
- * @param {Number} currentIndex start string index of ASN.1 object
- * @param {Array of Number} nthList array list of nth
- * @return {Number} hexadecimal string of ASN.1 TLV refered by nthList
- * @since 1.1
+ * @fileOverview
+ * @name asn1hex-1.1.js
+ * @author Kenji Urushima kenji.urushima@gmail.com
+ * @version asn1hex 1.1.6 (2015-Jun-11)
+ * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
*/
-function _asnhex_getDecendantHexTLVByNthList(h, currentIndex, nthList) {
- var idx = _asnhex_getDecendantIndexByNthList(h, currentIndex, nthList);
- return _asnhex_getHexOfTLV_AtObj(h, idx);
-}
-/**
- * get hexadecimal string of ASN.1 V refered by current index and nth index list.
- * @name getDecendantHexVByNthList
- * @memberOf ASN1HEX
- * @function
- * @param {String} h hexadecimal string of ASN.1 DER encoded data
- * @param {Number} currentIndex start string index of ASN.1 object
- * @param {Array of Number} nthList array list of nth
- * @return {Number} hexadecimal string of ASN.1 V refered by nthList
- * @since 1.1
+/*
+ * MEMO:
+ * f('3082025b02...', 2) ... 82025b ... 3bytes
+ * f('020100', 2) ... 01 ... 1byte
+ * f('0203001...', 2) ... 03 ... 1byte
+ * f('02818003...', 2) ... 8180 ... 2bytes
+ * f('3080....0000', 2) ... 80 ... -1
+ *
+ * Requirements:
+ * - ASN.1 type octet length MUST be 1.
+ * (i.e. ASN.1 primitives like SET, SEQUENCE, INTEGER, OCTETSTRING ...)
*/
-function _asnhex_getDecendantHexVByNthList(h, currentIndex, nthList) {
- var idx = _asnhex_getDecendantIndexByNthList(h, currentIndex, nthList);
- return _asnhex_getHexOfV_AtObj(h, idx);
-}
-
-// ========== class definition ==============================
/**
* ASN.1 DER encoded hexadecimal string utility class
+ * @name ASN1HEX
* @class ASN.1 DER encoded hexadecimal string utility class
- * @author Kenji Urushima
- * @version 1.1 (09 May 2012)
- * @see <a href="http://kjur.github.com/jsrsasigns/">'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/</a>
- * @since 1.1
+ * @since jsrsasign 1.1
*/
-function ASN1HEX() {
- return ASN1HEX;
-}
+var intShim = require('jsbn');
-ASN1HEX.getByteLengthOfL_AtObj = _asnhex_getByteLengthOfL_AtObj;
-ASN1HEX.getHexOfL_AtObj = _asnhex_getHexOfL_AtObj;
-ASN1HEX.getIntOfL_AtObj = _asnhex_getIntOfL_AtObj;
-ASN1HEX.getStartPosOfV_AtObj = _asnhex_getStartPosOfV_AtObj;
-ASN1HEX.getHexOfV_AtObj = _asnhex_getHexOfV_AtObj;
-ASN1HEX.getHexOfTLV_AtObj = _asnhex_getHexOfTLV_AtObj;
-ASN1HEX.getPosOfNextSibling_AtObj = _asnhex_getPosOfNextSibling_AtObj;
-ASN1HEX.getPosArrayOfChildren_AtObj = _asnhex_getPosArrayOfChildren_AtObj;
-ASN1HEX.getNthChildIndex_AtObj = _asnhex_getNthChildIndex_AtObj;
-ASN1HEX.getDecendantIndexByNthList = _asnhex_getDecendantIndexByNthList;
-ASN1HEX.getDecendantHexVByNthList = _asnhex_getDecendantHexVByNthList;
-ASN1HEX.getDecendantHexTLVByNthList = _asnhex_getDecendantHexTLVByNthList;
-/*! x509-1.1.js (c) 2012 Kenji Urushima | kjur.github.com/jsrsasign/license
+var ASN1HEX = new function() {
+ /**
+ * get byte length for ASN.1 L(length) bytes
+ * @name getByteLengthOfL_AtObj
+ * @memberOf ASN1HEX
+ * @function
+ * @param {String} s hexadecimal string of ASN.1 DER encoded data
+ * @param {Number} pos string index
+ * @return byte length for ASN.1 L(length) bytes
+ */
+ this.getByteLengthOfL_AtObj = function(s, pos) {
+ if (s.substring(pos + 2, pos + 3) != '8') return 1;
+ var i = parseInt(s.substring(pos + 3, pos + 4));
+ if (i == 0) return -1; // length octet '80' indefinite length
+ if (0 < i && i < 10) return i + 1; // including '8?' octet;
+ return -2; // malformed format
+ };
+
+ /**
+ * get hexadecimal string for ASN.1 L(length) bytes
+ * @name getHexOfL_AtObj
+ * @memberOf ASN1HEX
+ * @function
+ * @param {String} s hexadecimal string of ASN.1 DER encoded data
+ * @param {Number} pos string index
+ * @return {String} hexadecimal string for ASN.1 L(length) bytes
+ */
+ this.getHexOfL_AtObj = function(s, pos) {
+ var len = this.getByteLengthOfL_AtObj(s, pos);
+ if (len < 1) return '';
+ return s.substring(pos + 2, pos + 2 + len * 2);
+ };
+
+ // getting ASN.1 length value at the position 'idx' of
+ // hexa decimal string 's'.
+ //
+ // f('3082025b02...', 0) ... 82025b ... ???
+ // f('020100', 0) ... 01 ... 1
+ // f('0203001...', 0) ... 03 ... 3
+ // f('02818003...', 0) ... 8180 ... 128
+ /**
+ * get integer value of ASN.1 length for ASN.1 data
+ * @name getIntOfL_AtObj
+ * @memberOf ASN1HEX
+ * @function
+ * @param {String} s hexadecimal string of ASN.1 DER encoded data
+ * @param {Number} pos string index
+ * @return ASN.1 L(length) integer value
+ */
+ this.getIntOfL_AtObj = function(s, pos) {
+ var hLength = this.getHexOfL_AtObj(s, pos);
+ if (hLength == '') return -1;
+ var bi;
+ if (parseInt(hLength.substring(0, 1)) < 8) {
+ bi = new BigInteger(hLength, 16);
+ } else {
+ bi = new BigInteger(hLength.substring(2), 16);
+ }
+ return bi.intValue();
+ };
+
+ /**
+ * get ASN.1 value starting string position for ASN.1 object refered by index 'idx'.
+ * @name getStartPosOfV_AtObj
+ * @memberOf ASN1HEX
+ * @function
+ * @param {String} s hexadecimal string of ASN.1 DER encoded data
+ * @param {Number} pos string index
+ */
+ this.getStartPosOfV_AtObj = function(s, pos) {
+ var l_len = this.getByteLengthOfL_AtObj(s, pos);
+ if (l_len < 0) return l_len;
+ return pos + (l_len + 1) * 2;
+ };
+
+ /**
+ * get hexadecimal string of ASN.1 V(value)
+ * @name getHexOfV_AtObj
+ * @memberOf ASN1HEX
+ * @function
+ * @param {String} s hexadecimal string of ASN.1 DER encoded data
+ * @param {Number} pos string index
+ * @return {String} hexadecimal string of ASN.1 value.
+ */
+ this.getHexOfV_AtObj = function(s, pos) {
+ var pos1 = this.getStartPosOfV_AtObj(s, pos);
+ var len = this.getIntOfL_AtObj(s, pos);
+ return s.substring(pos1, pos1 + len * 2);
+ };
+
+ /**
+ * get hexadecimal string of ASN.1 TLV at
+ * @name getHexOfTLV_AtObj
+ * @memberOf ASN1HEX
+ * @function
+ * @param {String} s hexadecimal string of ASN.1 DER encoded data
+ * @param {Number} pos string index
+ * @return {String} hexadecimal string of ASN.1 TLV.
+ * @since 1.1
+ */
+ this.getHexOfTLV_AtObj = function(s, pos) {
+ var hT = s.substr(pos, 2);
+ var hL = this.getHexOfL_AtObj(s, pos);
+ var hV = this.getHexOfV_AtObj(s, pos);
+ return hT + hL + hV;
+ };
+
+ /**
+ * get next sibling starting index for ASN.1 object string
+ * @name getPosOfNextSibling_AtObj
+ * @memberOf ASN1HEX
+ * @function
+ * @param {String} s hexadecimal string of ASN.1 DER encoded data
+ * @param {Number} pos string index
+ * @return next sibling starting index for ASN.1 object string
+ */
+ this.getPosOfNextSibling_AtObj = function(s, pos) {
+ var pos1 = this.getStartPosOfV_AtObj(s, pos);
+ var len = this.getIntOfL_AtObj(s, pos);
+ return pos1 + len * 2;
+ };
+
+ /**
+ * get array of indexes of child ASN.1 objects
+ * @name getPosArrayOfChildren_AtObj
+ * @memberOf ASN1HEX
+ * @function
+ * @param {String} s hexadecimal string of ASN.1 DER encoded data
+ * @param {Number} start string index of ASN.1 object
+ * @return {Array of Number} array of indexes for childen of ASN.1 objects
+ */
+ this.getPosArrayOfChildren_AtObj = function(h, pos) {
+ var a = new Array();
+ var p0 = this.getStartPosOfV_AtObj(h, pos);
+ a.push(p0);
+
+ var len = this.getIntOfL_AtObj(h, pos);
+ var p = p0;
+ var k = 0;
+ while (1) {
+ var pNext = this.getPosOfNextSibling_AtObj(h, p);
+ if (pNext == null || (pNext - p0 >= (len * 2))) break;
+ if (k >= 200) break;
+
+ a.push(pNext);
+ p = pNext;
+
+ k++;
+ }
+
+ return a;
+ };
+
+ /**
+ * get string index of nth child object of ASN.1 object refered by h, idx
+ * @name getNthChildIndex_AtObj
+ * @memberOf ASN1HEX
+ * @function
+ * @param {String} h hexadecimal string of ASN.1 DER encoded data
+ * @param {Number} idx start string index of ASN.1 object
+ * @param {Number} nth for child
+ * @return {Number} string index of nth child.
+ * @since 1.1
+ */
+ this.getNthChildIndex_AtObj = function(h, idx, nth) {
+ var a = this.getPosArrayOfChildren_AtObj(h, idx);
+ return a[nth];
+ };
+
+ // ========== decendant methods ==============================
+ /**
+ * get string index of nth child object of ASN.1 object refered by h, idx
+ * @name getDecendantIndexByNthList
+ * @memberOf ASN1HEX
+ * @function
+ * @param {String} h hexadecimal string of ASN.1 DER encoded data
+ * @param {Number} currentIndex start string index of ASN.1 object
+ * @param {Array of Number} nthList array list of nth
+ * @return {Number} string index refered by nthList
+ * @since 1.1
+ * @example
+ * The "nthList" is a index list of structured ASN.1 object
+ * reference. Here is a sample structure and "nthList"s which
+ * refers each objects.
+ *
+ * SQUENCE -
+ * SEQUENCE - [0]
+ * IA5STRING 000 - [0, 0]
+ * UTF8STRING 001 - [0, 1]
+ * SET - [1]
+ * IA5STRING 010 - [1, 0]
+ * UTF8STRING 011 - [1, 1]
+ */
+ this.getDecendantIndexByNthList = function(h, currentIndex, nthList) {
+ if (nthList.length == 0) {
+ return currentIndex;
+ }
+ var firstNth = nthList.shift();
+ var a = this.getPosArrayOfChildren_AtObj(h, currentIndex);
+ return this.getDecendantIndexByNthList(h, a[firstNth], nthList);
+ };
+
+ /**
+ * get hexadecimal string of ASN.1 TLV refered by current index and nth index list.
+ * @name getDecendantHexTLVByNthList
+ * @memberOf ASN1HEX
+ * @function
+ * @param {String} h hexadecimal string of ASN.1 DER encoded data
+ * @param {Number} currentIndex start string index of ASN.1 object
+ * @param {Array of Number} nthList array list of nth
+ * @return {Number} hexadecimal string of ASN.1 TLV refered by nthList
+ * @since 1.1
+ */
+ this.getDecendantHexTLVByNthList = function(h, currentIndex, nthList) {
+ var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList);
+ return this.getHexOfTLV_AtObj(h, idx);
+ };
+
+ /**
+ * get hexadecimal string of ASN.1 V refered by current index and nth index list.
+ * @name getDecendantHexVByNthList
+ * @memberOf ASN1HEX
+ * @function
+ * @param {String} h hexadecimal string of ASN.1 DER encoded data
+ * @param {Number} currentIndex start string index of ASN.1 object
+ * @param {Array of Number} nthList array list of nth
+ * @return {Number} hexadecimal string of ASN.1 V refered by nthList
+ * @since 1.1
+ */
+ this.getDecendantHexVByNthList = function(h, currentIndex, nthList) {
+ var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList);
+ return this.getHexOfV_AtObj(h, idx);
+ };
+};
+
+/*
+ * @since asn1hex 1.1.4
*/
-//
-// x509.js - X509 class to read subject public key from certificate.
-//
-// version: 1.1 (10-May-2012)
-//
-// Copyright (c) 2010-2012 Kenji Urushima (kenji.urushima@gmail.com)
-//
-// This software is licensed under the terms of the MIT License.
-// http://kjur.github.com/jsrsasign/license
-//
-// The above copyright and license notice shall be
-// included in all copies or substantial portions of the Software.
-//
-
-// Depends:
-// base64.js
-// rsa.js
-// asn1hex.js
-
-function _x509_pemToBase64(sCertPEM) {
- var s = sCertPEM;
- s = s.replace("-----BEGIN CERTIFICATE-----", "");
- s = s.replace("-----END CERTIFICATE-----", "");
- s = s.replace(/[ \n]+/g, "");
- return s;
-}
-
-function _x509_pemToHex(sCertPEM) {
- var b64Cert = _x509_pemToBase64(sCertPEM);
- var hCert = b64tohex(b64Cert);
- return hCert;
-}
-
-function _x509_getHexTbsCertificateFromCert(hCert) {
- var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0);
- return pTbsCert;
-}
-
-// NOTE: privateKeyUsagePeriod field of X509v2 not supported.
-// NOTE: v1 and v3 supported
-function _x509_getSubjectPublicKeyInfoPosFromCertHex(hCert) {
- var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0);
- var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pTbsCert);
- if (a.length < 1) return -1;
- if (hCert.substring(a[0], a[0] + 10) == "a003020102") { // v3
- if (a.length < 6) return -1;
- return a[6];
- } else {
- if (a.length < 5) return -1;
- return a[5];
- }
-}
-
-// NOTE: Without BITSTRING encapsulation.
-// If pInfo is supplied, it is the position in hCert of the SubjectPublicKeyInfo.
-function _x509_getSubjectPublicKeyPosFromCertHex(hCert, pInfo) {
- if (pInfo == null)
- pInfo = _x509_getSubjectPublicKeyInfoPosFromCertHex(hCert);
- if (pInfo == -1) return -1;
- var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pInfo);
-
- if (a.length != 2) return -1;
- var pBitString = a[1];
- if (hCert.substring(pBitString, pBitString + 2) != '03') return -1;
- var pBitStringV = ASN1HEX.getStartPosOfV_AtObj(hCert, pBitString);
-
- if (hCert.substring(pBitStringV, pBitStringV + 2) != '00') return -1;
- return pBitStringV + 2;
-}
-
-// If p is supplied, it is the public key position in hCert.
-function _x509_getPublicKeyHexArrayFromCertHex(hCert, p) {
- if (p == null)
- p = _x509_getSubjectPublicKeyPosFromCertHex(hCert);
- var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, p);
- //var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a[3]);
- if(LOG>4){
- console.log('a is now');
- console.log(a);
- }
-
- //if (a.length != 2) return [];
- if (a.length < 2) return [];
-
- var hN = ASN1HEX.getHexOfV_AtObj(hCert, a[0]);
- var hE = ASN1HEX.getHexOfV_AtObj(hCert, a[1]);
- if (hN != null && hE != null) {
- return [hN, hE];
- } else {
- return [];
- }
-}
-
-function _x509_getPublicKeyHexArrayFromCertPEM(sCertPEM) {
- var hCert = _x509_pemToHex(sCertPEM);
- var a = _x509_getPublicKeyHexArrayFromCertHex(hCert);
- return a;
-}
-
-// ===== get basic fields from hex =====================================
-/**
- * get hexadecimal string of serialNumber field of certificate.<br/>
- * @name getSerialNumberHex
- * @memberOf X509#
- * @function
- */
-function _x509_getSerialNumberHex() {
- return ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 1]);
-}
+ASN1HEX.getVbyList = function(h, currentIndex, nthList, checkingTag) {
+ var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList);
+ if (idx === undefined) {
+ throw "can't find nthList object";
+ }
+ if (checkingTag !== undefined) {
+ if (h.substr(idx, 2) != checkingTag) {
+ throw "checking tag doesn't match: " +
+ h.substr(idx,2) + "!=" + checkingTag;
+ }
+ }
+ return this.getHexOfV_AtObj(h, idx);
+};
/**
- * get hexadecimal string of issuer field of certificate.<br/>
- * @name getIssuerHex
- * @memberOf X509#
+ * get OID string from hexadecimal encoded value
+ * @name hextooidstr
+ * @memberOf ASN1HEX
* @function
+ * @param {String} hex hexadecmal string of ASN.1 DER encoded OID value
+ * @return {String} OID string (ex. '1.2.3.4.567')
+ * @since asn1hex 1.1.5
*/
-function _x509_getIssuerHex() {
- return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3]);
-}
+ASN1HEX.hextooidstr = function(hex) {
+ var zeroPadding = function(s, len) {
+ if (s.length >= len) return s;
+ return new Array(len - s.length + 1).join('0') + s;
+ };
+
+ var a = [];
+
+ // a[0], a[1]
+ var hex0 = hex.substr(0, 2);
+ var i0 = parseInt(hex0, 16);
+ a[0] = new String(Math.floor(i0 / 40));
+ a[1] = new String(i0 % 40);
+
+ // a[2]..a[n]
+ var hex1 = hex.substr(2);
+ var b = [];
+ for (var i = 0; i < hex1.length / 2; i++) {
+ b.push(parseInt(hex1.substr(i * 2, 2), 16));
+ }
+ var c = [];
+ var cbin = "";
+ for (var i = 0; i < b.length; i++) {
+ if (b[i] & 0x80) {
+ cbin = cbin + zeroPadding((b[i] & 0x7f).toString(2), 7);
+ } else {
+ cbin = cbin + zeroPadding((b[i] & 0x7f).toString(2), 7);
+ c.push(new String(parseInt(cbin, 2)));
+ cbin = "";
+ }
+ }
+
+ var s = a.join(".");
+ if (c.length > 0) s = s + "." + c.join(".");
+ return s;
+};
/**
- * get string of issuer field of certificate.<br/>
- * @name getIssuerString
- * @memberOf X509#
+ * get string of simple ASN.1 dump from hexadecimal ASN.1 data
+ * @name dump
+ * @memberOf ASN1HEX
* @function
+ * @param {String} hex hexadecmal string of ASN.1 data
+ * @param {Array} associative array of flags for dump (OPTION)
+ * @param {Number} idx string index for starting dump (OPTION)
+ * @param {String} indent string (OPTION)
+ * @return {String} string of simple ASN.1 dump
+ * @since jsrsasign 4.8.3 asn1hex 1.1.6
+ * @description
+ * This method will get an ASN.1 dump from
+ * hexadecmal string of ASN.1 DER encoded data.
+ * Here are features:
+ * <ul>
+ * <li>ommit long hexadecimal string</li>
+ * <li>dump encapsulated OCTET STRING (good for X.509v3 extensions)</li>
+ * <li>structured/primitive context specific tag support (i.e. [0], [3] ...)</li>
+ * <li>automatic decode for implicit primitive context specific tag
+ * (good for X.509v3 extension value)
+ * <ul>
+ * <li>if hex starts '68747470'(i.e. http) it is decoded as utf8 encoded string.</li>
+ * <li>if it is in 'subjectAltName' extension value and is '[2]'(dNSName) tag
+ * value will be encoded as utf8 string</li>
+ * <li>otherwise it shows as hexadecimal string</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * @example
+ * // ASN.1 INTEGER
+ * ASN1HEX.dump('0203012345')
+ * ↓
+ * INTEGER 012345
+ * // ASN.1 Object Identifier
+ * ASN1HEX.dump('06052b0e03021a')
+ * ↓
+ * ObjectIdentifier sha1 (1 3 14 3 2 26)
+ * // ASN.1 SEQUENCE
+ * ASN1HEX.dump('3006020101020102')
+ * ↓
+ * SEQUENCE
+ * INTEGER 01
+ * INTEGER 02
+ * // ASN.1 DUMP FOR X.509 CERTIFICATE
+ * ASN1HEX.dump(X509.pemToHex(certPEM))
+ * ↓
+ * SEQUENCE
+ * SEQUENCE
+ * [0]
+ * INTEGER 02
+ * INTEGER 0c009310d206dbe337553580118ddc87
+ * SEQUENCE
+ * ObjectIdentifier SHA256withRSA (1 2 840 113549 1 1 11)
+ * NULL
+ * SEQUENCE
+ * SET
+ * SEQUENCE
+ * ObjectIdentifier countryName (2 5 4 6)
+ * PrintableString 'US'
+ * :
*/
-function _x509_getIssuerString() {
- return _x509_hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3]));
-}
+ASN1HEX.dump = function(hex, flags, idx, indent) {
+ var _skipLongHex = function(hex, limitNumOctet) {
+ if (hex.length <= limitNumOctet * 2) {
+ return hex;
+ } else {
+ var s = hex.substr(0, limitNumOctet) +
+ "..(total " + hex.length / 2 + "bytes).." +
+ hex.substr(hex.length - limitNumOctet, limitNumOctet);
+ return s;
+ };
+ };
+
+ if (flags === undefined) flags = { "ommit_long_octet": 32 };
+ if (idx === undefined) idx = 0;
+ if (indent === undefined) indent = "";
+ var skipLongHex = flags.ommit_long_octet;
+
+ if (hex.substr(idx, 2) == "01") {
+ var v = ASN1HEX.getHexOfV_AtObj(hex, idx);
+ if (v == "00") {
+ return indent + "BOOLEAN FALSE\n";
+ } else {
+ return indent + "BOOLEAN TRUE\n";
+ }
+ }
+ if (hex.substr(idx, 2) == "02") {
+ var v = ASN1HEX.getHexOfV_AtObj(hex, idx);
+ return indent + "INTEGER " + _skipLongHex(v, skipLongHex) + "\n";
+ }
+ if (hex.substr(idx, 2) == "03") {
+ var v = ASN1HEX.getHexOfV_AtObj(hex, idx);
+ return indent + "BITSTRING " + _skipLongHex(v, skipLongHex) + "\n";
+ }
+ if (hex.substr(idx, 2) == "04") {
+ var v = ASN1HEX.getHexOfV_AtObj(hex, idx);
+ if (ASN1HEX.isASN1HEX(v)) {
+ var s = indent + "OCTETSTRING, encapsulates\n";
+ s = s + ASN1HEX.dump(v, flags, 0, indent + " ");
+ return s;
+ } else {
+ return indent + "OCTETSTRING " + _skipLongHex(v, skipLongHex) + "\n";
+ }
+ }
+ if (hex.substr(idx, 2) == "05") {
+ return indent + "NULL\n";
+ }
+ if (hex.substr(idx, 2) == "06") {
+ var hV = ASN1HEX.getHexOfV_AtObj(hex, idx);
+ var oidDot = KJUR.asn1.ASN1Util.oidHexToInt(hV);
+ var oidName = KJUR.asn1.x509.OID.oid2name(oidDot);
+ var oidSpc = oidDot.replace(/\./g, ' ');
+ if (oidName != '') {
+ return indent + "ObjectIdentifier " + oidName + " (" + oidSpc + ")\n";
+ } else {
+ return indent + "ObjectIdentifier (" + oidSpc + ")\n";
+ }
+ }
+ if (hex.substr(idx, 2) == "0c") {
+ return indent + "UTF8String '" + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "'\n";
+ }
+ if (hex.substr(idx, 2) == "13") {
+ return indent + "PrintableString '" + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "'\n";
+ }
+ if (hex.substr(idx, 2) == "14") {
+ return indent + "TeletexString '" + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "'\n";
+ }
+ if (hex.substr(idx, 2) == "16") {
+ return indent + "IA5String '" + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "'\n";
+ }
+ if (hex.substr(idx, 2) == "17") {
+ return indent + "UTCTime " + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "\n";
+ }
+ if (hex.substr(idx, 2) == "18") {
+ return indent + "GeneralizedTime " + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "\n";
+ }
+ if (hex.substr(idx, 2) == "30") {
+ if (hex.substr(idx, 4) == "3000") {
+ return indent + "SEQUENCE {}\n";
+ }
+
+ var s = indent + "SEQUENCE\n";
+ var aIdx = ASN1HEX.getPosArrayOfChildren_AtObj(hex, idx);
+
+ var flagsTemp = flags;
+
+ if ((aIdx.length == 2 || aIdx.length == 3) &&
+ hex.substr(aIdx[0], 2) == "06" &&
+ hex.substr(aIdx[aIdx.length - 1], 2) == "04") { // supposed X.509v3 extension
+ var oidHex = ASN1HEX.getHexOfV_AtObj(hex, aIdx[0]);
+ var oidDot = KJUR.asn1.ASN1Util.oidHexToInt(oidHex);
+ var oidName = KJUR.asn1.x509.OID.oid2name(oidDot);
+
+ var flagsClone = JSON.parse(JSON.stringify(flags));
+ flagsClone.x509ExtName = oidName;
+ flagsTemp = flagsClone;
+ }
+
+ for (var i = 0; i < aIdx.length; i++) {
+ s = s + ASN1HEX.dump(hex, flagsTemp, aIdx[i], indent + " ");
+ }
+ return s;
+ }
+ if (hex.substr(idx, 2) == "31") {
+ var s = indent + "SET\n";
+ var aIdx = ASN1HEX.getPosArrayOfChildren_AtObj(hex, idx);
+ for (var i = 0; i < aIdx.length; i++) {
+ s = s + ASN1HEX.dump(hex, flags, aIdx[i], indent + " ");
+ }
+ return s;
+ }
+ var tag = parseInt(hex.substr(idx, 2), 16);
+ if ((tag & 128) != 0) { // context specific
+ var tagNumber = tag & 31;
+ if ((tag & 32) != 0) { // structured tag
+ var s = indent + "[" + tagNumber + "]\n";
+ var aIdx = ASN1HEX.getPosArrayOfChildren_AtObj(hex, idx);
+ for (var i = 0; i < aIdx.length; i++) {
+ s = s + ASN1HEX.dump(hex, flags, aIdx[i], indent + " ");
+ }
+ return s;
+ } else { // primitive tag
+ var v = ASN1HEX.getHexOfV_AtObj(hex, idx);
+ if (v.substr(0, 8) == "68747470") { // http
+ v = hextoutf8(v);
+ }
+ if (flags.x509ExtName === "subjectAltName" &&
+ tagNumber == 2) {
+ v = hextoutf8(v);
+ }
+
+ var s = indent + "[" + tagNumber + "] " + v + "\n";
+ return s;
+ }
+ }
+ return indent + "UNKNOWN(" + hex.substr(idx, 2) + ") " + ASN1HEX.getHexOfV_AtObj(hex, idx) + "\n";
+};
/**
- * get hexadecimal string of subject field of certificate.<br/>
- * @name getSubjectHex
- * @memberOf X509#
+ * check wheather the string is ASN.1 hexadecimal string or not
+ * @name isASN1HEX
+ * @memberOf ASN1HEX
* @function
+ * @param {String} hex string to check whether it is hexadecmal string for ASN.1 DER or not
+ * @return {Boolean} true if it is hexadecimal string of ASN.1 data otherwise false
+ * @since jsrsasign 4.8.3 asn1hex 1.1.6
+ * @description
+ * This method checks wheather the argument 'hex' is a hexadecimal string of
+ * ASN.1 data or not.
+ * @example
+ * ASN1HEX.isASN1HEX('0203012345') → true // PROPER ASN.1 INTEGER
+ * ASN1HEX.isASN1HEX('0203012345ff') → false // TOO LONG VALUE
+ * ASN1HEX.isASN1HEX('02030123') → false // TOO SHORT VALUE
+ * ASN1HEX.isASN1HEX('fa3bcd') → false // WRONG FOR ASN.1
*/
-function _x509_getSubjectHex() {
- return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5]);
-}
+ASN1HEX.isASN1HEX = function(hex) {
+ if (hex.length % 2 == 1) return false;
+
+ var intL = ASN1HEX.getIntOfL_AtObj(hex, 0);
+ var tV = hex.substr(0, 2);
+ var lV = ASN1HEX.getHexOfL_AtObj(hex, 0);
+ var hVLength = hex.length - tV.length - lV.length;
+ if (hVLength == intL * 2) return true;
+
+ return false;
+};
+
+exports.ASN1HEX = ASN1HEX;
+module.exports = exports;
+/*! x509-1.1.6.js (c) 2012-2015 Kenji Urushima | kjur.github.com/jsrsasign/license
+ */
+/*
+ * x509.js - X509 class to read subject public key from certificate.
+ *
+ * Copyright (c) 2010-2015 Kenji Urushima (kenji.urushima@gmail.com)
+ *
+ * This software is licensed under the terms of the MIT License.
+ * http://kjur.github.com/jsrsasign/license
+ *
+ * The above copyright and license notice shall be
+ * included in all copies or substantial portions of the Software.
+ */
/**
- * get string of subject field of certificate.<br/>
- * @name getSubjectString
- * @memberOf X509#
- * @function
+ * @fileOverview
+ * @name x509-1.1.js
+ * @author Kenji Urushima kenji.urushima@gmail.com
+ * @version x509 1.1.6 (2015-May-20)
+ * @since jsrsasign 1.x.x
+ * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
*/
-function _x509_getSubjectString() {
- return _x509_hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5]));
-}
-/**
- * get notBefore field string of certificate.<br/>
- * @name getNotBefore
- * @memberOf X509#
- * @function
+/*
+ * Depends:
+ * base64.js
+ * rsa.js
+ * asn1hex.js
*/
-function _x509_getNotBefore() {
- var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 0]);
- s = s.replace(/(..)/g, "%$1");
- s = decodeURIComponent(s);
- return s;
-}
-
-/**
- * get notAfter field string of certificate.<br/>
- * @name getNotAfter
- * @memberOf X509#
- * @function
- */
-function _x509_getNotAfter() {
- var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 1]);
- s = s.replace(/(..)/g, "%$1");
- s = decodeURIComponent(s);
- return s;
-}
-
-// ===== read certificate =====================================
-
-_x509_DN_ATTRHEX = {
- "0603550406": "C",
- "060355040a": "O",
- "060355040b": "OU",
- "0603550403": "CN",
- "0603550405": "SN",
- "0603550408": "ST",
- "0603550407": "L" };
-
-function _x509_hex2dn(hDN) {
- var s = "";
- var a = ASN1HEX.getPosArrayOfChildren_AtObj(hDN, 0);
- for (var i = 0; i < a.length; i++) {
- var hRDN = ASN1HEX.getHexOfTLV_AtObj(hDN, a[i]);
- s = s + "/" + _x509_hex2rdn(hRDN);
- }
- return s;
-}
-
-function _x509_hex2rdn(hRDN) {
- var hType = ASN1HEX.getDecendantHexTLVByNthList(hRDN, 0, [0, 0]);
- var hValue = ASN1HEX.getDecendantHexVByNthList(hRDN, 0, [0, 1]);
- var type = "";
- try { type = _x509_DN_ATTRHEX[hType]; } catch (ex) { type = hType; }
- hValue = hValue.replace(/(..)/g, "%$1");
- var value = decodeURIComponent(hValue);
- return type + "=" + value;
-}
-
-// ===== read certificate =====================================
-
-
-/**
- * read PEM formatted X.509 certificate from string.<br/>
- * @name readCertPEM
- * @memberOf X509#
- * @function
- * @param {String} sCertPEM string for PEM formatted X.509 certificate
- */
-function _x509_readCertPEM(sCertPEM) {
- var hCert = _x509_pemToHex(sCertPEM);
- var a = _x509_getPublicKeyHexArrayFromCertHex(hCert);
- if(LOG>4){
- console.log('HEX VALUE IS ' + hCert);
- console.log('type of a' + typeof a);
- console.log('a VALUE IS ');
- console.log(a);
- console.log('a[0] VALUE IS ' + a[0]);
- console.log('a[1] VALUE IS ' + a[1]);
- }
- var rsa = new RSAKey();
- rsa.setPublic(a[0], a[1]);
- this.subjectPublicKeyRSA = rsa;
- this.subjectPublicKeyRSA_hN = a[0];
- this.subjectPublicKeyRSA_hE = a[1];
- this.hex = hCert;
-}
-
-/**
- * read hex formatted X.509 certificate from string.
- * @name readCertHex
- * @memberOf X509#
- * @function
- * @param {String} hCert string for hex formatted X.509 certificate
- */
-function _x509_readCertHex(hCert) {
- hCert = hCert.toLowerCase();
- var a = _x509_getPublicKeyHexArrayFromCertHex(hCert);
- var rsa = new RSAKey();
- rsa.setPublic(a[0], a[1]);
- this.subjectPublicKeyRSA = rsa;
- this.subjectPublicKeyRSA_hN = a[0];
- this.subjectPublicKeyRSA_hE = a[1];
- this.hex = hCert;
-}
-
-function _x509_readCertPEMWithoutRSAInit(sCertPEM) {
- var hCert = _x509_pemToHex(sCertPEM);
- var a = _x509_getPublicKeyHexArrayFromCertHex(hCert);
- this.subjectPublicKeyRSA.setPublic(a[0], a[1]);
- this.subjectPublicKeyRSA_hN = a[0];
- this.subjectPublicKeyRSA_hE = a[1];
- this.hex = hCert;
-}
/**
* X.509 certificate class.<br/>
@@ -6364,24 +5348,698 @@
* @version 1.0.1 (08 May 2012)
* @see <a href="http://kjur.github.com/jsrsasigns/">'jwrsasign'(RSA Sign JavaScript Library) home page http://kjur.github.com/jsrsasign/</a>
*/
+
+var b64tohex = require('./base64.js').b64tohex;
+var RSAKey = require('./rsa.js').RSAKey;
+var ASN1HEX = require('./asn1hex-1.1.js').ASN1HEX;
+
function X509() {
- this.subjectPublicKeyRSA = null;
- this.subjectPublicKeyRSA_hN = null;
- this.subjectPublicKeyRSA_hE = null;
- this.hex = null;
-}
+ this.subjectPublicKeyRSA = null;
+ this.subjectPublicKeyRSA_hN = null;
+ this.subjectPublicKeyRSA_hE = null;
+ this.hex = null;
-X509.prototype.readCertPEM = _x509_readCertPEM;
-X509.prototype.readCertHex = _x509_readCertHex;
-X509.prototype.readCertPEMWithoutRSAInit = _x509_readCertPEMWithoutRSAInit;
-X509.prototype.getSerialNumberHex = _x509_getSerialNumberHex;
-X509.prototype.getIssuerHex = _x509_getIssuerHex;
-X509.prototype.getSubjectHex = _x509_getSubjectHex;
-X509.prototype.getIssuerString = _x509_getIssuerString;
-X509.prototype.getSubjectString = _x509_getSubjectString;
-X509.prototype.getNotBefore = _x509_getNotBefore;
-X509.prototype.getNotAfter = _x509_getNotAfter;
+ // ===== get basic fields from hex =====================================
+ /**
+ * get hexadecimal string of serialNumber field of certificate.<br/>
+ * @name getSerialNumberHex
+ * @memberOf X509#
+ * @function
+ */
+ this.getSerialNumberHex = function() {
+ return ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 1]);
+ };
+
+ /**
+ * get hexadecimal string of issuer field TLV of certificate.<br/>
+ * @name getIssuerHex
+ * @memberOf X509#
+ * @function
+ */
+ this.getIssuerHex = function() {
+ return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3]);
+ };
+
+ /**
+ * get string of issuer field of certificate.<br/>
+ * @name getIssuerString
+ * @memberOf X509#
+ * @function
+ */
+ this.getIssuerString = function() {
+ return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 3]));
+ };
+
+ /**
+ * get hexadecimal string of subject field of certificate.<br/>
+ * @name getSubjectHex
+ * @memberOf X509#
+ * @function
+ */
+ this.getSubjectHex = function() {
+ return ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5]);
+ };
+
+ /**
+ * get string of subject field of certificate.<br/>
+ * @name getSubjectString
+ * @memberOf X509#
+ * @function
+ */
+ this.getSubjectString = function() {
+ return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex, 0, [0, 5]));
+ };
+
+ /**
+ * get notBefore field string of certificate.<br/>
+ * @name getNotBefore
+ * @memberOf X509#
+ * @function
+ */
+ this.getNotBefore = function() {
+ var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 0]);
+ s = s.replace(/(..)/g, "%$1");
+ s = decodeURIComponent(s);
+ return s;
+ };
+
+ /**
+ * get notAfter field string of certificate.<br/>
+ * @name getNotAfter
+ * @memberOf X509#
+ * @function
+ */
+ this.getNotAfter = function() {
+ var s = ASN1HEX.getDecendantHexVByNthList(this.hex, 0, [0, 4, 1]);
+ s = s.replace(/(..)/g, "%$1");
+ s = decodeURIComponent(s);
+ return s;
+ };
+
+ // ===== read certificate public key ==========================
+
+ // ===== read certificate =====================================
+ /**
+ * read PEM formatted X.509 certificate from string.<br/>
+ * @name readCertPEM
+ * @memberOf X509#
+ * @function
+ * @param {String} sCertPEM string for PEM formatted X.509 certificate
+ */
+ this.readCertPEM = function(sCertPEM) {
+ var hCert = X509.pemToHex(sCertPEM);
+ var a = X509.getPublicKeyHexArrayFromCertHex(hCert);
+ var rsa = new RSAKey();
+ rsa.setPublic(a[0], a[1]);
+ this.subjectPublicKeyRSA = rsa;
+ this.subjectPublicKeyRSA_hN = a[0];
+ this.subjectPublicKeyRSA_hE = a[1];
+ this.hex = hCert;
+ };
+
+ this.readCertPEMWithoutRSAInit = function(sCertPEM) {
+ var hCert = X509.pemToHex(sCertPEM);
+ var a = X509.getPublicKeyHexArrayFromCertHex(hCert);
+ this.subjectPublicKeyRSA.setPublic(a[0], a[1]);
+ this.subjectPublicKeyRSA_hN = a[0];
+ this.subjectPublicKeyRSA_hE = a[1];
+ this.hex = hCert;
+ };
+};
+
+X509.pemToBase64 = function(sCertPEM) {
+ var s = sCertPEM;
+ s = s.replace("-----BEGIN CERTIFICATE-----", "");
+ s = s.replace("-----END CERTIFICATE-----", "");
+ s = s.replace(/[ \n]+/g, "");
+ return s;
+};
+
+X509.pemToHex = function(sCertPEM) {
+ var b64Cert = X509.pemToBase64(sCertPEM);
+ var hCert = b64tohex(b64Cert);
+ return hCert;
+};
+
+// NOTE: Without BITSTRING encapsulation.
+X509.getSubjectPublicKeyPosFromCertHex = function(hCert) {
+ var pInfo = X509.getSubjectPublicKeyInfoPosFromCertHex(hCert);
+ if (pInfo == -1) return -1;
+ var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pInfo);
+ if (a.length != 2) return -1;
+ var pBitString = a[1];
+ if (hCert.substring(pBitString, pBitString + 2) != '03') return -1;
+ var pBitStringV = ASN1HEX.getStartPosOfV_AtObj(hCert, pBitString);
+
+ if (hCert.substring(pBitStringV, pBitStringV + 2) != '00') return -1;
+ return pBitStringV + 2;
+};
+
+// NOTE: privateKeyUsagePeriod field of X509v2 not supported.
+// NOTE: v1 and v3 supported
+X509.getSubjectPublicKeyInfoPosFromCertHex = function(hCert) {
+ var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0);
+ var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pTbsCert);
+ if (a.length < 1) return -1;
+ if (hCert.substring(a[0], a[0] + 10) == "a003020102") { // v3
+ if (a.length < 6) return -1;
+ return a[6];
+ } else {
+ if (a.length < 5) return -1;
+ return a[5];
+ }
+};
+
+X509.getPublicKeyHexArrayFromCertHex = function(hCert) {
+ var p = X509.getSubjectPublicKeyPosFromCertHex(hCert);
+ var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, p);
+ if (a.length != 2) return [];
+ var hN = ASN1HEX.getHexOfV_AtObj(hCert, a[0]);
+ var hE = ASN1HEX.getHexOfV_AtObj(hCert, a[1]);
+ if (hN != null && hE != null) {
+ return [hN, hE];
+ } else {
+ return [];
+ }
+};
+
+X509.getHexTbsCertificateFromCert = function(hCert) {
+ var pTbsCert = ASN1HEX.getStartPosOfV_AtObj(hCert, 0);
+ return pTbsCert;
+};
+
+X509.getPublicKeyHexArrayFromCertPEM = function(sCertPEM) {
+ var hCert = X509.pemToHex(sCertPEM);
+ var a = X509.getPublicKeyHexArrayFromCertHex(hCert);
+ return a;
+};
+
+X509.hex2dn = function(hDN) {
+ var s = "";
+ var a = ASN1HEX.getPosArrayOfChildren_AtObj(hDN, 0);
+ for (var i = 0; i < a.length; i++) {
+ var hRDN = ASN1HEX.getHexOfTLV_AtObj(hDN, a[i]);
+ s = s + "/" + X509.hex2rdn(hRDN);
+ }
+ return s;
+};
+
+X509.hex2rdn = function(hRDN) {
+ var hType = ASN1HEX.getDecendantHexTLVByNthList(hRDN, 0, [0, 0]);
+ var hValue = ASN1HEX.getDecendantHexVByNthList(hRDN, 0, [0, 1]);
+ var type = "";
+ try { type = X509.DN_ATTRHEX[hType]; } catch (ex) { type = hType; }
+ hValue = hValue.replace(/(..)/g, "%$1");
+ var value = decodeURIComponent(hValue);
+ return type + "=" + value;
+};
+
+X509.DN_ATTRHEX = {
+ "0603550406": "C",
+ "060355040a": "O",
+ "060355040b": "OU",
+ "0603550403": "CN",
+ "0603550405": "SN",
+ "0603550408": "ST",
+ "0603550407": "L"
+};
+
+/**
+ * get RSAKey/ECDSA public key object from PEM certificate string
+ * @name getPublicKeyFromCertPEM
+ * @memberOf X509
+ * @function
+ * @param {String} sCertPEM PEM formatted RSA/ECDSA/DSA X.509 certificate
+ * @return returns RSAKey/KJUR.crypto.{ECDSA,DSA} object of public key
+ * @since x509 1.1.1
+ * @description
+ * NOTE: DSA is also supported since x509 1.1.2.
+ */
+X509.getPublicKeyFromCertPEM = function(sCertPEM) {
+ var info = X509.getPublicKeyInfoPropOfCertPEM(sCertPEM);
+
+ if (info.algoid == "2a864886f70d010101") { // RSA
+ var aRSA = KEYUTIL.parsePublicRawRSAKeyHex(info.keyhex);
+ var key = new RSAKey();
+ key.setPublic(aRSA.n, aRSA.e);
+ return key;
+ } else if (info.algoid == "2a8648ce3d0201") { // ECC
+ var curveName = KJUR.crypto.OID.oidhex2name[info.algparam];
+ var key = new KJUR.crypto.ECDSA({'curve': curveName, 'info': info.keyhex});
+ key.setPublicKeyHex(info.keyhex);
+ return key;
+ } else if (info.algoid == "2a8648ce380401") { // DSA 1.2.840.10040.4.1
+ var p = ASN1HEX.getVbyList(info.algparam, 0, [0], "02");
+ var q = ASN1HEX.getVbyList(info.algparam, 0, [1], "02");
+ var g = ASN1HEX.getVbyList(info.algparam, 0, [2], "02");
+ var y = ASN1HEX.getHexOfV_AtObj(info.keyhex, 0);
+ y = y.substr(2);
+ var key = new KJUR.crypto.DSA();
+ key.setPublic(new BigInteger(p, 16),
+ new BigInteger(q, 16),
+ new BigInteger(g, 16),
+ new BigInteger(y, 16));
+ return key;
+ } else {
+ throw "unsupported key";
+ }
+};
+
+/**
+ * get public key information from PEM certificate
+ * @name getPublicKeyInfoPropOfCertPEM
+ * @memberOf X509
+ * @function
+ * @param {String} sCertPEM string of PEM formatted certificate
+ * @return {Hash} hash of information for public key
+ * @since x509 1.1.1
+ * @description
+ * Resulted associative array has following properties:
+ * <ul>
+ * <li>algoid - hexadecimal string of OID of asymmetric key algorithm</li>
+ * <li>algparam - hexadecimal string of OID of ECC curve name or null</li>
+ * <li>keyhex - hexadecimal string of key in the certificate</li>
+ * </ul>
+ * @since x509 1.1.1
+ */
+X509.getPublicKeyInfoPropOfCertPEM = function(sCertPEM) {
+ var result = {};
+ result.algparam = null;
+ var hCert = X509.pemToHex(sCertPEM);
+
+ // 1. Certificate ASN.1
+ var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, 0);
+ if (a1.length != 3)
+ throw "malformed X.509 certificate PEM (code:001)"; // not 3 item of seq Cert
+
+ // 2. tbsCertificate
+ if (hCert.substr(a1[0], 2) != "30")
+ throw "malformed X.509 certificate PEM (code:002)"; // tbsCert not seq
+
+ var a2 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a1[0]);
+
+ // 3. subjectPublicKeyInfo
+ if (a2.length < 7)
+ throw "malformed X.509 certificate PEM (code:003)"; // no subjPubKeyInfo
+
+ var a3 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a2[6]);
+
+ if (a3.length != 2)
+ throw "malformed X.509 certificate PEM (code:004)"; // not AlgId and PubKey
+
+ // 4. AlgId
+ var a4 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a3[0]);
+
+ if (a4.length != 2)
+ throw "malformed X.509 certificate PEM (code:005)"; // not 2 item in AlgId
+
+ result.algoid = ASN1HEX.getHexOfV_AtObj(hCert, a4[0]);
+
+ if (hCert.substr(a4[1], 2) == "06") { // EC
+ result.algparam = ASN1HEX.getHexOfV_AtObj(hCert, a4[1]);
+ } else if (hCert.substr(a4[1], 2) == "30") { // DSA
+ result.algparam = ASN1HEX.getHexOfTLV_AtObj(hCert, a4[1]);
+ }
+
+ // 5. Public Key Hex
+ if (hCert.substr(a3[1], 2) != "03")
+ throw "malformed X.509 certificate PEM (code:006)"; // not bitstring
+
+ var unusedBitAndKeyHex = ASN1HEX.getHexOfV_AtObj(hCert, a3[1]);
+ result.keyhex = unusedBitAndKeyHex.substr(2);
+
+ return result;
+};
+
+/**
+ * get position of subjectPublicKeyInfo field from HEX certificate
+ * @name getPublicKeyInfoPosOfCertHEX
+ * @memberOf X509
+ * @function
+ * @param {String} hCert hexadecimal string of certificate
+ * @return {Integer} position in hexadecimal string
+ * @since x509 1.1.4
+ * @description
+ * get position for SubjectPublicKeyInfo field in the hexadecimal string of
+ * certificate.
+ */
+X509.getPublicKeyInfoPosOfCertHEX = function(hCert) {
+ // 1. Certificate ASN.1
+ var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, 0);
+ if (a1.length != 3)
+ throw "malformed X.509 certificate PEM (code:001)"; // not 3 item of seq Cert
+
+ // 2. tbsCertificate
+ if (hCert.substr(a1[0], 2) != "30")
+ throw "malformed X.509 certificate PEM (code:002)"; // tbsCert not seq
+
+ var a2 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a1[0]);
+
+ // 3. subjectPublicKeyInfo
+ if (a2.length < 7)
+ throw "malformed X.509 certificate PEM (code:003)"; // no subjPubKeyInfo
+
+ return a2[6];
+};
+
+/**
+ * get array of X.509 V3 extension value information in hex string of certificate
+ * @name getV3ExtInfoListOfCertHex
+ * @memberOf X509
+ * @function
+ * @param {String} hCert hexadecimal string of X.509 certificate binary
+ * @return {Array} array of result object by {@link X509.getV3ExtInfoListOfCertHex}
+ * @since x509 1.1.5
+ * @description
+ * This method will get all extension information of a X.509 certificate.
+ * Items of resulting array has following properties:
+ * <ul>
+ * <li>posTLV - index of ASN.1 TLV for the extension. same as 'pos' argument.</li>
+ * <li>oid - dot noted string of extension oid (ex. 2.5.29.14)</li>
+ * <li>critical - critical flag value for this extension</li>
+ * <li>posV - index of ASN.1 TLV for the extension value.
+ * This is a position of a content of ENCAPSULATED OCTET STRING.</li>
+ * </ul>
+ * @example
+ * hCert = X509.pemToHex(certGithubPEM);
+ * a = X509.getV3ExtInfoListOfCertHex(hCert);
+ * // Then a will be an array of like following:
+ * [{posTLV: 1952, oid: "2.5.29.35", critical: false, posV: 1968},
+ * {posTLV: 1974, oid: "2.5.29.19", critical: true, posV: 1986}, ...]
+ */
+X509.getV3ExtInfoListOfCertHex = function(hCert) {
+ // 1. Certificate ASN.1
+ var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, 0);
+ if (a1.length != 3)
+ throw "malformed X.509 certificate PEM (code:001)"; // not 3 item of seq Cert
+
+ // 2. tbsCertificate
+ if (hCert.substr(a1[0], 2) != "30")
+ throw "malformed X.509 certificate PEM (code:002)"; // tbsCert not seq
+
+ var a2 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a1[0]);
+
+ // 3. v3Extension EXPLICIT Tag [3]
+ // ver, seri, alg, iss, validity, subj, spki, (iui,) (sui,) ext
+ if (a2.length < 8)
+ throw "malformed X.509 certificate PEM (code:003)"; // tbsCert num field too short
+
+ if (hCert.substr(a2[7], 2) != "a3")
+ throw "malformed X.509 certificate PEM (code:004)"; // not [3] tag
+
+ var a3 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a2[7]);
+ if (a3.length != 1)
+ throw "malformed X.509 certificate PEM (code:005)"; // [3]tag numChild!=1
+
+ // 4. v3Extension SEQUENCE
+ if (hCert.substr(a3[0], 2) != "30")
+ throw "malformed X.509 certificate PEM (code:006)"; // not SEQ
+
+ var a4 = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, a3[0]);
+
+ // 5. v3Extension item position
+ var numExt = a4.length;
+ var aInfo = new Array(numExt);
+ for (var i = 0; i < numExt; i++) {
+ aInfo[i] = X509.getV3ExtItemInfo_AtObj(hCert, a4[i]);
+ }
+ return aInfo;
+};
+
+/**
+ * get X.509 V3 extension value information at the specified position
+ * @name getV3ExtItemInfo_AtObj
+ * @memberOf X509
+ * @function
+ * @param {String} hCert hexadecimal string of X.509 certificate binary
+ * @param {Integer} pos index of hexadecimal string for the extension
+ * @return {Object} properties for the extension
+ * @since x509 1.1.5
+ * @description
+ * This method will get some information of a X.509 V extension
+ * which is referred by an index of hexadecimal string of X.509
+ * certificate.
+ * Resulting object has following properties:
+ * <ul>
+ * <li>posTLV - index of ASN.1 TLV for the extension. same as 'pos' argument.</li>
+ * <li>oid - dot noted string of extension oid (ex. 2.5.29.14)</li>
+ * <li>critical - critical flag value for this extension</li>
+ * <li>posV - index of ASN.1 TLV for the extension value.
+ * This is a position of a content of ENCAPSULATED OCTET STRING.</li>
+ * </ul>
+ * This method is used by {@link X509.getV3ExtInfoListOfCertHex} internally.
+ */
+X509.getV3ExtItemInfo_AtObj = function(hCert, pos) {
+ var info = {};
+
+ // posTLV - extension TLV
+ info.posTLV = pos;
+
+ var a = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pos);
+ if (a.length != 2 && a.length != 3)
+ throw "malformed X.509v3 Ext (code:001)"; // oid,(critical,)val
+
+ // oid - extension OID
+ if (hCert.substr(a[0], 2) != "06")
+ throw "malformed X.509v3 Ext (code:002)"; // not OID "06"
+ var valueHex = ASN1HEX.getHexOfV_AtObj(hCert, a[0]);
+ info.oid = ASN1HEX.hextooidstr(valueHex);
+
+ // critical - extension critical flag
+ info.critical = false; // critical false by default
+ if (a.length == 3) info.critical = true;
+
+ // posV - content TLV position of encapsulated
+ // octet string of V3 extension value.
+ var posExtV = a[a.length - 1];
+ if (hCert.substr(posExtV, 2) != "04")
+ throw "malformed X.509v3 Ext (code:003)"; // not EncapOctet "04"
+ info.posV = ASN1HEX.getStartPosOfV_AtObj(hCert, posExtV);
+
+ return info;
+};
+
+/**
+ * get X.509 V3 extension value ASN.1 TLV for specified oid or name
+ * @name getHexOfTLV_V3ExtValue
+ * @memberOf X509
+ * @function
+ * @param {String} hCert hexadecimal string of X.509 certificate binary
+ * @param {String} oidOrName oid or name for extension (ex. 'keyUsage' or '2.5.29.15')
+ * @return {String} hexadecimal string of extension ASN.1 TLV
+ * @since x509 1.1.6
+ * @description
+ * This method will get X.509v3 extension value of ASN.1 TLV
+ * which is specifyed by extension name or oid.
+ * @example
+ * hExtValue = X509.getHexOfTLV_V3ExtValue(hCert, "keyUsage");
+ * // hExtValue will be such like '030205a0'.
+ */
+X509.getHexOfTLV_V3ExtValue = function(hCert, oidOrName) {
+ var pos = X509.getPosOfTLV_V3ExtValue(hCert, oidOrName);
+ if (pos == -1) return '';
+ return ASN1HEX.getHexOfTLV_AtObj(hCert, pos);
+};
+
+/**
+ * get X.509 V3 extension value ASN.1 V for specified oid or name
+ * @name getHexOfV_V3ExtValue
+ * @memberOf X509
+ * @function
+ * @param {String} hCert hexadecimal string of X.509 certificate binary
+ * @param {String} oidOrName oid or name for extension (ex. 'keyUsage' or '2.5.29.15')
+ * @return {String} hexadecimal string of extension ASN.1 TLV
+ * @since x509 1.1.6
+ * @description
+ * This method will get X.509v3 extension value of ASN.1 value
+ * which is specifyed by extension name or oid.
+ * If there is no such extension in the certificate,
+ * it returns empty string (i.e. '').
+ * Available extension names and oids are defined
+ * in the {@link KJUR.asn1.x509.OID} class.
+ * @example
+ * hExtValue = X509.getHexOfV_V3ExtValue(hCert, "keyUsage");
+ * // hExtValue will be such like '05a0'.
+ */
+X509.getHexOfV_V3ExtValue = function(hCert, oidOrName) {
+ var pos = X509.getPosOfTLV_V3ExtValue(hCert, oidOrName);
+ if (pos == -1) return '';
+ return ASN1HEX.getHexOfV_AtObj(hCert, pos);
+};
+
+/**
+ * get index in the certificate hexa string for specified oid or name specified extension
+ * @name getPosOfTLV_V3ExtValue
+ * @memberOf X509
+ * @function
+ * @param {String} hCert hexadecimal string of X.509 certificate binary
+ * @param {String} oidOrName oid or name for extension (ex. 'keyUsage' or '2.5.29.15')
+ * @return {Integer} index in the hexadecimal string of certficate for specified extension
+ * @since x509 1.1.6
+ * @description
+ * This method will get X.509v3 extension value of ASN.1 V(value)
+ * which is specifyed by extension name or oid.
+ * If there is no such extension in the certificate,
+ * it returns empty string (i.e. '').
+ * Available extension names and oids are defined
+ * in the {@link KJUR.asn1.x509.OID} class.
+ * @example
+ * idx = X509.getPosOfV_V3ExtValue(hCert, "keyUsage");
+ * // The 'idx' will be index in the string for keyUsage value ASN.1 TLV.
+ */
+X509.getPosOfTLV_V3ExtValue = function(hCert, oidOrName) {
+ var oid = oidOrName;
+ if (! oidOrName.match(/^[0-9.]+$/)) oid = KJUR.asn1.x509.OID.name2oid(oidOrName);
+ if (oid == '') return -1;
+
+ var infoList = X509.getV3ExtInfoListOfCertHex(hCert);
+ for (var i = 0; i < infoList.length; i++) {
+ var info = infoList[i];
+ if (info.oid == oid) return info.posV;
+ }
+ return -1;
+};
+
+X509.KEYUSAGE_NAME = [
+ "digitalSignature",
+ "nonRepudiation",
+ "keyEncipherment",
+ "dataEncipherment",
+ "keyAgreement",
+ "keyCertSign",
+ "cRLSign",
+ "encipherOnly",
+ "decipherOnly"
+];
+
+/**
+ * get KeyUsage extension value as binary string in the certificate
+ * @name getExtKeyUsageBin
+ * @memberOf X509
+ * @function
+ * @param {String} hCert hexadecimal string of X.509 certificate binary
+ * @return {String} binary string of key usage bits (ex. '101')
+ * @since x509 1.1.6
+ * @description
+ * This method will get key usage extension value
+ * as binary string such like '101'.
+ * Key usage bits definition is in the RFC 5280.
+ * If there is no key usage extension in the certificate,
+ * it returns empty string (i.e. '').
+ * @example
+ * bKeyUsage = X509.getExtKeyUsageBin(hCert);
+ * // bKeyUsage will be such like '101'.
+ * // 1 - digitalSignature
+ * // 0 - nonRepudiation
+ * // 1 - keyEncipherment
+ */
+X509.getExtKeyUsageBin = function(hCert) {
+ var hKeyUsage = X509.getHexOfV_V3ExtValue(hCert, "keyUsage");
+ if (hKeyUsage == '') return '';
+ if (hKeyUsage.length % 2 != 0 || hKeyUsage.length <= 2)
+ throw "malformed key usage value";
+ var unusedBits = parseInt(hKeyUsage.substr(0, 2));
+ var bKeyUsage = parseInt(hKeyUsage.substr(2), 16).toString(2);
+ return bKeyUsage.substr(0, bKeyUsage.length - unusedBits);
+};
+
+/**
+ * get KeyUsage extension value as names in the certificate
+ * @name getExtKeyUsageString
+ * @memberOf X509
+ * @function
+ * @param {String} hCert hexadecimal string of X.509 certificate binary
+ * @return {String} comma separated string of key usage
+ * @since x509 1.1.6
+ * @description
+ * This method will get key usage extension value
+ * as comma separated string of usage names.
+ * If there is no key usage extension in the certificate,
+ * it returns empty string (i.e. '').
+ * @example
+ * sKeyUsage = X509.getExtKeyUsageString(hCert);
+ * // sKeyUsage will be such like 'digitalSignature,keyEncipherment'.
+ */
+X509.getExtKeyUsageString = function(hCert) {
+ var bKeyUsage = X509.getExtKeyUsageBin(hCert);
+ var a = new Array();
+ for (var i = 0; i < bKeyUsage.length; i++) {
+ if (bKeyUsage.substr(i, 1) == "1") a.push(X509.KEYUSAGE_NAME[i]);
+ }
+ return a.join(",");
+};
+
+/**
+ * get AuthorityInfoAccess extension value in the certificate as associative array
+ * @name getExtAIAInfo
+ * @memberOf X509
+ * @function
+ * @param {String} hCert hexadecimal string of X.509 certificate binary
+ * @return {Object} associative array of AIA extension properties
+ * @since x509 1.1.6
+ * @description
+ * This method will get authority info access value
+ * as associate array which has following properties:
+ * <ul>
+ * <li>ocsp - array of string for OCSP responder URL</li>
+ * <li>caissuer - array of string for caIssuer value (i.e. CA certificates URL)</li>
+ * </ul>
+ * If there is no key usage extension in the certificate,
+ * it returns null;
+ * @example
+ * oAIA = X509.getExtAIAInfo(hCert);
+ * // result will be such like:
+ * // oAIA.ocsp = ["http://ocsp.foo.com"];
+ * // oAIA.caissuer = ["http://rep.foo.com/aaa.p8m"];
+ */
+X509.getExtAIAInfo = function(hCert) {
+ var result = {};
+ result.ocsp = [];
+ result.caissuer = [];
+ var pos1 = X509.getPosOfTLV_V3ExtValue(hCert, "authorityInfoAccess");
+ if (pos1 == -1) return null;
+ if (hCert.substr(pos1, 2) != "30") // extnValue SEQUENCE
+ throw "malformed AIA Extn Value";
+
+ var posAccDescList = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, pos1);
+ for (var i = 0; i < posAccDescList.length; i++) {
+ var p = posAccDescList[i];
+ var posAccDescChild = ASN1HEX.getPosArrayOfChildren_AtObj(hCert, p);
+ if (posAccDescChild.length != 2)
+ throw "malformed AccessDescription of AIA Extn";
+ var pOID = posAccDescChild[0];
+ var pName = posAccDescChild[1];
+ if (ASN1HEX.getHexOfV_AtObj(hCert, pOID) == "2b06010505073001") {
+ if (hCert.substr(pName, 2) == "86") {
+ result.ocsp.push(hextoutf8(ASN1HEX.getHexOfV_AtObj(hCert, pName)));
+ }
+ }
+ if (ASN1HEX.getHexOfV_AtObj(hCert, pOID) == "2b06010505073002") {
+ if (hCert.substr(pName, 2) == "86") {
+ result.caissuer.push(hextoutf8(ASN1HEX.getHexOfV_AtObj(hCert, pName)));
+ }
+ }
+ }
+ return result;
+};
+
+/*
+ X509.prototype.readCertPEM = _x509_readCertPEM;
+ X509.prototype.readCertPEMWithoutRSAInit = _x509_readCertPEMWithoutRSAInit;
+ X509.prototype.getSerialNumberHex = _x509_getSerialNumberHex;
+ X509.prototype.getIssuerHex = _x509_getIssuerHex;
+ X509.prototype.getSubjectHex = _x509_getSubjectHex;
+ X509.prototype.getIssuerString = _x509_getIssuerString;
+ X509.prototype.getSubjectString = _x509_getSubjectString;
+ X509.prototype.getNotBefore = _x509_getNotBefore;
+ X509.prototype.getNotAfter = _x509_getNotAfter;
+*/
+
+exports.X509 = X509;
+module.exports = exports;
+/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
+ */
// Copyright (c) 2005 Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.
@@ -6396,7 +6054,7 @@
var j_lm = ((canary&0xffffff)==0xefcafe);
// (public) Constructor
-function BigInteger(a,b,c) {
+var BigInteger = function BigInteger(a,b,c) {
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,256);
@@ -6502,7 +6160,7 @@
this.t = 1;
this.s = (x<0)?-1:0;
if(x > 0) this[0] = x;
- else if(x < -1) this[0] = x+DV;
+ else if(x < -1) this[0] = x+this.DV;
else this.t = 0;
}
@@ -6596,7 +6254,7 @@
if(r != 0) return r;
var i = this.t;
r = i-a.t;
- if(r != 0) return r;
+ if(r != 0) return (this.s<0)?-r:r;
while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
return 0;
}
@@ -6941,6 +6599,8 @@
// "constants"
BigInteger.ZERO = nbv(0);
BigInteger.ONE = nbv(1);
+/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
+ */
// Copyright (c) 2005-2009 Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.
@@ -6948,6 +6608,7 @@
// Extended JavaScript BN functions, required for RSA private ops.
// Version 1.1: new BigInteger("0", 10) returns "proper" zero
+// Version 1.2: square() API, isProbablePrime fix
// (public)
function bnClone() { var r = nbi(); this.copyTo(r); return r; }
@@ -7233,6 +6894,9 @@
// (public) this * a
function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; }
+// (public) this^2
+function bnSquare() { var r = nbi(); this.squareTo(r); return r; }
+
// (public) this / a
function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; }
@@ -7485,7 +7149,7 @@
if(d.signum() < 0) return d.add(m); else return d;
}
-var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509];
+var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];
var lplim = (1<<26)/lowprimes[lowprimes.length-1];
// (public) test primality with certainty >= 1-.5^t
@@ -7517,7 +7181,8 @@
if(t > lowprimes.length) t = lowprimes.length;
var a = nbi();
for(var i = 0; i < t; ++i) {
- a.fromInt(lowprimes[i]);
+ //Pick bases at random, instead of starting at 2
+ a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);
var y = a.modPow(r,this);
if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
var j = 1;
@@ -7581,6 +7246,9 @@
BigInteger.prototype.gcd = bnGCD;
BigInteger.prototype.isProbablePrime = bnIsProbablePrime;
+// JSBN-specific extension
+BigInteger.prototype.square = bnSquare;
+
// BigInteger interfaces not implemented in jsbn:
// BigInteger(int signum, byte[] magnitude)
@@ -7589,623 +7257,30318 @@
// int hashCode()
// long longValue()
// static BigInteger valueOf(long val)
+
+exports.BigInteger = BigInteger;
+module.exports = exports;
/**
- * @author: Meki Cherkaoui, Jeff Thompson, Wentao Shang
- * See COPYING for copyright and distribution information.
- * This class represents the top-level object for communicating with an NDN host.
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Wentao Shang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
*/
-var LOG = 0;
+/** @ignore */
+var ASN1HEX = require('../contrib/securityLib/asn1hex-1.1.js').ASN1HEX /** @ignore */
+var KJUR = require('../contrib/securityLib/crypto-1.0.js').KJUR /** @ignore */
+var RSAKey = require('../contrib/securityLib/rsasign-1.2.js').RSAKey /** @ignore */
+var b64tohex = require('../contrib/securityLib/base64.js').b64tohex
-/**
- * settings is an associative array with the following defaults:
- * {
- * getTransport: function() { return new WebSocketTransport(); },
- * getHostAndPort: transport.defaultGetHostAndPort,
- * host: null, // If null, use getHostAndPort when connecting.
- * port: 9696,
- * onopen: function() { if (LOG > 3) console.log("NDN connection established."); },
- * onclose: function() { if (LOG > 3) console.log("NDN connection closed."); },
- * verify: true // If false, don't verify and call upcall with Closure.UPCALL_CONTENT_UNVERIFIED.
- * }
- *
- * getHostAndPort is a function, on each call it returns a new { host: host, port: port } or
- * null if there are no more hosts.
- *
- * This throws an exception if NDN.supported is false.
- */
-var NDN = function NDN(settings) {
- if (!NDN.supported)
- throw new Error("The necessary JavaScript support is not available on this platform.");
+// Library namespace
+/** @ignore */
+var ndn = ndn || {};
- settings = (settings || {});
- var getTransport = (settings.getTransport || function() { return new WebSocketTransport(); });
- this.transport = getTransport();
- this.getHostAndPort = (settings.getHostAndPort || this.transport.defaultGetHostAndPort);
- this.host = (settings.host !== undefined ? settings.host : null);
- this.port = (settings.port || 9696);
- this.readyStatus = NDN.UNOPEN;
- this.verify = (settings.verify !== undefined ? settings.verify : true);
- // Event handler
- this.onopen = (settings.onopen || function() { if (LOG > 3) console.log("NDN connection established."); });
- this.onclose = (settings.onclose || function() { if (LOG > 3) console.log("NDN connection closed."); });
- this.ndndid = null;
+/** @ignore */
+var exports = ndn;
+
+// Factory method to create hasher objects
+exports.createHash = function(alg)
+{
+ if (alg != 'sha256')
+ throw new Error('createHash: unsupported algorithm.');
+
+ var obj = {};
+
+ obj.md = new KJUR.crypto.MessageDigest({alg: "sha256", prov: "cryptojs"});
+
+ obj.update = function(buf) {
+ this.md.updateHex(buf.toString('hex'));
+ };
+
+ obj.digest = function(encoding) {
+ var hexDigest = this.md.digest();
+ if (encoding == 'hex')
+ return hexDigest;
+ else if (encoding == 'base64')
+ return new Buffer(hexDigest, 'hex').toString('base64');
+ else
+ return new Buffer(hexDigest, 'hex');
+ };
+
+ return obj;
};
-NDN.UNOPEN = 0; // created but not opened yet
-NDN.OPENED = 1; // connection to ndnd opened
-NDN.CLOSED = 2; // connection to ndnd closed
+// Factory method to create HMAC objects.
+exports.createHmac = function(algorithm, key)
+{
+ if (algorithm !== 'sha256')
+ throw new Error('createHmac: unsupported algorithm.');
+
+ var obj = {};
+
+ obj.md = new KJUR.crypto.Mac({alg: "HmacSHA256", pass: {hex: key.toString('hex')}});
+
+ obj.update = function(buf) {
+ this.md.updateHex(buf.toString('hex'));
+ };
+
+ obj.digest = function(encoding) {
+ var hexDigest = this.md.doFinal();
+ if (encoding == 'hex')
+ return hexDigest;
+ else if (encoding == 'base64')
+ return new Buffer(hexDigest, 'hex').toString('base64');
+ else
+ return new Buffer(hexDigest, 'hex');
+ };
+
+ return obj;
+};
+
+// Factory method to create RSA signer objects
+exports.createSign = function(alg)
+{
+ if (alg != 'RSA-SHA256')
+ throw new Error('createSign: unsupported algorithm.');
+
+ var obj = {};
+
+ obj.arr = [];
+
+ obj.update = function(buf) {
+ this.arr.push(buf);
+ };
+
+ obj.sign = function(keypem) {
+ var rsa = new RSAKey();
+ rsa.readPrivateKeyFromPEMString(keypem);
+ var signer = new KJUR.crypto.Signature({alg: "SHA256withRSA", prov: "cryptojs/jsrsa"});
+ signer.initSign(rsa);
+ for (var i = 0; i < this.arr.length; ++i)
+ signer.updateHex(this.arr[i].toString('hex'));
+
+ return new Buffer(signer.sign(), 'hex');
+ };
+
+ return obj;
+};
+
+// Factory method to create RSA verifier objects
+exports.createVerify = function(alg)
+{
+ if (alg != 'RSA-SHA256')
+ throw new Error('createSign: unsupported algorithm.');
+
+ var obj = {};
+
+ obj.arr = [];
+
+ obj.update = function(buf) {
+ this.arr.push(buf);
+ };
+
+ var getSubjectPublicKeyPosFromHex = function(hPub) {
+ var a = ASN1HEX.getPosArrayOfChildren_AtObj(hPub, 0);
+ if (a.length != 2)
+ return -1;
+ var pBitString = a[1];
+ if (hPub.substring(pBitString, pBitString + 2) != '03')
+ return -1;
+ var pBitStringV = ASN1HEX.getStartPosOfV_AtObj(hPub, pBitString);
+ if (hPub.substring(pBitStringV, pBitStringV + 2) != '00')
+ return -1;
+ return pBitStringV + 2;
+ };
+
+ var publicKeyPemToDer = function(publicKeyPem) {
+ // Remove the '-----XXX-----' from the beginning and the end of the public
+ // key and also remove any \n in the public key string.
+ var lines = publicKeyPem.split('\n');
+ var pub = "";
+ for (var i = 1; i < lines.length - 1; i++)
+ pub += lines[i];
+ return new Buffer(pub, 'base64');
+ }
+
+ var readPublicDER = function(pub_der) {
+ var hex = pub_der.toString('hex');
+ var p = getSubjectPublicKeyPosFromHex(hex);
+ var a = ASN1HEX.getPosArrayOfChildren_AtObj(hex, p);
+ if (a.length != 2)
+ return null;
+ var hN = ASN1HEX.getHexOfV_AtObj(hex, a[0]);
+ var hE = ASN1HEX.getHexOfV_AtObj(hex, a[1]);
+ var rsaKey = new RSAKey();
+ rsaKey.setPublic(hN, hE);
+ return rsaKey;
+ };
+
+ obj.verify = function(keypem, sig) {
+ var rsa = readPublicDER(publicKeyPemToDer(keypem));
+ var signer = new KJUR.crypto.Signature({alg: "SHA256withRSA", prov: "cryptojs/jsrsa"});
+ signer.initVerifyByPublicKey(rsa);
+ for (var i = 0; i < this.arr.length; i++)
+ signer.updateHex(this.arr[i].toString('hex'));
+ var hSig = sig.toString('hex');
+ return signer.verify(hSig);
+ };
+
+ return obj;
+};
+
+exports.randomBytes = function(size)
+{
+ // TODO: Use a cryptographic random number generator.
+ var result = new Buffer(size);
+ for (var i = 0; i < size; ++i)
+ result[i] = Math.floor(Math.random() * 256);
+ return result;
+};
+
+// contrib/feross/buffer.js needs base64.toByteArray. Define it here so that
+// we don't have to include the entire base64 module.
+exports.toByteArray = function(str) {
+ var hex = b64tohex(str);
+ var result = [];
+ hex.replace(/(..)/g, function(ss) {
+ result.push(parseInt(ss, 16));
+ });
+ return result;
+};
+
+module.exports = exports
+// After this we include contrib/feross/buffer.js to define the Buffer class.
+var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+var base64js = {};
+
+;(function (exports) {
+ 'use strict';
+
+ var Arr = (typeof Uint8Array !== 'undefined')
+ ? Uint8Array
+ : Array
+
+ var PLUS = '+'.charCodeAt(0)
+ var SLASH = '/'.charCodeAt(0)
+ var NUMBER = '0'.charCodeAt(0)
+ var LOWER = 'a'.charCodeAt(0)
+ var UPPER = 'A'.charCodeAt(0)
+
+ function decode (elt) {
+ var code = elt.charCodeAt(0)
+ if (code === PLUS)
+ return 62 // '+'
+ if (code === SLASH)
+ return 63 // '/'
+ if (code < NUMBER)
+ return -1 //no match
+ if (code < NUMBER + 10)
+ return code - NUMBER + 26 + 26
+ if (code < UPPER + 26)
+ return code - UPPER
+ if (code < LOWER + 26)
+ return code - LOWER + 26
+ }
+
+ function b64ToByteArray (b64) {
+ var i, j, l, tmp, placeHolders, arr
+
+ if (b64.length % 4 > 0) {
+ throw new Error('Invalid string. Length must be a multiple of 4')
+ }
+
+ // the number of equal signs (place holders)
+ // if there are two placeholders, than the two characters before it
+ // represent one byte
+ // if there is only one, then the three characters before it represent 2 bytes
+ // this is just a cheap hack to not do indexOf twice
+ var len = b64.length
+ placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0
+
+ // base64 is 4/3 + up to two characters of the original data
+ arr = new Arr(b64.length * 3 / 4 - placeHolders)
+
+ // if there are placeholders, only get up to the last complete 4 chars
+ l = placeHolders > 0 ? b64.length - 4 : b64.length
+
+ var L = 0
+
+ function push (v) {
+ arr[L++] = v
+ }
+
+ for (i = 0, j = 0; i < l; i += 4, j += 3) {
+ tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
+ push((tmp & 0xFF0000) >> 16)
+ push((tmp & 0xFF00) >> 8)
+ push(tmp & 0xFF)
+ }
+
+ if (placeHolders === 2) {
+ tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
+ push(tmp & 0xFF)
+ } else if (placeHolders === 1) {
+ tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
+ push((tmp >> 8) & 0xFF)
+ push(tmp & 0xFF)
+ }
+
+ return arr
+ }
+
+ function uint8ToBase64 (uint8) {
+ var i,
+ extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
+ output = "",
+ temp, length
+
+ function encode (num) {
+ return lookup.charAt(num)
+ }
+
+ function tripletToBase64 (num) {
+ return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
+ }
+
+ // go through the array every three bytes, we'll deal with trailing stuff later
+ for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
+ temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
+ output += tripletToBase64(temp)
+ }
+
+ // pad the end with zeros, but make sure to not forget the extra bytes
+ switch (extraBytes) {
+ case 1:
+ temp = uint8[uint8.length - 1]
+ output += encode(temp >> 2)
+ output += encode((temp << 4) & 0x3F)
+ output += '=='
+ break
+ case 2:
+ temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
+ output += encode(temp >> 10)
+ output += encode((temp >> 4) & 0x3F)
+ output += encode((temp << 2) & 0x3F)
+ output += '='
+ break
+ }
+
+ return output
+ }
+
+ exports.toByteArray = b64ToByteArray
+ exports.fromByteArray = uint8ToBase64
+})(base64js);
+var ieee = {};
+
+ieee.read = function(buffer, offset, isLE, mLen, nBytes) {
+ var e, m,
+ eLen = nBytes * 8 - mLen - 1,
+ eMax = (1 << eLen) - 1,
+ eBias = eMax >> 1,
+ nBits = -7,
+ i = isLE ? (nBytes - 1) : 0,
+ d = isLE ? -1 : 1,
+ s = buffer[offset + i];
+
+ i += d;
+
+ e = s & ((1 << (-nBits)) - 1);
+ s >>= (-nBits);
+ nBits += eLen;
+ for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8);
+
+ m = e & ((1 << (-nBits)) - 1);
+ e >>= (-nBits);
+ nBits += mLen;
+ for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8);
+
+ if (e === 0) {
+ e = 1 - eBias;
+ } else if (e === eMax) {
+ return m ? NaN : ((s ? -1 : 1) * Infinity);
+ } else {
+ m = m + Math.pow(2, mLen);
+ e = e - eBias;
+ }
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
+};
+
+ieee.write = function(buffer, value, offset, isLE, mLen, nBytes) {
+ var e, m, c,
+ eLen = nBytes * 8 - mLen - 1,
+ eMax = (1 << eLen) - 1,
+ eBias = eMax >> 1,
+ rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0),
+ i = isLE ? 0 : (nBytes - 1),
+ d = isLE ? 1 : -1,
+ s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
+
+ value = Math.abs(value);
+
+ if (isNaN(value) || value === Infinity) {
+ m = isNaN(value) ? 1 : 0;
+ e = eMax;
+ } else {
+ e = Math.floor(Math.log(value) / Math.LN2);
+ if (value * (c = Math.pow(2, -e)) < 1) {
+ e--;
+ c *= 2;
+ }
+ if (e + eBias >= 1) {
+ value += rt / c;
+ } else {
+ value += rt * Math.pow(2, 1 - eBias);
+ }
+ if (value * c >= 2) {
+ e++;
+ c /= 2;
+ }
+
+ if (e + eBias >= eMax) {
+ m = 0;
+ e = eMax;
+ } else if (e + eBias >= 1) {
+ m = (value * c - 1) * Math.pow(2, mLen);
+ e = e + eBias;
+ } else {
+ m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
+ e = 0;
+ }
+ }
+
+ for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8);
+
+ e = (e << mLen) | m;
+ eLen += mLen;
+ for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8);
+
+ buffer[offset + i - d] |= s * 128;
+};
+
+exports.ieee = ieee;
+/**
+ * The buffer module from node.js, for the browser.
+ * @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
+ *
+ * Copyright (C) 2013 Feross Aboukhadijeh, and other contributors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * @license MIT
+ */
+
+var base64 = base64js;
+var ieee754 = require('./ieee754.js').ieee
+
+exports.Buffer = Buffer
+exports.SlowBuffer = Buffer
+exports.INSPECT_MAX_BYTES = 50
+Buffer.poolSize = 8192
+
+/**
+ * If `Buffer._useTypedArrays`:
+ * === true Use Uint8Array implementation (fastest)
+ * === false Use Object implementation (compatible down to IE6)
+ */
+Buffer._useTypedArrays = (function () {
+ // Detect if browser supports Typed Arrays. Supported browsers are IE 10+, Firefox 4+,
+ // Chrome 7+, Safari 5.1+, Opera 11.6+, iOS 4.2+. If the browser does not support adding
+ // properties to `Uint8Array` instances, then that's the same as no `Uint8Array` support
+ // because we need to be able to add all the node Buffer API methods. This is an issue
+ // in Firefox 4-29. Now fixed: https://bugzilla.mozilla.org/show_bug.cgi?id=695438
+ try {
+ var buf = new ArrayBuffer(0)
+ var arr = new Uint8Array(buf)
+ arr.foo = function () { return 42 }
+ return 42 === arr.foo() &&
+ typeof arr.subarray === 'function' // Chrome 9-10 lack `subarray`
+ } catch (e) {
+ return false
+ }
+})()
+
+/**
+ * Class: Buffer
+ * =============
+ *
+ * The Buffer constructor returns instances of `Uint8Array` that are augmented
+ * with function properties for all the node `Buffer` API functions. We use
+ * `Uint8Array` so that square bracket notation works as expected -- it returns
+ * a single octet.
+ *
+ * By augmenting the instances, we can avoid modifying the `Uint8Array`
+ * prototype.
+ */
+function Buffer (subject, encoding, noZero) {
+ if (!(this instanceof Buffer))
+ return new Buffer(subject, encoding, noZero)
+
+ var type = typeof subject
+
+ // Workaround: node's base64 implementation allows for non-padded strings
+ // while base64-js does not.
+ if (encoding === 'base64' && type === 'string') {
+ subject = Buffer.stringtrim(subject)
+ while (subject.length % 4 !== 0) {
+ subject = subject + '='
+ }
+ }
+
+ // Find the length
+ var length
+ if (type === 'number')
+ length = Buffer.coerce(subject)
+ else if (type === 'string')
+ length = Buffer.byteLength(subject, encoding)
+ else if (type === 'object')
+ length = Buffer.coerce(subject.length) // assume that object is array-like
+ else
+ throw new Error('First argument needs to be a number, array or string.')
+
+ var buf
+ if (Buffer._useTypedArrays) {
+ // Preferred: Return an augmented `Uint8Array` instance for best performance
+ buf = Buffer._augment(new Uint8Array(length))
+ } else {
+ // Fallback: Return THIS instance of Buffer (created by `new`)
+ buf = this
+ buf.length = length
+ buf._isBuffer = true
+ }
+
+ var i
+ if (Buffer._useTypedArrays && typeof subject.byteLength === 'number') {
+ // Speed optimization -- use set if we're copying from a typed array
+ buf._set(subject)
+ } else if (Buffer.isArrayish(subject)) {
+ // Treat array-ish objects as a byte array
+ if (Buffer.isBuffer(subject)) {
+ for (i = 0; i < length; i++)
+ buf[i] = subject.readUInt8(i)
+ } else {
+ for (i = 0; i < length; i++)
+ buf[i] = ((subject[i] % 256) + 256) % 256
+ }
+ } else if (type === 'string') {
+ buf.write(subject, 0, encoding)
+ } else if (type === 'number' && !Buffer._useTypedArrays && !noZero) {
+ for (i = 0; i < length; i++) {
+ buf[i] = 0
+ }
+ }
+
+ return buf
+}
+
+// STATIC METHODS
+// ==============
+
+Buffer.isEncoding = function (encoding) {
+ switch (String(encoding).toLowerCase()) {
+ case 'hex':
+ case 'utf8':
+ case 'utf-8':
+ case 'ascii':
+ case 'binary':
+ case 'base64':
+ case 'raw':
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return true
+ default:
+ return false
+ }
+}
+
+Buffer.isBuffer = function (b) {
+ return !!(b !== null && b !== undefined && b._isBuffer)
+}
+
+Buffer.byteLength = function (str, encoding) {
+ var ret
+ str = str.toString()
+ switch (encoding || 'utf8') {
+ case 'hex':
+ ret = str.length / 2
+ break
+ case 'utf8':
+ case 'utf-8':
+ ret = Buffer.utf8ToBytes(str).length
+ break
+ case 'ascii':
+ case 'binary':
+ case 'raw':
+ ret = str.length
+ break
+ case 'base64':
+ ret = Buffer.base64ToBytes(str).length
+ break
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ ret = str.length * 2
+ break
+ default:
+ throw new Error('Unknown encoding')
+ }
+ return ret
+}
+
+Buffer.concat = function (list, totalLength) {
+ Buffer.assert(Buffer.isArray(list), 'Usage: Buffer.concat(list[, length])')
+
+ if (list.length === 0) {
+ return new Buffer(0)
+ } else if (list.length === 1) {
+ return list[0]
+ }
+
+ var i
+ if (totalLength === undefined) {
+ totalLength = 0
+ for (i = 0; i < list.length; i++) {
+ totalLength += list[i].length
+ }
+ }
+
+ var buf = new Buffer(totalLength)
+ var pos = 0
+ for (i = 0; i < list.length; i++) {
+ var item = list[i]
+ item.copy(buf, pos)
+ pos += item.length
+ }
+ return buf
+}
+
+Buffer.compare = function (a, b) {
+ Buffer.assert(Buffer.isBuffer(a) && Buffer.isBuffer(b), 'Arguments must be Buffers')
+ var x = a.length
+ var y = b.length
+ for (var i = 0, len = Math.min(x, y); i < len && a[i] === b[i]; i++) {}
+ if (i !== len) {
+ x = a[i]
+ y = b[i]
+ }
+ if (x < y) {
+ return -1
+ }
+ if (y < x) {
+ return 1
+ }
+ return 0
+}
+
+// BUFFER INSTANCE METHODS
+// =======================
+
+Buffer.hexWrite = function(buf, string, offset, length) {
+ offset = Number(offset) || 0
+ var remaining = buf.length - offset
+ if (!length) {
+ length = remaining
+ } else {
+ length = Number(length)
+ if (length > remaining) {
+ length = remaining
+ }
+ }
+
+ // must be an even number of digits
+ var strLen = string.length
+ Buffer.assert(strLen % 2 === 0, 'Invalid hex string')
+
+ if (length > strLen / 2) {
+ length = strLen / 2
+ }
+ for (var i = 0; i < length; i++) {
+ var b = parseInt(string.substr(i * 2, 2), 16)
+ Buffer.assert(!isNaN(b), 'Invalid hex string')
+ buf[offset + i] = b
+ }
+ return i
+}
+
+Buffer.utf8Write = function(buf, string, offset, length) {
+ var charsWritten = Buffer.blitBuffer(Buffer.utf8ToBytes(string), buf, offset, length)
+ return charsWritten
+}
+
+Buffer.asciiWrite = function(buf, string, offset, length) {
+ var charsWritten = Buffer.blitBuffer(Buffer.asciiToBytes(string), buf, offset, length)
+ return charsWritten
+}
+
+Buffer.binaryWrite = function(buf, string, offset, length) {
+ return Buffer.asciiWrite(buf, string, offset, length)
+}
+
+Buffer.base64Write = function(buf, string, offset, length) {
+ var charsWritten = Buffer.blitBuffer(Buffer.base64ToBytes(string), buf, offset, length)
+ return charsWritten
+}
+
+Buffer.utf16leWrite = function(buf, string, offset, length) {
+ var charsWritten = Buffer.blitBuffer(Buffer.utf16leToBytes(string), buf, offset, length)
+ return charsWritten
+}
+
+Buffer.prototype.write = function (string, offset, length, encoding) {
+ // Support both (string, offset, length, encoding)
+ // and the legacy (string, encoding, offset, length)
+ if (isFinite(offset)) {
+ if (!isFinite(length)) {
+ encoding = length
+ length = undefined
+ }
+ } else { // legacy
+ var swap = encoding
+ encoding = offset
+ offset = length
+ length = swap
+ }
+
+ offset = Number(offset) || 0
+ var remaining = this.length - offset
+ if (!length) {
+ length = remaining
+ } else {
+ length = Number(length)
+ if (length > remaining) {
+ length = remaining
+ }
+ }
+ encoding = String(encoding || 'utf8').toLowerCase()
+
+ var ret
+ switch (encoding) {
+ case 'hex':
+ ret = Buffer.hexWrite(this, string, offset, length)
+ break
+ case 'utf8':
+ case 'utf-8':
+ ret = Buffer.utf8Write(this, string, offset, length)
+ break
+ case 'ascii':
+ ret = Buffer.asciiWrite(this, string, offset, length)
+ break
+ case 'binary':
+ ret = Buffer.binaryWrite(this, string, offset, length)
+ break
+ case 'base64':
+ ret = Buffer.base64Write(this, string, offset, length)
+ break
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ ret = Buffer.utf16leWrite(this, string, offset, length)
+ break
+ default:
+ throw new Error('Unknown encoding')
+ }
+ return ret
+}
+
+Buffer.prototype.toString = function (encoding, start, end) {
+ var self = this
+
+ encoding = String(encoding || 'utf8').toLowerCase()
+ start = Number(start) || 0
+ end = (end === undefined) ? self.length : Number(end)
+
+ // Fastpath empty strings
+ if (end === start)
+ return ''
+
+ var ret
+ switch (encoding) {
+ case 'hex':
+ ret = Buffer.hexSlice(self, start, end)
+ break
+ case 'utf8':
+ case 'utf-8':
+ ret = Buffer.utf8Slice(self, start, end)
+ break
+ case 'ascii':
+ ret = Buffer.asciiSlice(self, start, end)
+ break
+ case 'binary':
+ ret = Buffer.binarySlice(self, start, end)
+ break
+ case 'base64':
+ ret = Buffer.base64Slice(self, start, end)
+ break
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ ret = utf16leSlice(self, start, end)
+ break
+ default:
+ throw new Error('Unknown encoding')
+ }
+ return ret
+}
+
+Buffer.prototype.toJSON = function () {
+ return {
+ type: 'Buffer',
+ data: Array.prototype.slice.call(this._arr || this, 0)
+ }
+}
+
+Buffer.prototype.equals = function (b) {
+ Buffer.assert(Buffer.isBuffer(b), 'Argument must be a Buffer')
+ return Buffer.compare(this, b) === 0
+}
+
+Buffer.prototype.compare = function (b) {
+ Buffer.assert(Buffer.isBuffer(b), 'Argument must be a Buffer')
+ return Buffer.compare(this, b)
+}
+
+// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
+Buffer.prototype.copy = function (target, target_start, start, end) {
+ var source = this
+
+ if (!start) start = 0
+ if (!end && end !== 0) end = this.length
+ if (!target_start) target_start = 0
+
+ // Copy 0 bytes; we're done
+ if (end === start) return
+ if (target.length === 0 || source.length === 0) return
+
+ // Fatal error conditions
+ Buffer.assert(end >= start, 'sourceEnd < sourceStart')
+ Buffer.assert(target_start >= 0 && target_start < target.length,
+ 'targetStart out of bounds')
+ Buffer.assert(start >= 0 && start < source.length, 'sourceStart out of bounds')
+ Buffer.assert(end >= 0 && end <= source.length, 'sourceEnd out of bounds')
+
+ // Are we oob?
+ if (end > this.length)
+ end = this.length
+ if (target.length - target_start < end - start)
+ end = target.length - target_start + start
+
+ var len = end - start
+
+ if (len < 100 || !Buffer._useTypedArrays) {
+ for (var i = 0; i < len; i++) {
+ target[i + target_start] = this[i + start]
+ }
+ } else {
+ target._set(this.subarray(start, start + len), target_start)
+ }
+}
+
+Buffer.base64Slice = function(buf, start, end) {
+ if (start === 0 && end === buf.length) {
+ return base64.fromByteArray(buf)
+ } else {
+ return base64.fromByteArray(buf.slice(start, end))
+ }
+}
+
+Buffer.utf8Slice = function(buf, start, end) {
+ var res = ''
+ var tmp = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; i++) {
+ if (buf[i] <= 0x7F) {
+ res += Buffer.decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
+ tmp = ''
+ } else {
+ tmp += '%' + buf[i].toString(16)
+ }
+ }
+
+ return res + Buffer.decodeUtf8Char(tmp)
+}
+
+Buffer.asciiSlice = function(buf, start, end) {
+ var ret = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; i++) {
+ ret += String.fromCharCode(buf[i])
+ }
+ return ret
+}
+
+Buffer.binarySlice = function(buf, start, end) {
+ return Buffer.asciiSlice(buf, start, end)
+}
+
+Buffer.hexSlice = function(buf, start, end) {
+ var len = buf.length
+
+ if (!start || start < 0) start = 0
+ if (!end || end < 0 || end > len) end = len
+
+ var out = ''
+ for (var i = start; i < end; i++) {
+ out += Buffer.toHex(buf[i])
+ }
+ return out
+}
+
+function utf16leSlice (buf, start, end) {
+ var bytes = buf.slice(start, end)
+ var res = ''
+ for (var i = 0; i < bytes.length; i += 2) {
+ res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
+ }
+ return res
+}
+
+Buffer.prototype.slice = function (start, end) {
+ var len = this.length
+ start = Buffer.clamp(start, len, 0)
+ end = Buffer.clamp(end, len, len)
+
+ if (Buffer._useTypedArrays) {
+ return Buffer._augment(this.subarray(start, end))
+ } else {
+ var sliceLen = end - start
+ var newBuf = new Buffer(sliceLen, undefined, true)
+ for (var i = 0; i < sliceLen; i++) {
+ newBuf[i] = this[i + start]
+ }
+ return newBuf
+ }
+}
+
+// `get` will be removed in Node 0.13+
+Buffer.prototype.get = function (offset) {
+ console.log('.get() is deprecated. Access using array indexes instead.')
+ return this.readUInt8(offset)
+}
+
+// `set` will be removed in Node 0.13+
+Buffer.prototype.set = function (v, offset) {
+ console.log('.set() is deprecated. Access using array indexes instead.')
+ return this.writeUInt8(v, offset)
+}
+
+Buffer.prototype.readUInt8 = function (offset, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset < this.length, 'Trying to read beyond buffer length')
+ }
+
+ if (offset >= this.length)
+ return
+
+ return this[offset]
+}
+
+Buffer.readUInt16 = function(buf, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset + 1 < buf.length, 'Trying to read beyond buffer length')
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ var val
+ if (littleEndian) {
+ val = buf[offset]
+ if (offset + 1 < len)
+ val |= buf[offset + 1] << 8
+ } else {
+ val = buf[offset] << 8
+ if (offset + 1 < len)
+ val |= buf[offset + 1]
+ }
+ return val
+}
+
+Buffer.prototype.readUInt16LE = function (offset, noAssert) {
+ return Buffer.readUInt16(this, offset, true, noAssert)
+}
+
+Buffer.prototype.readUInt16BE = function (offset, noAssert) {
+ return Buffer.readUInt16(this, offset, false, noAssert)
+}
+
+Buffer.readUInt32 = function(buf, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset + 3 < buf.length, 'Trying to read beyond buffer length')
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ var val
+ if (littleEndian) {
+ if (offset + 2 < len)
+ val = buf[offset + 2] << 16
+ if (offset + 1 < len)
+ val |= buf[offset + 1] << 8
+ val |= buf[offset]
+ if (offset + 3 < len)
+ val = val + (buf[offset + 3] << 24 >>> 0)
+ } else {
+ if (offset + 1 < len)
+ val = buf[offset + 1] << 16
+ if (offset + 2 < len)
+ val |= buf[offset + 2] << 8
+ if (offset + 3 < len)
+ val |= buf[offset + 3]
+ val = val + (buf[offset] << 24 >>> 0)
+ }
+ return val
+}
+
+Buffer.prototype.readUInt32LE = function (offset, noAssert) {
+ return Buffer.readUInt32(this, offset, true, noAssert)
+}
+
+Buffer.prototype.readUInt32BE = function (offset, noAssert) {
+ return Buffer.readUInt32(this, offset, false, noAssert)
+}
+
+Buffer.prototype.readInt8 = function (offset, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(offset !== undefined && offset !== null,
+ 'missing offset')
+ Buffer.assert(offset < this.length, 'Trying to read beyond buffer length')
+ }
+
+ if (offset >= this.length)
+ return
+
+ var neg = this[offset] & 0x80
+ if (neg)
+ return (0xff - this[offset] + 1) * -1
+ else
+ return this[offset]
+}
+
+Buffer.readInt16 = function(buf, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset + 1 < buf.length, 'Trying to read beyond buffer length')
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ var val = Buffer.readUInt16(buf, offset, littleEndian, true)
+ var neg = val & 0x8000
+ if (neg)
+ return (0xffff - val + 1) * -1
+ else
+ return val
+}
+
+Buffer.prototype.readInt16LE = function (offset, noAssert) {
+ return Buffer.readInt16(this, offset, true, noAssert)
+}
+
+Buffer.prototype.readInt16BE = function (offset, noAssert) {
+ return Buffer.readInt16(this, offset, false, noAssert)
+}
+
+Buffer.readInt32 = function(buf, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset + 3 < buf.length, 'Trying to read beyond buffer length')
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ var val = Buffer.readUInt32(buf, offset, littleEndian, true)
+ var neg = val & 0x80000000
+ if (neg)
+ return (0xffffffff - val + 1) * -1
+ else
+ return val
+}
+
+Buffer.prototype.readInt32LE = function (offset, noAssert) {
+ return Buffer.readInt32(this, offset, true, noAssert)
+}
+
+Buffer.prototype.readInt32BE = function (offset, noAssert) {
+ return Buffer.readInt32(this, offset, false, noAssert)
+}
+
+Buffer.readFloat = function(buf, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ Buffer.assert(offset + 3 < buf.length, 'Trying to read beyond buffer length')
+ }
+
+ return ieee754.read(buf, offset, littleEndian, 23, 4)
+}
+
+Buffer.prototype.readFloatLE = function (offset, noAssert) {
+ return Buffer.readFloat(this, offset, true, noAssert)
+}
+
+Buffer.prototype.readFloatBE = function (offset, noAssert) {
+ return Buffer.readFloat(this, offset, false, noAssert)
+}
+
+Buffer.readDouble = function(buf, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ Buffer.assert(offset + 7 < buf.length, 'Trying to read beyond buffer length')
+ }
+
+ return ieee754.read(buf, offset, littleEndian, 52, 8)
+}
+
+Buffer.prototype.readDoubleLE = function (offset, noAssert) {
+ return Buffer.readDouble(this, offset, true, noAssert)
+}
+
+Buffer.prototype.readDoubleBE = function (offset, noAssert) {
+ return Buffer.readDouble(this, offset, false, noAssert)
+}
+
+Buffer.prototype.writeUInt8 = function (value, offset, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(value !== undefined && value !== null, 'missing value')
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset < this.length, 'trying to write beyond buffer length')
+ Buffer.verifuint(value, 0xff)
+ }
+
+ if (offset >= this.length) return
+
+ this[offset] = value
+ return offset + 1
+}
+
+Buffer.writeUInt16 = function(buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(value !== undefined && value !== null, 'missing value')
+ Buffer.assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset + 1 < buf.length, 'trying to write beyond buffer length')
+ Buffer.verifuint(value, 0xffff)
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ for (var i = 0, j = Math.min(len - offset, 2); i < j; i++) {
+ buf[offset + i] =
+ (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
+ (littleEndian ? i : 1 - i) * 8
+ }
+ return offset + 2
+}
+
+Buffer.prototype.writeUInt16LE = function (value, offset, noAssert) {
+ return Buffer.writeUInt16(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeUInt16BE = function (value, offset, noAssert) {
+ return Buffer.writeUInt16(this, value, offset, false, noAssert)
+}
+
+Buffer.writeUInt32 = function(buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(value !== undefined && value !== null, 'missing value')
+ Buffer.assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset + 3 < buf.length, 'trying to write beyond buffer length')
+ Buffer.verifuint(value, 0xffffffff)
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ for (var i = 0, j = Math.min(len - offset, 4); i < j; i++) {
+ buf[offset + i] =
+ (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
+ }
+ return offset + 4
+}
+
+Buffer.prototype.writeUInt32LE = function (value, offset, noAssert) {
+ return Buffer.writeUInt32(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeUInt32BE = function (value, offset, noAssert) {
+ return Buffer.writeUInt32(this, value, offset, false, noAssert)
+}
+
+Buffer.prototype.writeInt8 = function (value, offset, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(value !== undefined && value !== null, 'missing value')
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset < this.length, 'Trying to write beyond buffer length')
+ Buffer.verifsint(value, 0x7f, -0x80)
+ }
+
+ if (offset >= this.length)
+ return
+
+ if (value >= 0)
+ this.writeUInt8(value, offset, noAssert)
+ else
+ this.writeUInt8(0xff + value + 1, offset, noAssert)
+ return offset + 1
+}
+
+Buffer.writeInt16 = function(buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(value !== undefined && value !== null, 'missing value')
+ Buffer.assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset + 1 < buf.length, 'Trying to write beyond buffer length')
+ Buffer.verifsint(value, 0x7fff, -0x8000)
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ if (value >= 0)
+ Buffer.writeUInt16(buf, value, offset, littleEndian, noAssert)
+ else
+ Buffer.writeUInt16(buf, 0xffff + value + 1, offset, littleEndian, noAssert)
+ return offset + 2
+}
+
+Buffer.prototype.writeInt16LE = function (value, offset, noAssert) {
+ return Buffer.writeInt16(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeInt16BE = function (value, offset, noAssert) {
+ return Buffer.writeInt16(this, value, offset, false, noAssert)
+}
+
+Buffer.writeInt32 = function(buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(value !== undefined && value !== null, 'missing value')
+ Buffer.assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset + 3 < buf.length, 'Trying to write beyond buffer length')
+ Buffer.verifsint(value, 0x7fffffff, -0x80000000)
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ if (value >= 0)
+ Buffer.writeUInt32(buf, value, offset, littleEndian, noAssert)
+ else
+ Buffer.writeUInt32(buf, 0xffffffff + value + 1, offset, littleEndian, noAssert)
+ return offset + 4
+}
+
+Buffer.prototype.writeInt32LE = function (value, offset, noAssert) {
+ return Buffer.writeInt32(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeInt32BE = function (value, offset, noAssert) {
+ return Buffer.writeInt32(this, value, offset, false, noAssert)
+}
+
+Buffer.writeFloat = function(buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(value !== undefined && value !== null, 'missing value')
+ Buffer.assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset + 3 < buf.length, 'Trying to write beyond buffer length')
+ Buffer.verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38)
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ ieee754.write(buf, value, offset, littleEndian, 23, 4)
+ return offset + 4
+}
+
+Buffer.prototype.writeFloatLE = function (value, offset, noAssert) {
+ return Buffer.writeFloat(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeFloatBE = function (value, offset, noAssert) {
+ return Buffer.writeFloat(this, value, offset, false, noAssert)
+}
+
+Buffer.writeDouble = function(buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ Buffer.assert(value !== undefined && value !== null, 'missing value')
+ Buffer.assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
+ Buffer.assert(offset !== undefined && offset !== null, 'missing offset')
+ Buffer.assert(offset + 7 < buf.length,
+ 'Trying to write beyond buffer length')
+ Buffer.verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308)
+ }
+
+ var len = buf.length
+ if (offset >= len)
+ return
+
+ ieee754.write(buf, value, offset, littleEndian, 52, 8)
+ return offset + 8
+}
+
+Buffer.prototype.writeDoubleLE = function (value, offset, noAssert) {
+ return Buffer.writeDouble(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeDoubleBE = function (value, offset, noAssert) {
+ return Buffer.writeDouble(this, value, offset, false, noAssert)
+}
+
+// fill(value, start=0, end=buffer.length)
+Buffer.prototype.fill = function (value, start, end) {
+ if (!value) value = 0
+ if (!start) start = 0
+ if (!end) end = this.length
+
+ Buffer.assert(end >= start, 'end < start')
+
+ // Fill 0 bytes; we're done
+ if (end === start) return
+ if (this.length === 0) return
+
+ Buffer.assert(start >= 0 && start < this.length, 'start out of bounds')
+ Buffer.assert(end >= 0 && end <= this.length, 'end out of bounds')
+
+ var i
+ if (typeof value === 'number') {
+ for (i = start; i < end; i++) {
+ this[i] = value
+ }
+ } else {
+ var bytes = Buffer.utf8ToBytes(value.toString())
+ var len = bytes.length
+ for (i = start; i < end; i++) {
+ this[i] = bytes[i % len]
+ }
+ }
+
+ return this
+}
+
+Buffer.prototype.inspect = function () {
+ var out = []
+ var len = this.length
+ for (var i = 0; i < len; i++) {
+ out[i] = Buffer.toHex(this[i])
+ if (i === exports.INSPECT_MAX_BYTES) {
+ out[i + 1] = '...'
+ break
+ }
+ }
+ return '<Buffer ' + out.join(' ') + '>'
+}
+
+/**
+ * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance.
+ * Added in Node 0.12. Only available in browsers that support ArrayBuffer.
+ */
+Buffer.prototype.toArrayBuffer = function () {
+ if (typeof Uint8Array !== 'undefined') {
+ if (Buffer._useTypedArrays) {
+ return (new Buffer(this)).buffer
+ } else {
+ var buf = new Uint8Array(this.length)
+ for (var i = 0, len = buf.length; i < len; i += 1) {
+ buf[i] = this[i]
+ }
+ return buf.buffer
+ }
+ } else {
+ throw new Error('Buffer.toArrayBuffer not supported in this browser')
+ }
+}
+
+// HELPER FUNCTIONS
+// ================
+
+var BP = Buffer.prototype
+
+/**
+ * Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods
+ */
+Buffer._augment = function (arr) {
+ arr._isBuffer = true
+
+ // save reference to original Uint8Array get/set methods before overwriting
+ arr._get = arr.get
+ arr._set = arr.set
+
+ // deprecated, will be removed in node 0.13+
+ arr.get = BP.get
+ arr.set = BP.set
+
+ arr.write = BP.write
+ arr.toString = BP.toString
+ arr.toLocaleString = BP.toString
+ arr.toJSON = BP.toJSON
+ arr.equals = BP.equals
+ arr.compare = BP.compare
+ arr.copy = BP.copy
+ arr.slice = BP.slice
+ arr.readUInt8 = BP.readUInt8
+ arr.readUInt16LE = BP.readUInt16LE
+ arr.readUInt16BE = BP.readUInt16BE
+ arr.readUInt32LE = BP.readUInt32LE
+ arr.readUInt32BE = BP.readUInt32BE
+ arr.readInt8 = BP.readInt8
+ arr.readInt16LE = BP.readInt16LE
+ arr.readInt16BE = BP.readInt16BE
+ arr.readInt32LE = BP.readInt32LE
+ arr.readInt32BE = BP.readInt32BE
+ arr.readFloatLE = BP.readFloatLE
+ arr.readFloatBE = BP.readFloatBE
+ arr.readDoubleLE = BP.readDoubleLE
+ arr.readDoubleBE = BP.readDoubleBE
+ arr.writeUInt8 = BP.writeUInt8
+ arr.writeUInt16LE = BP.writeUInt16LE
+ arr.writeUInt16BE = BP.writeUInt16BE
+ arr.writeUInt32LE = BP.writeUInt32LE
+ arr.writeUInt32BE = BP.writeUInt32BE
+ arr.writeInt8 = BP.writeInt8
+ arr.writeInt16LE = BP.writeInt16LE
+ arr.writeInt16BE = BP.writeInt16BE
+ arr.writeInt32LE = BP.writeInt32LE
+ arr.writeInt32BE = BP.writeInt32BE
+ arr.writeFloatLE = BP.writeFloatLE
+ arr.writeFloatBE = BP.writeFloatBE
+ arr.writeDoubleLE = BP.writeDoubleLE
+ arr.writeDoubleBE = BP.writeDoubleBE
+ arr.fill = BP.fill
+ arr.inspect = BP.inspect
+ arr.toArrayBuffer = BP.toArrayBuffer
+
+ return arr
+}
+
+Buffer.stringtrim = function(str) {
+ if (str.trim) return str.trim()
+ return str.replace(/^\s+|\s+$/g, '')
+}
+
+// slice(start, end)
+Buffer.clamp = function(index, len, defaultValue) {
+ if (typeof index !== 'number') return defaultValue
+ index = ~~index; // Coerce to integer.
+ if (index >= len) return len
+ if (index >= 0) return index
+ index += len
+ if (index >= 0) return index
+ return 0
+}
+
+Buffer.coerce = function(length) {
+ // Coerce length to a number (possibly NaN), round up
+ // in case it's fractional (e.g. 123.456) then do a
+ // double negate to coerce a NaN to 0. Easy, right?
+ length = ~~Math.ceil(+length)
+ return length < 0 ? 0 : length
+}
+
+Buffer.isArray = function(subject) {
+ return (Array.isArray || function (subject) {
+ return Object.prototype.toString.call(subject) === '[object Array]'
+ })(subject)
+}
+
+Buffer.isArrayish = function(subject) {
+ return Buffer.isArray(subject) || Buffer.isBuffer(subject) ||
+ subject && typeof subject === 'object' &&
+ typeof subject.length === 'number'
+}
+
+Buffer.toHex = function(n) {
+ if (n < 16) return '0' + n.toString(16)
+ return n.toString(16)
+}
+
+Buffer.utf8ToBytes = function(str) {
+ var byteArray = []
+ for (var i = 0; i < str.length; i++) {
+ var b = str.charCodeAt(i)
+ if (b <= 0x7F) {
+ byteArray.push(b)
+ } else {
+ var start = i
+ if (b >= 0xD800 && b <= 0xDFFF) i++
+ var h = encodeURIComponent(str.slice(start, i+1)).substr(1).split('%')
+ for (var j = 0; j < h.length; j++) {
+ byteArray.push(parseInt(h[j], 16))
+ }
+ }
+ }
+ return byteArray
+}
+
+Buffer.asciiToBytes = function(str) {
+ var byteArray = []
+ for (var i = 0; i < str.length; i++) {
+ // Node's code seems to be doing this and not & 0x7F..
+ byteArray.push(str.charCodeAt(i) & 0xFF)
+ }
+ return byteArray
+}
+
+Buffer.utf16leToBytes = function(str) {
+ var c, hi, lo
+ var byteArray = []
+ for (var i = 0; i < str.length; i++) {
+ c = str.charCodeAt(i)
+ hi = c >> 8
+ lo = c % 256
+ byteArray.push(lo)
+ byteArray.push(hi)
+ }
+
+ return byteArray
+}
+
+Buffer.base64ToBytes = function(str) {
+ return base64.toByteArray(str)
+}
+
+Buffer.blitBuffer = function(src, dst, offset, length) {
+ for (var i = 0; i < length; i++) {
+ if ((i + offset >= dst.length) || (i >= src.length))
+ break
+ dst[i + offset] = src[i]
+ }
+ return i
+}
+
+Buffer.decodeUtf8Char = function(str) {
+ try {
+ return decodeURIComponent(str)
+ } catch (err) {
+ return String.fromCharCode(0xFFFD) // UTF 8 invalid char
+ }
+}
/*
- * Return true if necessary JavaScript support is available, else log an error and return false.
+ * We have to make sure that the value is a valid integer. This means that it
+ * is non-negative. It has no fractional component and that it does not
+ * exceed the maximum allowed value.
*/
-NDN.getSupported = function() {
+Buffer.verifuint = function(value, max) {
+ Buffer.assert(typeof value === 'number', 'cannot write a non-number as a number')
+ Buffer.assert(value >= 0, 'specified a negative value for writing an unsigned value')
+ Buffer.assert(value <= max, 'value is larger than maximum value for type')
+ Buffer.assert(Math.floor(value) === value, 'value has a fractional component')
+}
+
+Buffer.verifsint = function(value, max, min) {
+ Buffer.assert(typeof value === 'number', 'cannot write a non-number as a number')
+ Buffer.assert(value <= max, 'value larger than maximum allowed value')
+ Buffer.assert(value >= min, 'value smaller than minimum allowed value')
+ Buffer.assert(Math.floor(value) === value, 'value has a fractional component')
+}
+
+Buffer.verifIEEE754 = function(value, max, min) {
+ Buffer.assert(typeof value === 'number', 'cannot write a non-number as a number')
+ Buffer.assert(value <= max, 'value larger than maximum allowed value')
+ Buffer.assert(value >= min, 'value smaller than minimum allowed value')
+}
+
+Buffer.assert = function(test, message) {
+ if (!test) throw new Error(message || 'Failed assertion')
+}
+/**
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * The Log class holds the global static variable LOG.
+ */
+var Log = function Log()
+{
+}
+
+exports.Log = Log;
+
+/**
+ * LOG is the level for logging debugging statements. 0 means no log messages.
+ * @type Number
+ */
+Log.LOG = 0;
+/**
+ * Encapsulate a Buffer and support dynamic reallocation.
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var printStackTrace = require('../../contrib/stacktrace/stacktrace.js').printStackTrace;
+
+/**
+ * NdnCommon has static NDN utility methods and constants.
+ * @constructor
+ */
+var NdnCommon = {};
+
+exports.NdnCommon = NdnCommon;
+
+/**
+ * The practical limit of the size of a network-layer packet. If a packet is
+ * larger than this, the library or application MAY drop it. This constant is
+ * defined in this low-level class so that internal code can use it, but
+ * applications should use the static API method
+ * Face.getMaxNdnPacketSize() which is equivalent.
+ */
+NdnCommon.MAX_NDN_PACKET_SIZE = 8800;
+
+/**
+ * Get the error message plus its stack trace.
+ * @param {Error} error The error object.
+ * @return {string} The error message, plus the stack trace with each line
+ * separated by '\n'.
+ */
+NdnCommon.getErrorWithStackTrace = function(error)
+{
+ return error + '\n' + printStackTrace({e: error}).join('\n');
+};
+
+/**
+ * Check for Indexed DB support and call onComplete with the result as described
+ * below. This has to use an onComplete callback since IndexedDB is async.
+ * @param {function} onComplete This calls onComplete(haveIndexedDb) where
+ * haveIndexedDb is true if the browser has Indexed DB support, otherwise false.
+ */
+NdnCommon.checkIndexedDb = function(onComplete)
+{
+ try {
+ var database = new Dexie("test-Dexie-support");
+ database.version(1).stores({});
+ database.open();
+
+ // Give Dexie a little time to open.
+ setTimeout(function() {
+ try {
+ onComplete(database.isOpen());
+ } catch (ex) {
+ onComplete(false);
+ }
+ }, 200);
+ } catch (ex) {
+ onComplete(false);
+ }
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var NdnCommon = require('./ndn-common.js').NdnCommon;
+
+/**
+ * An ExponentialReExpress uses an internal onTimeout to express the interest again
+ * with double the interestLifetime. See ExponentialReExpress.makeOnTimeout,
+ * which you should call instead of the private constructor.
+ * Create a new ExponentialReExpress where onTimeout expresses the interest
+ * again with double the interestLifetime. If the interesLifetime goes
+ * over settings.maxInterestLifetime, then call the given onTimeout. If this
+ * internally gets onData, just call the given onData.
+ * @constructor
+ */
+var ExponentialReExpress = function ExponentialReExpress
+ (face, onData, onTimeout, settings)
+{
+ settings = (settings || {});
+ this.face = face;
+ this.callerOnData = onData;
+ this.callerOnTimeout = onTimeout;
+
+ this.maxInterestLifetime = (settings.maxInterestLifetime || 16000);
+};
+
+exports.ExponentialReExpress = ExponentialReExpress;
+
+/**
+ * Return a callback to use in expressInterest for onTimeout which will express
+ * the interest again with double the interestLifetime. If the interesLifetime
+ * goes over maxInterestLifetime (see settings below), then call the provided
+ * onTimeout. If a Data packet is received, this calls the provided onData.
+ * Use it like this:
+ * var onData = function() { ... };
+ * var onTimeout = function() { ... };
+ * face.expressInterest
+ * (interest, onData,
+ * ExponentialReExpress.makeOnTimeout(face, onData, onTimeout));
+ * @param {Face} face This calls face.expressInterest.
+ * @param {function} onData When a matching data packet is received, this calls
+ * onData(interest, data) where interest is the interest given to
+ * expressInterest and data is the received Data object. This is normally the
+ * same onData you initially passed to expressInterest.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onTimeout If the interesLifetime goes over
+ * maxInterestLifetime, this calls onTimeout(interest). However, if onTimeout is
+ * null, this does not use it.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {Object} settings (optional) If not null, an associative array with
+ * the following defaults:
+ * {
+ * maxInterestLifetime: 16000 // milliseconds
+ * }
+ * @return {function} The onTimeout callback to pass to expressInterest.
+ */
+ExponentialReExpress.makeOnTimeout = function(face, onData, onTimeout, settings)
+{
+ var reExpress = new ExponentialReExpress(face, onData, onTimeout, settings);
+ return function(interest) { reExpress.onTimeout(interest); };
+};
+
+ExponentialReExpress.prototype.onTimeout = function(interest)
+{
+ var interestLifetime = interest.getInterestLifetimeMilliseconds();
+ if (interestLifetime == null) {
+ // Can't re-express.
+ if (this.callerOnTimeout) {
+ try {
+ this.callerOnTimeout(interest);
+ } catch (ex) {
+ console.log("Error in onTimeout: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ return;
+ }
+
+ var nextInterestLifetime = interestLifetime * 2;
+ if (nextInterestLifetime > this.maxInterestLifetime) {
+ if (this.callerOnTimeout) {
+ try {
+ this.callerOnTimeout(interest);
+ } catch (ex) {
+ console.log("Error in onTimeout: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ return;
+ }
+
+ var nextInterest = interest.clone();
+ nextInterest.setInterestLifetimeMilliseconds(nextInterestLifetime);
+ var thisObject = this;
+ this.face.expressInterest
+ (nextInterest, this.callerOnData,
+ function(localInterest) { thisObject.onTimeout(localInterest); });
+};
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * A Blob holds an immutable byte array implemented as a Buffer. This should be
+ * treated like a string which is a pointer to an immutable string. (It is OK to
+ * pass a pointer to the string because the new owner can’t change the bytes of
+ * the string.) Blob does not inherit from Buffer. Instead you must call buf()
+ * to get the byte array which reminds you that you should not change the
+ * contents. Also remember that buf() can return null.
+ * @param {Blob|Buffer|Array<number>} value (optional) If value is a Blob, take
+ * another pointer to the Buffer without copying. If value is a Buffer or byte
+ * array, copy to create a new Buffer. If omitted, buf() will return null.
+ * @param {boolean} copy (optional) If true, copy the contents of
+ * value into a new Buffer. If false, just use the existing value without
+ * copying. If omitted, then copy the contents (unless value is already a Blob).
+ * IMPORTANT: If copy is false, if you keep a pointer to the value then you must
+ * treat the value as immutable and promise not to change it.
+ * @constructor
+ */
+var Blob = function Blob(value, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ if (value == null)
+ this.buffer = null;
+ else if (typeof value === 'object' && value instanceof Blob)
+ // Use the existing buffer. Don't need to check for copy.
+ this.buffer = value.buffer;
+ else {
+ if (typeof value === 'string')
+ // Convert from a string to utf-8 byte encoding.
+ this.buffer = new Buffer(value, 'utf8');
+ else {
+ if (copy)
+ // We are copying, so just make another Buffer.
+ this.buffer = new Buffer(value);
+ else {
+ if (Buffer.isBuffer(value))
+ // We can use as-is.
+ this.buffer = value;
+ else
+ // We need a Buffer, so copy.
+ this.buffer = new Buffer(value);
+ }
+ }
+ }
+
+ // Set the length to be "JavaScript-like".
+ this.length = this.buffer != null ? this.buffer.length : 0;
+};
+
+exports.Blob = Blob;
+
+/**
+ * Return the length of the immutable byte array.
+ * @return {number} The length of the array. If buf() is null, return 0.
+ */
+Blob.prototype.size = function()
+{
+ if (this.buffer != null)
+ return this.buffer.length;
+ else
+ return 0;
+};
+
+/**
+ * Return the immutable byte array. DO NOT change the contents of the Buffer.
+ * If you need to change it, make a copy.
+ * @return {Buffer} The Buffer holding the immutable byte array, or null.
+ */
+Blob.prototype.buf = function()
+{
+ return this.buffer;
+};
+
+/**
+ * Return true if the array is null, otherwise false.
+ * @return {boolean} True if the array is null.
+ */
+Blob.prototype.isNull = function()
+{
+ return this.buffer == null;
+};
+
+/**
+ * Return the hex representation of the bytes in the byte array.
+ * @return {string} The hex string.
+ */
+Blob.prototype.toHex = function()
+{
+ if (this.buffer == null)
+ return "";
+ else
+ return this.buffer.toString('hex');
+};
+
+/**
+ * Decode the byte array as UTF8 and return the Unicode string.
+ * @return A unicode string, or "" if the buffer is null.
+ */
+Blob.prototype.toString = function()
+{
+ if (this.buffer == null)
+ return "";
+ else
+ return this.buffer.toString('utf8');
+};
+
+/**
+ * Check if the value of this Blob equals the other blob.
+ * @param {Blob} other The other Blob to check.
+ * @return {boolean} if this isNull and other isNull or if the bytes of this
+ * blob equal the bytes of the other.
+ */
+Blob.prototype.equals = function(other)
+{
+ if (this.isNull())
+ return other.isNull();
+ else if (other.isNull())
+ return false;
+ else {
+ if (this.buffer.length != other.buffer.length)
+ return false;
+
+ for (var i = 0; i < this.buffer.length; ++i) {
+ if (this.buffer[i] != other.buffer[i])
+ return false;
+ }
+
+ return true;
+ }
+};
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('./blob.js').Blob;
+
+/**
+ * A SignedBlob extends Blob to keep the offsets of a signed portion (e.g., the
+ * bytes of Data packet). This inherits from Blob, including Blob.size and Blob.buf.
+ * @param {Blob|Buffer|Array<number>} value (optional) If value is a Blob, take
+ * another pointer to the Buffer without copying. If value is a Buffer or byte
+ * array, copy to create a new Buffer. If omitted, buf() will return null.
+ * @param {number} signedPortionBeginOffset (optional) The offset in the
+ * encoding of the beginning of the signed portion. If omitted, set to 0.
+ * @param {number} signedPortionEndOffset (optional) The offset in the encoding
+ * of the end of the signed portion. If omitted, set to 0.
+ * @constructor
+ */
+var SignedBlob = function SignedBlob(value, signedPortionBeginOffset, signedPortionEndOffset)
+{
+ // Call the base constructor.
+ Blob.call(this, value);
+
+ if (this.buffer == null) {
+ this.signedPortionBeginOffset = 0;
+ this.signedPortionEndOffset = 0;
+ }
+ else if (typeof value === 'object' && value instanceof SignedBlob) {
+ // Copy the SignedBlob, allowing override for offsets.
+ this.signedPortionBeginOffset = signedPortionBeginOffset == null ?
+ value.signedPortionBeginOffset : signedPortionBeginOffset;
+ this.signedPortionEndOffset = signedPortionEndOffset == null ?
+ value.signedPortionEndOffset : signedPortionEndOffset;
+ }
+ else {
+ this.signedPortionBeginOffset = signedPortionBeginOffset || 0;
+ this.signedPortionEndOffset = signedPortionEndOffset || 0;
+ }
+
+ if (this.buffer == null)
+ this.signedBuffer = null;
+ else
+ this.signedBuffer = this.buffer.slice
+ (this.signedPortionBeginOffset, this.signedPortionEndOffset);
+};
+
+SignedBlob.prototype = new Blob();
+SignedBlob.prototype.name = "SignedBlob";
+
+exports.SignedBlob = SignedBlob;
+
+/**
+ * Return the length of the signed portion of the immutable byte array.
+ * @return {number} The length of the signed portion. If signedBuf() is null,
+ * return 0.
+ */
+SignedBlob.prototype.signedSize = function()
+{
+ if (this.signedBuffer != null)
+ return this.signedBuffer.length;
+ else
+ return 0;
+};
+
+/**
+ * Return a the signed portion of the immutable byte array.
+ * @return {Buffer} A slice into the Buffer which is the signed portion.
+ * If the pointer to the array is null, return null.
+ */
+SignedBlob.prototype.signedBuf = function() { return this.signedBuffer; };
+
+/**
+ * Return the offset in the array of the beginning of the signed portion.
+ * @return {number} The offset in the array.
+ */
+SignedBlob.prototype.getSignedPortionBeginOffset = function()
+{
+ return this.signedPortionBeginOffset;
+};
+
+/**
+ * Return the offset in the array of the end of the signed portion.
+ * @return {number} The offset in the array.
+ */
+SignedBlob.prototype.getSignedPortionEndOffset = function()
+{
+ return this.signedPortionEndOffset;
+};
+/**
+ * Encapsulate a Buffer and support dynamic reallocation.
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * Create a DynamicBuffer where this.array is a Buffer of size length.
+ * To access the array, use this.array or call slice.
+ * @constructor
+ * @param {number} length the initial length of the array. If null, use a default.
+ */
+var DynamicBuffer = function DynamicBuffer(length)
+{
+ if (!length)
+ length = 16;
+
+ this.array = new Buffer(length);
+};
+
+exports.DynamicBuffer = DynamicBuffer;
+
+/**
+ * Ensure that this.array has the length, reallocate and copy if necessary.
+ * Update the length of this.array which may be greater than length.
+ * @param {number} length The minimum length for the array.
+ */
+DynamicBuffer.prototype.ensureLength = function(length)
+{
+ if (this.array.length >= length)
+ return;
+
+ // See if double is enough.
+ var newLength = this.array.length * 2;
+ if (length > newLength)
+ // The needed length is much greater, so use it.
+ newLength = length;
+
+ var newArray = new Buffer(newLength);
+ this.array.copy(newArray);
+ this.array = newArray;
+};
+
+/**
+ * Copy the value to this.array at offset, reallocating if necessary.
+ * @param {Buffer} value The buffer to copy.
+ * @param {number} offset The offset in the buffer to start copying into.
+ * @return {number} The new offset which is offset + value.length.
+ */
+DynamicBuffer.prototype.copy = function(value, offset)
+{
+ this.ensureLength(value.length + offset);
+
+ if (Buffer.isBuffer(value))
+ value.copy(this.array, offset);
+ else
+ // Need to make value a Buffer to copy.
+ new Buffer(value).copy(this.array, offset);
+
+ return offset + value.length;
+};
+
+/**
+ * Ensure that this.array has the length. If necessary, reallocate the array
+ * and shift existing data to the back of the new array.
+ * Update the length of this.array which may be greater than length.
+ * @param {number} length The minimum length for the array.
+ */
+DynamicBuffer.prototype.ensureLengthFromBack = function(length)
+{
+ if (this.array.length >= length)
+ return;
+
+ // See if double is enough.
+ var newLength = this.array.length * 2;
+ if (length > newLength)
+ // The needed length is much greater, so use it.
+ newLength = length;
+
+ var newArray = new Buffer(newLength);
+ // Copy to the back of newArray.
+ this.array.copy(newArray, newArray.length - this.array.length);
+ this.array = newArray;
+};
+
+/**
+ * First call ensureLengthFromBack to make sure the bytearray has
+ * offsetFromBack bytes, then copy value into the array starting
+ * offsetFromBack bytes from the back of the array.
+ * @param {Buffer} value The buffer to copy.
+ * @param {number} offsetFromBack The offset from the back of the array to start
+ * copying.
+ */
+DynamicBuffer.prototype.copyFromBack = function(value, offsetFromBack)
+{
+ this.ensureLengthFromBack(offsetFromBack);
+
+ if (Buffer.isBuffer(value))
+ value.copy(this.array, this.array.length - offsetFromBack);
+ else
+ // Need to make value a Buffer to copy.
+ new Buffer(value).copy(this.array, this.array.length - offsetFromBack);
+};
+
+/**
+ * Return this.array.slice(begin, end);
+ * @param {number} begin The begin index for the slice.
+ * @param {number} end (optional) The end index for the slice.
+ * @return {Buffer} The buffer slice.
+ */
+DynamicBuffer.prototype.slice = function(begin, end)
+{
+ if (end == undefined)
+ return this.array.slice(begin);
+ else
+ return this.array.slice(begin, end);
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * A ChangeCounter keeps a target object whose change count is tracked by a
+ * local change count. You can set to a new target which updates the local
+ * change count, and you can call checkChanged to check if the target (or one of
+ * the target's targets) has been changed. The target object must have a method
+ * getChangeCount.
+ *
+ * Create a new ChangeCounter to track the given target. If target is not null,
+ * this sets the local change counter to target.getChangeCount().
+ * @param {object} target The target to track, as an object with the method
+ * getChangeCount().
+ * @constructor
+ */
+var ChangeCounter = function ChangeCounter(target)
+{
+ this.target = target;
+ this.changeCount = (target == null ? 0 : target.getChangeCount());
+};
+
+exports.ChangeCounter = ChangeCounter;
+
+/**
+ * Get the target object. If the target is changed, then checkChanged will
+ * detect it.
+ * @return {object} The target, as an object with the method
+ * getChangeCount().
+ */
+ChangeCounter.prototype.get = function()
+{
+ return this.target;
+};
+
+/**
+ * Set the target to the given target. If target is not null, this sets the
+ * local change counter to target.getChangeCount().
+ * @param {object} target The target to track, as an object with the method
+ * getChangeCount().
+ */
+ChangeCounter.prototype.set = function(target)
+{
+ this.target = target;
+ this.changeCount = (target == null ? 0 : target.getChangeCount());
+};
+
+/**
+ * If the target's change count is different than the local change count, then
+ * update the local change count and return true. Otherwise return false,
+ * meaning that the target has not changed. Also, if the target is null,
+ * simply return false. This is useful since the target (or one of the target's
+ * targets) may be changed and you need to find out.
+ * @return {boolean} True if the change count has been updated, false if not.
+ */
+ChangeCounter.prototype.checkChanged = function()
+{
+ if (this.target == null)
+ return false;
+
+ var targetChangeCount = this.target.getChangeCount();
+ if (this.changeCount != targetChangeCount) {
+ this.changeCount = targetChangeCount;
+ return true;
+ }
+ else
+ return false;
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var NdnCommon = require('./ndn-common.js').NdnCommon;
+
+/**
+ * A SyncPromise is a promise which is immediately fulfilled or rejected, used
+ * to return a promise in synchronous code.
+ * This private constructor creates a SyncPromise fulfilled or rejected with the
+ * given value. You should normally not call this constructor but call
+ * SyncPromise.resolve or SyncPromise.reject. Note that we don't need a
+ * constructor like SyncPromise(function(resolve, reject)) because this would be
+ * for scheduling the function to be called later, which we don't do.
+ * @param {any} value If isRejected is false, this is the value of the fulfilled
+ * promise, else if isRejected is true, this is the error.
+ * @param {boolean} isRejected True to create a promise in the rejected state,
+ * where value is the error.
+ * @constructor
+ */
+var SyncPromise = function SyncPromise(value, isRejected)
+{
+ this.value = value;
+ this.isRejected = isRejected;
+};
+
+exports.SyncPromise = SyncPromise;
+
+/**
+ * If this promise is fulfilled, immediately call onFulfilled with the fulfilled
+ * value as described below. Otherwise, if this promise is rejected, immediately
+ * call onRejected with the error as described below.
+ * @param {function} (optional) onFulfilled If this promise is fulfilled, this
+ * calls onFulfilled(value) with the value of this promise and returns the
+ * result. The function should return a promise. To use all synchronous code,
+ * onFulfilled should return SyncPromise.resolve(newValue).
+ * @param {function} (optional) onRejected If this promise is rejected, this
+ * calls onRejected(err) with the error value of this promise and returns the
+ * result. The function should return a promise. To use all synchronous code,
+ * onFulfilled should return SyncPromise.resolve(newValue) (or throw an
+ * exception).
+ * @return {Promise|SyncPromise} If this promise is fulfilled, return the result
+ * of calling onFulfilled(value). Note that this does not create a promise which
+ * is scheduled to execute later. Rather it immediately calls onFulfilled which
+ * should return a promise. But if onFulfilled is undefined, simply return this
+ * promise to pass it forward. If this promise is rejected, return the result of
+ * calling onRejected(err) with the error value. But if onRejected is undefined,
+ * simply return this promise to pass it forward. However, if onFulfilled or
+ * onRejected throws an exception, then return a new SyncPromise in the rejected
+ * state with the exception.
+ */
+SyncPromise.prototype.then = function(onFulfilled, onRejected)
+{
+ if (this.isRejected) {
+ if (onRejected) {
+ try {
+ return onRejected(this.value);
+ }
+ catch(err) {
+ return new SyncPromise(err, true);
+ }
+ }
+ else
+ // Pass the error forward.
+ return this;
+ }
+ else {
+ if (onFulfilled) {
+ try {
+ return onFulfilled(this.value);
+ }
+ catch(err) {
+ return new SyncPromise(err, true);
+ }
+ }
+ else
+ // Pass the fulfilled value forward.
+ return this;
+ }
+};
+
+/**
+ * Call this.then(undefined, onRejected) and return the result. If this promise
+ * is rejected then onRejected will process it. If this promise is fulfilled,
+ * this simply passes it forward.
+ */
+SyncPromise.prototype.catch = function(onRejected)
+{
+ return this.then(undefined, onRejected);
+};
+
+/**
+ * Return a new SyncPromise which is already fulfilled to the given value.
+ * @param {any} value The value of the promise.
+ */
+SyncPromise.resolve = function(value)
+{
+ return new SyncPromise(value, false);
+};
+
+/**
+ * Return a new SyncPromise which is already rejected with the given error.
+ * @param {any} err The error for the rejected promise.
+ */
+SyncPromise.reject = function(err)
+{
+ return new SyncPromise(err, true);
+};
+
+/**
+ * This static method checks if the promise is a SyncPromise and immediately
+ * returns its value or throws the error if promise is rejected. If promise is
+ * not a SyncPromise, this throws an exception since it is not possible to
+ * immediately get the value. This can be used with "promise-based" code which
+ * you expect to always return a SyncPromise to operate in synchronous mode.
+ * @param {SyncPromise} promise The SyncPromise with the value to get.
+ * @return {any} The value of the promise.
+ * @throws Error If promise is not a SyncPromise.
+ * @throws any If promise is a SyncPromise in the rejected state, this throws
+ * the error.
+ */
+SyncPromise.getValue = function(promise)
+{
+ if (promise instanceof SyncPromise) {
+ if (promise.isRejected)
+ throw promise.value;
+ else
+ return promise.value;
+ }
+ else
+ throw new Error("Cannot return immediately because promise is not a SyncPromise");
+};
+
+/**
+ * This can be called with complete(onComplete, promise) or
+ * complete(onComplete, onError, promise) to handle both synchronous and
+ * asynchronous code based on whether the caller supplies the onComlete callback.
+ * If onComplete is defined, call promise.then with a function which calls
+ * onComplete(value) when fulfilled (possibly in asynchronous mode). If
+ * onComplete is undefined, then we are in synchronous mode so return
+ * SyncPromise.getValue(promise) which will throw an exception if the promise is
+ * not a SyncPromise (or is a SyncPromise in the rejected state).
+ * @param {function} onComplete If defined, this calls promise.then to fulfill
+ * the promise, then calls onComplete(value) with the value of the promise.
+ * If onComplete is undefined, the return value is described below.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an error when this calls promise.then, this calls
+ * onError(err) with the value of the error. If onComplete is undefined, then
+ * onError is ignored and this will call SyncPromise.getValue(promise) which may
+ * throw an exception.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {Promise|SyncPromise} promise If onComplete is defined, this calls
+ * promise.then. Otherwise, this calls SyncPromise.getValue(promise).
+ * @return {any} If onComplete is undefined, return SyncPromise.getValue(promise).
+ * Otherwise, if onComplete is supplied then return undefined and use
+ * onComplete as described above.
+ * @throws Error If onComplete is undefined and promise is not a SyncPromise.
+ * @throws any If onComplete is undefined and promise is a SyncPromise in the
+ * rejected state.
+ */
+SyncPromise.complete = function(onComplete, onErrorOrPromise, promise)
+{
+ var onError;
+ if (promise)
+ onError = onErrorOrPromise;
+ else {
+ promise = onErrorOrPromise;
+ onError = null;
+ }
+
+ if (onComplete)
+ promise
+ .then(function(value) {
+ try {
+ onComplete(value);
+ } catch (ex) {
+ console.log("Error in onComplete: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }, function(err) {
+ if (onError) {
+ try {
+ onError(err);
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ else {
+ if (promise instanceof SyncPromise)
+ throw err;
+ else
+ // We are in an async promise callback, so a thrown exception won't
+ // reach the caller. Just log it.
+ console.log("Uncaught exception from a Promise: " +
+ NdnCommon.getErrorWithStackTrace(err));
+ }
+ });
+ else
+ return SyncPromise.getValue(promise);
+};
+/**
+ * This class contains utilities to help parse the data
+ *
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Meki Cheraoui
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * A DataUtils has static methods for converting data.
+ * @constructor
+ */
+var DataUtils = {};
+
+exports.DataUtils = DataUtils;
+
+/*
+ * NOTE THIS IS CURRENTLY NOT BEING USED
+ *
+ */
+
+DataUtils.keyStr = "ABCDEFGHIJKLMNOP" +
+ "QRSTUVWXYZabcdef" +
+ "ghijklmnopqrstuv" +
+ "wxyz0123456789+/" +
+ "=";
+
+/**
+ * Raw String to Base 64
+ */
+DataUtils.stringtoBase64 = function stringtoBase64(input)
+{
+ //input = escape(input);
+ var output = "";
+ var chr1, chr2, chr3 = "";
+ var enc1, enc2, enc3, enc4 = "";
+ var i = 0;
+
+ do {
+ chr1 = input.charCodeAt(i++);
+ chr2 = input.charCodeAt(i++);
+ chr3 = input.charCodeAt(i++);
+
+ enc1 = chr1 >> 2;
+ enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+ enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+ enc4 = chr3 & 63;
+
+ if (isNaN(chr2))
+ enc3 = enc4 = 64;
+ else if (isNaN(chr3))
+ enc4 = 64;
+
+ output = output +
+ DataUtils.keyStr.charAt(enc1) +
+ DataUtils.keyStr.charAt(enc2) +
+ DataUtils.keyStr.charAt(enc3) +
+ DataUtils.keyStr.charAt(enc4);
+ chr1 = chr2 = chr3 = "";
+ enc1 = enc2 = enc3 = enc4 = "";
+ } while (i < input.length);
+
+ return output;
+};
+
+/**
+ * Base 64 to Raw String
+ */
+DataUtils.base64toString = function base64toString(input)
+{
+ var output = "";
+ var chr1, chr2, chr3 = "";
+ var enc1, enc2, enc3, enc4 = "";
+ var i = 0;
+
+ // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
+ var base64test = /[^A-Za-z0-9\+\/\=]/g;
+ /* Test for invalid characters. */
+ if (base64test.exec(input)) {
+ alert("There were invalid base64 characters in the input text.\n" +
+ "Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" +
+ "Expect errors in decoding.");
+ }
+
+ input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+ do {
+ enc1 = DataUtils.keyStr.indexOf(input.charAt(i++));
+ enc2 = DataUtils.keyStr.indexOf(input.charAt(i++));
+ enc3 = DataUtils.keyStr.indexOf(input.charAt(i++));
+ enc4 = DataUtils.keyStr.indexOf(input.charAt(i++));
+
+ chr1 = (enc1 << 2) | (enc2 >> 4);
+ chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+ chr3 = ((enc3 & 3) << 6) | enc4;
+
+ output = output + String.fromCharCode(chr1);
+
+ if (enc3 != 64)
+ output = output + String.fromCharCode(chr2);
+
+ if (enc4 != 64)
+ output = output + String.fromCharCode(chr3);
+
+ chr1 = chr2 = chr3 = "";
+ enc1 = enc2 = enc3 = enc4 = "";
+ } while (i < input.length);
+
+ return output;
+};
+
+/**
+ * Buffer to Hex String
+ */
+DataUtils.toHex = function(buffer)
+{
+ return buffer.toString('hex');
+};
+
+/**
+ * Raw string to hex string.
+ */
+DataUtils.stringToHex = function(args)
+{
+ var ret = "";
+ for (var i = 0; i < args.length; ++i) {
+ var value = args.charCodeAt(i);
+ ret += (value < 16 ? "0" : "") + value.toString(16);
+ }
+ return ret;
+};
+
+/**
+ * Buffer to raw string.
+ */
+DataUtils.toString = function(buffer)
+{
+ return buffer.toString('binary');
+};
+
+/**
+ * Hex String to Buffer.
+ */
+DataUtils.toNumbers = function(str)
+{
+ return new Buffer(str, 'hex');
+};
+
+/**
+ * Hex String to raw string.
+ */
+DataUtils.hexToRawString = function(str)
+{
+ if (typeof str =='string') {
+ var ret = "";
+ str.replace(/(..)/g, function(s) {
+ ret += String.fromCharCode(parseInt(s, 16));
+ });
+ return ret;
+ }
+};
+
+/**
+ * Raw String to Buffer.
+ */
+DataUtils.toNumbersFromString = function(str)
+{
+ return new Buffer(str, 'binary');
+};
+
+/**
+ * If value is a string, then interpret it as a raw string and convert to
+ * a Buffer. Otherwise assume it is a Buffer or array type and just return it.
+ * @param {string|any} value
+ * @return {Buffer}
+ */
+DataUtils.toNumbersIfString = function(value)
+{
+ if (typeof value === 'string')
+ return new Buffer(value, 'binary');
+ else
+ return value;
+};
+
+/**
+ * Encode str as utf8 and return as Buffer.
+ */
+DataUtils.stringToUtf8Array = function(str)
+{
+ return new Buffer(str, 'utf8');
+};
+
+/**
+ * arrays is an array of Buffer. Return a new Buffer which is the concatenation of all.
+ */
+DataUtils.concatArrays = function(arrays)
+{
+ return Buffer.concat(arrays);
+};
+
+// TODO: Take Buffer and use TextDecoder when available.
+DataUtils.decodeUtf8 = function(utftext)
+{
+ var string = "";
+ var i = 0;
+ var c = 0;
+ var c1 = 0;
+ var c2 = 0;
+
+ while (i < utftext.length) {
+ c = utftext.charCodeAt(i);
+
+ if (c < 128) {
+ string += String.fromCharCode(c);
+ i++;
+ }
+ else if (c > 191 && c < 224) {
+ c2 = utftext.charCodeAt(i + 1);
+ string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
+ i += 2;
+ }
+ else {
+ c2 = utftext.charCodeAt(i+1);
+ var c3 = utftext.charCodeAt(i+2);
+ string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+ i += 3;
+ }
+ }
+
+ return string;
+};
+
+/**
+ * Return true if a1 and a2 are the same length with equal elements.
+ */
+DataUtils.arraysEqual = function(a1, a2)
+{
+ // A simple sanity check that it is an array.
+ if (!a1.slice)
+ throw new Error("DataUtils.arraysEqual: a1 is not an array");
+ if (!a2.slice)
+ throw new Error("DataUtils.arraysEqual: a2 is not an array");
+
+ if (a1.length != a2.length)
+ return false;
+
+ for (var i = 0; i < a1.length; ++i) {
+ if (a1[i] != a2[i])
+ return false;
+ }
+
+ return true;
+};
+
+/**
+ * Convert the big endian Buffer to an unsigned int.
+ * Don't check for overflow.
+ */
+DataUtils.bigEndianToUnsignedInt = function(bytes)
+{
+ var result = 0;
+ for (var i = 0; i < bytes.length; ++i) {
+ // Multiply by 0x100 instead of shift by 8 because << is restricted to 32 bits.
+ result *= 0x100;
+ result += bytes[i];
+ }
+ return result;
+};
+
+/**
+ * Convert the int value to a new big endian Buffer and return.
+ * If value is 0 or negative, return new Buffer(0).
+ */
+DataUtils.nonNegativeIntToBigEndian = function(value)
+{
+ value = Math.round(value);
+ if (value <= 0)
+ return new Buffer(0);
+
+ // Assume value is not over 64 bits.
+ var size = 8;
+ var result = new Buffer(size);
+ var i = 0;
+ while (value != 0) {
+ ++i;
+ result[size - i] = value & 0xff;
+ // Divide by 0x100 and floor instead of shift by 8 because >> is restricted to 32 bits.
+ value = Math.floor(value / 0x100);
+ }
+ return result.slice(size - i, size);
+};
+
+/**
+ * Modify array to randomly shuffle the elements.
+ */
+DataUtils.shuffle = function(array)
+{
+ for (var i = array.length - 1; i >= 1; --i) {
+ // j is from 0 to i.
+ var j = Math.floor(Math.random() * (i + 1));
+ var temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+ }
+};
+
+/**
+ * Decode the base64-encoded private key PEM and return the binary DER.
+ * @param {string} The PEM-encoded private key.
+ * @return {Buffer} The binary DER.
+ *
+ */
+DataUtils.privateKeyPemToDer = function(privateKeyPem)
+{
+ // Remove the '-----XXX-----' from the beginning and the end of the key and
+ // also remove any \n in the key string.
+ var lines = privateKeyPem.split('\n');
+ var privateKey = "";
+ for (var i = 1; i < lines.length - 1; i++)
+ privateKey += lines[i];
+
+ return new Buffer(privateKey, 'base64');
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * Create a new DecodingException wrapping the given error object.
+ * Call with: throw new DecodingException(new Error("message")).
+ * @constructor
+ * @param {Error} error The exception created with new Error.
+ */
+function DecodingException(error)
+{
+ if (error) {
+ error.__proto__ = DecodingException.prototype;
+ return error;
+ }
+}
+DecodingException.prototype = new Error();
+DecodingException.prototype.name = "DecodingException";
+
+exports.DecodingException = DecodingException;
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * The Tlv class has static type codes for the NDN-TLV wire format.
+ * @constructor
+ */
+var Tlv = function Tlv()
+{
+};
+
+exports.Tlv = Tlv;
+
+Tlv.Interest = 5;
+Tlv.Data = 6;
+Tlv.Name = 7;
+Tlv.ImplicitSha256DigestComponent = 1;
+Tlv.NameComponent = 8;
+Tlv.Selectors = 9;
+Tlv.Nonce = 10;
+// <Unassigned> = 11;
+Tlv.InterestLifetime = 12;
+Tlv.MinSuffixComponents = 13;
+Tlv.MaxSuffixComponents = 14;
+Tlv.PublisherPublicKeyLocator = 15;
+Tlv.Exclude = 16;
+Tlv.ChildSelector = 17;
+Tlv.MustBeFresh = 18;
+Tlv.Any = 19;
+Tlv.MetaInfo = 20;
+Tlv.Content = 21;
+Tlv.SignatureInfo = 22;
+Tlv.SignatureValue = 23;
+Tlv.ContentType = 24;
+Tlv.FreshnessPeriod = 25;
+Tlv.FinalBlockId = 26;
+Tlv.SignatureType = 27;
+Tlv.KeyLocator = 28;
+Tlv.KeyLocatorDigest = 29;
+Tlv.SelectedDelegation = 32;
+Tlv.FaceInstance = 128;
+Tlv.ForwardingEntry = 129;
+Tlv.StatusResponse = 130;
+Tlv.Action = 131;
+Tlv.FaceID = 132;
+Tlv.IPProto = 133;
+Tlv.Host = 134;
+Tlv.Port = 135;
+Tlv.MulticastInterface = 136;
+Tlv.MulticastTTL = 137;
+Tlv.ForwardingFlags = 138;
+Tlv.StatusCode = 139;
+Tlv.StatusText = 140;
+
+Tlv.SignatureType_DigestSha256 = 0;
+Tlv.SignatureType_SignatureSha256WithRsa = 1;
+Tlv.SignatureType_SignatureSha256WithEcdsa = 3;
+Tlv.SignatureType_SignatureHmacWithSha256 = 4;
+
+Tlv.ContentType_Default = 0;
+Tlv.ContentType_Link = 1;
+Tlv.ContentType_Key = 2;
+
+Tlv.NfdCommand_ControlResponse = 101;
+Tlv.NfdCommand_StatusCode = 102;
+Tlv.NfdCommand_StatusText = 103;
+
+Tlv.ControlParameters_ControlParameters = 104;
+Tlv.ControlParameters_FaceId = 105;
+Tlv.ControlParameters_Uri = 114;
+Tlv.ControlParameters_LocalControlFeature = 110;
+Tlv.ControlParameters_Origin = 111;
+Tlv.ControlParameters_Cost = 106;
+Tlv.ControlParameters_Flags = 108;
+Tlv.ControlParameters_Strategy = 107;
+Tlv.ControlParameters_ExpirationPeriod = 109;
+
+Tlv.LpPacket_LpPacket = 100;
+Tlv.LpPacket_Fragment = 80;
+Tlv.LpPacket_Sequence = 81;
+Tlv.LpPacket_FragIndex = 82;
+Tlv.LpPacket_FragCount = 83;
+Tlv.LpPacket_Nack = 800;
+Tlv.LpPacket_NackReason = 801;
+Tlv.LpPacket_NextHopFaceId = 816;
+Tlv.LpPacket_IncomingFaceId = 817;
+Tlv.LpPacket_CachePolicy = 820;
+Tlv.LpPacket_CachePolicyType = 821;
+Tlv.LpPacket_IGNORE_MIN = 800;
+Tlv.LpPacket_IGNORE_MAX = 959;
+
+Tlv.Link_Preference = 30;
+Tlv.Link_Delegation = 31;
+
+Tlv.Encrypt_EncryptedContent = 130;
+Tlv.Encrypt_EncryptionAlgorithm = 131;
+Tlv.Encrypt_EncryptedPayload = 132;
+Tlv.Encrypt_InitialVector = 133;
+
+// For RepetitiveInterval.
+Tlv.Encrypt_StartDate = 134;
+Tlv.Encrypt_EndDate = 135;
+Tlv.Encrypt_IntervalStartHour = 136;
+Tlv.Encrypt_IntervalEndHour = 137;
+Tlv.Encrypt_NRepeats = 138;
+Tlv.Encrypt_RepeatUnit = 139;
+Tlv.Encrypt_RepetitiveInterval = 140;
+
+// For Schedule.
+Tlv.Encrypt_WhiteIntervalList = 141;
+Tlv.Encrypt_BlackIntervalList = 142;
+Tlv.Encrypt_Schedule = 143;
+
+/**
+ * Strip off the lower 32 bits of x and divide by 2^32, returning the "high
+ * bytes" above 32 bits. This is necessary because JavaScript << and >> are
+ * restricted to 32 bits.
+ * (This could be a general function, but we define it here so that the
+ * Tlv encoder/decoder is self-contained.)
+ * @param {number} x
+ * @return {number}
+ */
+Tlv.getHighBytes = function(x)
+{
+ // Don't use floor because we expect the caller to use & and >> on the result
+ // which already strip off the fraction.
+ return (x - (x % 0x100000000)) / 0x100000000;
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var DynamicBuffer = require('../../util/dynamic-buffer.js').DynamicBuffer; /** @ignore */
+var Tlv = require('./tlv.js').Tlv;
+
+/**
+ * Create a new TlvEncoder with an initialCapacity for the encoding buffer.
+ * @constructor
+ * @param {number} initialCapacity (optional) The initial capacity of the
+ * encoding buffer. If omitted, use a default value.
+ */
+var TlvEncoder = function TlvEncoder(initialCapacity)
+{
+ initialCapacity = initialCapacity || 16;
+ this.output = new DynamicBuffer(initialCapacity);
+ // length is the number of bytes that have been written to the back of
+ // this.output.array.
+ this.length = 0;
+};
+
+exports.TlvEncoder = TlvEncoder;
+
+/**
+ * Get the number of bytes that have been written to the output. You can
+ * save this number, write sub TLVs, then subtract the new length from this
+ * to get the total length of the sub TLVs.
+ * @return {number} The number of bytes that have been written to the output.
+ */
+TlvEncoder.prototype.getLength = function()
+{
+ return this.length;
+};
+
+/**
+ * Encode varNumber as a VAR-NUMBER in NDN-TLV and write it to this.output just
+ * before this.length from the back. Advance this.length.
+ * @param {number} varNumber The non-negative number to encode.
+ */
+TlvEncoder.prototype.writeVarNumber = function(varNumber)
+{
+ if (varNumber < 253) {
+ this.length += 1;
+ this.output.ensureLengthFromBack(this.length);
+ this.output.array[this.output.array.length - this.length] = varNumber & 0xff;
+ }
+ else if (varNumber <= 0xffff) {
+ this.length += 3;
+ this.output.ensureLengthFromBack(this.length);
+ var offset = this.output.array.length - this.length;
+ this.output.array[offset] = 253;
+ this.output.array[offset + 1] = (varNumber >> 8) & 0xff;
+ this.output.array[offset + 2] = varNumber & 0xff;
+ }
+ else if (varNumber <= 0xffffffff) {
+ this.length += 5;
+ this.output.ensureLengthFromBack(this.length);
+ var offset = this.output.array.length - this.length;
+ this.output.array[offset] = 254;
+ this.output.array[offset + 1] = (varNumber >> 24) & 0xff;
+ this.output.array[offset + 2] = (varNumber >> 16) & 0xff;
+ this.output.array[offset + 3] = (varNumber >> 8) & 0xff;
+ this.output.array[offset + 4] = varNumber & 0xff;
+ }
+ else {
+ this.length += 9;
+ this.output.ensureLengthFromBack(this.length);
+ var offset = this.output.array.length - this.length;
+ this.output.array[offset] = 255;
+ var highBytes = Tlv.getHighBytes(varNumber);
+ this.output.array[offset + 1] = (highBytes >> 24) & 0xff;
+ this.output.array[offset + 2] = (highBytes >> 16) & 0xff;
+ this.output.array[offset + 3] = (highBytes >> 8) & 0xff;
+ this.output.array[offset + 4] = (highBytes) & 0xff;
+ this.output.array[offset + 5] = (varNumber >> 24) & 0xff;
+ this.output.array[offset + 6] = (varNumber >> 16) & 0xff;
+ this.output.array[offset + 7] = (varNumber >> 8) & 0xff;
+ this.output.array[offset + 8] = varNumber & 0xff;
+ }
+};
+
+/**
+ * Encode the type and length as VAR-NUMBER and write to this.output just before
+ * this.length from the back. Advance this.length.
+ * @param {number} type The type of the TLV.
+ * @param {number} length The non-negative length of the TLV.
+ */
+TlvEncoder.prototype.writeTypeAndLength = function(type, length)
+{
+ // Write backwards.
+ this.writeVarNumber(length);
+ this.writeVarNumber(type);
+};
+
+/**
+ * Write value as a non-negative integer and write it to this.output just before
+ * this.length from the back. Advance this.length.
+ * @param {number} value The non-negative integer to encode.
+ */
+TlvEncoder.prototype.writeNonNegativeInteger = function(value)
+{
+ if (value < 0)
+ throw new Error("TLV integer value may not be negative");
+
+ // JavaScript doesn't distinguish int from float, so round.
+ value = Math.round(value);
+
+ if (value <= 0xff) {
+ this.length += 1;
+ this.output.ensureLengthFromBack(this.length);
+ this.output.array[this.output.array.length - this.length] = value & 0xff;
+ }
+ else if (value <= 0xffff) {
+ this.length += 2;
+ this.output.ensureLengthFromBack(this.length);
+ var offset = this.output.array.length - this.length;
+ this.output.array[offset] = (value >> 8) & 0xff;
+ this.output.array[offset + 1] = value & 0xff;
+ }
+ else if (value <= 0xffffffff) {
+ this.length += 4;
+ this.output.ensureLengthFromBack(this.length);
+ var offset = this.output.array.length - this.length;
+ this.output.array[offset] = (value >> 24) & 0xff;
+ this.output.array[offset + 1] = (value >> 16) & 0xff;
+ this.output.array[offset + 2] = (value >> 8) & 0xff;
+ this.output.array[offset + 3] = value & 0xff;
+ }
+ else {
+ this.length += 8;
+ this.output.ensureLengthFromBack(this.length);
+ var offset = this.output.array.length - this.length;
+ var highBytes = Tlv.getHighBytes(value);
+ this.output.array[offset] = (highBytes >> 24) & 0xff;
+ this.output.array[offset + 1] = (highBytes >> 16) & 0xff;
+ this.output.array[offset + 2] = (highBytes >> 8) & 0xff;
+ this.output.array[offset + 3] = (highBytes) & 0xff;
+ this.output.array[offset + 4] = (value >> 24) & 0xff;
+ this.output.array[offset + 5] = (value >> 16) & 0xff;
+ this.output.array[offset + 6] = (value >> 8) & 0xff;
+ this.output.array[offset + 7] = value & 0xff;
+ }
+};
+
+/**
+ * Write the type, then the length of the encoded value then encode value as a
+ * non-negative integer and write it to this.output just before this.length from
+ * the back. Advance this.length.
+ * @param {number} type The type of the TLV.
+ * @param {number} value The non-negative integer to encode.
+ */
+TlvEncoder.prototype.writeNonNegativeIntegerTlv = function(type, value)
+{
+ // Write backwards.
+ var saveNBytes = this.length;
+ this.writeNonNegativeInteger(value);
+ this.writeTypeAndLength(type, this.length - saveNBytes);
+};
+
+/**
+ * If value is negative or null then do nothing, otherwise call
+ * writeNonNegativeIntegerTlv.
+ * @param {number} type The type of the TLV.
+ * @param {number} value If negative or null do nothing, otherwise the integer
+ * to encode.
+ */
+TlvEncoder.prototype.writeOptionalNonNegativeIntegerTlv = function(type, value)
+{
+ if (value != null && value >= 0)
+ this.writeNonNegativeIntegerTlv(type, value);
+};
+
+/**
+ * Write the buffer value to this.output just before this.length from the back.
+ * Advance this.length.
+ * @param {Buffer} buffer The byte array with the bytes to write. If value is
+ * null, then do nothing.
+ */
+TlvEncoder.prototype.writeBuffer = function(buffer)
+{
+ if (buffer == null)
+ return;
+
+ this.length += buffer.length;
+ this.output.copyFromBack(buffer, this.length);
+};
+
+/**
+ * Write the type, then the length of the buffer then the buffer value to
+ * this.output just before this.length from the back. Advance this.length.
+ * @param {number} type The type of the TLV.
+ * @param {Buffer} value The byte array with the bytes of the blob. If value is
+ null, then just write the type and length 0.
+ */
+TlvEncoder.prototype.writeBlobTlv = function(type, value)
+{
+ if (value == null) {
+ this.writeTypeAndLength(type, 0);
+ return;
+ }
+
+ // Write backwards, starting with the blob array.
+ this.writeBuffer(value);
+ this.writeTypeAndLength(type, value.length);
+};
+
+/**
+ * If the byte array is null or zero length then do nothing, otherwise call
+ * writeBlobTlv.
+ * @param {number} type The type of the TLV.
+ * @param {Buffer} value If null or zero length do nothing, otherwise the byte
+ * array with the bytes of the blob.
+ */
+TlvEncoder.prototype.writeOptionalBlobTlv = function(type, value)
+{
+ if (value != null && value.length > 0)
+ this.writeBlobTlv(type, value);
+};
+
+/**
+ * Get a slice of the encoded bytes.
+ * @return {Buffer} A slice backed by the encoding Buffer.
+ */
+TlvEncoder.prototype.getOutput = function()
+{
+ return this.output.array.slice(this.output.array.length - this.length);
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var DecodingException = require('../decoding-exception.js').DecodingException;
+
+/**
+ * Create a new TlvDecoder for decoding the input in the NDN-TLV wire format.
+ * @constructor
+ * @param {Buffer} input The buffer with the bytes to decode.
+ */
+var TlvDecoder = function TlvDecoder(input)
+{
+ this.input = input;
+ this.offset = 0;
+};
+
+exports.TlvDecoder = TlvDecoder;
+
+/**
+ * Decode VAR-NUMBER in NDN-TLV and return it. Update offset.
+ * @return {number} The decoded VAR-NUMBER.
+ */
+TlvDecoder.prototype.readVarNumber = function()
+{
+ // Assume array values are in the range 0 to 255.
+ var firstOctet = this.input[this.offset];
+ this.offset += 1;
+ if (firstOctet < 253)
+ return firstOctet;
+ else
+ return this.readExtendedVarNumber(firstOctet);
+};
+
+/**
+ * A private function to do the work of readVarNumber, given the firstOctet
+ * which is >= 253.
+ * @param {number} firstOctet The first octet which is >= 253, used to decode
+ * the remaining bytes.
+ * @return {number} The decoded VAR-NUMBER.
+ */
+TlvDecoder.prototype.readExtendedVarNumber = function(firstOctet)
+{
+ var result;
+ // This is a private function so we know firstOctet >= 253.
+ if (firstOctet == 253) {
+ result = ((this.input[this.offset] << 8) +
+ this.input[this.offset + 1]);
+ this.offset += 2;
+ }
+ else if (firstOctet == 254) {
+ // Use abs because << 24 can set the high bit of the 32-bit int making it negative.
+ result = (Math.abs(this.input[this.offset] << 24) +
+ (this.input[this.offset + 1] << 16) +
+ (this.input[this.offset + 2] << 8) +
+ this.input[this.offset + 3]);
+ this.offset += 4;
+ }
+ else {
+ // Get the high byte first because JavaScript << is restricted to 32 bits.
+ // Use abs because << 24 can set the high bit of the 32-bit int making it negative.
+ var highByte = Math.abs(this.input[this.offset] << 24) +
+ (this.input[this.offset + 1] << 16) +
+ (this.input[this.offset + 2] << 8) +
+ this.input[this.offset + 3];
+ result = (highByte * 0x100000000 +
+ (this.input[this.offset + 4] << 24) +
+ (this.input[this.offset + 5] << 16) +
+ (this.input[this.offset + 6] << 8) +
+ this.input[this.offset + 7]);
+ this.offset += 8;
+ }
+
+ return result;
+};
+
+/**
+ * Decode the type and length from this's input starting at offset, expecting
+ * the type to be expectedType and return the length. Update offset. Also make
+ * sure the decoded length does not exceed the number of bytes remaining in the
+ * input.
+ * @param {number} expectedType The expected type.
+ * @return {number} The length of the TLV.
+ * @throws DecodingException if (did not get the expected TLV type or the TLV length
+ * exceeds the buffer length.
+ */
+TlvDecoder.prototype.readTypeAndLength = function(expectedType)
+{
+ var type = this.readVarNumber();
+ if (type != expectedType)
+ throw new DecodingException(new Error("Did not get the expected TLV type"));
+
+ var length = this.readVarNumber();
+ if (this.offset + length > this.input.length)
+ throw new DecodingException(new Error("TLV length exceeds the buffer length"));
+
+ return length;
+};
+
+/**
+ * Decode the type and length from the input starting at offset, expecting the
+ * type to be expectedType. Update offset. Also make sure the decoded length
+ * does not exceed the number of bytes remaining in the input. Return the offset
+ * of the end of this parent TLV, which is used in decoding optional nested
+ * TLVs. After reading all nested TLVs, call finishNestedTlvs.
+ * @param {number} expectedType The expected type.
+ * @return {number} The offset of the end of the parent TLV.
+ * @throws DecodingException if did not get the expected TLV type or the TLV
+ * length exceeds the buffer length.
+ */
+TlvDecoder.prototype.readNestedTlvsStart = function(expectedType)
+{
+ return this.readTypeAndLength(expectedType) + this.offset;
+};
+
+/**
+ * Call this after reading all nested TLVs to skip any remaining unrecognized
+ * TLVs and to check if the offset after the final nested TLV matches the
+ * endOffset returned by readNestedTlvsStart.
+ * @param {number} endOffset The offset of the end of the parent TLV, returned
+ * by readNestedTlvsStart.
+ * @throws DecodingException if the TLV length does not equal the total length
+ * of the nested TLVs.
+ */
+TlvDecoder.prototype.finishNestedTlvs = function(endOffset)
+{
+ // We expect offset to be endOffset, so check this first.
+ if (this.offset == endOffset)
+ return;
+
+ // Skip remaining TLVs.
+ while (this.offset < endOffset) {
+ // Skip the type VAR-NUMBER.
+ this.readVarNumber();
+ // Read the length and update offset.
+ var length = this.readVarNumber();
+ this.offset += length;
+
+ if (this.offset > this.input.length)
+ throw new DecodingException(new Error("TLV length exceeds the buffer length"));
+ }
+
+ if (this.offset != endOffset)
+ throw new DecodingException(new Error
+ ("TLV length does not equal the total length of the nested TLVs"));
+};
+
+/**
+ * Decode the type from this's input starting at offset, and if it is the
+ * expectedType, then return true, else false. However, if this's offset is
+ * greater than or equal to endOffset, then return false and don't try to read
+ * the type. Do not update offset.
+ * @param {number} expectedType The expected type.
+ * @param {number} endOffset The offset of the end of the parent TLV, returned
+ * by readNestedTlvsStart.
+ * @return {boolean} true if the type of the next TLV is the expectedType,
+ * otherwise false.
+ */
+TlvDecoder.prototype.peekType = function(expectedType, endOffset)
+{
+ if (this.offset >= endOffset)
+ // No more sub TLVs to look at.
+ return false;
+ else {
+ var saveOffset = this.offset;
+ var type = this.readVarNumber();
+ // Restore offset.
+ this.offset = saveOffset;
+
+ return type == expectedType;
+ }
+};
+
+/**
+ * Decode a non-negative integer in NDN-TLV and return it. Update offset by
+ * length.
+ * @param {number} length The number of bytes in the encoded integer.
+ * @return {number} The integer.
+ * @throws DecodingException if length is an invalid length for a TLV
+ * non-negative integer.
+ */
+TlvDecoder.prototype.readNonNegativeInteger = function(length)
+{
+ var result;
+ if (length == 1)
+ result = this.input[this.offset];
+ else if (length == 2)
+ result = ((this.input[this.offset] << 8) +
+ this.input[this.offset + 1]);
+ else if (length == 4)
+ // Use abs because << 24 can set the high bit of the 32-bit int making it negative.
+ result = (Math.abs(this.input[this.offset] << 24) +
+ (this.input[this.offset + 1] << 16) +
+ (this.input[this.offset + 2] << 8) +
+ this.input[this.offset + 3]);
+ else if (length == 8) {
+ // Use abs because << 24 can set the high bit of the 32-bit int making it negative.
+ var highByte = Math.abs(this.input[this.offset] << 24) +
+ (this.input[this.offset + 1] << 16) +
+ (this.input[this.offset + 2] << 8) +
+ this.input[this.offset + 3];
+ result = (highByte * 0x100000000 +
+ Math.abs(this.input[this.offset + 4] << 24) +
+ (this.input[this.offset + 5] << 16) +
+ (this.input[this.offset + 6] << 8) +
+ this.input[this.offset + 7]);
+ }
+ else
+ throw new DecodingException(new Error("Invalid length for a TLV nonNegativeInteger"));
+
+ this.offset += length;
+ return result;
+};
+
+/**
+ * Decode the type and length from this's input starting at offset, expecting
+ * the type to be expectedType. Then decode a non-negative integer in NDN-TLV
+ * and return it. Update offset.
+ * @param {number} expectedType The expected type.
+ * @return {number} The integer.
+ * @throws DecodingException if did not get the expected TLV type or can't
+ * decode the value.
+ */
+TlvDecoder.prototype.readNonNegativeIntegerTlv = function(expectedType)
+{
+ var length = this.readTypeAndLength(expectedType);
+ return this.readNonNegativeInteger(length);
+};
+
+/**
+ * Peek at the next TLV, and if it has the expectedType then call
+ * readNonNegativeIntegerTlv and return the integer. Otherwise, return null.
+ * However, if this's offset is greater than or equal to endOffset, then return
+ * null and don't try to read the type.
+ * @param {number} expectedType The expected type.
+ * @param {number} endOffset The offset of the end of the parent TLV, returned
+ * by readNestedTlvsStart.
+ * @return {number} The integer or null if the next TLV doesn't have the
+ * expected type.
+ */
+TlvDecoder.prototype.readOptionalNonNegativeIntegerTlv = function
+ (expectedType, endOffset)
+{
+ if (this.peekType(expectedType, endOffset))
+ return this.readNonNegativeIntegerTlv(expectedType);
+ else
+ return null;
+};
+
+/**
+ * Decode the type and length from this's input starting at offset, expecting
+ * the type to be expectedType. Then return an array of the bytes in the value.
+ * Update offset.
+ * @param {number} expectedType The expected type.
+ * @return {Buffer} The bytes in the value as a slice on the buffer. This is
+ * not a copy of the bytes in the input buffer. If you need a copy, then you
+ * must make a copy of the return value.
+ * @throws DecodingException if did not get the expected TLV type.
+ */
+TlvDecoder.prototype.readBlobTlv = function(expectedType)
+{
+ var length = this.readTypeAndLength(expectedType);
+ var result = this.input.slice(this.offset, this.offset + length);
+
+ // readTypeAndLength already checked if length exceeds the input buffer.
+ this.offset += length;
+ return result;
+};
+
+/**
+ * Peek at the next TLV, and if it has the expectedType then call readBlobTlv
+ * and return the value. Otherwise, return null. However, if this's offset is
+ * greater than or equal to endOffset, then return null and don't try to read
+ * the type.
+ * @param {number} expectedType The expected type.
+ * @param {number} endOffset The offset of the end of the parent TLV, returned
+ * by readNestedTlvsStart.
+ * @return {Buffer} The bytes in the value as a slice on the buffer or null if
+ * the next TLV doesn't have the expected type. This is not a copy of the bytes
+ * in the input buffer. If you need a copy, then you must make a copy of the
+ * return value.
+ */
+TlvDecoder.prototype.readOptionalBlobTlv = function(expectedType, endOffset)
+{
+ if (this.peekType(expectedType, endOffset))
+ return this.readBlobTlv(expectedType);
+ else
+ return null;
+};
+
+/**
+ * Peek at the next TLV, and if it has the expectedType then read a type and
+ * value, ignoring the value, and return true. Otherwise, return false.
+ * However, if this's offset is greater than or equal to endOffset, then return
+ * false and don't try to read the type.
+ * @param {number} expectedType The expected type.
+ * @param {number} endOffset The offset of the end of the parent TLV, returned
+ * by readNestedTlvsStart.
+ * @return {boolean} true, or else false if the next TLV doesn't have the
+ * expected type.
+ */
+TlvDecoder.prototype.readBooleanTlv = function(expectedType, endOffset)
+{
+ if (this.peekType(expectedType, endOffset)) {
+ var length = this.readTypeAndLength(expectedType);
+ // We expect the length to be 0, but update offset anyway.
+ this.offset += length;
+ return true;
+ }
+ else
+ return false;
+};
+
+/**
+ * Get the offset into the input, used for the next read.
+ * @return {number} The offset.
+ */
+TlvDecoder.prototype.getOffset = function()
+{
+ return this.offset;
+};
+
+/**
+ * Set the offset into the input, used for the next read.
+ * @param {number} offset The new offset.
+ */
+TlvDecoder.prototype.seek = function(offset)
+{
+ this.offset = offset;
+};
+
+/**
+ * Return an array of a slice of the input for the given offset range.
+ * @param {number} beginOffset The offset in the input of the beginning of the
+ * slice.
+ * @param {number} endOffset The offset in the input of the end of the slice.
+ * @return {Buffer} The bytes in the value as a slice on the buffer. This is
+ * not a copy of the bytes in the input buffer. If you need a copy, then you
+ * must make a copy of the return value.
+ */
+TlvDecoder.prototype.getSlice = function(beginOffset, endOffset)
+{
+ return this.input.slice(beginOffset, endOffset);
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var TlvDecoder = require('./tlv-decoder.js').TlvDecoder;
+
+/**
+ * A TlvStructureDecoder finds the end of an NDN-TLV element, even if the
+ * element is supplied in parts.
+ * Create and initialize a TlvStructureDecoder.
+ * @constructor
+ */
+var TlvStructureDecoder = function TlvStructureDecoder()
+{
+ this.gotElementEnd_ = false;
+ this.offset_ = 0;
+ this.state_ = TlvStructureDecoder.READ_TYPE;
+ this.headerLength_ = 0;
+ this.useHeaderBuffer_ = false;
+ // 8 bytes is enough to hold the extended bytes in the length encoding
+ // where it is an 8-byte number.
+ this.headerBuffer_ = new Buffer(8);
+ this.nBytesToRead_ = 0;
+};
+
+exports.TlvStructureDecoder = TlvStructureDecoder;
+
+TlvStructureDecoder.READ_TYPE = 0;
+TlvStructureDecoder.READ_TYPE_BYTES = 1;
+TlvStructureDecoder.READ_LENGTH = 2;
+TlvStructureDecoder.READ_LENGTH_BYTES = 3;
+TlvStructureDecoder.READ_VALUE_BYTES = 4;
+
+/**
+ * Continue scanning input starting from this.offset_ to find the element end.
+ * If the end of the element which started at offset 0 is found, this returns
+ * true and getOffset() is the length of the element. Otherwise, this returns
+ * false which means you should read more into input and call again.
+ * @param {Buffer} input The input buffer. You have to pass in input each time
+ * because the buffer could be reallocated.
+ * @return {boolean} true if found the element end, false if not.
+ */
+TlvStructureDecoder.prototype.findElementEnd = function(input)
+{
+ if (this.gotElementEnd_)
+ // Someone is calling when we already got the end.
+ return true;
+
+ var decoder = new TlvDecoder(input);
+
+ while (true) {
+ if (this.offset_ >= input.length)
+ // All the cases assume we have some input. Return and wait for more.
+ return false;
+
+ if (this.state_ == TlvStructureDecoder.READ_TYPE) {
+ var firstOctet = input[this.offset_];
+ this.offset_ += 1;
+ if (firstOctet < 253)
+ // The value is simple, so we can skip straight to reading the length.
+ this.state_ = TlvStructureDecoder.READ_LENGTH;
+ else {
+ // Set up to skip the type bytes.
+ if (firstOctet == 253)
+ this.nBytesToRead_ = 2;
+ else if (firstOctet == 254)
+ this.nBytesToRead_ = 4;
+ else
+ // value == 255.
+ this.nBytesToRead_ = 8;
+
+ this.state_ = TlvStructureDecoder.READ_TYPE_BYTES;
+ }
+ }
+ else if (this.state_ == TlvStructureDecoder.READ_TYPE_BYTES) {
+ var nRemainingBytes = input.length - this.offset_;
+ if (nRemainingBytes < this.nBytesToRead_) {
+ // Need more.
+ this.offset_ += nRemainingBytes;
+ this.nBytesToRead_ -= nRemainingBytes;
+ return false;
+ }
+
+ // Got the type bytes. Move on to read the length.
+ this.offset_ += this.nBytesToRead_;
+ this.state_ = TlvStructureDecoder.READ_LENGTH;
+ }
+ else if (this.state_ == TlvStructureDecoder.READ_LENGTH) {
+ var firstOctet = input[this.offset_];
+ this.offset_ += 1;
+ if (firstOctet < 253) {
+ // The value is simple, so we can skip straight to reading
+ // the value bytes.
+ this.nBytesToRead_ = firstOctet;
+ if (this.nBytesToRead_ == 0) {
+ // No value bytes to read. We're finished.
+ this.gotElementEnd_ = true;
+ return true;
+ }
+
+ this.state_ = TlvStructureDecoder.READ_VALUE_BYTES;
+ }
+ else {
+ // We need to read the bytes in the extended encoding of
+ // the length.
+ if (firstOctet == 253)
+ this.nBytesToRead_ = 2;
+ else if (firstOctet == 254)
+ this.nBytesToRead_ = 4;
+ else
+ // value == 255.
+ this.nBytesToRead_ = 8;
+
+ // We need to use firstOctet in the next state.
+ this.firstOctet_ = firstOctet;
+ this.state_ = TlvStructureDecoder.READ_LENGTH_BYTES;
+ }
+ }
+ else if (this.state_ == TlvStructureDecoder.READ_LENGTH_BYTES) {
+ var nRemainingBytes = input.length - this.offset_;
+ if (!this.useHeaderBuffer_ && nRemainingBytes >= this.nBytesToRead_) {
+ // We don't have to use the headerBuffer. Set nBytesToRead.
+ decoder.seek(this.offset_);
+
+ this.nBytesToRead_ = decoder.readExtendedVarNumber(this.firstOctet_);
+ // Update this.offset_ to the decoder's offset after reading.
+ this.offset_ = decoder.getOffset();
+ }
+ else {
+ this.useHeaderBuffer_ = true;
+
+ var nNeededBytes = this.nBytesToRead_ - this.headerLength_;
+ if (nNeededBytes > nRemainingBytes) {
+ // We can't get all of the header bytes from this input.
+ // Save in headerBuffer.
+ if (this.headerLength_ + nRemainingBytes > this.headerBuffer_.length)
+ // We don't expect this to happen.
+ throw new Error
+ ("Cannot store more header bytes than the size of headerBuffer");
+ input.slice(this.offset_, this.offset_ + nRemainingBytes).copy
+ (this.headerBuffer_, this.headerLength_);
+ this.offset_ += nRemainingBytes;
+ this.headerLength_ += nRemainingBytes;
+
+ return false;
+ }
+
+ // Copy the remaining bytes into headerBuffer, read the
+ // length and set nBytesToRead.
+ if (this.headerLength_ + nNeededBytes > this.headerBuffer_.length)
+ // We don't expect this to happen.
+ throw new Error
+ ("Cannot store more header bytes than the size of headerBuffer");
+ input.slice(this.offset_, this.offset_ + nNeededBytes).copy
+ (this.headerBuffer_, this.headerLength_);
+ this.offset_ += nNeededBytes;
+
+ // Use a local decoder just for the headerBuffer.
+ var bufferDecoder = new TlvDecoder(this.headerBuffer_);
+ // Replace nBytesToRead with the length of the value.
+ this.nBytesToRead_ = bufferDecoder.readExtendedVarNumber(this.firstOctet_);
+ }
+
+ if (this.nBytesToRead_ == 0) {
+ // No value bytes to read. We're finished.
+ this.gotElementEnd_ = true;
+ return true;
+ }
+
+ // Get ready to read the value bytes.
+ this.state_ = TlvStructureDecoder.READ_VALUE_BYTES;
+ }
+ else if (this.state_ == TlvStructureDecoder.READ_VALUE_BYTES) {
+ var nRemainingBytes = input.length - this.offset_;
+ if (nRemainingBytes < this.nBytesToRead_) {
+ // Need more.
+ this.offset_ += nRemainingBytes;
+ this.nBytesToRead_ -= nRemainingBytes;
+ return false;
+ }
+
+ // Got the bytes. We're finished.
+ this.offset_ += this.nBytesToRead_;
+ this.gotElementEnd_ = true;
+ return true;
+ }
+ else
+ // We don't expect this to happen.
+ throw new Error("findElementEnd: unrecognized state");
+ }
+};
+
+/**
+ * Get the current offset into the input buffer.
+ * @return {number} The offset.
+ */
+TlvStructureDecoder.prototype.getOffset = function()
+{
+ return this.offset_;
+};
+
+/**
+ * Set the offset into the input, used for the next read.
+ * @param {number} offset The new offset.
+ */
+TlvStructureDecoder.prototype.seek = function(offset)
+{
+ this.offset_ = offset;
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var TlvEncoder = require('./tlv/tlv-encoder.js').TlvEncoder; /** @ignore */
+var TlvDecoder = require('./tlv/tlv-decoder.js').TlvDecoder; /** @ignore */
+var Blob = require('../util/blob.js').Blob; /** @ignore */
+var Name = require('../name.js').Name; /** @ignore */
+
+/**
+ * ProtobufTlv has static methods to encode and decode an Protobuf Message
+ * object as NDN-TLV. The Protobuf tag value is used as the TLV type code. A
+ * Protobuf message is encoded/decoded as a nested TLV encoding. Protobuf types
+ * uint32, uint64 and enum are encoded/decoded as TLV nonNegativeInteger. (It is
+ * an error if an enum value is negative.) Protobuf types bytes and string are
+ * encoded/decoded as TLV bytes. The Protobuf type bool is encoded/decoded as a
+ * TLV boolean (a zero length value for True, omitted for False). The Protobuf
+ * type double is encoded/decoded as an 8-byte little-endian IEEE 754 double.
+ * Other Protobuf types are an error.
+ *
+ * Protobuf has no "outer" message type, so you need to put your TLV message
+ * inside an outer "typeless" message.
+ * @constructor
+ */
+var ProtobufTlv = function ProtobufTlv()
+{
+};
+
+exports.ProtobufTlv = ProtobufTlv;
+
+// Load ProtoBuf.Reflect.Message.Field dynamically so that protobufjs is optional.
+ProtobufTlv._Field = null;
+ProtobufTlv.establishField = function()
+{
+ if (ProtobufTlv._Field === null) {
try {
- var dummy = new Uint8Array(1).subarray(0, 1);
+ // Using protobuf.min.js in the browser.
+ ProtobufTlv._Field = dcodeIO.ProtoBuf.Reflect.Message.Field;
+ }
+ catch (ex) {
+ // Using protobufjs in node.
+ ProtobufTlv._Field = require("protobufjs").Reflect.Message.Field;
+ }
+ }
+}
+
+/**
+ * Encode the Protobuf message object as NDN-TLV. This calls
+ * message.encodeAB() to ensure that all required fields are present and
+ * raises an exception if not. (This does not use the result of toArrayBuffer().)
+ * @param {ProtoBuf.Builder.Message} message The Protobuf message object.
+ * @param {ProtoBuf.Reflect.T} descriptor The reflection descriptor for the
+ * message. For example, if the message is of type "MyNamespace.MyMessage" then
+ * the descriptor is builder.lookup("MyNamespace.MyMessage").
+ * @return {Blob} The encoded buffer in a Blob object.
+ */
+ProtobufTlv.encode = function(message, descriptor)
+{
+ ProtobufTlv.establishField();
+
+ message.encodeAB();
+ var encoder = new TlvEncoder();
+ ProtobufTlv._encodeMessageValue(message, descriptor, encoder);
+ return new Blob(encoder.getOutput(), false);
+};
+
+/**
+ * Decode the input as NDN-TLV and update the fields of the Protobuf message
+ * object.
+ * @param {ProtoBuf.Builder.Message} message The Protobuf message object. This
+ * does not first clear the object.
+ * @param {ProtoBuf.Reflect.T} descriptor The reflection descriptor for the
+ * message. For example, if the message is of type "MyNamespace.MyMessage" then
+ * the descriptor is builder.lookup("MyNamespace.MyMessage").
+ * @param {Blob|Buffer} input The buffer with the bytes to decode.
+ */
+ProtobufTlv.decode = function(message, descriptor, input)
+{
+ ProtobufTlv.establishField();
+
+ // If input is a blob, get its buf().
+ var decodeBuffer = typeof input === 'object' && input instanceof Blob ?
+ input.buf() : input;
+
+ var decoder = new TlvDecoder(decodeBuffer);
+ ProtobufTlv._decodeMessageValue
+ (message, descriptor, decoder, decodeBuffer.length);
+};
+
+ProtobufTlv._encodeMessageValue = function(message, descriptor, encoder)
+{
+ var fields = descriptor.getChildren(ProtobufTlv._Field);
+ // Encode the fields backwards.
+ for (var iField = fields.length - 1; iField >= 0; --iField) {
+ var field = fields[iField];
+ var tlvType = field.id;
+
+ var values;
+ if (field.repeated)
+ values = message[field.name];
+ else {
+ if (message[field.name] != null)
+ // Make a singleton list.
+ values = [message[field.name]];
+ else
+ continue;
+ }
+
+ // Encode the values backwards.
+ for (var iValue = values.length - 1; iValue >= 0; --iValue) {
+ var value = values[iValue];
+
+ if (field.type.name == "message") {
+ var saveLength = encoder.getLength();
+
+ // Encode backwards.
+ ProtobufTlv._encodeMessageValue(value, field.resolvedType, encoder);
+ encoder.writeTypeAndLength(tlvType, encoder.getLength() - saveLength);
+ }
+ else if (field.type.name == "uint32" ||
+ field.type.name == "uint64")
+ encoder.writeNonNegativeIntegerTlv(tlvType, value);
+ else if (field.type.name == "enum") {
+ if (value < 0)
+ throw new Error("ProtobufTlv::encode: ENUM value may not be negative");
+ encoder.writeNonNegativeIntegerTlv(tlvType, value);
+ }
+ else if (field.type.name == "bytes") {
+ var buffer = value.toBuffer();
+ if (buffer.length == undefined)
+ // We are not running in Node.js, so assume we are using the dcodeIO
+ // browser implementation based on ArrayBuffer.
+ buffer = new Uint8Array(value.toArrayBuffer());
+ encoder.writeBlobTlv(tlvType, buffer);
+ }
+ else if (field.type.name == "string")
+ // Use Blob to convert.
+ encoder.writeBlobTlv(tlvType, new Blob(value, false).buf());
+ else if (field.type.name == "bool") {
+ if (value)
+ encoder.writeTypeAndLength(tlvType, 0);
+ }
+ else if (field.type.name == "double") {
+ var encoding = new Buffer(8);
+ encoding.writeDoubleLE(value, 0);
+ encoder.writeBlobTlv(tlvType, encoding);
+ }
+ else
+ throw new Error("ProtobufTlv::encode: Unknown field type");
+ }
+ }
+};
+
+ProtobufTlv._decodeMessageValue = function(message, descriptor, decoder, endOffset)
+{
+ var fields = descriptor.getChildren(ProtobufTlv._Field);
+ for (var iField = 0; iField < fields.length; ++iField) {
+ var field = fields[iField];
+ var tlvType = field.id;
+
+ if (!field.required && !decoder.peekType(tlvType, endOffset))
+ continue;
+
+ if (field.repeated) {
+ while (decoder.peekType(tlvType, endOffset)) {
+ if (field.type.name == "message") {
+ var innerEndOffset = decoder.readNestedTlvsStart(tlvType);
+ var value = new (field.resolvedType.build())();
+ message.add(field.name, value);
+ ProtobufTlv._decodeMessageValue
+ (value, field.resolvedType, decoder, innerEndOffset);
+ decoder.finishNestedTlvs(innerEndOffset);
+ }
+ else
+ message.add
+ (field.name,
+ ProtobufTlv._decodeFieldValue(field, tlvType, decoder, endOffset));
+ }
+ }
+ else {
+ if (field.type.name == "message") {
+ var innerEndOffset = decoder.readNestedTlvsStart(tlvType);
+ var value = new (field.resolvedType.build())();
+ message.set(field.name, value);
+ ProtobufTlv._decodeMessageValue
+ (value, field.resolvedType, decoder, innerEndOffset);
+ decoder.finishNestedTlvs(innerEndOffset);
+ }
+ else
+ message.set
+ (field.name,
+ ProtobufTlv._decodeFieldValue(field, tlvType, decoder, endOffset));
+ }
+ }
+};
+
+/**
+ * This is a helper for _decodeMessageValue. Decode a single field and return
+ * the value. Assume the field.type.name is not "message".
+ */
+ProtobufTlv._decodeFieldValue = function(field, tlvType, decoder, endOffset)
+{
+ if (field.type.name == "uint32" ||
+ field.type.name == "uint64" ||
+ field.type.name == "enum")
+ return decoder.readNonNegativeIntegerTlv(tlvType);
+ else if (field.type.name == "bytes")
+ return decoder.readBlobTlv(tlvType);
+ else if (field.type.name == "string")
+ return decoder.readBlobTlv(tlvType).toString();
+ else if (field.type.name == "bool")
+ return decoder.readBooleanTlv(tlvType, endOffset);
+ else if (field.type.name == "double")
+ return decoder.readBlobTlv(tlvType).readDoubleLE(0);
+ else
+ throw new Error("ProtobufTlv.decode: Unknown field type");
+};
+
+/**
+ * Return a Name made from the component array in a Protobuf message object,
+ * assuming that it was defined with "repeated bytes". For example:
+ * message Name {
+ * repeated bytes component = 8;
+ * }
+ * @param {Array} componentArray The array from the Protobuf message object
+ * representing the "repeated bytes" component array.
+ * @return A new Name.
+ */
+ProtobufTlv.toName = function(componentArray)
+{
+ var name = new Name();
+ for (var i = 0; i < componentArray.length; ++i)
+ name.append
+ (new Blob(new Buffer(componentArray[i].toBinary(), "binary")), false);
+
+ return name;
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From code in ndn-cxx by Yingdi Yu <yingdi@cs.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * @constructor
+ */
+var OID = function OID(oid)
+{
+ if (typeof oid === 'string') {
+ var splitString = oid.split(".");
+ this.oid = [];
+ for (var i = 0; i < splitString.length; ++i)
+ this.oid.push(parseInt(splitString[i]));
+ }
+ else
+ // Assume oid is an array of int. Make a copy.
+ this.oid = oid.slice(0, oid.length);
+};
+
+exports.OID = OID;
+
+OID.prototype.getIntegerList = function()
+{
+ return this.oid;
+};
+
+OID.prototype.setIntegerList = function(oid)
+{
+ // Make a copy.
+ this.oid = oid.slice(0, oid.length);
+};
+
+OID.prototype.toString = function()
+{
+ var result = "";
+ for (var i = 0; i < this.oid.length; ++i) {
+ if (i !== 0)
+ result += ".";
+ result += this.oid[i];
+ }
+
+ return result;
+};
+
+OID.prototype.equals = function(other)
+{
+ if (!(other instanceof OID))
+ return false;
+ if (this.oid.length !== other.oid.length)
+ return false;
+
+ for (var i = 0; i < this.oid.length; ++i) {
+ if (this.oid[i] != other.oid[i])
+ return false;
+ }
+ return true;
+};
+/**
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * Create a WireFormat base class where the encode and decode methods throw an error. You should use a derived class like TlvWireFormat.
+ * @constructor
+ */
+var WireFormat = function WireFormat() {
+};
+
+exports.WireFormat = WireFormat;
+
+/**
+ * Encode name and return the encoding. Your derived class should override.
+ * @param {Name} name The Name to encode.
+ * @return {Blob} A Blob containing the encoding.
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.encodeName = function(name)
+{
+ throw new Error("encodeName is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Decode input as a name and set the fields of the Name object.
+ * Your derived class should override.
+ * @param {Name} name The Name object whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.decodeName = function(name, input, copy)
+{
+ throw new Error("decodeName is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Encode interest and return the encoding. Your derived class should override.
+ * @param {Interest} interest The Interest to encode.
+ * @return {object} An associative array with fields
+ * (encoding, signedPortionBeginOffset, signedPortionEndOffset) where encoding
+ * is a Blob containing the encoding, signedPortionBeginOffset is the offset in
+ * the encoding of the beginning of the signed portion, and
+ * signedPortionEndOffset is the offset in the encoding of the end of the signed
+ * portion. The signed portion starts from the first name component and ends
+ * just before the final name component (which is assumed to be a signature for
+ * a signed interest).
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.encodeInterest = function(interest)
+{
+ throw new Error("encodeInterest is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Decode input as an interest and set the fields of the interest object.
+ * Your derived class should override.
+ * @param {Interest} interest The Interest object whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @return {object} An associative array with fields
+ * (signedPortionBeginOffset, signedPortionEndOffset) where
+ * signedPortionBeginOffset is the offset in the encoding of the beginning of
+ * the signed portion, and signedPortionEndOffset is the offset in the encoding
+ * of the end of the signed portion. The signed portion starts from the first
+ * name component and ends just before the final name component (which is
+ * assumed to be a signature for a signed interest).
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.decodeInterest = function(interest, input, copy)
+{
+ throw new Error("decodeInterest is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Encode data and return the encoding and signed offsets. Your derived class
+ * should override.
+ * @param {Data} data The Data object to encode.
+ * @return {object} An associative array with fields
+ * (encoding, signedPortionBeginOffset, signedPortionEndOffset) where encoding
+ * is a Blob containing the encoding, signedPortionBeginOffset is the offset in
+ * the encoding of the beginning of the signed portion, and
+ * signedPortionEndOffset is the offset in the encoding of the end of the
+ * signed portion.
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.encodeData = function(data)
+{
+ throw new Error("encodeData is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Decode input as a data packet, set the fields in the data object, and return
+ * the signed offsets. Your derived class should override.
+ * @param {Data} data The Data object whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @return {object} An associative array with fields
+ * (signedPortionBeginOffset, signedPortionEndOffset) where
+ * signedPortionBeginOffset is the offset in the encoding of the beginning of
+ * the signed portion, and signedPortionEndOffset is the offset in the encoding
+ * of the end of the signed portion.
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.decodeData = function(data, input, copy)
+{
+ throw new Error("decodeData is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Encode controlParameters and return the encoding. Your derived class should
+ * override.
+ * @param {ControlParameters} controlParameters The ControlParameters object to
+ * encode.
+ * @return {Blob} A Blob containing the encoding.
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.encodeControlParameters = function(controlParameters)
+{
+ throw new Error("encodeControlParameters is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Decode input as a controlParameters and set the fields of the
+ * controlParameters object. Your derived class should override.
+ * @param {ControlParameters} controlParameters The ControlParameters object
+ * whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.decodeControlParameters = function
+ (controlParameters, input, copy)
+{
+ throw new Error("decodeControlParameters is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Encode controlResponse and return the encoding. Your derived class should
+ * override.
+ * @param {ControlResponse} controlResponse The ControlResponse object to
+ * encode.
+ * @return {Blob} A Blob containing the encoding.
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.encodeControlResponse = function(controlResponse)
+{
+ throw new Error("encodeControlResponse is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Decode input as a controlResponse and set the fields of the
+ * controlResponse object. Your derived class should override.
+ * @param {ControlResponse} controlResponse The ControlResponse object
+ * whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.decodeControlResponse = function
+ (controlResponse, input, copy)
+{
+ throw new Error("decodeControlResponse is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Encode signature as a SignatureInfo and return the encoding. Your derived
+ * class should override.
+ * @param {Signature} signature An object of a subclass of Signature to encode.
+ * @return {Blob} A Blob containing the encoding.
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.encodeSignatureInfo = function(signature)
+{
+ throw new Error("encodeSignatureInfo is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Decode signatureInfo as a signature info and signatureValue as the related
+ * SignatureValue, and return a new object which is a subclass of Signature.
+ * Your derived class should override.
+ * @param {Buffer} signatureInfo The buffer with the signature info bytes to
+ * decode.
+ * @param {Buffer} signatureValue The buffer with the signature value to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @return {Signature} A new object which is a subclass of Signature.
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.decodeSignatureInfoAndValue = function
+ (signatureInfo, signatureValue, copy)
+{
+ throw new Error("decodeSignatureInfoAndValue is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Encode the signatureValue in the Signature object as a SignatureValue (the
+ * signature bits) and return the encoding. Your derived class should override.
+ * @param {Signature} signature An object of a subclass of Signature with the
+ * signature value to encode.
+ * @return {Blob} A Blob containing the encoding.
+ * @throws Error This always throws an "unimplemented" error. The derived class should override.
+ */
+WireFormat.prototype.encodeSignatureValue = function(signature)
+{
+ throw new Error("encodeSignatureValue is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Decode input as an NDN-TLV LpPacket and set the fields of the lpPacket object.
+ * Your derived class should override.
+ * @param {LpPacket} lpPacket The LpPacket object whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @throws Error This always throws an "unimplemented" error. The derived class
+ * should override.
+ */
+WireFormat.prototype.decodeLpPacket = function(lpPacket, input, copy)
+{
+ throw new Error
+ ("decodeLpPacket is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Encode the DelegationSet and return the encoding. Your derived class
+ * should override.
+ * @param {DelegationSet} delegationSet The DelegationSet object to
+ * encode.
+ * @return {Blob} A Blob containing the encoding.
+ * @throws Error This always throws an "unimplemented" error. The derived class
+ * should override.
+ */
+WireFormat.prototype.encodeDelegationSet = function(delegationSet)
+{
+ throw new Error
+ ("encodeDelegationSet is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Decode input as an DelegationSet and set the fields of the
+ * delegationSet object. Your derived class should override.
+ * @param {DelegationSet} delegationSet The DelegationSet object
+ * whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @throws Error This always throws an "unimplemented" error. The derived class
+ * should override.
+ */
+WireFormat.prototype.decodeDelegationSet = function(delegationSet, input, copy)
+{
+ throw new Error
+ ("decodeDelegationSet is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Encode the EncryptedContent and return the encoding. Your derived class
+ * should override.
+ * @param {EncryptedContent} encryptedContent The EncryptedContent object to
+ * encode.
+ * @return {Blob} A Blob containing the encoding.
+ * @throws Error This always throws an "unimplemented" error. The derived class
+ * should override.
+ */
+WireFormat.prototype.encodeEncryptedContent = function(encryptedContent)
+{
+ throw new Error
+ ("encodeEncryptedContent is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Decode input as an EncryptedContent and set the fields of the
+ * encryptedContent object. Your derived class should override.
+ * @param {EncryptedContent} encryptedContent The EncryptedContent object
+ * whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @throws Error This always throws an "unimplemented" error. The derived class
+ * should override.
+ */
+WireFormat.prototype.decodeEncryptedContent = function
+ (encryptedContent, input, copy)
+{
+ throw new Error
+ ("decodeEncryptedContent is unimplemented in the base WireFormat class. You should use a derived class.");
+};
+
+/**
+ * Set the static default WireFormat used by default encoding and decoding
+ * methods.
+ * @param {WireFormat} wireFormat An object of a subclass of WireFormat.
+ */
+WireFormat.setDefaultWireFormat = function(wireFormat)
+{
+ WireFormat.defaultWireFormat = wireFormat;
+};
+
+/**
+ * Return the default WireFormat used by default encoding and decoding methods
+ * which was set with setDefaultWireFormat.
+ * @return {WireFormat} An object of a subclass of WireFormat.
+ */
+WireFormat.getDefaultWireFormat = function()
+{
+ return WireFormat.defaultWireFormat;
+};
+
+// Invoke TlvWireFormat to set the default format.
+// Since tlv-wire-format.js includes this file, put this at the bottom
+// to avoid problems with cycles of require.
+var TlvWireFormat = require('./tlv-wire-format.js').TlvWireFormat;
+/**
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var DataUtils = require('./data-utils.js').DataUtils; /** @ignore */
+var Tlv = require('./tlv/tlv.js').Tlv; /** @ignore */
+var TlvStructureDecoder = require('./tlv/tlv-structure-decoder.js').TlvStructureDecoder; /** @ignore */
+var DecodingException = require('./decoding-exception.js').DecodingException; /** @ignore */
+var NdnCommon = require('../util/ndn-common.js').NdnCommon; /** @ignore */
+var LOG = require('../log.js').Log.LOG;
+
+/**
+ * A ElementReader lets you call onReceivedData multiple times which uses a
+ * TlvStructureDecoder to detect the end of a TLV element and calls
+ * elementListener.onReceivedElement(element) with the element. This handles
+ * the case where a single call to onReceivedData may contain multiple elements.
+ * @constructor
+ * @param {object} elementListener An object with an onReceivedElement method.
+ */
+var ElementReader = function ElementReader(elementListener)
+{
+ this.elementListener_ = elementListener;
+ this.dataParts_ = [];
+ this.tlvStructureDecoder_ = new TlvStructureDecoder();
+};
+
+exports.ElementReader = ElementReader;
+
+/**
+ * Continue to read data until the end of an element, then call
+ * this.elementListener_.onReceivedElement(element). The buffer passed to
+ * onReceivedElement is only valid during this call. If you need the data
+ * later, you must copy.
+ * @param {Buffer} data The Buffer with the incoming element's bytes.
+ */
+ElementReader.prototype.onReceivedData = function(data)
+{
+ // Process multiple elements in the data.
+ while (true) {
+ var gotElementEnd;
+ var offset;
+
+ try {
+ if (this.dataParts_.length === 0) {
+ // This is the beginning of an element.
+ if (data.length <= 0)
+ // Wait for more data.
+ return;
+ }
+
+ // Scan the input to check if a whole TLV element has been read.
+ this.tlvStructureDecoder_.seek(0);
+ gotElementEnd = this.tlvStructureDecoder_.findElementEnd(data);
+ offset = this.tlvStructureDecoder_.getOffset();
} catch (ex) {
- console.log("NDN not available: Uint8Array not supported. " + ex);
+ // Reset to read a new element on the next call.
+ this.dataParts_ = [];
+ this.tlvStructureDecoder_ = new TlvStructureDecoder();
+
+ throw ex;
+ }
+
+ if (gotElementEnd) {
+ // Got the remainder of an element. Report to the caller.
+ var element;
+ if (this.dataParts_.length === 0)
+ element = data.slice(0, offset);
+ else {
+ this.dataParts_.push(data.slice(0, offset));
+ element = DataUtils.concatArrays(this.dataParts_);
+ this.dataParts_ = [];
+ }
+
+ // Reset to read a new element. Do this before calling onReceivedElement
+ // in case it throws an exception.
+ data = data.slice(offset, data.length);
+ this.tlvStructureDecoder_ = new TlvStructureDecoder();
+
+ this.elementListener_.onReceivedElement(element);
+ if (data.length == 0)
+ // No more data in the packet.
+ return;
+
+ // else loop back to decode.
+ }
+ else {
+ // Save a copy. We will call concatArrays later.
+ var totalLength = data.length;
+ for (var i = 0; i < this.dataParts_.length; ++i)
+ totalLength += this.dataParts_[i].length;
+ if (totalLength > NdnCommon.MAX_NDN_PACKET_SIZE) {
+ // Reset to read a new element on the next call.
+ this.dataParts_ = [];
+ this.tlvStructureDecoder_ = new TlvStructureDecoder();
+
+ throw new DecodingException(new Error
+ ("The incoming packet exceeds the maximum limit Face.getMaxNdnPacketSize()"));
+ }
+
+ this.dataParts_.push(new Buffer(data));
+ if (LOG > 3) console.log('Incomplete packet received. Length ' + data.length + '. Wait for more input.');
+ return;
+ }
+ }
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * Create a new DerDecodingException wrapping the given error object.
+ * Call with: throw new DerDecodingException(new Error("message")).
+ * @constructor
+ * @param {Error} error The exception created with new Error.
+ */
+function DerDecodingException(error)
+{
+ if (error) {
+ error.__proto__ = DerDecodingException.prototype;
+ return error;
+ }
+}
+DerDecodingException.prototype = new Error();
+DerDecodingException.prototype.name = "DerDecodingException";
+
+exports.DerDecodingException = DerDecodingException;
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * Create a new DerEncodingException wrapping the given error object.
+ * Call with: throw new DerEncodingException(new Error("message")).
+ * @constructor
+ * @param {Error} error The exception created with new Error.
+ */
+function DerEncodingException(error)
+{
+ if (error) {
+ error.__proto__ = DerEncodingException.prototype;
+ return error;
+ }
+}
+DerEncodingException.prototype = new Error();
+DerEncodingException.prototype.name = "DerEncodingException";
+
+exports.DerEncodingException = DerEncodingException;
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From PyNDN der.py by Adeola Bannis <thecodemaiden@gmail.com>.
+ * @author: Originally from code in ndn-cxx by Yingdi Yu <yingdi@cs.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * The DerNodeType enum defines the known DER node types.
+ */
+var DerNodeType = function DerNodeType()
+{
+}
+
+exports.DerNodeType = DerNodeType;
+
+DerNodeType.Eoc = 0;
+DerNodeType.Boolean = 1;
+DerNodeType.Integer = 2;
+DerNodeType.BitString = 3;
+DerNodeType.OctetString = 4;
+DerNodeType.Null = 5;
+DerNodeType.ObjectIdentifier = 6;
+DerNodeType.ObjectDescriptor = 7;
+DerNodeType.External = 40;
+DerNodeType.Real = 9;
+DerNodeType.Enumerated = 10;
+DerNodeType.EmbeddedPdv = 43;
+DerNodeType.Utf8String = 12;
+DerNodeType.RelativeOid = 13;
+DerNodeType.Sequence = 48;
+DerNodeType.Set = 49;
+DerNodeType.NumericString = 18;
+DerNodeType.PrintableString = 19;
+DerNodeType.T61String = 20;
+DerNodeType.VideoTexString = 21;
+DerNodeType.Ia5String = 22;
+DerNodeType.UtcTime = 23;
+DerNodeType.GeneralizedTime = 24;
+DerNodeType.GraphicString = 25;
+DerNodeType.VisibleString = 26;
+DerNodeType.GeneralString = 27;
+DerNodeType.UniversalString = 28;
+DerNodeType.CharacterString = 29;
+DerNodeType.BmpString = 30;
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From PyNDN der_node.py by Adeola Bannis <thecodemaiden@gmail.com>.
+ * @author: Originally from code in ndn-cxx by Yingdi Yu <yingdi@cs.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var DynamicBuffer = require('../../util/dynamic-buffer.js').DynamicBuffer; /** @ignore */
+var Blob = require('../../util/blob.js').Blob; /** @ignore */
+var DerDecodingException = require('./der-decoding-exception.js').DerDecodingException; /** @ignore */
+var DerEncodingException = require('./der-encoding-exception.js').DerEncodingException; /** @ignore */
+var DerNodeType = require('./der-node-type.js').DerNodeType;
+
+/**
+ * DerNode implements the DER node types used in encoding/decoding DER-formatted
+ * data.
+ *
+ * Create a generic DER node with the given nodeType. This is a private
+ * constructor used by one of the public DerNode subclasses defined below.
+ * @param {number} nodeType One of the defined DER DerNodeType constants.
+ * @constructor
+ */
+var DerNode = function DerNode(nodeType)
+{
+ this.nodeType_ = nodeType;
+ this.parent_ = null;
+ this.header_ = new Buffer(0);
+ this.payload_ = new DynamicBuffer(0);
+ this.payloadPosition_ = 0;
+};
+
+exports.DerNode = DerNode;
+
+/**
+ * Return the number of bytes in DER
+ * @return {number}
+ */
+DerNode.prototype.getSize = function()
+{
+ return this.header_.length + this.payloadPosition_;
+};
+
+/**
+ * Encode the given size and update the header.
+ * @param {number} size
+ */
+DerNode.prototype.encodeHeader = function(size)
+{
+ var buffer = new DynamicBuffer(10);
+ var bufferPosition = 0;
+ buffer.array[bufferPosition++] = this.nodeType_;
+ if (size < 0)
+ // We don't expect this to happen since this is an internal method and
+ // always called with the non-negative size() of some buffer.
+ throw new Error("encodeHeader: DER object has negative length");
+ else if (size <= 127)
+ buffer.array[bufferPosition++] = size & 0xff;
+ else {
+ var tempBuf = new DynamicBuffer(10);
+ // We encode backwards from the back.
+
+ var val = size;
+ var n = 0;
+ while (val != 0) {
+ ++n;
+ tempBuf.ensureLengthFromBack(n);
+ tempBuf.array[tempBuf.array.length - n] = val & 0xff;
+ val >>= 8;
+ }
+ var nTempBufBytes = n + 1;
+ tempBuf.ensureLengthFromBack(nTempBufBytes);
+ tempBuf.array[tempBuf.array.length - nTempBufBytes] = ((1<<7) | n) & 0xff;
+
+ buffer.copy(tempBuf.slice(tempBuf.array.length - nTempBufBytes), bufferPosition);
+ bufferPosition += nTempBufBytes;
+ }
+
+ this.header_ = buffer.slice(0, bufferPosition);
+};
+
+/**
+ * Extract the header from an input buffer and return the size.
+ * @param {Buffer} inputBuf The input buffer to read from.
+ * @param {number} startIdx The offset into the buffer.
+ * @return {number} The parsed size in the header.
+ */
+DerNode.prototype.decodeHeader = function(inputBuf, startIdx)
+{
+ var idx = startIdx;
+
+ var nodeType = inputBuf[idx] & 0xff;
+ idx += 1;
+
+ this.nodeType_ = nodeType;
+
+ var sizeLen = inputBuf[idx] & 0xff;
+ idx += 1;
+
+ var header = new DynamicBuffer(10);
+ var headerPosition = 0;
+ header.array[headerPosition++] = nodeType;
+ header.array[headerPosition++] = sizeLen;
+
+ var size = sizeLen;
+ var isLongFormat = (sizeLen & (1 << 7)) != 0;
+ if (isLongFormat) {
+ var lenCount = sizeLen & ((1<<7) - 1);
+ size = 0;
+ while (lenCount > 0) {
+ var b = inputBuf[idx];
+ idx += 1;
+ header.ensureLength(headerPosition + 1);
+ header.array[headerPosition++] = b;
+ size = 256 * size + (b & 0xff);
+ lenCount -= 1;
+ }
+ }
+
+ this.header_ = header.slice(0, headerPosition);
+ return size;
+};
+
+/**
+ * Get the raw data encoding for this node.
+ * @return {Blob} The raw data encoding.
+ */
+DerNode.prototype.encode = function()
+{
+ var buffer = new Buffer(this.getSize());
+
+ this.header_.copy(buffer);
+ this.payload_.slice(0, this.payloadPosition_).copy(buffer, this.header_.length);
+
+ return new Blob(buffer, false);
+};
+
+/**
+ * Decode and store the data from an input buffer.
+ * @param {Buffer} inputBuf The input buffer to read from. This reads from
+ * startIdx (regardless of the buffer's position) and does not change the
+ * position.
+ * @param {number} startIdx The offset into the buffer.
+ */
+DerNode.prototype.decode = function(inputBuf, startIdx)
+{
+ var idx = startIdx;
+ var payloadSize = this.decodeHeader(inputBuf, idx);
+ var skipBytes = this.header_.length;
+ if (payloadSize > 0) {
+ idx += skipBytes;
+ this.payloadAppend(inputBuf.slice(idx, idx + payloadSize));
+ }
+};
+
+/**
+ * Copy buffer to this.payload_ at this.payloadPosition_ and update
+ * this.payloadPosition_.
+ * @param {Buffer} buffer The buffer to copy.
+ */
+DerNode.prototype.payloadAppend = function(buffer)
+{
+ this.payloadPosition_ = this.payload_.copy(buffer, this.payloadPosition_);
+}
+
+/**
+ * Parse the data from the input buffer recursively and return the root as an
+ * object of a subclass of DerNode.
+ * @param {Buffer} inputBuf The input buffer to read from.
+ * @param {number} startIdx (optional) The offset into the buffer. If omitted,
+ * use 0.
+ * @return {DerNode} An object of a subclass of DerNode.
+ */
+DerNode.parse = function(inputBuf, startIdx)
+{
+ if (startIdx == undefined)
+ startIdx = 0;
+
+ var nodeType = inputBuf[startIdx] & 0xff;
+ // Don't increment idx. We're just peeking.
+
+ var newNode;
+ if (nodeType === DerNodeType.Boolean)
+ newNode = new DerNode.DerBoolean();
+ else if (nodeType === DerNodeType.Integer)
+ newNode = new DerNode.DerInteger();
+ else if (nodeType === DerNodeType.BitString)
+ newNode = new DerNode.DerBitString();
+ else if (nodeType === DerNodeType.OctetString)
+ newNode = new DerNode.DerOctetString();
+ else if (nodeType === DerNodeType.Null)
+ newNode = new DerNode.DerNull();
+ else if (nodeType === DerNodeType.ObjectIdentifier)
+ newNode = new DerNode.DerOid();
+ else if (nodeType === DerNodeType.Sequence)
+ newNode = new DerNode.DerSequence();
+ else if (nodeType === DerNodeType.PrintableString)
+ newNode = new DerNode.DerPrintableString();
+ else if (nodeType === DerNodeType.GeneralizedTime)
+ newNode = new DerNode.DerGeneralizedTime();
+ else
+ throw new DerDecodingException(new Error("Unimplemented DER type " + nodeType));
+
+ newNode.decode(inputBuf, startIdx);
+ return newNode;
+};
+
+/**
+ * Convert the encoded data to a standard representation. Overridden by some
+ * subclasses (e.g. DerBoolean).
+ * @return {Blob} The encoded data as a Blob.
+ */
+DerNode.prototype.toVal = function()
+{
+ return this.encode();
+};
+
+/**
+ * Get a copy of the payload bytes.
+ * @return {Blob} A copy of the payload.
+ */
+DerNode.prototype.getPayload = function()
+{
+ return new Blob(this.payload_.slice(0, this.payloadPosition_), true);
+};
+
+/**
+ * If this object is a DerNode.DerSequence, get the children of this node.
+ * Otherwise, throw an exception. (DerSequence overrides to implement this
+ * method.)
+ * @return {Array<DerNode>} The children as an array of DerNode.
+ * @throws DerDecodingException if this object is not a DerSequence.
+ */
+DerNode.prototype.getChildren = function()
+{
+ throw new DerDecodingException(new Error
+ ("getChildren: This DerNode is not DerSequence"));
+};
+
+/**
+ * Check that index is in bounds for the children list, return children[index].
+ * @param {Array<DerNode>} children The list of DerNode, usually returned by
+ * another call to getChildren.
+ * @param {number} index The index of the children.
+ * @return {DerNode.DerSequence} children[index].
+ * @throws DerDecodingException if index is out of bounds or if children[index]
+ * is not a DerSequence.
+ */
+DerNode.getSequence = function(children, index)
+{
+ if (index < 0 || index >= children.length)
+ throw new DerDecodingException(new Error
+ ("getSequence: Child index is out of bounds"));
+
+ if (!(children[index] instanceof DerNode.DerSequence))
+ throw new DerDecodingException(new Error
+ ("getSequence: Child DerNode is not a DerSequence"));
+
+ return children[index];
+};
+
+/**
+ * A DerStructure extends DerNode to hold other DerNodes.
+ * Create a DerStructure with the given nodeType. This is a private
+ * constructor. To create an object, use DerSequence.
+ * @param {number} nodeType One of the defined DER DerNodeType constants.
+ */
+DerNode.DerStructure = function DerStructure(nodeType)
+{
+ // Call the base constructor.
+ DerNode.call(this, nodeType);
+
+ this.childChanged_ = false;
+ this.nodeList_ = []; // Of DerNode.
+ this.size_ = 0;
+};
+DerNode.DerStructure.prototype = new DerNode();
+DerNode.DerStructure.prototype.name = "DerStructure";
+
+/**
+ * Get the total length of the encoding, including children.
+ * @return {number} The total (header + payload) length.
+ */
+DerNode.DerStructure.prototype.getSize = function()
+{
+ if (this.childChanged_) {
+ this.updateSize();
+ this.childChanged_ = false;
+ }
+
+ this.encodeHeader(this.size_);
+ return this.size_ + this.header_.length;
+};
+
+/**
+ * Get the children of this node.
+ * @return {Array<DerNode>} The children as an array of DerNode.
+ */
+DerNode.DerStructure.prototype.getChildren = function()
+{
+ return this.nodeList_;
+};
+
+DerNode.DerStructure.prototype.updateSize = function()
+{
+ var newSize = 0;
+
+ for (var i = 0; i < this.nodeList_.length; ++i) {
+ var n = this.nodeList_[i];
+ newSize += n.getSize();
+ }
+
+ this.size_ = newSize;
+ this.childChanged_ = false;
+};
+
+/**
+ * Add a child to this node.
+ * @param {DerNode} node The child node to add.
+ * @param {boolean} (optional) notifyParent Set to true to cause any containing
+ * nodes to update their size. If omitted, use false.
+ */
+DerNode.DerStructure.prototype.addChild = function(node, notifyParent)
+{
+ node.parent_ = this;
+ this.nodeList_.push(node);
+
+ if (notifyParent) {
+ if (this.parent_ != null)
+ this.parent_.setChildChanged();
+ }
+
+ this.childChanged_ = true;
+};
+
+/**
+ * Mark the child list as dirty, so that we update size when necessary.
+ */
+DerNode.DerStructure.prototype.setChildChanged = function()
+{
+ if (this.parent_ != null)
+ this.parent_.setChildChanged();
+ this.childChanged_ = true;
+};
+
+/**
+ * Override the base encode to return raw data encoding for this node and its
+ * children.
+ * @return {Blob} The raw data encoding.
+ */
+DerNode.DerStructure.prototype.encode = function()
+{
+ var buffer = new DynamicBuffer(10);
+ var bufferPosition = 0;
+ this.updateSize();
+ this.encodeHeader(this.size_);
+ bufferPosition = buffer.copy(this.header_, bufferPosition);
+
+ for (var i = 0; i < this.nodeList_.length; ++i) {
+ var n = this.nodeList_[i];
+ var encodedChild = n.encode();
+ bufferPosition = buffer.copy(encodedChild.buf(), bufferPosition);
+ }
+
+ return new Blob(buffer.slice(0, bufferPosition), false);
+};
+
+/**
+ * Override the base decode to decode and store the data from an input
+ * buffer. Recursively populates child nodes.
+ * @param {Buffer} inputBuf The input buffer to read from.
+ * @param {number} startIdx The offset into the buffer.
+ */
+DerNode.DerStructure.prototype.decode = function(inputBuf, startIdx)
+{
+ var idx = startIdx;
+ this.size_ = this.decodeHeader(inputBuf, idx);
+ idx += this.header_.length;
+
+ var accSize = 0;
+ while (accSize < this.size_) {
+ var node = DerNode.parse(inputBuf, idx);
+ var size = node.getSize();
+ idx += size;
+ accSize += size;
+ this.addChild(node, false);
+ }
+};
+
+////////
+// Now for all the node types...
+////////
+
+/**
+ * A DerByteString extends DerNode to handle byte strings.
+ * Create a DerByteString with the given inputData and nodeType. This is a
+ * private constructor used by one of the public subclasses such as
+ * DerOctetString or DerPrintableString.
+ * @param {Buffer} inputData An input buffer containing the string to encode.
+ * @param {number} nodeType One of the defined DER DerNodeType constants.
+ */
+DerNode.DerByteString = function DerByteString(inputData, nodeType)
+{
+ // Call the base constructor.
+ DerNode.call(this, nodeType);
+
+ if (inputData != null) {
+ this.payloadAppend(inputData);
+ this.encodeHeader(inputData.length);
+ }
+};
+DerNode.DerByteString.prototype = new DerNode();
+DerNode.DerByteString.prototype.name = "DerByteString";
+
+/**
+ * Override to return just the byte string.
+ * @return {Blob} The byte string as a copy of the payload buffer.
+ */
+DerNode.DerByteString.prototype.toVal = function()
+{
+ return this.getPayload();
+};
+
+/**
+ * DerBoolean extends DerNode to encode a boolean value.
+ * Create a new DerBoolean for the value.
+ * @param {boolean} value The value to encode.
+ */
+DerNode.DerBoolean = function DerBoolean(value)
+{
+ // Call the base constructor.
+ DerNode.call(this, DerNodeType.Boolean);
+
+ if (value != undefined) {
+ var val = value ? 0xff : 0x00;
+ this.payload_.ensureLength(this.payloadPosition_ + 1);
+ this.payload_.array[this.payloadPosition_++] = val;
+ this.encodeHeader(1);
+ }
+};
+DerNode.DerBoolean.prototype = new DerNode();
+DerNode.DerBoolean.prototype.name = "DerBoolean";
+
+DerNode.DerBoolean.prototype.toVal = function()
+{
+ var val = this.payload_.array[0];
+ return val != 0x00;
+};
+
+/**
+ * DerInteger extends DerNode to encode an integer value.
+ * Create a new DerInteger for the value.
+ * @param {number|Buffer} integer The value to encode. If integer is a Buffer
+ * byte array of a positive integer, you must ensure that the first byte is less
+ * than 0x80.
+ */
+DerNode.DerInteger = function DerInteger(integer)
+{
+ // Call the base constructor.
+ DerNode.call(this, DerNodeType.Integer);
+
+ if (integer != undefined) {
+ if (Buffer.isBuffer(integer)) {
+ if (integer.length > 0 && integer[0] >= 0x80)
+ throw new DerEncodingException(new Error
+ ("DerInteger: Negative integers are not currently supported"));
+
+ if (integer.length == 0)
+ this.payloadAppend(new Buffer([0]));
+ else
+ this.payloadAppend(integer);
+ }
+ else {
+ // JavaScript doesn't distinguish int from float, so round.
+ integer = Math.round(integer);
+
+ if (integer < 0)
+ throw new DerEncodingException(new Error
+ ("DerInteger: Negative integers are not currently supported"));
+
+ // Convert the integer to bytes the easy/slow way.
+ var temp = new DynamicBuffer(10);
+ // We encode backwards from the back.
+ var length = 0;
+ while (true) {
+ ++length;
+ temp.ensureLengthFromBack(length);
+ temp.array[temp.array.length - length] = integer & 0xff;
+ integer >>= 8;
+
+ if (integer <= 0)
+ // We check for 0 at the end so we encode one byte if it is 0.
+ break;
+ }
+
+ if (temp.array[temp.array.length - length] >= 0x80) {
+ // Make it a non-negative integer.
+ ++length;
+ temp.ensureLengthFromBack(length);
+ temp.array[temp.array.length - length] = 0;
+ }
+
+ this.payloadAppend(temp.slice(temp.array.length - length));
+ }
+
+ this.encodeHeader(this.payloadPosition_);
+ }
+};
+DerNode.DerInteger.prototype = new DerNode();
+DerNode.DerInteger.prototype.name = "DerInteger";
+
+DerNode.DerInteger.prototype.toVal = function()
+{
+ if (this.payloadPosition_ > 0 && this.payload_.array[0] >= 0x80)
+ throw new DerDecodingException(new Error
+ ("DerInteger: Negative integers are not currently supported"));
+
+ var result = 0;
+ for (var i = 0; i < this.payloadPosition_; ++i) {
+ result <<= 8;
+ result += this.payload_.array[i];
+ }
+
+ return result;
+};
+
+/**
+ * A DerBitString extends DerNode to handle a bit string.
+ * Create a DerBitString with the given padding and inputBuf.
+ * @param {Buffer} inputBuf An input buffer containing the bit octets to encode.
+ * @param {number} paddingLen The number of bits of padding at the end of the bit
+ * string. Should be less than 8.
+ */
+DerNode.DerBitString = function DerBitString(inputBuf, paddingLen)
+{
+ // Call the base constructor.
+ DerNode.call(this, DerNodeType.BitString);
+
+ if (inputBuf != undefined) {
+ this.payload_.ensureLength(this.payloadPosition_ + 1);
+ this.payload_.array[this.payloadPosition_++] = paddingLen & 0xff;
+ this.payloadAppend(inputBuf);
+ this.encodeHeader(this.payloadPosition_);
+ }
+};
+DerNode.DerBitString.prototype = new DerNode();
+DerNode.DerBitString.prototype.name = "DerBitString";
+
+/**
+ * DerOctetString extends DerByteString to encode a string of bytes.
+ * Create a new DerOctetString for the inputData.
+ * @param {Buffer} inputData An input buffer containing the string to encode.
+ */
+DerNode.DerOctetString = function DerOctetString(inputData)
+{
+ // Call the base constructor.
+ DerNode.DerByteString.call(this, inputData, DerNodeType.OctetString);
+};
+DerNode.DerOctetString.prototype = new DerNode.DerByteString();
+DerNode.DerOctetString.prototype.name = "DerOctetString";
+
+/**
+ * A DerNull extends DerNode to encode a null value.
+ * Create a DerNull.
+ */
+DerNode.DerNull = function DerNull()
+{
+ // Call the base constructor.
+ DerNode.call(this, DerNodeType.Null);
+ this.encodeHeader(0);
+};
+DerNode.DerNull.prototype = new DerNode();
+DerNode.DerNull.prototype.name = "DerNull";
+
+/**
+ * A DerOid extends DerNode to represent an object identifier.
+ * Create a DerOid with the given object identifier. The object identifier
+ * string must begin with 0,1, or 2 and must contain at least 2 digits.
+ * @param {string|OID} oid The OID string or OID object to encode.
+ */
+DerNode.DerOid = function DerOid(oid)
+{
+ // Call the base constructor.
+ DerNode.call(this, DerNodeType.ObjectIdentifier);
+
+ if (oid != undefined) {
+ if (typeof oid === 'string') {
+ var splitString = oid.split(".");
+ var parts = [];
+ for (var i = 0; i < splitString.length; ++i)
+ parts.push(parseInt(splitString[i]));
+
+ this.prepareEncoding(parts);
+ }
+ else
+ // Assume oid is of type OID.
+ this.prepareEncoding(oid.getIntegerList());
+ }
+};
+DerNode.DerOid.prototype = new DerNode();
+DerNode.DerOid.prototype.name = "DerOid";
+
+/**
+ * Encode a sequence of integers into an OID object and set the payload.
+ * @param {Array<number>} value The array of integers.
+ */
+DerNode.DerOid.prototype.prepareEncoding = function(value)
+{
+ var firstNumber;
+ if (value.length == 0)
+ throw new DerEncodingException(new Error("No integer in OID"));
+ else {
+ if (value[0] >= 0 && value[0] <= 2)
+ firstNumber = value[0] * 40;
+ else
+ throw new DerEncodingException(new Error("First integer in OID is out of range"));
+ }
+
+ if (value.length >= 2) {
+ if (value[1] >= 0 && value[1] <= 39)
+ firstNumber += value[1];
+ else
+ throw new DerEncodingException(new Error("Second integer in OID is out of range"));
+ }
+
+ var encodedBuffer = new DynamicBuffer(10);
+ var encodedBufferPosition = 0;
+ encodedBufferPosition = encodedBuffer.copy
+ (DerNode.DerOid.encode128(firstNumber), encodedBufferPosition);
+
+ if (value.length > 2) {
+ for (var i = 2; i < value.length; ++i)
+ encodedBufferPosition = encodedBuffer.copy
+ (DerNode.DerOid.encode128(value[i]), encodedBufferPosition);
+ }
+
+ this.encodeHeader(encodedBufferPosition);
+ this.payloadAppend(encodedBuffer.slice(0, encodedBufferPosition));
+};
+
+/**
+ * Compute the encoding for one part of an OID, where values greater than 128
+ * must be encoded as multiple bytes.
+ * @param {number} value A component of an OID.
+ * @return {Buffer} The encoded buffer.
+ */
+DerNode.DerOid.encode128 = function(value)
+{
+ var mask = (1 << 7) - 1;
+ var outBytes = new DynamicBuffer(10);
+ var outBytesLength = 0;
+ // We encode backwards from the back.
+
+ if (value < 128) {
+ ++outBytesLength;
+ outBytes.array[outBytes.array.length - outBytesLength] = value & mask;
+ }
+ else {
+ ++outBytesLength;
+ outBytes.array[outBytes.array.length - outBytesLength] = value & mask;
+ value >>= 7;
+
+ while (value != 0) {
+ ++outBytesLength;
+ outBytes.ensureLengthFromBack(outBytesLength);
+ outBytes.array[outBytes.array.length - outBytesLength] =
+ (value & mask) | (1 << 7);
+ value >>= 7;
+ }
+ }
+
+ return outBytes.slice(outBytes.array.length - outBytesLength);
+};
+
+/**
+ * Convert an encoded component of the encoded OID to the original integer.
+ * @param {number} offset The offset into this node's payload.
+ * @param {Array<number>} skip Set skip[0] to the number of payload bytes to skip.
+ * @return {number} The original integer.
+ */
+DerNode.DerOid.prototype.decode128 = function(offset, skip)
+{
+ var flagMask = 0x80;
+ var result = 0;
+ var oldOffset = offset;
+
+ while ((this.payload_.array[offset] & flagMask) != 0) {
+ result = 128 * result + (this.payload_.array[offset] & 0xff) - 128;
+ offset += 1;
+ }
+
+ result = result * 128 + (this.payload_.array[offset] & 0xff);
+
+ skip[0] = offset - oldOffset + 1;
+ return result;
+};
+
+/**
+ * Override to return the string representation of the OID.
+ * @return {string} The string representation of the OID.
+ */
+DerNode.DerOid.prototype.toVal = function()
+{
+ var offset = 0;
+ var components = []; // of number.
+
+ while (offset < this.payloadPosition_) {
+ var skip = [0];
+ var nextVal = this.decode128(offset, skip);
+ offset += skip[0];
+ components.push(nextVal);
+ }
+
+ // For some odd reason, the first digits are represented in one byte.
+ var firstByte = components[0];
+ var firstDigit = Math.floor(firstByte / 40);
+ var secondDigit = firstByte % 40;
+
+ var result = firstDigit + "." + secondDigit;
+ for (var i = 1; i < components.length; ++i)
+ result += "." + components[i];
+
+ return result;
+};
+
+/**
+ * A DerSequence extends DerStructure to contains an ordered sequence of other
+ * nodes.
+ * Create a DerSequence.
+ */
+DerNode.DerSequence = function DerSequence()
+{
+ // Call the base constructor.
+ DerNode.DerStructure.call(this, DerNodeType.Sequence);
+};
+DerNode.DerSequence.prototype = new DerNode.DerStructure();
+DerNode.DerSequence.prototype.name = "DerSequence";
+
+/**
+ * A DerPrintableString extends DerByteString to handle a a printable string. No
+ * escaping or other modification is done to the string.
+ * Create a DerPrintableString with the given inputData.
+ * @param {Buffer} inputData An input buffer containing the string to encode.
+ */
+DerNode.DerPrintableString = function DerPrintableString(inputData)
+{
+ // Call the base constructor.
+ DerNode.DerByteString.call(this, inputData, DerNodeType.PrintableString);
+};
+DerNode.DerPrintableString.prototype = new DerNode.DerByteString();
+DerNode.DerPrintableString.prototype.name = "DerPrintableString";
+
+/**
+ * A DerGeneralizedTime extends DerNode to represent a date and time, with
+ * millisecond accuracy.
+ * Create a DerGeneralizedTime with the given milliseconds since 1970.
+ * @param {number} msSince1970 The timestamp as milliseconds since Jan 1, 1970.
+ */
+DerNode.DerGeneralizedTime = function DerGeneralizedTime(msSince1970)
+{
+ // Call the base constructor.
+ DerNode.call(this, DerNodeType.GeneralizedTime);
+
+ if (msSince1970 != undefined) {
+ var derTime = DerNode.DerGeneralizedTime.toDerTimeString(msSince1970);
+ // Use Blob to convert to a Buffer.
+ this.payloadAppend(new Blob(derTime).buf());
+ this.encodeHeader(this.payloadPosition_);
+ }
+};
+DerNode.DerGeneralizedTime.prototype = new DerNode();
+DerNode.DerGeneralizedTime.prototype.name = "DerGeneralizedTime";
+
+/**
+ * Convert a UNIX timestamp to the internal string representation.
+ * @param {type} msSince1970 Timestamp as milliseconds since Jan 1, 1970.
+ * @return {string} The string representation.
+ */
+DerNode.DerGeneralizedTime.toDerTimeString = function(msSince1970)
+{
+ var utcTime = new Date(Math.round(msSince1970));
+ return utcTime.getUTCFullYear() +
+ DerNode.DerGeneralizedTime.to2DigitString(utcTime.getUTCMonth() + 1) +
+ DerNode.DerGeneralizedTime.to2DigitString(utcTime.getUTCDate()) +
+ DerNode.DerGeneralizedTime.to2DigitString(utcTime.getUTCHours()) +
+ DerNode.DerGeneralizedTime.to2DigitString(utcTime.getUTCMinutes()) +
+ DerNode.DerGeneralizedTime.to2DigitString(utcTime.getUTCSeconds()) +
+ "Z";
+};
+
+/**
+ * A private method to zero pad an integer to 2 digits.
+ * @param {number} x The number to pad. Assume it is a non-negative integer.
+ * @return {string} The padded string.
+ */
+DerNode.DerGeneralizedTime.to2DigitString = function(x)
+{
+ var result = x.toString();
+ return result.length === 1 ? "0" + result : result;
+};
+
+/**
+ * Override to return the milliseconds since 1970.
+ * @return {number} The timestamp value as milliseconds since 1970.
+ */
+DerNode.DerGeneralizedTime.prototype.toVal = function()
+{
+ var timeStr = this.payload_.slice(0, this.payloadPosition_).toString();
+ return Date.UTC
+ (parseInt(timeStr.substr(0, 4)),
+ parseInt(timeStr.substr(4, 2) - 1),
+ parseInt(timeStr.substr(6, 2)),
+ parseInt(timeStr.substr(8, 2)),
+ parseInt(timeStr.substr(10, 2)),
+ parseInt(timeStr.substr(12, 2)));
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From PyNDN boost_info_parser by Adeola Bannis.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var fs = require('fs');
+
+/**
+ * BoostInfoTree is provided for compatibility with the Boost INFO property list
+ * format used in ndn-cxx.
+ *
+ * Each node in the tree may have a name and a value as well as associated
+ * sub-trees. The sub-tree names are not unique, and so sub-trees are stored as
+ * dictionaries where the key is a sub-tree name and the values are the
+ * sub-trees sharing the same name.
+ *
+ * Nodes can be accessed with a path syntax, as long as nodes in the path do not
+ * contain the path separator '/' in their names.
+ * @constructor
+ */
+var BoostInfoTree = function BoostInfoTree(value, parent)
+{
+ // subtrees is an array of {key: treeName, value: subtreeList} where
+ // treeName is a string and subtreeList is an array of BoostInfoTree.
+ // We can't use a dictionary because we want the keys to be in order.
+ this.subtrees = [];
+ this.value = value;
+ this.parent = parent;
+
+ this.lastChild = null;
+};
+
+/**
+ * Insert a BoostInfoTree as a sub-tree with the given name.
+ * @param {string} treeName The name of the new sub-tree.
+ * @param {BoostInfoTree} newTree The sub-tree to add.
+ */
+BoostInfoTree.prototype.addSubtree = function(treeName, newTree)
+{
+ var subtreeList = this.find(treeName);
+ if (subtreeList !== null)
+ subtreeList.push(newTree);
+ else
+ this.subtrees.push({key: treeName, value: [newTree]});
+
+ newTree.parent = this;
+ this.lastChild = newTree;
+};
+
+/**
+ * Create a new BoostInfo and insert it as a sub-tree with the given name.
+ * @param {string} treeName The name of the new sub-tree.
+ * @param {string} value The value associated with the new sub-tree.
+ * @return {BoostInfoTree} The created sub-tree.
+ */
+BoostInfoTree.prototype.createSubtree = function(treeName, value)
+{
+ var newTree = new BoostInfoTree(value, this);
+ this.addSubtree(treeName, newTree);
+ return newTree;
+};
+
+/**
+ * Look up using the key and return a list of the subtrees.
+ * @param {string} key The key which may be a path separated with '/'.
+ * @return {Array<BoostInfoTree>} A new array with pointers to the subtrees.
+ */
+BoostInfoTree.prototype.get = function(key)
+{
+ // Strip beginning '/'.
+ key = key.replace(/^\/+/, "");
+ if (key.length === 0)
+ return [this];
+ var path = key.split('/');
+
+ var subtrees = this.find(path[0]);
+ if (subtrees === null)
+ return [];
+ if (path.length == 1)
+ return subtrees.slice(0);
+
+ var newPath = path.slice(1).join('/');
+ var foundVals = [];
+ for (var i = 0; i < subtrees.length; ++i) {
+ var t = subtrees[i];
+ var partial = t.get(newPath);
+ foundVals = foundVals.concat(partial);
+ }
+ return foundVals;
+};
+
+/**
+ * Look up using the key and return string value of the first subtree.
+ * @param {string} key The key which may be a path separated with '/'.
+ * @return {string} The string value or null if not found.
+ */
+BoostInfoTree.prototype.getFirstValue = function(key)
+{
+ var list = this.get(key);
+ if (list.length >= 1)
+ return list[0].value;
+ else
+ return null;
+};
+
+BoostInfoTree.prototype.getValue = function() { return this.value; };
+
+BoostInfoTree.prototype.getParent = function() { return this.parent; };
+
+BoostInfoTree.prototype.getLastChild = function() { return this.lastChild; };
+
+BoostInfoTree.prototype.prettyPrint = function(indentLevel)
+{
+ indentLevel = indentLevel || 1;
+
+ var prefix = Array(indentLevel + 1).join(' ');
+ var s = "";
+
+ if (this.parent != null) {
+ if (this.value && this.value.length > 0)
+ s += "\"" + this.value + "\"";
+ s += "\n";
+ }
+
+ if (this.subtrees.length > 0) {
+ if (this.parent)
+ s += prefix + "{\n";
+ var nextLevel = Array(indentLevel + 2 + 1).join(' ');
+ for (var i = 0; i < this.subtrees.length; ++i) {
+ for (var iSubTree = 0; iSubTree < this.subtrees[i].value.length; ++iSubTree)
+ s += nextLevel + this.subtrees[i].key + " " +
+ this.subtrees[i].value[iSubTree].prettyPrint(indentLevel + 2);
+ }
+
+ if (this.parent)
+ s += prefix + "}\n";
+ }
+
+ return s;
+};
+
+BoostInfoTree.prototype.toString = function()
+{
+ return this.prettyPrint();
+};
+
+/**
+ * Use treeName to find the array of BoostInfoTree in this.subtrees.
+ * @param {string} treeName The key in this.subtrees to search for. This does a
+ * flat search in subtrees_. It does not split by '/' into a path.
+ * @return {Array<BoostInfoTree>} A array of BoostInfoTree, or null if not found.
+ */
+BoostInfoTree.prototype.find = function(treeName)
+{
+ for (var i = 0; i < this.subtrees.length; ++i) {
+ if (this.subtrees[i].key == treeName)
+ return this.subtrees[i].value;
+ }
+
+ return null;
+};
+
+/**
+ * A BoostInfoParser reads files in Boost's INFO format and constructs a
+ * BoostInfoTree.
+ * @constructor
+ */
+var BoostInfoParser = function BoostInfoParser()
+{
+ this.root = new BoostInfoTree();
+};
+
+exports.BoostInfoParser = BoostInfoParser;
+exports.BoostInfoTree = BoostInfoTree; // debug
+
+/**
+ * Add the contents of the file or input string to the root BoostInfoTree. There
+ * are two forms:
+ * read(fileName) reads fileName from the file system.
+ * read(input, inputName) reads from the input, in which case inputName is used
+ * only for log messages, etc.
+ * @param {string} fileName The path to the INFO file.
+ * @param {string} input The contents of the INFO file, with lines separated by
+ * "\n" or "\r\n".
+ * @param {string} inputName Use with input for log messages, etc.
+ */
+BoostInfoParser.prototype.read = function(fileNameOrInput, inputName)
+{
+ var input;
+ if (typeof inputName == 'string')
+ input = fileNameOrInput;
+ else {
+ // No inputName, so assume the first arg is the file name.
+ var fileName = fileNameOrInput;
+ inputName = fileName;
+ input = fs.readFileSync(fileName).toString();
+ }
+
+ var ctx = this.root;
+ var thisParser = this;
+ input.split(/\r?\n/).forEach(function(line) {
+ ctx = thisParser.parseLine(line.trim(), ctx);
+ });
+};
+
+/**
+ * Write the root tree of this BoostInfoParser as file in Boost's INFO format.
+ * @param {string} fileName The output path.
+ */
+BoostInfoParser.prototype.write = function(fileName)
+{
+ fs.writeFileSync(fileName, "" + this.root);
+};
+
+/**
+ * Get the root tree of this parser.
+ * @return {BoostInfoTree} The root BoostInfoTree.
+ */
+BoostInfoParser.prototype.getRoot = function() { return this.root; };
+
+/**
+ * Similar to Python's shlex.split, split s into an array of strings which are
+ * separated by whitespace, treating a string within quotes as a single entity
+ * regardless of whitespace between the quotes. Also allow a backslash to escape
+ * the next character.
+ * @param {string} s The input string to split.
+ * @return {Array<string>} An array of strings.
+ */
+BoostInfoParser.shlex_split = function(s)
+{
+ var result = [];
+ if (s == "")
+ return result;
+ var whiteSpace = " \t\n\r";
+ var iStart = 0;
+
+ while (true) {
+ // Move iStart past whitespace.
+ while (whiteSpace.indexOf(s[iStart]) >= 0) {
+ iStart += 1;
+ if (iStart >= s.length)
+ // Done.
+ return result;
+ }
+
+ // Move iEnd to the end of the token.
+ var iEnd = iStart;
+ var inQuotation = false;
+ var token = "";
+ while (true) {
+ if (s[iEnd] == '\\') {
+ // Append characters up to the backslash, skip the backslash and
+ // move iEnd past the escaped character.
+ token += s.substring(iStart, iEnd);
+ iStart = iEnd + 1;
+ iEnd = iStart;
+ if (iEnd >= s.length)
+ // An unusual case: A backslash at the end of the string.
+ break;
+ }
+ else {
+ if (inQuotation) {
+ if (s[iEnd] == '\"') {
+ // Append characters up to the end quote and skip.
+ token += s.substring(iStart, iEnd);
+ iStart = iEnd + 1;
+ inQuotation = false;
+ }
+ }
+ else {
+ if (s[iEnd] == '\"') {
+ // Append characters up to the start quote and skip.
+ token += s.substring(iStart, iEnd);
+ iStart = iEnd + 1;
+ inQuotation = true;
+ }
+ else
+ if (whiteSpace.indexOf(s[iEnd]) >= 0)
+ break;
+ }
+ }
+
+ iEnd += 1;
+ if (iEnd >= s.length)
+ break;
+ }
+
+ token += s.substring(iStart, iEnd);
+ result.push(token);
+ if (iEnd >= s.length)
+ // Done.
+ return result;
+
+ iStart = iEnd;
+ }
+};
+
+BoostInfoParser.prototype.parseLine = function(line, context)
+{
+ // Skip blank lines and comments.
+ var commentStart = line.indexOf(';');
+ if (commentStart >= 0)
+ line = line.substring(0, commentStart).trim();
+ if (line.length == 0)
+ return context;
+
+ // Usually we are expecting key and optional value.
+ var strings = BoostInfoParser.shlex_split(line);
+ var isSectionStart = false;
+ var isSectionEnd = false;
+ for (var i = 0; i < strings.length; ++i) {
+ isSectionStart = (isSectionStart || strings[i] == "{");
+ isSectionEnd = (isSectionEnd || strings[i] == "}");
+ }
+
+ if (!isSectionStart && !isSectionEnd) {
+ var key = strings[0];
+ var val;
+ if (strings.length > 1)
+ val = strings[1];
+ context.createSubtree(key, val);
+
+ return context;
+ }
+
+ // OK, who is the joker who put a { on the same line as the key name?!
+ var sectionStart = line.indexOf('{');
+ if (sectionStart > 0) {
+ var firstPart = line.substring(0, sectionStart);
+ var secondPart = line.substring(sectionStart);
+
+ var ctx = this.parseLine(firstPart, context);
+ return this.parseLine(secondPart, ctx);
+ }
+
+ // If we encounter a {, we are beginning a new context.
+ // TODO: Error if there was already a subcontext here.
+ if (line[0] == '{') {
+ context = context.getLastChild();
+ return context;
+ }
+
+ // If we encounter a }, we are ending a list context.
+ if (line[0] == '}') {
+ context = context.getParent();
+ return context;
+ }
+
+ throw runtime_error("BoostInfoParser: input line is malformed");
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Name = require('../name.js').Name; /** @ignore */
+var InterestFilter = require('../interest-filter.js').InterestFilter; /** @ignore */
+var ForwardingFlags = require('../forwarding-flags.js').ForwardingFlags; /** @ignore */
+var WireFormat = require('../encoding/wire-format.js').WireFormat; /** @ignore */
+var LOG = require('../log.js').Log.LOG;
+
+/**
+ * A MemoryContentCache holds a set of Data packets and answers an Interest to
+ * return the correct Data packet. The cache is periodically cleaned up to
+ * remove each stale Data packet based on its FreshnessPeriod (if it has one).
+ * @note This class is an experimental feature. See the API docs for more detail at
+ * http://named-data.net/doc/ndn-ccl-api/memory-content-cache.html .
+ *
+ * Create a new MemoryContentCache to use the given Face.
+ *
+ * @param {Face} face The Face to use to call registerPrefix and
+ * setInterestFilter, and which will call this object's OnInterest callback.
+ * @param {number} cleanupIntervalMilliseconds (optional) The interval
+ * in milliseconds between each check to clean up stale content in the cache. If
+ * omitted, use a default of 1000 milliseconds. If this is a large number, then
+ * effectively the stale content will not be removed from the cache.
+ * @constructor
+ */
+var MemoryContentCache = function MemoryContentCache
+ (face, cleanupIntervalMilliseconds)
+{
+ cleanupIntervalMilliseconds = (cleanupIntervalMilliseconds || 1000.0);
+
+ this.face = face;
+ this.cleanupIntervalMilliseconds = cleanupIntervalMilliseconds;
+ this.nextCleanupTime = new Date().getTime() + cleanupIntervalMilliseconds;
+
+ this.onDataNotFoundForPrefix = {}; /**< The map key is the prefix.toUri().
+ The value is an OnInterest function. */
+ this.interestFilterIdList = []; /**< elements are number */
+ this.registeredPrefixIdList = []; /**< elements are number */
+ this.noStaleTimeCache = []; /**< elements are MemoryContentCache.Content */
+ this.staleTimeCache = []; /**< elements are MemoryContentCache.StaleTimeContent */
+ //StaleTimeContent::Compare contentCompare_;
+ this.emptyComponent = new Name.Component();
+ this.pendingInterestTable = [];
+
+ var thisMemoryContentCache = this;
+ this.storePendingInterestCallback = function
+ (localPrefix, localInterest, localFace, localInterestFilterId, localFilter) {
+ thisMemoryContentCache.storePendingInterest(localInterest, localFace);
+ };
+};
+
+exports.MemoryContentCache = MemoryContentCache;
+
+/**
+ * Call registerPrefix on the Face given to the constructor so that this
+ * MemoryContentCache will answer interests whose name has the prefix.
+ * Alternatively, if the Face's registerPrefix has already been called,
+ * then you can call this object's setInterestFilter.
+ * @param {Name} prefix The Name for the prefix to register. This copies the Name.
+ * @param {function} onRegisterFailed If this fails to register the prefix for
+ * any reason, this calls onRegisterFailed(prefix) where prefix is the prefix
+ * given to registerPrefix.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onRegisterSuccess (optional) When this receives a success
+ * message, this calls onRegisterSuccess[0](prefix, registeredPrefixId). If
+ * onRegisterSuccess is [null] or omitted, this does not use it. (As a special
+ * case, this optional parameter is supplied as an array of one function,
+ * instead of just a function, in order to detect when it is used instead of the
+ * following optional onDataNotFound function.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onDataNotFound (optional) If a data packet for an interest
+ * is not found in the cache, this forwards the interest by calling
+ * onDataNotFound(prefix, interest, face, interestFilterId, filter). Your
+ * callback can find the Data packet for the interest and call
+ * face.putData(data). If your callback cannot find the Data packet, it can
+ * optionally call storePendingInterest(interest, face) to store the pending
+ * interest in this object to be satisfied by a later call to add(data). If you
+ * want to automatically store all pending interests, you can simply use
+ * getStorePendingInterest() for onDataNotFound. If onDataNotFound is omitted or
+ * null, this does not use it.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {ForwardingFlags} flags (optional) See Face.registerPrefix.
+ * @param {WireFormat} wireFormat (optional) See Face.registerPrefix.
+ */
+MemoryContentCache.prototype.registerPrefix = function
+ (prefix, onRegisterFailed, onRegisterSuccess, onDataNotFound, flags, wireFormat)
+{
+ var arg3 = onRegisterSuccess;
+ var arg4 = onDataNotFound;
+ var arg5 = flags;
+ var arg6 = wireFormat;
+ // arg3, arg4, arg5, arg6 may be:
+ // [OnRegisterSuccess], OnDataNotFound, ForwardingFlags, WireFormat
+ // [OnRegisterSuccess], OnDataNotFound, ForwardingFlags, null
+ // [OnRegisterSuccess], OnDataNotFound, WireFormat, null
+ // [OnRegisterSuccess], OnDataNotFound, null, null
+ // [OnRegisterSuccess], ForwardingFlags, WireFormat, null
+ // [OnRegisterSuccess], ForwardingFlags, null, null
+ // [OnRegisterSuccess], WireFormat, null, null
+ // [OnRegisterSuccess], null, null, null
+ // OnDataNotFound, ForwardingFlags, WireFormat, null
+ // OnDataNotFound, ForwardingFlags, null, null
+ // OnDataNotFound, WireFormat, null, null
+ // OnDataNotFound, null, null, null
+ // ForwardingFlags, WireFormat, null, null
+ // ForwardingFlags, null, null, null
+ // WireFormat, null, null, null
+ // null, null, null, null
+ if (typeof arg3 === "object" && arg3.length === 1 &&
+ typeof arg3[0] === "function")
+ onRegisterSuccess = arg3[0];
+ else
+ onRegisterSuccess = null;
+
+ if (typeof arg3 === "function")
+ onDataNotFound = arg3;
+ else if (typeof arg4 === "function")
+ onDataNotFound = arg4;
+ else
+ onDataNotFound = null;
+
+ if (arg3 instanceof ForwardingFlags)
+ flags = arg3;
+ else if (arg4 instanceof ForwardingFlags)
+ flags = arg4;
+ else if (arg5 instanceof ForwardingFlags)
+ flags = arg5;
+ else
+ flags = new ForwardingFlags();
+
+ if (arg3 instanceof WireFormat)
+ wireFormat = arg3;
+ else if (arg4 instanceof WireFormat)
+ wireFormat = arg4;
+ else if (arg5 instanceof WireFormat)
+ wireFormat = arg5;
+ else if (arg6 instanceof WireFormat)
+ wireFormat = arg6;
+ else
+ wireFormat = WireFormat.getDefaultWireFormat();
+
+ if (onDataNotFound)
+ this.onDataNotFoundForPrefix[prefix.toUri()] = onDataNotFound;
+ var registeredPrefixId = this.face.registerPrefix
+ (prefix, this.onInterest.bind(this), onRegisterFailed, onRegisterSuccess,
+ flags, wireFormat);
+ this.registeredPrefixIdList.push(registeredPrefixId);
+};
+
+/**
+ * Call setInterestFilter on the Face given to the constructor so that this
+ * MemoryContentCache will answer interests whose name matches the filter.
+ * There are two forms of setInterestFilter.
+ * The first form uses the exact given InterestFilter:
+ * setInterestFilter(filter, [onDataNotFound]).
+ * The second form creates an InterestFilter from the given prefix Name:
+ * setInterestFilter(prefix, [onDataNotFound]).
+ * @param {InterestFilter} filter The InterestFilter with a prefix and optional
+ * regex filter used to match the name of an incoming Interest. This makes a
+ * copy of filter.
+ * @param {Name} prefix The Name prefix used to match the name of an incoming
+ * Interest.
+ * @param {function} onDataNotFound (optional) If a data packet for an interest
+ * is not found in the cache, this forwards the interest by calling
+ * onDataNotFound(prefix, interest, face, interestFilterId, filter). Your
+ * callback can find the Data packet for the interest and call
+ * face.putData(data). If your callback cannot find the Data packet, it can
+ * optionally call storePendingInterest(interest, face) to store the pending
+ * interest in this object to be satisfied by a later call to add(data). If you
+ * want to automatically store all pending interests, you can simply use
+ * getStorePendingInterest() for onDataNotFound. If onDataNotFound is omitted or
+ * null, this does not use it.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ */
+MemoryContentCache.prototype.setInterestFilter = function
+ (filterOrPrefix, onDataNotFound)
+{
+ if (onDataNotFound) {
+ var prefix;
+ if (typeof filterOrPrefix === 'object' && filterOrPrefix instanceof InterestFilter)
+ prefix = filterOrPrefix.getPrefix();
+ else
+ prefix = filterOrPrefix;
+ this.onDataNotFoundForPrefix[prefix.toUri()] = onDataNotFound;
+ }
+ var interestFilterId = this.face.setInterestFilter
+ (filterOrPrefix, this.onInterest.bind(this));
+ this.interestFilterIdList.push(interestFilterId);
+};
+
+/**
+ * Call Face.unsetInterestFilter and Face.removeRegisteredPrefix for all the
+ * prefixes given to the setInterestFilter and registerPrefix method on this
+ * MemoryContentCache object so that it will not receive interests any more. You
+ * can call this if you want to "shut down" this MemoryContentCache while your
+ * application is still running.
+ */
+MemoryContentCache.prototype.unregisterAll = function()
+{
+ for (var i = 0; i < this.interestFilterIdList.length; ++i)
+ this.face.unsetInterestFilter(this.interestFilterIdList[i]);
+ this.interestFilterIdList = [];
+
+ for (var i = 0; i < this.registeredPrefixIdList.length; ++i)
+ this.face.removeRegisteredPrefix(this.registeredPrefixIdList[i]);
+ this.registeredPrefixIdList = [];
+
+ // Also clear each onDataNotFoundForPrefix given to registerPrefix.
+ this.onDataNotFoundForPrefix = {};
+};
+
+/**
+ * Add the Data packet to the cache so that it is available to use to answer
+ * interests. If data.getMetaInfo().getFreshnessPeriod() is not null, set the
+ * staleness time to now plus data.getMetaInfo().getFreshnessPeriod(), which is
+ * checked during cleanup to remove stale content. This also checks if
+ * cleanupIntervalMilliseconds milliseconds have passed and removes stale
+ * content from the cache. After removing stale content, remove timed-out
+ * pending interests from storePendingInterest(), then if the added Data packet
+ * satisfies any interest, send it through the face and remove the interest
+ * from the pending interest table.
+ * @param {Data} data The Data packet object to put in the cache. This copies
+ * the fields from the object.
+ */
+MemoryContentCache.prototype.add = function(data)
+{
+ this.doCleanup();
+
+ if (data.getMetaInfo().getFreshnessPeriod() != null &&
+ data.getMetaInfo().getFreshnessPeriod() >= 0.0) {
+ // The content will go stale, so use staleTimeCache.
+ var content = new MemoryContentCache.StaleTimeContent(data);
+ // Insert into staleTimeCache, sorted on content.staleTimeMilliseconds.
+ // Search from the back since we expect it to go there.
+ var i = this.staleTimeCache.length - 1;
+ while (i >= 0) {
+ if (this.staleTimeCache[i].staleTimeMilliseconds <= content.staleTimeMilliseconds)
+ break;
+ --i;
+ }
+ // Element i is the greatest less than or equal to
+ // content.staleTimeMilliseconds, so insert after it.
+ this.staleTimeCache.splice(i + 1, 0, content);
+ }
+ else
+ // The data does not go stale, so use noStaleTimeCache.
+ this.noStaleTimeCache.push(new MemoryContentCache.Content(data));
+
+ // Remove timed-out interests and check if the data packet matches any pending
+ // interest.
+ // Go backwards through the list so we can erase entries.
+ var nowMilliseconds = new Date().getTime();
+ for (var i = this.pendingInterestTable.length - 1; i >= 0; --i) {
+ if (this.pendingInterestTable[i].isTimedOut(nowMilliseconds)) {
+ this.pendingInterestTable.splice(i, 1);
+ continue;
+ }
+ if (this.pendingInterestTable[i].getInterest().matchesName(data.getName())) {
+ try {
+ // Send to the same face from the original call to onInterest.
+ // wireEncode returns the cached encoding if available.
+ this.pendingInterestTable[i].getFace().send(data.wireEncode().buf());
+ }
+ catch (ex) {
+ if (LOG > 0)
+ console.log("" + ex);
+ return;
+ }
+
+ // The pending interest is satisfied, so remove it.
+ this.pendingInterestTable.splice(i, 1);
+ }
+ }
+};
+
+/**
+ * Store an interest from an OnInterest callback in the internal pending
+ * interest table (normally because there is no Data packet available yet to
+ * satisfy the interest). add(data) will check if the added Data packet
+ * satisfies any pending interest and send it through the face.
+ * @param {Interest} interest The Interest for which we don't have a Data packet
+ * yet. You should not modify the interest after calling this.
+ * @param {Face} face The Face with the connection which received
+ * the interest. This comes from the OnInterest callback.
+ */
+MemoryContentCache.prototype.storePendingInterest = function(interest, face)
+{
+ this.pendingInterestTable.push
+ (new MemoryContentCache.PendingInterest(interest, face));
+};
+
+/**
+ * Return a callback to use for onDataNotFound in registerPrefix which simply
+ * calls storePendingInterest() to store the interest that doesn't match a
+ * Data packet. add(data) will check if the added Data packet satisfies any
+ * pending interest and send it.
+ * @return {function} A callback to use for onDataNotFound in registerPrefix().
+ */
+MemoryContentCache.prototype.getStorePendingInterest = function()
+{
+ return this.storePendingInterestCallback;
+};
+
+/**
+ * This is the OnInterest callback which is called when the library receives
+ * an interest whose name has the prefix given to registerPrefix. First check
+ * if cleanupIntervalMilliseconds milliseconds have passed and remove stale
+ * content from the cache. Then search the cache for the Data packet, matching
+ * any interest selectors including ChildSelector, and send the Data packet
+ * to the face. If no matching Data packet is in the cache, call
+ * the callback in onDataNotFoundForPrefix (if defined).
+ */
+MemoryContentCache.prototype.onInterest = function
+ (prefix, interest, face, interestFilterId, filter)
+{
+ this.doCleanup();
+
+ var selectedComponent = 0;
+ var selectedEncoding = null;
+ // We need to iterate over both arrays.
+ var totalSize = this.staleTimeCache.length + this.noStaleTimeCache.length;
+ for (var i = 0; i < totalSize; ++i) {
+ var content;
+ if (i < this.staleTimeCache.length)
+ content = this.staleTimeCache[i];
+ else
+ // We have iterated over the first array. Get from the second.
+ content = this.noStaleTimeCache[i - this.staleTimeCache.length];
+
+ if (interest.matchesName(content.getName())) {
+ if (interest.getChildSelector() < 0) {
+ // No child selector, so send the first match that we have found.
+ face.send(content.getDataEncoding());
+ return;
+ }
+ else {
+ // Update selectedEncoding based on the child selector.
+ var component;
+ if (content.getName().size() > interest.getName().size())
+ component = content.getName().get(interest.getName().size());
+ else
+ component = this.emptyComponent;
+
+ var gotBetterMatch = false;
+ if (selectedEncoding === null)
+ // Save the first match.
+ gotBetterMatch = true;
+ else {
+ if (interest.getChildSelector() == 0) {
+ // Leftmost child.
+ if (component.compare(selectedComponent) < 0)
+ gotBetterMatch = true;
+ }
+ else {
+ // Rightmost child.
+ if (component.compare(selectedComponent) > 0)
+ gotBetterMatch = true;
+ }
+ }
+
+ if (gotBetterMatch) {
+ selectedComponent = component;
+ selectedEncoding = content.getDataEncoding();
+ }
+ }
+ }
+ }
+
+ if (selectedEncoding !== null)
+ // We found the leftmost or rightmost child.
+ face.send(selectedEncoding);
+ else {
+ // Call the onDataNotFound callback (if defined).
+ var onDataNotFound = this.onDataNotFoundForPrefix[prefix.toUri()];
+ if (onDataNotFound)
+ onDataNotFound(prefix, interest, face, interestFilterId, filter);
+ }
+};
+
+/**
+ * Check if now is greater than nextCleanupTime and, if so, remove stale
+ * content from staleTimeCache and reset nextCleanupTime based on
+ * cleanupIntervalMilliseconds. Since add(Data) does a sorted insert into
+ * staleTimeCache, the check for stale data is quick and does not require
+ * searching the entire staleTimeCache.
+ */
+MemoryContentCache.prototype.doCleanup = function()
+{
+ var now = new Date().getTime();
+ if (now >= this.nextCleanupTime) {
+ // staleTimeCache is sorted on staleTimeMilliseconds, so we only need to
+ // erase the stale entries at the front, then quit.
+ while (this.staleTimeCache.length > 0 && this.staleTimeCache[0].isStale(now))
+ this.staleTimeCache.shift();
+
+ this.nextCleanupTime = now + this.cleanupIntervalMilliseconds;
+ }
+};
+
+/**
+ * Content is a private class to hold the name and encoding for each entry
+ * in the cache. This base class is for a Data packet without a FreshnessPeriod.
+ *
+ * Create a new Content entry to hold data's name and wire encoding.
+ * @param {Data} data The Data packet whose name and wire encoding are copied.
+ */
+MemoryContentCache.Content = function MemoryContentCacheContent(data)
+{
+ // Allow an undefined data so that StaleTimeContent can set the prototype.
+ if (data) {
+ // Copy the name.
+ this.name = new Name(data.getName());
+ // wireEncode returns the cached encoding if available.
+ this.dataEncoding = data.wireEncode().buf();
+ }
+};
+
+MemoryContentCache.Content.prototype.getName = function() { return this.name; };
+
+MemoryContentCache.Content.prototype.getDataEncoding = function() { return this.dataEncoding; };
+
+/**
+ * StaleTimeContent extends Content to include the staleTimeMilliseconds for
+ * when this entry should be cleaned up from the cache.
+ *
+ * Create a new StaleTimeContent to hold data's name and wire encoding as well
+ * as the staleTimeMilliseconds which is now plus
+ * data.getMetaInfo().getFreshnessPeriod().
+ * @param {Data} data The Data packet whose name and wire encoding are copied.
+ */
+MemoryContentCache.StaleTimeContent = function MemoryContentCacheStaleTimeContent
+ (data)
+{
+ // Call the base constructor.
+ MemoryContentCache.Content.call(this, data);
+
+ // Set up staleTimeMilliseconds which is The time when the content becomse
+ // stale in milliseconds according to new Date().getTime().
+ this.staleTimeMilliseconds = new Date().getTime() +
+ data.getMetaInfo().getFreshnessPeriod();
+};
+
+MemoryContentCache.StaleTimeContent.prototype = new MemoryContentCache.Content();
+MemoryContentCache.StaleTimeContent.prototype.name = "StaleTimeContent";
+
+/**
+ * Check if this content is stale.
+ * @param {number} nowMilliseconds The current time in milliseconds from
+ * new Date().getTime().
+ * @return {boolean} True if this content is stale, otherwise false.
+ */
+MemoryContentCache.StaleTimeContent.prototype.isStale = function(nowMilliseconds)
+{
+ return this.staleTimeMilliseconds <= nowMilliseconds;
+};
+
+/**
+ * A PendingInterest holds an interest which onInterest received but could
+ * not satisfy. When we add a new data packet to the cache, we will also check
+ * if it satisfies a pending interest.
+ */
+MemoryContentCache.PendingInterest = function MemoryContentCachePendingInterest
+ (interest, face)
+{
+ this.interest = interest;
+ this.face = face;
+
+ if (this.interest.getInterestLifetimeMilliseconds() >= 0.0)
+ this.timeoutMilliseconds = (new Date()).getTime() +
+ this.interest.getInterestLifetimeMilliseconds();
+ else
+ this.timeoutMilliseconds = -1.0;
+};
+
+/**
+ * Return the interest given to the constructor.
+ */
+MemoryContentCache.PendingInterest.prototype.getInterest = function()
+{
+ return this.interest;
+};
+
+/**
+ * Return the face given to the constructor.
+ */
+MemoryContentCache.PendingInterest.prototype.getFace = function()
+{
+ return this.face;
+};
+
+/**
+ * Check if this interest is timed out.
+ * @param {number} nowMilliseconds The current time in milliseconds from
+ * new Date().getTime().
+ * @return {boolean} True if this interest timed out, otherwise false.
+ */
+MemoryContentCache.PendingInterest.prototype.isTimedOut = function(nowMilliseconds)
+{
+ return this.timeoutTimeMilliseconds >= 0.0 &&
+ nowMilliseconds >= this.timeoutTimeMilliseconds;
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From PyNDN ndn_regex.py by Adeola Bannis.
+ * Originally from Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Name = require('../name.js').Name;
+
+/**
+ * An NdnRegexMatcher has static methods to convert an NDN regex
+ * (http://redmine.named-data.net/projects/ndn-cxx/wiki/Regex) to a JavaScript
+ * RegExp that can match against URIs.
+ * @constructor
+ */
+var NdnRegexMatcher = function NdnRegexMatcher()
+{
+};
+
+exports.NdnRegexMatcher = NdnRegexMatcher;
+
+/**
+ * Determine if the provided NDN regex matches the given Name.
+ * @param {string} pattern The NDN regex.
+ * @param {Name} name The Name to match against the regex.
+ * @return {Object} The match object from String.match, or null if the pattern
+ * does not match.
+ */
+NdnRegexMatcher.match = function(pattern, name)
+{
+ var nameUri = name.toUri();
+
+ pattern = NdnRegexMatcher.sanitizeSets(pattern);
+
+ pattern = pattern.replace(/<>/g, "(?:<.+?>)");
+ pattern = pattern.replace(/>/g, "");
+ pattern = pattern.replace(/<(?!!)/g, "/");
+
+ return nameUri.match(new RegExp(pattern));
+};
+
+NdnRegexMatcher.sanitizeSets = function(pattern)
+{
+ var newPattern = pattern;
+
+ // Positive sets can be changed to (comp1|comp2).
+ // Negative sets must be changed to negative lookahead assertions.
+
+ var regex1 = /\[(\^?)(.*?)\]/g;
+ var match;
+ while ((match = regex1.exec(pattern)) !== null) {
+ // Insert | between components.
+ // Match 2 is the last match, so we use the hack of working backwards from
+ // lastIndex. If possible, this should be changed to a more direct solution.
+ var start = regex1.lastIndex - "]".length - match[2].length;
+ var end = start + match[2].length;
+ if (start - end === 0)
+ continue;
+ var oldStr = match[2];
+ var newStr = oldStr.replace(/></g, ">|<");
+ newPattern = newPattern.substr(0, start) + newStr + newPattern.substr(end);
+ }
+
+ // Replace [] with (), or (?! ) for negative lookahead.
+ // If we use negative lookahead, we also have to consume one component.
+ var isNegative = newPattern.indexOf("[^") >= 0;
+ if (isNegative) {
+ newPattern = newPattern.replace(/\[\^/g, "(?:(?!");
+ newPattern = newPattern.replace(/\]/g, ")(?:/.*)*)");
+ }
+ else {
+ newPattern = newPattern.replace(/\[/g, "(");
+ newPattern = newPattern.replace(/\]/g, ")");
+ }
+
+ return newPattern;
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-cxx util/segment-fetcher https://github.com/named-data/ndn-cxx
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Interest = require('../interest.js').Interest; /** @ignore */
+var Blob = require('./blob.js').Blob; /** @ignore */
+var KeyChain = require('../security/key-chain.js').KeyChain; /** @ignore */
+var NdnCommon = require('./ndn-common.js').NdnCommon;
+
+/**
+ * SegmentFetcher is a utility class to fetch the latest version of segmented data.
+ *
+ * SegmentFetcher assumes that the data is named /<prefix>/<version>/<segment>,
+ * where:
+ * - <prefix> is the specified name prefix,
+ * - <version> is an unknown version that needs to be discovered, and
+ * - <segment> is a segment number. (The number of segments is unknown and is
+ * controlled by the `FinalBlockId` field in at least the last Data packet.
+ *
+ * The following logic is implemented in SegmentFetcher:
+ *
+ * 1. Express the first Interest to discover the version:
+ *
+ * >> Interest: /<prefix>?ChildSelector=1&MustBeFresh=true
+ *
+ * 2. Infer the latest version of the Data: <version> = Data.getName().get(-2)
+ *
+ * 3. If the segment number in the retrieved packet == 0, go to step 5.
+ *
+ * 4. Send an Interest for segment 0:
+ *
+ * >> Interest: /<prefix>/<version>/<segment=0>
+ *
+ * 5. Keep sending Interests for the next segment while the retrieved Data does
+ * not have a FinalBlockId or the FinalBlockId != Data.getName().get(-1).
+ *
+ * >> Interest: /<prefix>/<version>/<segment=(N+1))>
+ *
+ * 6. Call the onComplete callback with a Blob that concatenates the content
+ * from all the segmented objects.
+ *
+ * If an error occurs during the fetching process, the onError callback is called
+ * with a proper error code. The following errors are possible:
+ *
+ * - `INTEREST_TIMEOUT`: if any of the Interests times out
+ * - `DATA_HAS_NO_SEGMENT`: if any of the retrieved Data packets don't have a segment
+ * as the last component of the name (not counting the implicit digest)
+ * - `SEGMENT_VERIFICATION_FAILED`: if any retrieved segment fails
+ * the user-provided VerifySegment callback or KeyChain verifyData.
+ * - `IO_ERROR`: for I/O errors when sending an Interest.
+ *
+ * In order to validate individual segments, a KeyChain needs to be supplied.
+ * If verifyData fails, the fetching process is aborted with
+ * SEGMENT_VERIFICATION_FAILED. If data validation is not required, pass null.
+ *
+ * Example:
+ * var onComplete = function(content) { ... }
+ *
+ * var onError = function(errorCode, message) { ... }
+ *
+ * var interest = new Interest(new Name("/data/prefix"));
+ * interest.setInterestLifetimeMilliseconds(1000);
+ *
+ * SegmentFetcher.fetch(face, interest, null, onComplete, onError);
+ *
+ * This is a private constructor to create a new SegmentFetcher to use the Face.
+ * An application should use SegmentFetcher.fetch. If validatorKeyChain is not
+ * null, use it and ignore verifySegment. After creating the SegmentFetcher,
+ * call fetchFirstSegment.
+ * @param {Face} face This calls face.expressInterest to fetch more segments.
+ * @param validatorKeyChain {KeyChain} If this is not null, use its verifyData
+ * instead of the verifySegment callback.
+ * @param {function} verifySegment When a Data packet is received this calls
+ * verifySegment(data) where data is a Data object. If it returns False then
+ * abort fetching and call onError with
+ * SegmentFetcher.ErrorCode.SEGMENT_VERIFICATION_FAILED.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onComplete When all segments are received, call
+ * onComplete(content) where content is a Blob which has the concatenation of
+ * the content of all the segments.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError Call onError.onError(errorCode, message) for
+ * timeout or an error processing segments. errorCode is a value from
+ * SegmentFetcher.ErrorCode and message is a related string.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @constructor
+ */
+var SegmentFetcher = function SegmentFetcher
+ (face, validatorKeyChain, verifySegment, onComplete, onError)
+{
+ this.face = face;
+ this.validatorKeyChain = validatorKeyChain;
+ this.verifySegment = verifySegment;
+ this.onComplete = onComplete;
+ this.onError = onError;
+
+ this.contentParts = []; // of Buffer
+};
+
+exports.SegmentFetcher = SegmentFetcher;
+
+/**
+ * An ErrorCode value is passed in the onError callback.
+ */
+SegmentFetcher.ErrorCode = {
+ INTEREST_TIMEOUT: 1,
+ DATA_HAS_NO_SEGMENT: 2,
+ SEGMENT_VERIFICATION_FAILED: 3
+};
+
+/**
+ * DontVerifySegment may be used in fetch to skip validation of Data packets.
+ */
+SegmentFetcher.DontVerifySegment = function(data)
+{
+ return true;
+};
+
+/**
+ * Initiate segment fetching. For more details, see the documentation for the
+ * class. There are two forms of fetch:
+ * fetch(face, baseInterest, validatorKeyChain, onComplete, onError)
+ * and
+ * fetch(face, baseInterest, verifySegment, onComplete, onError)
+ * @param {Face} face This calls face.expressInterest to fetch more segments.
+ * @param {Interest} baseInterest An Interest for the initial segment of the
+ * requested data, where baseInterest.getName() has the name prefix. This
+ * interest may include a custom InterestLifetime and selectors that will
+ * propagate to all subsequent Interests. The only exception is that the initial
+ * Interest will be forced to include selectors "ChildSelector=1" and
+ * "MustBeFresh=true" which will be turned off in subsequent Interests.
+ * @param validatorKeyChain {KeyChain} When a Data packet is received this calls
+ * validatorKeyChain.verifyData(data). If validation fails then abortfetching
+ * and call onError with SEGMENT_VERIFICATION_FAILED. This does not make a copy
+ * of the KeyChain; the object must remain valid while fetching.
+ * If validatorKeyChain is null, this does not validate the data packet.
+ * @param {function} verifySegment When a Data packet is received this calls
+ * verifySegment(data) where data is a Data object. If it returns False then
+ * abort fetching and call onError with
+ * SegmentFetcher.ErrorCode.SEGMENT_VERIFICATION_FAILED. If data validation is
+ * not required, use SegmentFetcher.DontVerifySegment.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onComplete When all segments are received, call
+ * onComplete(content) where content is a Blob which has the concatenation of
+ * the content of all the segments.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError Call onError.onError(errorCode, message) for
+ * timeout or an error processing segments. errorCode is a value from
+ * SegmentFetcher.ErrorCode and message is a related string.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ */
+SegmentFetcher.fetch = function
+ (face, baseInterest, validatorKeyChainOrVerifySegment, onComplete, onError)
+{
+ if (validatorKeyChainOrVerifySegment == null ||
+ validatorKeyChainOrVerifySegment instanceof KeyChain)
+ new SegmentFetcher
+ (face, validatorKeyChainOrVerifySegment, SegmentFetcher.DontVerifySegment,
+ onComplete, onError)
+ .fetchFirstSegment(baseInterest);
+ else
+ new SegmentFetcher
+ (face, null, validatorKeyChainOrVerifySegment, onComplete, onError)
+ .fetchFirstSegment(baseInterest);
+};
+
+SegmentFetcher.prototype.fetchFirstSegment = function(baseInterest)
+{
+ var interest = new Interest(baseInterest);
+ interest.setChildSelector(1);
+ interest.setMustBeFresh(true);
+ var thisSegmentFetcher = this;
+ this.face.expressInterest
+ (interest,
+ function(originalInterest, data)
+ { thisSegmentFetcher.onData(originalInterest, data); },
+ function(interest) { thisSegmentFetcher.onTimeout(interest); });
+};
+
+SegmentFetcher.prototype.fetchNextSegment = function
+ (originalInterest, dataName, segment)
+{
+ // Start with the original Interest to preserve any special selectors.
+ var interest = new Interest(originalInterest);
+ // Changing a field clears the nonce so that the library will generate a new
+ // one.
+ interest.setChildSelector(0);
+ interest.setMustBeFresh(false);
+ interest.setName(dataName.getPrefix(-1).appendSegment(segment));
+ var thisSegmentFetcher = this;
+ this.face.expressInterest
+ (interest, function(originalInterest, data)
+ { thisSegmentFetcher.onData(originalInterest, data); },
+ function(interest) { thisSegmentFetcher.onTimeout(interest); });
+};
+
+SegmentFetcher.prototype.onData = function(originalInterest, data)
+{
+ if (this.validatorKeyChain != null) {
+ try {
+ var thisSegmentFetcher = this;
+ this.validatorKeyChain.verifyData
+ (data,
+ function(localData) {
+ thisSegmentFetcher.onVerified(localData, originalInterest);
+ },
+ this.onValidationFailed.bind(this));
+ } catch (ex) {
+ console.log("Error in KeyChain.verifyData: " + ex);
+ }
+ }
+ else {
+ if (!this.verifySegment(data)) {
+ try {
+ this.onError
+ (SegmentFetcher.ErrorCode.SEGMENT_VERIFICATION_FAILED,
+ "Segment verification failed");
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return;
+ }
+
+ this.onVerified(data, originalInterest);
+ }
+};
+
+SegmentFetcher.prototype.onVerified = function(data, originalInterest)
+{
+ if (!SegmentFetcher.endsWithSegmentNumber(data.getName())) {
+ // We don't expect a name without a segment number. Treat it as a bad packet.
+ try {
+ this.onError
+ (SegmentFetcher.ErrorCode.DATA_HAS_NO_SEGMENT,
+ "Got an unexpected packet without a segment number: " +
+ data.getName().toUri());
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ else {
+ var currentSegment = 0;
+ try {
+ currentSegment = data.getName().get(-1).toSegment();
+ }
+ catch (ex) {
+ try {
+ this.onError
+ (SegmentFetcher.ErrorCode.DATA_HAS_NO_SEGMENT,
+ "Error decoding the name segment number " +
+ data.getName().get(-1).toEscapedString() + ": " + ex);
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return;
+ }
+
+ var expectedSegmentNumber = this.contentParts.length;
+ if (currentSegment != expectedSegmentNumber)
+ // Try again to get the expected segment. This also includes the case
+ // where the first segment is not segment 0.
+ this.fetchNextSegment
+ (originalInterest, data.getName(), expectedSegmentNumber);
+ else {
+ // Save the content and check if we are finished.
+ this.contentParts.push(data.getContent().buf());
+
+ if (data.getMetaInfo().getFinalBlockId().getValue().size() > 0) {
+ var finalSegmentNumber = 0;
+ try {
+ finalSegmentNumber = (data.getMetaInfo().getFinalBlockId().toSegment());
+ }
+ catch (ex) {
+ try {
+ this.onError
+ (SegmentFetcher.ErrorCode.DATA_HAS_NO_SEGMENT,
+ "Error decoding the FinalBlockId segment number " +
+ data.getMetaInfo().getFinalBlockId().toEscapedString() +
+ ": " + ex);
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return;
+ }
+
+ if (currentSegment == finalSegmentNumber) {
+ // We are finished.
+
+ // Concatenate to get content.
+ var content = Buffer.concat(this.contentParts);
+ try {
+ this.onComplete(new Blob(content, false));
+ } catch (ex) {
+ console.log("Error in onComplete: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return;
+ }
+ }
+
+ // Fetch the next segment.
+ this.fetchNextSegment
+ (originalInterest, data.getName(), expectedSegmentNumber + 1);
+ }
+ }
+}
+
+SegmentFetcher.prototype.onValidationFailed = function(data, reason)
+{
+ try {
+ this.onError
+ (SegmentFetcher.ErrorCode.SEGMENT_VERIFICATION_FAILED,
+ "Segment verification failed for " + data.getName().toUri() +
+ " . Reason: " + reason);
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+};
+
+SegmentFetcher.prototype.onTimeout = function(interest)
+{
+ try {
+ this.onError
+ (SegmentFetcher.ErrorCode.INTEREST_TIMEOUT,
+ "Time out for interest " + interest.getName().toUri());
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+};
+
+/**
+ * Check if the last component in the name is a segment number.
+ * @param {Name} name The name to check.
+ * @return {boolean} True if the name ends with a segment number, otherwise false.
+ */
+SegmentFetcher.endsWithSegmentNumber = function(name)
+{
+ return name.size() >= 1 && name.get(-1).isSegment();
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * Transport is a base class for specific transport classes such as TcpTransport.
+ * @constructor
+ */
+var Transport = function Transport()
+{
+};
+
+exports.Transport = Transport;
+
+/**
+ * Transport.ConnectionInfo is a base class for connection information used by
+ * subclasses of Transport.
+ */
+Transport.ConnectionInfo = function TransportConnectionInfo()
+{
+};
+
+/**
+ * Determine whether this transport connecting according to connectionInfo is to
+ * a node on the current machine. This affects the processing of
+ * Face.registerPrefix(): if the NFD is local, registration occurs with the
+ * '/localhost/nfd...' prefix; if non-local, the library will attempt to use
+ * remote prefix registration using '/localhop/nfd...'
+ * @param {Transport.ConnectionInfo} connectionInfo A ConnectionInfo with the
+ * host to check.
+ * @param {function} onResult On success, this calls onResult(isLocal) where
+ * isLocal is true if the host is local, false if not. We use callbacks because
+ * this may need to do an asynchronous DNS lookup.
+ * @param {function} onError On failure for DNS lookup or other error, this
+ * calls onError(message) where message is an error string.
+ */
+Transport.prototype.isLocal = function(connectionInfo, onResult, onError)
+{
+ onError("Transport.isLocal is not implemented");
+};
+/**
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: Wentao Shang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * A MicroForwarderTransport extends Transport to connect to the browser's
+ * micro forwarder service. This assumes that the MicroForwarder extensions has
+ * been installed.
+ * @constructor
+ */
+var MicroForwarderTransport = function MicroForwarderTransport()
+{
+ // Call the base constructor.
+ Transport.call(this);
+
+ this.elementReader = null;
+ this.connectionInfo = null; // Read by Face.
+ this.onReceivedObject = null;
+
+ var thisTransport = this;
+ window.addEventListener("message", function(event) {
+ // We only accept messages from ourselves
+ if (event.source != window)
+ return;
+
+ if (event.data.type && (event.data.type == "FromMicroForwarderStub")) {
+ var obj = event.data.object;
+ if (obj.type && obj.type == "Buffer") {
+ if (thisTransport.elementReader != null)
+ thisTransport.elementReader.onReceivedData(new Buffer(obj.data));
+ }
+ else {
+ if (thisTransport.onReceivedObject)
+ thisTransport.onReceivedObject(obj);
+ }
+ }
+ }, false);
+};
+
+MicroForwarderTransport.prototype = new Transport();
+MicroForwarderTransport.prototype.name = "MicroForwarderTransport";
+
+/**
+ * Create a new MicroForwarderTransport.ConnectionInfo which extends
+ * Transport.ConnectionInfo to hold info for the micro forwarer connection.
+ */
+MicroForwarderTransport.ConnectionInfo = function MicroForwarderTransportConnectionInfo()
+{
+ // Call the base constructor.
+ Transport.ConnectionInfo .call(this);
+};
+
+MicroForwarderTransport.ConnectionInfo.prototype = new Transport.ConnectionInfo();
+MicroForwarderTransport.ConnectionInfo.prototype.name = "MicroForwarderTransport.ConnectionInfo";
+
+/**
+ * Check if the fields of this MicroForwarderTransport.ConnectionInfo equal the other
+ * MicroForwarderTransport.ConnectionInfo.
+ * @param {MicroForwarderTransport.ConnectionInfo} The other object to check.
+ * @return {boolean} True if the objects have equal fields, false if not.
+ */
+MicroForwarderTransport.ConnectionInfo.prototype.equals = function(other)
+{
+ if (other == null)
+ return false;
+ return true;
+};
+
+MicroForwarderTransport.ConnectionInfo.prototype.toString = function()
+{
+ return "{}";
+};
+
+/**
+ * Set the onReceivedObject callback, replacing any previous callback.
+ * @param {function} onReceivedObject (optional) If supplied and the received
+ * object type field is not "Buffer" then just call this.onReceivedObject(obj).
+ * If this is null, then don't call it.
+ */
+MicroForwarderTransport.prototype.setOnReceivedObject = function(onReceivedObject)
+{
+ this.onReceivedObject = onReceivedObject;
+}
+
+/**
+ * Determine whether this transport connecting according to connectionInfo is to
+ * a node on the current machine. Unix transports are always local.
+ * @param {MicroForwarderTransport.ConnectionInfo} connectionInfo This is ignored.
+ * @param {function} onResult This calls onResult(true) because micro forwarder
+ * transports are always local.
+ * @param {function} onError This is ignored.
+ */
+MicroForwarderTransport.prototype.isLocal = function(connectionInfo, onResult, onError)
+{
+ onResult(true);
+};
+
+/**
+ * Connect to the micro forwarder according to the info in connectionInfo.
+ * Listen on the connection to read an entire packet element and call
+ * elementListener.onReceivedElement(element). However, if the received object
+ * type field is not "Buffer" then just call this.onReceivedObject(obj).
+ * @param {MicroForwarderTransport.ConnectionInfo} connectionInfo
+ * @param {object} elementListener The elementListener with function
+ * onReceivedElement which must remain valid during the life of this object.
+ * @param {function} onopenCallback Once connected, call onopenCallback().
+ * @param {function} onclosedCallback (optional) If the connection is closed by
+ * the remote host, call onclosedCallback(). If omitted or null, don't call it.
+ */
+MicroForwarderTransport.prototype.connect = function
+ (connectionInfo, elementListener, onopenCallback, onclosedCallback)
+{
+ // The window listener is already set up.
+ this.elementReader = new ElementReader(elementListener);
+ this.connectionInfo = connectionInfo;
+ onopenCallback();
+};
+
+/**
+ * Send the JavaScript over the connection created by connect.
+ * @param {object} obj The object to send. It should have a field "type". If
+ * "type" is "Buffer" then it is processed like an NDN packet.
+ */
+MicroForwarderTransport.prototype.sendObject = function(obj)
+{
+ window.postMessage({
+ type: "FromMicroForwarderTransport",
+ object: obj
+ }, "*");
+};
+
+/**
+ * Send the buffer over the connection created by connect.
+ * @param {Buffer} buffer The bytes to send.
+ */
+MicroForwarderTransport.prototype.send = function(buffer)
+{
+ if (this.connectionInfo == null) {
+ console.log("MicroForwarderTransport connection is not established.");
+ return;
+ }
+
+ this.sendObject(buffer.toJSON());
+};
+/**
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: Wentao Shang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * A RuntimePortTransport extends Transport to connect to a WebExtensions
+ * runtime.port.
+ * @param {function} onReceivedObject (optional) If supplied and the received
+ * object type field is not "Buffer" then just call this.onReceivedObject(obj).
+ * If this is null, then don't call it.
+ * @constructor
+ */
+var RuntimePortTransport = function RuntimePortTransport(onReceivedObject)
+{
+ // Call the base constructor.
+ Transport.call(this);
+
+ this.elementReader = null;
+ this.connectionInfo = null; // Read by Face.
+ this.onReceivedObject = onReceivedObject;
+ this.port = null;
+};
+
+RuntimePortTransport.prototype = new Transport();
+RuntimePortTransport.prototype.name = "RuntimePortTransport";
+
+/**
+ * Create a new RuntimePortTransport.ConnectionInfo which extends
+ * Transport.ConnectionInfo to hold the runtime.port used to connect.
+ * @param {runtime.port} port The runtime.port object.
+ */
+RuntimePortTransport.ConnectionInfo = function RuntimePortTransportConnectionInfo
+ (port)
+{
+ // Call the base constructor.
+ Transport.ConnectionInfo .call(this);
+
+ this.port = port;
+};
+
+RuntimePortTransport.ConnectionInfo.prototype = new Transport.ConnectionInfo();
+RuntimePortTransport.ConnectionInfo.prototype.name = "RuntimePortTransport.ConnectionInfo";
+
+/**
+ * Check if the fields of this RuntimePortTransport.ConnectionInfo equal the other
+ * RuntimePortTransport.ConnectionInfo.
+ * @param {RuntimePortTransport.ConnectionInfo} The other object to check.
+ * @return {boolean} True if the objects have equal fields, false if not.
+ */
+RuntimePortTransport.ConnectionInfo.prototype.equals = function(other)
+{
+ if (other == null || other.port == undefined)
+ return false;
+ return this.port == other.port;
+};
+
+RuntimePortTransport.ConnectionInfo.prototype.toString = function()
+{
+ return "{}";
+};
+
+/**
+ * Set the onReceivedObject callback, replacing any previous callback.
+ * @param {function} onReceivedObject (optional) If supplied and the received
+ * object type field is not "Buffer" then just call this.onReceivedObject(obj).
+ * If this is null, then don't call it.
+ */
+RuntimePortTransport.prototype.setOnReceivedObject = function(onReceivedObject)
+{
+ this.onReceivedObject = onReceivedObject;
+}
+
+/**
+ * Determine whether this transport connecting according to connectionInfo is to
+ * a node on the current machine. RuntimePortTransport is always local.
+ * @param {RuntimePortTransport.ConnectionInfo} connectionInfo This is ignored.
+ * @param {function} onResult This calls onResult(true) because a runtime.port
+ * is always local.
+ * @param {function} onError This is ignored.
+ */
+RuntimePortTransport.prototype.isLocal = function(connectionInfo, onResult, onError)
+{
+ onResult(true);
+};
+
+/**
+ * Connect to the runtime.port in connectionInfo. For a received object obj, if
+ * obj.type is "Buffer", read an entire packet element from obj.data and call
+ * elementListener.onReceivedElement(element). Otherwise just call
+ * onReceivedObject(obj) using the callback given to the constructor.
+ * @param {RuntimePortTransport.ConnectionInfo} connectionInfo The
+ * ConnectionInfo with the runtime.port.
+ * @param {object} elementListener The elementListener with function
+ * onReceivedElement which must remain valid during the life of this object.
+ * @param {function} onOpenCallback Once connected, call onOpenCallback().
+ * @param {function} onClosedCallback (optional) If the connection is closed by
+ * the remote host, call onClosedCallback(). If omitted or null, don't call it.
+ */
+RuntimePortTransport.prototype.connect = function
+ (connectionInfo, elementListener, onOpenCallback, onClosedCallback)
+{
+ // The window listener is already set up.
+ this.elementReader = new ElementReader(elementListener);
+ this.connectionInfo = connectionInfo;
+ this.port = this.connectionInfo.port;
+
+ // Add a listener to wait for a message object from the tab
+ var thisTransport = this;
+ this.port.onMessage.addListener(function(obj) {
+ if (obj.type == "Buffer")
+ thisTransport.elementReader.onReceivedData
+ (Buffer.isBuffer(obj.data) ? obj.data : new Buffer(obj.data));
+ else {
+ if (thisTransport.onReceivedObject != null)
+ thisTransport.onReceivedObject(obj);
+ }
+ });
+
+ this.port.onDisconnect.addListener(function() {
+ thisTransport.port = null;
+ if (onClosedCallback != null)
+ onClosedCallback();
+ });
+
+ onOpenCallback();
+};
+
+/**
+ * Send the JavaScript object over the connection created by connect.
+ * @param {object} obj The object to send. If it is a JSON Buffer then it is
+ * processed like an NDN packet.
+ */
+RuntimePortTransport.prototype.sendObject = function(obj)
+{
+ if (this.port == null) {
+ console.log("RuntimePortTransport connection is not established.");
+ return;
+ }
+
+ this.port.postMessage(obj);
+};
+
+/**
+ * Send the buffer over the connection created by connect.
+ * @param {Buffer} buffer The bytes to send.
+ */
+RuntimePortTransport.prototype.send = function(buffer)
+{
+ this.sendObject(buffer.toJSON());
+};
+/**
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Wentao Shang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var ElementReader = require('../encoding/element-reader.js').ElementReader; /** @ignore */
+var LOG = require('../log.js').Log.LOG; /** @ignore */
+var Transport = require('./transport.js').Transport; /** @ignore */
+var Face;
+
+/**
+ * @constructor
+ */
+var WebSocketTransport = function WebSocketTransport()
+{
+ // Call the base constructor.
+ Transport.call(this);
+
+ if (!WebSocket)
+ throw new Error("WebSocket support is not available on this platform.");
+
+ this.ws = null;
+ this.connectionInfo = null; // Read by Face.
+ this.elementReader = null;
+ this.defaultGetConnectionInfo = Face.makeShuffledHostGetConnectionInfo
+ (["A.ws.ndn.ucla.edu", "B.ws.ndn.ucla.edu", "C.ws.ndn.ucla.edu", "D.ws.ndn.ucla.edu",
+ "E.ws.ndn.ucla.edu", "F.ws.ndn.ucla.edu", "G.ws.ndn.ucla.edu", "H.ws.ndn.ucla.edu",
+ "I.ws.ndn.ucla.edu", "J.ws.ndn.ucla.edu", "K.ws.ndn.ucla.edu", "L.ws.ndn.ucla.edu",
+ "M.ws.ndn.ucla.edu", "N.ws.ndn.ucla.edu"],
+ 9696,
+ function(host, port) { return new WebSocketTransport.ConnectionInfo(host, port); });
+};
+
+WebSocketTransport.prototype = new Transport();
+WebSocketTransport.prototype.name = "WebSocketTransport";
+
+WebSocketTransport.importFace = function(face){
+ Face = face;
+};
+
+exports.WebSocketTransport = WebSocketTransport;
+
+/**
+ * Create a new WebSocketTransport.ConnectionInfo which extends
+ * Transport.ConnectionInfo to hold the host and port info for the WebSocket
+ * connection.
+ * @param {string} host The host for the connection. However, if the host string
+ * begins with "ws:" or "wss:", then ignore port and use the string as the full
+ * endpoint URI.
+ * @param {number} port (optional) The port number for the connection. If
+ * omitted, use 9696.
+ */
+WebSocketTransport.ConnectionInfo = function WebSocketTransportConnectionInfo
+ (host, port)
+{
+ // Call the base constructor.
+ Transport.ConnectionInfo .call(this);
+
+ port = (port !== undefined ? port : 9696);
+
+ this.host = host;
+ this.port = port;
+};
+
+WebSocketTransport.ConnectionInfo.prototype = new Transport.ConnectionInfo();
+WebSocketTransport.ConnectionInfo.prototype.name = "WebSocketTransport.ConnectionInfo";
+
+/**
+ * Check if the fields of this WebSocketTransport.ConnectionInfo equal the other
+ * WebSocketTransport.ConnectionInfo.
+ * @param {WebSocketTransport.ConnectionInfo} The other object to check.
+ * @return {boolean} True if the objects have equal fields, false if not.
+ */
+WebSocketTransport.ConnectionInfo.prototype.equals = function(other)
+{
+ if (other == null || other.host == undefined || other.port == undefined)
+ return false;
+ return this.host == other.host && this.port == other.port;
+};
+
+WebSocketTransport.ConnectionInfo.prototype.toString = function()
+{
+ if (this.hostIsUri())
+ return "{ uri: " + this.host + " }";
+ else
+ return "{ host: " + this.host + ", port: " + this.port + " }";
+};
+
+WebSocketTransport.ConnectionInfo.prototype.hostIsUri = function()
+{
+ return this.host.substr(0, 3) == "ws:" ||
+ this.host.substr(0, 4) == "wss:";
+}
+
+/**
+ * Determine whether this transport connecting according to connectionInfo is to
+ * a node on the current machine. WebSocket transports are always non-local.
+ * @param {WebSocketTransport.ConnectionInfo} connectionInfo This is ignored.
+ * @param {function} onResult This calls onResult(false) because WebSocket
+ * transports are always non-local.
+ * @param {function} onError This is ignored.
+ */
+WebSocketTransport.prototype.isLocal = function(connectionInfo, onResult, onError)
+{
+ onResult(false);
+};
+
+/**
+ * Connect to a WebSocket according to the info in connectionInfo. Listen on
+ * the port to read an entire packet element and call
+ * elementListener.onReceivedElement(element). Note: this connect method
+ * previously took a Face object which is deprecated and renamed as the method
+ * connectByFace.
+ * @param {WebSocketTransport.ConnectionInfo} connectionInfo A
+ * WebSocketTransport.ConnectionInfo.
+ * @param {object} elementListener The elementListener with function
+ * onReceivedElement which must remain valid during the life of this object.
+ * @param {function} onopenCallback Once connected, call onopenCallback().
+ * @param {function} onclosedCallback (optional) If the connection is closed by
+ * the remote host, call onclosedCallback(). If omitted or null, don't call it.
+ */
+WebSocketTransport.prototype.connect = function
+ (connectionInfo, elementListener, onopenCallback, onclosedCallback)
+{
+ this.close();
+
+ var uri = connectionInfo.hostIsUri() ?
+ connectionInfo.host : 'ws://' + connectionInfo.host + ':' + connectionInfo.port;
+ this.ws = new WebSocket(uri);
+ if (LOG > 0) console.log('ws connection created.');
+ this.connectionInfo = connectionInfo;
+
+ this.ws.binaryType = "arraybuffer";
+
+ this.elementReader = new ElementReader(elementListener);
+ var self = this;
+ this.ws.onmessage = function(ev) {
+ var result = ev.data;
+ //console.log('RecvHandle called.');
+
+ if (result == null || result == undefined || result == "") {
+ console.log('INVALID ANSWER');
+ }
+ else if (result instanceof ArrayBuffer) {
+ // The Buffer constructor expects an instantiated array.
+ var bytearray = new Buffer(new Uint8Array(result));
+
+ if (LOG > 3) console.log('BINARY RESPONSE IS ' + bytearray.toString('hex'));
+
+ try {
+ // Find the end of the element and call onReceivedElement.
+ self.elementReader.onReceivedData(bytearray);
+ } catch (ex) {
+ console.log("NDN.ws.onmessage exception: " + ex);
+ return;
+ }
+ }
+ }
+
+ this.ws.onopen = function(ev) {
+ if (LOG > 3) console.log(ev);
+ if (LOG > 3) console.log('ws.onopen: WebSocket connection opened.');
+ if (LOG > 3) console.log('ws.onopen: ReadyState: ' + this.readyState);
+ // Face.registerPrefix will fetch the ndndid when needed.
+
+ onopenCallback();
+ }
+
+ this.ws.onerror = function(ev) {
+ console.log('ws.onerror: ReadyState: ' + this.readyState);
+ console.log(ev);
+ console.log('ws.onerror: WebSocket error: ' + ev.data);
+ }
+
+ this.ws.onclose = function(ev) {
+ console.log('ws.onclose: WebSocket connection closed.');
+ self.ws = null;
+
+ if (onclosedCallback != null)
+ onclosedCallback();
+ }
+};
+
+/**
+ * @deprecated This is deprecated. You should not call Transport.connect
+ * directly, since it is called by Face methods.
+ */
+WebSocketTransport.prototype.connectByFace = function(face, onopenCallback)
+{
+ this.connect
+ (face.connectionInfo, face, onopenCallback,
+ function() { face.closeByTransport(); });
+};
+
+/**
+ * Send the Uint8Array data.
+ */
+WebSocketTransport.prototype.send = function(data)
+{
+ if (this.ws != null) {
+ // If we directly use data.buffer to feed ws.send(),
+ // WebSocket may end up sending a packet with 10000 bytes of data.
+ // That is, WebSocket will flush the entire buffer
+ // regardless of the offset of the Uint8Array. So we have to create
+ // a new Uint8Array buffer with just the right size and copy the
+ // content from binaryInterest to the new buffer.
+ // ---Wentao
+ var bytearray = new Uint8Array(data.length);
+ bytearray.set(data);
+ this.ws.send(bytearray.buffer);
+ if (LOG > 3) console.log('ws.send() returned.');
+ }
+ else
+ console.log('WebSocket connection is not established.');
+};
+
+/**
+ * Close the connection.
+ */
+WebSocketTransport.prototype.close = function()
+{
+ if (this.ws != null)
+ delete this.ws;
+}
+
+/**
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// The Face constructor uses TcpTransport by default which is not available in the browser, so override to WebSocketTransport.
+exports.TcpTransport = require("./transport/web-socket-transport").WebSocketTransport;
+/**
+ * This class represents a Name as an array of components where each is a byte array.
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Meki Cheraoui, Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('./util/blob.js').Blob; /** @ignore */
+var DataUtils = require('./encoding/data-utils.js').DataUtils; /** @ignore */
+var LOG = require('./log.js').Log.LOG;
+var DecodingException = require('./encoding/decoding-exception.js').DecodingException;
+
+/**
+ * Create a new Name from components.
+ *
+ * @constructor
+ * @param {string|Name|Array<string|Array<number>|ArrayBuffer|Buffer|Name>} components if a string, parse it as a URI. If a Name, add a deep copy of its components.
+ * Otherwise it is an array of components which are appended according to Name.append, so
+ * convert each and store it as an array of Buffer. If a component is a string, encode as utf8.
+ */
+var Name = function Name(components)
+{
+ if (typeof components == 'string') {
+ if (LOG > 3) console.log('Content Name String ' + components);
+ this.components = Name.createNameArray(components);
+ }
+ else if (typeof components === 'object') {
+ this.components = [];
+ if (components instanceof Name)
+ this.append(components);
+ else {
+ for (var i = 0; i < components.length; ++i)
+ this.append(components[i]);
+ }
+ }
+ else if (components == null)
+ this.components = [];
+ else
+ if (LOG > 1) console.log("NO CONTENT NAME GIVEN");
+
+ this.changeCount = 0;
+};
+
+exports.Name = Name;
+
+/**
+ * Create a new GENERIC Name.Component with a copy of the given value.
+ * (To create an ImplicitSha256Digest component, use fromImplicitSha256Digest.)
+ * @param {Name.Component|String|Array<number>|ArrayBuffer|Buffer} value If the value is a string, encode it as utf8 (but don't unescape).
+ * @constructor
+ */
+Name.Component = function NameComponent(value)
+{
+ if (typeof value === 'object' && value instanceof Name.Component) {
+ // The copy constructor.
+ this.value_ = value.value_;
+ this.type_ = value.type_;
+ return;
+ }
+
+ if (!value)
+ this.value_ = new Blob([]);
+ else if (typeof value === 'object' && typeof ArrayBuffer !== 'undefined' &&
+ value instanceof ArrayBuffer)
+ // Make a copy. Turn the value into a Uint8Array since the Buffer
+ // constructor doesn't take an ArrayBuffer.
+ this.value_ = new Blob(new Buffer(new Uint8Array(value)), false);
+ else if (typeof value === 'object' && value instanceof Blob)
+ this.value_ = value;
+ else
+ // Blob will make a copy if needed.
+ this.value_ = new Blob(value);
+
+ this.type_ = Name.Component.ComponentType.GENERIC;
+};
+
+/**
+ * A Name.Component.ComponentType specifies the recognized types of a name
+ * component.
+ */
+Name.Component.ComponentType = {
+ IMPLICIT_SHA256_DIGEST: 1,
+ GENERIC: 8
+};
+
+/**
+ * Get the component value.
+ * @return {Blob} The component value.
+ */
+Name.Component.prototype.getValue = function()
+{
+ return this.value_;
+};
+
+/**
+ * @deprecated Use getValue. This method returns a Buffer which is the former
+ * behavior of getValue, and should only be used while updating your code.
+ */
+Name.Component.prototype.getValueAsBuffer = function()
+{
+ // Assume the caller won't modify it.
+ return this.value_.buf();
+};
+
+/**
+ * @deprecated Use getValue which returns a Blob.
+ */
+Object.defineProperty(Name.Component.prototype, "value",
+ { get: function() { return this.getValueAsBuffer(); } });
+
+/**
+ * Convert this component value to a string by escaping characters according to the NDN URI Scheme.
+ * This also adds "..." to a value with zero or more ".".
+ * This adds a type code prefix as needed, such as "sha256digest=".
+ * @return {string} The escaped string.
+ */
+Name.Component.prototype.toEscapedString = function()
+{
+ if (this.type_ === Name.Component.ComponentType.IMPLICIT_SHA256_DIGEST)
+ return "sha256digest=" + this.value_.toHex();
+ else
+ return Name.toEscapedString(this.value_.buf());
+};
+
+/**
+ * Check if this component is a segment number according to NDN naming
+ * conventions for "Segment number" (marker 0x00).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @return {number} True if this is a segment number.
+ */
+Name.Component.prototype.isSegment = function()
+{
+ return this.value_.size() >= 1 && this.value_.buf()[0] == 0x00 &&
+ this.isGeneric();
+};
+
+/**
+ * Check if this component is a segment byte offset according to NDN
+ * naming conventions for segment "Byte offset" (marker 0xFB).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @return True if this is a segment byte offset.
+ */
+Name.Component.prototype.isSegmentOffset = function()
+{
+ return this.value_.size() >= 1 && this.value_.buf()[0] == 0xFB &&
+ this.isGeneric();
+};
+
+/**
+ * Check if this component is a version number according to NDN naming
+ * conventions for "Versioning" (marker 0xFD).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @return {number} True if this is a version number.
+ */
+Name.Component.prototype.isVersion = function()
+{
+ return this.value_.size() >= 1 && this.value_.buf()[0] == 0xFD &&
+ this.isGeneric();
+};
+
+/**
+ * Check if this component is a timestamp according to NDN naming
+ * conventions for "Timestamp" (marker 0xFC).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @return True if this is a timestamp.
+ */
+Name.Component.prototype.isTimestamp = function()
+{
+ return this.value_.size() >= 1 && this.value_.buf()[0] == 0xFC &&
+ this.isGeneric();
+};
+
+/**
+ * Check if this component is a sequence number according to NDN naming
+ * conventions for "Sequencing" (marker 0xFE).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @return True if this is a sequence number.
+ */
+Name.Component.prototype.isSequenceNumber = function()
+{
+ return this.value_.size() >= 1 && this.value_.buf()[0] == 0xFE &&
+ this.isGeneric();
+};
+
+/**
+ * Check if this component is a generic component.
+ * @return {boolean} True if this is an generic component.
+ */
+Name.Component.prototype.isGeneric = function()
+{
+ return this.type_ === Name.Component.ComponentType.GENERIC;
+};
+
+/**
+ * Check if this component is an ImplicitSha256Digest component.
+ * @return {boolean} True if this is an ImplicitSha256Digest component.
+ */
+Name.Component.prototype.isImplicitSha256Digest = function()
+{
+ return this.type_ === Name.Component.ComponentType.IMPLICIT_SHA256_DIGEST;
+};
+
+/**
+ * Interpret this name component as a network-ordered number and return an integer.
+ * @return {number} The integer number.
+ */
+Name.Component.prototype.toNumber = function()
+{
+ return DataUtils.bigEndianToUnsignedInt(this.value_.buf());
+};
+
+/**
+ * Interpret this name component as a network-ordered number with a marker and
+ * return an integer.
+ * @param {number} marker The required first byte of the component.
+ * @return {number} The integer number.
+ * @throws Error If the first byte of the component does not equal the marker.
+ */
+Name.Component.prototype.toNumberWithMarker = function(marker)
+{
+ if (this.value_.size() == 0 || this.value_.buf()[0] != marker)
+ throw new Error("Name component does not begin with the expected marker");
+
+ return DataUtils.bigEndianToUnsignedInt(this.value_.buf().slice(1));
+};
+
+/**
+ * Interpret this name component as a segment number according to NDN naming
+ * conventions for "Segment number" (marker 0x00).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @return {number} The integer segment number.
+ * @throws Error If the first byte of the component is not the expected marker.
+ */
+Name.Component.prototype.toSegment = function()
+{
+ return this.toNumberWithMarker(0x00);
+};
+
+/**
+ * Interpret this name component as a segment byte offset according to NDN
+ * naming conventions for segment "Byte offset" (marker 0xFB).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @return The integer segment byte offset.
+ * @throws Error If the first byte of the component is not the expected marker.
+ */
+Name.Component.prototype.toSegmentOffset = function()
+{
+ return this.toNumberWithMarker(0xFB);
+};
+
+/**
+ * Interpret this name component as a version number according to NDN naming
+ * conventions for "Versioning" (marker 0xFD). Note that this returns
+ * the exact number from the component without converting it to a time
+ * representation.
+ * @return {number} The integer version number.
+ * @throws Error If the first byte of the component is not the expected marker.
+ */
+Name.Component.prototype.toVersion = function()
+{
+ return this.toNumberWithMarker(0xFD);
+};
+
+/**
+ * Interpret this name component as a timestamp according to NDN naming
+ * conventions for "Timestamp" (marker 0xFC).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @return The number of microseconds since the UNIX epoch (Thursday,
+ * 1 January 1970) not counting leap seconds.
+ * @throws Error If the first byte of the component is not the expected marker.
+ */
+Name.Component.prototype.toTimestamp = function()
+{
+ return this.toNumberWithMarker(0xFC);
+};
+
+/**
+ * Interpret this name component as a sequence number according to NDN naming
+ * conventions for "Sequencing" (marker 0xFE).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @return The integer sequence number.
+ * @throws Error If the first byte of the component is not the expected marker.
+ */
+Name.Component.prototype.toSequenceNumber = function()
+{
+ return this.toNumberWithMarker(0xFE);
+};
+
+/**
+ * Create a component whose value is the nonNegativeInteger encoding of the
+ * number.
+ * @param {number} number
+ * @return {Name.Component}
+ */
+Name.Component.fromNumber = function(number)
+{
+ var encoder = new TlvEncoder(8);
+ encoder.writeNonNegativeInteger(number);
+ return new Name.Component(new Blob(encoder.getOutput(), false));
+};
+
+/**
+ * Create a component whose value is the marker appended with the
+ * nonNegativeInteger encoding of the number.
+ * @param {number} number
+ * @param {number} marker
+ * @return {Name.Component}
+ */
+Name.Component.fromNumberWithMarker = function(number, marker)
+{
+ var encoder = new TlvEncoder(9);
+ // Encode backwards.
+ encoder.writeNonNegativeInteger(number);
+ encoder.writeNonNegativeInteger(marker);
+ return new Name.Component(new Blob(encoder.getOutput(), false));
+};
+
+/**
+ * Create a component with the encoded segment number according to NDN
+ * naming conventions for "Segment number" (marker 0x00).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * param {number} segment The segment number.
+ * returns {Name.Component} The new Component.
+ */
+Name.Component.fromSegment = function(segment)
+{
+ return Name.Component.fromNumberWithMarker(segment, 0x00);
+};
+
+/**
+ * Create a component with the encoded segment byte offset according to NDN
+ * naming conventions for segment "Byte offset" (marker 0xFB).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * param {number} segmentOffset The segment byte offset.
+ * returns {Name.Component} The new Component.
+ */
+Name.Component.fromSegmentOffset = function(segmentOffset)
+{
+ return Name.Component.fromNumberWithMarker(segmentOffset, 0xFB);
+};
+
+/**
+ * Create a component with the encoded version number according to NDN
+ * naming conventions for "Versioning" (marker 0xFD).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * Note that this encodes the exact value of version without converting from a
+ * time representation.
+ * param {number} version The version number.
+ * returns {Name.Component} The new Component.
+ */
+Name.Component.fromVersion = function(version)
+{
+ return Name.Component.fromNumberWithMarker(version, 0xFD);
+};
+
+/**
+ * Create a component with the encoded timestamp according to NDN naming
+ * conventions for "Timestamp" (marker 0xFC).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * param {number} timestamp The number of microseconds since the UNIX epoch (Thursday,
+ * 1 January 1970) not counting leap seconds.
+ * returns {Name.Component} The new Component.
+ */
+Name.Component.fromTimestamp = function(timestamp)
+{
+ return Name.Component.fromNumberWithMarker(timestamp, 0xFC);
+};
+
+/**
+ * Create a component with the encoded sequence number according to NDN naming
+ * conventions for "Sequencing" (marker 0xFE).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * param {number} sequenceNumber The sequence number.
+ * returns {Name.Component} The new Component.
+ */
+Name.Component.fromSequenceNumber = function(sequenceNumber)
+{
+ return Name.Component.fromNumberWithMarker(sequenceNumber, 0xFE);
+};
+
+/**
+ * Create a component of type ImplicitSha256DigestComponent, so that
+ * isImplicitSha256Digest() is true.
+ * @param {Blob|Buffer} digest The SHA-256 digest value.
+ * @return {Name.Component} The new Component.
+ * @throws DecodingException If the digest length is not 32 bytes.
+ */
+Name.Component.fromImplicitSha256Digest = function(digest)
+{
+ digestBlob = typeof digest === 'object' && digest instanceof Blob ?
+ digest : new Blob(digest, true);
+ if (digestBlob.size() !== 32)
+ throw new DecodingException
+ ("Name.Component.fromImplicitSha256Digest: The digest length must be 32 bytes");
+
+ var result = new Name.Component(digestBlob);
+ result.type_ = Name.Component.ComponentType.IMPLICIT_SHA256_DIGEST;
+ return result;
+};
+
+/**
+ * Get the successor of this component, as described in Name.getSuccessor.
+ * @return {Name.Component} A new Name.Component which is the successor of this.
+ */
+Name.Component.prototype.getSuccessor = function()
+{
+ // Allocate an extra byte in case the result is larger.
+ var result = new Buffer(this.value_.size() + 1);
+
+ var carry = true;
+ for (var i = this.value_.size() - 1; i >= 0; --i) {
+ if (carry) {
+ result[i] = (this.value_.buf()[i] + 1) & 0xff;
+ carry = (result[i] === 0);
+ }
+ else
+ result[i] = this.value_.buf()[i];
+ }
+
+ if (carry)
+ // Assume all the bytes were set to zero (or the component was empty). In
+ // NDN ordering, carry does not mean to prepend a 1, but to make a component
+ // one byte longer of all zeros.
+ result[result.length - 1] = 0;
+ else
+ // We didn't need the extra byte.
+ result = result.slice(0, this.value_.size());
+
+ return new Name.Component(new Blob(result, false));
+};
+
+/**
+ * Check if this is the same component as other.
+ * @param {Name.Component} other The other Component to compare with.
+ * @return {Boolean} true if the components are equal, otherwise false.
+ */
+Name.Component.prototype.equals = function(other)
+{
+ return typeof other === 'object' && other instanceof Name.Component &&
+ this.value_.equals(other.value_) && this.type_ === other.type_;
+};
+
+/**
+ * Compare this to the other Component using NDN canonical ordering.
+ * @param {Name.Component} other The other Component to compare with.
+ * @return {number} 0 if they compare equal, -1 if this comes before other in
+ * the canonical ordering, or 1 if this comes after other in the canonical
+ * ordering.
+ *
+ * @see http://named-data.net/doc/0.2/technical/CanonicalOrder.html
+ */
+Name.Component.prototype.compare = function(other)
+{
+ if (this.type_ < other.type_)
+ return -1;
+ if (this.type_ > other.type_)
+ return 1;
+
+ return Name.Component.compareBuffers(this.value_.buf(), other.value_.buf());
+};
+
+/**
+ * Do the work of Name.Component.compare to compare the component buffers.
+ * @param {Buffer} component1
+ * @param {Buffer} component2
+ * @return {number} 0 if they compare equal, -1 if component1 comes before
+ * component2 in the canonical ordering, or 1 if component1 comes after
+ * component2 in the canonical ordering.
+ */
+Name.Component.compareBuffers = function(component1, component2)
+{
+ if (component1.length < component2.length)
+ return -1;
+ if (component1.length > component2.length)
+ return 1;
+
+ for (var i = 0; i < component1.length; ++i) {
+ if (component1[i] < component2[i])
+ return -1;
+ if (component1[i] > component2[i])
+ return 1;
+ }
+
+ return 0;
+};
+
+/**
+ * @deprecated Use toUri.
+ */
+Name.prototype.getName = function()
+{
+ return this.toUri();
+};
+
+/** Parse uri as a URI and return an array of Buffer components.
+ */
+Name.createNameArray = function(uri)
+{
+ uri = uri.trim();
+ if (uri.length <= 0)
+ return [];
+
+ var iColon = uri.indexOf(':');
+ if (iColon >= 0) {
+ // Make sure the colon came before a '/'.
+ var iFirstSlash = uri.indexOf('/');
+ if (iFirstSlash < 0 || iColon < iFirstSlash)
+ // Omit the leading protocol such as ndn:
+ uri = uri.substr(iColon + 1, uri.length - iColon - 1).trim();
+ }
+
+ if (uri[0] == '/') {
+ if (uri.length >= 2 && uri[1] == '/') {
+ // Strip the authority following "//".
+ var iAfterAuthority = uri.indexOf('/', 2);
+ if (iAfterAuthority < 0)
+ // Unusual case: there was only an authority.
+ return [];
+ else
+ uri = uri.substr(iAfterAuthority + 1, uri.length - iAfterAuthority - 1).trim();
+ }
+ else
+ uri = uri.substr(1, uri.length - 1).trim();
+ }
+
+ var array = uri.split('/');
+
+ // Unescape the components.
+ var sha256digestPrefix = "sha256digest=";
+ for (var i = 0; i < array.length; ++i) {
+ var component;
+ if (array[i].substr(0, sha256digestPrefix.length) == sha256digestPrefix) {
+ var hexString = array[i].substr(sha256digestPrefix.length).trim();
+ component = Name.Component.fromImplicitSha256Digest
+ (new Blob(new Buffer(hexString, 'hex')), false);
+ }
+ else
+ component = new Name.Component(Name.fromEscapedString(array[i]));
+
+ if (component.getValue().isNull()) {
+ // Ignore the illegal componenent. This also gets rid of a trailing '/'.
+ array.splice(i, 1);
+ --i;
+ continue;
+ }
+ else
+ array[i] = component;
+ }
+
+ return array;
+};
+
+/**
+ * Parse the uri according to the NDN URI Scheme and set the name with the
+ * components.
+ * @param {string} uri The URI string.
+ */
+Name.prototype.set = function(uri)
+{
+ this.components = Name.createNameArray(uri);
+ ++this.changeCount;
+};
+
+/**
+ * Convert the component to a Buffer and append a GENERIC component to this Name.
+ * Return this Name object to allow chaining calls to add.
+ * @param {Name.Component|String|Array<number>|ArrayBuffer|Buffer|Name} component If a component is a string, encode as utf8 (but don't unescape).
+ * @return {Name}
+ */
+Name.prototype.append = function(component)
+{
+ if (typeof component == 'object' && component instanceof Name) {
+ var components;
+ if (component == this)
+ // special case, when we need to create a copy
+ components = this.components.slice(0, this.components.length);
+ else
+ components = component.components;
+
+ for (var i = 0; i < components.length; ++i)
+ this.components.push(new Name.Component(components[i]));
+ }
+ else if (typeof component === 'object' && component instanceof Name.Component)
+ // The Component is immutable, so use it as is.
+ this.components.push(component);
+ else
+ // Just use the Name.Component constructor.
+ this.components.push(new Name.Component(component));
+
+ ++this.changeCount;
+ return this;
+};
+
+/**
+ * @deprecated Use append.
+ */
+Name.prototype.add = function(component)
+{
+ return this.append(component);
+};
+
+/**
+ * Clear all the components.
+ */
+Name.prototype.clear = function()
+{
+ this.components = [];
+ ++this.changeCount;
+};
+
+/**
+ * Return the escaped name string according to NDN URI Scheme.
+ * @param {boolean} includeScheme (optional) If true, include the "ndn:" scheme
+ * in the URI, e.g. "ndn:/example/name". If false, just return the path, e.g.
+ * "/example/name". If ommitted, then just return the path which is the default
+ * case where toUri() is used for display.
+ * @return {String}
+ */
+Name.prototype.toUri = function(includeScheme)
+{
+ if (this.size() == 0)
+ return includeScheme ? "ndn:/" : "/";
+
+ var result = includeScheme ? "ndn:" : "";
+
+ for (var i = 0; i < this.size(); ++i)
+ result += "/"+ this.components[i].toEscapedString();
+
+ return result;
+};
+
+/**
+ * @deprecated Use toUri.
+ */
+Name.prototype.to_uri = function()
+{
+ return this.toUri();
+};
+
+Name.prototype.toString = function() { return this.toUri(); }
+
+/**
+ * Append a component with the encoded segment number according to NDN
+ * naming conventions for "Segment number" (marker 0x00).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @param {number} segment The segment number.
+ * @return {Name} This name so that you can chain calls to append.
+ */
+Name.prototype.appendSegment = function(segment)
+{
+ return this.append(Name.Component.fromSegment(segment));
+};
+
+/**
+ * Append a component with the encoded segment byte offset according to NDN
+ * naming conventions for segment "Byte offset" (marker 0xFB).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @param {number} segmentOffset The segment byte offset.
+ * @return {Name} This name so that you can chain calls to append.
+ */
+Name.prototype.appendSegmentOffset = function(segmentOffset)
+{
+ return this.append(Name.Component.fromSegmentOffset(segmentOffset));
+};
+
+/**
+ * Append a component with the encoded version number according to NDN
+ * naming conventions for "Versioning" (marker 0xFD).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * Note that this encodes the exact value of version without converting from a time representation.
+ * @param {number} version The version number.
+ * @return {Name} This name so that you can chain calls to append.
+ */
+Name.prototype.appendVersion = function(version)
+{
+ return this.append(Name.Component.fromVersion(version));
+};
+
+/**
+ * Append a component with the encoded timestamp according to NDN naming
+ * conventions for "Timestamp" (marker 0xFC).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @param {number} timestamp The number of microseconds since the UNIX epoch (Thursday,
+ * 1 January 1970) not counting leap seconds.
+ * @return This name so that you can chain calls to append.
+ */
+Name.prototype.appendTimestamp = function(timestamp)
+{
+ return this.append(Name.Component.fromTimestamp(timestamp));
+};
+
+/**
+ * Append a component with the encoded sequence number according to NDN naming
+ * conventions for "Sequencing" (marker 0xFE).
+ * http://named-data.net/doc/tech-memos/naming-conventions.pdf
+ * @param {number} sequenceNumber The sequence number.
+ * @return This name so that you can chain calls to append.
+ */
+Name.prototype.appendSequenceNumber = function(sequenceNumber)
+{
+ return this.append(Name.Component.fromSequenceNumber(sequenceNumber));
+};
+
+/**
+ * Append a component of type ImplicitSha256DigestComponent, so that
+ * isImplicitSha256Digest() is true.
+ * @param {Blob|Buffer} digest The SHA-256 digest value.
+ * @return This name so that you can chain calls to append.
+ * @throws DecodingException If the digest length is not 32 bytes.
+ */
+Name.prototype.appendImplicitSha256Digest = function(digest)
+{
+ return this.append(Name.Component.fromImplicitSha256Digest(digest));
+};
+
+/**
+ * @deprecated Use appendSegment.
+ */
+Name.prototype.addSegment = function(number)
+{
+ return this.appendSegment(number);
+};
+
+/**
+ * Get a new name, constructed as a subset of components.
+ * @param {number} iStartComponent The index if the first component to get. If
+ * iStartComponent is -N then return return components starting from
+ * name.size() - N.
+ * @param {number} (optional) nComponents The number of components starting at
+ * iStartComponent. If omitted or greater than the size of this name, get until
+ * the end of the name.
+ * @return {Name} A new name.
+ */
+Name.prototype.getSubName = function(iStartComponent, nComponents)
+{
+ if (iStartComponent < 0)
+ iStartComponent = this.components.length - (-iStartComponent);
+
+ if (nComponents == undefined)
+ nComponents = this.components.length - iStartComponent;
+
+ var result = new Name();
+
+ var iEnd = iStartComponent + nComponents;
+ for (var i = iStartComponent; i < iEnd && i < this.components.length; ++i)
+ result.components.push(this.components[i]);
+
+ return result;
+};
+
+/**
+ * Return a new Name with the first nComponents components of this Name.
+ * @param {number} nComponents The number of prefix components. If nComponents is -N then return the prefix up
+ * to name.size() - N. For example getPrefix(-1) returns the name without the final component.
+ * @return {Name} A new name.
+ */
+Name.prototype.getPrefix = function(nComponents)
+{
+ if (nComponents < 0)
+ return this.getSubName(0, this.components.length + nComponents);
+ else
+ return this.getSubName(0, nComponents);
+};
+
+/**
+ * @deprecated Use getPrefix(-nComponents).
+ */
+Name.prototype.cut = function(nComponents)
+{
+ return new Name(this.components.slice(0, this.components.length - nComponents));
+};
+
+/**
+ * Return the number of name components.
+ * @return {number}
+ */
+Name.prototype.size = function()
+{
+ return this.components.length;
+};
+
+/**
+ * Get a Name Component by index number.
+ * @param {Number} i The index of the component, starting from 0. However, if i is negative, return the component
+ * at size() - (-i).
+ * @return {Name.Component} The name component at the index. You must not
+ * change the returned Name.Component object.
+ */
+Name.prototype.get = function(i)
+{
+ if (i >= 0) {
+ if (i >= this.components.length)
+ throw new Error("Name.get: Index is out of bounds");
+
+ return this.components[i];
+ }
+ else {
+ // Negative index.
+ if (i < -this.components.length)
+ throw new Error("Name.get: Index is out of bounds");
+
+ return this.components[this.components.length - (-i)];
+ }
+};
+
+/**
+ * @deprecated Use size().
+ */
+Name.prototype.getComponentCount = function()
+{
+ return this.components.length;
+};
+
+/**
+ * @deprecated To get just the component value array, use get(i).getValue().buf().
+ */
+Name.prototype.getComponent = function(i)
+{
+ return new Buffer(this.components[i].getValue().buf());
+};
+
+/**
+ * The "file name" in a name is the last component that isn't blank and doesn't start with one of the
+ * special marker octets (for version, etc.). Return the index in this.components of
+ * the file name, or -1 if not found.
+ */
+Name.prototype.indexOfFileName = function()
+{
+ for (var i = this.size() - 1; i >= 0; --i) {
+ var component = this.components[i].getValue().buf();
+ if (component.length <= 0)
+ continue;
+
+ if (component[0] == 0 || component[0] == 0xC0 || component[0] == 0xC1 ||
+ (component[0] >= 0xF5 && component[0] <= 0xFF))
+ continue;
+
+ return i;
+ }
+
+ return -1;
+};
+
+/**
+ * Encode this Name for a particular wire format.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {Blob} The encoded buffer in a Blob object.
+ */
+Name.prototype.wireEncode = function(wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ return wireFormat.encodeName(this);
+};
+
+/**
+ * Decode the input using a particular wire format and update this Name.
+ * @param {Blob|Buffer} input The buffer with the bytes to decode.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to decode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ */
+Name.prototype.wireDecode = function(input, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ if (typeof input === 'object' && input instanceof Blob)
+ // Input is a blob, so get its buf() and set copy false.
+ wireFormat.decodeName(this, input.buf(), false);
+ else
+ wireFormat.decodeName(this, input, true);
+};
+
+/**
+ * Compare this to the other Name using NDN canonical ordering. If the first
+ * components of each name are not equal, this returns -1 if the first comes
+ * before the second using the NDN canonical ordering for name components, or 1
+ * if it comes after. If they are equal, this compares the second components of
+ * each name, etc. If both names are the same up to the size of the shorter
+ * name, this returns -1 if the first name is shorter than the second or 1 if it
+ * is longer. For example, std::sort gives: /a/b/d /a/b/cc /c /c/a /bb . This
+ * is intuitive because all names with the prefix /a are next to each other.
+ * But it may be also be counter-intuitive because /c comes before /bb according
+ * to NDN canonical ordering since it is shorter.
+ * The first form of compare is simply compare(other). The second form is
+ * compare(iStartComponent, nComponents, other [, iOtherStartComponent] [, nOtherComponents])
+ * which is equivalent to
+ * self.getSubName(iStartComponent, nComponents).compare
+ * (other.getSubName(iOtherStartComponent, nOtherComponents)) .
+ * @param {number} iStartComponent The index if the first component of this name
+ * to get. If iStartComponent is -N then compare components starting from
+ * name.size() - N.
+ * @param {number} nComponents The number of components starting at
+ * iStartComponent. If greater than the size of this name, compare until the end
+ * of the name.
+ * @param {Name} other The other Name to compare with.
+ * @param {number} iOtherStartComponent (optional) The index if the first
+ * component of the other name to compare. If iOtherStartComponent is -N then
+ * compare components starting from other.size() - N. If omitted, compare
+ * starting from index 0.
+ * @param {number} nOtherComponents (optional) The number of components
+ * starting at iOtherStartComponent. If omitted or greater than the size of this
+ * name, compare until the end of the name.
+ * @return {number} 0 If they compare equal, -1 if self comes before other in
+ * the canonical ordering, or 1 if self comes after other in the canonical
+ * ordering.
+ * @see http://named-data.net/doc/0.2/technical/CanonicalOrder.html
+ */
+Name.prototype.compare = function
+ (iStartComponent, nComponents, other, iOtherStartComponent, nOtherComponents)
+{
+ if (iStartComponent instanceof Name) {
+ // compare(other)
+ other = iStartComponent;
+ iStartComponent = 0;
+ nComponents = this.size();
+ }
+
+ if (iOtherStartComponent == undefined)
+ iOtherStartComponent = 0;
+ if (nOtherComponents == undefined)
+ nOtherComponents = other.size();
+
+ if (iStartComponent < 0)
+ iStartComponent = this.size() - (-iStartComponent);
+ if (iOtherStartComponent < 0)
+ iOtherStartComponent = other.size() - (-iOtherStartComponent);
+
+ nComponents = Math.min(nComponents, this.size() - iStartComponent);
+ nOtherComponents = Math.min(nOtherComponents, other.size() - iOtherStartComponent);
+
+ var count = Math.min(nComponents, nOtherComponents);
+ for (var i = 0; i < count; ++i) {
+ var comparison = this.components[iStartComponent + i].compare
+ (other.components[iOtherStartComponent + i]);
+ if (comparison == 0)
+ // The components at this index are equal, so check the next components.
+ continue;
+
+ // Otherwise, the result is based on the components at this index.
+ return comparison;
+ }
+
+ // The components up to min(this.size(), other.size()) are equal, so the
+ // shorter name is less.
+ if (nComponents < nOtherComponents)
+ return -1;
+ else if (nComponents > nOtherComponents)
+ return 1;
+ else
+ return 0;
+};
+
+/**
+ * Return true if this Name has the same components as name.
+ */
+Name.prototype.equals = function(name)
+{
+ if (this.components.length != name.components.length)
+ return false;
+
+ // Start from the last component because they are more likely to differ.
+ for (var i = this.components.length - 1; i >= 0; --i) {
+ if (!this.components[i].equals(name.components[i]))
+ return false;
+ }
+
+ return true;
+};
+
+/**
+ * @deprecated Use equals.
+ */
+Name.prototype.equalsName = function(name)
+{
+ return this.equals(name);
+};
+
+/**
+ * Find the last component in name that has a ContentDigest and return the digest value as Buffer,
+ * or null if not found. See Name.getComponentContentDigestValue.
+ */
+Name.prototype.getContentDigestValue = function()
+{
+ for (var i = this.size() - 1; i >= 0; --i) {
+ var digestValue = Name.getComponentContentDigestValue(this.components[i]);
+ if (digestValue != null)
+ return digestValue;
+ }
+
+ return null;
+};
+
+/**
+ * If component is a ContentDigest, return the digest value as a Buffer slice (don't modify!).
+ * If not a ContentDigest, return null.
+ * A ContentDigest component is Name.ContentDigestPrefix + 32 bytes + Name.ContentDigestSuffix.
+ */
+Name.getComponentContentDigestValue = function(component)
+{
+ if (typeof component == 'object' && component instanceof Name.Component)
+ component = component.getValue().buf();
+
+ var digestComponentLength = Name.ContentDigestPrefix.length + 32 + Name.ContentDigestSuffix.length;
+ // Check for the correct length and equal ContentDigestPrefix and ContentDigestSuffix.
+ if (component.length == digestComponentLength &&
+ DataUtils.arraysEqual(component.slice(0, Name.ContentDigestPrefix.length),
+ Name.ContentDigestPrefix) &&
+ DataUtils.arraysEqual(component.slice
+ (component.length - Name.ContentDigestSuffix.length, component.length),
+ Name.ContentDigestSuffix))
+ return component.slice(Name.ContentDigestPrefix.length, Name.ContentDigestPrefix.length + 32);
+ else
+ return null;
+};
+
+// Meta GUID "%C1.M.G%C1" + ContentDigest with a 32 byte BLOB.
+Name.ContentDigestPrefix = new Buffer([0xc1, 0x2e, 0x4d, 0x2e, 0x47, 0xc1, 0x01, 0xaa, 0x02, 0x85]);
+Name.ContentDigestSuffix = new Buffer([0x00]);
+
+
+/**
+ * Return value as an escaped string according to NDN URI Scheme.
+ * We can't use encodeURIComponent because that doesn't encode all the
+ * characters we want to.
+ * This does not add a type code prefix such as "sha256digest=".
+ * @param {Buffer|Name.Component} value The value or Name.Component to escape.
+ * @return {string} The escaped string.
+ */
+Name.toEscapedString = function(value)
+{
+ if (typeof value == 'object' && value instanceof Name.Component)
+ value = value.getValue().buf();
+ else if (typeof value === 'object' && value instanceof Blob)
+ value = value.buf();
+
+ var result = "";
+ var gotNonDot = false;
+ for (var i = 0; i < value.length; ++i) {
+ if (value[i] != 0x2e) {
+ gotNonDot = true;
+ break;
+ }
+ }
+ if (!gotNonDot) {
+ // Special case for component of zero or more periods. Add 3 periods.
+ result = "...";
+ for (var i = 0; i < value.length; ++i)
+ result += ".";
+ }
+ else {
+ for (var i = 0; i < value.length; ++i) {
+ var x = value[i];
+ // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
+ if (x >= 0x30 && x <= 0x39 || x >= 0x41 && x <= 0x5a ||
+ x >= 0x61 && x <= 0x7a || x == 0x2b || x == 0x2d ||
+ x == 0x2e || x == 0x5f)
+ result += String.fromCharCode(x);
+ else
+ result += "%" + (x < 16 ? "0" : "") + x.toString(16).toUpperCase();
+ }
+ }
+ return result;
+};
+
+/**
+ * Make a blob value by decoding the escapedString according to NDN URI Scheme.
+ * If escapedString is "", "." or ".." then return null, which means to skip the
+ * component in the name.
+ * This does not check for a type code prefix such as "sha256digest=".
+ * @param {string} escapedString The escaped string to decode.
+ * @return {Blob} The unescaped Blob value. If the escapedString is not a valid
+ * escaped component, then the Blob isNull().
+ */
+Name.fromEscapedString = function(escapedString)
+{
+ var value = unescape(escapedString.trim());
+
+ if (value.match(/[^.]/) == null) {
+ // Special case for value of only periods.
+ if (value.length <= 2)
+ // Zero, one or two periods is illegal. Ignore this componenent to be
+ // consistent with the C implementation.
+ return new Blob();
+ else
+ // Remove 3 periods.
+ return new Blob
+ (DataUtils.toNumbersFromString(value.substr(3, value.length - 3)), false);
+ }
+ else
+ return new Blob(DataUtils.toNumbersFromString(value), false);
+};
+
+/**
+ * @deprecated Use fromEscapedString. This method returns a Buffer which is the former
+ * behavior of fromEscapedString, and should only be used while updating your code.
+ */
+Name.fromEscapedStringAsBuffer = function(escapedString)
+{
+ return Name.fromEscapedString(escapedString).buf();
+};
+
+/**
+ * Get the successor of this name which is defined as follows.
+ *
+ * N represents the set of NDN Names, and X,Y ∈ N.
+ * Operator < is defined by the NDN canonical order on N.
+ * Y is the successor of X, if (a) X < Y, and (b) ∄ Z ∈ N s.t. X < Z < Y.
+ *
+ * In plain words, the successor of a name is the same name, but with its last
+ * component advanced to a next possible value.
+ *
+ * Examples:
+ *
+ * - The successor of / is /%00
+ * - The successor of /%00%01/%01%02 is /%00%01/%01%03
+ * - The successor of /%00%01/%01%FF is /%00%01/%02%00
+ * - The successor of /%00%01/%FF%FF is /%00%01/%00%00%00
+ *
+ * @return {Name} A new name which is the successor of this.
+ */
+Name.prototype.getSuccessor = function()
+{
+ if (this.size() == 0) {
+ // Return "/%00".
+ var result = new Name();
+ result.append(new Blob(new Buffer([0]), false));
+ return result;
+ }
+ else
+ return this.getPrefix(-1).append(this.get(-1).getSuccessor());
+};
+
+/**
+ * Return true if the N components of this name are the same as the first N
+ * components of the given name.
+ * @param {Name} name The name to check.
+ * @return {Boolean} true if this matches the given name. This always returns
+ * true if this name is empty.
+ */
+Name.prototype.match = function(name)
+{
+ var i_name = this.components;
+ var o_name = name.components;
+
+ // This name is longer than the name we are checking it against.
+ if (i_name.length > o_name.length)
+ return false;
+
+ // Check if at least one of given components doesn't match. Check from last to
+ // first since the last components are more likely to differ.
+ for (var i = i_name.length - 1; i >= 0; --i) {
+ if (!i_name[i].equals(o_name[i]))
+ return false;
+ }
+
+ return true;
+};
+
+/**
+ * Return true if the N components of this name are the same as the first N
+ * components of the given name.
+ * @param {Name} name The name to check.
+ * @return {Boolean} true if this matches the given name. This always returns
+ * true if this name is empty.
+ */
+Name.prototype.isPrefixOf = function(name) { return this.match(name); }
+
+/**
+ * Get the change count, which is incremented each time this object is changed.
+ * @return {number} The change count.
+ */
+Name.prototype.getChangeCount = function()
+{
+ return this.changeCount;
+};
+
+// Put these requires at the bottom to avoid circular references.
+var TlvEncoder = require('./encoding/tlv/tlv-encoder.js').TlvEncoder;
+var WireFormat = require('./encoding/wire-format.js').WireFormat;
+/**
+ * This class represents an NDN KeyLocator object.
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Meki Cheraoui
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('./util/blob.js').Blob; /** @ignore */
+var ChangeCounter = require('./util/change-counter.js').ChangeCounter; /** @ignore */
+var Name = require('./name.js').Name;
+
+/**
+ * KeyLocator
+ */
+var KeyLocatorType = {
+ KEYNAME: 1,
+ KEY_LOCATOR_DIGEST: 2
+};
+
+exports.KeyLocatorType = KeyLocatorType;
+
+/**
+ * @constructor
+ */
+var KeyLocator = function KeyLocator(input, type)
+{
+ if (typeof input === 'object' && input instanceof KeyLocator) {
+ // Copy from the input KeyLocator.
+ this.type_ = input.type_;
+ this.keyName_ = new ChangeCounter(new Name(input.getKeyName()));
+ this.keyData_ = input.keyData_;
+ }
+ else {
+ this.type_ = type;
+ this.keyName_ = new ChangeCounter(new Name());
+ this.keyData_ = new Blob();
+
+ if (type == KeyLocatorType.KEYNAME)
+ this.keyName_.set(typeof input === 'object' && input instanceof Name ?
+ new Name(input) : new Name());
+ else if (type == KeyLocatorType.KEY_LOCATOR_DIGEST)
+ this.keyData_ = new Blob(input);
+ }
+
+ this.changeCount_ = 0;
+};
+
+exports.KeyLocator = KeyLocator;
+
+/**
+ * Get the key locator type. If KeyLocatorType.KEYNAME, you may also
+ * getKeyName(). If KeyLocatorType.KEY_LOCATOR_DIGEST, you may also
+ * getKeyData() to get the digest.
+ * @return {number} The key locator type as a KeyLocatorType enum value,
+ * or null if not specified.
+ */
+KeyLocator.prototype.getType = function() { return this.type_; };
+
+/**
+ * Get the key name. This is meaningful if getType() is KeyLocatorType.KEYNAME.
+ * @return {Name} The key name. If not specified, the Name is empty.
+ */
+KeyLocator.prototype.getKeyName = function()
+{
+ return this.keyName_.get();
+};
+
+/**
+ * Get the key data. If getType() is KeyLocatorType.KEY_LOCATOR_DIGEST, this is
+ * the digest bytes.
+ * @return {Blob} The key data, or an isNull Blob if not specified.
+ */
+KeyLocator.prototype.getKeyData = function()
+{
+ return this.keyData_;
+};
+
+/**
+ * @deprecated Use getKeyData. This method returns a Buffer which is the former
+ * behavior of getKeyData, and should only be used while updating your code.
+ */
+KeyLocator.prototype.getKeyDataAsBuffer = function()
+{
+ return this.getKeyData().buf();
+};
+
+/**
+ * Set the key locator type. If KeyLocatorType.KEYNAME, you must also
+ * setKeyName(). If KeyLocatorType.KEY_LOCATOR_DIGEST, you must also
+ * setKeyData() to the digest.
+ * @param {number} type The key locator type as a KeyLocatorType enum value. If
+ * null, the type is unspecified.
+ */
+KeyLocator.prototype.setType = function(type)
+{
+ this.type_ = type;
+ ++this.changeCount_;
+};
+
+/**
+ * Set key name to a copy of the given Name. This is the name if getType()
+ * is KeyLocatorType.KEYNAME.
+ * @param {Name} name The key name which is copied.
+ */
+KeyLocator.prototype.setKeyName = function(name)
+{
+ this.keyName_.set(typeof name === 'object' && name instanceof Name ?
+ new Name(name) : new Name());
+ ++this.changeCount_;
+};
+
+/**
+ * Set the key data to the given value. This is the digest bytes if getType() is
+ * KeyLocatorType.KEY_LOCATOR_DIGEST.
+ * @param {Blob} keyData A Blob with the key data bytes.
+ */
+KeyLocator.prototype.setKeyData = function(keyData)
+{
+ this.keyData_ = typeof keyData === 'object' && keyData instanceof Blob ?
+ keyData : new Blob(keyData);
+ ++this.changeCount_;
+};
+
+/**
+ * Clear the keyData and set the type to not specified.
+ */
+KeyLocator.prototype.clear = function()
+{
+ this.type_ = null;
+ this.keyName_.set(new Name());
+ this.keyData_ = new Blob();
+ ++this.changeCount_;
+};
+
+/**
+ * Check if this key locator has the same values as the given key locator.
+ * @param {KeyLocator} other The other key locator to check.
+ * @return {boolean} true if the key locators are equal, otherwise false.
+ */
+KeyLocator.prototype.equals = function(other)
+{
+ if (this.type_ != other.type_)
+ return false;
+
+ if (this.type_ == KeyLocatorType.KEYNAME) {
+ if (!this.getKeyName().equals(other.getKeyName()))
+ return false;
+ }
+ else if (this.type_ == KeyLocatorType.KEY_LOCATOR_DIGEST) {
+ if (!this.getKeyData().equals(other.getKeyData()))
return false;
}
return true;
};
-NDN.supported = NDN.getSupported();
-
-NDN.ndndIdFetcher = new Name('/%C1.M.S.localhost/%C1.M.SRV/ndnd/KEY');
-
-NDN.prototype.createRoute = function(host, port) {
- this.host=host;
- this.port=port;
-};
-
-
-NDN.KeyStore = new Array();
-
-var KeyStoreEntry = function KeyStoreEntry(name, rsa, time) {
- this.keyName = name; // KeyName
- this.rsaKey = rsa; // RSA key
- this.timeStamp = time; // Time Stamp
-};
-
-NDN.addKeyEntry = function(/* KeyStoreEntry */ keyEntry) {
- var result = NDN.getKeyByName(keyEntry.keyName);
- if (result == null)
- NDN.KeyStore.push(keyEntry);
- else
- result = keyEntry;
-};
-
-NDN.getKeyByName = function(/* KeyName */ name) {
- var result = null;
-
- for (var i = 0; i < NDN.KeyStore.length; i++) {
- if (NDN.KeyStore[i].keyName.contentName.match(name.contentName)) {
- if (result == null ||
- NDN.KeyStore[i].keyName.contentName.components.length > result.keyName.contentName.components.length)
- result = NDN.KeyStore[i];
- }
- }
-
- return result;
-};
-
-// For fetching data
-NDN.PITTable = new Array();
-
-var PITEntry = function PITEntry(interest, closure) {
- this.interest = interest; // Interest
- this.closure = closure; // Closure
- this.timerID = -1; // Timer ID
-};
-
-/*
- * Return the entry from NDN.PITTable where the name conforms to the interest selectors, and
- * the interest name is the longest that matches name.
+/**
+ * If the signature is a type that has a KeyLocator (so that,
+ * getFromSignature will succeed), return true.
+ * Note: This is a static method of KeyLocator instead of a method of
+ * Signature so that the Signature base class does not need to be overloaded
+ * with all the different kinds of information that various signature
+ * algorithms may use.
+ * @param {Signature} signature An object of a subclass of Signature.
+ * @return {boolean} True if the signature is a type that has a KeyLocator,
+ * otherwise false.
*/
-NDN.getEntryForExpressedInterest = function(/*Name*/ name) {
- var result = null;
-
- for (var i = 0; i < NDN.PITTable.length; i++) {
- if (NDN.PITTable[i].interest.matches_name(name)) {
- if (result == null ||
- NDN.PITTable[i].interest.name.components.length > result.interest.name.components.length)
- result = NDN.PITTable[i];
- }
- }
-
- return result;
-};
-
-// For publishing data
-NDN.CSTable = new Array();
-
-var CSEntry = function CSEntry(name, closure) {
- this.name = name; // String
- this.closure = closure; // Closure
-};
-
-function getEntryForRegisteredPrefix(name) {
- for (var i = 0; i < NDN.CSTable.length; i++) {
- if (NDN.CSTable[i].name.match(name) != null)
- return NDN.CSTable[i];
- }
- return null;
+KeyLocator.canGetFromSignature = function(signature)
+{
+ return signature instanceof Sha256WithRsaSignature ||
+ signature instanceof Sha256WithEcdsaSignature ||
+ signature instanceof HmacWithSha256Signature;
}
-/*
- * Return a function that selects a host at random from hostList and returns { host: host, port: port }.
- * If no more hosts remain, return null.
+/**
+ * If the signature is a type that has a KeyLocator, then return it. Otherwise
+ * throw an error.
+ * @param {Signature} signature An object of a subclass of Signature.
+ * @return {KeyLocator} The signature's KeyLocator. It is an error if signature
+ * doesn't have a KeyLocator.
*/
-NDN.makeShuffledGetHostAndPort = function(hostList, port) {
- // Make a copy.
- hostList = hostList.slice(0, hostList.length);
- DataUtils.shuffle(hostList);
+KeyLocator.getFromSignature = function(signature)
+{
+ if (signature instanceof Sha256WithRsaSignature ||
+ signature instanceof Sha256WithEcdsaSignature ||
+ signature instanceof HmacWithSha256Signature)
+ return signature.getKeyLocator();
+ else
+ throw new Error
+ ("KeyLocator.getFromSignature: Signature type does not have a KeyLocator");
+}
- return function() {
- if (hostList.length == 0)
- return null;
+/**
+ * Get the change count, which is incremented each time this object (or a child
+ * object) is changed.
+ * @return {number} The change count.
+ */
+KeyLocator.prototype.getChangeCount = function()
+{
+ // Make sure each of the checkChanged is called.
+ var changed = this.keyName_.checkChanged();
+ if (changed)
+ // A child object has changed, so update the change count.
+ ++this.changeCount_;
- return { host: hostList.splice(0, 1)[0], port: port };
- };
+ return this.changeCount_;
};
-/** Encode name as an Interest. If template is not null, use its attributes.
- * Send the interest to host:port, read the entire response and call
- * closure.upcall(Closure.UPCALL_CONTENT (or Closure.UPCALL_CONTENT_UNVERIFIED),
- * new UpcallInfo(this, interest, 0, contentObject)).
+// Define properties so we can change member variable types and implement changeCount_.
+Object.defineProperty(KeyLocator.prototype, "type",
+ { get: function() { return this.getType(); },
+ set: function(val) { this.setType(val); } });
+/**
+ * @@deprecated Use getKeyData and setKeyData.
*/
-NDN.prototype.expressInterest = function(
- // Name
- name,
- // Closure
- closure,
- // Interest
- template) {
- var interest = new Interest(name);
- if (template != null) {
- interest.minSuffixComponents = template.minSuffixComponents;
- interest.maxSuffixComponents = template.maxSuffixComponents;
- interest.publisherPublicKeyDigest = template.publisherPublicKeyDigest;
- interest.exclude = template.exclude;
- interest.childSelector = template.childSelector;
- interest.answerOriginKind = template.answerOriginKind;
- interest.scope = template.scope;
- interest.interestLifetime = template.interestLifetime;
+Object.defineProperty(KeyLocator.prototype, "keyData",
+ { get: function() { return this.getKeyDataAsBuffer(); },
+ set: function(val) { this.setKeyData(val); } });
+
+// Put this last to avoid a require loop.
+var Sha256WithRsaSignature = require('./sha256-with-rsa-signature.js').Sha256WithRsaSignature;
+var Sha256WithEcdsaSignature = require('./sha256-with-ecdsa-signature.js').Sha256WithEcdsaSignature;
+var HmacWithSha256Signature = require('./hmac-with-sha256-signature.js').HmacWithSha256Signature;
+/**
+ * This class represents an NDN Data MetaInfo object.
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Meki Cheraoui
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Name = require('./name.js').Name;
+
+/**
+ * A ContentType specifies the content type in a MetaInfo object. If the
+ * content type in the packet is not a recognized enum value, then we use
+ * ContentType.OTHER_CODE and you can call MetaInfo.getOtherTypeCode(). We do
+ * this to keep the recognized content type values independent of packet
+ * encoding formats.
+ */
+var ContentType = {
+ BLOB:0,
+ LINK:1,
+ KEY: 2,
+ NACK:3,
+ OTHER_CODE: 0x7fff
+};
+
+exports.ContentType = ContentType;
+
+/**
+ * Create a new MetaInfo with the optional values.
+ * @constructor
+ */
+var MetaInfo = function MetaInfo(publisherOrMetaInfo, timestamp, type, locator, freshnessSeconds, finalBlockId)
+{
+ if (timestamp)
+ throw new Error
+ ("MetaInfo constructor: timestamp support has been removed.");
+ if (locator)
+ throw new Error
+ ("MetaInfo constructor: locator support has been removed.");
+
+ if (typeof publisherOrMetaInfo === 'object' &&
+ publisherOrMetaInfo instanceof MetaInfo) {
+ // Copy values.
+ var metaInfo = publisherOrMetaInfo;
+ this.publisher_ = metaInfo.publisher_;
+ this.type_ = metaInfo.type_;
+ this.otherTypeCode_ = metaInfo.otherTypeCode_;
+ this.freshnessPeriod_ = metaInfo.freshnessPeriod_;
+ this.finalBlockId_ = metaInfo.finalBlockId_;
+ }
+ else {
+ if (publisherOrMetaInfo)
+ throw new Error
+ ("MetaInfo constructor: publisher support has been removed.");
+
+ this.type = type == null || type < 0 ? ContentType.BLOB : type;
+ this.otherTypeCode_ = -1;
+ this.freshnessSeconds = freshnessSeconds; // deprecated
+ this.finalBlockID = finalBlockId; // byte array // deprecated
+ }
+
+ this.changeCount_ = 0;
+};
+
+exports.MetaInfo = MetaInfo;
+
+/**
+ * Get the content type.
+ * @return {number} The content type as an int from ContentType. If this is
+ * ContentType.OTHER_CODE, then call getOtherTypeCode() to get the unrecognized
+ * content type code.
+ */
+MetaInfo.prototype.getType = function()
+{
+ return this.type_;
+};
+
+/**
+ * Get the content type code from the packet which is other than a recognized
+ * ContentType enum value. This is only meaningful if getType() is
+ * ContentType.OTHER_CODE.
+ * @return {number} The type code.
+ */
+MetaInfo.prototype.getOtherTypeCode = function()
+{
+ return this.otherTypeCode_;
+};
+
+/**
+ * Get the freshness period.
+ * @return {number} The freshness period in milliseconds, or null if not
+ * specified.
+ */
+MetaInfo.prototype.getFreshnessPeriod = function()
+{
+ return this.freshnessPeriod_;
+};
+
+/**
+ * Get the final block ID.
+ * @return {Name.Component} The final block ID as a Name.Component. If the
+ * Name.Component getValue().size() is 0, then the final block ID is not specified.
+ */
+MetaInfo.prototype.getFinalBlockId = function()
+{
+ return this.finalBlockId_;
+};
+
+/**
+ * @deprecated Use getFinalBlockId.
+ */
+MetaInfo.prototype.getFinalBlockID = function()
+{
+ return this.getFinalBlockId();
+};
+
+/**
+ * @deprecated Use getFinalBlockId. This method returns a Buffer which is the former
+ * behavior of getFinalBlockId, and should only be used while updating your code.
+ */
+MetaInfo.prototype.getFinalBlockIDAsBuffer = function()
+{
+ return this.finalBlockId_.getValue().buf();
+};
+
+/**
+ * Set the content type.
+ * @param {number} type The content type as an int from ContentType. If null,
+ * this uses ContentType.BLOB. If the packet's content type is not a recognized
+ * ContentType enum value, use ContentType.OTHER_CODE and call setOtherTypeCode().
+ */
+MetaInfo.prototype.setType = function(type)
+{
+ this.type_ = type == null || type < 0 ? ContentType.BLOB : type;
+ ++this.changeCount_;
+};
+
+/**
+ * Set the packet’s content type code to use when the content type enum is
+ * ContentType.OTHER_CODE. If the packet’s content type code is a recognized
+ * enum value, just call setType().
+ * @param {number} otherTypeCode The packet’s unrecognized content type code,
+ * which must be non-negative.
+ */
+MetaInfo.prototype.setOtherTypeCode = function(otherTypeCode)
+{
+ if (otherTypeCode < 0)
+ throw new Error("MetaInfo other type code must be non-negative");
+
+ this.otherTypeCode_ = otherTypeCode;
+ ++this.changeCount_;
+};
+
+/**
+ * Set the freshness period.
+ * @param {number} freshnessPeriod The freshness period in milliseconds, or null
+ * for not specified.
+ */
+MetaInfo.prototype.setFreshnessPeriod = function(freshnessPeriod)
+{
+ if (freshnessPeriod == null || freshnessPeriod < 0)
+ this.freshnessPeriod_ = null;
+ else
+ this.freshnessPeriod_ = freshnessPeriod;
+ ++this.changeCount_;
+};
+
+/**
+ * Set the final block ID.
+ * @param {Name.Component} finalBlockId The final block ID as a Name.Component.
+ * If not specified, set to a new default Name.Component(), or to a
+ * Name.Component where getValue().size() is 0.
+ */
+MetaInfo.prototype.setFinalBlockId = function(finalBlockId)
+{
+ this.finalBlockId_ = typeof finalBlockId === 'object' &&
+ finalBlockId instanceof Name.Component ?
+ finalBlockId : new Name.Component(finalBlockId);
+ ++this.changeCount_;
+};
+
+/**
+ * @deprecated Use setFinalBlockId.
+ */
+MetaInfo.prototype.setFinalBlockID = function(finalBlockId)
+{
+ this.setFinalBlockId(finalBlockId);
+};
+
+/**
+ * Get the change count, which is incremented each time this object is changed.
+ * @return {number} The change count.
+ */
+MetaInfo.prototype.getChangeCount = function()
+{
+ return this.changeCount_;
+};
+
+// Define properties so we can change member variable types and implement changeCount_.
+Object.defineProperty(MetaInfo.prototype, "type",
+ { get: function() { return this.getType(); },
+ set: function(val) { this.setType(val); } });
+/**
+ * @deprecated Use getFreshnessPeriod and setFreshnessPeriod.
+ */
+Object.defineProperty(MetaInfo.prototype, "freshnessSeconds",
+ { get: function() {
+ if (this.freshnessPeriod_ == null || this.freshnessPeriod_ < 0)
+ return null;
+ else
+ // Convert from milliseconds.
+ return this.freshnessPeriod_ / 1000.0;
+ },
+ set: function(val) {
+ if (val == null || val < 0)
+ this.freshnessPeriod_ = null;
+ else
+ // Convert to milliseconds.
+ this.freshnessPeriod_ = val * 1000.0;
+ ++this.changeCount_;
+ } });
+/**
+ * @deprecated Use getFinalBlockId and setFinalBlockId.
+ */
+Object.defineProperty(MetaInfo.prototype, "finalBlockID",
+ { get: function() { return this.getFinalBlockIDAsBuffer(); },
+ set: function(val) { this.setFinalBlockId(val); } });
+/**
+ * This class represents an NDN Data Signature object.
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('./util/blob.js').Blob; /** @ignore */
+var ChangeCounter = require('./util/change-counter.js').ChangeCounter; /** @ignore */
+var KeyLocator = require('./key-locator.js').KeyLocator;
+
+/**
+ * Create a new Sha256WithEcdsaSignature object, possibly copying values from
+ * another object.
+ *
+ * @param {Sha256WithEcdsaSignature} value (optional) If value is a
+ * Sha256WithEcdsaSignature, copy its values. If value is omitted, the
+ * keyLocator is the default with unspecified values and the signature is
+ * unspecified.
+ * @constructor
+ */
+var Sha256WithEcdsaSignature = function Sha256WithEcdsaSignature(value)
+{
+ if (typeof value === 'object' && value instanceof Sha256WithEcdsaSignature) {
+ // Copy the values.
+ this.keyLocator_ = new ChangeCounter(new KeyLocator(value.getKeyLocator()));
+ this.signature_ = value.signature_;
+ }
+ else {
+ this.keyLocator_ = new ChangeCounter(new KeyLocator());
+ this.signature_ = new Blob();
+ }
+
+ this.changeCount_ = 0;
+};
+
+exports.Sha256WithEcdsaSignature = Sha256WithEcdsaSignature;
+
+/**
+ * Create a new Sha256WithEcdsaSignature which is a copy of this object.
+ * @return {Sha256WithEcdsaSignature} A new object which is a copy of this
+ * object.
+ */
+Sha256WithEcdsaSignature.prototype.clone = function()
+{
+ return new Sha256WithEcdsaSignature(this);
+};
+
+/**
+ * Get the key locator.
+ * @return {KeyLocator} The key locator.
+ */
+Sha256WithEcdsaSignature.prototype.getKeyLocator = function()
+{
+ return this.keyLocator_.get();
+};
+
+/**
+ * Get the data packet's signature bytes.
+ * @return {Blob} The signature bytes. If not specified, the value isNull().
+ */
+Sha256WithEcdsaSignature.prototype.getSignature = function()
+{
+ return this.signature_;
+};
+
+/**
+ * Set the key locator to a copy of the given keyLocator.
+ * @param {KeyLocator} keyLocator The KeyLocator to copy.
+ */
+Sha256WithEcdsaSignature.prototype.setKeyLocator = function(keyLocator)
+{
+ this.keyLocator_.set(typeof keyLocator === 'object' &&
+ keyLocator instanceof KeyLocator ?
+ new KeyLocator(keyLocator) : new KeyLocator());
+ ++this.changeCount_;
+};
+
+/**
+ * Set the data packet's signature bytes.
+ * @param {Blob} signature
+ */
+Sha256WithEcdsaSignature.prototype.setSignature = function(signature)
+{
+ this.signature_ = typeof signature === 'object' && signature instanceof Blob ?
+ signature : new Blob(signature);
+ ++this.changeCount_;
+};
+
+/**
+ * Get the change count, which is incremented each time this object (or a child
+ * object) is changed.
+ * @return {number} The change count.
+ */
+Sha256WithEcdsaSignature.prototype.getChangeCount = function()
+{
+ // Make sure each of the checkChanged is called.
+ var changed = this.keyLocator_.checkChanged();
+ if (changed)
+ // A child object has changed, so update the change count.
+ ++this.changeCount_;
+
+ return this.changeCount_;
+};
+/**
+ * This class represents an NDN Data Signature object.
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Meki Cheraoui
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('./util/blob.js').Blob; /** @ignore */
+var ChangeCounter = require('./util/change-counter.js').ChangeCounter; /** @ignore */
+var KeyLocator = require('./key-locator.js').KeyLocator;
+
+/**
+ * Create a new Sha256WithRsaSignature object, possibly copying values from
+ * another object.
+ *
+ * @param {Sha256WithRsaSignature} value (optional) If value is a
+ * Sha256WithRsaSignature, copy its values. If value is omitted, the keyLocator
+ * is the default with unspecified values and the signature is unspecified.
+ * @constructor
+ */
+var Sha256WithRsaSignature = function Sha256WithRsaSignature(value)
+{
+ if (typeof value === 'object' && value instanceof Sha256WithRsaSignature) {
+ // Copy the values.
+ this.keyLocator_ = new ChangeCounter(new KeyLocator(value.getKeyLocator()));
+ this.signature_ = value.signature_;
+ }
+ else {
+ this.keyLocator_ = new ChangeCounter(new KeyLocator());
+ this.signature_ = new Blob();
+ }
+
+ this.changeCount_ = 0;
+};
+
+exports.Sha256WithRsaSignature = Sha256WithRsaSignature;
+
+/**
+ * Create a new Sha256WithRsaSignature which is a copy of this object.
+ * @return {Sha256WithRsaSignature} A new object which is a copy of this object.
+ */
+Sha256WithRsaSignature.prototype.clone = function()
+{
+ return new Sha256WithRsaSignature(this);
+};
+
+/**
+ * Get the key locator.
+ * @return {KeyLocator} The key locator.
+ */
+Sha256WithRsaSignature.prototype.getKeyLocator = function()
+{
+ return this.keyLocator_.get();
+};
+
+/**
+ * Get the data packet's signature bytes.
+ * @return {Blob} The signature bytes. If not specified, the value isNull().
+ */
+Sha256WithRsaSignature.prototype.getSignature = function()
+{
+ return this.signature_;
+};
+
+/**
+ * @deprecated Use getSignature. This method returns a Buffer which is the former
+ * behavior of getSignature, and should only be used while updating your code.
+ */
+Sha256WithRsaSignature.prototype.getSignatureAsBuffer = function()
+{
+ return this.signature_.buf();
+};
+
+/**
+ * Set the key locator to a copy of the given keyLocator.
+ * @param {KeyLocator} keyLocator The KeyLocator to copy.
+ */
+Sha256WithRsaSignature.prototype.setKeyLocator = function(keyLocator)
+{
+ this.keyLocator_.set(typeof keyLocator === 'object' &&
+ keyLocator instanceof KeyLocator ?
+ new KeyLocator(keyLocator) : new KeyLocator());
+ ++this.changeCount_;
+};
+
+/**
+ * Set the data packet's signature bytes.
+ * @param {Blob} signature
+ */
+Sha256WithRsaSignature.prototype.setSignature = function(signature)
+{
+ this.signature_ = typeof signature === 'object' && signature instanceof Blob ?
+ signature : new Blob(signature);
+ ++this.changeCount_;
+};
+
+/**
+ * Get the change count, which is incremented each time this object (or a child
+ * object) is changed.
+ * @return {number} The change count.
+ */
+Sha256WithRsaSignature.prototype.getChangeCount = function()
+{
+ // Make sure each of the checkChanged is called.
+ var changed = this.keyLocator_.checkChanged();
+ if (changed)
+ // A child object has changed, so update the change count.
+ ++this.changeCount_;
+
+ return this.changeCount_;
+};
+
+// Define properties so we can change member variable types and implement changeCount_.
+Object.defineProperty(Sha256WithRsaSignature.prototype, "keyLocator",
+ { get: function() { return this.getKeyLocator(); },
+ set: function(val) { this.setKeyLocator(val); } });
+/**
+ * @@deprecated Use getSignature and setSignature.
+ */
+Object.defineProperty(Sha256WithRsaSignature.prototype, "signature",
+ { get: function() { return this.getSignatureAsBuffer(); },
+ set: function(val) { this.setSignature(val); } });
+/**
+ * This class represents an NDN Data Signature object.
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('./util/blob.js').Blob;
+
+/**
+ * A GenericSignature extends Signature and holds the encoding bytes of the
+ * SignatureInfo so that the application can process experimental signature
+ * types. When decoding a packet, if the type of SignatureInfo is not
+ * recognized, the library creates a GenericSignature.
+ * Create a new GenericSignature object, possibly copying values from another
+ * object.
+ *
+ * @param {GenericSignature} value (optional) If value is a GenericSignature,
+ * copy its values.
+ * @constructor
+ */
+var GenericSignature = function GenericSignature(value)
+{
+ if (typeof value === 'object' && value instanceof GenericSignature) {
+ // Copy the values.
+ this.signature_ = value.signature_;
+ this.signatureInfoEncoding_ = value.signatureInfoEncoding_;
+ this.typeCode_ = value.typeCode_;
+ }
+ else {
+ this.signature_ = new Blob();
+ this.signatureInfoEncoding_ = new Blob();
+ this.typeCode_ = null;
+ }
+
+ this.changeCount_ = 0;
+};
+
+exports.GenericSignature = GenericSignature;
+
+/**
+ * Create a new GenericSignature which is a copy of this object.
+ * @return {GenericSignature} A new object which is a copy of this object.
+ */
+GenericSignature.prototype.clone = function()
+{
+ return new GenericSignature(this);
+};
+
+/**
+ * Get the data packet's signature bytes.
+ * @return {Blob} The signature bytes. If not specified, the value isNull().
+ */
+GenericSignature.prototype.getSignature = function()
+{
+ return this.signature_;
+};
+
+/**
+ * @deprecated Use getSignature. This method returns a Buffer which is the former
+ * behavior of getSignature, and should only be used while updating your code.
+ */
+GenericSignature.prototype.getSignatureAsBuffer = function()
+{
+ return this.signature_.buf();
+};
+
+/**
+ * Get the bytes of the entire signature info encoding (including the type
+ * code).
+ * @return {Blob} The encoding bytes. If not specified, the value isNull().
+ */
+GenericSignature.prototype.getSignatureInfoEncoding = function()
+{
+ return this.signatureInfoEncoding_;
+};
+
+/**
+ * Get the type code of the signature type. When wire decode calls
+ * setSignatureInfoEncoding, it sets the type code. Note that the type code
+ * is ignored during wire encode, which simply uses getSignatureInfoEncoding()
+ * where the encoding already has the type code.
+ * @return {number} The type code, or null if not known.
+ */
+GenericSignature.prototype.getTypeCode = function() { return this.typeCode_; };
+
+/**
+ * Set the data packet's signature bytes.
+ * @param {Blob} signature
+ */
+GenericSignature.prototype.setSignature = function(signature)
+{
+ this.signature_ = typeof signature === 'object' && signature instanceof Blob ?
+ signature : new Blob(signature);
+ ++this.changeCount_;
+};
+
+/**
+ * Set the bytes of the entire signature info encoding (including the type
+ * code).
+ * @param {Blob} signatureInfoEncoding A Blob with the encoding bytes.
+ * @param {number} (optional) The type code of the signature type, or null if
+ * not known. (When a GenericSignature is created by wire decoding, it sets
+ * the typeCode.)
+ */
+GenericSignature.prototype.setSignatureInfoEncoding = function
+ (signatureInfoEncoding, typeCode)
+{
+ this.signatureInfoEncoding_ =
+ typeof signatureInfoEncoding === 'object' && signatureInfoEncoding instanceof Blob ?
+ signatureInfoEncoding : new Blob(signatureInfoEncoding);
+ this.typeCode_ = typeCode;
+ ++this.changeCount_;
+};
+
+/**
+ * Get the change count, which is incremented each time this object (or a child
+ * object) is changed.
+ * @return {number} The change count.
+ */
+GenericSignature.prototype.getChangeCount = function()
+{
+ return this.changeCount_;
+};
+
+/**
+ * @@deprecated Use getSignature and setSignature.
+ */
+Object.defineProperty(GenericSignature.prototype, "signature",
+ { get: function() { return this.getSignatureAsBuffer(); },
+ set: function(val) { this.setSignature(val); } });
+/**
+ * This class represents an NDN Data Signature object.
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('./util/blob.js').Blob; /** @ignore */
+var ChangeCounter = require('./util/change-counter.js').ChangeCounter; /** @ignore */
+var KeyLocator = require('./key-locator.js').KeyLocator;
+
+/**
+ * An HmacWithSha256Signature holds the signature bits and other info
+ * representing an HmacWithSha256 signature in a packet.
+ * Create a new HmacWithSha256Signature object, possibly copying values from
+ * another object.
+ *
+ * @param {HmacWithSha256Signature} value (optional) If value is a
+ * HmacWithSha256Signature, copy its values. If value is omitted, the keyLocator
+ * is the default with unspecified values and the signature is unspecified.
+ * @constructor
+ */
+var HmacWithSha256Signature = function HmacWithSha256Signature(value)
+{
+ if (typeof value === 'object' && value instanceof HmacWithSha256Signature) {
+ // Copy the values.
+ this.keyLocator_ = new ChangeCounter(new KeyLocator(value.getKeyLocator()));
+ this.signature_ = value.signature_;
+ }
+ else {
+ this.keyLocator_ = new ChangeCounter(new KeyLocator());
+ this.signature_ = new Blob();
+ }
+
+ this.changeCount_ = 0;
+};
+
+exports.HmacWithSha256Signature = HmacWithSha256Signature;
+
+/**
+ * Create a new HmacWithSha256Signature which is a copy of this object.
+ * @return {HmacWithSha256Signature} A new object which is a copy of this object.
+ */
+HmacWithSha256Signature.prototype.clone = function()
+{
+ return new HmacWithSha256Signature(this);
+};
+
+/**
+ * Get the key locator.
+ * @return {KeyLocator} The key locator.
+ */
+HmacWithSha256Signature.prototype.getKeyLocator = function()
+{
+ return this.keyLocator_.get();
+};
+
+/**
+ * Get the data packet's signature bytes.
+ * @return {Blob} The signature bytes. If not specified, the value isNull().
+ */
+HmacWithSha256Signature.prototype.getSignature = function()
+{
+ return this.signature_;
+};
+
+/**
+ * @deprecated Use getSignature. This method returns a Buffer which is the former
+ * behavior of getSignature, and should only be used while updating your code.
+ */
+HmacWithSha256Signature.prototype.getSignatureAsBuffer = function()
+{
+ return this.signature_.buf();
+};
+
+/**
+ * Set the key locator to a copy of the given keyLocator.
+ * @param {KeyLocator} keyLocator The KeyLocator to copy.
+ */
+HmacWithSha256Signature.prototype.setKeyLocator = function(keyLocator)
+{
+ this.keyLocator_.set(typeof keyLocator === 'object' &&
+ keyLocator instanceof KeyLocator ?
+ new KeyLocator(keyLocator) : new KeyLocator());
+ ++this.changeCount_;
+};
+
+/**
+ * Set the data packet's signature bytes.
+ * @param {Blob} signature
+ */
+HmacWithSha256Signature.prototype.setSignature = function(signature)
+{
+ this.signature_ = typeof signature === 'object' && signature instanceof Blob ?
+ signature : new Blob(signature);
+ ++this.changeCount_;
+};
+
+/**
+ * Get the change count, which is incremented each time this object (or a child
+ * object) is changed.
+ * @return {number} The change count.
+ */
+HmacWithSha256Signature.prototype.getChangeCount = function()
+{
+ // Make sure each of the checkChanged is called.
+ var changed = this.keyLocator_.checkChanged();
+ if (changed)
+ // A child object has changed, so update the change count.
+ ++this.changeCount_;
+
+ return this.changeCount_;
+};
+
+// Define properties so we can change member variable types and implement changeCount_.
+Object.defineProperty(HmacWithSha256Signature.prototype, "keyLocator",
+ { get: function() { return this.getKeyLocator(); },
+ set: function(val) { this.setKeyLocator(val); } });
+/**
+ * @@deprecated Use getSignature and setSignature.
+ */
+Object.defineProperty(HmacWithSha256Signature.prototype, "signature",
+ { get: function() { return this.getSignatureAsBuffer(); },
+ set: function(val) { this.setSignature(val); } });
+/**
+ * This class represents an NDN Data Signature object.
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('./util/blob.js').Blob;
+
+/**
+ * A DigestSha256Signature extends Signature and holds the signature bits (which
+ * are only the SHA256 digest) and an empty SignatureInfo for a data packet or
+ * signed interest.
+ *
+ * Create a new DigestSha256Signature object, possibly copying values from
+ * another object.
+ *
+ * @param {DigestSha256Signature} value (optional) If value is a
+ * DigestSha256Signature, copy its values. If value is omitted, the signature
+ * is unspecified.
+ * @constructor
+ */
+var DigestSha256Signature = function DigestSha256Signature(value)
+{
+ if (typeof value === 'object' && value instanceof DigestSha256Signature)
+ // Copy the values.
+ this.signature_ = value.signature_;
+ else
+ this.signature_ = new Blob();
+
+ this.changeCount_ = 0;
+};
+
+exports.DigestSha256Signature = DigestSha256Signature;
+
+/**
+ * Create a new DigestSha256Signature which is a copy of this object.
+ * @return {DigestSha256Signature} A new object which is a copy of this object.
+ */
+DigestSha256Signature.prototype.clone = function()
+{
+ return new DigestSha256Signature(this);
+};
+
+/**
+ * Get the signature bytes (which are only the digest).
+ * @return {Blob} The signature bytes. If not specified, the value isNull().
+ */
+DigestSha256Signature.prototype.getSignature = function()
+{
+ return this.signature_;
+};
+
+/**
+ * Set the signature bytes to the given value.
+ * @param {Blob} signature
+ */
+DigestSha256Signature.prototype.setSignature = function(signature)
+{
+ this.signature_ = typeof signature === 'object' && signature instanceof Blob ?
+ signature : new Blob(signature);
+ ++this.changeCount_;
+};
+
+/**
+ * Get the change count, which is incremented each time this object is changed.
+ * @return {number} The change count.
+ */
+DigestSha256Signature.prototype.getChangeCount = function()
+{
+ return this.changeCount_;
+};
+
+// Define properties so we can change member variable types and implement changeCount_.
+/**
+ * @@deprecated Use getSignature and setSignature.
+ */
+Object.defineProperty(DigestSha256Signature.prototype, "signature",
+ { get: function() { return this.getSignature(); },
+ set: function(val) { this.setSignature(val); } });
+/**
+ * This class represents an NDN Data object.
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Meki Cheraoui
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('./util/blob.js').Blob; /** @ignore */
+var SignedBlob = require('./util/signed-blob.js').SignedBlob; /** @ignore */
+var ChangeCounter = require('./util/change-counter.js').ChangeCounter; /** @ignore */
+var Name = require('./name.js').Name; /** @ignore */
+var Sha256WithRsaSignature = require('./sha256-with-rsa-signature.js').Sha256WithRsaSignature; /** @ignore */
+var MetaInfo = require('./meta-info.js').MetaInfo; /** @ignore */
+var IncomingFaceId = require('./lp/incoming-face-id.js').IncomingFaceId; /** @ignore */
+var WireFormat = require('./encoding/wire-format.js').WireFormat; /** @ignore */
+var Crypto = require('./crypto.js');
+
+/**
+ * Create a new Data with the optional values. There are 2 forms of constructor:
+ * new Data([name] [, content]);
+ * new Data(name, metaInfo [, content]);
+ *
+ * @constructor
+ * @param {Name} name
+ * @param {MetaInfo} metaInfo
+ * @param {Buffer} content
+ */
+var Data = function Data(nameOrData, metaInfoOrContent, arg3)
+{
+ if (nameOrData instanceof Data) {
+ // The copy constructor.
+ var data = nameOrData;
+
+ // Copy the Data object.
+ this.name_ = new ChangeCounter(new Name(data.getName()));
+ this.metaInfo_ = new ChangeCounter(new MetaInfo(data.getMetaInfo()));
+ this.signature_ = new ChangeCounter(data.getSignature().clone());
+ this.content_ = data.content_;
+ this.defaultWireEncoding_ = data.getDefaultWireEncoding();
+ this.defaultFullName_ = data.defaultFullName_;
+ this.defaultWireEncodingFormat_ = data.defaultWireEncodingFormat_;
+ }
+ else {
+ var name = nameOrData;
+ if (typeof name === 'string')
+ this.name_ = new ChangeCounter(new Name(name));
+ else
+ this.name_ = new ChangeCounter(typeof name === 'object' && name instanceof Name ?
+ new Name(name) : new Name());
+
+ var metaInfo;
+ var content;
+ if (typeof metaInfoOrContent === 'object' &&
+ metaInfoOrContent instanceof MetaInfo) {
+ metaInfo = metaInfoOrContent;
+ content = arg3;
+ }
+ else {
+ metaInfo = null;
+ content = metaInfoOrContent;
+ }
+
+ this.metaInfo_ = new ChangeCounter(typeof metaInfo === 'object' && metaInfo instanceof MetaInfo ?
+ new MetaInfo(metaInfo) : new MetaInfo());
+
+ this.content_ = typeof content === 'object' && content instanceof Blob ?
+ content : new Blob(content, true);
+
+ this.signature_ = new ChangeCounter(new Sha256WithRsaSignature());
+ this.defaultWireEncoding_ = new SignedBlob();
+ this.defaultFullName_ = new Name();
+ this.defaultWireEncodingFormat_ = null;
+ }
+
+ this.getDefaultWireEncodingChangeCount_ = 0;
+ this.changeCount_ = 0;
+ this.lpPacket_ = null;
+};
+
+exports.Data = Data;
+
+/**
+ * Get the data packet's name.
+ * @return {Name} The name. If not specified, the name size() is 0.
+ */
+Data.prototype.getName = function()
+{
+ return this.name_.get();
+};
+
+/**
+ * Get the data packet's meta info.
+ * @return {MetaInfo} The meta info.
+ */
+Data.prototype.getMetaInfo = function()
+{
+ return this.metaInfo_.get();
+};
+
+/**
+ * Get the data packet's signature object.
+ * @return {Signature} The signature object.
+ */
+Data.prototype.getSignature = function()
+{
+ return this.signature_.get();
+};
+
+/**
+ * Get the data packet's content.
+ * @return {Blob} The content as a Blob, which isNull() if unspecified.
+ */
+Data.prototype.getContent = function()
+{
+ return this.content_;
+};
+
+/**
+ * @deprecated Use getContent. This method returns a Buffer which is the former
+ * behavior of getContent, and should only be used while updating your code.
+ */
+Data.prototype.getContentAsBuffer = function()
+{
+ return this.content_.buf();
+};
+
+/**
+ * Return the default wire encoding, which was encoded with
+ * getDefaultWireEncodingFormat().
+ * @return {SignedBlob} The default wire encoding, whose isNull() may be true
+ * if there is no default wire encoding.
+ */
+Data.prototype.getDefaultWireEncoding = function()
+{
+ if (this.getDefaultWireEncodingChangeCount_ != this.getChangeCount()) {
+ // The values have changed, so the default wire encoding is invalidated.
+ this.defaultWireEncoding_ = new SignedBlob();
+ this.defaultWireEncodingFormat_ = null;
+ this.getDefaultWireEncodingChangeCount_ = this.getChangeCount();
+ }
+
+ return this.defaultWireEncoding_;
+};
+
+/**
+ * Get the WireFormat which is used by getDefaultWireEncoding().
+ * @return {WireFormat} The WireFormat, which is only meaningful if the
+ * getDefaultWireEncoding() is not isNull().
+ */
+Data.prototype.getDefaultWireEncodingFormat = function()
+{
+ return this.defaultWireEncodingFormat_;
+};
+
+/**
+ * Get the incoming face ID according to the incoming packet header.
+ * @return {number} The incoming face ID. If not specified, return null.
+ */
+Data.prototype.getIncomingFaceId = function()
+{
+ var field =
+ this.lpPacket_ === null ? null : IncomingFaceId.getFirstHeader(this.lpPacket_);
+ return field === null ? null : field.getFaceId();
+};
+
+/**
+ * Get the Data packet's full name, which includes the final
+ * ImplicitSha256Digest component based on the wire encoding for a particular
+ * wire format.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {Name} The full name. You must not change the Name object - if you
+ * need to change it then make a copy.
+ */
+Data.prototype.getFullName = function(wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ // The default full name depends on the default wire encoding.
+ if (!this.getDefaultWireEncoding().isNull() &&
+ this.defaultFullName_.size() > 0 &&
+ this.getDefaultWireEncodingFormat() == wireFormat)
+ // We already have a full name. A non-null default wire encoding means
+ // that the Data packet fields have not changed.
+ return this.defaultFullName_;
+
+ var fullName = new Name(this.getName());
+ var hash = Crypto.createHash('sha256');
+ // wireEncode will use the cached encoding if possible.
+ hash.update(this.wireEncode(wireFormat).buf());
+ fullName.appendImplicitSha256Digest(new Blob(hash.digest(), false));
+
+ if (wireFormat == WireFormat.getDefaultWireFormat())
+ // wireEncode has already set defaultWireEncodingFormat_.
+ this.defaultFullName_ = fullName;
+
+ return fullName;
+};
+
+/**
+ * Set name to a copy of the given Name.
+ * @param {Name} name The Name which is copied.
+ * @return {Data} This Data so that you can chain calls to update values.
+ */
+Data.prototype.setName = function(name)
+{
+ this.name_.set(typeof name === 'object' && name instanceof Name ?
+ new Name(name) : new Name());
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Set metaInfo to a copy of the given MetaInfo.
+ * @param {MetaInfo} metaInfo The MetaInfo which is copied.
+ * @return {Data} This Data so that you can chain calls to update values.
+ */
+Data.prototype.setMetaInfo = function(metaInfo)
+{
+ this.metaInfo_.set(typeof metaInfo === 'object' && metaInfo instanceof MetaInfo ?
+ new MetaInfo(metaInfo) : new MetaInfo());
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Set the signature to a copy of the given signature.
+ * @param {Signature} signature The signature object which is cloned.
+ * @return {Data} This Data so that you can chain calls to update values.
+ */
+Data.prototype.setSignature = function(signature)
+{
+ this.signature_.set(signature == null ?
+ new Sha256WithRsaSignature() : signature.clone());
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Set the content to the given value.
+ * @param {Blob|Buffer} content The content bytes. If content is not a Blob,
+ * then create a new Blob to copy the bytes (otherwise take another pointer to
+ * the same Blob).
+ * @return {Data} This Data so that you can chain calls to update values.
+ */
+Data.prototype.setContent = function(content)
+{
+ this.content_ = typeof content === 'object' && content instanceof Blob ?
+ content : new Blob(content, true);
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Encode this Data for a particular wire format. If wireFormat is the default
+ * wire format, also set the defaultWireEncoding field to the encoded result.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {SignedBlob} The encoded buffer in a SignedBlob object.
+ */
+Data.prototype.wireEncode = function(wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ if (!this.getDefaultWireEncoding().isNull() &&
+ this.getDefaultWireEncodingFormat() == wireFormat)
+ // We already have an encoding in the desired format.
+ return this.getDefaultWireEncoding();
+
+ var result = wireFormat.encodeData(this);
+ var wireEncoding = new SignedBlob
+ (result.encoding, result.signedPortionBeginOffset,
+ result.signedPortionEndOffset);
+
+ if (wireFormat == WireFormat.getDefaultWireFormat())
+ // This is the default wire encoding.
+ this.setDefaultWireEncoding
+ (wireEncoding, WireFormat.getDefaultWireFormat());
+ return wireEncoding;
+};
+
+/**
+ * Decode the input using a particular wire format and update this Data. If
+ * wireFormat is the default wire format, also set the defaultWireEncoding to
+ * another pointer to the input.
+ * @param {Blob|Buffer} input The buffer with the bytes to decode.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to decode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ */
+Data.prototype.wireDecode = function(input, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ var result;
+ if (typeof input === 'object' && input instanceof Blob)
+ // Input is a blob, so get its buf() and set copy false.
+ result = wireFormat.decodeData(this, input.buf(), false);
+ else
+ result = wireFormat.decodeData(this, input, true);
+
+ if (wireFormat == WireFormat.getDefaultWireFormat())
+ // This is the default wire encoding. In the Blob constructor, set copy
+ // true, but if input is already a Blob, it won't copy.
+ this.setDefaultWireEncoding(new SignedBlob
+ (new Blob(input, true), result.signedPortionBeginOffset,
+ result.signedPortionEndOffset),
+ WireFormat.getDefaultWireFormat());
+ else
+ this.setDefaultWireEncoding(new SignedBlob(), null);
+};
+
+/**
+ * An internal library method to set the LpPacket for an incoming packet. The
+ * application should not call this.
+ * @param {LpPacket} lpPacket The LpPacket. This does not make a copy.
+ * @return {Data} This Data so that you can chain calls to update values.
+ * @note This is an experimental feature. This API may change in the future.
+ */
+Data.prototype.setLpPacket = function(lpPacket)
+{
+ this.lpPacket_ = lpPacket;
+ // Don't update changeCount_ since this doesn't affect the wire encoding.
+ return this;
+}
+
+/**
+ * Get the change count, which is incremented each time this object (or a child
+ * object) is changed.
+ * @return {number} The change count.
+ */
+Data.prototype.getChangeCount = function()
+{
+ // Make sure each of the checkChanged is called.
+ var changed = this.name_.checkChanged();
+ changed = this.metaInfo_.checkChanged() || changed;
+ changed = this.signature_.checkChanged() || changed;
+ if (changed)
+ // A child object has changed, so update the change count.
+ ++this.changeCount_;
+
+ return this.changeCount_;
+};
+
+Data.prototype.setDefaultWireEncoding = function
+ (defaultWireEncoding, defaultWireEncodingFormat)
+{
+ this.defaultWireEncoding_ = defaultWireEncoding;
+ this.defaultWireEncodingFormat_ = defaultWireEncodingFormat;
+ // Set getDefaultWireEncodingChangeCount_ so that the next call to
+ // getDefaultWireEncoding() won't clear _defaultWireEncoding.
+ this.getDefaultWireEncodingChangeCount_ = this.getChangeCount();
+};
+
+// Define properties so we can change member variable types and implement changeCount_.
+Object.defineProperty(Data.prototype, "name",
+ { get: function() { return this.getName(); },
+ set: function(val) { this.setName(val); } });
+Object.defineProperty(Data.prototype, "metaInfo",
+ { get: function() { return this.getMetaInfo(); },
+ set: function(val) { this.setMetaInfo(val); } });
+Object.defineProperty(Data.prototype, "signature",
+ { get: function() { return this.getSignature(); },
+ set: function(val) { this.setSignature(val); } });
+/**
+ * @deprecated Use getContent and setContent.
+ */
+Object.defineProperty(Data.prototype, "content",
+ { get: function() { return this.getContentAsBuffer(); },
+ set: function(val) { this.setContent(val); } });
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * Create a new SecurityException to report an exception from the security
+ * library, wrapping the given error object.
+ * Call with: throw new SecurityException(new Error("message")).
+ * @constructor
+ * @param {Error} error The exception created with new Error.
+ */
+function SecurityException(error)
+{
+ if (error) {
+ error.__proto__ = SecurityException.prototype;
+ return error;
+ }
+}
+
+SecurityException.prototype = new Error();
+SecurityException.prototype.name = "SecurityException";
+
+exports.SecurityException = SecurityException;
+
+function UnrecognizedKeyFormatException(error)
+{
+ // Call the base constructor.
+ SecurityException.call(this, error);
+}
+UnrecognizedKeyFormatException.prototype = new SecurityException();
+UnrecognizedKeyFormatException.prototype.name = "UnrecognizedKeyFormatException";
+
+exports.UnrecognizedKeyFormatException = UnrecognizedKeyFormatException;
+
+function UnrecognizedDigestAlgorithmException(error)
+{
+ // Call the base constructor.
+ SecurityException.call(this, error);
+}
+UnrecognizedDigestAlgorithmException.prototype = new SecurityException();
+UnrecognizedDigestAlgorithmException.prototype.name = "UnrecognizedDigestAlgorithmException";
+
+exports.UnrecognizedDigestAlgorithmException = UnrecognizedDigestAlgorithmException;
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * This module defines constants used by the security library.
+ */
+
+/**
+ * The KeyType integer is used by the Sqlite key storage, so don't change them.
+ * Make these the same as ndn-cxx in case the storage file is shared.
+ * @constructor
+ */
+var KeyType = function KeyType()
+{
+}
+
+exports.KeyType = KeyType;
+
+KeyType.RSA = 0;
+KeyType.ECDSA = 1;
+KeyType.AES = 128;
+
+var KeyClass = function KeyClass()
+{
+};
+
+exports.KeyClass = KeyClass;
+
+KeyClass.PUBLIC = 1;
+KeyClass.PRIVATE = 2;
+KeyClass.SYMMETRIC = 3;
+
+var DigestAlgorithm = function DigestAlgorithm()
+{
+};
+
+exports.DigestAlgorithm = DigestAlgorithm;
+
+DigestAlgorithm.SHA256 = 1;
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var KeyType = require('./security-types.js').KeyType;
+
+/**
+ * KeyParams is a base class for key parameters. Its subclasses are used to
+ * store parameters for key generation. You should create one of the subclasses,
+ * for example RsaKeyParams.
+ * @constructor
+ */
+var KeyParams = function KeyParams(keyType)
+{
+ this.keyType = keyType;
+};
+
+exports.KeyParams = KeyParams;
+
+KeyParams.prototype.getKeyType = function()
+{
+ return this.keyType;
+};
+
+var RsaKeyParams = function RsaKeyParams(size)
+{
+ // Call the base constructor.
+ KeyParams.call(this, RsaKeyParams.getType());
+
+ if (size == null)
+ size = RsaKeyParams.getDefaultSize();
+ this.size = size;
+};
+
+RsaKeyParams.prototype = new KeyParams();
+RsaKeyParams.prototype.name = "RsaKeyParams";
+
+exports.RsaKeyParams = RsaKeyParams;
+
+RsaKeyParams.prototype.getKeySize = function()
+{
+ return this.size;
+};
+
+RsaKeyParams.getDefaultSize = function() { return 2048; };
+
+RsaKeyParams.getType = function() { return KeyType.RSA; };
+
+var EcdsaKeyParams = function EcdsaKeyParams(size)
+{
+ // Call the base constructor.
+ KeyParams.call(this, EcdsaKeyParams.getType());
+
+ if (size == null)
+ size = EcdsaKeyParams.getDefaultSize();
+ this.size = size;
+};
+
+EcdsaKeyParams.prototype = new KeyParams();
+EcdsaKeyParams.prototype.name = "EcdsaKeyParams";
+
+exports.EcdsaKeyParams = EcdsaKeyParams;
+
+EcdsaKeyParams.prototype.getKeySize = function()
+{
+ return this.size;
+};
+
+EcdsaKeyParams.getDefaultSize = function() { return 256; };
+
+EcdsaKeyParams.getType = function() { return KeyType.ECDSA; };
+
+var AesKeyParams = function AesKeyParams(size)
+{
+ // Call the base constructor.
+ KeyParams.call(this, AesKeyParams.getType());
+
+ if (size == null)
+ size = AesKeyParams.getDefaultSize();
+ this.size = size;
+};
+
+AesKeyParams.prototype = new KeyParams();
+AesKeyParams.prototype.name = "AesKeyParams";
+
+exports.AesKeyParams = AesKeyParams;
+
+AesKeyParams.prototype.getKeySize = function()
+{
+ return this.size;
+};
+
+AesKeyParams.getDefaultSize = function() { return 64; };
+
+AesKeyParams.getType = function() { return KeyType.AES; };
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// Use capitalized Crypto to not clash with the browser's crypto.subtle.
+/** @ignore */
+var Crypto = require('../../crypto.js'); /** @ignore */
+var Blob = require('../../util/blob.js').Blob; /** @ignore */
+var DerDecodingException = require('../../encoding/der/der-decoding-exception.js').DerDecodingException; /** @ignore */
+var DerNode = require('../../encoding/der/der-node.js').DerNode; /** @ignore */
+var SecurityException = require('../security-exception.js').SecurityException; /** @ignore */
+var UnrecognizedKeyFormatException = require('../security-exception.js').UnrecognizedKeyFormatException; /** @ignore */
+var KeyType = require('../security-types.js').KeyType; /** @ignore */
+var DigestAlgorithm = require('../security-types.js').DigestAlgorithm;
+
+/**
+ * Create a new PublicKey by decoding the keyDer. Set the key type from the
+ * decoding.
+ * @param {Blob} keyDer The blob of the SubjectPublicKeyInfo DER.
+ * @throws UnrecognizedKeyFormatException if can't decode the key DER.
+ * @constructor
+ */
+var PublicKey = function PublicKey(keyDer)
+{
+ if (!keyDer) {
+ this.keyDer = new Blob();
+ this.keyType = null;
+ return;
+ }
+
+ this.keyDer = keyDer;
+
+ // Get the public key OID.
+ var oidString = null;
+ try {
+ var parsedNode = DerNode.parse(keyDer.buf(), 0);
+ var rootChildren = parsedNode.getChildren();
+ var algorithmIdChildren = DerNode.getSequence(rootChildren, 0).getChildren();
+ oidString = algorithmIdChildren[0].toVal();
+ }
+ catch (ex) {
+ throw new UnrecognizedKeyFormatException(new Error
+ ("PublicKey.decodeKeyType: Error decoding the public key: " + ex.message));
+ }
+
+ // Verify that the we can decode.
+ if (oidString == PublicKey.RSA_ENCRYPTION_OID) {
+ this.keyType = KeyType.RSA;
+ // TODO: Check RSA decoding.
+ }
+ else if (oidString == PublicKey.EC_ENCRYPTION_OID) {
+ this.keyType = KeyType.ECDSA;
+ // TODO: Check EC decoding.
+ }
+};
+
+exports.PublicKey = PublicKey;
+
+/**
+ * Encode the public key into DER.
+ * @return {DerNode} The encoded DER syntax tree.
+ */
+PublicKey.prototype.toDer = function()
+{
+ return DerNode.parse(this.keyDer.buf());
+};
+
+/**
+ * Get the key type.
+ * @return {number} The key type as an int from KeyType.
+ */
+PublicKey.prototype.getKeyType = function()
+{
+ return this.keyType;
+};
+
+/**
+ * Get the digest of the public key.
+ * @param {number} digestAlgorithm (optional) The integer from DigestAlgorithm,
+ * such as DigestAlgorithm.SHA256. If omitted, use DigestAlgorithm.SHA256 .
+ * @return {Blob} The digest value.
+ */
+PublicKey.prototype.getDigest = function(digestAlgorithm)
+{
+ if (digestAlgorithm == undefined)
+ digestAlgorithm = DigestAlgorithm.SHA256;
+
+ if (digestAlgorithm == DigestAlgorithm.SHA256) {
+ var hash = Crypto.createHash('sha256');
+ hash.update(this.keyDer.buf());
+ return new Blob(hash.digest(), false);
+ }
+ else
+ throw new SecurityException(new Error("Wrong format!"));
+};
+
+/**
+ * Get the raw bytes of the public key in DER format.
+ * @return {Blob} The public key DER.
+ */
+PublicKey.prototype.getKeyDer = function()
+{
+ return this.keyDer;
+};
+
+PublicKey.RSA_ENCRYPTION_OID = "1.2.840.113549.1.1.1";
+PublicKey.EC_ENCRYPTION_OID = "1.2.840.10045.2.1";
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var DerNode = require('../../encoding/der/der-node.js').DerNode; /** @ignore */
+var OID = require('../../encoding/oid.js').OID;
+
+/**
+ * A CertificateExtension represents the Extension entry in a certificate.
+ * Create a new CertificateExtension.
+ * @param {string|OID} oid The oid of subject description entry.
+ * @param {boolean} isCritical If true, the extension must be handled.
+ * @param {Blob} value The extension value.
+ * @constructor
+ */
+var CertificateExtension = function CertificateExtension(oid, isCritical, value)
+{
+ if (typeof oid === 'string')
+ this.extensionId = new OID(oid);
+ else
+ // Assume oid is already an OID.
+ this.extensionId = oid;
+
+ this.isCritical = isCritical;
+ this.extensionValue = value;
+};
+
+exports.CertificateExtension = CertificateExtension;
+
+/**
+ * Encode the object into a DER syntax tree.
+ * @return {DerNode} The encoded DER syntax tree.
+ */
+CertificateExtension.prototype.toDer = function()
+{
+ var root = new DerNode.DerSequence();
+
+ var extensionId = new DerNode.DerOid(this.extensionId);
+ var isCritical = new DerNode.DerBoolean(this.isCritical);
+ var extensionValue = new DerNode.DerOctetString(this.extensionValue.buf());
+
+ root.addChild(extensionId);
+ root.addChild(isCritical);
+ root.addChild(extensionValue);
+
+ root.getSize();
+
+ return root;
+};
+
+CertificateExtension.prototype.toDerBlob = function()
+{
+ return this.toDer().encode();
+};
+
+CertificateExtension.prototype.getOid = function()
+{
+ return this.extensionId;
+};
+
+CertificateExtension.prototype.getIsCritical = function()
+{
+ return this.isCritical;
+};
+
+CertificateExtension.prototype.getValue = function()
+{
+ return this.extensionValue;
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('../../util/blob.js').Blob; /** @ignore */
+var OID = require('../../encoding/oid.js').OID; /** @ignore */
+var DerNode = require('../../encoding/der/der-node.js').DerNode;
+
+/**
+ * A CertificateSubjectDescription represents the SubjectDescription entry in a
+ * Certificate.
+ * Create a new CertificateSubjectDescription.
+ * @param {string|OID} oid The oid of the subject description entry.
+ * @param {string} value The value of the subject description entry.
+ * @constructor
+ */
+var CertificateSubjectDescription = function CertificateSubjectDescription
+ (oid, value)
+{
+ if (typeof oid === 'string')
+ this.oid = new OID(oid);
+ else
+ // Assume oid is already an OID.
+ this.oid = oid;
+
+ this.value = value;
+};
+
+exports.CertificateSubjectDescription = CertificateSubjectDescription;
+
+/**
+ * Encode the object into a DER syntax tree.
+ * @return {DerNode} The encoded DER syntax tree.
+ */
+CertificateSubjectDescription.prototype.toDer = function()
+{
+ var root = new DerNode.DerSequence();
+
+ var oid = new DerNode.DerOid(this.oid);
+ // Use Blob to convert the String to a ByteBuffer.
+ var value = new DerNode.DerPrintableString(new Blob(this.value).buf());
+
+ root.addChild(oid);
+ root.addChild(value);
+
+ return root;
+};
+
+CertificateSubjectDescription.prototype.getOidString = function()
+{
+ return this.oid.toString();
+};
+
+CertificateSubjectDescription.prototype.getValue = function()
+{
+ return this.value;
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Data = require('../../data.js').Data; /** @ignore */
+var ContentType = require('../../meta-info.js').ContentType; /** @ignore */
+var WireFormat = require('../../encoding/wire-format.js').WireFormat; /** @ignore */
+var DerNode = require('../../encoding/der/der-node.js').DerNode; /** @ignore */
+var KeyType = require('../../security/security-types.js').KeyType; /** @ignore */
+var PublicKey = require('./public-key.js').PublicKey; /** @ignore */
+var CertificateSubjectDescription = require('./certificate-subject-description.js').CertificateSubjectDescription; /** @ignore */
+var CertificateExtension = require('./certificate-extension.js').CertificateExtension;
+
+/**
+ * Create a Certificate from the content in the data packet (if not omitted).
+ * @param {Data} data (optional) The data packet with the content to decode.
+ * If omitted, create a Certificate with default values and the Data content
+ * is empty.
+ * @constructor
+ */
+var Certificate = function Certificate(data)
+{
+ // Call the base constructor.
+ if (data != undefined)
+ Data.call(this, data);
+ else
+ Data.call(this);
+
+ this.subjectDescriptionList = []; // of CertificateSubjectDescription
+ this.extensionList = []; // of CertificateExtension
+ this.notBefore = Number.MAX_VALUE; // MillisecondsSince1970
+ this.notAfter = -Number.MAX_VALUE; // MillisecondsSince1970
+ this.key = new PublicKey();
+
+ if (data != undefined)
+ this.decode();
+};
+Certificate.prototype = new Data();
+Certificate.prototype.name = "Certificate";
+
+exports.Certificate = Certificate;
+
+/**
+ * Encode the contents of the certificate in DER format and set the Content
+ * and MetaInfo fields.
+ */
+Certificate.prototype.encode = function()
+{
+ var root = this.toDer();
+ this.setContent(root.encode());
+ this.getMetaInfo().setType(ContentType.KEY);
+};
+
+/**
+ * Add a subject description.
+ * @param {CertificateSubjectDescription} description The description to be added.
+ */
+Certificate.prototype.addSubjectDescription = function(description)
+{
+ this.subjectDescriptionList.push(description);
+};
+
+/**
+ * Get the subject description list.
+ * @return {Array<CertificateSubjectDescription>} The subject description list.
+ */
+Certificate.prototype.getSubjectDescriptionList = function()
+{
+ return this.subjectDescriptionList;
+};
+
+/**
+ * Add a certificate extension.
+ * @param {CertificateSubjectDescription} extension The extension to be added.
+ */
+Certificate.prototype.addExtension = function(extension)
+{
+ this.extensionList.push(extension);
+};
+
+/**
+ * Get the certificate extension list.
+ * @return {Array<CertificateExtension>} The extension list.
+ */
+Certificate.prototype.getExtensionList = function()
+{
+ return this.extensionList;
+};
+
+Certificate.prototype.setNotBefore = function(notBefore)
+{
+ this.notBefore = notBefore;
+};
+
+Certificate.prototype.getNotBefore = function()
+{
+ return this.notBefore;
+};
+
+Certificate.prototype.setNotAfter = function(notAfter)
+{
+ this.notAfter = notAfter;
+};
+
+Certificate.prototype.getNotAfter = function()
+{
+ return this.notAfter;
+};
+
+Certificate.prototype.setPublicKeyInfo = function(key)
+{
+ this.key = key;
+};
+
+Certificate.prototype.getPublicKeyInfo = function()
+{
+ return this.key;
+};
+
+/**
+ * Check if the certificate is valid.
+ * @return {Boolean} True if the current time is earlier than notBefore.
+ */
+Certificate.prototype.isTooEarly = function()
+{
+ var now = new Date().getTime();
+ return now < this.notBefore;
+};
+
+/**
+ * Check if the certificate is valid.
+ * @return {Boolean} True if the current time is later than notAfter.
+ */
+Certificate.prototype.isTooLate = function()
+{
+ var now = new Date().getTime();
+ return now > this.notAfter;
+};
+
+/**
+ * Encode the certificate fields in DER format.
+ * @return {DerSequence} The DER encoded contents of the certificate.
+ */
+Certificate.prototype.toDer = function()
+{
+ var root = new DerNode.DerSequence();
+ var validity = new DerNode.DerSequence();
+ var notBefore = new DerNode.DerGeneralizedTime(this.notBefore);
+ var notAfter = new DerNode.DerGeneralizedTime(this.notAfter);
+
+ validity.addChild(notBefore);
+ validity.addChild(notAfter);
+
+ root.addChild(validity);
+
+ var subjectList = new DerNode.DerSequence();
+ for (var i = 0; i < this.subjectDescriptionList.length; ++i)
+ subjectList.addChild(this.subjectDescriptionList[i].toDer());
+
+ root.addChild(subjectList);
+ root.addChild(this.key.toDer());
+
+ if (this.extensionList.length > 0) {
+ var extensionList = new DerNode.DerSequence();
+ for (var i = 0; i < this.extensionList.length; ++i)
+ extensionList.addChild(this.extensionList[i].toDer());
+ root.addChild(extensionList);
+ }
+
+ return root;
+};
+
+/**
+ * Populate the fields by the decoding DER data from the Content.
+ */
+Certificate.prototype.decode = function()
+{
+ var root = DerNode.parse(this.getContent().buf());
+
+ // We need to ensure that there are:
+ // validity (notBefore, notAfter)
+ // subject list
+ // public key
+ // (optional) extension list
+
+ var rootChildren = root.getChildren();
+ // 1st: validity info
+ var validityChildren = DerNode.getSequence(rootChildren, 0).getChildren();
+ this.notBefore = validityChildren[0].toVal();
+ this.notAfter = validityChildren[1].toVal();
+
+ // 2nd: subjectList
+ var subjectChildren = DerNode.getSequence(rootChildren, 1).getChildren();
+ for (var i = 0; i < subjectChildren.length; ++i) {
+ var sd = DerNode.getSequence(subjectChildren, i);
+ var descriptionChildren = sd.getChildren();
+ var oidStr = descriptionChildren[0].toVal();
+ var value = descriptionChildren[1].toVal().buf().toString('binary');
+
+ this.addSubjectDescription(new CertificateSubjectDescription(oidStr, value));
+ }
+
+ // 3rd: public key
+ var publicKeyInfo = rootChildren[2].encode();
+ this.key = new PublicKey(publicKeyInfo);
+
+ if (rootChildren.length > 3) {
+ var extensionChildren = DerNode.getSequence(rootChildren, 3).getChildren();
+ for (var i = 0; i < extensionChildren.length; ++i) {
+ var extInfo = DerNode.getSequence(extensionChildren, i);
+
+ var children = extInfo.getChildren();
+ var oidStr = children[0].toVal();
+ var isCritical = children[1].toVal();
+ var value = children[2].toVal();
+ this.addExtension(new CertificateExtension(oidStr, isCritical, value));
+ }
+ }
+};
+
+/**
+ * Override to call the base class wireDecode then populate the certificate
+ * fields.
+ * @param {Blob|Buffer} input The buffer with the bytes to decode.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to decode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ */
+Certificate.prototype.wireDecode = function(input, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ Data.prototype.wireDecode.call(this, input, wireFormat);
+ this.decode();
+};
+
+Certificate.prototype.toString = function()
+{
+ var s = "Certificate name:\n";
+ s += " " + this.getName().toUri() + "\n";
+ s += "Validity:\n";
+
+ var notBeforeStr = Certificate.toIsoString(Math.round(this.notBefore));
+ var notAfterStr = Certificate.toIsoString(Math.round(this.notAfter));
+
+ s += " NotBefore: " + notBeforeStr + "\n";
+ s += " NotAfter: " + notAfterStr + "\n";
+ for (var i = 0; i < this.subjectDescriptionList.length; ++i) {
+ var sd = this.subjectDescriptionList[i];
+ s += "Subject Description:\n";
+ s += " " + sd.getOidString() + ": " + sd.getValue() + "\n";
+ }
+
+ s += "Public key bits:\n";
+ var keyDer = this.key.getKeyDer();
+ var encodedKey = keyDer.buf().toString('base64');
+ for (var i = 0; i < encodedKey.length; i += 64)
+ s += encodedKey.substring(i, Math.min(i + 64, encodedKey.length)) + "\n";
+
+ if (this.extensionList.length > 0) {
+ s += "Extensions:\n";
+ for (var i = 0; i < this.extensionList.length; ++i) {
+ var ext = this.extensionList[i];
+ s += " OID: " + ext.getOid() + "\n";
+ s += " Is critical: " + (ext.getIsCritical() ? 'Y' : 'N') + "\n";
+
+ s += " Value: " + ext.getValue().toHex() + "\n" ;
+ }
+ }
+
+ return s;
+};
+
+/**
+ * Convert a UNIX timestamp to ISO time representation with the "T" in the middle.
+ * @param {type} msSince1970 Timestamp as milliseconds since Jan 1, 1970.
+ * @return {string} The string representation.
+ */
+Certificate.toIsoString = function(msSince1970)
+{
+ var utcTime = new Date(Math.round(msSince1970));
+ return utcTime.getUTCFullYear() +
+ Certificate.to2DigitString(utcTime.getUTCMonth() + 1) +
+ Certificate.to2DigitString(utcTime.getUTCDate()) +
+ "T" +
+ Certificate.to2DigitString(utcTime.getUTCHours()) +
+ Certificate.to2DigitString(utcTime.getUTCMinutes()) +
+ Certificate.to2DigitString(utcTime.getUTCSeconds());
+};
+
+/**
+ * A private method to zero pad an integer to 2 digits.
+ * @param {number} x The number to pad. Assume it is a non-negative integer.
+ * @return {string} The padded string.
+ */
+Certificate.to2DigitString = function(x)
+{
+ var result = x.toString();
+ return result.length === 1 ? "0" + result : result;
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Data = require('../../data.js').Data; /** @ignore */
+var Name = require('../../name.js').Name; /** @ignore */
+var SecurityException = require('../../security//security-exception.js').SecurityException; /** @ignore */
+var Certificate = require('./certificate.js').Certificate; /** @ignore */
+var WireFormat = require('../../encoding/wire-format.js').WireFormat;
+
+/**
+ * @constructor
+ */
+var IdentityCertificate = function IdentityCertificate(data)
+{
+ // Call the base constructor.
+ if (data != undefined)
+ // This works if data is Data or IdentityCertificate.
+ Certificate.call(this, data);
+ else
+ Certificate.call(this);
+
+ this.publicKeyName = new Name();
+
+ if (data instanceof IdentityCertificate) {
+ // The copy constructor.
+ this.publicKeyName = new Name(data.publicKeyName);
+ }
+ else if (data instanceof Data) {
+ if (!IdentityCertificate.isCorrectName(data.getName()))
+ throw new SecurityException(new Error("Wrong Identity Certificate Name!"));
+
+ this.setPublicKeyName();
+ }
+};
+IdentityCertificate.prototype = new Certificate();
+IdentityCertificate.prototype.name = "IdentityCertificate";
+
+exports.IdentityCertificate = IdentityCertificate;
+
+/**
+ * Override the base class method to check that the name is a valid identity
+ * certificate name.
+ * @param {Name} name The identity certificate name which is copied.
+ * @return {Data} This Data so that you can chain calls to update values.
+ */
+IdentityCertificate.prototype.setName = function(name)
+{
+ if (!IdentityCertificate.isCorrectName(name))
+ throw new SecurityException(new Error("Wrong Identity Certificate Name!"));
+
+ // Call the super class method.
+ Certificate.prototype.setName.call(this, name);
+ this.setPublicKeyName();
+ return this;
+};
+
+/**
+ * Override to call the base class wireDecode then update the public key name.
+ * @param {Blob|Buffer} input The buffer with the bytes to decode.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to decode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ */
+IdentityCertificate.prototype.wireDecode = function(input, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ Certificate.prototype.wireDecode.call(this, input, wireFormat);
+ this.setPublicKeyName();
+};
+
+IdentityCertificate.prototype.getPublicKeyName = function()
+{
+ return this.publicKeyName;
+};
+
+IdentityCertificate.isIdentityCertificate = function(certificate)
+{
+ return IdentityCertificate.isCorrectName(certificate.getName());
+};
+
+/**
+ * Get the public key name from the full certificate name.
+ * @param {Name} certificateName The full certificate name.
+ * @return {Name} The related public key name.
+ */
+IdentityCertificate.certificateNameToPublicKeyName = function(certificateName)
+{
+ var idString = "ID-CERT";
+ var foundIdString = false;
+ var idCertComponentIndex = certificateName.size() - 1;
+ for (; idCertComponentIndex + 1 > 0; --idCertComponentIndex) {
+ if (certificateName.get(idCertComponentIndex).toEscapedString() == idString) {
+ foundIdString = true;
+ break;
+ }
+ }
+
+ if (!foundIdString)
+ throw new Error
+ ("Incorrect identity certificate name " + certificateName.toUri());
+
+ var tempName = certificateName.getSubName(0, idCertComponentIndex);
+ var keyString = "KEY";
+ var foundKeyString = false;
+ var keyComponentIndex = 0;
+ for (; keyComponentIndex < tempName.size(); keyComponentIndex++) {
+ if (tempName.get(keyComponentIndex).toEscapedString() == keyString) {
+ foundKeyString = true;
+ break;
+ }
+ }
+
+ if (!foundKeyString)
+ throw new Error
+ ("Incorrect identity certificate name " + certificateName.toUri());
+
+ return tempName
+ .getSubName(0, keyComponentIndex)
+ .append(tempName.getSubName
+ (keyComponentIndex + 1, tempName.size() - keyComponentIndex - 1));
+};
+
+IdentityCertificate.isCorrectName = function(name)
+{
+ var i = name.size() - 1;
+
+ var idString = "ID-CERT";
+ for (; i >= 0; i--) {
+ if (name.get(i).toEscapedString() == idString)
+ break;
+ }
+
+ if (i < 0)
+ return false;
+
+ var keyIdx = 0;
+ var keyString = "KEY";
+ for (; keyIdx < name.size(); keyIdx++) {
+ if(name.get(keyIdx).toEscapedString() == keyString)
+ break;
+ }
+
+ if (keyIdx >= name.size())
+ return false;
+
+ return true;
+};
+
+IdentityCertificate.prototype.setPublicKeyName = function()
+{
+ this.publicKeyName = IdentityCertificate.certificateNameToPublicKeyName
+ (this.getName());
+};
+
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Name = require('../../name.js').Name; /** @ignore */
+var SecurityException = require('../security-exception.js').SecurityException; /** @ignore */
+var SyncPromise = require('../../util/sync-promise.js').SyncPromise;
+
+/**
+ * IdentityStorage is a base class for the storage of identity, public keys and
+ * certificates. Private keys are stored in PrivateKeyStorage.
+ * This is an abstract base class. A subclass must implement the methods.
+ * @constructor
+ */
+var IdentityStorage = function IdentityStorage()
+{
+};
+
+exports.IdentityStorage = IdentityStorage;
+
+/**
+ * Check if the specified identity already exists.
+ * @param {Name} identityName The identity name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns true if the identity
+ * exists.
+ */
+IdentityStorage.prototype.doesIdentityExistPromise = function
+ (identityName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.doesIdentityExistPromise is not implemented"));
+};
+
+/**
+ * Check if the specified identity already exists.
+ * @param {Name} identityName The identity name.
+ * @return {boolean} true if the identity exists, otherwise false.
+ * @throws Error If doesIdentityExistPromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+IdentityStorage.prototype.doesIdentityExist = function(identityName)
+{
+ return SyncPromise.getValue(this.doesIdentityExistPromise(identityName, true));
+};
+
+/**
+ * Add a new identity. Do nothing if the identity already exists.
+ * @param {Name} identityName The identity name to be added.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when the identity is
+ * added.
+ */
+IdentityStorage.prototype.addIdentityPromise = function(identityName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.addIdentityPromise is not implemented"));
+};
+
+/**
+ * Add a new identity. Do nothing if the identity already exists.
+ * @param {Name} identityName The identity name to be added.
+ * @throws Error If addIdentityPromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+IdentityStorage.prototype.addIdentity = function(identityName)
+{
+ return SyncPromise.getValue(this.addIdentityPromise(identityName, true));
+};
+
+/**
+ * Revoke the identity.
+ * @return {boolean} true if the identity was revoked, false if not.
+ */
+IdentityStorage.prototype.revokeIdentity = function()
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.revokeIdentity is not implemented"));
+};
+
+/**
+ * Generate a name for a new key belonging to the identity.
+ * @param {Name} identityName The identity name.
+ * @param {boolean} useKsk If true, generate a KSK name, otherwise a DSK name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns the generated key Name.
+ */
+IdentityStorage.prototype.getNewKeyNamePromise = function
+ (identityName, useKsk, useSync)
+{
+ var timestamp = Math.floor(new Date().getTime() / 1000.0);
+ while (timestamp <= IdentityStorage.lastTimestamp)
+ // Make the timestamp unique.
+ timestamp += 1;
+ IdentityStorage.lastTimestamp = timestamp;
+
+ // Get the number of seconds as a string.
+ var seconds = "" + timestamp;
+
+ var keyIdStr;
+ if (useKsk)
+ keyIdStr = "ksk-" + seconds;
+ else
+ keyIdStr = "dsk-" + seconds;
+
+ var keyName = new Name(identityName).append(keyIdStr);
+
+ return this.doesKeyExistPromise(keyName, useSync)
+ .then(function(exists) {
+ if (exists)
+ throw new SecurityException(new Error("Key name already exists"));
+
+ return SyncPromise.resolve(keyName);
+ });
+};
+
+/**
+ * Generate a name for a new key belonging to the identity.
+ * @param {Name} identityName The identity name.
+ * @param {boolean} useKsk If true, generate a KSK name, otherwise a DSK name.
+ * @return {Name} The generated key name.
+ * @throws Error If getNewKeyNamePromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+IdentityStorage.prototype.getNewKeyName = function(identityName, useKsk)
+{
+ return SyncPromise.getValue
+ (this.getNewKeyNamePromise(identityName, useKsk, true));
+};
+
+/**
+ * Check if the specified key already exists.
+ * @param {Name} keyName The name of the key.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns true if the key exists.
+ */
+IdentityStorage.prototype.doesKeyExistPromise = function(keyName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.doesKeyExistPromise is not implemented"));
+};
+
+/**
+ * Check if the specified key already exists.
+ * @param {Name} keyName The name of the key.
+ * @return {boolean} true if the key exists, otherwise false.
+ * @throws Error If doesKeyExistPromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+IdentityStorage.prototype.doesKeyExist = function(keyName)
+{
+ return SyncPromise.getValue(this.doesKeyExistPromise(keyName, true));
+};
+
+/**
+ * Add a public key to the identity storage. Also call addIdentity to ensure
+ * that the identityName for the key exists. However, if the key already
+ * exists, do nothing.
+ * @param {Name} keyName The name of the public key to be added.
+ * @param {number} keyType Type of the public key to be added from KeyType, such
+ * as KeyType.RSA..
+ * @param {Blob} publicKeyDer A blob of the public key DER to be added.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when complete.
+ */
+IdentityStorage.prototype.addKeyPromise = function
+ (keyName, keyType, publicKeyDer, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.addKeyPromise is not implemented"));
+};
+
+/**
+ * Add a public key to the identity storage. Also call addIdentity to ensure
+ * that the identityName for the key exists.
+ * @param {Name} keyName The name of the public key to be added.
+ * @param {number} keyType Type of the public key to be added from KeyType, such
+ * as KeyType.RSA..
+ * @param {Blob} publicKeyDer A blob of the public key DER to be added.
+ * @throws SecurityException if a key with the keyName already exists.
+ * @throws Error If addKeyPromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+IdentityStorage.prototype.addKey = function(keyName, keyType, publicKeyDer)
+{
+ return SyncPromise.getValue
+ (this.addKeyPromise(keyName, keyType, publicKeyDer, true));
+};
+
+/**
+ * Get the public key DER blob from the identity storage.
+ * @param {Name} keyName The name of the requested public key.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the DER Blob, or a
+ * promise rejected with SecurityException if the key doesn't exist.
+ */
+IdentityStorage.prototype.getKeyPromise = function(keyName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.getKeyPromise is not implemented"));
+};
+
+/**
+ * Get the public key DER blob from the identity storage.
+ * @param {Name} keyName The name of the requested public key.
+ * @return {Blob} The DER Blob.
+ * @throws SecurityException if the key doesn't exist.
+ * @throws Error If getKeyPromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+IdentityStorage.prototype.getKey = function(keyName)
+{
+ return SyncPromise.getValue(this.getKeyPromise(keyName, true));
+};
+
+/**
+ * Activate a key. If a key is marked as inactive, its private part will not be
+ * used in packet signing.
+ * @param {Name} keyName name of the key
+ */
+IdentityStorage.prototype.activateKey = function(keyName)
+{
+ throw new Error("IdentityStorage.activateKey is not implemented");
+};
+
+/**
+ * Deactivate a key. If a key is marked as inactive, its private part will not
+ * be used in packet signing.
+ * @param {Name} keyName name of the key
+ */
+IdentityStorage.prototype.deactivateKey = function(keyName)
+{
+ throw new Error("IdentityStorage.deactivateKey is not implemented");
+};
+
+/**
+ * Check if the specified certificate already exists.
+ * @param {Name} certificateName The name of the certificate.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns true if the certificate
+ * exists.
+ */
+IdentityStorage.prototype.doesCertificateExistPromise = function
+ (certificateName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.doesCertificateExistPromise is not implemented"));
+};
+
+/**
+ * Check if the specified certificate already exists.
+ * @param {Name} certificateName The name of the certificate.
+ * @return {boolean} true if the certificate exists, otherwise false.
+ * @throws Error If doesCertificateExistPromise doesn't return a SyncPromise
+ * which is already fulfilled.
+ */
+IdentityStorage.prototype.doesCertificateExist = function(certificateName)
+{
+ return SyncPromise.getValue
+ (this.doesCertificateExistPromise(certificateName, true));
+};
+
+/**
+ * Add a certificate to the identity storage. Also call addKey to ensure that
+ * the certificate key exists. If the certificate is already installed, don't
+ * replace it.
+ * @param {IdentityCertificate} certificate The certificate to be added. This
+ * makes a copy of the certificate.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when finished.
+ */
+IdentityStorage.prototype.addCertificatePromise = function(certificate, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.addCertificatePromise is not implemented"));
+};
+
+/**
+ * Add a certificate to the identity storage.
+ * @param {IdentityCertificate} certificate The certificate to be added. This
+ * makes a copy of the certificate.
+ * @throws SecurityException if the certificate is already installed.
+ * @throws Error If addCertificatePromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+IdentityStorage.prototype.addCertificate = function(certificate)
+{
+ return SyncPromise.getValue(this.addCertificatePromise(certificate, true));
+};
+
+/**
+ * Get a certificate from the identity storage.
+ * @param {Name} certificateName The name of the requested certificate.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the requested
+ * IdentityCertificate, or a promise rejected with SecurityException if the
+ * certificate doesn't exist.
+ */
+IdentityStorage.prototype.getCertificatePromise = function
+ (certificateName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.getCertificatePromise is not implemented"));
+};
+
+/**
+ * Get a certificate from the identity storage.
+ * @param {Name} certificateName The name of the requested certificate.
+ * @return {IdentityCertificate} The requested certificate.
+ * @throws SecurityException if the certificate doesn't exist.
+ * @throws Error If getCertificatePromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+IdentityStorage.prototype.getCertificate = function(certificateName)
+{
+ return SyncPromise.getValue(this.getValuePromise(certificateName, true));
+};
+
+/**
+ * Get the TPM locator associated with this storage.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise|SyncPromise} A promise which returns the TPM locator, or a
+ * promise rejected with SecurityException if the TPM locator doesn't exist.
+ */
+IdentityStorage.prototype.getTpmLocatorPromise = function(useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.getTpmLocatorPromise is not implemented"));
+};
+
+/**
+ * Get the TPM locator associated with this storage.
+ * @return {string} The TPM locator.
+ * @throws SecurityException if the TPM locator doesn't exist.
+ * @throws Error If getTpmLocatorPromise doesn't return a SyncPromise which is
+ * already fulfilled.
+ */
+IdentityStorage.prototype.getTpmLocator = function()
+{
+ return SyncPromise.getValue(this.getTpmLocatorPromise(true));
+};
+
+/*****************************************
+ * Get/Set Default *
+ *****************************************/
+
+/**
+ * Get the default identity.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the Name of default
+ * identity, or a promise rejected with SecurityException if the default
+ * identity is not set.
+ */
+IdentityStorage.prototype.getDefaultIdentityPromise = function(useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.getDefaultIdentityPromise is not implemented"));
+};
+
+/**
+ * Get the default identity.
+ * @return {Name} The name of default identity.
+ * @throws SecurityException if the default identity is not set.
+ * @throws Error If getDefaultIdentityPromise doesn't return a SyncPromise
+ * which is already fulfilled.
+ */
+IdentityStorage.prototype.getDefaultIdentity = function()
+{
+ return SyncPromise.getValue
+ (this.getDefaultIdentityPromise(true));
+};
+
+/**
+ * Get the default key name for the specified identity.
+ * @param {Name} identityName The identity name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the default key Name,
+ * or a promise rejected with SecurityException if the default key name for the
+ * identity is not set.
+ */
+IdentityStorage.prototype.getDefaultKeyNameForIdentityPromise = function
+ (identityName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.getDefaultKeyNameForIdentityPromise is not implemented"));
+};
+
+/**
+ * Get the default key name for the specified identity.
+ * @param {Name} identityName The identity name.
+ * @return {Name} The default key name.
+ * @throws SecurityException if the default key name for the identity is not set.
+ * @throws Error If getDefaultKeyNameForIdentityPromise doesn't return a
+ * SyncPromise which is already fulfilled.
+ */
+IdentityStorage.prototype.getDefaultKeyNameForIdentity = function(identityName)
+{
+ return SyncPromise.getValue
+ (this.getDefaultKeyNameForIdentityPromise(identityName, true));
+};
+
+/**
+ * Get the default certificate name for the specified identity.
+ * @param {Name} identityName The identity name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the default certificate
+ * Name, or a promise rejected with SecurityException if the default key name
+ * for the identity is not set or the default certificate name for the key name
+ * is not set.
+ */
+IdentityStorage.prototype.getDefaultCertificateNameForIdentityPromise = function
+ (identityName, useSync)
+{
+ var thisStorage = this;
+ return this.getDefaultKeyNameForIdentityPromise(identityName)
+ .then(function(keyName) {
+ return thisStorage.getDefaultCertificateNameForKeyPromise(keyName);
+ });
+};
+
+/**
+ * Get the default certificate name for the specified identity.
+ * @param {Name} identityName The identity name.
+ * @return {Name} The default certificate name.
+ * @throws SecurityException if the default key name for the identity is not
+ * set or the default certificate name for the key name is not set.
+ * @throws Error If getDefaultCertificateNameForIdentityPromise doesn't return
+ * a SyncPromise which is already fulfilled.
+ */
+IdentityStorage.prototype.getDefaultCertificateNameForIdentity = function
+ (identityName)
+{
+ return SyncPromise.getValue
+ (this.getDefaultCertificateNameForIdentityPromise(identityName, true));
+};
+
+/**
+ * Get the default certificate name for the specified key.
+ * @param {Name} keyName The key name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the default certificate
+ * Name, or a promise rejected with SecurityException if the default certificate
+ * name for the key name is not set.
+ */
+IdentityStorage.prototype.getDefaultCertificateNameForKeyPromise = function
+ (keyName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.getDefaultCertificateNameForKeyPromise is not implemented"));
+};
+
+/**
+ * Get the default certificate name for the specified key.
+ * @param {Name} keyName The key name.
+ * @return {Name} The default certificate name.
+ * @throws SecurityException if the default certificate name for the key name
+ * is not set.
+ * @throws Error If getDefaultCertificateNameForKeyPromise doesn't return a
+ * SyncPromise which is already fulfilled.
+ */
+IdentityStorage.prototype.getDefaultCertificateNameForKey = function(keyName)
+{
+ return SyncPromise.getValue
+ (this.getDefaultCertificateNameForKeyPromise(keyName, true));
+};
+
+/**
+ * Append all the identity names to the nameList.
+ * @param {Array<Name>} nameList Append result names to nameList.
+ * @param {boolean} isDefault If true, add only the default identity name. If
+ * false, add only the non-default identity names.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when the names are added to
+ * nameList.
+ */
+IdentityStorage.prototype.getAllIdentitiesPromise = function
+ (nameList, isDefault, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.getAllIdentitiesPromise is not implemented"));
+};
+
+/**
+ * Append all the key names of a particular identity to the nameList.
+ * @param {Name} identityName The identity name to search for.
+ * @param {Array<Name>} nameList Append result names to nameList.
+ * @param {boolean} isDefault If true, add only the default key name. If false,
+ * add only the non-default key names.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when the names are
+ * added to nameList.
+ */
+IdentityStorage.prototype.getAllKeyNamesOfIdentityPromise = function
+ (identityName, nameList, isDefault, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.getAllKeyNamesOfIdentityPromise is not implemented"));
+};
+
+/**
+ * Append all the certificate names of a particular key name to the nameList.
+ * @param {Name} keyName The key name to search for.
+ * @param {Array<Name>} nameList Append result names to nameList.
+ * @param {boolean} isDefault If true, add only the default certificate name.
+ * If false, add only the non-default certificate names.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when the names are added to
+ * nameList.
+ */
+IdentityStorage.prototype.getAllCertificateNamesOfKeyPromise = function
+ (keyName, nameList, isDefault, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.getAllCertificateNamesOfKeyPromise is not implemented"));
+};
+
+/**
+ * Append all the key names of a particular identity to the nameList.
+ * @param {Name} identityName The identity name to search for.
+ * @param {Array<Name>} nameList Append result names to nameList.
+ * @param {boolean} isDefault If true, add only the default key name. If false,
+ * add only the non-default key names.
+ * @throws Error If getAllKeyNamesOfIdentityPromise doesn't return a
+ * SyncPromise which is already fulfilled.
+ */
+IdentityStorage.prototype.getAllKeyNamesOfIdentity = function
+ (identityName, nameList, isDefault)
+{
+ return SyncPromise.getValue
+ (this.getAllKeyNamesOfIdentityPromise(identityName, nameList, isDefault, true));
+};
+
+/**
+ * Set the default identity. If the identityName does not exist, then clear the
+ * default identity so that getDefaultIdentity() throws an exception.
+ * @param {Name} identityName The default identity name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when the default
+ * identity is set.
+ */
+IdentityStorage.prototype.setDefaultIdentityPromise = function
+ (identityName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.setDefaultIdentityPromise is not implemented"));
+};
+
+/**
+ * Set the default identity. If the identityName does not exist, then clear the
+ * default identity so that getDefaultIdentity() throws an exception.
+ * @param {Name} identityName The default identity name.
+ * @throws Error If setDefaultIdentityPromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+IdentityStorage.prototype.setDefaultIdentity = function(identityName)
+{
+ return SyncPromise.getValue
+ (this.setDefaultIdentityPromise(identityName, true));
+};
+
+/**
+ * Set a key as the default key of an identity. The identity name is inferred
+ * from keyName.
+ * @param {Name} keyName The name of the key.
+ * @param {Name} identityNameCheck (optional) The identity name to check that the
+ * keyName contains the same identity name. If an empty name, it is ignored.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when the default key
+ * name is set.
+ */
+IdentityStorage.prototype.setDefaultKeyNameForIdentityPromise = function
+ (keyName, identityNameCheck, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.setDefaultKeyNameForIdentityPromise is not implemented"));
+};
+
+/**
+ * Set a key as the default key of an identity. The identity name is inferred
+ * from keyName.
+ * @param {Name} keyName The name of the key.
+ * @param {Name} identityNameCheck (optional) The identity name to check that the
+ * keyName contains the same identity name. If an empty name, it is ignored.
+ * @throws Error If setDefaultKeyNameForIdentityPromise doesn't return a
+ * SyncPromise which is already fulfilled.
+ */
+IdentityStorage.prototype.setDefaultKeyNameForIdentity = function
+ (keyName, identityNameCheck)
+{
+ return SyncPromise.getValue
+ (this.setDefaultKeyNameForIdentityPromise(keyName, identityNameCheck, true));
+};
+
+/**
+ * Set the default key name for the specified identity.
+ * @param {Name} keyName The key name.
+ * @param {Name} certificateName The certificate name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when the default
+ * certificate name is set.
+ */
+IdentityStorage.prototype.setDefaultCertificateNameForKeyPromise = function
+ (keyName, certificateName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.setDefaultCertificateNameForKeyPromise is not implemented"));
+};
+
+/**
+ * Set the default key name for the specified identity.
+ * @param {Name} keyName The key name.
+ * @param {Name} certificateName The certificate name.
+ * @throws Error If setDefaultCertificateNameForKeyPromise doesn't return a
+ * SyncPromise which is already fulfilled.
+ */
+IdentityStorage.prototype.setDefaultCertificateNameForKey = function
+ (keyName, certificateName)
+{
+ return SyncPromise.getValue
+ (this.setDefaultCertificateNameForKeyPromise(keyName, certificateName, true));
+};
+
+/**
+ * Get the certificate of the default identity.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the requested
+ * IdentityCertificate or null if not found.
+ */
+IdentityStorage.prototype.getDefaultCertificatePromise = function(useSync)
+{
+ var thisStorage = this;
+ return this.getDefaultIdentityPromise(useSync)
+ .then(function(identityName) {
+ return thisStorage.getDefaultCertificateNameForIdentityPromise
+ (identityName, useSync);
+ }, function(ex) {
+ // The default is not defined.
+ return SyncPromise.resolve(null);
+ })
+ .then(function(certName) {
+ if (certName == null)
+ return SyncPromise.resolve(null);
+
+ return thisStorage.getCertificatePromise(certName, useSync);
+ });
+};
+
+/**
+ * Get the certificate of the default identity.
+ * @return {IdentityCertificate} The requested certificate. If not found,
+ * return null.
+ * @throws Error If getDefaultCertificatePromise doesn't return a SyncPromise
+ * which is already fulfilled.
+ */
+IdentityStorage.prototype.getDefaultCertificate = function()
+{
+ return SyncPromise.getValue
+ (this.getDefaultCertificatePromise(true));
+};
+
+/*****************************************
+ * Delete Methods *
+ *****************************************/
+
+/**
+ * Delete a certificate.
+ * @param {Name} certificateName The certificate name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when the certificate
+ * info is deleted.
+ */
+IdentityStorage.prototype.deleteCertificateInfoPromise = function
+ (certificateName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.deleteCertificateInfoPromise is not implemented"));
+};
+
+/**
+ * Delete a certificate.
+ * @param {Name} certificateName The certificate name.
+ * @throws Error If deleteCertificateInfoPromise doesn't return a SyncPromise
+ * which is already fulfilled.
+ */
+IdentityStorage.prototype.deleteCertificateInfo = function(certificateName)
+{
+ return SyncPromise.getValue
+ (this.deleteCertificateInfoPromise(certificateName, true));
+};
+
+/**
+ * Delete a public key and related certificates.
+ * @param {Name} keyName The key name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when the public key
+ * info is deleted.
+ */
+IdentityStorage.prototype.deletePublicKeyInfoPromise = function(keyName, useSync)
+{
+ return SyncPromise.reject
+ (new Error("IdentityStorage.deletePublicKeyInfoPromise is not implemented"));
+};
+
+/**
+ * Delete a public key and related certificates.
+ * @param {Name} keyName The key name.
+ * @throws Error If deletePublicKeyInfoPromise doesn't return a SyncPromise
+ * which is already fulfilled.
+ */
+IdentityStorage.prototype.deletePublicKeyInfo = function(keyName)
+{
+ return SyncPromise.getValue
+ (this.deletePublicKeyInfoPromise(keyName, true));
+};
+
+/**
+ * Delete an identity and related public keys and certificates.
+ * @param {Name} identityName The identity name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when the identity info
+ * is deleted.
+ */
+IdentityStorage.prototype.deleteIdentityInfoPromise = function
+ (identityName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("IdentityStorage.deleteIdentityInfoPromise is not implemented"));
+};
+
+/**
+ * Delete an identity and related public keys and certificates.
+ * @param {Name} identityName The identity name.
+ * @throws Error If deleteIdentityInfoPromise doesn't return a SyncPromise
+ * which is already fulfilled.
+ */
+IdentityStorage.prototype.deleteIdentityInfo = function(identityName)
+{
+ return SyncPromise.getValue
+ (this.deleteIdentityInfoPromise(identityName, true));
+};
+
+// Track the lastTimestamp so that each timestamp is unique.
+IdentityStorage.lastTimestamp = Math.floor(new Date().getTime() / 1000.0);
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// Don't require modules since this is meant for the browser, not Node.js.
+
+/**
+ * IndexedDbIdentityStorage extends IdentityStorage and implements its methods
+ * to store identity, public key and certificate objects using the browser's
+ * IndexedDB service.
+ * @constructor
+ */
+var IndexedDbIdentityStorage = function IndexedDbIdentityStorage()
+{
+ IdentityStorage.call(this);
+
+ this.database = new Dexie("ndnsec-public-info");
+ // The database schema imitates MemoryIdentityStorage.
+ this.database.version(1).stores({
+ // A table for global values. It currently only has the defaultIdentityUri.
+ // "key" is the key like "defaultIdentityUri" // string
+ // "value" is the value. For "defaultIdentityUri" the value is the
+ // default identity name URI, or absent if not defined. // string
+ globals: "key",
+
+ // "identityNameUri" is the identity name URI // string
+ // "defaultKeyUri" is the default key name URI or null // string
+ identity: "identityNameUri",
+
+ // "keyNameUri" is the key name URI // string
+ // "keyType" is the type of the public key // number from KeyType
+ // "keyDer" is the public key DER // Uint8Array
+ // "defaultCertificateUri" is the default cert name URI or null // string
+ publicKey: "keyNameUri",
+
+ // "certificateNameUri" is the certificate name URI // string
+ // "encoding" is the certificate wire encoding // Uint8Array
+ certificate: "certificateNameUri"
+ });
+ this.database.open();
+};
+
+IndexedDbIdentityStorage.prototype = new IdentityStorage();
+IndexedDbIdentityStorage.prototype.name = "IndexedDbIdentityStorage";
+
+/**
+ * Check if the specified identity already exists.
+ * @param {Name} identityName The identity name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which returns true if the identity exists.
+ */
+IndexedDbIdentityStorage.prototype.doesIdentityExistPromise = function
+ (identityName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.doesIdentityExistPromise is only supported for async")));
+
+ return this.database.identity.where("identityNameUri").equals
+ (identityName.toUri())
+ .count()
+ .then(function(count) {
+ return Promise.resolve(count > 0);
+ });
+};
+
+/**
+ * Add a new identity. Do nothing if the identity already exists.
+ * @param {Name} identityName The identity name to be added.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when the identity is added.
+ */
+IndexedDbIdentityStorage.prototype.addIdentityPromise = function
+ (identityName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.addIdentityPromise is only supported for async")));
+
+ var thisStorage = this;
+ return this.doesIdentityExistPromise(identityName)
+ .then(function(exists) {
+ if (exists)
+ // Do nothing.
+ return Promise.resolve();
+
+ return thisStorage.database.identity.put
+ ({ identityNameUri: identityName.toUri(), defaultKeyUri: null });
+ });
+};
+
+/**
+ * Check if the specified key already exists.
+ * @param {Name} keyName The name of the key.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which returns true if the key exists.
+ */
+IndexedDbIdentityStorage.prototype.doesKeyExistPromise = function
+ (keyName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.doesKeyExistPromise is only supported for async")));
+
+ return this.database.publicKey.where("keyNameUri").equals(keyName.toUri())
+ .count()
+ .then(function(count) {
+ return Promise.resolve(count > 0);
+ });
+};
+
+/**
+ * Add a public key to the identity storage. Also call addIdentity to ensure
+ * that the identityName for the key exists. However, if the key already
+ * exists, do nothing.
+ * @param {Name} keyName The name of the public key to be added.
+ * @param {number} keyType Type of the public key to be added from KeyType, such
+ * as KeyType.RSA..
+ * @param {Blob} publicKeyDer A blob of the public key DER to be added.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when complete.
+ */
+IndexedDbIdentityStorage.prototype.addKeyPromise = function
+ (keyName, keyType, publicKeyDer, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.addKeyPromise is only supported for async")));
+
+ if (keyName.size() === 0)
+ return Promise.resolve();
+
+ var thisStorage = this;
+ return this.doesKeyExistPromise(keyName)
+ .then(function(exists) {
+ if (exists)
+ return Promise.resolve();
+
+ var identityName = keyName.getPrefix(-1);
+ return thisStorage.addIdentityPromise(identityName)
+ .then(function() {
+ return thisStorage.database.publicKey.put
+ ({ keyNameUri: keyName.toUri(), keyType: keyType,
+ keyDer: new Blob(publicKeyDer, true).buf(),
+ defaultCertificate: null });
+ });
+ });
+};
+
+/**
+ * Get the public key DER blob from the identity storage.
+ * @param {Name} keyName The name of the requested public key.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which returns the DER Blob, or a promise rejected
+ * with SecurityException if the key doesn't exist.
+ */
+IndexedDbIdentityStorage.prototype.getKeyPromise = function(keyName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.getKeyPromise is only supported for async")));
+
+ if (keyName.size() === 0)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage::getKeyPromise: Empty keyName")));
+
+ return this.database.publicKey.get(keyName.toUri())
+ .then(function(publicKeyEntry) {
+ if (publicKeyEntry)
+ return Promise.resolve(new Blob(publicKeyEntry.keyDer));
+ else
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage::getKeyPromise: The key does not exist")));
+ });
+};
+
+/**
+ * Check if the specified certificate already exists.
+ * @param {Name} certificateName The name of the certificate.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which returns true if the certificate exists.
+ */
+IndexedDbIdentityStorage.prototype.doesCertificateExistPromise = function
+ (certificateName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.doesCertificateExistPromise is only supported for async")));
+
+ return this.database.certificate.where("certificateNameUri").equals
+ (certificateName.toUri())
+ .count()
+ .then(function(count) {
+ return Promise.resolve(count > 0);
+ });
+};
+
+/**
+ * Add a certificate to the identity storage. Also call addKey to ensure that
+ * the certificate key exists. If the certificate is already installed, don't
+ * replace it.
+ * @param {IdentityCertificate} certificate The certificate to be added. This
+ * makes a copy of the certificate.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when finished.
+ */
+IndexedDbIdentityStorage.prototype.addCertificatePromise = function
+ (certificate, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.addCertificatePromise is only supported for async")));
+
+ var certificateName = certificate.getName();
+ var keyName = certificate.getPublicKeyName();
+
+ var thisStorage = this;
+ return this.addKeyPromise
+ (keyName, certificate.getPublicKeyInfo().getKeyType(),
+ certificate.getPublicKeyInfo().getKeyDer(), useSync)
+ .then(function() {
+ return thisStorage.doesCertificateExistPromise(certificateName);
+ })
+ .then(function(exists) {
+ if (exists)
+ return Promise.resolve();
+
+ // Insert the certificate.
+ // wireEncode returns the cached encoding if available.
+ return thisStorage.database.certificate.put
+ ({ certificateNameUri: certificateName.toUri(),
+ encoding: certificate.wireEncode().buf() });
+ });
+};
+
+/**
+ * Get a certificate from the identity storage.
+ * @param {Name} certificateName The name of the requested certificate.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which returns the requested
+ * IdentityCertificate, or a promise rejected with SecurityException if the
+ * certificate doesn't exist.
+ */
+IndexedDbIdentityStorage.prototype.getCertificatePromise = function
+ (certificateName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.getCertificatePromise is only supported for async")));
+
+ return this.database.certificate.get(certificateName.toUri())
+ .then(function(certificateEntry) {
+ if (certificateEntry) {
+ var certificate = new IdentityCertificate();
+ try {
+ certificate.wireDecode(certificateEntry.encoding);
+ } catch (ex) {
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage::getCertificatePromise: The certificate cannot be decoded")));
+ }
+ return Promise.resolve(certificate);
}
else
- interest.interestLifetime = 4000; // default interest timeout value in milliseconds.
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage::getCertificatePromise: The certificate does not exist")));
+ });
+};
- if (this.host == null || this.port == null) {
- if (this.getHostAndPort == null)
- console.log('ERROR: host OR port NOT SET');
- else {
- var thisNDN = this;
- this.connectAndExecute
- (function() { thisNDN.reconnectAndExpressInterest(interest, closure); });
+/*****************************************
+ * Get/Set Default *
+ *****************************************/
+
+/**
+ * Get the default identity.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which returns the Name of default identity,
+ * or a promise rejected with SecurityException if the default identity is not
+ * set.
+ */
+IndexedDbIdentityStorage.prototype.getDefaultIdentityPromise = function(useSync)
+{
+ return this.database.globals.get("defaultIdentityUri")
+ .then(function(defaultIdentityEntry) {
+ if (defaultIdentityEntry)
+ return Promise.resolve(new Name(defaultIdentityEntry.value));
+ else
+ throw new SecurityException(new Error
+ ("IndexedDbIdentityStorage.getDefaultIdentity: The default identity is not defined"));
+ });
+};
+
+/**
+ * Get the default key name for the specified identity.
+ * @param {Name} identityName The identity name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which returns the default key Name, or a
+ * promise rejected with SecurityException if the default key name for the
+ * identity is not set.
+ */
+IndexedDbIdentityStorage.prototype.getDefaultKeyNameForIdentityPromise = function
+ (identityName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.getDefaultKeyNameForIdentityPromise is only supported for async")));
+
+ return this.database.identity.get(identityName.toUri())
+ .then(function(identityEntry) {
+ if (identityEntry) {
+ if (identityEntry.defaultKeyUri != null)
+ return Promise.resolve(new Name(identityEntry.defaultKeyUri));
+ else
+ throw new SecurityException(new Error("No default key set."));
+ }
+ else
+ throw new SecurityException(new Error("Identity not found."));
+ });
+};
+
+/**
+ * Get the default certificate name for the specified key.
+ * @param {Name} keyName The key name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which returns the default certificate Name,
+ * or a promise rejected with SecurityException if the default certificate name
+ * for the key name is not set.
+ */
+IndexedDbIdentityStorage.prototype.getDefaultCertificateNameForKeyPromise = function
+ (keyName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.getDefaultCertificateNameForKeyPromise is only supported for async")));
+
+ return this.database.publicKey.get(keyName.toUri())
+ .then(function(publicKeyEntry) {
+ if (publicKeyEntry) {
+ if (publicKeyEntry.defaultCertificateUri != null)
+ return Promise.resolve(new Name(publicKeyEntry.defaultCertificateUri));
+ else
+ throw new SecurityException(new Error("No default certificate set."));
+ }
+ else
+ throw new SecurityException(new Error("Key not found."));
+ });
+};
+
+/**
+ * Append all the identity names to the nameList.
+ * @param {Array<Name>} nameList Append result names to nameList.
+ * @param {boolean} isDefault If true, add only the default identity name. If
+ * false, add only the non-default identity names.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when the names are added to
+ * nameList.
+ */
+IndexedDbIdentityStorage.prototype.getAllIdentitiesPromise = function
+ (nameList, isDefault, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.getAllIdentitiesPromise is only supported for async")));
+
+ var defaultIdentityName = null;
+ var thisStorage = this;
+ return this.getDefaultIdentityPromise()
+ .then(function(localDefaultIdentityName) {
+ defaultIdentityName = localDefaultIdentityName;
+ return SyncPromise.resolve();
+ }, function(err) {
+ // The default identity name was not found.
+ return SyncPromise.resolve();
+ })
+ .then(function() {
+ return thisStorage.database.identity.each(function(identityEntry) {
+ var identityName = new Name(identityEntry.identityNameUri);
+ var identityNameIsDefault =
+ (defaultIdentityName !== null && identityName.equals(defaultIdentityName));
+ if (isDefault && identityNameIsDefault)
+ nameList.push(identityName);
+ else if (!isDefault && !identityNameIsDefault)
+ nameList.push(identityName);
+ });
+ });
+};
+
+/**
+ * Append all the key names of a particular identity to the nameList.
+ * @param {Name} identityName The identity name to search for.
+ * @param {Array<Name>} nameList Append result names to nameList.
+ * @param {boolean} isDefault If true, add only the default key name. If false,
+ * add only the non-default key names.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when the names are added to
+ * nameList.
+ */
+IndexedDbIdentityStorage.prototype.getAllKeyNamesOfIdentityPromise = function
+ (identityName, nameList, isDefault, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.getAllKeyNamesOfIdentityPromise is only supported for async")));
+
+ var defaultKeyName = null;
+ var thisStorage = this;
+ return this.getDefaultKeyNameForIdentityPromise(identityName)
+ .then(function(localDefaultKeyName) {
+ defaultKeyName = localDefaultKeyName;
+ return SyncPromise.resolve();
+ }, function(err) {
+ // The default key name was not found.
+ return SyncPromise.resolve();
+ })
+ .then(function() {
+ // Iterate through each publicKey a to find ones that match identityName.
+ // This is a little inefficient, but we don't expect the in-browser
+ // database to be very big, we don't expect to use this function often (for
+ // deleting an identity), and this is simpler than complicating the database
+ // schema to store the identityName with each publicKey.
+ return thisStorage.database.publicKey.each(function(publicKeyEntry) {
+ var keyName = new Name(publicKeyEntry.keyNameUri);
+ var keyIdentityName = keyName.getPrefix(-1);
+
+ if (keyIdentityName.equals(identityName)) {
+ var keyNameIsDefault =
+ (defaultKeyName !== null && keyName.equals(defaultKeyName));
+ if (isDefault && keyNameIsDefault)
+ nameList.push(keyName);
+ else if (!isDefault && !keyNameIsDefault)
+ nameList.push(keyName);
+ }
+ });
+ });
+};
+
+/**
+ * Append all the certificate names of a particular key name to the nameList.
+ * @param {Name} keyName The key name to search for.
+ * @param {Array<Name>} nameList Append result names to nameList.
+ * @param {boolean} isDefault If true, add only the default certificate name.
+ * If false, add only the non-default certificate names.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when the names are added to
+ * nameList.
+ */
+IndexedDbIdentityStorage.prototype.getAllCertificateNamesOfKeyPromise = function
+ (keyName, nameList, isDefault, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.getAllCertificateNamesOfKeyPromise is only supported for async")));
+
+ var defaultCertificateName = null;
+ var thisStorage = this;
+ return this.getDefaultCertificateNameForKeyPromise(keyName)
+ .then(function(localDefaultCertificateName) {
+ defaultCertificateName = localDefaultCertificateName;
+ return SyncPromise.resolve();
+ }, function(err) {
+ // The default certificate name was not found.
+ return SyncPromise.resolve();
+ })
+ .then(function() {
+ // Iterate through each certificate record a to find ones that match keyName.
+ // This is a little inefficient, but we don't expect the in-browser
+ // database to be very big, we don't expect to use this function often (for
+ // deleting an identity), and this is simpler than complicating the database
+ // schema to store the keyName with each certificate record.
+ return thisStorage.database.certificate.each(function(certificateEntry) {
+ var certificateName = new Name(certificateEntry.certificateNameUri);
+ var certificateKeyName = IdentityCertificate.certificateNameToPublicKeyName
+ (certificateName);
+
+ if (certificateKeyName.equals(keyName)) {
+ var certificateNameIsDefault =
+ (defaultCertificateName !== null &&
+ certificateName.equals(defaultCertificateName));
+ if (isDefault && certificateNameIsDefault)
+ nameList.push(certificateName);
+ else if (!isDefault && !certificateNameIsDefault)
+ nameList.push(certificateName);
+ }
+ });
+ });
+};
+
+/**
+ * Set the default identity. If the identityName does not exist, then clear the
+ * default identity so that getDefaultIdentity() throws an exception.
+ * @param {Name} identityName The default identity name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when the default identity is set.
+ */
+IndexedDbIdentityStorage.prototype.setDefaultIdentityPromise = function
+ (identityName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.setDefaultIdentityPromise is only supported for async")));
+
+ var thisStorage = this;
+ return this.doesIdentityExistPromise(identityName)
+ .then(function(exists) {
+ if (exists)
+ return thisStorage.database.globals.put
+ ({ key: "defaultIdentityUri", value: identityName.toUri() });
+ else
+ // The identity doesn't exist, so clear the default.
+ return thisStorage.database.globals.delete("defaultIdentityUri");
+ });
+};
+
+/**
+ * Set a key as the default key of an identity. The identity name is inferred
+ * from keyName.
+ * @param {Name} keyName The name of the key.
+ * @param {Name} identityNameCheck (optional) The identity name to check that the
+ * keyName contains the same identity name. If an empty name, it is ignored.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when the default key name is
+ * set.
+ */
+IndexedDbIdentityStorage.prototype.setDefaultKeyNameForIdentityPromise = function
+ (keyName, identityNameCheck, useSync)
+{
+ useSync = (typeof identityNameCheck === "boolean") ? identityNameCheck : useSync;
+ identityNameCheck = (identityNameCheck instanceof Name) ? identityNameCheck : null;
+
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.setDefaultKeyNameForIdentityPromise is only supported for async")));
+
+ var identityName = keyName.getPrefix(-1);
+
+ if (identityNameCheck != null && identityNameCheck.size() > 0 &&
+ !identityNameCheck.equals(identityName))
+ return Promise.reject(new SecurityException(new Error
+ ("The specified identity name does not match the key name")));
+
+ // update does nothing if the identityName doesn't exist.
+ return this.database.identity.update
+ (identityName.toUri(), { defaultKeyUri: keyName.toUri() });
+};
+
+/**
+ * Set the default key name for the specified identity.
+ * @param {Name} keyName The key name.
+ * @param {Name} certificateName The certificate name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when the default certificate
+ * name is set.
+ */
+IndexedDbIdentityStorage.prototype.setDefaultCertificateNameForKeyPromise = function
+ (keyName, certificateName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.setDefaultCertificateNameForKeyPromise is only supported for async")));
+
+ // update does nothing if the keyName doesn't exist.
+ return this.database.publicKey.update
+ (keyName.toUri(), { defaultCertificateUri: certificateName.toUri() });
+};
+
+/*****************************************
+ * Delete Methods *
+ *****************************************/
+
+/**
+ * Delete a certificate.
+ * @param {Name} certificateName The certificate name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when the certificate info is
+ * deleted.
+ */
+IndexedDbIdentityStorage.prototype.deleteCertificateInfoPromise = function
+ (certificateName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.deleteCertificateInfoPromise is only supported for async")));
+
+ if (certificateName.size() === 0)
+ return Promise.resolve();
+
+ return this.database.certificate.delete(certificateName.toUri());
+};
+
+/**
+ * Delete a public key and related certificates.
+ * @param {Name} keyName The key name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when the public key info is
+ * deleted.
+ */
+IndexedDbIdentityStorage.prototype.deletePublicKeyInfoPromise = function
+ (keyName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.deletePublicKeyInfoPromise is only supported for async")));
+
+ if (keyName.size() === 0)
+ return Promise.resolve();
+
+ var thisStorage = this;
+ return this.database.publicKey.delete(keyName.toUri())
+ .then(function() {
+ // Iterate through each certificate to find ones that match keyName. This is
+ // a little inefficient, but we don't expect the in-browswer database to be
+ // very big, we don't expect to delete often, and this is simpler than
+ // complicating the database schema to store the keyName with each certificate.
+ return thisStorage.database.certificate.each(function(certificateEntry) {
+ if (IdentityCertificate.certificateNameToPublicKeyName
+ (new Name(certificateEntry.certificateNameUri)).equals(keyName))
+ thisStorage.database.certificate.delete
+ (certificateEntry.certificateNameUri);
+ });
+ });
+};
+
+/**
+ * Delete an identity and related public keys and certificates.
+ * @param {Name} identityName The identity name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which fulfills when the identity info is
+ * deleted.
+ */
+IndexedDbIdentityStorage.prototype.deleteIdentityInfoPromise = function
+ (identityName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbIdentityStorage.deleteIdentityInfoPromise is only supported for async")));
+
+ var thisStorage = this;
+ return this.database.identity.delete(identityName.toUri())
+ // Iterate through each publicKey and certificate to find ones that match
+ // identityName. This is a little inefficient, but we don't expect the
+ // in-browswer database to be very big, we don't expect to delete often, and
+ // this is simpler than complicating the database schema to store the
+ // identityName with each publicKey and certificate.
+ .then(function() {
+ return thisStorage.database.publicKey.each(function(publicKeyEntry) {
+ var keyIdentityName = new Name(publicKeyEntry.keyNameUri).getPrefix(-1);
+ if (keyIdentityName.equals(identityName))
+ thisStorage.database.publicKey.delete(publicKeyEntry.keyNameUri);
+ });
+ })
+ .then(function() {
+ return thisStorage.database.certificate.each(function(certificateEntry) {
+ var certificateKeyName = IdentityCertificate.certificateNameToPublicKeyName
+ (new Name(certificateEntry.certificateNameUri));
+ var certificateIdentityName = certificateKeyName.getPrefix(-1);
+ if (certificateIdentityName.equals(identityName))
+ thisStorage.database.certificate.delete
+ (certificateEntry.certificateNameUri);
+ });
+ });
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Name = require('../../name.js').Name; /** @ignore */
+var Blob = require('../../util/blob.js').Blob; /** @ignore */
+var SecurityException = require('../security-exception.js').SecurityException; /** @ignore */
+var IdentityCertificate = require('../certificate/identity-certificate.js').IdentityCertificate; /** @ignore */
+var SyncPromise = require('../../util/sync-promise.js').SyncPromise; /** @ignore */
+var IdentityStorage = require('./identity-storage.js').IdentityStorage;
+
+/**
+ * MemoryIdentityStorage extends IdentityStorage and implements its methods to
+ * store identity, public key and certificate objects in memory. The application
+ * must get the objects through its own means and add the objects to the
+ * MemoryIdentityStorage object. To use permanent file-based storage, see
+ * BasicIdentityStorage.
+ * @constructor
+ */
+var MemoryIdentityStorage = function MemoryIdentityStorage()
+{
+ // Call the base constructor.
+ IdentityStorage.call(this);
+
+ // The map key is the identityName.toUri(). The value is the object
+ // {defaultKey // Name
+ // }.
+ this.identityStore = {};
+ // The default identity in identityStore, or "" if not defined.
+ this.defaultIdentity = "";
+ // The key is the keyName.toUri(). The value is the object
+ // {keyType, // number from KeyType
+ // keyDer // Blob
+ // defaultCertificate // Name
+ // }.
+ this.keyStore = {};
+ // The key is the key is the certificateName.toUri(). The value is the
+ // encoded certificate.
+ this.certificateStore = {};
+};
+
+MemoryIdentityStorage.prototype = new IdentityStorage();
+MemoryIdentityStorage.prototype.name = "MemoryIdentityStorage";
+
+exports.MemoryIdentityStorage = MemoryIdentityStorage;
+/**
+ * Check if the specified identity already exists.
+ * @param {Name} identityName The identity name.
+ * @return {SyncPromise} A promise which returns true if the identity exists.
+ */
+MemoryIdentityStorage.prototype.doesIdentityExistPromise = function(identityName)
+{
+ return SyncPromise.resolve
+ (this.identityStore[identityName.toUri()] !== undefined);
+};
+
+/**
+ * Add a new identity. Do nothing if the identity already exists.
+ * @param {Name} identityName The identity name to be added.
+ * @return {SyncPromise} A promise which fulfills when the identity is added.
+ */
+MemoryIdentityStorage.prototype.addIdentityPromise = function(identityName)
+{
+ var identityUri = identityName.toUri();
+ if (this.identityStore[identityUri] === undefined)
+ this.identityStore[identityUri] = { defaultKey: null };
+
+ return SyncPromise.resolve();
+};
+
+/**
+ * Check if the specified key already exists.
+ * @param {Name} keyName The name of the key.
+ * @return {SyncPromise} A promise which returns true if the key exists.
+ */
+MemoryIdentityStorage.prototype.doesKeyExistPromise = function(keyName)
+{
+ return SyncPromise.resolve(this.keyStore[keyName.toUri()] !== undefined);
+};
+
+/**
+ * Add a public key to the identity storage. Also call addIdentity to ensure
+ * that the identityName for the key exists. However, if the key already
+ * exists, do nothing.
+ * @param {Name} keyName The name of the public key to be added.
+ * @param {number} keyType Type of the public key to be added from KeyType, such
+ * as KeyType.RSA..
+ * @param {Blob} publicKeyDer A blob of the public key DER to be added.
+ * @return {SyncPromise} A promise which fulfills when complete.
+ */
+MemoryIdentityStorage.prototype.addKeyPromise = function
+ (keyName, keyType, publicKeyDer)
+{
+ if (keyName.size() === 0)
+ return SyncPromise.resolve();
+
+ if (this.doesKeyExist(keyName))
+ return SyncPromise.resolve();
+
+ var identityName = keyName.getSubName(0, keyName.size() - 1);
+
+ this.addIdentity(identityName);
+
+ this.keyStore[keyName.toUri()] =
+ { keyType: keyType, keyDer: new Blob(publicKeyDer), defaultCertificate: null };
+
+ return SyncPromise.resolve();
+};
+
+/**
+ * Get the public key DER blob from the identity storage.
+ * @param {Name} keyName The name of the requested public key.
+ * @return {SyncPromise} A promise which returns the DER Blob, or a promise
+ * rejected with SecurityException if the key doesn't exist.
+ */
+MemoryIdentityStorage.prototype.getKeyPromise = function(keyName)
+{
+ if (keyName.size() === 0)
+ return SyncPromise.reject(new SecurityException(new Error
+ ("MemoryIdentityStorage::getKeyPromise: Empty keyName")));
+
+ var keyNameUri = keyName.toUri();
+ var entry = this.keyStore[keyNameUri];
+ if (entry === undefined)
+ return SyncPromise.reject(new SecurityException(new Error
+ ("MemoryIdentityStorage::getKeyPromise: The key does not exist")));
+
+ return SyncPromise.resolve(entry.keyDer);
+};
+
+/**
+ * Check if the specified certificate already exists.
+ * @param {Name} certificateName The name of the certificate.
+ * @return {SyncPromise} A promise which returns true if the certificate exists.
+ */
+MemoryIdentityStorage.prototype.doesCertificateExistPromise = function
+ (certificateName)
+{
+ return SyncPromise.resolve
+ (this.certificateStore[certificateName.toUri()] !== undefined);
+};
+
+/**
+ * Add a certificate to the identity storage. Also call addKey to ensure that
+ * the certificate key exists. If the certificate is already installed, don't
+ * replace it.
+ * @param {IdentityCertificate} certificate The certificate to be added. This
+ * makes a copy of the certificate.
+ * @return {SyncPromise} A promise which fulfills when finished.
+ */
+MemoryIdentityStorage.prototype.addCertificatePromise = function(certificate)
+{
+ var certificateName = certificate.getName();
+ var keyName = certificate.getPublicKeyName();
+
+ this.addKey(keyName, certificate.getPublicKeyInfo().getKeyType(),
+ certificate.getPublicKeyInfo().getKeyDer());
+
+ if (this.doesCertificateExist(certificateName))
+ return SyncPromise.resolve();
+
+ // Insert the certificate.
+ // wireEncode returns the cached encoding if available.
+ this.certificateStore[certificateName.toUri()] = certificate.wireEncode();
+
+ return SyncPromise.resolve();
+};
+
+/**
+ * Get a certificate from the identity storage.
+ * @param {Name} certificateName The name of the requested certificate.
+ * @return {SyncPromise} A promise which returns the requested
+ * IdentityCertificate, or a promise rejected with SecurityException if the
+ * certificate doesn't exist.
+ */
+MemoryIdentityStorage.prototype.getCertificatePromise = function
+ (certificateName)
+{
+ var certificateNameUri = certificateName.toUri();
+ if (this.certificateStore[certificateNameUri] === undefined)
+ return SyncPromise.reject(new SecurityException(new Error
+ ("MemoryIdentityStorage::getCertificatePromise: The certificate does not exist")));
+
+ var certificate = new IdentityCertificate();
+ try {
+ certificate.wireDecode(this.certificateStore[certificateNameUri]);
+ } catch (ex) {
+ return SyncPromise.reject(new SecurityException(new Error
+ ("MemoryIdentityStorage::getCertificatePromise: The certificate cannot be decoded")));
+ }
+ return SyncPromise.resolve(certificate);
+};
+
+/**
+ * Get the TPM locator associated with this storage.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise|SyncPromise} A promise which returns the TPM locator, or a
+ * promise rejected with SecurityException if the TPM locator doesn't exist.
+ */
+IdentityStorage.prototype.getTpmLocatorPromise = function(useSync)
+{
+ return SyncPromise.resolve("tpm-memory:");
+};
+
+/*****************************************
+ * Get/Set Default *
+ *****************************************/
+
+/**
+ * Get the default identity.
+ * @return {SyncPromise} A promise which returns the Name of default identity,
+ * or a promise rejected with SecurityException if the default identity is not
+ * set.
+ */
+MemoryIdentityStorage.prototype.getDefaultIdentityPromise = function()
+{
+ if (this.defaultIdentity.length === 0)
+ return SyncPromise.reject(new SecurityException(new Error
+ ("MemoryIdentityStorage.getDefaultIdentity: The default identity is not defined")));
+
+ return SyncPromise.resolve(new Name(this.defaultIdentity));
+};
+
+/**
+ * Get the default key name for the specified identity.
+ * @param {Name} identityName The identity name.
+ * @return {SyncPromise} A promise which returns the default key Name, or a
+ * promise rejected with SecurityException if the default key name for the
+ * identity is not set.
+ */
+MemoryIdentityStorage.prototype.getDefaultKeyNameForIdentityPromise = function
+ (identityName)
+{
+ var identityUri = identityName.toUri();
+ if (this.identityStore[identityUri] !== undefined) {
+ if (this.identityStore[identityUri].defaultKey != null)
+ return SyncPromise.resolve(this.identityStore[identityUri].defaultKey);
+ else
+ return SyncPromise.reject(new SecurityException(new Error
+ ("No default key set.")));
+ }
+ else
+ return SyncPromise.reject(new SecurityException(new Error("Identity not found.")));
+};
+
+/**
+ * Get the default certificate name for the specified key.
+ * @param {Name} keyName The key name.
+ * @return {SyncPromise} A promise which returns the default certificate Name,
+ * or a promise rejected with SecurityException if the default certificate name
+ * for the key name is not set.
+ */
+MemoryIdentityStorage.prototype.getDefaultCertificateNameForKeyPromise = function
+ (keyName)
+{
+ var keyUri = keyName.toUri();
+ if (this.keyStore[keyUri] !== undefined) {
+ if (this.keyStore[keyUri].defaultCertificate != null)
+ return SyncPromise.resolve(this.keyStore[keyUri].defaultCertificate);
+ else
+ return SyncPromise.reject(new SecurityException(new Error
+ ("No default certificate set.")));
+ }
+ else
+ return SyncPromise.reject(new SecurityException(new Error("Key not found.")));
+};
+
+/**
+ * Set the default identity. If the identityName does not exist, then clear the
+ * default identity so that getDefaultIdentity() throws an exception.
+ * @param {Name} identityName The default identity name.
+ * @return {SyncPromise} A promise which fulfills when the default identity is set.
+ */
+MemoryIdentityStorage.prototype.setDefaultIdentityPromise = function
+ (identityName)
+{
+ var identityUri = identityName.toUri();
+ if (this.identityStore[identityUri] !== undefined)
+ this.defaultIdentity = identityUri;
+ else
+ // The identity doesn't exist, so clear the default.
+ this.defaultIdentity = "";
+
+ return SyncPromise.resolve();
+};
+
+/**
+ * Set a key as the default key of an identity. The identity name is inferred
+ * from keyName.
+ * @param {Name} keyName The name of the key.
+ * @param {Name} identityNameCheck (optional) The identity name to check that the
+ * keyName contains the same identity name. If an empty name, it is ignored.
+ * @return {SyncPromise} A promise which fulfills when the default key name is
+ * set.
+ */
+MemoryIdentityStorage.prototype.setDefaultKeyNameForIdentityPromise = function
+ (keyName, identityNameCheck)
+{
+ identityNameCheck = (identityNameCheck instanceof Name) ? identityNameCheck : null;
+
+ var identityName = keyName.getPrefix(-1);
+
+ if (identityNameCheck != null && identityNameCheck.size() > 0 &&
+ !identityNameCheck.equals(identityName))
+ return SyncPromise.reject(new SecurityException(new Error
+ ("The specified identity name does not match the key name")));
+
+ var identityUri = identityName.toUri();
+ if (this.identityStore[identityUri] !== undefined)
+ this.identityStore[identityUri].defaultKey = new Name(keyName);
+
+ return SyncPromise.resolve();
+};
+
+/**
+ * Set the default key name for the specified identity.
+ * @param {Name} keyName The key name.
+ * @param {Name} certificateName The certificate name.
+ * @return {SyncPromise} A promise which fulfills when the default certificate
+ * name is set.
+ */
+MemoryIdentityStorage.prototype.setDefaultCertificateNameForKeyPromise = function
+ (keyName, certificateName)
+{
+ var keyUri = keyName.toUri();
+ if (this.keyStore[keyUri] !== undefined)
+ this.keyStore[keyUri].defaultCertificate = new Name(certificateName);
+
+ return SyncPromise.resolve();
+};
+
+/*****************************************
+ * Delete Methods *
+ *****************************************/
+
+/**
+ * Delete a certificate.
+ * @param {Name} certificateName The certificate name.
+ * @return {SyncPromise} A promise which fulfills when the certificate
+ * info is deleted.
+ */
+MemoryIdentityStorage.prototype.deleteCertificateInfoPromise = function
+ (certificateName)
+{
+ return SyncPromise.reject(new Error
+ ("MemoryIdentityStorage.deleteCertificateInfoPromise is not implemented"));
+};
+
+/**
+ * Delete a public key and related certificates.
+ * @param {Name} keyName The key name.
+ * @return {SyncPromise} A promise which fulfills when the public key info is
+ * deleted.
+ */
+MemoryIdentityStorage.prototype.deletePublicKeyInfoPromise = function(keyName)
+{
+ return SyncPromise.reject(new Error
+ ("MemoryIdentityStorage.deletePublicKeyInfoPromise is not implemented"));
+};
+
+/**
+ * Delete an identity and related public keys and certificates.
+ * @param {Name} identity The identity name.
+ * @return {SyncPromise} A promise which fulfills when the identity info is
+ * deleted.
+ */
+MemoryIdentityStorage.prototype.deleteIdentityInfoPromise = function(identity)
+{
+ return SyncPromise.reject(new Error
+ ("MemoryIdentityStorage.deleteIdentityInfoPromise is not implemented"));
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var SyncPromise = require('../../util/sync-promise.js').SyncPromise; /** @ignore */
+var DerNode = require('../../encoding/der/der-node.js').DerNode;
+
+/**
+ * PrivateKeyStorage is an abstract class which declares methods for working
+ * with a private key storage. You should use a subclass.
+ * @constructor
+ */
+var PrivateKeyStorage = function PrivateKeyStorage()
+{
+};
+
+exports.PrivateKeyStorage = PrivateKeyStorage;
+
+/**
+ * Generate a pair of asymmetric keys.
+ * @param {Name} keyName The name of the key pair.
+ * @param {KeyParams} params The parameters of the key.
+ * @param {boolean} (optional) useSync If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the pair is
+ * generated.
+ */
+PrivateKeyStorage.prototype.generateKeyPairPromise = function
+ (keyName, params, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("PrivateKeyStorage.generateKeyPairPromise is not implemented"));
+};
+
+/**
+ * Generate a pair of asymmetric keys.
+ * @param {Name} keyName The name of the key pair.
+ * @param {KeyParams} params The parameters of the key.
+ * @throws Error If generateKeyPairPromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+PrivateKeyStorage.prototype.generateKeyPair = function(keyName, params)
+{
+ SyncPromise.getValue(this.generateKeyPairPromise(keyName, params, true));
+};
+
+/**
+ * Delete a pair of asymmetric keys. If the key doesn't exist, do nothing.
+ * @param {Name} keyName The name of the key pair.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the key pair is
+ * deleted.
+ */
+PrivateKeyStorage.prototype.deleteKeyPairPromise = function(keyName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("PrivateKeyStorage.deleteKeyPairPromise is not implemented"));
+};
+
+/**
+ * Delete a pair of asymmetric keys. If the key doesn't exist, do nothing.
+ * @param {Name} keyName The name of the key pair.
+ * @throws Error If deleteKeyPairPromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+PrivateKeyStorage.prototype.deleteKeyPair = function(keyName)
+{
+ SyncPromise.getValue(this.deleteKeyPairPromise(keyName, true));
+};
+
+/**
+ * Get the public key
+ * @param {Name} keyName The name of public key.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns the PublicKey.
+ */
+PrivateKeyStorage.prototype.getPublicKeyPromise = function(keyName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("PrivateKeyStorage.getPublicKeyPromise is not implemented"));
+};
+
+/**
+ * Get the public key
+ * @param {Name} keyName The name of public key.
+ * @return {PublicKey} The public key.
+ * @throws Error If getPublicKeyPromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+PrivateKeyStorage.prototype.getPublicKey = function(keyName)
+{
+ return SyncPromise.getValue(this.getPublicKeyPromise(keyName, true));
+};
+
+/**
+ * Fetch the private key for keyName and sign the data to produce a signature Blob.
+ * @param {Buffer} data Pointer to the input byte array.
+ * @param {Name} keyName The name of the signing key.
+ * @param {number} digestAlgorithm (optional) The digest algorithm from
+ * DigestAlgorithm, such as DigestAlgorithm.SHA256. If omitted, use
+ * DigestAlgorithm.SHA256.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns the signature Blob.
+ */
+PrivateKeyStorage.prototype.signPromise = function
+ (data, keyName, digestAlgorithm, useSync)
+{
+ return SyncPromise.reject(new Error("PrivateKeyStorage.sign is not implemented"));
+};
+
+/**
+ * Fetch the private key for keyName and sign the data to produce a signature Blob.
+ * @param {Buffer} data Pointer to the input byte array.
+ * @param {Name} keyName The name of the signing key.
+ * @param {number} digestAlgorithm (optional) The digest algorithm from
+ * DigestAlgorithm, such as DigestAlgorithm.SHA256. If omitted, use
+ * DigestAlgorithm.SHA256.
+ * @return {Blob} The signature Blob.
+ * @throws Error If signPromise doesn't return a SyncPromise which is already
+ * fulfilled.
+ */
+PrivateKeyStorage.prototype.sign = function(data, keyName, digestAlgorithm)
+{
+ return SyncPromise.getValue
+ (this.signPromise(data, keyName, digestAlgorithm, true));
+};
+
+/**
+ * Decrypt data.
+ * @param {Name} keyName The name of the decrypting key.
+ * @param {Buffer} data The byte to be decrypted.
+ * @param {boolean} isSymmetric (optional) If true symmetric encryption is used,
+ * otherwise asymmetric encryption is used. If omitted, use asymmetric
+ * encryption.
+ * @return {Blob} The decrypted data.
+ */
+PrivateKeyStorage.prototype.decrypt = function(keyName, data, isSymmetric)
+{
+ throw new Error("PrivateKeyStorage.decrypt is not implemented");
+};
+
+/**
+ * Encrypt data.
+ * @param {Name} keyName The name of the encrypting key.
+ * @param {Buffer} data The byte to be encrypted.
+ * @param {boolean} isSymmetric (optional) If true symmetric encryption is used,
+ * otherwise asymmetric encryption is used. If omitted, use asymmetric
+ * encryption.
+ * @return {Blob} The encrypted data.
+ */
+PrivateKeyStorage.prototype.encrypt = function(keyName, data, isSymmetric)
+{
+ throw new Error("PrivateKeyStorage.encrypt is not implemented");
+};
+
+/**
+ * @brief Generate a symmetric key.
+ * @param {Name} keyName The name of the key.
+ * @param {KeyParams} params The parameters of the key.
+ */
+PrivateKeyStorage.prototype.generateKey = function(keyName, params)
+{
+ throw new Error("PrivateKeyStorage.generateKey is not implemented");
+};
+
+/**
+ * Check if a particular key exists.
+ * @param {Name} keyName The name of the key.
+ * @param {number} keyClass The class of the key, e.g. KeyClass.PUBLIC,
+ * KeyClass.PRIVATE, or KeyClass.SYMMETRIC.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns true if the key exists.
+ */
+PrivateKeyStorage.prototype.doesKeyExistPromise = function
+ (keyName, keyClass, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("PrivateKeyStorage.doesKeyExist is not implemented"));
+};
+
+/**
+ * Check if a particular key exists.
+ * @param {Name} keyName The name of the key.
+ * @param {number} keyClass The class of the key, e.g. KeyClass.PUBLIC,
+ * KeyClass.PRIVATE, or KeyClass.SYMMETRIC.
+ * @return {boolean} True if the key exists.
+ * @throws Error If doesKeyExistPromise doesn't return a SyncPromise which
+ * is already fulfilled.
+ */
+PrivateKeyStorage.prototype.doesKeyExist = function(keyName, keyClass)
+{
+ return SyncPromise.getValue(this.doesKeyExistPromise(keyName, keyClass, true));
+};
+
+/**
+ * Encode the private key to a PKCS #8 private key. We do this explicitly here
+ * to avoid linking to extra OpenSSL libraries.
+ * @param {Buffer} privateKeyDer The input private key DER.
+ * @param {OID} oid The OID of the privateKey.
+ * @param {DerNode} parameters The DerNode of the parameters for the OID.
+ * @return {Blob} The PKCS #8 private key DER.
+ */
+PrivateKeyStorage.encodePkcs8PrivateKey = function
+ (privateKeyDer, oid, parameters)
+{
+ var algorithmIdentifier = new DerNode.DerSequence();
+ algorithmIdentifier.addChild(new DerNode.DerOid(oid));
+ algorithmIdentifier.addChild(parameters);
+
+ var result = new DerNode.DerSequence();
+ result.addChild(new DerNode.DerInteger(0));
+ result.addChild(algorithmIdentifier);
+ result.addChild(new DerNode.DerOctetString(privateKeyDer));
+
+ return result.encode();
+};
+
+/**
+ * Encode the RSAKey private key as a PKCS #1 private key.
+ * @param {RSAKey} rsaKey The RSAKey private key.
+ * @return {Blob} The PKCS #1 private key DER.
+ */
+PrivateKeyStorage.encodePkcs1PrivateKeyFromRSAKey = function(rsaKey)
+{
+ // Imitate KJUR getEncryptedPKCS5PEMFromRSAKey.
+ var result = new DerNode.DerSequence();
+
+ result.addChild(new DerNode.DerInteger(0));
+ result.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(rsaKey.n)));
+ result.addChild(new DerNode.DerInteger(rsaKey.e));
+ result.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(rsaKey.d)));
+ result.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(rsaKey.p)));
+ result.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(rsaKey.q)));
+ result.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(rsaKey.dmp1)));
+ result.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(rsaKey.dmq1)));
+ result.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(rsaKey.coeff)));
+
+ return result.encode();
+};
+
+/**
+ * Encode the public key values in the RSAKey private key as a
+ * SubjectPublicKeyInfo.
+ * @param {RSAKey} rsaKey The RSAKey private key with the public key values.
+ * @return {Blob} The SubjectPublicKeyInfo DER.
+ */
+PrivateKeyStorage.encodePublicKeyFromRSAKey = function(rsaKey)
+{
+ var rsaPublicKey = new DerNode.DerSequence();
+
+ rsaPublicKey.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(rsaKey.n)));
+ rsaPublicKey.addChild(new DerNode.DerInteger(rsaKey.e));
+
+ var algorithmIdentifier = new DerNode.DerSequence();
+ algorithmIdentifier.addChild
+ (new DerNode.DerOid(new OID(PrivateKeyStorage.RSA_ENCRYPTION_OID)));
+ algorithmIdentifier.addChild(new DerNode.DerNull());
+
+ var result = new DerNode.DerSequence();
+
+ result.addChild(algorithmIdentifier);
+ result.addChild(new DerNode.DerBitString(rsaPublicKey.encode().buf(), 0));
+
+ return result.encode();
+};
+
+/**
+ * Convert a BigInteger to a Buffer.
+ * @param {BigInteger} bigInteger The BigInteger.
+ * @return {Buffer} The Buffer.
+ */
+PrivateKeyStorage.bigIntegerToBuffer = function(bigInteger)
+{
+ // Imitate KJUR.asn1.ASN1Util.bigIntToMinTwosComplementsHex.
+ var hex = bigInteger.toString(16);
+ if (hex.substr(0, 1) == "-")
+ throw new Error
+ ("PrivateKeyStorage.bigIntegerToBuffer: Negative integers are not currently supported");
+
+ if (hex.length % 2 == 1)
+ // Odd number of characters.
+ hex = "0" + hex;
+ else {
+ if (! hex.match(/^[0-7]/))
+ // The first byte is >= 0x80, so prepend a zero to keep it positive.
+ hex = "00" + hex;
+ }
+
+ return new Buffer(hex, 'hex');
+};
+
+PrivateKeyStorage.RSA_ENCRYPTION_OID = "1.2.840.113549.1.1.1";
+PrivateKeyStorage.EC_ENCRYPTION_OID = "1.2.840.10045.2.1";
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// Use capitalized Crypto to not clash with the browser's crypto.subtle.
+/** @ignore */
+var Crypto = require('../../crypto.js'); /** @ignore */
+var Blob = require('../../util/blob.js').Blob; /** @ignore */
+var SecurityException = require('../security-exception.js').SecurityException; /** @ignore */
+var PublicKey = require('../certificate/public-key.js').PublicKey; /** @ignore */
+var KeyClass = require('../security-types.js').KeyClass; /** @ignore */
+var KeyType = require('../security-types').KeyType; /** @ignore */
+var DigestAlgorithm = require('../security-types.js').DigestAlgorithm; /** @ignore */
+var DataUtils = require('../../encoding/data-utils.js').DataUtils; /** @ignore */
+var PrivateKeyStorage = require('./private-key-storage.js').PrivateKeyStorage; /** @ignore */
+var DerNode = require('../../encoding/der/der-node.js').DerNode; /** @ignore */
+var OID = require('../../encoding/oid.js').OID; /** @ignore */
+var SyncPromise = require('../../util/sync-promise.js').SyncPromise; /** @ignore */
+var UseSubtleCrypto = require('../../use-subtle-crypto-node.js').UseSubtleCrypto; /** @ignore */
+var rsaKeygen = null;
+try {
+ // This should be installed with: sudo npm install rsa-keygen
+ rsaKeygen = require('rsa-keygen');
+}
+catch (e) {}
+
+/**
+ * MemoryPrivateKeyStorage class extends PrivateKeyStorage to implement private
+ * key storage in memory.
+ * @constructor
+ */
+var MemoryPrivateKeyStorage = function MemoryPrivateKeyStorage()
+{
+ // Call the base constructor.
+ PrivateKeyStorage.call(this);
+
+ // The key is the keyName.toUri(). The value is security.certificate.PublicKey.
+ this.publicKeyStore = {};
+ // The key is the keyName.toUri(). The value is the object
+ // {keyType, // number from KeyType
+ // privateKey // The PEM-encoded private key.
+ // }.
+ this.privateKeyStore = {};
+};
+
+MemoryPrivateKeyStorage.prototype = new PrivateKeyStorage();
+MemoryPrivateKeyStorage.prototype.name = "MemoryPrivateKeyStorage";
+
+exports.MemoryPrivateKeyStorage = MemoryPrivateKeyStorage;
+
+/**
+ * Set the public key for the keyName.
+ * @param {Name} keyName The key name.
+ * @param {number} keyType The KeyType, such as KeyType.RSA.
+ * @param {Buffer} publicKeyDer The public key DER byte array.
+ */
+MemoryPrivateKeyStorage.prototype.setPublicKeyForKeyName = function
+ (keyName, keyType, publicKeyDer)
+{
+ this.publicKeyStore[keyName.toUri()] = new PublicKey
+ (new Blob(publicKeyDer, true));
+};
+
+/**
+ * Set the private key for the keyName.
+ * @param {Name} keyName The key name.
+ * @param {number} keyType The KeyType, such as KeyType.RSA.
+ * @param {Buffer} privateKeyDer The private key DER byte array.
+ */
+MemoryPrivateKeyStorage.prototype.setPrivateKeyForKeyName = function
+ (keyName, keyType, privateKeyDer)
+{
+ // Encode the DER as PEM.
+ var keyBase64 = privateKeyDer.toString('base64');
+ var keyPem;
+ if (keyType === KeyType.RSA) {
+ keyPem = "-----BEGIN RSA PRIVATE KEY-----\n";
+ for (var i = 0; i < keyBase64.length; i += 64)
+ keyPem += (keyBase64.substr(i, 64) + "\n");
+ keyPem += "-----END RSA PRIVATE KEY-----";
+ }
+ else if (keyType === KeyType.ECDSA) {
+ keyPem = "-----BEGIN EC PRIVATE KEY-----\n";
+ for (var i = 0; i < keyBase64.length; i += 64)
+ keyPem += (keyBase64.substr(i, 64) + "\n");
+ keyPem += "-----END EC PRIVATE KEY-----";
+ }
+ else
+ throw new SecurityException(new Error
+ ("MemoryPrivateKeyStorage: KeyType is not supported"));
+
+ this.privateKeyStore[keyName.toUri()] =
+ { keyType: keyType, privateKey: keyPem };
+};
+
+/**
+ * Set the public and private key for the keyName.
+ * @param {Name} keyName The key name.
+ * @param {number} keyType The KeyType, such as KeyType.RSA.
+ * @param {Buffer} publicKeyDer The public key DER byte array.
+ * @param {Buffer} privateKeyDer The private key DER byte array.
+ */
+MemoryPrivateKeyStorage.prototype.setKeyPairForKeyName = function
+ (keyName, keyType, publicKeyDer, privateKeyDer)
+{
+ this.setPublicKeyForKeyName(keyName, keyType, publicKeyDer);
+ this.setPrivateKeyForKeyName(keyName, keyType, privateKeyDer);
+};
+
+/**
+ * Generate a pair of asymmetric keys.
+ * @param {Name} keyName The name of the key pair.
+ * @param {KeyParams} params The parameters of the key.
+ * @param {boolean} useSync (optional) If true then use blocking crypto and
+ * return a SyncPromise which is already fulfilled. If omitted or false, if
+ * possible use crypto.subtle and return an async Promise, otherwise use
+ * blocking crypto and return a SyncPromise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the pair is
+ * generated.
+ */
+MemoryPrivateKeyStorage.prototype.generateKeyPairPromise = function
+ (keyName, params, useSync)
+{
+ if (this.doesKeyExist(keyName, KeyClass.PUBLIC))
+ return SyncPromise.reject(new SecurityException(new Error
+ ("Public key already exists")));
+ if (this.doesKeyExist(keyName, KeyClass.PRIVATE))
+ return SyncPromise.reject(new SecurityException(new Error
+ ("Private key already exists")));
+
+ var thisStore = this;
+
+ if (UseSubtleCrypto() && !useSync) {
+ if (params.getKeyType() === KeyType.RSA) {
+ var privateKey = null;
+ var publicKeyDer = null;
+
+ return crypto.subtle.generateKey
+ ({ name: "RSASSA-PKCS1-v1_5", modulusLength: params.getKeySize(),
+ publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
+ hash: {name: "SHA-256"} },
+ true, ["sign", "verify"])
+ .then(function(key) {
+ privateKey = key.privateKey;
+
+ // Export the public key to DER.
+ return crypto.subtle.exportKey("spki", key.publicKey);
+ })
+ .then(function(exportedPublicKey) {
+ publicKeyDer = new Blob(new Uint8Array(exportedPublicKey), false).buf();
+
+ // Export the private key to DER.
+ return crypto.subtle.exportKey("pkcs8", privateKey);
+ })
+ .then(function(pkcs8Der) {
+ // Crypto.subtle exports the private key as PKCS #8. Decode it to find
+ // the inner private key DER.
+ var parsedNode = DerNode.parse
+ (new Blob(new Uint8Array(pkcs8Der), false).buf());
+ // Get the value of the 3rd child which is the octet string.
+ var privateKeyDer = parsedNode.getChildren()[2].toVal();
+
+ // Save the key pair.
+ thisStore.setKeyPairForKeyName
+ (keyName, params.getKeyType(), publicKeyDer, privateKeyDer.buf());
+
+ // sign will use subtleKey directly.
+ thisStore.privateKeyStore[keyName.toUri()].subtleKey = privateKey;
+
+ return Promise.resolve();
+ });
+ }
+ else
+ return SyncPromise.reject(new SecurityException(new Error
+ ("Only RSA key generation currently supported")));
+ }
+ else {
+ return SyncPromise.resolve()
+ .then(function() {
+ if (typeof RSAKey !== 'undefined') {
+ // Assume we are in the browser.
+ if (params.getKeyType() === KeyType.RSA) {
+ var rsaKey = new RSAKey();
+ rsaKey.generate(params.getKeySize(), '010001');
+ thisStore.setKeyPairForKeyName
+ (keyName, params.getKeyType(),
+ PrivateKeyStorage.encodePublicKeyFromRSAKey(rsaKey).buf(),
+ PrivateKeyStorage.encodePkcs1PrivateKeyFromRSAKey(rsaKey).buf());
}
- }
- else
- this.reconnectAndExpressInterest(interest, closure);
+ else
+ return SyncPromise.reject(new SecurityException(new Error
+ ("Only RSA key generation currently supported")));
+ }
+ else {
+ // Assume we are in Node.js.
+ var publicKeyDer;
+ var privateKeyPem;
+
+ if (params.getKeyType() === KeyType.RSA) {
+ if (!rsaKeygen)
+ return SyncPromise.reject(new SecurityException(new Error
+ ("Need to install rsa-keygen: sudo npm install rsa-keygen")));
+
+ var keyPair = rsaKeygen.generate(params.getKeySize());
+
+ // Get the public key DER from the PEM string.
+ var publicKeyBase64 = keyPair.public_key.toString().replace
+ ("-----BEGIN PUBLIC KEY-----", "").replace
+ ("-----END PUBLIC KEY-----", "");
+ publicKeyDer = new Buffer(publicKeyBase64, 'base64');
+
+ privateKeyPem = keyPair.private_key.toString();
+ }
+ else
+ return SyncPromise.reject(new SecurityException(new Error
+ ("Only RSA key generation currently supported")));
+
+ thisStore.setPublicKeyForKeyName(keyName, params.getKeyType(), publicKeyDer);
+ thisStore.privateKeyStore[keyName.toUri()] =
+ { keyType: params.getKeyType(), privateKey: privateKeyPem };
+ }
+
+ return SyncPromise.resolve();
+ });
+ }
};
+/**
+ * Delete a pair of asymmetric keys. If the key doesn't exist, do nothing.
+ * @param {Name} keyName The name of the key pair.
+ * @return {SyncPromise} A promise that fulfills when the key pair is deleted.
+ */
+MemoryPrivateKeyStorage.prototype.deleteKeyPairPromise = function(keyName)
+{
+ var keyUri = keyName.toUri();
+
+ delete this.publicKeyStore[keyUri];
+ delete this.privateKeyStore[keyUri];
+
+ return SyncPromise.resolve();
+};
+
+/**
+ * Get the public key
+ * @param {Name} keyName The name of public key.
+ * @return {SyncPromise} A promise that returns the PublicKey.
+ */
+MemoryPrivateKeyStorage.prototype.getPublicKeyPromise = function(keyName)
+{
+ var keyUri = keyName.toUri();
+ var publicKey = this.publicKeyStore[keyUri];
+ if (publicKey === undefined)
+ return SyncPromise.reject(new SecurityException(new Error
+ ("MemoryPrivateKeyStorage: Cannot find public key " + keyName.toUri())));
+
+ return SyncPromise.resolve(publicKey);
+};
+
+/**
+ * Fetch the private key for keyName and sign the data to produce a signature Blob.
+ * @param {Buffer} data Pointer to the input byte array.
+ * @param {Name} keyName The name of the signing key.
+ * @param {number} digestAlgorithm (optional) The digest algorithm from
+ * DigestAlgorithm, such as DigestAlgorithm.SHA256. If omitted, use
+ * DigestAlgorithm.SHA256.
+ * @param {boolean} useSync (optional) If true then use blocking crypto and
+ * return a SyncPromise which is already fulfilled. If omitted or false, if
+ * possible use crypto.subtle and return an async Promise, otherwise use
+ * blocking crypto and return a SyncPromise.
+ * @return {Promise|SyncPromise} A promise that returns the signature Blob.
+ */
+MemoryPrivateKeyStorage.prototype.signPromise = function
+ (data, keyName, digestAlgorithm, useSync)
+{
+ useSync = (typeof digestAlgorithm === "boolean") ? digestAlgorithm : useSync;
+ digestAlgorithm = (typeof digestAlgorithm === "boolean" || !digestAlgorithm) ? DigestAlgorithm.SHA256 : digestAlgorithm;
+
+ if (digestAlgorithm != DigestAlgorithm.SHA256)
+ return SyncPromise.reject(new SecurityException(new Error
+ ("MemoryPrivateKeyStorage.sign: Unsupported digest algorithm")));
+
+ // Find the private key.
+ var keyUri = keyName.toUri();
+ var privateKey = this.privateKeyStore[keyUri];
+ if (privateKey === undefined)
+ return SyncPromise.reject(new SecurityException(new Error
+ ("MemoryPrivateKeyStorage: Cannot find private key " + keyUri)));
+
+ if (UseSubtleCrypto() && !useSync){
+ var algo = {name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};
+
+ if (!privateKey.subtleKey){
+ //this is the first time in the session that we're using crypto subtle with this key
+ //so we have to convert to pkcs8 and import it.
+ //assigning it to privateKey.subtleKey means we only have to do this once per session,
+ //giving us a small, but not insignificant, performance boost.
+ var privateDER = DataUtils.privateKeyPemToDer(privateKey.privateKey);
+ var pkcs8 = PrivateKeyStorage.encodePkcs8PrivateKey
+ (privateDER, new OID(PrivateKeyStorage.RSA_ENCRYPTION_OID),
+ new DerNode.DerNull()).buf();
+
+ var promise = crypto.subtle.importKey("pkcs8", pkcs8.buffer, algo, true, ["sign"]).then(function(subtleKey){
+ //cache the crypto.subtle key object
+ privateKey.subtleKey = subtleKey;
+ return crypto.subtle.sign(algo, subtleKey, data);
+ });
+ } else {
+ // The crypto.subtle key has been cached on a previous sign or from keygen.
+ var promise = crypto.subtle.sign(algo, privateKey.subtleKey, data);
+ }
+
+ return promise.then(function(signature){
+ var result = new Blob(new Uint8Array(signature), true);
+ return Promise.resolve(result);
+ });
+ } else {
+ var signer;
+ if (privateKey.keyType === KeyType.RSA)
+ signer = Crypto.createSign("RSA-SHA256");
+ else if (privateKey.keyType === KeyType.ECDSA)
+ // Just create a "sha256". The Crypto library will infer ECDSA from the key.
+ signer = Crypto.createSign("sha256");
+ else
+ // We don't expect this to happen since setPrivateKeyForKeyName already checked.
+ return SyncPromise.reject(new SecurityException(new Error
+ ("MemoryPrivateKeyStorage.sign: Unrecognized private key type")));
+
+ signer.update(data);
+ var signature = new Buffer
+ (DataUtils.toNumbersIfString(signer.sign(privateKey.privateKey)));
+ var result = new Blob(signature, false);
+
+ return SyncPromise.resolve(result);
+ }
+};
+
+/**
+ * Check if a particular key exists.
+ * @param {Name} keyName The name of the key.
+ * @param {number} keyClass The class of the key, e.g. KeyClass.PUBLIC,
+ * KeyClass.PRIVATE, or KeyClass.SYMMETRIC.
+ * @return {SyncPromise} A promise which returns true if the key exists.
+ */
+MemoryPrivateKeyStorage.prototype.doesKeyExistPromise = function
+ (keyName, keyClass)
+{
+ var keyUri = keyName.toUri();
+ var result = false;
+ if (keyClass == KeyClass.PUBLIC)
+ result = this.publicKeyStore[keyUri] !== undefined;
+ else if (keyClass == KeyClass.PRIVATE)
+ result = this.privateKeyStore[keyUri] !== undefined;
+
+ return SyncPromise.resolve(result);
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// Use capitalized Crypto to not clash with the browser's crypto.subtle.
+var Crypto = require('../../crypto.js');
+// Don't require other modules since this is meant for the browser, not Node.js.
+
+/**
+ * IndexedDbPrivateKeyStorage extends PrivateKeyStorage to implement private key
+ * storage using the browser's IndexedDB service.
+ * @constructor
+ */
+var IndexedDbPrivateKeyStorage = function IndexedDbPrivateKeyStorage()
+{
+ PrivateKeyStorage.call(this);
+
+ this.database = new Dexie("ndnsec-tpm");
+ this.database.version(1).stores({
+ // "nameHash" is transformName(keyName) // string
+ // "encoding" is the public key DER // Uint8Array
+ publicKey: "nameHash",
+
+ // "nameHash" is transformName(keyName) // string
+ // "encoding" is the PKCS 8 private key DER // Uint8Array
+ privateKey: "nameHash"
+ });
+ this.database.open();
+};
+
+IndexedDbPrivateKeyStorage.prototype = new PrivateKeyStorage();
+IndexedDbPrivateKeyStorage.prototype.name = "IndexedDbPrivateKeyStorage";
+
+/**
+ * Generate a pair of asymmetric keys.
+ * @param {Name} keyName The name of the key pair.
+ * @param {KeyParams} params The parameters of the key.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the pair is generated.
+ */
+IndexedDbPrivateKeyStorage.prototype.generateKeyPairPromise = function
+ (keyName, params, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbPrivateKeyStorage.generateKeyPairPromise is only supported for async")));
+
+ var thisStorage = this;
+
+ return thisStorage.doesKeyExistPromise(keyName, KeyClass.PUBLIC)
+ .then(function(exists) {
+ if (exists)
+ throw new Error("Public key already exists");
+
+ return thisStorage.doesKeyExistPromise(keyName, KeyClass.PRIVATE);
+ })
+ .then(function(exists) {
+ if (exists)
+ throw new Error("Private key already exists");
+
+ if (params.getKeyType() === KeyType.RSA) {
+ var privateKey = null;
+ var publicKeyDer = null;
+
+ return crypto.subtle.generateKey
+ ({ name: "RSASSA-PKCS1-v1_5", modulusLength: params.getKeySize(),
+ publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
+ hash: {name: "SHA-256"} },
+ true, ["sign", "verify"])
+ .then(function(key) {
+ privateKey = key.privateKey;
+
+ // Export the public key to DER.
+ return crypto.subtle.exportKey("spki", key.publicKey);
+ })
+ .then(function(exportedPublicKey) {
+ publicKeyDer = new Uint8Array(exportedPublicKey);
+
+ // Export the private key to DER.
+ return crypto.subtle.exportKey("pkcs8", privateKey);
+ })
+ .then(function(pkcs8Der) {
+ // Save the key pair
+ return thisStorage.database.transaction
+ ("rw", thisStorage.database.privateKey, thisStorage.database.publicKey, function () {
+ thisStorage.database.publicKey.put
+ ({nameHash: IndexedDbPrivateKeyStorage.transformName(keyName),
+ encoding: publicKeyDer});
+ thisStorage.database.privateKey.put
+ ({nameHash: IndexedDbPrivateKeyStorage.transformName(keyName),
+ encoding: new Uint8Array(pkcs8Der)});
+ });
+ });
+ }
+ else
+ throw new Error("Only RSA key generation currently supported");
+ });
+};
+
+
+/**
+ * Delete a pair of asymmetric keys. If the key doesn't exist, do nothing.
+ * @param {Name} keyName The name of the key pair.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the key pair is deleted.
+ */
+IndexedDbPrivateKeyStorage.prototype.deleteKeyPairPromise = function
+ (keyName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbPrivateKeyStorage.deleteKeyPairPromise is only supported for async")));
+
+ var thisStorage = this;
+ // delete does nothing if the key doesn't exist.
+ return this.database.publicKey.delete
+ (IndexedDbPrivateKeyStorage.transformName(keyName))
+ .then(function() {
+ return thisStorage.database.privateKey.delete
+ (IndexedDbPrivateKeyStorage.transformName(keyName));
+ });
+};
+
+/**
+ * Get the public key
+ * @param {Name} keyName The name of public key.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that returns the PublicKey.
+ */
+IndexedDbPrivateKeyStorage.prototype.getPublicKeyPromise = function
+ (keyName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbPrivateKeyStorage.getPublicKeyPromise is only supported for async")));
+
+ return this.database.publicKey.get
+ (IndexedDbPrivateKeyStorage.transformName(keyName))
+ .then(function(publicKeyEntry) {
+ return Promise.resolve(new PublicKey(new Blob(publicKeyEntry.encoding)));
+ });
+};
+
+/**
+ * Fetch the private key for keyName and sign the data to produce a signature Blob.
+ * @param {Buffer} data Pointer to the input byte array.
+ * @param {Name} keyName The name of the signing key.
+ * @param {number} digestAlgorithm (optional) The digest algorithm from
+ * DigestAlgorithm, such as DigestAlgorithm.SHA256. If omitted, use
+ * DigestAlgorithm.SHA256.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that returns the signature Blob.
+ */
+IndexedDbPrivateKeyStorage.prototype.signPromise = function
+ (data, keyName, digestAlgorithm, useSync)
+{
+ useSync = (typeof digestAlgorithm === "boolean") ? digestAlgorithm : useSync;
+ digestAlgorithm = (typeof digestAlgorithm === "boolean" || !digestAlgorithm) ? DigestAlgorithm.SHA256 : digestAlgorithm;
+
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbPrivateKeyStorage.signPromise is only supported for async")));
+
+ if (digestAlgorithm != DigestAlgorithm.SHA256)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbPrivateKeyStorage.sign: Unsupported digest algorithm")));
+
+ // TODO: Support non-RSA keys.
+ var algo = { name: "RSASSA-PKCS1-v1_5", hash: {name: "SHA-256" }};
+
+ // Find the private key.
+ return this.database.privateKey.get
+ (IndexedDbPrivateKeyStorage.transformName(keyName))
+ .then(function(privateKeyEntry) {
+ return crypto.subtle.importKey
+ ("pkcs8", new Blob(privateKeyEntry.encoding).buf(), algo, true, ["sign"]);
+ })
+ .then(function(privateKey) {
+ return crypto.subtle.sign(algo, privateKey, data);
+ })
+ .then(function(signature) {
+ return Promise.resolve(new Blob(new Uint8Array(signature), true));
+ });
+};
+
+/**
+ * Check if a particular key exists.
+ * @param {Name} keyName The name of the key.
+ * @param {number} keyClass The class of the key, e.g. KeyClass.PUBLIC,
+ * KeyClass.PRIVATE, or KeyClass.SYMMETRIC.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise which returns true if the key exists.
+ */
+IndexedDbPrivateKeyStorage.prototype.doesKeyExistPromise = function
+ (keyName, keyClass, useSync)
+{
+ if (useSync)
+ return Promise.reject(new SecurityException(new Error
+ ("IndexedDbPrivateKeyStorage.doesKeyExistPromise is only supported for async")));
+
+ var table = null;
+ if (keyClass == KeyClass.PUBLIC)
+ table = this.database.publicKey;
+ else if (keyClass == KeyClass.PRIVATE)
+ table = this.database.privateKey;
+ else
+ // Silently say that anything else doesn't exist.
+ return Promise.resolve(false);
+
+ return table.where("nameHash").equals
+ (IndexedDbPrivateKeyStorage.transformName(keyName))
+ .count()
+ .then(function(count) {
+ return Promise.resolve(count > 0);
+ });
+};
+
+/**
+ * Transform the key name into the base64 encoding of the hash (the same as in
+ * FilePrivateKeyStorage without the file name extension).
+ */
+IndexedDbPrivateKeyStorage.transformName = function(keyName)
+{
+ var hash = Crypto.createHash('sha256');
+ hash.update(new Buffer(keyName.toUri()));
+ var fileName = hash.digest('base64');
+ return fileName.replace(/\//g, '%');
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// Use capitalized Crypto to not clash with the browser's crypto.subtle.
+/** @ignore */
+var Crypto = require('../../crypto.js'); /** @ignore */
+var Name = require('../../name.js').Name; /** @ignore */
+var Data = require('../../data.js').Data; /** @ignore */
+var Blob = require('../../util/blob.js').Blob; /** @ignore */
+var ConfigFile = require('../../util/config-file.js').ConfigFile; /** @ignore */
+var DigestSha256Signature = require('../../digest-sha256-signature.js').DigestSha256Signature; /** @ignore */
+var Sha256WithRsaSignature = require('../../sha256-with-rsa-signature.js').Sha256WithRsaSignature; /** @ignore */
+var Sha256WithEcdsaSignature = require('../../sha256-with-ecdsa-signature.js').Sha256WithEcdsaSignature; /** @ignore */
+var KeyLocatorType = require('../../key-locator.js').KeyLocatorType; /** @ignore */
+var WireFormat = require('../../encoding/wire-format.js').WireFormat; /** @ignore */
+var SecurityException = require('../security-exception.js').SecurityException; /** @ignore */
+var DigestAlgorithm = require('../security-types.js').DigestAlgorithm; /** @ignore */
+var KeyType = require('../security-types.js').KeyType; /** @ignore */
+var RsaKeyParams = require('../key-params.js').RsaKeyParams; /** @ignore */
+var IdentityCertificate = require('../certificate/identity-certificate.js').IdentityCertificate; /** @ignore */
+var PublicKey = require('../certificate/public-key.js').PublicKey; /** @ignore */
+var CertificateSubjectDescription = require('../certificate/certificate-subject-description.js').CertificateSubjectDescription; /** @ignore */
+var SyncPromise = require('../../util/sync-promise.js').SyncPromise; /** @ignore */
+var BasicIdentityStorage = require('./basic-identity-storage.js').BasicIdentityStorage; /** @ignore */
+var FilePrivateKeyStorage = require('./file-private-key-storage.js').FilePrivateKeyStorage;
+
+/**
+ * An IdentityManager is the interface of operations related to identity, keys,
+ * and certificates.
+ *
+ * Create a new IdentityManager to use the IdentityStorage and
+ * PrivateKeyStorage.
+ * @param {IdentityStorage} identityStorage An object of a subclass of
+ * IdentityStorage. In Node.js, if this is omitted then use BasicIdentityStorage.
+ * @param {PrivateKeyStorage} privateKeyStorage An object of a subclass of
+ * PrivateKeyStorage. In Node.js, if this is omitted then use the default
+ * PrivateKeyStorage for your system, which is FilePrivateKeyStorage for any
+ * system other than OS X. (OS X key chain storage is not yet implemented, so
+ * you must supply a different PrivateKeyStorage.)
+ * @throws SecurityException if this is not in Node.js and identityStorage or
+ * privateKeyStorage is omitted.
+ * @constructor
+ */
+var IdentityManager = function IdentityManager
+ (identityStorage, privateKeyStorage)
+{
+ if (privateKeyStorage) {
+ // Don't call checkTpm() when using a custom PrivateKeyStorage.
+ if (!identityStorage)
+ // We don't expect this to happen.
+ throw new Error
+ ("IdentityManager: A custom privateKeyStorage is supplied with a null identityStorage")
+
+ this.identityStorage = identityStorage;
+ this.privateKeyStorage = privateKeyStorage;
+ }
+ else {
+ if (!ConfigFile)
+ // Assume we are in the browser.
+ throw new SecurityException(new Error
+ ("IdentityManager: If not in Node.js then you must supply identityStorage and privateKeyStorage."));
+ var config = new ConfigFile();
+
+ var canonicalTpmLocator = [null];
+ var thisStorage = this;
+ // Make the function that BasicIdentityStorage will call the first time it
+ // is used. It has to be an async promise becuase getTpmLocatorPromise is async.
+ function initialCheckPromise()
+ {
+ return thisStorage.checkTpmPromise_(canonicalTpmLocator[0]);
+ }
+
+ this.identityStorage = identityStorage ? identityStorage
+ : IdentityManager.getDefaultIdentityStorage_(config, initialCheckPromise);
+ this.privateKeyStorage = IdentityManager.getDefaultPrivateKeyStorage_
+ (config, canonicalTpmLocator);
+ }
+};
+
+exports.IdentityManager = IdentityManager;
+
+/**
+ * Create an identity by creating a pair of Key-Signing-Key (KSK) for this
+ * identity and a self-signed certificate of the KSK. If a key pair or
+ * certificate for the identity already exists, use it.
+ * @param {Name} identityName The name of the identity.
+ * @params {KeyParams} params The key parameters if a key needs to be generated
+ * for the identity.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the name of the default
+ * certificate of the identity.
+ */
+IdentityManager.prototype.createIdentityAndCertificatePromise = function
+ (identityName, params, useSync)
+{
+ var thisManager = this;
+ var generateKey = true;
+ var keyName = null;
+
+ return this.identityStorage.addIdentityPromise(identityName, useSync)
+ .then(function() {
+ return thisManager.identityStorage.getDefaultKeyNameForIdentityPromise
+ (identityName, useSync)
+ .then(function(localKeyName) {
+ keyName = localKeyName;
+
+ // Set generateKey.
+ return thisManager.identityStorage.getKeyPromise(keyName, useSync)
+ .then(function(publicKeyDer) {
+ var key = new PublicKey(publicKeyDer);
+ if (key.getKeyType() == params.getKeyType())
+ // The key exists and has the same type, so don't need to generate one.
+ generateKey = false;
+ return SyncPromise.resolve();
+ });
+ }, function(err) {
+ if (!(err instanceof SecurityException))
+ throw err;
+
+ // The key doesn't exist, so leave generateKey true.
+ return SyncPromise.resolve();
+ });
+ })
+ .then(function() {
+ if (generateKey)
+ return thisManager.generateKeyPairPromise(identityName, true, params, useSync)
+ .then(function(localKeyName) {
+ keyName = localKeyName;
+ return thisManager.identityStorage.setDefaultKeyNameForIdentityPromise
+ (keyName, useSync);
+ });
+ else
+ // Don't generate a key pair. Use the existing keyName.
+ return SyncPromise.resolve();
+ })
+ .then(function() {
+ return thisManager.identityStorage.getDefaultCertificateNameForKeyPromise
+ (keyName, useSync)
+ .then(function(certName) {
+ // The cert exists, so don't need to make it.
+ return SyncPromise.resolve(certName);
+ }, function(err) {
+ if (!(err instanceof SecurityException))
+ throw err;
+
+ // The cert doesn't exist, so make one.
+ var certName;
+ return thisManager.selfSignPromise(keyName, useSync)
+ .then(function(selfCert) {
+ certName = selfCert.getName();
+ return thisManager.addCertificateAsIdentityDefaultPromise(selfCert, useSync);
+ })
+ .then(function() {
+ return SyncPromise.resolve(certName);
+ });
+ });
+ });
+};
+
+/**
+ * Create an identity by creating a pair of Key-Signing-Key (KSK) for this
+ * identity and a self-signed certificate of the KSK. If a key pair or
+ * certificate for the identity already exists, use it.
+ * @param {Name} identityName The name of the identity.
+ * @params {KeyParams} params The key parameters if a key needs to be generated
+ * for the identity.
+ * @param {function} onComplete (optional) This calls onComplete(certificateName)
+ * with the name of the default certificate of the identity. If omitted, the
+ * return value is described below. (Some crypto libraries only use a callback,
+ * so onComplete is required to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some crypto libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {Name} If onComplete is omitted, return the name of the default
+ * certificate of the identity. Otherwise, if onComplete is supplied then return
+ * undefined and use onComplete as described above.
+ */
+IdentityManager.prototype.createIdentityAndCertificate = function
+ (identityName, params, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.createIdentityAndCertificatePromise(identityName, params, !onComplete));
+};
+
+/**
+ * Create an identity by creating a pair of Key-Signing-Key (KSK) for this
+ * identity and a self-signed certificate of the KSK. If a key pair or
+ * certificate for the identity already exists, use it.
+ * @deprecated Use createIdentityAndCertificate which returns the
+ * certificate name instead of the key name. You can use
+ * IdentityCertificate.certificateNameToPublicKeyName to convert the
+ * certificate name to the key name.
+ * @param {Name} identityName The name of the identity.
+ * @params {KeyParams} params The key parameters if a key needs to be generated
+ * for the identity.
+ * @return {Name} The key name of the auto-generated KSK of the identity.
+ */
+IdentityManager.prototype.createIdentity = function(identityName, params)
+{
+ return IdentityCertificate.certificateNameToPublicKeyName
+ (this.createIdentityAndCertificate(identityName, params));
+};
+
+/**
+ * Delete the identity from the public and private key storage. If the
+ * identity to be deleted is the current default system default, this will not
+ * delete the identity and will return immediately.
+ * @param {Name} identityName The name of the identity.
+ * @param {function} onComplete (optional) This calls onComplete() when the
+ * operation is complete. If omitted, do not use it. (Some database libraries
+ * only use a callback, so onComplete is required to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ */
+IdentityManager.prototype.deleteIdentity = function
+ (identityName, onComplete, onError)
+{
+ var useSync = !onComplete;
+ var thisManager = this;
+
+ var doDelete = true;
+
+ var mainPromise = this.identityStorage.getDefaultIdentityPromise(useSync)
+ .then(function(defaultIdentityName) {
+ if (defaultIdentityName.equals(identityName))
+ // Don't delete the default identity!
+ doDelete = false;
+
+ return SyncPromise.resolve();
+ }, function(err) {
+ // There is no default identity to check.
+ return SyncPromise.resolve();
+ })
+ .then(function() {
+ if (!doDelete)
+ return SyncPromise.resolve();
+
+ var keysToDelete = [];
+ return thisManager.identityStorage.getAllKeyNamesOfIdentityPromise
+ (identityName, keysToDelete, true)
+ .then(function() {
+ return thisManager.identityStorage.getAllKeyNamesOfIdentityPromise
+ (identityName, keysToDelete, false);
+ })
+ .then(function() {
+ return thisManager.identityStorage.deleteIdentityInfoPromise(identityName);
+ })
+ .then(function() {
+ // Recursively loop through keysToDelete, calling deleteKeyPairPromise.
+ function deleteKeyLoop(i) {
+ if (i >= keysToDelete.length)
+ return SyncPromise.resolve();
+
+ return thisManager.privateKeyStorage.deleteKeyPairPromise(keysToDelete[i])
+ .then(function() {
+ return deleteKeyLoop(i + 1);
+ });
+ }
+
+ return deleteKeyLoop(0);
+ });
+ });
+
+ return SyncPromise.complete(onComplete, onError, mainPromise);
+};
+
+/**
+ * Set the default identity. If the identityName does not exist, then clear the
+ * default identity so that getDefaultIdentity() throws an exception.
+ * @param {Name} identityName The default identity name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when the default
+ * identity is set.
+ */
+IdentityManager.prototype.setDefaultIdentityPromise = function
+ (identityName, useSync)
+{
+ return this.identityStorage.setDefaultIdentityPromise(identityName, useSync);
+};
+
+/**
+ * Set the default identity. If the identityName does not exist, then clear the
+ * default identity so that getDefaultIdentity() throws an exception.
+ * @param {Name} identityName The default identity name.
+ * @param {function} onComplete (optional) This calls onComplete() when complete.
+ * (Some database libraries only use a callback, so onComplete is required to
+ * use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ */
+IdentityManager.prototype.setDefaultIdentity = function
+ (identityName, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.setDefaultIdentityPromise(identityName, !onComplete));
+};
+
+/**
+ * Get the default identity.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the Name of default
+ * identity, or a promise rejected with SecurityException if the default
+ * identity is not set.
+ */
+IdentityManager.prototype.getDefaultIdentityPromise = function(useSync)
+{
+ return this.identityStorage.getDefaultIdentityPromise(useSync);
+};
+
+/**
+ * Get the default identity.
+ * @param {function} onComplete (optional) This calls onComplete(identityName)
+ * with name of the default identity. If omitted, the return value is described
+ * below. (Some database libraries only use a callback, so onComplete is required
+ * to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {Name} If onComplete is omitted, return the name of the default
+ * identity. Otherwise, if onComplete is supplied then return undefined and use
+ * onComplete as described above.
+ * @throws SecurityException if the default identity is not set. However, if
+ * onComplete and onError are defined, then if there is an exception return
+ * undefined and call onError(exception).
+ */
+IdentityManager.prototype.getDefaultIdentity = function(onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.getDefaultIdentityPromise(!onComplete));
+};
+
+/**
+ * Get the certificate of the default identity.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the requested
+ * IdentityCertificate or null if not found.
+ */
+IdentityManager.prototype.getDefaultCertificatePromise = function(useSync)
+{
+ return this.identityStorage.getDefaultCertificatePromise(useSync);
+};
+
+/**
+ * Generate a pair of RSA keys for the specified identity.
+ * @param {Name} identityName The name of the identity.
+ * @param {boolean} isKsk True for generating a Key-Signing-Key (KSK), false for
+ * a Data-Signing-Key (DSK).
+ * @param {number} keySize The size of the key.
+ * @return {Name} The generated key name.
+ */
+IdentityManager.prototype.generateRSAKeyPair = function
+ (identityName, isKsk, keySize)
+{
+ // For now, require sync. This method may be removed from the API.
+ return SyncPromise.getValue
+ (this.generateKeyPairPromise
+ (identityName, isKsk, new RsaKeyParams(keySize), true));
+};
+
+/**
+ * Set a key as the default key of an identity. The identity name is inferred
+ * from keyName.
+ * @param {Name} keyName The name of the key.
+ * @param {Name} identityNameCheck (optional) The identity name to check that the
+ * keyName contains the same identity name. If an empty name, it is ignored.
+ * @param {function} onComplete (optional) This calls onComplete() when complete.
+ * (Some database libraries only use a callback, so onComplete is required to
+ * use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ */
+IdentityManager.prototype.setDefaultKeyForIdentity = function
+ (keyName, identityNameCheck, onComplete, onError)
+{
+ onError = (typeof identityNameCheck === "function") ? onComplete : onError;
+ onComplete = (typeof identityNameCheck === "function") ?
+ identityNameCheck : onComplete;
+ identityNameCheck = (typeof identityNameCheck === "function" || !identityNameCheck) ?
+ new Name() : identityNameCheck;
+
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.setDefaultKeyNameForIdentityPromise
+ (keyName, identityNameCheck, !onComplete));
+};
+
+/**
+ * Get the default key for an identity.
+ * @param {Name} identityName The name of the identity.
+ * @param {function} onComplete (optional) This calls onComplete(keyName)
+ * with name of the default key. If omitted, the return value is described
+ * below. (Some database libraries only use a callback, so onComplete is required
+ * to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {Name} If onComplete is omitted, return the default key name.
+ * Otherwise, if onComplete is supplied then return undefined and use onComplete
+ * as described above.
+ * @throws SecurityException if the default key name for the identity is not set.
+ * However, if onComplete and onError are defined, then if there is an exception
+ * return undefined and call onError(exception).
+ */
+IdentityManager.prototype.getDefaultKeyNameForIdentity = function
+ (identityName, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.getDefaultKeyNameForIdentityPromise
+ (identityName, !onComplete));
+};
+
+/**
+ * Generate a pair of RSA keys for the specified identity and set it as default
+ * key for the identity.
+ * @param {Name} identityName The name of the identity.
+ * @param {boolean} isKsk True for generating a Key-Signing-Key (KSK), false for
+ * a Data-Signing-Key (DSK).
+ * @param {number} keySize The size of the key.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If false, this may return a SyncPromise or an async
+ * Promise.
+ * @return {Promise|SyncPromise} A promise which returns the generated key name.
+ */
+IdentityManager.prototype.generateRSAKeyPairAsDefaultPromise = function
+ (identityName, isKsk, keySize, useSync)
+{
+ var newKeyName;
+ var thisManager = this;
+ return this.generateKeyPairPromise(identityName, isKsk, new RsaKeyParams(keySize))
+ .then(function(localKeyName) {
+ newKeyName = localKeyName;
+
+ return thisManager.identityStorage.setDefaultKeyNameForIdentityPromise
+ (newKeyName);
+ })
+ .then(function() {
+ return SyncPromise.resolve(newKeyName);
+ });
+};
+
+/**
+ * Generate a pair of RSA keys for the specified identity and set it as default
+ * key for the identity.
+ * @param {Name} identityName The name of the identity.
+ * @param {boolean} isKsk True for generating a Key-Signing-Key (KSK), false for
+ * a Data-Signing-Key (DSK).
+ * @param {number} keySize The size of the key.
+ * @return {Name} The generated key name.
+ */
+IdentityManager.prototype.generateRSAKeyPairAsDefault = function
+ (identityName, isKsk, keySize)
+{
+ return SyncPromise.getValue
+ (this.generateRSAKeyPairAsDefaultPromise(identityName, isKsk, keySize, true));
+};
+
+/**
+ * Get the public key with the specified name.
+ * @param {Name} keyName The name of the key.
+ * @param {function} onComplete (optional) This calls onComplete(publicKey)
+ * with PublicKey. If omitted, the return value is described below. (Some database
+ * libraries only use a callback, so onComplete is required to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {PublicKey} If onComplete is omitted, return the public key.
+ * Otherwise, if onComplete is supplied then return undefined and use onComplete
+ * as described above.
+ */
+IdentityManager.prototype.getPublicKey = function(keyName, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.getKeyPromise(keyName, !onComplete)
+ .then(function(keyDer) {
+ return SyncPromise.resolve(new PublicKey(keyDer));
+ }));
+};
+
+// TODO: Add two versions of createIdentityCertificate.
+
+/**
+ * Prepare an unsigned identity certificate.
+ * @param {Name} keyName The key name, e.g., `/{identity_name}/ksk-123456`.
+ * @param {PublicKey} publicKey (optional) The public key to sign. If ommited,
+ * use the keyName to get the public key from the identity storage.
+ * @param {Name} signingIdentity The signing identity.
+ * @param {number} notBefore See IdentityCertificate.
+ * @param {number} notAfter See IdentityCertificate.
+ * @param {Array<CertificateSubjectDescription>} subjectDescription A list of
+ * CertificateSubjectDescription. See IdentityCertificate. If null or empty,
+ * this adds a an ATTRIBUTE_NAME based on the keyName.
+ * @param {Name} certPrefix (optional) The prefix before the `KEY` component. If
+ * null or omitted, this infers the certificate name according to the relation
+ * between the signingIdentity and the subject identity. If the signingIdentity
+ * is a prefix of the subject identity, `KEY` will be inserted after the
+ * signingIdentity, otherwise `KEY` is inserted after subject identity (i.e.,
+ * before `ksk-...`).
+ * @param {function} onComplete (optional) This calls onComplete(certificate)
+ * with the unsigned IdentityCertificate, or null if the inputs are invalid. If
+ * omitted, the return value is described below. (Some database libraries only
+ * use a callback, so onComplete is required to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {IdentityCertificate} If onComplete is omitted, return the the
+ * unsigned IdentityCertificate, or null if the inputs are invalid. Otherwise,
+ * if onComplete is supplied then return undefined and use onComplete as
+ * described above.
+ */
+IdentityManager.prototype.prepareUnsignedIdentityCertificate = function
+ (keyName, publicKey, signingIdentity, notBefore, notAfter, subjectDescription,
+ certPrefix, onComplete, onError)
+{
+ if (!(publicKey instanceof PublicKey)) {
+ // The publicKey was omitted. Shift arguments.
+ onError = onComplete;
+ onComplete = certPrefix;
+ certPrefix = subjectDescription;
+ subjectDescription = notAfter;
+ notAfter = notBefore;
+ notBefore = signingIdentity;
+ signingIdentity = publicKey;
+ publicKey = null;
+ }
+
+ // certPrefix may be omitted or null, so check for it and the following args.
+ var arg7 = certPrefix;
+ var arg8 = onComplete;
+ var arg9 = onError;
+ if (arg7 instanceof Name)
+ certPrefix = arg7;
+ else
+ certPrefix = null;
+
+ if (typeof arg7 === 'function') {
+ onComplete = arg7;
+ onError = arg8;
+ }
+ else if (typeof arg8 === 'function') {
+ onComplete = arg8;
+ onError = arg9;
+ }
+ else {
+ onComplete = null;
+ onError = null;
+ }
+
+ var promise;
+ if (publicKey == null)
+ promise = this.prepareUnsignedIdentityCertificatePromise
+ (keyName, signingIdentity, notBefore, notAfter, subjectDescription,
+ certPrefix, !onComplete);
+ else
+ promise = this.prepareUnsignedIdentityCertificatePromise
+ (keyName, publicKey, signingIdentity, notBefore, notAfter,
+ subjectDescription, certPrefix, !onComplete);
+ return SyncPromise.complete(onComplete, onError, promise);
+};
+
+/**
+ * Prepare an unsigned identity certificate.
+ * @param {Name} keyName The key name, e.g., `/{identity_name}/ksk-123456`.
+ * @param {PublicKey} publicKey (optional) The public key to sign. If ommited,
+ * use the keyName to get the public key from the identity storage.
+ * @param {Name} signingIdentity The signing identity.
+ * @param {number} notBefore See IdentityCertificate.
+ * @param {number} notAfter See IdentityCertificate.
+ * @param {Array<CertificateSubjectDescription>} subjectDescription A list of
+ * CertificateSubjectDescription. See IdentityCertificate. If null or empty,
+ * this adds a an ATTRIBUTE_NAME based on the keyName.
+ * @param {Name} certPrefix (optional) The prefix before the `KEY` component. If
+ * null or omitted, this infers the certificate name according to the relation
+ * between the signingIdentity and the subject identity. If the signingIdentity
+ * is a prefix of the subject identity, `KEY` will be inserted after the
+ * signingIdentity, otherwise `KEY` is inserted after subject identity (i.e.,
+ * before `ksk-...`).
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns the unsigned
+ * IdentityCertificate, or that returns null if the inputs are invalid.
+ */
+IdentityManager.prototype.prepareUnsignedIdentityCertificatePromise = function
+ (keyName, publicKey, signingIdentity, notBefore, notAfter, subjectDescription,
+ certPrefix, useSync)
+{
+ if (!(publicKey instanceof PublicKey)) {
+ // The publicKey was omitted. Shift arguments.
+ useSync = certPrefix;
+ certPrefix = subjectDescription;
+ subjectDescription = notAfter;
+ notAfter = notBefore;
+ notBefore = signingIdentity;
+ signingIdentity = publicKey;
+ publicKey = null;
+ }
+
+ // certPrefix may be omitted or null, so check for it and the following arg.
+ var arg7 = certPrefix;
+ var arg8 = useSync;
+ if (arg7 instanceof Name)
+ certPrefix = arg7;
+ else
+ certPrefix = null;
+
+ if (typeof arg7 === 'boolean')
+ useSync = arg7;
+ else if (typeof arg8 === 'boolean')
+ useSync = arg8;
+ else
+ useSync = false;
+
+ var promise;
+ if (publicKey == null) {
+ promise = this.identityStorage.getKeyPromise(keyName, useSync)
+ .then(function(keyDer) {
+ publicKey = new PublicKey(keyDer);
+ return SyncPromise.resolve();
+ });
+ }
+ else
+ promise = SyncPromise.resolve();
+
+ return promise
+ .then(function() {
+ return SyncPromise.resolve
+ (IdentityManager.prepareUnsignedIdentityCertificateHelper_
+ (keyName, publicKey, signingIdentity, notBefore, notAfter,
+ subjectDescription, certPrefix));
+ });
+};
+
+/**
+ * A helper for prepareUnsignedIdentityCertificatePromise where the publicKey
+ * is known.
+ */
+IdentityManager.prepareUnsignedIdentityCertificateHelper_ = function
+ (keyName, publicKey, signingIdentity, notBefore, notAfter, subjectDescription,
+ certPrefix)
+{
+ if (keyName.size() < 1)
+ return null;
+
+ var tempKeyIdPrefix = keyName.get(-1).toEscapedString();
+ if (tempKeyIdPrefix.length < 4)
+ return null;
+ keyIdPrefix = tempKeyIdPrefix.substr(0, 4);
+ if (keyIdPrefix != "ksk-" && keyIdPrefix != "dsk-")
+ return null;
+
+ var certificate = new IdentityCertificate();
+ var certName = new Name();
+
+ if (certPrefix == null) {
+ // No certificate prefix hint, so infer the prefix.
+ if (signingIdentity.match(keyName))
+ certName.append(signingIdentity)
+ .append("KEY")
+ .append(keyName.getSubName(signingIdentity.size()))
+ .append("ID-CERT")
+ .appendVersion(new Date().getTime());
+ else
+ certName.append(keyName.getPrefix(-1))
+ .append("KEY")
+ .append(keyName.get(-1))
+ .append("ID-CERT")
+ .appendVersion(new Date().getTime());
+ }
+ else {
+ // A cert prefix hint is supplied, so determine the cert name.
+ if (certPrefix.match(keyName) && !certPrefix.equals(keyName))
+ certName.append(certPrefix)
+ .append("KEY")
+ .append(keyName.getSubName(certPrefix.size()))
+ .append("ID-CERT")
+ .appendVersion(new Date().getTime());
+ else
+ return null;
+ }
+
+ certificate.setName(certName);
+ certificate.setNotBefore(notBefore);
+ certificate.setNotAfter(notAfter);
+ certificate.setPublicKeyInfo(publicKey);
+
+ if (subjectDescription == null || subjectDescription.length === 0)
+ certificate.addSubjectDescription(new CertificateSubjectDescription
+ ("2.5.4.41", keyName.getPrefix(-1).toUri()));
+ else {
+ for (var i = 0; i < subjectDescription.length; ++i)
+ certificate.addSubjectDescription(subjectDescription[i]);
+ }
+
+ try {
+ certificate.encode();
+ } catch (ex) {
+ throw SecurityException(new Error("DerEncodingException: " + ex));
+ }
+
+ return certificate;
+};
+
+/**
+ * Add a certificate into the public key identity storage.
+ * @param {IdentityCertificate} certificate The certificate to to added. This
+ * makes a copy of the certificate.
+ * @param {function} onComplete (optional) This calls onComplete() when complete.
+ * (Some database libraries only use a callback, so onComplete is required to
+ * use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ */
+IdentityManager.prototype.addCertificate = function
+ (certificate, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.addCertificatePromise(certificate, !onComplete));
+};
+
+/**
+ * Set the certificate as the default for its corresponding key.
+ * @param {IdentityCertificate} certificate The certificate.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If false, this may return a SyncPromise or an async
+ * Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when the default
+ * certificate is set.
+ */
+IdentityManager.prototype.setDefaultCertificateForKeyPromise = function
+ (certificate, useSync)
+{
+ var thisManager = this;
+
+ var keyName = certificate.getPublicKeyName();
+ return this.identityStorage.doesKeyExistPromise(keyName, useSync)
+ .then(function(exists) {
+ if (!exists)
+ throw new SecurityException(new Error
+ ("No corresponding Key record for certificate!"));
+
+ return thisManager.identityStorage.setDefaultCertificateNameForKeyPromise
+ (keyName, certificate.getName(), useSync);
+ });
+};
+
+/**
+ * Set the certificate as the default for its corresponding key.
+ * @param {IdentityCertificate} certificate The certificate.
+ * @param {function} onComplete (optional) This calls onComplete() when complete.
+ * (Some database libraries only use a callback, so onComplete is required to
+ * use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ */
+IdentityManager.prototype.setDefaultCertificateForKey = function
+ (certificate, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.setDefaultCertificateForKeyPromise(certificate, !onComplete));
+};
+
+/**
+ * Add a certificate into the public key identity storage and set the
+ * certificate as the default for its corresponding identity.
+ * @param {IdentityCertificate} certificate The certificate to be added. This
+ * makes a copy of the certificate.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If false, this may return a SyncPromise or an async
+ * Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when the certificate
+ * is added.
+ */
+IdentityManager.prototype.addCertificateAsIdentityDefaultPromise = function
+ (certificate, useSync)
+{
+ var thisManager = this;
+ return this.identityStorage.addCertificatePromise(certificate, useSync)
+ .then(function() {
+ var keyName = certificate.getPublicKeyName();
+ return thisManager.identityStorage.setDefaultKeyNameForIdentityPromise
+ (keyName, useSync);
+ })
+ .then(function() {
+ return thisManager.setDefaultCertificateForKeyPromise(certificate, useSync);
+ });
+};
+
+/**
+ * Add a certificate into the public key identity storage and set the
+ * certificate as the default of its corresponding key.
+ * @param {IdentityCertificate} certificate The certificate to be added. This
+ * makes a copy of the certificate.
+ * @param {function} onComplete (optional) This calls onComplete() when complete.
+ * (Some database libraries only use a callback, so onComplete is required to use
+ * these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ */
+IdentityManager.prototype.addCertificateAsDefault = function
+ (certificate, onComplete, onError)
+{
+ var useSync = !onComplete;
+ var thisManager = this;
+
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.addCertificatePromise(certificate, useSync)
+ .then(function() {
+ return thisManager.setDefaultCertificateForKeyPromise(certificate, useSync);
+ }));
+};
+
+/**
+ * Get a certificate which is still valid with the specified name.
+ * @param {Name} certificateName The name of the requested certificate.
+ * @param {function} onComplete (optional) This calls onComplete(certificate)
+ * with the requested IdentityCertificate. If omitted, the return value is
+ * described below. (Some database libraries only use a callback, so onComplete
+ * is required to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {IdentityCertificate} If onComplete is omitted, return the requested
+ * certificate. Otherwise, if onComplete is supplied then return undefined and
+ * use onComplete as described above.
+ */
+IdentityManager.prototype.getCertificate = function
+ (certificateName, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.getCertificatePromise
+ (certificateName, false, !onComplete));
+};
+
+/**
+ * Get the default certificate name for the specified identity.
+ * @param {Name} identityName The identity name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the default certificate
+ * Name, or a promise rejected with SecurityException if the default key name
+ * for the identity is not set or the default certificate name for the key name
+ * is not set.
+ */
+IdentityManager.prototype.getDefaultCertificateNameForIdentityPromise = function
+ (identityName, useSync)
+{
+ return this.identityStorage.getDefaultCertificateNameForIdentityPromise
+ (identityName, useSync);
+}
+
+/**
+ * Get the default certificate name for the specified identity, which will be
+ * used when signing is performed based on identity.
+ * @param {Name} identityName The name of the specified identity.
+ * @param {function} onComplete (optional) This calls onComplete(certificateName)
+ * with name of the default certificate. If omitted, the return value is described
+ * below. (Some database libraries only use a callback, so onComplete is required
+ * to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {Name} If onComplete is omitted, return the default certificate name.
+ * Otherwise, if onComplete is supplied then return undefined and use
+ * onComplete as described above.
+ * @throws SecurityException if the default key name for the identity is not
+ * set or the default certificate name for the key name is not set. However, if
+ * onComplete and onError are defined, then if there is an exception return
+ * undefined and call onError(exception).
+ */
+IdentityManager.prototype.getDefaultCertificateNameForIdentity = function
+ (identityName, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.getDefaultCertificateNameForIdentityPromise
+ (identityName, !onComplete));
+};
+
+/**
+ * Get the default certificate name of the default identity, which will be used
+ * when signing is based on identity and the identity is not specified.
+ * @param {function} onComplete (optional) This calls onComplete(certificateName)
+ * with name of the default certificate. If omitted, the return value is described
+ * below. (Some database libraries only use a callback, so onComplete is required
+ * to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {Name} If onComplete is omitted, return the default certificate name.
+ * Otherwise, if onComplete is supplied then return undefined and use
+ * onComplete as described above.
+ * @throws SecurityException if the default identity is not set or the default
+ * key name for the identity is not set or the default certificate name for
+ * the key name is not set. However, if onComplete and onError are defined, then
+ * if there is an exception return undefined and call onError(exception).
+ */
+IdentityManager.prototype.getDefaultCertificateName = function
+ (onComplete, onError)
+{
+ var useSync = !onComplete;
+ var thisManager = this;
+
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.getDefaultIdentityPromise(useSync)
+ .then(function(identityName) {
+ return thisManager.identityStorage.getDefaultCertificateNameForIdentityPromise
+ (identityName, useSync);
+ }));
+};
+
+/**
+ * Append all the identity names to the nameList.
+ * @param {Array<Name>} nameList Append result names to nameList.
+ * @param {boolean} isDefault If true, add only the default identity name. If
+ * false, add only the non-default identity names.
+ * @param {function} onComplete (optional) This calls onComplete() when finished
+ * adding to nameList. If omitted, this returns when complete. (Some database
+ * libraries only use a callback, so onComplete is required to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {void} If onComplete is omitted, return when complete. Otherwise, if
+ * onComplete is supplied then return undefined and use onComplete as described
+ * above.
+ */
+IdentityManager.prototype.getAllIdentities = function
+ (nameList, isDefault, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.getAllIdentitiesPromise
+ (nameList, isDefault, !onComplete));
+};
+
+/**
+ * Append all the key names of a particular identity to the nameList.
+ * @param {Name} identityName The identity name to search for.
+ * @param {Array<Name>} nameList Append result names to nameList.
+ * @param {boolean} isDefault If true, add only the default key name. If false,
+ * add only the non-default key names.
+ * @param {function} onComplete (optional) This calls onComplete() when finished
+ * adding to nameList. If omitted, this returns when complete. (Some database
+ * libraries only use a callback, so onComplete is required to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {void} If onComplete is omitted, return when complete. Otherwise, if
+ * onComplete is supplied then return undefined and use onComplete as described
+ * above.
+ */
+IdentityManager.prototype.getAllKeyNamesOfIdentity = function
+ (identityName, nameList, isDefault, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.getAllKeyNamesOfIdentityPromise
+ (identityName, nameList, isDefault, !onComplete));
+};
+
+/**
+ * Append all the certificate names of a particular key name to the nameList.
+ * @param {Name} keyName The key name to search for.
+ * @param {Array<Name>} nameList Append result names to nameList.
+ * @param {boolean} isDefault If true, add only the default certificate name. If
+ * false, add only the non-default certificate names.
+ * @param {function} onComplete (optional) This calls onComplete() when finished
+ * adding to nameList. If omitted, this returns when complete. (Some database
+ * libraries only use a callback, so onComplete is required to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {void} If onComplete is omitted, return when complete. Otherwise, if
+ * onComplete is supplied then return undefined and use onComplete as described
+ * above.
+ */
+IdentityManager.prototype.getAllCertificateNamesOfKey = function
+ (keyName, nameList, isDefault, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.identityStorage.getAllCertificateNamesOfKeyPromise
+ (keyName, nameList, isDefault, !onComplete));
+};
+
+/**
+ * Sign the Data packet or byte array data based on the certificate name.
+ * @param {Data|Buffer} target If this is a Data object, wire encode for signing,
+ * update its signature and key locator field and wireEncoding. If it is a
+ * Buffer, sign it to produce a Signature object.
+ * @param {Name} certificateName The Name identifying the certificate which
+ * identifies the signing key.
+ * @param {WireFormat} (optional) The WireFormat for calling encodeData, or
+ * WireFormat.getDefaultWireFormat() if omitted.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns the generated Signature
+ * object (if target is a Buffer) or the target (if target is Data).
+ */
+IdentityManager.prototype.signByCertificatePromise = function
+ (target, certificateName, wireFormat, useSync)
+{
+ useSync = (typeof wireFormat === "boolean") ? wireFormat : useSync;
+ wireFormat = (typeof wireFormat === "boolean" || !wireFormat) ? WireFormat.getDefaultWireFormat() : wireFormat;
+
+ var keyName = IdentityManager.certificateNameToPublicKeyName(certificateName);
+
+ var thisManager = this;
+ if (target instanceof Data) {
+ var data = target;
+ var digestAlgorithm = [0];
+
+ return this.makeSignatureByCertificatePromise
+ (certificateName, digestAlgorithm, useSync)
+ .then(function(signature) {
+ data.setSignature(signature);
+ // Encode once to get the signed portion.
+ var encoding = data.wireEncode(wireFormat);
+
+ return thisManager.privateKeyStorage.signPromise
+ (encoding.signedBuf(), keyName, digestAlgorithm[0], useSync);
+ })
+ .then(function(signatureValue) {
+ data.getSignature().setSignature(signatureValue);
+ // Encode again to include the signature.
+ data.wireEncode(wireFormat);
+
+ return SyncPromise.resolve(data);
+ });
+ }
+ else {
+ var digestAlgorithm = [0];
+ return this.makeSignatureByCertificatePromise
+ (certificateName, digestAlgorithm, useSync)
+ .then(function(signature) {
+ return thisManager.privateKeyStorage.signPromise
+ (target, keyName, digestAlgorithm[0], useSync);
+ })
+ .then(function (signatureValue) {
+ signature.setSignature(signatureValue);
+ return SyncPromise.resolve(signature);
+ });
+ }
+};
+
+/**
+ * Sign the Data packet or byte array data based on the certificate name.
+ * @param {Data|Buffer} target If this is a Data object, wire encode for signing,
+ * update its signature and key locator field and wireEncoding. If it is a
+ * Buffer, sign it to produce a Signature object.
+ * @param {Name} certificateName The Name identifying the certificate which
+ * identifies the signing key.
+ * @param {WireFormat} (optional) The WireFormat for calling encodeData, or
+ * WireFormat.getDefaultWireFormat() if omitted.
+ * @param {function} onComplete (optional) If target is a Data object, this calls
+ * onComplete(data) with the supplied Data object which has been modified to set
+ * its signature. If target is a Buffer, this calls onComplete(signature) where
+ * signature is the produced Signature object. If omitted, the return value is
+ * described below. (Some crypto libraries only use a callback, so onComplete is
+ * required to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some crypto libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {Signature} If onComplete is omitted, return the generated Signature
+ * object (if target is a Buffer) or the target (if target is Data). Otherwise,
+ * if onComplete is supplied then return undefined and use onComplete as described
+ * above.
+ */
+IdentityManager.prototype.signByCertificate = function
+ (target, certificateName, wireFormat, onComplete, onError)
+{
+ onError = (typeof wireFormat === "function") ? onComplete : onError;
+ onComplete = (typeof wireFormat === "function") ? wireFormat : onComplete;
+ wireFormat = (typeof wireFormat === "function" || !wireFormat) ? WireFormat.getDefaultWireFormat() : wireFormat;
+
+ return SyncPromise.complete(onComplete, onError,
+ this.signByCertificatePromise
+ (target, certificateName, wireFormat, !onComplete));
+};
+
+/**
+ * Append a SignatureInfo to the Interest name, sign the name components and
+ * append a final name component with the signature bits.
+ * @param {Interest} interest The Interest object to be signed. This appends
+ * name components of SignatureInfo and the signature bits.
+ * @param {Name} certificateName The certificate name of the key to use for
+ * signing.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the input. If omitted, use WireFormat getDefaultWireFormat().
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns the supplied Interest.
+ */
+IdentityManager.prototype.signInterestByCertificatePromise = function
+ (interest, certificateName, wireFormat, useSync)
+{
+ useSync = (typeof wireFormat === "boolean") ? wireFormat : useSync;
+ wireFormat = (typeof wireFormat === "boolean" || !wireFormat) ? WireFormat.getDefaultWireFormat() : wireFormat;
+
+ var thisManager = this;
+ var signature;
+ var digestAlgorithm = [0];
+ return this.makeSignatureByCertificatePromise
+ (certificateName, digestAlgorithm, useSync)
+ .then(function(localSignature) {
+ signature = localSignature;
+ // Append the encoded SignatureInfo.
+ interest.getName().append(wireFormat.encodeSignatureInfo(signature));
+
+ // Append an empty signature so that the "signedPortion" is correct.
+ interest.getName().append(new Name.Component());
+ // Encode once to get the signed portion.
+ var encoding = interest.wireEncode(wireFormat);
+ var keyName = IdentityManager.certificateNameToPublicKeyName
+ (certificateName);
+
+ return thisManager.privateKeyStorage.signPromise
+ (encoding.signedBuf(), keyName, digestAlgorithm[0], useSync);
+ })
+ .then(function(signatureValue) {
+ signature.setSignature(signatureValue);
+
+ // Remove the empty signature and append the real one.
+ interest.setName(interest.getName().getPrefix(-1).append
+ (wireFormat.encodeSignatureValue(signature)));
+ return SyncPromise.resolve(interest);
+ });
+};
+
+/**
+ * Append a SignatureInfo to the Interest name, sign the name components and
+ * append a final name component with the signature bits.
+ * @param {Interest} interest The Interest object to be signed. This appends
+ * name components of SignatureInfo and the signature bits.
+ * @param {Name} certificateName The certificate name of the key to use for
+ * signing.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the input. If omitted, use WireFormat getDefaultWireFormat().
+ * @param {function} onComplete (optional) This calls onComplete(interest) with
+ * the supplied Interest object which has been modified to set its signature. If
+ * omitted, then return when the interest has been signed. (Some crypto
+ * libraries only use a callback, so onComplete is required to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some crypto libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {Signature} If onComplete is omitted, return the interest. Otherwise,
+ * if onComplete is supplied then return undefined and use onComplete as
+ * described above.
+ */
+IdentityManager.prototype.signInterestByCertificate = function
+ (interest, certificateName, wireFormat, onComplete, onError)
+{
+ onError = (typeof wireFormat === "function") ? onComplete : onError;
+ onComplete = (typeof wireFormat === "function") ? wireFormat : onComplete;
+ wireFormat = (typeof wireFormat === "function" || !wireFormat) ? WireFormat.getDefaultWireFormat() : wireFormat;
+
+ return SyncPromise.complete(onComplete, onError,
+ this.signInterestByCertificatePromise
+ (interest, certificateName, wireFormat, !onComplete));
+};
+
+/**
+ * Wire encode the Data object, digest it and set its SignatureInfo to a
+ * DigestSha256.
+ * @param {Data} data The Data object to be signed. This updates its signature
+ * and wireEncoding.
+ * @param {WireFormat} (optional) The WireFormat for calling encodeData, or
+ * WireFormat.getDefaultWireFormat() if omitted.
+ */
+IdentityManager.prototype.signWithSha256 = function(data, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ data.setSignature(new DigestSha256Signature());
+ // Encode once to get the signed portion.
+ var encoding = data.wireEncode(wireFormat);
+
+ // Digest and set the signature.
+ var hash = Crypto.createHash('sha256');
+ hash.update(encoding.signedBuf());
+ data.getSignature().setSignature(new Blob(hash.digest(), false));
+
+ // Encode again to include the signature.
+ data.wireEncode(wireFormat);
+};
+
+/**
+ * Append a SignatureInfo for DigestSha256 to the Interest name, digest the
+ * name components and append a final name component with the signature bits
+ * (which is the digest).
+ * @param {Interest} interest The Interest object to be signed. This appends
+ * name components of SignatureInfo and the signature bits.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the input. If omitted, use WireFormat getDefaultWireFormat().
+ */
+IdentityManager.prototype.signInterestWithSha256 = function(interest, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ var signature = new DigestSha256Signature();
+
+ // Append the encoded SignatureInfo.
+ interest.getName().append(wireFormat.encodeSignatureInfo(signature));
+
+ // Append an empty signature so that the "signedPortion" is correct.
+ interest.getName().append(new Name.Component());
+ // Encode once to get the signed portion.
+ var encoding = interest.wireEncode(wireFormat);
+
+ // Digest and set the signature.
+ var hash = Crypto.createHash('sha256');
+ hash.update(encoding.signedBuf());
+ signature.setSignature(new Blob(hash.digest(), false));
+
+ // Remove the empty signature and append the real one.
+ interest.setName(interest.getName().getPrefix(-1).append
+ (wireFormat.encodeSignatureValue(signature)));
+};
+
+/**
+ * Generate a self-signed certificate for a public key.
+ * @param {Name} keyName The name of the public key.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If false, this may return a SyncPromise or an async
+ * Promise.
+ * @return {Promise|SyncPromise} A promise which returns the generated
+ * IdentityCertificate.
+ */
+IdentityManager.prototype.selfSignPromise = function(keyName, useSync)
+{
+ var certificate = new IdentityCertificate();
+
+ var thisManager = this;
+ return this.identityStorage.getKeyPromise(keyName, useSync)
+ .then(function(keyBlob) {
+ var publicKey = new PublicKey(keyBlob);
+
+ var notBefore = new Date().getTime();
+ var notAfter = notBefore + 2 * 365 * 24 * 3600 * 1000; // about 2 years
+
+ certificate.setNotBefore(notBefore);
+ certificate.setNotAfter(notAfter);
+
+ var certificateName = keyName.getPrefix(-1).append("KEY").append
+ (keyName.get(-1)).append("ID-CERT").appendVersion(certificate.getNotBefore());
+ certificate.setName(certificateName);
+
+ certificate.setPublicKeyInfo(publicKey);
+ certificate.addSubjectDescription(new CertificateSubjectDescription
+ ("2.5.4.41", keyName.toUri()));
+ certificate.encode();
+
+ return thisManager.signByCertificatePromise
+ (certificate, certificate.getName(), useSync);
+ })
+};
+
+/**
+ * Generate a self-signed certificate for a public key.
+ * @param {Name} keyName The name of the public key.
+ * @param {function} onComplete (optional) This calls onComplete(certificate)
+ * with the the generated IdentityCertificate. If omitted, the return value is
+ * described below. (Some crypto libraries only use a callback, so onComplete is
+ * required to use these.)
+ * @return {IdentityCertificate} If onComplete is omitted, return the
+ * generated certificate. Otherwise, if onComplete is supplied then return
+ * undefined and use onComplete as described above.
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some crypto libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ */
+IdentityManager.prototype.selfSign = function(keyName, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.selfSignPromise(keyName, !onComplete));
+};
+
+/**
+ * Get the public key name from the full certificate name.
+ *
+ * @param {Name} certificateName The full certificate name.
+ * @return {Name} The related public key name.
+ * TODO: Move this to IdentityCertificate
+ */
+IdentityManager.certificateNameToPublicKeyName = function(certificateName)
+{
+ var i = certificateName.size() - 1;
+ var idString = "ID-CERT";
+ while (i >= 0) {
+ if (certificateName.get(i).toEscapedString() == idString)
+ break;
+ --i;
+ }
+
+ var tmpName = certificateName.getSubName(0, i);
+ var keyString = "KEY";
+ i = 0;
+ while (i < tmpName.size()) {
+ if (tmpName.get(i).toEscapedString() == keyString)
+ break;
+ ++i;
+ }
+
+ return tmpName.getSubName(0, i).append(tmpName.getSubName
+ (i + 1, tmpName.size() - i - 1));
+};
+
+/**
+ * Return a new Signature object based on the signature algorithm of the public
+ * key with keyName (derived from certificateName).
+ * @param {Name} certificateName The certificate name.
+ * @param {Array} digestAlgorithm Set digestAlgorithm[0] to the signature
+ * algorithm's digest algorithm, e.g. DigestAlgorithm.SHA256.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If false, this may return a SyncPromise or an async
+ * Promise.
+ * @return {Promise|SyncPromise} A promise which returns a new object of the
+ * correct subclass of Signature.
+ */
+IdentityManager.prototype.makeSignatureByCertificatePromise = function
+ (certificateName, digestAlgorithm, useSync)
+{
+ var keyName = IdentityManager.certificateNameToPublicKeyName(certificateName);
+ return this.privateKeyStorage.getPublicKeyPromise(keyName, useSync)
+ .then(function(publicKey) {
+ var keyType = publicKey.getKeyType();
+
+ var signature = null;
+ if (keyType == KeyType.RSA) {
+ signature = new Sha256WithRsaSignature();
+ digestAlgorithm[0] = DigestAlgorithm.SHA256;
+
+ signature.getKeyLocator().setType(KeyLocatorType.KEYNAME);
+ signature.getKeyLocator().setKeyName(certificateName.getPrefix(-1));
+ }
+ else if (keyType == KeyType.ECDSA) {
+ signature = new Sha256WithEcdsaSignature();
+ digestAlgorithm[0] = DigestAlgorithm.SHA256;
+
+ signature.getKeyLocator().setType(KeyLocatorType.KEYNAME);
+ signature.getKeyLocator().setKeyName(certificateName.getPrefix(-1));
+ }
+ else
+ throw new SecurityException(new Error("Key type is not recognized"));
+
+ return SyncPromise.resolve(signature);
+ });
+};
+
+/**
+ * A private method to generate a pair of keys for the specified identity.
+ * @param {Name} identityName The name of the identity.
+ * @param {boolean} isKsk true for generating a Key-Signing-Key (KSK), false for
+ * a Data-Signing-Key (DSK).
+ * @param {KeyParams} params The parameters of the key.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If false, this may return a SyncPromise or an async
+ * Promise.
+ * @return {Promise|SyncPromise} A promise which returns the generated key name.
+ */
+IdentityManager.prototype.generateKeyPairPromise = function
+ (identityName, isKsk, params, useSync)
+{
+ var keyName;
+ var thisManager = this;
+ return this.identityStorage.getNewKeyNamePromise(identityName, isKsk, useSync)
+ .then(function(localKeyName) {
+ keyName = localKeyName;
+ return thisManager.privateKeyStorage.generateKeyPairPromise
+ (keyName, params, useSync);
+ })
+ .then(function() {
+ return thisManager.privateKeyStorage.getPublicKeyPromise
+ (keyName, useSync);
+ })
+ .then(function(publicKey) {
+ return thisManager.identityStorage.addKeyPromise
+ (keyName, params.getKeyType(), publicKey.getKeyDer());
+ })
+ .then(function() {
+ return SyncPromise.resolve(keyName);
+ });
+};
+
+/**
+ * Get the IdentityStorage from the pib value in the configuration file if
+ * supplied. Otherwise, get the default for this platform.
+ * @param {ConfigFile} config The configuration file to check.
+ * @param {function} initialCheckPromise This is passed to the
+ * BasicIdentityStorage constructor. See it for details.
+ * @return {IdentityStorage} A new IdentityStorage.
+ */
+IdentityManager.getDefaultIdentityStorage_ = function(config, initialCheckPromise)
+{
+ // Assume we are in Node.js.
+ var pibLocator = config.get("pib", "");
+
+ if (pibLocator !== "") {
+ // Don't support non-default locations for now.
+ if (pibLocator !== "pib-sqlite3")
+ throw new SecurityException(new Error
+ ("Invalid config file pib value: " + pibLocator));
+ }
+
+ return new BasicIdentityStorage(initialCheckPromise);
+};
+
+/**
+ * Get the PrivateKeyStorage from the tpm value in the configuration file if
+ * supplied. Otherwise, get the default for this platform.
+ * @param {ConfigFile} config The configuration file to check.
+ * @param {Array<string>} canonicalTpmLocator Set canonicalTpmLocator[0] to the
+ * canonical value including the colon, * e.g. "tpm-file:".
+ * @return A new PrivateKeyStorage.
+ */
+IdentityManager.getDefaultPrivateKeyStorage_ = function
+ (config, canonicalTpmLocator)
+{
+ var tpmLocator = config.get("tpm", "");
+
+ if (tpmLocator === "") {
+ // Assume we are in Node.js, so check the system.
+ if (process.platform === "darwin") {
+ canonicalTpmLocator[0] = "tpm-osxkeychain:";
+ throw new SecurityException(new Error
+ ("IdentityManager: OS X key chain storage is not yet implemented. You must supply a privateKeyStorage."));
+ }
+ else {
+ canonicalTpmLocator[0] = "tpm-file:";
+ return new FilePrivateKeyStorage();
+ }
+ }
+ else if (tpmLocator === "tpm-osxkeychain") {
+ canonicalTpmLocator[0] = "tpm-osxkeychain:";
+ throw new SecurityException(new Error
+ ("IdentityManager: tpm-osxkeychain is not yet implemented."));
+ }
+ else if (tpmLocator === "tpm-file") {
+ canonicalTpmLocator[0] = "tpm-file:";
+ return new FilePrivateKeyStorage();
+ }
+ else
+ throw new SecurityException(new Error
+ ("Invalid config file tpm value: " + tpmLocator));
+};
+
+/**
+ * Check that identityStorage.getTpmLocatorPromise() (if defined) matches the
+ * canonicalTpmLocator. This has to be an async Promise because it calls async
+ * getTpmLocatorPromise.
+ * @param canonicalTpmLocator The canonical locator from
+ * getDefaultPrivateKeyStorage().
+ * @return {Promise} A promise which resolves if canonicalTpmLocator is OK, or a
+ * promise rejected with SecurityException if the private key storage does not
+ * match.
+ */
+IdentityManager.prototype.checkTpmPromise_ = function(canonicalTpmLocator)
+{
+ return this.identityStorage.getTpmLocatorPromise()
+ .then(function(tpmLocator) {
+ // Just check. If a PIB reset is required, expect ndn-cxx/NFD to do it.
+ if (tpmLocator !== "" && tpmLocator !== canonicalTpmLocator)
+ return Promise.reject(new SecurityException(new Error
+ ("The TPM locator supplied does not match the TPM locator in the PIB: " +
+ tpmLocator + " != " + canonicalTpmLocator)));
+ else
+ return Promise.resolve();
+ }, function(err) {
+ // The TPM locator is not set in the PIB yet.
+ return Promise.resolve();
+ });
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * A ValidationRequest is used to return information from
+ * PolicyManager.checkVerificationPolicy.
+ *
+ * Create a new ValidationRequest with the given values.
+ * @param {Interest} interest An interest for fetching more data.
+ * @param {function} onVerified If the signature is verified, this calls
+ * onVerified(data).
+ * @param {function} onValidationFailed If the signature check fails, this calls
+ * onValidationFailed(data, reason).
+ * @param {number} retry The number of retrials when there is an interest timeout.
+ * @param {number} stepCount The number of verification steps that have been
+ * done, used to track the verification progress.
+ * @constructor
+ */
+var ValidationRequest = function ValidationRequest
+ (interest, onVerified, onValidationFailed, retry, stepCount)
+{
+ this.interest = interest;
+ this.onVerified = onVerified;
+ this.onValidationFailed = onValidationFailed;
+ this.retry = retry;
+ this.stepCount = stepCount;
+};
+
+exports.ValidationRequest = ValidationRequest;
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// Use capitalized Crypto to not clash with the browser's crypto.subtle.
+/** @ignore */
+var Crypto = require('../../crypto.js'); /** @ignore */
+var Blob = require('../../util/blob.js').Blob; /** @ignore */
+var DataUtils = require('../../encoding/data-utils.js').DataUtils; /** @ignore */
+var SecurityException = require('../security-exception.js').SecurityException; /** @ignore */
+var DigestSha256Signature = require('../../digest-sha256-signature.js').DigestSha256Signature; /** @ignore */
+var Sha256WithRsaSignature = require('../../sha256-with-rsa-signature.js').Sha256WithRsaSignature; /** @ignore */
+var Sha256WithEcdsaSignature = require('../../sha256-with-ecdsa-signature.js').Sha256WithEcdsaSignature; /** @ignore */
+var UseSubtleCrypto = require("../../use-subtle-crypto-node.js").UseSubtleCrypto;
+
+/**
+ * A PolicyManager is an abstract base class to represent the policy for
+ * verifying data packets. You must create an object of a subclass.
+ * @constructor
+ */
+var PolicyManager = function PolicyManager()
+{
+};
+
+exports.PolicyManager = PolicyManager;
+
+/**
+ * Check if the received data packet or signed interest can escape from
+ * verification and be trusted as valid.
+ * Your derived class should override.
+ *
+ * @param {Data|Interest} dataOrInterest The received data packet or interest.
+ * @return {boolean} True if the data or interest does not need to be verified
+ * to be trusted as valid, otherwise false.
+ */
+PolicyManager.prototype.skipVerifyAndTrust = function(dataOrInterest)
+{
+ throw new Error("PolicyManager.skipVerifyAndTrust is not implemented");
+};
+
+/**
+ * Check if this PolicyManager has a verification rule for the received data
+ * packet or signed interest.
+ * Your derived class should override.
+ *
+ * @param {Data|Interest} dataOrInterest The received data packet or interest.
+ * @return {boolean} True if the data or interest must be verified, otherwise
+ * false.
+ */
+PolicyManager.prototype.requireVerify = function(dataOrInterest)
+{
+ throw new Error("PolicyManager.requireVerify is not implemented");
+};
+
+/**
+ * Check whether the received data or interest packet complies with the
+ * verification policy, and get the indication of the next verification step.
+ * Your derived class should override.
+ *
+ * @param {Data|Interest} dataOrInterest The Data object or interest with the
+ * signature to check.
+ * @param {number} stepCount The number of verification steps that have been
+ * done, used to track the verification progress.
+ * @param {function} onVerified If the signature is verified, this calls
+ * onVerified(dataOrInterest).
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onValidationFailed If the signature check fails, this calls
+ * onValidationFailed(dataOrInterest, reason).
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {WireFormat} wireFormat
+ * @return {ValidationRequest} The indication of next verification step, or
+ * null if there is no further step.
+ */
+PolicyManager.prototype.checkVerificationPolicy = function
+ (dataOrInterest, stepCount, onVerified, onValidationFailed, wireFormat)
+{
+ throw new Error("PolicyManager.checkVerificationPolicy is not implemented");
+};
+
+/**
+ * Check if the signing certificate name and data name satisfy the signing
+ * policy.
+ * Your derived class should override.
+ *
+ * @param {Name} dataName The name of data to be signed.
+ * @param {Name} certificateName The name of signing certificate.
+ * @return {boolean} True if the signing certificate can be used to sign the
+ * data, otherwise false.
+ */
+PolicyManager.prototype.checkSigningPolicy = function(dataName, certificateName)
+{
+ throw new Error("PolicyManager.checkSigningPolicy is not implemented");
+};
+
+/**
+ * Infer the signing identity name according to the policy. If the signing
+ * identity cannot be inferred, return an empty name.
+ * Your derived class should override.
+ *
+ * @param {Name} dataName The name of data to be signed.
+ * @return {Name} The signing identity or an empty name if cannot infer.
+ */
+PolicyManager.prototype.inferSigningIdentity = function(dataName)
+{
+ throw new Error("PolicyManager.inferSigningIdentity is not implemented");
+};
+
+// The first time verify is called, it sets this to determine if a signature
+// buffer needs to be converted to a string for the crypto verifier.
+PolicyManager.verifyUsesString_ = null;
+PolicyManager.setVerifyUsesString_ = function()
+{
+ var hashResult = Crypto.createHash('sha256').digest();
+ // If the hash result is a string, we assume that this is a version of
+ // crypto where verify also uses a string signature.
+ PolicyManager.verifyUsesString_ = (typeof hashResult === 'string');
+};
+
+/**
+ * Check the type of signature and use the publicKeyDer to verify the
+ * signedBlob using the appropriate signature algorithm.
+ * @param {Signature} signature An object of a subclass of Signature, e.g.
+ * Sha256WithRsaSignature.
+ * @param {SignedBlob} signedBlob the SignedBlob with the signed portion to
+ * verify.
+ * @param {Blob} publicKeyDer The DER-encoded public key used to verify the
+ * signature.
+ * @param {function} onComplete This calls onComplete(true) if the signature
+ * verifies, otherwise onComplete(false).
+ * @throws SecurityException if the signature type is not recognized or if
+ * publicKeyDer can't be decoded.
+ */
+PolicyManager.verifySignature = function
+ (signature, signedBlob, publicKeyDer, onComplete)
+{
+ if (signature instanceof Sha256WithRsaSignature) {
+ if (publicKeyDer.isNull()) {
+ onComplete(false);
+ return;
+ }
+ PolicyManager.verifySha256WithRsaSignature
+ (signature.getSignature(), signedBlob, publicKeyDer, onComplete);
+ }
+ else if (signature instanceof Sha256WithEcdsaSignature) {
+ if (publicKeyDer.isNull()) {
+ onComplete(false);
+ return;
+ }
+ PolicyManager.verifySha256WithEcdsaSignature
+ (signature.getSignature(), signedBlob, publicKeyDer, onComplete);
+ }
+ else if (signature instanceof DigestSha256Signature)
+ PolicyManager.verifyDigestSha256Signature
+ (signature.getSignature(), signedBlob, onComplete);
+ else
+ // We don't expect this to happen.
+ throw new SecurityException(new Error
+ ("PolicyManager.verify: Signature type is unknown"));
+};
+
+/**
+ * Verify the RSA signature on the SignedBlob using the given public key.
+ * @param {Blob} signature The signature bits.
+ * @param {SignedBlob} signedBlob the SignedBlob with the signed portion to
+ * verify.
+ * @param {Blob} publicKeyDer The DER-encoded public key used to verify the
+ * signature.
+ * @param {function} onComplete This calls onComplete(true) if the signature
+ * verifies, otherwise onComplete(false).
+ */
+PolicyManager.verifySha256WithRsaSignature = function
+ (signature, signedBlob, publicKeyDer, onComplete)
+{
+ if (UseSubtleCrypto()){
+ var algo = {name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};
+
+ crypto.subtle.importKey("spki", publicKeyDer.buf().buffer, algo, true, ["verify"]).then(function(publicKey){
+ return crypto.subtle.verify(algo, publicKey, signature.buf(), signedBlob.signedBuf())
+ }).then(function(verified){
+ onComplete(verified);
+ });
+ } else {
+ if (PolicyManager.verifyUsesString_ === null)
+ PolicyManager.setVerifyUsesString_();
+
+ // The crypto verifier requires a PEM-encoded public key.
+ var keyBase64 = publicKeyDer.buf().toString('base64');
+ var keyPem = "-----BEGIN PUBLIC KEY-----\n";
+ for (var i = 0; i < keyBase64.length; i += 64)
+ keyPem += (keyBase64.substr(i, 64) + "\n");
+ keyPem += "-----END PUBLIC KEY-----";
+
+ var verifier = Crypto.createVerify('RSA-SHA256');
+ verifier.update(signedBlob.signedBuf());
+ var signatureBytes = PolicyManager.verifyUsesString_ ?
+ DataUtils.toString(signature.buf()) : signature.buf();
+ onComplete(verifier.verify(keyPem, signatureBytes));
+ }
+};
+
+/**
+ * Verify the ECDSA signature on the SignedBlob using the given public key.
+ * @param {Blob} signature The signature bits.
+ * @param {SignedBlob} signedBlob the SignedBlob with the signed portion to
+ * verify.
+ * @param {Blob} publicKeyDer The DER-encoded public key used to verify the
+ * signature.
+ * @param {function} onComplete This calls onComplete(true) if the signature
+ * verifies, otherwise onComplete(false).
+ */
+PolicyManager.verifySha256WithEcdsaSignature = function
+ (signature, signedBlob, publicKeyDer, onComplete)
+{
+ if (UseSubtleCrypto()) {
/*
+ var algo = {name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};
+
+ crypto.subtle.importKey("spki", publicKeyDer.buf().buffer, algo, true, ["verify"]).then(function(publicKey){
+ return crypto.subtle.verify(algo, publicKey, signature.buf(), signedBlob.signedBuf())
+ }).then(function(verified){
+ onComplete(verified);
+ });
+*/ onComplete(false);
+ } else {
+ if (PolicyManager.verifyUsesString_ === null)
+ PolicyManager.setVerifyUsesString_();
+
+ // The crypto verifier requires a PEM-encoded public key.
+ var keyBase64 = publicKeyDer.buf().toString("base64");
+ var keyPem = "-----BEGIN PUBLIC KEY-----\n";
+ for (var i = 0; i < keyBase64.length; i += 64)
+ keyPem += (keyBase64.substr(i, 64) + "\n");
+ keyPem += "-----END PUBLIC KEY-----";
+
+ // Just create a "sha256". The Crypto library will infer ECDSA from the key.
+ var verifier = Crypto.createVerify("sha256");
+ verifier.update(signedBlob.signedBuf());
+ var signatureBytes = PolicyManager.verifyUsesString_ ?
+ DataUtils.toString(signature.buf()) : signature.buf();
+ onComplete(verifier.verify(keyPem, signatureBytes));
+ }
+};
+
+/**
+ * Verify the DigestSha256 signature on the SignedBlob by verifying that the
+ * digest of SignedBlob equals the signature.
+ * @param {Blob} signature The signature bits.
+ * @param {SignedBlob} signedBlob the SignedBlob with the signed portion to
+ * verify.
+ * @param {function} onComplete This calls onComplete(true) if the signature
+ * verifies, otherwise onComplete(false).
+ */
+PolicyManager.verifyDigestSha256Signature = function
+ (signature, signedBlob, onComplete)
+{
+ // Set signedPortionDigest to the digest of the signed portion of the signedBlob.
+ var hash = Crypto.createHash('sha256');
+ hash.update(signedBlob.signedBuf());
+ var signedPortionDigest = new Blob(hash.digest(), false);
+
+ onComplete(signedPortionDigest.equals(signature));
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From PyNDN certificate_cache.py by Adeola Bannis.
+ * Originally from Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var IdentityCertificate = require('../certificate/identity-certificate.js').IdentityCertificate;
+
+/**
+ * A CertificateCache is used to save other users' certificate during
+ * verification.
+ * @constructor
+ */
+var CertificateCache = function CertificateCache()
+{
+ // The key is the certificate name URI. The value is the wire encoding Blob.
+ this.cache = {};
+};
+
+exports.CertificateCache = CertificateCache;
+
+/**
+ * Insert the certificate into the cache. Assumes the timestamp is not yet
+ * removed from the name.
+ * @param {IdentityCertificate} certificate The certificate to insert.
+ */
+CertificateCache.prototype.insertCertificate = function(certificate)
+{
+ var certName = certificate.getName().getPrefix(-1);
+ this.cache[certName.toUri()] = certificate.wireEncode();
+};
+
+/**
+ * Remove a certificate from the cache. This does nothing if it is not present.
+ * @param {Name} certificateName The name of the certificate to remove. This
+ * assumes there is no timestamp in the name.
+ */
+CertificateCache.prototype.deleteCertificate = function(certificateName)
+{
+ delete this.cache[certificateName.toUri()];
+};
+
+/**
+ * Fetch a certificate from the cache.
+ * @param {Name} certificateName The name of the certificate to remove. This
+ * assumes there is no timestamp in the name.
+ * @return {IdentityCertificate} A new copy of the IdentityCertificate, or null
+ * if not found.
+ */
+CertificateCache.prototype.getCertificate = function(certificateName)
+{
+ var certData = this.cache[certificateName.toUri()];
+ if (certData === undefined)
+ return null;
+
+ var cert = new IdentityCertificate();
+ cert.wireDecode(certData);
+ return cert;
+};
+
+/**
+ * Clear all certificates from the store.
+ */
+CertificateCache.prototype.reset = function()
+{
+ this.cache = {};
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From PyNDN config_policy_manager.py by Adeola Bannis.
+ * Originally from Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var fs = require('fs'); /** @ignore */
+var path = require('path'); /** @ignore */
+var Name = require('../../name.js').Name; /** @ignore */
+var Data = require('../../data.js').Data; /** @ignore */
+var Interest = require('../../interest.js').Interest; /** @ignore */
+var KeyLocator = require('../../key-locator.js').KeyLocator; /** @ignore */
+var KeyLocatorType = require('../../key-locator.js').KeyLocatorType; /** @ignore */
+var Blob = require('../../util/blob.js').Blob; /** @ignore */
+var IdentityCertificate = require('../certificate/identity-certificate.js').IdentityCertificate; /** @ignore */
+var BoostInfoParser = require('../../util/boost-info-parser.js').BoostInfoParser; /** @ignore */
+var NdnRegexMatcher = require('../../util/ndn-regex-matcher.js').NdnRegexMatcher; /** @ignore */
+var CertificateCache = require('./certificate-cache.js').CertificateCache; /** @ignore */
+var ValidationRequest = require('./validation-request.js').ValidationRequest; /** @ignore */
+var SecurityException = require('../security-exception.js').SecurityException; /** @ignore */
+var WireFormat = require('../../encoding/wire-format.js').WireFormat; /** @ignore */
+var PolicyManager = require('./policy-manager.js').PolicyManager; /** @ignore */
+var NdnCommon = require('../../util/ndn-common.js').NdnCommon;
+
+/**
+ * ConfigPolicyManager manages trust according to a configuration file in the
+ * Validator Configuration File Format
+ * (http://redmine.named-data.net/projects/ndn-cxx/wiki/CommandValidatorConf)
+ *
+ * Once a rule is matched, the ConfigPolicyManager looks in the
+ * CertificateCache for the IdentityCertificate matching the name in the KeyLocator
+ * and uses its public key to verify the data packet or signed interest. If the
+ * certificate can't be found, it is downloaded, verified and installed. A chain
+ * of certificates will be followed to a maximum depth.
+ * If the new certificate is accepted, it is used to complete the verification.
+ *
+ * The KeyLocators of data packets and signed interests MUST contain a name for
+ * verification to succeed.
+ *
+ * Create a new ConfigPolicyManager which will act on the rules specified in the
+ * configuration and download unknown certificates when necessary.
+ *
+ * @param {string} configFileName (optional) If not null or empty, the path to
+ * the configuration file containing verification rules. (This only works in
+ * Node.js since it reads files using the "fs" module.) Otherwise, you should
+ * separately call load().
+ * @param {CertificateCache} certificateCache (optional) A CertificateCache to
+ * hold known certificates. If this is null or omitted, then create an internal
+ * CertificateCache.
+ * @param {number} searchDepth (optional) The maximum number of links to follow
+ * when verifying a certificate chain. If omitted, use a default.
+ * @param {number} graceInterval (optional) The window of time difference
+ * (in milliseconds) allowed between the timestamp of the first interest signed with
+ * a new public key and the validation time. If omitted, use a default value.
+ * @param {number} keyTimestampTtl (optional) How long a public key's last-used
+ * timestamp is kept in the store (milliseconds). If omitted, use a default value.
+ * @param {number} maxTrackedKeys The maximum number of public key use
+ * timestamps to track. If omitted, use a default.
+ * @constructor
+ */
+var ConfigPolicyManager = function ConfigPolicyManager
+ (configFileName, certificateCache, searchDepth, graceInterval,
+ keyTimestampTtl, maxTrackedKeys)
+{
+ // Call the base constructor.
+ PolicyManager.call(this);
+
+ if (certificateCache == undefined)
+ certificateCache = null;
+ if (searchDepth == undefined)
+ searchDepth = 5;
+ if (graceInterval == undefined)
+ graceInterval = 3000;
+ if (keyTimestampTtl == undefined)
+ keyTimestampTtl = 3600000;
+ if (maxTrackedKeys == undefined)
+ maxTrackedKeys = 1000;
+
+ if (certificateCache == null)
+ this.certificateCache = new CertificateCache();
+ else
+ this.certificateCache = certificateCache;
+ this.maxDepth = searchDepth;
+ this.keyGraceInterval = graceInterval;
+ this.keyTimestampTtl = keyTimestampTtl;
+ this.maxTrackedKeys = maxTrackedKeys;
+
+ this.reset();
+
+ if (configFileName != null && configFileName != "")
+ this.load(configFileName);
+};
+
+ConfigPolicyManager.prototype = new PolicyManager();
+ConfigPolicyManager.prototype.name = "ConfigPolicyManager";
+
+exports.ConfigPolicyManager = ConfigPolicyManager;
+
+/**
+ * Reset the certificate cache and other fields to the constructor state.
+ */
+ConfigPolicyManager.prototype.reset = function()
+{
+ this.certificateCache.reset();
+
+ // Stores the fixed-signer certificate name associated with validation rules
+ // so we don't keep loading from files.
+ this.fixedCertificateCache = {};
+
+ // Stores the timestamps for each public key used in command interests to
+ // avoid replay attacks.
+ // Key is public key name, value is last timestamp.
+ this.keyTimestamps = {};
+
+ this.requiresVerification = true;
+
+ this.config = new BoostInfoParser();
+ this.refreshManager = new ConfigPolicyManager.TrustAnchorRefreshManager();
+};
+
+/**
+ * Call reset() and load the configuration rules from the file name or the input
+ * string. There are two forms:
+ * load(configFileName) reads configFileName from the file system. (This only
+ * works in Node.js since it reads files using the "fs" module.)
+ * load(input, inputName) reads from the input, in which case inputName is used
+ * only for log messages, etc.
+ * @param {string} configFileName The path to the file containing configuration
+ * rules.
+ * @param {string} input The contents of the configuration rules, with lines
+ * separated by "\n" or "\r\n".
+ * @param {string} inputName Use with input for log messages, etc.
+ */
+ConfigPolicyManager.prototype.load = function(configFileNameOrInput, inputName)
+{
+ this.reset();
+ this.config.read(configFileNameOrInput, inputName);
+ this.loadTrustAnchorCertificates();
+}
+
+/**
+ * Check if this PolicyManager has a verification rule for the received data.
+ * If the configuration file contains the trust anchor 'any', nothing is
+ * verified.
+ *
+ * @param {Data|Interest} dataOrInterest The received data packet or interest.
+ * @return {boolean} true if the data must be verified, otherwise false.
+ */
+ConfigPolicyManager.prototype.requireVerify = function(dataOrInterest)
+{
+ return this.requiresVerification;
+};
+
+/**
+ * Override to always indicate that the signing certificate name and data name
+ * satisfy the signing policy.
+ *
+ * @param {Name} dataName The name of data to be signed.
+ * @param {Name} certificateName The name of signing certificate.
+ * @return {boolean} True to indicate that the signing certificate can be used
+ * to sign the data.
+ */
+ConfigPolicyManager.prototype.checkSigningPolicy = function
+ (dataName, certificateName)
+{
+ return true;
+};
+
+/**
+ * Check if the received signed interest can escape from verification and be
+ * trusted as valid. If the configuration file contains the trust anchor
+ * 'any', nothing is verified.
+ *
+ * @param {Data|Interest} dataOrInterest The received data packet or interest.
+ * @return {boolean} true if the data or interest does not need to be verified
+ * to be trusted as valid, otherwise false.
+ */
+ConfigPolicyManager.prototype.skipVerifyAndTrust = function(dataOrInterest)
+{
+ return !this.requiresVerification;
+};
+
+/**
+ * Check whether the received data packet or interest complies with the
+ * verification policy, and get the indication of the next verification step.
+ *
+ * @param {Data|Interest} dataOrInterest The Data object or interest with the
+ * signature to check.
+ * @param {number} stepCount The number of verification steps that have been
+ * done, used to track the verification progress.
+ * @param {function} onVerified If the signature is verified, this calls
+ * onVerified(dataOrInterest).
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onValidationFailed If the signature check fails, this calls
+ * onValidationFailed(dataOrInterest, reason).
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {WireFormat} wireFormat
+ * @return {ValidationRequest} The indication of next verification step, or
+ * null if there is no further step.
+ */
+ConfigPolicyManager.prototype.checkVerificationPolicy = function
+ (dataOrInterest, stepCount, onVerified, onValidationFailed, wireFormat)
+{
+ if (stepCount > this.maxDepth) {
+ try {
+ onValidationFailed
+ (dataOrInterest, "The verification stepCount " + stepCount +
+ " exceeded the maxDepth " + this.maxDepth);
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return null;
+ }
+
+ var signature = ConfigPolicyManager.extractSignature(dataOrInterest, wireFormat);
+ // No signature -> fail.
+ if (signature == null) {
+ try {
+ onValidationFailed
+ (dataOrInterest, "Cannot extract the signature from " +
+ dataOrInterest.getName().toUri());
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return null;
+ }
+
+ if (!KeyLocator.canGetFromSignature(signature)) {
+ // We only support signature types with key locators.
+ try {
+ onValidationFailed
+ (dataOrInterest, "The signature type does not support a KeyLocator");
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return null;
+ }
+
+ var keyLocator = null;
+ try {
+ keyLocator = KeyLocator.getFromSignature(signature);
+ }
+ catch (ex) {
+ // No key locator -> fail.
+ try {
+ onValidationFailed
+ (dataOrInterest, "Error in KeyLocator.getFromSignature: " + ex);
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return null;
+ }
+
+ var signatureName = keyLocator.getKeyName();
+ // No key name in KeyLocator -> fail.
+ if (signatureName.size() == 0) {
+ try {
+ onValidationFailed
+ (dataOrInterest, "The signature KeyLocator doesn't have a key name");
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return null;
+ }
+
+ var objectName = dataOrInterest.getName();
+ var matchType = "data";
+
+ // For command interests, we need to ignore the last 4 components when
+ // matching the name.
+ if (dataOrInterest instanceof Interest) {
+ objectName = objectName.getPrefix(-4);
+ matchType = "interest";
+ }
+
+ // First see if we can find a rule to match this packet.
+ var matchedRule = this.findMatchingRule(objectName, matchType);
+
+ // No matching rule -> fail.
+ if (matchedRule == null) {
+ try {
+ onValidationFailed
+ (dataOrInterest, "No matching rule found for " + objectName.toUri());
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return null;
+ }
+
+ var failureReason = ["unknown"];
+ var signatureMatches = this.checkSignatureMatch
+ (signatureName, objectName, matchedRule, failureReason);
+ if (!signatureMatches) {
+ try {
+ onValidationFailed(dataOrInterest, failureReason[0]);
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return null;
+ }
+
+ // Before we look up keys, refresh any certificate directories.
+ this.refreshManager.refreshAnchors();
+
+ // Now finally check that the data or interest was signed correctly.
+ // If we don't actually have the certificate yet, create a
+ // ValidationRequest for it.
+ var foundCert = this.refreshManager.getCertificate(signatureName);
+ if (foundCert == null)
+ foundCert = this.certificateCache.getCertificate(signatureName);
+ var thisManager = this;
+ if (foundCert == null) {
+ var certificateInterest = new Interest(signatureName);
+ var onCertificateDownloadComplete = function(data) {
+ var certificate;
+ try {
+ certificate = new IdentityCertificate(data);
+ } catch (ex) {
+ try {
+ onValidationFailed
+ (dataOrInterest, "Cannot decode certificate " + data.getName().toUri());
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return null;
+ }
+ thisManager.certificateCache.insertCertificate(certificate);
+ thisManager.checkVerificationPolicy
+ (dataOrInterest, stepCount + 1, onVerified, onValidationFailed);
+ };
+
+ var nextStep = new ValidationRequest
+ (certificateInterest, onCertificateDownloadComplete, onValidationFailed,
+ 2, stepCount + 1);
+
+ return nextStep;
+ }
+
+ // For interests, we must check that the timestamp is fresh enough.
+ // We do this after (possibly) downloading the certificate to avoid
+ // filling the cache with bad keys.
+ if (dataOrInterest instanceof Interest) {
+ var keyName = foundCert.getPublicKeyName();
+ var timestamp = dataOrInterest.getName().get(-4).toNumber();
+
+ if (!this.interestTimestampIsFresh(keyName, timestamp, failureReason)) {
+ try {
+ onValidationFailed(dataOrInterest, failureReason[0]);
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return null;
+ }
+ }
+
+ // Certificate is known, so verify the signature.
+ this.verify(signature, dataOrInterest.wireEncode(), function (verified, reason) {
+ if (verified) {
+ try {
+ onVerified(dataOrInterest);
+ } catch (ex) {
+ console.log("Error in onVerified: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ if (dataOrInterest instanceof Interest)
+ thisManager.updateTimestampForKey(keyName, timestamp);
+ }
+ else {
+ try {
+ onValidationFailed(dataOrInterest, reason);
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ });
+};
+
+/**
+ * The configuration file allows 'trust anchor' certificates to be preloaded.
+ * The certificates may also be loaded from a directory, and if the 'refresh'
+ * option is set to an interval, the certificates are reloaded at the specified
+ * interval.
+ */
+ConfigPolicyManager.prototype.loadTrustAnchorCertificates = function()
+{
+ var anchors = this.config.getRoot().get("validator/trust-anchor");
+
+ for (var i = 0; i < anchors.length; ++i) {
+ var anchor = anchors[i];
+
+ var typeName = anchor.get("type")[0].getValue();
+ var isPath = false;
+ var certID;
+ if (typeName == 'file') {
+ certID = anchor.get("file-name")[0].getValue();
+ isPath = true;
+ }
+ else if (typeName == 'base64') {
+ certID = anchor.get("base64-string")[0].getValue();
+ isPath = false;
+ }
+ else if (typeName == "dir") {
+ var dirName = anchor.get("dir")[0].getValue();
+
+ var refreshPeriod = 0;
+ var refreshTrees = anchor.get("refresh");
+ if (refreshTrees.length >= 1) {
+ var refreshPeriodStr = refreshTrees[0].getValue();
+
+ var refreshMatch = refreshPeriodStr.match(/(\d+)([hms])/);
+ if (refreshMatch == null)
+ refreshPeriod = 0;
+ else {
+ refreshPeriod = parseInt(refreshMatch[1]);
+ if (refreshMatch[2] != 's') {
+ refreshPeriod *= 60;
+ if (refreshMatch[2] != 'm')
+ refreshPeriod *= 60;
+ }
+ }
+ }
+
+ // Convert refreshPeriod from seconds to milliseconds.
+ this.refreshManager.addDirectory(dirName, refreshPeriod * 1000);
+ continue;
+ }
+ else if (typeName == "any") {
+ // This disables all security!
+ this.requiresVerification = false;
+ break;
+ }
+
+ this.lookupCertificate(certID, isPath);
+ }
+};
+
+/**
+ * Once a rule is found to match data or a signed interest, the name in the
+ * KeyLocator must satisfy the condition in the 'checker' section of the rule,
+ * else the data or interest is rejected.
+ * @param {Name} signatureName The certificate name from the KeyLocator.
+ * @param {Name} objectName The name of the data packet or interest. In the case
+ * of signed interests, this excludes the timestamp, nonce and signature
+ * components.
+ * @param {BoostInfoTree} rule The rule from the configuration file that matches
+ * the data or interest.
+ * @param {Array<string>} failureReason If matching fails, set failureReason[0]
+ * to the failure reason.
+ * @return {boolean} True if matches.
+ */
+ConfigPolicyManager.prototype.checkSignatureMatch = function
+ (signatureName, objectName, rule, failureReason)
+{
+ var checker = rule.get("checker")[0];
+ var checkerType = checker.get("type")[0].getValue();
+ if (checkerType == "fixed-signer") {
+ var signerInfo = checker.get("signer")[0];
+ var signerType = signerInfo.get("type")[0].getValue();
+
+ var cert;
+ if (signerType == "file") {
+ cert = this.lookupCertificate
+ (signerInfo.get("file-name")[0].getValue(), true);
+ if (cert == null) {
+ failureReason[0] = "Can't find fixed-signer certificate file: " +
+ signerInfo.get("file-name")[0].getValue();
+ return false;
+ }
+ }
+ else if (signerType == "base64") {
+ cert = this.lookupCertificate
+ (signerInfo.get("base64-string")[0].getValue(), false);
+ if (cert == null) {
+ failureReason[0] = "Can't find fixed-signer certificate base64: " +
+ signerInfo.get("base64-string")[0].getValue();
+ return false;
+ }
+ }
+ else {
+ failureReason[0] = "Unrecognized fixed-signer signerType: " + signerType;
+ return false;
+ }
+
+ if (cert.getName().equals(signatureName))
+ return true;
+ else {
+ failureReason[0] = "fixed-signer cert name \"" + cert.getName().toUri() +
+ "\" does not equal signatureName \"" + signatureName.toUri() + "\"";
+ return false;
+ }
+ }
+ else if (checkerType == "hierarchical") {
+ // This just means the data/interest name has the signing identity as a prefix.
+ // That means everything before "ksk-?" in the key name.
+ var identityRegex = "^([^<KEY>]*)<KEY>(<>*)<ksk-.+><ID-CERT>";
+ var identityMatch = NdnRegexMatcher.match(identityRegex, signatureName);
+ if (identityMatch != null) {
+ var identityPrefix = new Name(identityMatch[1]).append
+ (new Name(identityMatch[2]));
+ if (ConfigPolicyManager.matchesRelation
+ (objectName, identityPrefix, "is-prefix-of"))
+ return true;
+ else {
+ failureReason[0] = "The hierarchical objectName \"" + objectName.toUri() +
+ "\" is not a prefix of \"" + identityPrefix.toUri() + "\"";
+ return false;
+ }
+ }
+ else {
+ failureReason[0] = "The hierarchical identityRegex \"" + identityRegex +
+ "\" does not match signatureName \"" + signatureName.toUri() + "\"";
+ return false;
+ }
+ }
+ else if (checkerType == "customized") {
+ var keyLocatorInfo = checker.get("key-locator")[0];
+ // Not checking type - only name is supported.
+
+ // Is this a simple relation?
+ var relationType = keyLocatorInfo.getFirstValue("relation");
+ if (relationType != null) {
+ var matchName = new Name(keyLocatorInfo.get("name")[0].getValue());
+ if (ConfigPolicyManager.matchesRelation
+ (signatureName, matchName, relationType))
+ return true;
+ else {
+ failureReason[0] = "The custom signatureName \"" + signatureName.toUri() +
+ "\" does not match matchName \"" + matchName.toUri() +
+ "\" using relation " + relationType;
+ return false;
+ }
+ }
+
+ // Is this a simple regex?
+ var keyRegex = keyLocatorInfo.getFirstValue("regex");
+ if (keyRegex != null) {
+ if (NdnRegexMatcher.match(keyRegex, signatureName) != null)
+ return true;
+ else {
+ failureReason[0] = "The custom signatureName \"" + signatureName.toUri() +
+ "\" does not regex match simpleKeyRegex \"" + keyRegex + "\"";
+ return false;
+ }
+ }
+
+ // Is this a hyper-relation?
+ var hyperRelationList = keyLocatorInfo.get("hyper-relation");
+ if (hyperRelationList.length >= 1) {
+ var hyperRelation = hyperRelationList[0];
+
+ var keyRegex = hyperRelation.getFirstValue("k-regex");
+ var keyExpansion = hyperRelation.getFirstValue("k-expand");
+ var nameRegex = hyperRelation.getFirstValue("p-regex");
+ var nameExpansion = hyperRelation.getFirstValue("p-expand");
+ var relationType = hyperRelation.getFirstValue("h-relation");
+ if (keyRegex != null && keyExpansion != null && nameRegex != null &&
+ nameExpansion != null && relationType != null) {
+ var keyMatch = NdnRegexMatcher.match(keyRegex, signatureName);
+ if (keyMatch == null || keyMatch[1] === undefined) {
+ failureReason[0] = "The custom hyper-relation signatureName \"" +
+ signatureName.toUri() + "\" does not match the keyRegex \"" +
+ keyRegex + "\"";
+ return false;
+ }
+ var keyMatchPrefix = ConfigPolicyManager.expand(keyMatch, keyExpansion);
+
+ var nameMatch = NdnRegexMatcher.match(nameRegex, objectName);
+ if (nameMatch == null || nameMatch[1] === undefined) {
+ failureReason[0] = "The custom hyper-relation objectName \"" +
+ objectName.toUri() + "\" does not match the nameRegex \"" +
+ nameRegex + "\"";
+ return false;
+ }
+ var nameMatchStr = ConfigPolicyManager.expand(nameMatch, nameExpansion);
+
+ if (ConfigPolicyManager.matchesRelation
+ (new Name(nameMatchStr), new Name(keyMatchPrefix), relationType))
+ return true;
+ else {
+ failureReason[0] = "The custom hyper-relation nameMatch \"" +
+ nameMatchStr + "\" does not match the keyMatchPrefix \"" +
+ keyMatchPrefix + "\" using relation " + relationType;
+ return false;
+ }
+ }
+ }
+ }
+
+ failureReason[0] = "Unrecognized checkerType: " + checkerType;
+ return false;
+};
+
+/**
+ * Similar to Python expand, return expansion where every \1, \2, etc. is
+ * replaced by match[1], match[2], etc. Note: Even though this is a general
+ * utility function, we define it locally because it is only tested to work in
+ * the cases used by this class.
+ * @param {Object} match The match object from String.match.
+ * @param {string} expansion The string with \1, \2, etc. to replace from match.
+ * @return {string} The expanded string.
+ */
+ConfigPolicyManager.expand = function(match, expansion)
+{
+ return expansion.replace
+ (/\\(\d)/g,
+ function(fullMatch, n) { return match[parseInt(n)];})
+};
+
+/**
+ * This looks up certificates specified as base64-encoded data or file names.
+ * These are cached by filename or encoding to avoid repeated reading of files
+ * or decoding.
+ * @param {string} certID
+ * @param {boolean} isPath
+ * @return {IdentityCertificate} The certificate object, or null if not found.
+ */
+ConfigPolicyManager.prototype.lookupCertificate = function(certID, isPath)
+{
+ var cert;
+
+ var cachedCertUri = this.fixedCertificateCache[certID];
+ if (cachedCertUri === undefined) {
+ if (isPath)
+ // load the certificate data (base64 encoded IdentityCertificate)
+ cert = ConfigPolicyManager.TrustAnchorRefreshManager.loadIdentityCertificateFromFile
+ (certID);
+ else {
+ var certData = new Buffer(certID, 'base64');
+ cert = new IdentityCertificate();
+ cert.wireDecode(certData);
+ }
+
+ var certUri = cert.getName().getPrefix(-1).toUri();
+ this.fixedCertificateCache[certID] = certUri;
+ this.certificateCache.insertCertificate(cert);
+ }
+ else
+ cert = this.certificateCache.getCertificate(new Name(cachedCertUri));
+
+ return cert;
+};
+
+/**
+ * Search the configuration file for the first rule that matches the data or
+ * signed interest name. In the case of interests, the name to match should
+ * exclude the timestamp, nonce, and signature components.
+ * @param {Name} objName The name to be matched.
+ * @param {string} matchType The rule type to match, "data" or "interest".
+ * @return {BoostInfoTree} The matching rule, or null if not found.
+ */
+ConfigPolicyManager.prototype.findMatchingRule = function(objName, matchType)
+{
+ var rules = this.config.getRoot().get("validator/rule");
+ for (var iRule = 0; iRule < rules.length; ++iRule) {
+ var r = rules[iRule];
+
+ if (r.get('for')[0].getValue() == matchType) {
+ var passed = true;
+ var filters = r.get('filter');
+ if (filters.length == 0)
+ // No filters means we pass!
+ return r;
+ else {
+ for (var iFilter = 0; iFilter < filters.length; ++iFilter) {
+ var f = filters[iFilter];
+
+ // Don't check the type - it can only be name for now.
+ // We need to see if this is a regex or a relation.
+ var regexPattern = f.getFirstValue("regex");
+ if (regexPattern === null) {
+ var matchRelation = f.get('relation')[0].getValue();
+ var matchUri = f.get('name')[0].getValue();
+ var matchName = new Name(matchUri);
+ passed = ConfigPolicyManager.matchesRelation(objName, matchName, matchRelation);
+ }
+ else
+ passed = (NdnRegexMatcher.match(regexPattern, objName) !== null);
+
+ if (!passed)
+ break;
+ }
+
+ if (passed)
+ return r;
+ }
+ }
+ }
+
+ return null;
+};
+
+/**
+ * Determines if a name satisfies the relation to matchName.
+ * @param {Name} name
+ * @param {Name} matchName
+ * @param {string} matchRelation Can be one of:
+ * 'is-prefix-of' - passes if the name is equal to or has the other
+ * name as a prefix
+ * 'is-strict-prefix-of' - passes if the name has the other name as a
+ * prefix, and is not equal
+ * 'equal' - passes if the two names are equal
+ * @return {boolean}
+ */
+ConfigPolicyManager.matchesRelation = function(name, matchName, matchRelation)
+{
+ var passed = false;
+ if (matchRelation == 'is-strict-prefix-of') {
+ if (matchName.size() == name.size())
+ passed = false;
+ else if (matchName.match(name))
+ passed = true;
+ }
+ else if (matchRelation == 'is-prefix-of') {
+ if (matchName.match(name))
+ passed = true;
+ }
+ else if (matchRelation == 'equal') {
+ if (matchName.equals(name))
+ passed = true;
+ }
+ return passed;
+};
+
+/**
+ * Extract the signature information from the interest name or from the data
+ * packet or interest.
+ * @param {Data|Interest} dataOrInterest The object whose signature is needed.
+ * @param {WireFormat} wireFormat (optional) The wire format used to decode
+ * signature information from the interest name.
+ * @return {Signature} The object of a sublcass of Signature or null if can't
+ * decode.
+ */
+ConfigPolicyManager.extractSignature = function(dataOrInterest, wireFormat)
+{
+ if (dataOrInterest instanceof Data)
+ return dataOrInterest.getSignature();
+ else if (dataOrInterest instanceof Interest) {
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ try {
+ var signature = wireFormat.decodeSignatureInfoAndValue
+ (dataOrInterest.getName().get(-2).getValue().buf(),
+ dataOrInterest.getName().get(-1).getValue().buf(), false);
+ }
+ catch (e) {
+ return null;
+ }
+
+ return signature;
+ }
+
+ return null;
+};
+
+/**
+ * Determine whether the timestamp from the interest is newer than the last use
+ * of this key, or within the grace interval on first use.
+ * @param {Name} keyName The name of the public key used to sign the interest.
+ * @param {number} timestamp The timestamp extracted from the interest name.
+ * @param {Array<string>} failureReason If matching fails, set failureReason[0]
+ * to the failure reason.
+ * @return {boolean} True if timestamp is fresh as described above.
+ */
+ConfigPolicyManager.prototype.interestTimestampIsFresh = function
+ (keyName, timestamp, failureReason)
+{
+ var lastTimestamp = this.keyTimestamps[keyName.toUri()];
+ if (lastTimestamp == undefined) {
+ var now = new Date().getTime();
+ var notBefore = now - this.keyGraceInterval;
+ var notAfter = now + this.keyGraceInterval;
+ if (!(timestamp > notBefore && timestamp < notAfter)) {
+ failureReason[0] =
+ "The command interest timestamp is not within the first use grace period of " +
+ this.keyGraceInterval + " milliseconds.";
+ return false;
+ }
+ else
+ return true;
+ }
+ else {
+ if (timestamp <= lastTimestamp) {
+ failureReason[0] =
+ "The command interest timestamp is not newer than the previous timestamp";
+ return false;
+ }
+ else
+ return true;
+ }
+};
+
+/**
+ * Trim the table size down if necessary, and insert/update the latest interest
+ * signing timestamp for the key. Any key which has not been used within the TTL
+ * period is purged. If the table is still too large, the oldest key is purged.
+ * @param {Name} keyName The name of the public key used to sign the interest.
+ * @param {number} timestamp The timestamp extracted from the interest name.
+ */
+ConfigPolicyManager.prototype.updateTimestampForKey = function
+ (keyName, timestamp)
+{
+ this.keyTimestamps[keyName.toUri()] = timestamp;
+
+ // JavaScript does have a direct way to get the number of entries, so first
+ // get the keysToErase while counting.
+ var keyTimestampsSize = 0;
+ var keysToErase = [];
+
+ var now = new Date().getTime();
+ var oldestTimestamp = now;
+ var oldestKey = null;
+ for (var keyUri in this.keyTimestamps) {
+ ++keyTimestampsSize;
+ var ts = this.keyTimestamps[keyUri];
+ if (now - ts > this.keyTimestampTtl)
+ keysToErase.push(keyUri);
+ else if (ts < oldestTimestamp) {
+ oldestTimestamp = ts;
+ oldestKey = keyUri;
+ }
+ }
+
+ if (keyTimestampsSize >= this.maxTrackedKeys) {
+ // Now delete the expired keys.
+ for (var i = 0; i < keysToErase.length; ++i) {
+ delete this.keyTimestamps[keysToErase[i]];
+ --keyTimestampsSize;
+ }
+
+ if (keyTimestampsSize > this.maxTrackedKeys)
+ // We have not removed enough.
+ delete this.keyTimestamps[oldestKey];
+ }
+};
+
+/**
+ * Check the type of signatureInfo to get the KeyLocator. Look in the
+ * IdentityStorage for the public key with the name in the KeyLocator and use it
+ * to verify the signedBlob. If the public key can't be found, return false.
+ * (This is a generalized method which can verify both a data packet and an
+ * interest.)
+ * @param {Signature} signatureInfo An object of a subclass of Signature, e.g.
+ * Sha256WithRsaSignature.
+ * @param {SignedBlob} signedBlob The SignedBlob with the signed portion to
+ * verify.
+ * @param {function} onComplete This calls onComplete(true, undefined) if the
+ * signature verifies, otherwise onComplete(false, reason).
+ */
+ConfigPolicyManager.prototype.verify = function
+ (signatureInfo, signedBlob, onComplete)
+{
+ // We have already checked once that there is a key locator.
+ var keyLocator = KeyLocator.getFromSignature(signatureInfo);
+
+ if (keyLocator.getType() == KeyLocatorType.KEYNAME) {
+ // Assume the key name is a certificate name.
+ var signatureName = keyLocator.getKeyName();
+ var certificate = this.refreshManager.getCertificate(signatureName);
+ if (certificate == null)
+ certificate = this.certificateCache.getCertificate(signatureName);
+ if (certificate == null) {
+ onComplete(false, "Cannot find a certificate with name " +
+ signatureName.toUri());
+ return;
+ }
+
+ var publicKeyDer = certificate.getPublicKeyInfo().getKeyDer();
+ if (publicKeyDer.isNull()) {
+ // Can't find the public key with the name.
+ onComplete(false, "There is no public key in the certificate with name " +
+ certificate.getName().toUri());
+ return;
+ }
+
+ PolicyManager.verifySignature
+ (signatureInfo, signedBlob, publicKeyDer, function(verified) {
+ if (verified)
+ onComplete(true);
+ else
+ onComplete
+ (false,
+ "The signature did not verify with the given public key");
+ });
+ }
+ else
+ onComplete(false, "The KeyLocator does not have a key name");
+};
+
+ConfigPolicyManager.TrustAnchorRefreshManager =
+ function ConfigPolicyManagerTrustAnchorRefreshManager()
+{
+ this.certificateCache = new CertificateCache();
+ // Maps the directory name to certificate names so they can be deleted when
+ // necessary. The key is the directory name string. The value is the object
+ // {certificateNames, // array of string
+ // nextRefresh, // number
+ // refreshPeriod // number
+ // }.
+ this.refreshDirectories = {};
+};
+
+ConfigPolicyManager.TrustAnchorRefreshManager.loadIdentityCertificateFromFile =
+ function(fileName)
+{
+ var encodedData = fs.readFileSync(fileName).toString();
+ var decodedData = new Buffer(encodedData, 'base64');
+ var cert = new IdentityCertificate();
+ cert.wireDecode(new Blob(decodedData, false));
+ return cert;
+};
+
+ConfigPolicyManager.TrustAnchorRefreshManager.prototype.getCertificate = function
+ (certificateName)
+{
+ // This assumes the timestamp is already removed.
+ return this.certificateCache.getCertificate(certificateName);
+};
+
+// refreshPeriod in milliseconds.
+ConfigPolicyManager.TrustAnchorRefreshManager.prototype.addDirectory = function
+ (directoryName, refreshPeriod)
+{
+ var allFiles;
+ try {
+ allFiles = fs.readdirSync(directoryName);
+ }
+ catch (e) {
+ throw new SecurityException(new Error
+ ("Cannot list files in directory " + directoryName));
+ }
+
+ var certificateNames = [];
+ for (var i = 0; i < allFiles.length; ++i) {
+ var cert;
+ try {
+ var fullPath = path.join(directoryName, allFiles[i]);
+ cert = ConfigPolicyManager.TrustAnchorRefreshManager.loadIdentityCertificateFromFile
+ (fullPath);
+ }
+ catch (e) {
+ // Allow files that are not certificates.
+ continue;
+ }
+
+ // Cut off the timestamp so it matches the KeyLocator Name format.
+ var certUri = cert.getName().getPrefix(-1).toUri();
+ this.certificateCache.insertCertificate(cert);
+ certificateNames.push(certUri);
+ }
+
+ this.refreshDirectories[directoryName] = {
+ certificates: certificateNames,
+ nextRefresh: new Date().getTime() + refreshPeriod,
+ refreshPeriod: refreshPeriod };
+};
+
+ConfigPolicyManager.TrustAnchorRefreshManager.prototype.refreshAnchors = function()
+{
+ var refreshTime = new Date().getTime();
+ for (var directory in this.refreshDirectories) {
+ var info = this.refreshDirectories[directory];
+ var nextRefreshTime = info.nextRefresh;
+ if (nextRefreshTime <= refreshTime) {
+ var certificateList = info.certificates.slice(0);
+ // Delete the certificates associated with this directory if possible
+ // then re-import.
+ // IdentityStorage subclasses may not support deletion.
+ for (var c in certificateList)
+ this.certificateCache.deleteCertificate(new Name(c));
+
+ this.addDirectory(directory, info.refreshPeriod);
+ }
+ }
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Name = require('../../name.js').Name; /** @ignore */
+var PolicyManager = require('./policy-manager.js').PolicyManager; /** @ignore */
+var NdnCommon = require('../../util/ndn-common.js').NdnCommon;
+
+/**
+ * @constructor
+ */
+var NoVerifyPolicyManager = function NoVerifyPolicyManager()
+{
+ // Call the base constructor.
+ PolicyManager.call(this);
+};
+
+NoVerifyPolicyManager.prototype = new PolicyManager();
+NoVerifyPolicyManager.prototype.name = "NoVerifyPolicyManager";
+
+exports.NoVerifyPolicyManager = NoVerifyPolicyManager;
+
+/**
+ * Override to always skip verification and trust as valid.
+ *
+ * @param {Data|Interest} dataOrInterest The received data packet or interest.
+ * @return {boolean} True.
+ */
+NoVerifyPolicyManager.prototype.skipVerifyAndTrust = function(dataOrInterest)
+{
+ return true;
+};
+
+/**
+ * Override to return false for no verification rule for the received data or
+ * signed interest.
+ *
+ * @param {Data|Interest} dataOrInterest The received data packet or interest.
+ * @return {boolean} False.
+ */
+NoVerifyPolicyManager.prototype.requireVerify = function(dataOrInterest)
+{
+ return false;
+};
+
+/**
+ * Override to call onVerified(data) and to indicate no further verification
+ * step.
+ *
+ * @param {Data|Interest} dataOrInterest The Data object or interest with the
+ * signature to check.
+ * @param {number} stepCount The number of verification steps that have been
+ * done, used to track the verification progress.
+ * @param {function} onVerified This does override to call
+ * onVerified(dataOrInterest).
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onValidationFailed Override to ignore this.
+ * @param {WireFormat} wireFormat
+ * @return {ValidationRequest} null for no further step for looking up a
+ * certificate chain.
+ */
+NoVerifyPolicyManager.prototype.checkVerificationPolicy = function
+ (dataOrInterest, stepCount, onVerified, onValidationFailed, wireFormat)
+{
+ try {
+ onVerified(dataOrInterest);
+ } catch (ex) {
+ console.log("Error in onVerified: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return null;
+};
+
+/**
+ * Override to always indicate that the signing certificate name and data name
+ * satisfy the signing policy.
+ *
+ * @param {Name} dataName The name of data to be signed.
+ * @param {Name} certificateName The name of signing certificate.
+ * @return {boolean} True to indicate that the signing certificate can be used
+ * to sign the data.
+ */
+NoVerifyPolicyManager.prototype.checkSigningPolicy = function
+ (dataName, certificateName)
+{
+ return true;
+};
+
+/**
+ * Override to indicate that the signing identity cannot be inferred.
+ *
+ * @param {Name} dataName The name of data to be signed.
+ * @return {Name} An empty name because cannot infer.
+ */
+NoVerifyPolicyManager.prototype.inferSigningIdentity = function(dataName)
+{
+ return new Name();
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Name = require('../../name.js').Name; /** @ignore */
+var Interest = require('../../interest.js').Interest; /** @ignore */
+var Data = require('../../data.js').Data; /** @ignore */
+var Blob = require('../../util/blob.js').Blob; /** @ignore */
+var IdentityCertificate = require('../certificate/identity-certificate.js').IdentityCertificate; /** @ignore */
+var KeyLocator = require('../../key-locator.js').KeyLocator; /** @ignore */
+var KeyLocatorType = require('../../key-locator.js').KeyLocatorType; /** @ignore */
+var SecurityException = require('../security-exception.js').SecurityException; /** @ignore */
+var WireFormat = require('../../encoding/wire-format.js').WireFormat; /** @ignore */
+var SyncPromise = require('../../util/sync-promise.js').SyncPromise; /** @ignore */
+var PolicyManager = require('./policy-manager.js').PolicyManager; /** @ignore */
+var NdnCommon = require('../../util/ndn-common.js').NdnCommon;
+
+/**
+ * A SelfVerifyPolicyManager implements a PolicyManager to look in the
+ * IdentityStorage for the public key with the name in the KeyLocator (if
+ * available) and use it to verify the data packet, without searching a
+ * certificate chain. If the public key can't be found, the verification fails.
+ *
+ * @param {IdentityStorage} identityStorage (optional) The IdentityStorage for
+ * looking up the public key. This object must remain valid during the life of
+ * this SelfVerifyPolicyManager. If omitted, then don't look for a public key
+ * with the name in the KeyLocator and rely on the KeyLocator having the full
+ * public key DER.
+ * @constructor
+ */
+var SelfVerifyPolicyManager = function SelfVerifyPolicyManager(identityStorage)
+{
+ // Call the base constructor.
+ PolicyManager.call(this);
+
+ this.identityStorage = identityStorage;
+};
+
+SelfVerifyPolicyManager.prototype = new PolicyManager();
+SelfVerifyPolicyManager.prototype.name = "SelfVerifyPolicyManager";
+
+exports.SelfVerifyPolicyManager = SelfVerifyPolicyManager;
+
+/**
+ * Never skip verification.
+ *
+ * @param {Data|Interest} dataOrInterest The received data packet or interest.
+ * @return {boolean} False.
+ */
+SelfVerifyPolicyManager.prototype.skipVerifyAndTrust = function(dataOrInterest)
+{
+ return false;
+};
+
+/**
+ * Always return true to use the self-verification rule for the received data.
+ *
+ * @param {Data|Interest} dataOrInterest The received data packet or interest.
+ * @return {boolean} True.
+ */
+SelfVerifyPolicyManager.prototype.requireVerify = function(dataOrInterest)
+{
+ return true;
+};
+
+/**
+ * Look in the IdentityStorage for the public key with the name in the
+ * KeyLocator (if available) and use it to verify the data packet. If the
+ * public key can't be found, call onValidationFailed.
+ *
+ * @param {Data|Interest} dataOrInterest The Data object or interest with the
+ * signature to check.
+ * @param {number} stepCount The number of verification steps that have been
+ * done, used to track the verification progress.
+ * @param {function} onVerified If the signature is verified, this calls
+ * onVerified(dataOrInterest).
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onValidationFailed If the signature check fails, this calls
+ * onValidationFailed(dataOrInterest, reason).
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {WireFormat} wireFormat
+ * @return {ValidationRequest} null for no further step for looking up a
+ * certificate chain.
+ */
+SelfVerifyPolicyManager.prototype.checkVerificationPolicy = function
+ (dataOrInterest, stepCount, onVerified, onValidationFailed, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ if (dataOrInterest instanceof Data) {
+ var data = dataOrInterest;
+ // wireEncode returns the cached encoding if available.
+ this.verify(data.getSignature(), data.wireEncode(), function(verified, reason) {
+ if (verified) {
+ try {
+ onVerified(data);
+ } catch (ex) {
+ console.log("Error in onVerified: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ else {
+ try {
+ onValidationFailed(data, reason);
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ });
+ }
+ else if (dataOrInterest instanceof Interest) {
+ var interest = dataOrInterest;
+
+ if (interest.getName().size() < 2) {
+ try {
+ onValidationFailed
+ (interest, "The signed interest has less than 2 components: " +
+ interest.getName().toUri());
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return;
+ }
+
+ // Decode the last two name components of the signed interest
+ var signature;
+ try {
+ signature = wireFormat.decodeSignatureInfoAndValue
+ (interest.getName().get(-2).getValue().buf(),
+ interest.getName().get(-1).getValue().buf(), false);
+ } catch (ex) {
+ try {
+ onValidationFailed
+ (interest, "Error decoding the signed interest signature: " + ex);
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return;
+ }
+
+ // wireEncode returns the cached encoding if available.
+ this.verify(signature, interest.wireEncode(), function(verified, reason) {
+ if (verified) {
+ try {
+ onVerified(interest);
+ } catch (ex) {
+ console.log("Error in onVerified: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ else {
+ try {
+ onValidationFailed(interest, reason);
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ });
+ }
+ else
+ throw new SecurityException(new Error
+ ("checkVerificationPolicy: unrecognized type for dataOrInterest"));
+
+ // No more steps, so return null.
+ return null;
+};
+
+/**
+ * Override to always indicate that the signing certificate name and data name
+ * satisfy the signing policy.
+ *
+ * @param {Name} dataName The name of data to be signed.
+ * @param {Name} certificateName The name of signing certificate.
+ * @return {boolean} True to indicate that the signing certificate can be used
+ * to sign the data.
+ */
+SelfVerifyPolicyManager.prototype.checkSigningPolicy = function
+ (dataName, certificateName)
+{
+ return true;
+};
+
+/**
+ * Override to indicate that the signing identity cannot be inferred.
+ *
+ * @param {Name} dataName The name of data to be signed.
+ * @return {Name} An empty name because cannot infer.
+ */
+SelfVerifyPolicyManager.prototype.inferSigningIdentity = function(dataName)
+{
+ return new Name();
+};
+
+/**
+ * Check the type of signatureInfo to get the KeyLocator. Look in the
+ * IdentityStorage for the public key with the name in the KeyLocator (if
+ * available) and use it to verify the signedBlob. If the public key can't be
+ * found, return false. (This is a generalized method which can verify both a
+ * Data packet and an interest.)
+ * @param {Signature} signatureInfo An object of a subclass of Signature, e.g.
+ * Sha256WithRsaSignature.
+ * @param {SignedBlob} signedBlob the SignedBlob with the signed portion to
+ * verify.
+ * @param {function} onComplete This calls onComplete(true, undefined) if the
+ * signature verifies, otherwise onComplete(false, reason).
+ */
+SelfVerifyPolicyManager.prototype.verify = function
+ (signatureInfo, signedBlob, onComplete)
+{
+ if (KeyLocator.canGetFromSignature(signatureInfo)) {
+ this.getPublicKeyDer
+ (KeyLocator.getFromSignature(signatureInfo), function(publicKeyDer, reason) {
+ if (publicKeyDer.isNull())
+ onComplete(false, reason);
+ else {
+ try {
+ PolicyManager.verifySignature
+ (signatureInfo, signedBlob, publicKeyDer, function(verified) {
+ if (verified)
+ onComplete(true);
+ else
+ onComplete
+ (false,
+ "The signature did not verify with the given public key");
+ });
+ } catch (ex) {
+ onComplete(false, "Error in verifySignature: " + ex);
+ }
+ }
+ });
+ }
+ else {
+ try {
+ // Assume that the signature type does not require a public key.
+ PolicyManager.verifySignature
+ (signatureInfo, signedBlob, null, function(verified) {
+ if (verified)
+ onComplete(true);
+ else
+ onComplete
+ (false, "The signature did not verify with the given public key");
+ });
+ } catch (ex) {
+ onComplete(false, "Error in verifySignature: " + ex);
+ }
+ }
+};
+
+/**
+ * Look in the IdentityStorage for the public key with the name in the
+ * KeyLocator (if available). If the public key can't be found, return and empty
+ * Blob.
+ * @param {KeyLocator} keyLocator The KeyLocator.
+ * @param {function} onComplete This calls
+ * onComplete(publicKeyDer, reason) where publicKeyDer is the public key
+ * DER Blob or an isNull Blob if not found and reason is the reason
+ * string if not found.
+ */
+SelfVerifyPolicyManager.prototype.getPublicKeyDer = function
+ (keyLocator, onComplete)
+{
+ if (keyLocator.getType() == KeyLocatorType.KEYNAME &&
+ this.identityStorage != null) {
+ var keyName;
+ try {
+ // Assume the key name is a certificate name.
+ keyName = IdentityCertificate.certificateNameToPublicKeyName
+ (keyLocator.getKeyName());
+ } catch (ex) {
+ onComplete
+ (new Blob(), "Cannot get a public key name from the certificate named: " +
+ keyLocator.getKeyName().toUri());
+ return;
+ }
+ SyncPromise.complete
+ (onComplete,
+ function(err) {
+ // The storage doesn't have the key.
+ onComplete
+ (new Blob(), "The identityStorage doesn't have the key named " +
+ keyName.toUri());
+ },
+ this.identityStorage.getKeyPromise(keyName));
+ }
+ else
+ // Can't find a key to verify.
+ onComplete(new Blob(), "The signature KeyLocator doesn't have a key name");
+};
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Crypto = require('../crypto.js'); /** @ignore */
+var Name = require('../name.js').Name; /** @ignore */
+var Interest = require('../interest.js').Interest; /** @ignore */
+var Data = require('../data.js').Data; /** @ignore */
+var Blob = require('../util/blob.js').Blob; /** @ignore */
+var WireFormat = require('../encoding/wire-format.js').WireFormat; /** @ignore */
+var SecurityException = require('./security-exception.js').SecurityException; /** @ignore */
+var RsaKeyParams = require('./key-params.js').RsaKeyParams; /** @ignore */
+var IdentityCertificate = require('./certificate/identity-certificate.js').IdentityCertificate; /** @ignore */
+var SyncPromise = require('../util/sync-promise.js').SyncPromise; /** @ignore */
+var NdnCommon = require('../util/ndn-common.js').NdnCommon; /** @ignore */
+var IdentityManager = require('./identity/identity-manager.js').IdentityManager; /** @ignore */
+var NoVerifyPolicyManager = require('./policy/no-verify-policy-manager.js').NoVerifyPolicyManager;
+
+/**
+ * A KeyChain provides a set of interfaces to the security library such as
+ * identity management, policy configuration and packet signing and verification.
+ * Note: This class is an experimental feature. See the API docs for more detail at
+ * http://named-data.net/doc/ndn-ccl-api/key-chain.html .
+ *
+ * Create a new KeyChain with the identityManager and policyManager.
+ * @param {IdentityManager} identityManager (optional) The identity manager as a
+ * subclass of IdentityManager. If omitted, use the default IdentityManager
+ * constructor.
+ * @param {PolicyManager} policyManager (optional) The policy manager as a
+ * subclass of PolicyManager. If omitted, use NoVerifyPolicyManager.
+ * @throws SecurityException if this is not in Node.js and this uses the default
+ * IdentityManager constructor. (See IdentityManager for details.)
+ * @constructor
+ */
+var KeyChain = function KeyChain(identityManager, policyManager)
+{
+ if (!identityManager)
+ identityManager = new IdentityManager();
+ if (!policyManager)
+ policyManager = new NoVerifyPolicyManager();
+
+ this.identityManager = identityManager;
+ this.policyManager = policyManager;
+ this.face = null;
+};
+
+exports.KeyChain = KeyChain;
+
+/*****************************************
+ * Identity Management *
+ *****************************************/
+
+/**
+ * Create an identity by creating a pair of Key-Signing-Key (KSK) for this
+ * identity and a self-signed certificate of the KSK. If a key pair or
+ * certificate for the identity already exists, use it.
+ * @param {Name} identityName The name of the identity.
+ * @param {KeyParams} params (optional) The key parameters if a key needs to be
+ * generated for the identity. If omitted, use KeyChain.DEFAULT_KEY_PARAMS.
+ * @param {function} onComplete (optional) This calls onComplete(certificateName)
+ * with name of the default certificate of the identity. If omitted, the return
+ * value is described below. (Some crypto libraries only use a callback, so
+ * onComplete is required to use these.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @return {Name} If onComplete is omitted, return the name of the default
+ * certificate of the identity. Otherwise, if onComplete is supplied then return
+ * undefined and use onComplete as described above.
+ */
+KeyChain.prototype.createIdentityAndCertificate = function
+ (identityName, params, onComplete, onError)
+{
+ onError = (typeof params === "function") ? onComplete : onError;
+ onComplete = (typeof params === "function") ? params : onComplete;
+ params = (typeof params === "function" || !params) ?
+ KeyChain.DEFAULT_KEY_PARAMS : params;
+
+ return this.identityManager.createIdentityAndCertificate
+ (identityName, params, onComplete, onError);
+};
+
+/**
+ * Create an identity by creating a pair of Key-Signing-Key (KSK) for this
+ * identity and a self-signed certificate of the KSK. If a key pair or
+ * certificate for the identity already exists, use it.
+ * @deprecated Use createIdentityAndCertificate which returns the
+ * certificate name instead of the key name. You can use
+ * IdentityCertificate.certificateNameToPublicKeyName to convert the
+ * certificate name to the key name.
+ * @param {Name} identityName The name of the identity.
+ * @param {KeyParams} params (optional) The key parameters if a key needs to be
+ * generated for the identity. If omitted, use KeyChain.DEFAULT_KEY_PARAMS.
+ * @return {Name} The key name of the auto-generated KSK of the identity.
+ */
+KeyChain.prototype.createIdentity = function(identityName, params)
+{
+ return IdentityCertificate.certificateNameToPublicKeyName
+ (this.createIdentityAndCertificate(identityName, params));
+};
+
+/**
+ * Delete the identity from the public and private key storage. If the
+ * identity to be deleted is the current default system default, this will not
+ * delete the identity and will return immediately.
+ * @param {Name} identityName The name of the identity.
+ * @param {function} onComplete (optional) This calls onComplete() when the
+ * operation is complete. If omitted, do not use it. (Some database libraries
+ * only use a callback, so onComplete is required to use these.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ */
+KeyChain.prototype.deleteIdentity = function
+ (identityName, onComplete, onError)
+{
+ this.identityManager.deleteIdentity(identityName, onComplete, onError);
+};
+
+/**
+ * Get the default identity.
+ * @param {function} onComplete (optional) This calls onComplete(identityName)
+ * with name of the default identity. If omitted, the return value is described
+ * below. (Some crypto libraries only use a callback, so onComplete is required
+ * to use these.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * @return {Name} If onComplete is omitted, return the name of the default
+ * identity. Otherwise, if onComplete is supplied then return undefined and use
+ * onComplete as described above.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @throws SecurityException if the default identity is not set. However, if
+ * onComplete and onError are defined, then if there is an exception return
+ * undefined and call onError(exception).
+ */
+KeyChain.prototype.getDefaultIdentity = function(onComplete, onError)
+{
+ return this.identityManager.getDefaultIdentity(onComplete, onError);
+};
+
+/**
+ * Get the default certificate name of the default identity, which will be used
+ * when signing is based on identity and the identity is not specified.
+ * @param {function} onComplete (optional) This calls onComplete(certificateName)
+ * with name of the default certificate. If omitted, the return value is described
+ * below. (Some crypto libraries only use a callback, so onComplete is required
+ * to use these.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @return {Name} If onComplete is omitted, return the default certificate name.
+ * Otherwise, if onComplete is supplied then return undefined and use onComplete
+ * as described above.
+ * @throws SecurityException if the default identity is not set or the default
+ * key name for the identity is not set or the default certificate name for
+ * the key name is not set. However, if onComplete and onError are defined, then
+ * if there is an exception return undefined and call onError(exception).
+ */
+KeyChain.prototype.getDefaultCertificateName = function(onComplete, onError)
+{
+ return this.identityManager.getDefaultCertificateName(onComplete, onError);
+};
+
+/**
+ * Generate a pair of RSA keys for the specified identity.
+ * @param {Name} identityName The name of the identity.
+ * @param {boolean} isKsk (optional) true for generating a Key-Signing-Key (KSK),
+ * false for a Data-Signing-Key (DSK). If omitted, generate a Data-Signing-Key.
+ * @param {number} keySize (optional) The size of the key. If omitted, use a
+ * default secure key size.
+ * @return {Name} The generated key name.
+ */
+KeyChain.prototype.generateRSAKeyPair = function(identityName, isKsk, keySize)
+{
+ keySize = (typeof isKsk === "boolean") ? isKsk : keySize;
+ isKsk = (typeof isKsk === "boolean") ? isKsk : false;
+
+ if (!keySize)
+ keySize = 2048;
+
+ return this.identityManager.generateRSAKeyPair(identityName, isKsk, keySize);
+};
+
+/**
+ * Set a key as the default key of an identity. The identity name is inferred
+ * from keyName.
+ * @param {Name} keyName The name of the key.
+ * @param {Name} identityNameCheck (optional) The identity name to check that the
+ * keyName contains the same identity name. If an empty name, it is ignored.
+ * @param {function} onComplete (optional) This calls onComplete() when complete.
+ * (Some database libraries only use a callback, so onComplete is required to
+ * use these.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ */
+KeyChain.prototype.setDefaultKeyForIdentity = function
+ (keyName, identityNameCheck, onComplete, onError)
+{
+ return this.identityManager.setDefaultKeyForIdentity
+ (keyName, identityNameCheck, onComplete, onError);
+};
+
+/**
+ * Generate a pair of RSA keys for the specified identity and set it as default
+ * key for the identity.
+ * @param {Name} identityName The name of the identity.
+ * @param {boolean} isKsk (optional) true for generating a Key-Signing-Key (KSK),
+ * false for a Data-Signing-Key (DSK). If omitted, generate a Data-Signing-Key.
+ * @param {number} keySize (optional) The size of the key. If omitted, use a
+ * default secure key size.
+ * @return {Name} The generated key name.
+ */
+KeyChain.prototype.generateRSAKeyPairAsDefault = function
+ (identityName, isKsk, keySize)
+{
+ return this.identityManager.generateRSAKeyPairAsDefault
+ (identityName, isKsk, keySize);
+};
+
+/**
+ * Create a public key signing request.
+ * @param {Name} keyName The name of the key.
+ * @return {Blob} The signing request data.
+ */
+KeyChain.prototype.createSigningRequest = function(keyName)
+{
+ return this.identityManager.getPublicKey(keyName).getKeyDer();
+};
+
+/**
+ * Install an identity certificate into the public key identity storage.
+ * @param {IdentityCertificate} certificate The certificate to to added.
+ * @param {function} onComplete (optional) This calls onComplete() when complete.
+ * (Some database libraries only use a callback, so onComplete is required to
+ * use these.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ */
+KeyChain.prototype.installIdentityCertificate = function
+ (certificate, onComplete, onError)
+{
+ this.identityManager.addCertificate(certificate, onComplete, onError);
+};
+
+/**
+ * Set the certificate as the default for its corresponding key.
+ * @param {IdentityCertificate} certificate The certificate.
+ * @param {function} onComplete (optional) This calls onComplete() when complete.
+ * (Some database libraries only use a callback, so onComplete is required to
+ * use these.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ */
+KeyChain.prototype.setDefaultCertificateForKey = function
+ (certificate, onComplete, onError)
+{
+ this.identityManager.setDefaultCertificateForKey
+ (certificate, onComplete, onError);
+};
+
+/**
+ * Get a certificate which is still valid with the specified name.
+ * @param {Name} certificateName The name of the requested certificate.
+ * @param {function} onComplete (optional) This calls onComplete(certificate)
+ * with the requested IdentityCertificate. If omitted, the return value is
+ * described below. (Some crypto libraries only use a callback, so onComplete is
+ * required to use these.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @return {IdentityCertificate} If onComplete is omitted, return the requested
+ * certificate. Otherwise, if onComplete is supplied then return undefined and
+ * use onComplete as described above.
+ */
+KeyChain.prototype.getCertificate = function
+ (certificateName, onComplete, onError)
+{
+ return this.identityManager.getCertificate
+ (certificateName, onComplete, onError);
+};
+
+/**
+ * @deprecated Use getCertificate.
+ */
+KeyChain.prototype.getIdentityCertificate = function
+ (certificateName, onComplete, onError)
+{
+ return this.identityManager.getCertificate
+ (certificateName, onComplete, onError);
+};
+
+/**
+ * Revoke a key.
+ * @param {Name} keyName The name of the key that will be revoked.
+ */
+KeyChain.prototype.revokeKey = function(keyName)
+{
+ //TODO: Implement
+};
+
+/**
+ * Revoke a certificate.
+ * @param {Name} certificateName The name of the certificate that will be
+ * revoked.
+ */
+KeyChain.prototype.revokeCertificate = function(certificateName)
+{
+ //TODO: Implement
+};
+
+/**
+ * Get the identity manager given to or created by the constructor.
+ * @return {IdentityManager} The identity manager.
+ */
+KeyChain.prototype.getIdentityManager = function()
+{
+ return this.identityManager;
+};
+
+/*****************************************
+ * Policy Management *
+ *****************************************/
+
+/**
+ * Get the policy manager given to or created by the constructor.
+ * @return {PolicyManager} The policy manager.
+ */
+KeyChain.prototype.getPolicyManager = function()
+{
+ return this.policyManager;
+};
+
+/*****************************************
+ * Sign/Verify *
+ *****************************************/
+
+/**
+ * Sign the target. If it is a Data or Interest object, set its signature. If it
+ * is an array, produce a Signature object. There are two forms of sign:
+ * sign(target, certificateName [, wireFormat] [, onComplete] [, onError]).
+ * sign(target [, wireFormat] [, onComplete] [, onError]).
+ * @param {Data|Interest|Buffer} target If this is a Data object, wire encode for
+ * signing, update its signature and key locator field and wireEncoding. If this
+ * is an Interest object, wire encode for signing, append a SignatureInfo to the
+ * Interest name, sign the name components and append a final name component
+ * with the signature bits. If it is an array, sign it and produce a Signature
+ * object.
+ * @param {Name} certificateName (optional) The certificate name of the key to
+ * use for signing. If omitted, use the default identity in the identity storage.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the input. If omitted, use WireFormat getDefaultWireFormat().
+ * @param {function} onComplete (optional) If target is a Data object, this calls
+ * onComplete(data) with the supplied Data object which has been modified to set
+ * its signature. If target is an Interest object, this calls
+ * onComplete(interest) with the supplied Interest object which has been
+ * modified to set its signature. If target is a Buffer, this calls
+ * onComplete(signature) where signature is the produced Signature object. If
+ * omitted, the return value is described below. (Some crypto libraries only use
+ * a callback, so onComplete is required to use these.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @return {Signature} If onComplete is omitted, return the generated Signature
+ * object (if target is a Buffer) or the target (if target is Data or Interest).
+ * Otherwise, if onComplete is supplied then return undefined and use onComplete as
+ * described above.
+ */
+KeyChain.prototype.sign = function
+ (target, certificateName, wireFormat, onComplete, onError)
+{
+ var arg2 = certificateName;
+ var arg3 = wireFormat;
+ var arg4 = onComplete;
+ var arg5 = onError;
+ // arg2, arg3, arg4, arg5
+ // certificateName, wireFormat, onComplete, onError
+ // certificateName, wireFormat, null, null
+ // certificateName, onComplete, onError, null
+ // certificateName, null, null, null
+ // wireFormat, onComplete, onError, null
+ // wireFormat, null, null, null
+ // onComplete, onError, null, null
+ // null, null, null, null
+ if (arg2 instanceof Name)
+ certificateName = arg2;
+ else
+ certificateName = null;
+
+ if (arg2 instanceof WireFormat)
+ wireFormat = arg2;
+ else if (arg3 instanceof WireFormat)
+ wireFormat = arg3;
+ else
+ wireFormat = null;
+
+ if (typeof arg2 === "function") {
+ onComplete = arg2;
+ onError = arg3;
+ }
+ else if (typeof arg3 === "function") {
+ onComplete = arg3;
+ onError = arg4;
+ }
+ else if (typeof arg4 === "function") {
+ onComplete = arg4;
+ onError = arg5;
+ }
+ else {
+ onComplete = null;
+ onError = null;
+ }
+
+ return SyncPromise.complete(onComplete, onError,
+ this.signPromise(target, certificateName, wireFormat, !onComplete));
+};
+
+/**
+ * Sign the target. If it is a Data or Interest object, set its signature. If it
+ * is an array, produce a Signature object. There are two forms of signPromise:
+ * signPromise(target, certificateName [, wireFormat] [, useSync]).
+ * sign(target [, wireFormat] [, useSync]).
+ * @param {Data|Interest|Buffer} target If this is a Data object, wire encode for
+ * signing, update its signature and key locator field and wireEncoding. If this
+ * is an Interest object, wire encode for signing, append a SignatureInfo to the
+ * Interest name, sign the name components and append a final name component
+ * with the signature bits. If it is an array, sign it and produce a Signature
+ * object.
+ * @param {Name} certificateName (optional) The certificate name of the key to
+ * use for signing. If omitted, use the default identity in the identity storage.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the input. If omitted, use WireFormat getDefaultWireFormat().
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns the generated Signature
+ * object (if target is a Buffer) or the target (if target is Data or Interest).
+ */
+KeyChain.prototype.signPromise = function
+ (target, certificateName, wireFormat, useSync)
+{
+ var arg2 = certificateName;
+ var arg3 = wireFormat;
+ var arg4 = useSync;
+ // arg2, arg3, arg4
+ // certificateName, wireFormat, useSync
+ // certificateName, wireFormat, null
+ // certificateName, useSync, null
+ // certificateName, null, null
+ // wireFormat, useSync, null
+ // wireFormat, null, null
+ // useSync, null, null
+ // null, null, null
+ if (arg2 instanceof Name)
+ certificateName = arg2;
+ else
+ certificateName = null;
+
+ if (arg2 instanceof WireFormat)
+ wireFormat = arg2;
+ else if (arg3 instanceof WireFormat)
+ wireFormat = arg3;
+ else
+ wireFormat = null;
+
+ if (typeof arg2 === 'boolean')
+ useSync = arg2;
+ else if (typeof arg3 === 'boolean')
+ useSync = arg3;
+ else if (typeof arg4 === 'boolean')
+ useSync = arg4;
+ else
+ useSync = false;
+
+ var thisKeyChain = this;
+ return SyncPromise.resolve()
+ .then(function() {
+ if (certificateName != null)
+ return SyncPromise.resolve();
+
+ // Get the default certificate name.
+ return thisKeyChain.identityManager.getDefaultCertificatePromise(useSync)
+ .then(function(signingCertificate) {
+ if (signingCertificate != null) {
+ certificateName = signingCertificate.getName();
+ return SyncPromise.resolve();
+ }
+
+ // Set the default certificate and default certificate name again.
+ return thisKeyChain.prepareDefaultCertificateNamePromise_(useSync)
+ .then(function(localCertificateName) {
+ certificateName =localCertificateName;
+ return SyncPromise.resolve();
+ });
+ });
+ })
+ .then(function() {
+ // certificateName is now set. Do the actual signing.
+ if (target instanceof Interest)
+ return thisKeyChain.identityManager.signInterestByCertificatePromise
+ (target, certificateName, wireFormat, useSync);
+ else if (target instanceof Data)
+ return thisKeyChain.identityManager.signByCertificatePromise
+ (target, certificateName, wireFormat, useSync);
+ else
+ return thisKeyChain.identityManager.signByCertificatePromise
+ (target, certificateName, useSync);
+ });
+};
+
+/**
+ * Sign the target. If it is a Data object, set its signature. If it is an
+ * array, produce a signature object.
+ * @param {Data|Buffer} target If this is a Data object, wire encode for
+ * signing, update its signature and key locator field and wireEncoding. If it
+ * is an array, sign it and return a Signature object.
+ * @param {Name} identityName (optional) The identity name for the key to use for
+ * signing. If omitted, infer the signing identity from the data packet name.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the input. If omitted, use WireFormat getDefaultWireFormat().
+ * @param {function} onComplete (optional) If target is a Data object, this calls
+ * onComplete(data) with the supplied Data object which has been modified to set
+ * its signature. If target is a Buffer, this calls
+ * onComplete(signature) where signature is the produced Signature object. If
+ * omitted, the return value is described below. (Some crypto libraries only use
+ * a callback, so onComplete is required to use these.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * with the exception. If onComplete is defined but onError is undefined, then
+ * this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @return {Signature} If onComplete is omitted, return the generated Signature
+ * object (if target is a Buffer) or undefined (if target is Data).
+ * Otherwise, if onComplete is supplied then return undefined and use onComplete
+ * as described above.
+ */
+KeyChain.prototype.signByIdentity = function
+ (target, identityName, wireFormat, onComplete, onError)
+{
+ onError = (typeof wireFormat === "function") ? onComplete : onError;
+ onComplete = (typeof wireFormat === "function") ? wireFormat : onComplete;
+ wireFormat = (typeof wireFormat === "function" || !wireFormat) ? WireFormat.getDefaultWireFormat() : wireFormat;
+
+ var useSync = !onComplete;
+ var thisKeyChain = this;
+
+ if (identityName == null)
+ identityName = new Name();
+
+ if (target instanceof Data) {
+ var data = target;
+
+ var mainPromise = SyncPromise.resolve()
+ .then(function() {
+ if (identityName.size() == 0) {
+ var inferredIdentity = thisKeyChain.policyManager.inferSigningIdentity
+ (data.getName());
+ if (inferredIdentity.size() == 0)
+ return thisKeyChain.identityManager.getDefaultCertificateNamePromise
+ (useSync);
+ else
+ return thisKeyChain.identityManager.getDefaultCertificateNameForIdentityPromise
+ (inferredIdentity, useSync);
+ }
+ else
+ return thisKeyChain.identityManager.getDefaultCertificateNameForIdentityPromise
+ (identityName, useSync);
+ })
+ .then(function(signingCertificateName) {
+ if (signingCertificateName.size() == 0)
+ throw new SecurityException(new Error
+ ("No qualified certificate name found!"));
+
+ if (!thisKeyChain.policyManager.checkSigningPolicy
+ (data.getName(), signingCertificateName))
+ throw new SecurityException(new Error
+ ("Signing Cert name does not comply with signing policy"));
+
+ return thisKeyChain.identityManager.signByCertificatePromise
+ (data, signingCertificateName, wireFormat, useSync);
+ });
+
+ return SyncPromise.complete(onComplete, onError, mainPromise);
+ }
+ else {
+ var array = target;
+
+ return SyncPromise.complete(onComplete, onError,
+ this.identityManager.getDefaultCertificateNameForIdentityPromise
+ (identityName, useSync)
+ .then(function(signingCertificateName) {
+ if (signingCertificateName.size() == 0)
+ throw new SecurityException(new Error
+ ("No qualified certificate name found!"));
+
+ return thisKeyChain.identityManager.signByCertificatePromise
+ (array, signingCertificateName, wireFormat, useSync);
+ }));
+ }
+};
+
+/**
+ * Sign the target using DigestSha256.
+ * @param {Data|Interest} target If this is a Data object, wire encode for
+ * signing, digest it and set its SignatureInfo to a DigestSha256, updating its
+ * signature and wireEncoding. If this is an Interest object, wire encode for
+ * signing, append a SignatureInfo for DigestSha256 to the Interest name, digest
+ * the name components and append a final name component with the signature bits.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the input. If omitted, use WireFormat getDefaultWireFormat().
+ */
+KeyChain.prototype.signWithSha256 = function(target, wireFormat)
+{
+ if (target instanceof Interest)
+ this.identityManager.signInterestWithSha256(target, wireFormat);
+ else
+ this.identityManager.signWithSha256(target, wireFormat);
+};
+
+/**
+ * Check the signature on the Data object and call either onVerify or
+ * onValidationFailed. We use callback functions because verify may fetch
+ * information to check the signature.
+ * @param {Data} data The Data object with the signature to check.
+ * @param {function} onVerified If the signature is verified, this calls
+ * onVerified(data).
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onValidationFailed If the signature check fails, this calls
+ * onValidationFailed(data, reason) with the Data object and reason string.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {number} stepCount
+ */
+KeyChain.prototype.verifyData = function
+ (data, onVerified, onValidationFailed, stepCount)
+{
+ if (stepCount == null)
+ stepCount = 0;
+
+ if (this.policyManager.requireVerify(data)) {
+ var nextStep = this.policyManager.checkVerificationPolicy
+ (data, stepCount, onVerified, onValidationFailed);
+ if (nextStep != null) {
+ var thisKeyChain = this;
+ this.face.expressInterest
+ (nextStep.interest,
+ function(callbackInterest, callbackData) {
+ thisKeyChain.onCertificateData(callbackInterest, callbackData, nextStep);
+ },
+ function(callbackInterest) {
+ thisKeyChain.onCertificateInterestTimeout
+ (callbackInterest, nextStep.retry, onValidationFailed, data, nextStep);
+ });
+ }
+ }
+ else if (this.policyManager.skipVerifyAndTrust(data)) {
+ try {
+ onVerified(data);
+ } catch (ex) {
+ console.log("Error in onVerified: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ else {
+ try {
+ onValidationFailed
+ (data, "The packet has no verify rule but skipVerifyAndTrust is false");
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+};
+
+/**
+ * Check the signature on the signed interest and call either onVerify or
+ * onValidationFailed. We use callback functions because verify may fetch
+ * information to check the signature.
+ * @param {Interest} interest The interest with the signature to check.
+ * @param {function} onVerified If the signature is verified, this calls
+ * onVerified(interest).
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onValidationFailed If the signature check fails, this calls
+ * onValidationFailed(interest, reason) with the Interest object and reason
+ * string.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ */
+KeyChain.prototype.verifyInterest = function
+ (interest, onVerified, onValidationFailed, stepCount, wireFormat)
+{
+ if (stepCount == null)
+ stepCount = 0;
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ if (this.policyManager.requireVerify(interest)) {
+ var nextStep = this.policyManager.checkVerificationPolicy
+ (interest, stepCount, onVerified, onValidationFailed, wireFormat);
+ if (nextStep != null) {
+ var thisKeyChain = this;
+ this.face.expressInterest
+ (nextStep.interest,
+ function(callbackInterest, callbackData) {
+ thisKeyChain.onCertificateData(callbackInterest, callbackData, nextStep);
+ },
+ function(callbackInterest) {
+ thisKeyChain.onCertificateInterestTimeout
+ (callbackInterest, nextStep.retry, onValidationFailed, interest,
+ nextStep);
+ });
+ }
+ }
+ else if (this.policyManager.skipVerifyAndTrust(interest)) {
+ try {
+ onVerified(interest);
+ } catch (ex) {
+ console.log("Error in onVerified: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ else {
+ try {
+ onValidationFailed
+ (interest,
+ "The packet has no verify rule but skipVerifyAndTrust is false");
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+};
+
+/**
+ * Set the Face which will be used to fetch required certificates.
+ * @param {Face} face A pointer to the Face object.
+ */
+KeyChain.prototype.setFace = function(face)
+{
+ this.face = face;
+};
+
+/**
+ * Wire encode the target, compute an HmacWithSha256 and update the signature
+ * value.
+ * Note: This method is an experimental feature. The API may change.
+ * @param {Data} target If this is a Data object, update its signature and wire
+ * encoding.
+ * @param {Blob} key The key for the HmacWithSha256.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the target. If omitted, use WireFormat getDefaultWireFormat().
+ */
+KeyChain.signWithHmacWithSha256 = function(target, key, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ if (target instanceof Data) {
+ var data = target;
+ // Encode once to get the signed portion.
+ var encoding = data.wireEncode(wireFormat);
+
+ var signer = Crypto.createHmac('sha256', key.buf());
+ signer.update(encoding.signedBuf());
+ data.getSignature().setSignature(
+ new Blob(signer.digest(), false));
+ }
+ else
+ throw new SecurityException(new Error
+ ("signWithHmacWithSha256: Unrecognized target type"));
+};
+
+/**
+ * Compute a new HmacWithSha256 for the target and verify it against the
+ * signature value.
+ * Note: This method is an experimental feature. The API may change.
+ * @param {Data} target The Data object to verify.
+ * @param {Blob} key The key for the HmacWithSha256.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the target. If omitted, use WireFormat getDefaultWireFormat().
+ * @return {boolean} True if the signature verifies, otherwise false.
+ */
+KeyChain.verifyDataWithHmacWithSha256 = function(data, key, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ // wireEncode returns the cached encoding if available.
+ var encoding = data.wireEncode(wireFormat);
+
+ var signer = Crypto.createHmac('sha256', key.buf());
+ signer.update(encoding.signedBuf());
+ var newSignatureBits = new Blob(signer.digest(), false);
+
+ // Use the flexible Blob.equals operator.
+ return newSignatureBits.equals(data.getSignature().getSignature());
+};
+
+KeyChain.DEFAULT_KEY_PARAMS = new RsaKeyParams();
+
+KeyChain.prototype.onCertificateData = function(interest, data, nextStep)
+{
+ // Try to verify the certificate (data) according to the parameters in nextStep.
+ this.verifyData
+ (data, nextStep.onVerified, nextStep.onValidationFailed, nextStep.stepCount);
+};
+
+KeyChain.prototype.onCertificateInterestTimeout = function
+ (interest, retry, onValidationFailed, originalDataOrInterest, nextStep)
+{
+ if (retry > 0) {
+ // Issue the same expressInterest as in verifyData except decrement retry.
+ var thisKeyChain = this;
+ this.face.expressInterest
+ (interest,
+ function(callbackInterest, callbackData) {
+ thisKeyChain.onCertificateData(callbackInterest, callbackData, nextStep);
+ },
+ function(callbackInterest) {
+ thisKeyChain.onCertificateInterestTimeout
+ (callbackInterest, retry - 1, onValidationFailed,
+ originalDataOrInterest, nextStep);
+ });
+ }
+ else {
+ try {
+ onValidationFailed
+ (originalDataOrInterest, "The retry count is zero after timeout for fetching " +
+ interest.getName().toUri());
+ } catch (ex) {
+ console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+};
+
+/**
+ * Get the default certificate from the identity storage and return its name.
+ * If there is no default identity or default certificate, then create one.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns the default certificate
+ * name.
+ */
+KeyChain.prototype.prepareDefaultCertificateNamePromise_ = function(useSync)
+{
+ var signingCertificate;
+ var thisKeyChain = this;
+ return this.identityManager.getDefaultCertificatePromise(useSync)
+ .then(function(localCertificate) {
+ signingCertificate = localCertificate;
+ if (signingCertificate != null)
+ return SyncPromise.resolve();
+
+ // Set the default certificate and get the certificate again.
+ return thisKeyChain.setDefaultCertificatePromise_(useSync)
+ .then(function() {
+ return thisKeyChain.identityManager.getDefaultCertificatePromise(useSync);
+ })
+ .then(function(localCertificate) {
+ signingCertificate = localCertificate;
+ return SyncPromise.resolve();
+ });
+ })
+ .then(function() {
+ return SyncPromise.resolve(signingCertificate.getName());
+ });
+}
+
+/**
+ * Create the default certificate if it is not initialized. If there is no
+ * default identity yet, creating a new tmp-identity.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that resolves when the default
+ * certificate is set.
+ */
+KeyChain.prototype.setDefaultCertificatePromise_ = function(useSync)
+{
+ var thisKeyChain = this;
+
+ return this.identityManager.getDefaultCertificatePromise(useSync)
+ .then(function(certificate) {
+ if (certificate != null)
+ // We already have a default certificate.
+ return SyncPromise.resolve();
+
+ var defaultIdentity;
+ return thisKeyChain.identityManager.getDefaultIdentityPromise(useSync)
+ .then(function(localDefaultIdentity) {
+ defaultIdentity = localDefaultIdentity;
+ return SyncPromise.resolve();
+ }, function(ex) {
+ // Create a default identity name.
+ randomComponent = Crypto.randomBytes(4);
+ defaultIdentity = new Name().append("tmp-identity")
+ .append(new Blob(randomComponent, false));
+
+ return SyncPromise.resolve();
+ })
+ .then(function() {
+ return thisKeyChain.identityManager.createIdentityAndCertificatePromise
+ (defaultIdentity, KeyChain.DEFAULT_KEY_PARAMS, useSync);
+ })
+ .then(function() {
+ return thisKeyChain.identityManager.setDefaultIdentityPromise
+ (defaultIdentity, useSync);
+ });
+ });
+};
+/**
+ * This class represents an Interest Exclude.
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Meki Cheraoui
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Name = require('./name.js').Name; /** @ignore */
+var DataUtils = require('./encoding/data-utils.js').DataUtils; /** @ignore */
+var Blob = require('./util/blob.js').Blob;
+
+/**
+ * Create a new Exclude.
+ * @constructor
+ * @param {Array<Name.Component|Buffer|Exclude.ANY>} values (optional) An array where each element is either a Name.Component, Buffer component or Exclude.ANY.
+ */
+var Exclude = function Exclude(values)
+{
+ this.values = [];
+
+ if (typeof values === 'object' && values instanceof Exclude)
+ // Copy the exclude.
+ this.values = values.values.slice(0);
+ else if (values) {
+ // Set the changeCount now since append expects it.
+ this.changeCount = 0;
+ for (var i = 0; i < values.length; ++i) {
+ if (values[i] == Exclude.ANY)
+ this.appendAny();
+ else
+ this.appendComponent(values[i]);
+ }
+ }
+
+ this.changeCount = 0;
+};
+
+exports.Exclude = Exclude;
+
+Exclude.ANY = "*";
+
+/**
+ * Get the number of entries.
+ * @return {number} The number of entries.
+ */
+Exclude.prototype.size = function() { return this.values.length; };
+
+/**
+ * Get the entry at the given index.
+ * @param {number} i The index of the entry, starting from 0.
+ * @return {Exclude.ANY|Name.Component} Exclude.ANY or a Name.Component.
+ */
+Exclude.prototype.get = function(i) { return this.values[i]; };
+
+/**
+ * Append an Exclude.ANY element.
+ * @return This Exclude so that you can chain calls to append.
+ */
+Exclude.prototype.appendAny = function()
+{
+ this.values.push(Exclude.ANY);
+ ++this.changeCount;
+ return this;
+};
+
+/**
+ * Append a component entry, copying from component.
+ * @param {Name.Component|Buffer} component
+ * @return This Exclude so that you can chain calls to append.
+ */
+Exclude.prototype.appendComponent = function(component)
+{
+ this.values.push(new Name.Component(component));
+ ++this.changeCount;
+ return this;
+};
+
+/**
+ * Clear all the entries.
+ */
+Exclude.prototype.clear = function()
+{
+ ++this.changeCount;
+ this.values = [];
+};
+
+/**
+ * Return a string with elements separated by "," and Exclude.ANY shown as "*".
+ */
+Exclude.prototype.toUri = function()
+{
+ if (this.values == null || this.values.length == 0)
+ return "";
+
+ var result = "";
+ for (var i = 0; i < this.values.length; ++i) {
+ if (i > 0)
+ result += ",";
+
+ if (this.values[i] == Exclude.ANY)
+ result += "*";
+ else
+ result += this.values[i].toEscapedString();
+ }
+ return result;
+};
+
+/**
+ * Return true if the component matches any of the exclude criteria.
+ */
+Exclude.prototype.matches = function(component)
+{
+ if (!(typeof component == 'object' && component instanceof Name.Component))
+ component = new Name.Component(component);
+
+ for (var i = 0; i < this.values.length; ++i) {
+ if (this.values[i] == Exclude.ANY) {
+ var lowerBound = null;
+ if (i > 0)
+ lowerBound = this.values[i - 1];
+
+ // Find the upper bound, possibly skipping over multiple ANY in a row.
+ var iUpperBound;
+ var upperBound = null;
+ for (iUpperBound = i + 1; iUpperBound < this.values.length; ++iUpperBound) {
+ if (this.values[iUpperBound] != Exclude.ANY) {
+ upperBound = this.values[iUpperBound];
+ break;
+ }
+ }
+
+ // If lowerBound != null, we already checked component equals lowerBound on the last pass.
+ // If upperBound != null, we will check component equals upperBound on the next pass.
+ if (upperBound != null) {
+ if (lowerBound != null) {
+ if (component.compare(lowerBound) > 0 &&
+ component.compare(upperBound) < 0)
+ return true;
+ }
+ else {
+ if (component.compare(upperBound) < 0)
+ return true;
+ }
+
+ // Make i equal iUpperBound on the next pass.
+ i = iUpperBound - 1;
+ }
+ else {
+ if (lowerBound != null) {
+ if (component.compare(lowerBound) > 0)
+ return true;
+ }
+ else
+ // this.values has only ANY.
+ return true;
+ }
+ }
+ else {
+ if (component.equals(this.values[i]))
+ return true;
+ }
+ }
+
+ return false;
+};
+
+/**
+ * Return -1 if component1 is less than component2, 1 if greater or 0 if equal.
+ * A component is less if it is shorter, otherwise if equal length do a byte comparison.
+ */
+Exclude.compareComponents = function(component1, component2)
+{
+ if (typeof component1 == 'object' && component1 instanceof Name.Component)
+ component1 = component1.getValue().buf();
+ if (typeof component2 == 'object' && component2 instanceof Name.Component)
+ component2 = component2.getValue().buf();
+
+ return Name.Component.compareBuffers(component1, component2);
+};
+
+/**
+ * Get the change count, which is incremented each time this object is changed.
+ * @return {number} The change count.
+ */
+Exclude.prototype.getChangeCount = function()
+{
+ return this.changeCount;
+};
+/**
+ * This class represents Interest Objects
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Meki Cheraoui
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Crypto = require('./crypto.js'); /** @ignore */
+var Blob = require('./util/blob.js').Blob; /** @ignore */
+var SignedBlob = require('./util/signed-blob.js').SignedBlob; /** @ignore */
+var ChangeCounter = require('./util/change-counter.js').ChangeCounter; /** @ignore */
+var Name = require('./name.js').Name; /** @ignore */
+var Exclude = require('./exclude.js').Exclude; /** @ignore */
+var Link = require('./link.js').Link; /** @ignore */
+var KeyLocator = require('./key-locator.js').KeyLocator; /** @ignore */
+var IncomingFaceId = require('./lp/incoming-face-id.js').IncomingFaceId; /** @ignore */
+var WireFormat = require('./encoding/wire-format.js').WireFormat;
+
+/**
+ * Create a new Interest with the optional values.
+ *
+ * @constructor
+ * @param {Name|Interest} nameOrInterest If this is an Interest, copy values from the interest and ignore the
+ * other arguments. Otherwise this is the optional name for the new Interest.
+ * @param {number} minSuffixComponents
+ * @param {number} maxSuffixComponents
+ */
+var Interest = function Interest
+ (nameOrInterest, minSuffixComponents, maxSuffixComponents,
+ publisherPublicKeyDigest, exclude, childSelector, answerOriginKind, scope,
+ interestLifetimeMilliseconds, nonce)
+{
+ if (publisherPublicKeyDigest)
+ throw new Error
+ ("Interest constructor: PublisherPublicKeyDigest support has been removed.");
+ if (answerOriginKind)
+ throw new Error
+ ("Interest constructor: answerOriginKind support has been removed. Use setMustBeFresh().");
+ if (scope)
+ throw new Error("Interest constructor: scope support has been removed.");
+
+ if (typeof nameOrInterest === 'object' && nameOrInterest instanceof Interest) {
+ // Special case: this is a copy constructor. Ignore all but the first argument.
+ var interest = nameOrInterest;
+ // Copy the name.
+ this.name_ = new ChangeCounter(new Name(interest.getName()));
+ this.maxSuffixComponents_ = interest.maxSuffixComponents_;
+ this.minSuffixComponents_ = interest.minSuffixComponents_;
+
+ this.keyLocator_ = new ChangeCounter(new KeyLocator(interest.getKeyLocator()));
+ this.exclude_ = new ChangeCounter(new Exclude(interest.getExclude()));
+ this.childSelector_ = interest.childSelector_;
+ this.mustBeFresh_ = interest.mustBeFresh_;
+ this.interestLifetimeMilliseconds_ = interest.interestLifetimeMilliseconds_;
+ this.nonce_ = interest.nonce_;
+ this.linkWireEncoding_ = interest.linkWireEncoding_;
+ this.linkWireEncodingFormat_ = interest.linkWireEncodingFormat_;
+ this.link_ = new ChangeCounter(null);
+ if (interest.link_.get() != null)
+ this.link_.set(new Link(interest.link_.get()));
+ this.selectedDelegationIndex_ = interest.selectedDelegationIndex_;
+ this.defaultWireEncoding_ = interest.getDefaultWireEncoding();
+ this.defaultWireEncodingFormat_ = interest.defaultWireEncodingFormat_;
+ }
+ else {
+ this.name_ = new ChangeCounter(typeof nameOrInterest === 'object' &&
+ nameOrInterest instanceof Name ?
+ new Name(nameOrInterest) : new Name());
+ this.maxSuffixComponents_ = maxSuffixComponents;
+ this.minSuffixComponents_ = minSuffixComponents;
+
+ this.keyLocator_ = new ChangeCounter(new KeyLocator());
+ this.exclude_ = new ChangeCounter(typeof exclude === 'object' && exclude instanceof Exclude ?
+ new Exclude(exclude) : new Exclude());
+ this.childSelector_ = childSelector;
+ this.mustBeFresh_ = true;
+ this.interestLifetimeMilliseconds_ = interestLifetimeMilliseconds;
+ this.nonce_ = typeof nonce === 'object' && nonce instanceof Blob ?
+ nonce : new Blob(nonce, true);
+ this.linkWireEncoding_ = new Blob();
+ this.linkWireEncodingFormat_ = null;
+ this.link_ = new ChangeCounter(null);
+ this.selectedDelegationIndex_ = null;
+ this.defaultWireEncoding_ = new SignedBlob();
+ this.defaultWireEncodingFormat_ = null;
+ }
+
+ this.getNonceChangeCount_ = 0;
+ this.getDefaultWireEncodingChangeCount_ = 0;
+ this.changeCount_ = 0;
+ this.lpPacket_ = null;
+};
+
+exports.Interest = Interest;
+
+Interest.RECURSIVE_POSTFIX = "*";
+
+Interest.CHILD_SELECTOR_LEFT = 0;
+Interest.CHILD_SELECTOR_RIGHT = 1;
+
+/**
+ * Check if this interest's name matches the given name (using Name.match) and
+ * the given name also conforms to the interest selectors.
+ * @param {Name} name The name to check.
+ * @return {boolean} True if the name and interest selectors match, False otherwise.
+ */
+Interest.prototype.matchesName = function(name)
+{
+ if (!this.getName().match(name))
+ return false;
+
+ if (this.minSuffixComponents_ != null &&
+ // Add 1 for the implicit digest.
+ !(name.size() + 1 - this.getName().size() >= this.minSuffixComponents_))
+ return false;
+ if (this.maxSuffixComponents_ != null &&
+ // Add 1 for the implicit digest.
+ !(name.size() + 1 - this.getName().size() <= this.maxSuffixComponents_))
+ return false;
+ if (this.getExclude() != null && name.size() > this.getName().size() &&
+ this.getExclude().matches(name.get(this.getName().size())))
+ return false;
+
+ return true;
+};
+
+/**
+ * @deprecated Use matchesName.
+ */
+Interest.prototype.matches_name = function(/*Name*/ name)
+{
+ return this.matchesName(name);
+};
+
+/**
+ * Check if the given Data packet can satisfy this Interest. This method
+ * considers the Name, MinSuffixComponents, MaxSuffixComponents,
+ * PublisherPublicKeyLocator, and Exclude. It does not consider the
+ * ChildSelector or MustBeFresh. This uses the given wireFormat to get the
+ * Data packet encoding for the full Name.
+ * @param {Data} data The Data packet to check.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the Data packet to get its full Name. If omitted, use
+ * WireFormat.getDefaultWireFormat().
+ * @return {boolean} True if the given Data packet can satisfy this Interest.
+ */
+Interest.prototype.matchesData = function(data, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ // Imitate ndn-cxx Interest::matchesData.
+ var interestNameLength = this.getName().size();
+ var dataName = data.getName();
+ var fullNameLength = dataName.size() + 1;
+
+ // Check MinSuffixComponents.
+ var hasMinSuffixComponents = (this.getMinSuffixComponents() != null);
+ var minSuffixComponents =
+ hasMinSuffixComponents ? this.getMinSuffixComponents() : 0;
+ if (!(interestNameLength + minSuffixComponents <= fullNameLength))
+ return false;
+
+ // Check MaxSuffixComponents.
+ var hasMaxSuffixComponents = (this.getMaxSuffixComponents() != null);
+ if (hasMaxSuffixComponents &&
+ !(interestNameLength + this.getMaxSuffixComponents() >= fullNameLength))
+ return false;
+
+ // Check the prefix.
+ if (interestNameLength === fullNameLength) {
+ if (this.getName().get(-1).isImplicitSha256Digest()) {
+ if (!this.getName().equals(data.getFullName(wireFormat)))
+ return false;
+ }
+ else
+ // The Interest Name is the same length as the Data full Name, but the
+ // last component isn't a digest so there's no possibility of matching.
+ return false;
+ }
+ else {
+ // The Interest Name should be a strict prefix of the Data full Name.
+ if (!this.getName().isPrefixOf(dataName))
+ return false;
+ }
+
+ // Check the Exclude.
+ // The Exclude won't be violated if the Interest Name is the same as the
+ // Data full Name.
+ if (this.getExclude().size() > 0 && fullNameLength > interestNameLength) {
+ if (interestNameLength == fullNameLength - 1) {
+ // The component to exclude is the digest.
+ if (this.getExclude().matches
+ (data.getFullName(wireFormat).get(interestNameLength)))
+ return false;
+ }
+ else {
+ // The component to exclude is not the digest.
+ if (this.getExclude().matches(dataName.get(interestNameLength)))
+ return false;
+ }
+ }
+
+ // Check the KeyLocator.
+ var publisherPublicKeyLocator = this.getKeyLocator();
+ if (publisherPublicKeyLocator.getType()) {
+ var signature = data.getSignature();
+ if (!KeyLocator.canGetFromSignature(signature))
+ // No KeyLocator in the Data packet.
+ return false;
+ if (!publisherPublicKeyLocator.equals
+ (KeyLocator.getFromSignature(signature)))
+ return false;
+ }
+
+ return true;
+};
+
+/**
+ * Return a new Interest with the same fields as this Interest.
+ */
+Interest.prototype.clone = function()
+{
+ return new Interest(this);
+};
+
+/**
+ * Get the interest Name.
+ * @return {Name} The name. The name size() may be 0 if not specified.
+ */
+Interest.prototype.getName = function() { return this.name_.get(); };
+
+/**
+ * Get the min suffix components.
+ * @return {number} The min suffix components, or null if not specified.
+ */
+Interest.prototype.getMinSuffixComponents = function()
+{
+ return this.minSuffixComponents_;
+};
+
+/**
+ * Get the max suffix components.
+ * @return {number} The max suffix components, or null if not specified.
+ */
+Interest.prototype.getMaxSuffixComponents = function()
+{
+ return this.maxSuffixComponents_;
+};
+
+/**
+ * Get the interest key locator.
+ * @return {KeyLocator} The key locator. If its getType() is null,
+ * then the key locator is not specified.
+ */
+Interest.prototype.getKeyLocator = function()
+{
+ return this.keyLocator_.get();
+};
+
+/**
+ * Get the exclude object.
+ * @return {Exclude} The exclude object. If the exclude size() is zero, then
+ * the exclude is not specified.
+ */
+Interest.prototype.getExclude = function() { return this.exclude_.get(); };
+
+/**
+ * Get the child selector.
+ * @return {number} The child selector, or null if not specified.
+ */
+Interest.prototype.getChildSelector = function()
+{
+ return this.childSelector_;
+};
+
+/**
+ * Get the must be fresh flag. If not specified, the default is true.
+ * @return {boolean} The must be fresh flag.
+ */
+Interest.prototype.getMustBeFresh = function()
+{
+ return this.mustBeFresh_;
+};
+
+/**
+ * Return the nonce value from the incoming interest. If you change any of the
+ * fields in this Interest object, then the nonce value is cleared.
+ * @return {Blob} The nonce. If not specified, the value isNull().
+ */
+Interest.prototype.getNonce = function()
+{
+ if (this.getNonceChangeCount_ != this.getChangeCount()) {
+ // The values have changed, so the existing nonce is invalidated.
+ this.nonce_ = new Blob();
+ this.getNonceChangeCount_ = this.getChangeCount();
+ }
+
+ return this.nonce_;
+};
+
+/**
+ * @deprecated Use getNonce. This method returns a Buffer which is the former
+ * behavior of getNonce, and should only be used while updating your code.
+ */
+Interest.prototype.getNonceAsBuffer = function()
+{
+ return this.getNonce().buf();
+};
+
+/**
+ * Check if this interest has a link object (or a link wire encoding which
+ * can be decoded to make the link object).
+ * @return {boolean} True if this interest has a link object, false if not.
+ */
+Interest.prototype.hasLink = function()
+{
+ return this.link_.get() != null || !this.linkWireEncoding_.isNull();
+};
+
+/**
+ * Get the link object. If necessary, decode it from the link wire encoding.
+ * @return {Link} The link object, or null if not specified.
+ * @throws DecodingException For error decoding the link wire encoding (if
+ * necessary).
+ */
+Interest.prototype.getLink = function()
+{
+ if (this.link_.get() != null)
+ return this.link_.get();
+ else if (!this.linkWireEncoding_.isNull()) {
+ // Decode the link object from linkWireEncoding_.
+ var link = new Link();
+ link.wireDecode(this.linkWireEncoding_, this.linkWireEncodingFormat_);
+ this.link_.set(link);
+
+ // Clear linkWireEncoding_ since it is now managed by the link object.
+ this.linkWireEncoding_ = new Blob();
+ this.linkWireEncodingFormat_ = null;
+
+ return link;
+ }
+ else
+ return null;
+};
+
+/**
+ * Get the wire encoding of the link object. If there is already a wire
+ * encoding then return it. Otherwise encode from the link object (if
+ * available).
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the Link. If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {Blob} The wire encoding, or an isNull Blob if the link is not
+ * specified.
+ */
+Interest.prototype.getLinkWireEncoding = function(wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ if (!this.linkWireEncoding_.isNull() && this.linkWireEncodingFormat_ == wireFormat)
+ return this.linkWireEncoding_;
+
+ var link = this.getLink();
+ if (link != null)
+ return link.wireEncode(wireFormat);
+ else
+ return new Blob();
+};
+
+/**
+ * Get the selected delegation index.
+ * @return {number} The selected delegation index. If not specified, return null.
+ */
+Interest.prototype.getSelectedDelegationIndex = function()
+{
+ return this.selectedDelegationIndex_;
+};
+
+/**
+ * Get the interest lifetime.
+ * @return {number} The interest lifetime in milliseconds, or null if not
+ * specified.
+ */
+Interest.prototype.getInterestLifetimeMilliseconds = function()
+{
+ return this.interestLifetimeMilliseconds_;
+};
+
+/**
+ * Return the default wire encoding, which was encoded with
+ * getDefaultWireEncodingFormat().
+ * @return {SignedBlob} The default wire encoding, whose isNull() may be true
+ * if there is no default wire encoding.
+ */
+Interest.prototype.getDefaultWireEncoding = function()
+{
+ if (this.getDefaultWireEncodingChangeCount_ != this.getChangeCount()) {
+ // The values have changed, so the default wire encoding is invalidated.
+ this.defaultWireEncoding_ = new SignedBlob();
+ this.defaultWireEncodingFormat_ = null;
+ this.getDefaultWireEncodingChangeCount_ = this.getChangeCount();
+ }
+
+ return this.defaultWireEncoding_;
+};
+
+/**
+ * Get the WireFormat which is used by getDefaultWireEncoding().
+ * @return {WireFormat} The WireFormat, which is only meaningful if the
+ * getDefaultWireEncoding() is not isNull().
+ */
+Interest.prototype.getDefaultWireEncodingFormat = function()
+{
+ return this.defaultWireEncodingFormat_;
+};
+
+/**
+ * Get the incoming face ID according to the incoming packet header.
+ * @return {number} The incoming face ID. If not specified, return null.
+ */
+Interest.prototype.getIncomingFaceId = function()
+{
+ var field =
+ this.lpPacket_ === null ? null : IncomingFaceId.getFirstHeader(this.lpPacket_);
+ return field === null ? null : field.getFaceId();
+};
+
+/**
+ * Set the interest name.
+ * Note: You can also call getName and change the name values directly.
+ * @param {Name} name The interest name. This makes a copy of the name.
+ * @return {Interest} This Interest so that you can chain calls to update values.
+ */
+Interest.prototype.setName = function(name)
+{
+ this.name_.set(typeof name === 'object' && name instanceof Name ?
+ new Name(name) : new Name());
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Set the min suffix components count.
+ * @param {number} minSuffixComponents The min suffix components count. If not
+ * specified, set to undefined.
+ * @return {Interest} This Interest so that you can chain calls to update values.
+ */
+Interest.prototype.setMinSuffixComponents = function(minSuffixComponents)
+{
+ this.minSuffixComponents_ = minSuffixComponents;
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Set the max suffix components count.
+ * @param {number} maxSuffixComponents The max suffix components count. If not
+ * specified, set to undefined.
+ * @return {Interest} This Interest so that you can chain calls to update values.
+ */
+Interest.prototype.setMaxSuffixComponents = function(maxSuffixComponents)
+{
+ this.maxSuffixComponents_ = maxSuffixComponents;
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Set this interest to use a copy of the given KeyLocator object.
+ * Note: You can also call getKeyLocator and change the key locator directly.
+ * @param {KeyLocator} keyLocator The KeyLocator object. This makes a copy of the object.
+ * If no key locator is specified, set to a new default KeyLocator(), or to a
+ * KeyLocator with an unspecified type.
+ * @return {Interest} This Interest so that you can chain calls to update values.
+ */
+Interest.prototype.setKeyLocator = function(keyLocator)
+{
+ this.keyLocator_.set
+ (typeof keyLocator === 'object' && keyLocator instanceof KeyLocator ?
+ new KeyLocator(keyLocator) : new KeyLocator());
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Set this interest to use a copy of the given exclude object. Note: You can
+ * also call getExclude and change the exclude entries directly.
+ * @param {Exclude} exclude The Exclude object. This makes a copy of the object.
+ * If no exclude is specified, set to a new default Exclude(), or to an Exclude
+ * with size() 0.
+ * @return {Interest} This Interest so that you can chain calls to update values.
+ */
+Interest.prototype.setExclude = function(exclude)
+{
+ this.exclude_.set(typeof exclude === 'object' && exclude instanceof Exclude ?
+ new Exclude(exclude) : new Exclude());
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Set the link wire encoding bytes, without decoding them. If there is
+ * a link object, set it to null. If you later call getLink(), it will
+ * decode the wireEncoding to create the link object.
+ * @param {Blob} encoding The Blob with the bytes of the link wire encoding.
+ * If no link is specified, set to an empty Blob() or call unsetLink().
+ * @param {WireFormat} wireFormat The wire format of the encoding, to be used
+ * later if necessary to decode. If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {Interest} This Interest so that you can chain calls to update values.
+ */
+Interest.prototype.setLinkWireEncoding = function(encoding, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ this.linkWireEncoding_ = encoding;
+ this.linkWireEncodingFormat_ = wireFormat;
+
+ // Clear the link object, assuming that it has a different encoding.
+ this.link_.set(null);
+
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Clear the link wire encoding and link object so that getLink() returns null.
+ * @return {Interest} This Interest so that you can chain calls to update values.
+ */
+Interest.prototype.unsetLink = function()
+{
+ return this.setLinkWireEncoding(new Blob(), null);
+};
+
+/**
+ * Set the selected delegation index.
+ * @param {number} selectedDelegationIndex The selected delegation index. If not
+ * specified, set to null.
+ * @return {Interest} This Interest so that you can chain calls to update values.
+ */
+Interest.prototype.setSelectedDelegationIndex = function(selectedDelegationIndex)
+{
+ this.selectedDelegationIndex_ = selectedDelegationIndex;
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Set the child selector.
+ * @param {number} childSelector The child selector. If not specified, set to
+ * undefined.
+ * @return {Interest} This Interest so that you can chain calls to update values.
+ */
+Interest.prototype.setChildSelector = function(childSelector)
+{
+ this.childSelector_ = childSelector;
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Set the MustBeFresh flag.
+ * @param {boolean} mustBeFresh True if the content must be fresh, otherwise
+ * false. If you do not set this flag, the default value is true.
+ * @return {Interest} This Interest so that you can chain calls to update values.
+ */
+Interest.prototype.setMustBeFresh = function(mustBeFresh)
+{
+ this.mustBeFresh_ = (mustBeFresh ? true : false);
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * Set the interest lifetime.
+ * @param {number} interestLifetimeMilliseconds The interest lifetime in
+ * milliseconds. If not specified, set to undefined.
+ * @return {Interest} This Interest so that you can chain calls to update values.
+ */
+Interest.prototype.setInterestLifetimeMilliseconds = function(interestLifetimeMilliseconds)
+{
+ this.interestLifetimeMilliseconds_ = interestLifetimeMilliseconds;
+ ++this.changeCount_;
+ return this;
+};
+
+/**
+ * @deprecated You should let the wire encoder generate a random nonce
+ * internally before sending the interest.
+ */
+Interest.prototype.setNonce = function(nonce)
+{
+ this.nonce_ = typeof nonce === 'object' && nonce instanceof Blob ?
+ nonce : new Blob(nonce, true);
+ // Set getNonceChangeCount_ so that the next call to getNonce() won't clear
+ // this.nonce_.
+ ++this.changeCount_;
+ this.getNonceChangeCount_ = this.getChangeCount();
+ return this;
+};
+
+/**
+ * Encode the name according to the "NDN URI Scheme". If there are interest selectors, append "?" and
+ * added the selectors as a query string. For example "/test/name?ndn.ChildSelector=1".
+ * Note: This is an experimental feature. See the API docs for more detail at
+ * http://named-data.net/doc/ndn-ccl-api/interest.html#interest-touri-method .
+ * @return {string} The URI string.
+ */
+Interest.prototype.toUri = function()
+{
+ var selectors = "";
+
+ if (this.minSuffixComponents_ != null)
+ selectors += "&ndn.MinSuffixComponents=" + this.minSuffixComponents_;
+ if (this.maxSuffixComponents_ != null)
+ selectors += "&ndn.MaxSuffixComponents=" + this.maxSuffixComponents_;
+ if (this.childSelector_ != null)
+ selectors += "&ndn.ChildSelector=" + this.childSelector_;
+ selectors += "&ndn.MustBeFresh=" + (this.mustBeFresh_ ? 1 : 0);
+ if (this.interestLifetimeMilliseconds_ != null)
+ selectors += "&ndn.InterestLifetime=" + this.interestLifetimeMilliseconds_;
+ if (this.getNonce().size() > 0)
+ selectors += "&ndn.Nonce=" + Name.toEscapedString(this.getNonce().buf());
+ if (this.getExclude() != null && this.getExclude().size() > 0)
+ selectors += "&ndn.Exclude=" + this.getExclude().toUri();
+
+ var result = this.getName().toUri();
+ if (selectors != "")
+ // Replace the first & with ?.
+ result += "?" + selectors.substr(1);
+
+ return result;
+};
+
+/**
+ * Encode this Interest for a particular wire format. If wireFormat is the
+ * default wire format, also set the defaultWireEncoding field to the encoded
+ * result.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {SignedBlob} The encoded buffer in a SignedBlob object.
+ */
+Interest.prototype.wireEncode = function(wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ if (!this.getDefaultWireEncoding().isNull() &&
+ this.getDefaultWireEncodingFormat() == wireFormat)
+ // We already have an encoding in the desired format.
+ return this.getDefaultWireEncoding();
+
+ var result = wireFormat.encodeInterest(this);
+ var wireEncoding = new SignedBlob
+ (result.encoding, result.signedPortionBeginOffset,
+ result.signedPortionEndOffset);
+
+ if (wireFormat == WireFormat.getDefaultWireFormat())
+ // This is the default wire encoding.
+ this.setDefaultWireEncoding
+ (wireEncoding, WireFormat.getDefaultWireFormat());
+ return wireEncoding;
+};
+
+/**
+ * Decode the input using a particular wire format and update this Interest. If
+ * wireFormat is the default wire format, also set the defaultWireEncoding to
+ * another pointer to the input.
+ * @param {Blob|Buffer} input The buffer with the bytes to decode.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to decode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ */
+Interest.prototype.wireDecode = function(input, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ var result;
+ if (typeof input === 'object' && input instanceof Blob)
+ // Input is a blob, so get its buf() and set copy false.
+ result = wireFormat.decodeInterest(this, input.buf(), false);
+ else
+ result = wireFormat.decodeInterest(this, input, true);
+
+ if (wireFormat == WireFormat.getDefaultWireFormat())
+ // This is the default wire encoding. In the Blob constructor, set copy
+ // true, but if input is already a Blob, it won't copy.
+ this.setDefaultWireEncoding(new SignedBlob
+ (new Blob(input, true), result.signedPortionBeginOffset,
+ result.signedPortionEndOffset),
+ WireFormat.getDefaultWireFormat());
+ else
+ this.setDefaultWireEncoding(new SignedBlob(), null);
+};
+
+/**
+ * Update the bytes of the nonce with new random values. This ensures that the
+ * new nonce value is different than the current one. If the current nonce is
+ * not specified, this does nothing.
+ */
+Interest.prototype.refreshNonce = function()
+{
+ var currentNonce = this.getNonce();
+ if (currentNonce.size() === 0)
+ return;
+
+ var newNonce;
+ while (true) {
+ newNonce = new Blob(Crypto.randomBytes(currentNonce.size()), false);
+ if (!newNonce.equals(currentNonce))
+ break;
+ }
+
+ this.nonce_ = newNonce;
+ // Set getNonceChangeCount_ so that the next call to getNonce() won't clear
+ // this.nonce_.
+ ++this.changeCount_;
+ this.getNonceChangeCount_ = this.getChangeCount();
+};
+
+/**
+ * An internal library method to set the LpPacket for an incoming packet. The
+ * application should not call this.
+ * @param {LpPacket} lpPacket The LpPacket. This does not make a copy.
+ * @return {Interest} This Interest so that you can chain calls to update values.
+ * @note This is an experimental feature. This API may change in the future.
+ */
+Interest.prototype.setLpPacket = function(lpPacket)
+{
+ this.lpPacket_ = lpPacket;
+ // Don't update changeCount_ since this doesn't affect the wire encoding.
+ return this;
+}
+
+/**
+ * Get the change count, which is incremented each time this object (or a child
+ * object) is changed.
+ * @return {number} The change count.
+ */
+Interest.prototype.getChangeCount = function()
+{
+ // Make sure each of the checkChanged is called.
+ var changed = this.name_.checkChanged();
+ changed = this.keyLocator_.checkChanged() || changed;
+ changed = this.exclude_.checkChanged() || changed;
+ if (changed)
+ // A child object has changed, so update the change count.
+ ++this.changeCount_;
+
+ return this.changeCount_;
+};
+
+Interest.prototype.setDefaultWireEncoding = function
+ (defaultWireEncoding, defaultWireEncodingFormat)
+{
+ this.defaultWireEncoding_ = defaultWireEncoding;
+ this.defaultWireEncodingFormat_ = defaultWireEncodingFormat;
+ // Set getDefaultWireEncodingChangeCount_ so that the next call to
+ // getDefaultWireEncoding() won't clear _defaultWireEncoding.
+ this.getDefaultWireEncodingChangeCount_ = this.getChangeCount();
+};
+
+// Define properties so we can change member variable types and implement changeCount_.
+Object.defineProperty(Interest.prototype, "name",
+ { get: function() { return this.getName(); },
+ set: function(val) { this.setName(val); } });
+Object.defineProperty(Interest.prototype, "minSuffixComponents",
+ { get: function() { return this.getMinSuffixComponents(); },
+ set: function(val) { this.setMinSuffixComponents(val); } });
+Object.defineProperty(Interest.prototype, "maxSuffixComponents",
+ { get: function() { return this.getMaxSuffixComponents(); },
+ set: function(val) { this.setMaxSuffixComponents(val); } });
+Object.defineProperty(Interest.prototype, "keyLocator",
+ { get: function() { return this.getKeyLocator(); },
+ set: function(val) { this.setKeyLocator(val); } });
+Object.defineProperty(Interest.prototype, "exclude",
+ { get: function() { return this.getExclude(); },
+ set: function(val) { this.setExclude(val); } });
+Object.defineProperty(Interest.prototype, "childSelector",
+ { get: function() { return this.getChildSelector(); },
+ set: function(val) { this.setChildSelector(val); } });
+/**
+ * @deprecated Use getInterestLifetimeMilliseconds and setInterestLifetimeMilliseconds.
+ */
+Object.defineProperty(Interest.prototype, "interestLifetime",
+ { get: function() { return this.getInterestLifetimeMilliseconds(); },
+ set: function(val) { this.setInterestLifetimeMilliseconds(val); } });
+/**
+ * @deprecated Use getNonce and setNonce.
+ */
+Object.defineProperty(Interest.prototype, "nonce",
+ { get: function() { return this.getNonceAsBuffer(); },
+ set: function(val) { this.setNonce(val); } });
+/**
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * A ForwardingFlags object holds the flags which specify how the forwarding daemon should forward an interest for
+ * a registered prefix. We use a separate ForwardingFlags object to retain future compatibility if the daemon forwarding
+ * bits are changed, amended or deprecated.
+ * Create a new ForwardingFlags with "childInherit" set and all other flags cleared.
+ * @constructor
+ */
+var ForwardingFlags = function ForwardingFlags(value)
+{
+ if (typeof value === 'object' && value instanceof ForwardingFlags) {
+ // Make a copy.
+ this.childInherit = value.childInherit;
+ this.capture = value.capture;
+ }
+ else {
+ this.childInherit = true;
+ this.capture = false;
+ }
+};
+
+exports.ForwardingFlags = ForwardingFlags;
+
+ForwardingFlags.NfdForwardingFlags_CHILD_INHERIT = 1;
+ForwardingFlags.NfdForwardingFlags_CAPTURE = 2;
+
+/**
+ * Get an integer with the bits set according to the NFD forwarding flags as
+ * used in the ControlParameters of the command interest.
+ * @return {number} An integer with the bits set.
+ */
+ForwardingFlags.prototype.getNfdForwardingFlags = function()
+{
+ var result = 0;
+
+ if (this.childInherit)
+ result |= ForwardingFlags.NfdForwardingFlags_CHILD_INHERIT;
+ if (this.capture)
+ result |= ForwardingFlags.NfdForwardingFlags_CAPTURE;
+
+ return result;
+};
+
+/**
+ * Set the flags according to the NFD forwarding flags as used in the
+ * ControlParameters of the command interest.
+ * @param {number} nfdForwardingFlags An integer with the bits set.
+ */
+ForwardingFlags.prototype.setNfdForwardingFlags = function(nfdForwardingFlags)
+{
+ this.childInherit =
+ ((nfdForwardingFlags & ForwardingFlags.NfdForwardingFlags_CHILD_INHERIT) != 0);
+ this.capture =
+ ((nfdForwardingFlags & ForwardingFlags.NfdForwardingFlags_CAPTURE) != 0);
+};
+
+/**
+ * Get the value of the "childInherit" flag.
+ * @return {Boolean} true if the flag is set, false if it is cleared.
+ */
+ForwardingFlags.prototype.getChildInherit = function() { return this.childInherit; };
+
+/**
+ * Get the value of the "capture" flag.
+ * @return {Boolean} true if the flag is set, false if it is cleared.
+ */
+ForwardingFlags.prototype.getCapture = function() { return this.capture; };
+
+/**
+ * Set the value of the "childInherit" flag
+ * @param {number} value true to set the flag, false to clear it.
+ */
+ForwardingFlags.prototype.setChildInherit = function(value) { this.childInherit = value; };
+
+/**
+ * Set the value of the "capture" flag
+ * @param {number} value true to set the flag, false to clear it.
+ */
+ForwardingFlags.prototype.setCapture = function(value) { this.capture = value; };
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var ForwardingFlags = require('./forwarding-flags.js').ForwardingFlags; /** @ignore */
+var Name = require('./name.js').Name; /** @ignore */
+var WireFormat = require('./encoding/wire-format.js').WireFormat; /** @ignore */
+var Blob = require('./util/blob.js').Blob;
+
+/**
+ * A ControlParameters which holds a Name and other fields for a
+ * ControlParameters which is used, for example, in the command interest to
+ * register a prefix with a forwarder. See
+ * http://redmine.named-data.net/projects/nfd/wiki/ControlCommand#ControlParameters
+ * @constructor
+ */
+var ControlParameters = function ControlParameters(value)
+{
+ if (typeof value === 'object' && value instanceof ControlParameters) {
+ // Make a deep copy.
+ this.name = value.name == null ? null : new Name(value.name);
+ this.faceId = value.faceId;
+ this.uri = value.uri;
+ this.localControlFeature = value.localControlFeature;
+ this.origin = value.origin;
+ this.cost = value.cost;
+ this.forwardingFlags = new ForwardingFlags(value.forwardingFlags);
+ this.strategy = new Name(value.strategy);
+ this.expirationPeriod = value.expirationPeriod;
+ }
+ else {
+ this.name = null;
+ this.faceId = null;
+ this.uri = '';
+ this.localControlFeature = null;
+ this.origin = null;
+ this.cost = null;
+ this.forwardingFlags = new ForwardingFlags();
+ this.strategy = new Name();
+ this.expirationPeriod = null;
+ }
+};
+
+exports.ControlParameters = ControlParameters;
+
+ControlParameters.prototype.clear = function()
+{
+ this.name = null;
+ this.faceId = null;
+ this.uri = '';
+ this.localControlFeature = null;
+ this.origin = null;
+ this.cost = null;
+ this.forwardingFlags = new ForwardingFlags();
+ this.strategy = new Name();
+ this.expirationPeriod = null;
+};
+
+/**
+ * Encode this ControlParameters for a particular wire format.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {Blob} The encoded buffer in a Blob object.
+ */
+ControlParameters.prototype.wireEncode = function(wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ return wireFormat.encodeControlParameters(this);
+};
+
+/**
+ * Decode the input using a particular wire format and update this
+ * ControlParameters.
+ * @param {Blob|Buffer} input The buffer with the bytes to decode.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to decode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ */
+ControlParameters.prototype.wireDecode = function(input, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ if (typeof input === 'object' && input instanceof Blob)
+ // Input is a blob, so get its buf() and set copy false.
+ wireFormat.decodeControlParameters(this, input.buf(), false);
+ else
+ wireFormat.decodeControlParameters(this, input, true);
+};
+
+/**
+ * Get the name.
+ * @return {Name} The name. If not specified, return null.
+ */
+ControlParameters.prototype.getName = function()
+{
+ return this.name;
+};
+
+/**
+ * Get the face ID.
+ * @return {number} The face ID, or null if not specified.
+ */
+ControlParameters.prototype.getFaceId = function()
+{
+ return this.faceId;
+};
+
+/**
+ * Get the URI.
+ * @return {string} The face URI, or an empty string if not specified.
+ */
+ControlParameters.prototype.getUri = function()
+{
+ return this.uri;
+};
+
+/**
+ * Get the local control feature value.
+ * @return {number} The local control feature value, or null if not specified.
+ */
+ControlParameters.prototype.getLocalControlFeature = function()
+{
+ return this.localControlFeature;
+};
+
+/**
+ * Get the origin value.
+ * @return {number} The origin value, or null if not specified.
+ */
+ControlParameters.prototype.getOrigin = function()
+{
+ return this.origin;
+};
+
+/**
+ * Get the cost value.
+ * @return {number} The cost value, or null if not specified.
+ */
+ControlParameters.prototype.getCost = function()
+{
+ return this.cost;
+};
+
+/**
+ * Get the ForwardingFlags object.
+ * @return {ForwardingFlags} The ForwardingFlags object.
+ */
+ControlParameters.prototype.getForwardingFlags = function()
+{
+ return this.forwardingFlags;
+};
+
+/**
+ * Get the strategy.
+ * @return {Name} The strategy or an empty Name
+ */
+ControlParameters.prototype.getStrategy = function()
+{
+ return this.strategy;
+};
+
+/**
+ * Get the expiration period.
+ * @return {number} The expiration period in milliseconds, or null if not specified.
+ */
+ControlParameters.prototype.getExpirationPeriod = function()
+{
+ return this.expirationPeriod;
+};
+
+/**
+ * Set the name.
+ * @param {Name} name The name. If not specified, set to null. If specified, this
+ * makes a copy of the name.
+ */
+ControlParameters.prototype.setName = function(name)
+{
+ this.name = typeof name === 'object' && name instanceof Name ?
+ new Name(name) : null;
+};
+
+/**
+ * Set the Face ID.
+ * @param {number} faceId The new face ID, or null for not specified.
+ */
+ControlParameters.prototype.setFaceId = function(faceId)
+{
+ this.faceId = faceId;
+};
+
+/**
+ * Set the URI.
+ * @param {string} uri The new uri, or an empty string for not specified.
+ */
+ControlParameters.prototype.setUri = function(uri)
+{
+ this.uri = uri || '';
+};
+
+/**
+ * Set the local control feature value.
+ * @param {number} localControlFeature The new local control feature value, or
+ * null for not specified.
+ */
+ControlParameters.prototype.setLocalControlFeature = function(localControlFeature)
+{
+ this.localControlFeature = localControlFeature;
+};
+
+/**
+ * Set the origin value.
+ * @param {number} origin The new origin value, or null for not specified.
+ */
+ControlParameters.prototype.setOrigin = function(origin)
+{
+ this.origin = origin;
+};
+
+/**
+ * Set the cost value.
+ * @param {number} cost The new cost value, or null for not specified.
+ */
+ControlParameters.prototype.setCost = function(cost)
+{
+ this.cost = cost;
+};
+
+/**
+ * Set the ForwardingFlags object to a copy of forwardingFlags. You can use
+ * getForwardingFlags() and change the existing ForwardingFlags object.
+ * @param {ForwardingFlags} forwardingFlags The new cost value, or null for not specified.
+ */
+ControlParameters.prototype.setForwardingFlags = function(forwardingFlags)
+{
+ this.forwardingFlags =
+ typeof forwardingFlags === 'object' && forwardingFlags instanceof ForwardingFlags ?
+ new ForwardingFlags(forwardingFlags) : new ForwardingFlags();
+};
+
+/**
+ * Set the strategy to a copy of the given Name.
+ * @param {Name} strategy The Name to copy, or an empty Name if not specified.
+ */
+ControlParameters.prototype.setStrategy = function(strategy)
+{
+ this.strategy = typeof strategy === 'object' && strategy instanceof Name ?
+ new Name(strategy) : new Name();
+};
+
+/**
+ * Set the expiration period.
+ * @param {number} expirationPeriod The expiration period in milliseconds, or
+ * null for not specified.
+ */
+ControlParameters.prototype.setExpirationPeriod = function(expirationPeriod)
+{
+ this.expirationPeriod = expirationPeriod;
+};
+/**
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var ControlParameters = require('./control-parameters.js').ControlParameters; /** @ignore */
+var WireFormat = require('./encoding/wire-format.js').WireFormat; /** @ignore */
+var Blob = require('./util/blob.js').Blob;
+
+/**
+ * A ControlResponse holds a status code, status text and other fields for a
+ * ControlResponse which is used, for example, in the response from sending a
+ * register prefix control command to a forwarder.
+ * @see http://redmine.named-data.net/projects/nfd/wiki/ControlCommand
+ * @constructor
+ */
+var ControlResponse = function ControlResponse(value)
+{
+ if (typeof value === 'object' && value instanceof ControlResponse) {
+ // Make a deep copy.
+ this.statusCode_ = value.statusCode_;
+ this.statusText_ = value.statusText_;
+ this.bodyAsControlParameters_ = value.bodyAsControlParameters_ == null ? null
+ : new ControlParameters(value.bodyAsControlParameters_);
+ }
+ else {
+ this.statusCode_ = null;
+ this.statusText_ = "";
+ this.bodyAsControlParameters_ = null;
+ }
+};
+
+exports.ControlResponse = ControlResponse;
+
+ControlResponse.prototype.clear = function()
+{
+ this.statusCode_ = null;
+ this.statusText_ = "";
+ this.bodyAsControlParameters_ = null;
+};
+
+/**
+ * Encode this ControlResponse for a particular wire format.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {Blob} The encoded buffer in a Blob object.
+ */
+ControlResponse.prototype.wireEncode = function(wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ return wireFormat.encodeControlResponse(this);
+};
+
+/**
+ * Decode the input using a particular wire format and update this
+ * ControlResponse.
+ * @param {Blob|Buffer} input The buffer with the bytes to decode.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to decode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ */
+ControlResponse.prototype.wireDecode = function(input, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ if (typeof input === 'object' && input instanceof Blob)
+ // Input is a blob, so get its buf() and set copy false.
+ wireFormat.decodeControlResponse(this, input.buf(), false);
+ else
+ wireFormat.decodeControlResponse(this, input, true);
+};
+
+/**
+ * Get the status code.
+ * @return {number} The status code. If not specified, return null.
+ */
+ControlResponse.prototype.getStatusCode = function()
+{
+ return this.statusCode_;
+};
+
+/**
+ * Get the status text.
+ * @return {string} The status text. If not specified, return "".
+ */
+ControlResponse.prototype.getStatusText = function()
+{
+ return this.statusText_;
+};
+
+/**
+ * Get the control response body as a ControlParameters.
+ * @return {ControlParameters} The ControlParameters, or null if the body is not
+ * specified or if it is not a ControlParameters.
+ */
+ControlResponse.prototype.getBodyAsControlParameters = function()
+{
+ return this.bodyAsControlParameters_;
+};
+
+/**
+ * Set the status code.
+ * @param statusCode {number} The status code. If not specified, set to null.
+ * @return {ControlResponse} This ControlResponse so that you can chain calls to
+ * update values.
+ */
+ControlResponse.prototype.setStatusCode = function(statusCode)
+{
+ this.statusCode_ = statusCode;
+ return this;
+};
+
+/**
+ * Set the status text.
+ * @param statusText {string} The status text. If not specified, set to "".
+ * @return {ControlResponse} This ControlResponse so that you can chain calls to
+ * update values.
+ */
+ControlResponse.prototype.setStatusText = function(statusText)
+{
+ this.statusText_ = statusText || "";
+ return this;
+};
+
+/**
+ * Set the control response body as a ControlParameters.
+ * @param {ControlParameters} controlParameters The ControlParameters for the
+ * body. This makes a copy of the ControlParameters. If not specified or if the
+ * body is not a ControlParameters, set to null.
+ * @return {ControlResponse} This ControlResponse so that you can chain calls to
+ * update values.
+ */
+ControlResponse.prototype.setBodyAsControlParameters = function(controlParameters)
+{
+ this.bodyAsControlParameters_ =
+ typeof controlParameters === 'object' && controlParameters instanceof ControlParameters ?
+ new ControlParameters(controlParameters) : null;
+ return this;
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Name = require('./name.js').Name; /** @ignore */
+var NdnRegexMatcher = require('./util/ndn-regex-matcher.js').NdnRegexMatcher;
+
+/**
+ * An InterestFilter holds a Name prefix and optional regex match expression for
+ * use in Face.setInterestFilter.
+ *
+ * Create an InterestFilter to match any Interest whose name starts with the
+ * given prefix. If the optional regexFilter is provided then the remaining
+ * components match the regexFilter regular expression as described in doesMatch.
+ * @param {InterestFilter|Name|string} prefix If prefix is another
+ * InterestFilter copy its values. If prefix is a Name then this makes a copy of
+ * the Name. Otherwise this creates a Name from the URI string.
+ * @param {string} regexFilter (optional) The regular expression for matching
+ * the remaining name components.
+ * @constructor
+ */
+var InterestFilter = function InterestFilter(prefix, regexFilter)
+{
+ if (typeof prefix === 'object' && prefix instanceof InterestFilter) {
+ // The copy constructor.
+ var interestFilter = prefix;
+ this.prefix = new Name(interestFilter.prefix);
+ this.regexFilter = interestFilter.regexFilter;
+ this.regexFilterPattern = interestFilter.regexFilterPattern;
+ }
+ else {
+ this.prefix = new Name(prefix);
+ if (regexFilter) {
+ this.regexFilter = regexFilter;
+ this.regexFilterPattern = InterestFilter.makePattern(regexFilter);
+ }
+ else {
+ this.regexFilter = null;
+ this.regexFilterPattern = null;
+ }
+ }
+};
+
+exports.InterestFilter = InterestFilter;
+
+/**
+ * Check if the given name matches this filter. Match if name starts with this
+ * filter's prefix. If this filter has the optional regexFilter then the
+ * remaining components match the regexFilter regular expression.
+ * For example, the following InterestFilter:
+ *
+ * InterestFilter("/hello", "<world><>+")
+ *
+ * will match all Interests, whose name has the prefix `/hello` which is
+ * followed by a component `world` and has at least one more component after it.
+ * Examples:
+ *
+ * /hello/world/!
+ * /hello/world/x/y/z
+ *
+ * Note that the regular expression will need to match all remaining components
+ * (e.g., there are implicit heading `^` and trailing `$` symbols in the
+ * regular expression).
+ * @param {Name} name The name to check against this filter.
+ * @return {boolean} True if name matches this filter, otherwise false.
+ */
+InterestFilter.prototype.doesMatch = function(name)
+{
+ if (name.size() < this.prefix.size())
+ return false;
+
+ if (this.hasRegexFilter()) {
+ // Perform a prefix match and regular expression match for the remaining
+ // components.
+ if (!this.prefix.match(name))
+ return false;
+
+ return null != NdnRegexMatcher.match
+ (this.regexFilterPattern, name.getSubName(this.prefix.size()));
+ }
+ else
+ // Just perform a prefix match.
+ return this.prefix.match(name);
+};
+
+/**
+ * Get the prefix given to the constructor.
+ * @return {Name} The prefix Name which you should not modify.
+ */
+InterestFilter.prototype.getPrefix = function() { return this.prefix; };
+
+/**
+ * Check if a regexFilter was supplied to the constructor.
+ * @return {boolean} True if a regexFilter was supplied to the constructor.
+ */
+InterestFilter.prototype.hasRegexFilter = function()
+{
+ return this.regexFilter != null;
+};
+
+/**
+ * Get the regex filter. This is only valid if hasRegexFilter() is true.
+ * @return {string} The regular expression for matching the remaining name
+ * components.
+ */
+InterestFilter.prototype.getRegexFilter = function() { return this.regexFilter; };
+
+/**
+ * If regexFilter doesn't already have them, add ^ to the beginning and $ to
+ * the end since these are required by NdnRegexMatcher.match.
+ * @param {string} regexFilter The regex filter.
+ * @return {string} The regex pattern with ^ and $.
+ */
+InterestFilter.makePattern = function(regexFilter)
+{
+ if (regexFilter.length == 0)
+ // We don't expect this.
+ return "^$";
+
+ var pattern = regexFilter;
+ if (pattern[0] != '^')
+ pattern = "^" + pattern;
+ if (pattern[pattern.length - 1] != '$')
+ pattern = pattern + "$";
+
+ return pattern;
+};
+/**
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Name = require('./name.js').Name; /** @ignore */
+var Blob = require('./util/blob.js').Blob; /** @ignore */
+var WireFormat = require('./encoding/wire-format.js').WireFormat;
+
+/**
+ * A DelegationSet holds a list of DelegationSet.Delegation entries which is
+ * used as the content of a Link instance. If you add elements with add(), then
+ * the list is a set sorted by preference number then by name. But wireDecode
+ * will add the elements from the wire encoding, preserving the given order and
+ * possible duplicates (in which case a DelegationSet really holds a "list" and
+ * not necessarily a "set").
+ *
+ * Create a new DelegationSet object, possibly copying values from another
+ * object.
+ *
+ * @param {DelegationSet} value (optional) If value is a DelegationSet, copy its
+ * values.
+ * @constructor
+ */
+var DelegationSet = function DelegationSet(value)
+{
+ if (typeof value === 'object' && value instanceof DelegationSet)
+ // Copy the list.
+ this.delegations_ = value.delegations_.slice(0);
+ else
+ this.delegations_ = []; // of DelegationSet.Delegation.
+};
+
+exports.DelegationSet = DelegationSet;
+
+/**
+ * A DelegationSet.Delegation holds a preference number and delegation name.
+ * Create a new DelegationSet.Delegation with the given values.
+ * @param {number} preference The preference number.
+ * @param {Name} name The delegation name. This makes a copy of the name.
+ * @constructor
+ */
+DelegationSet.Delegation = function DelegationSetDelegation(preference, name)
+{
+ this.preference_ = preference;
+ this.name_ = new Name(name);
+};
+
+/**
+ * Get the preference number.
+ * @return {number} The preference number.
+ */
+DelegationSet.Delegation.prototype.getPreference = function()
+{
+ return this.preference_;
+};
+
+/**
+ * Get the delegation name.
+ * @return {Name} The delegation name. NOTE: You must not change the name object -
+ * if you need to change it then make a copy.
+ */
+DelegationSet.Delegation.prototype.getName = function()
+{
+ return this.name_;
+};
+
+/**
+ * Compare this Delegation with other according to the ordering, based first
+ * on the preference number, then on the delegation name.
+ * @param {DelegationSet.Delegation} other The other Delegation to compare with.
+ * @return {number} 0 If they compare equal, -1 if this Delegation comes before
+ * other in the ordering, or 1 if this Delegation comes after.
+ */
+DelegationSet.Delegation.prototype.compare = function(other)
+{
+ if (this.preference_ < other.preference_)
+ return -1;
+ if (this.preference_ > other.preference_)
+ return 1;
+
+ return this.name_.compare(other.name_);
+};
+
+/**
+ * Add a new DelegationSet.Delegation to the list of delegations, sorted by
+ * preference number then by name. If there is already a delegation with the
+ * same name, update its preference, and remove any extra delegations with the
+ * same name.
+ * @param {number} preference The preference number.
+ * @param {Name} name The delegation name. This makes a copy of the name.
+ */
+DelegationSet.prototype.add = function(preference, name)
+{
+ this.remove(name);
+
+ var newDelegation = new DelegationSet.Delegation(preference, name);
+ // Find the index of the first entry where it is not less than newDelegation.
+ var i = 0;
+ while (i < this.delegations_.length) {
+ if (this.delegations_[i].compare(newDelegation) >= 0)
+ break;
+
+ ++i;
+ }
+
+ this.delegations_.splice(i, 0, newDelegation);
+};
+
+/**
+ * Add a new DelegationSet.Delegation to the end of the list of delegations,
+ * without sorting or updating any existing entries. This is useful for adding
+ * preferences from a wire encoding, preserving the supplied ordering and
+ * possible duplicates.
+ * @param {number} preference The preference number.
+ * @param {Name} name The delegation name. This makes a copy of the name.
+ */
+DelegationSet.prototype.addUnsorted = function(preference, name)
+{
+ this.delegations_.push(new DelegationSet.Delegation(preference, name));
+};
+
+/**
+ * Remove every DelegationSet.Delegation with the given name.
+ * @param {Name} name The name to match the name of the delegation(s) to be
+ * removed.
+ * @return {boolean} True if a DelegationSet.Delegation was removed, otherwise
+ * false.
+ */
+DelegationSet.prototype.remove = function(name)
+{
+ var wasRemoved = false;
+ // Go backwards through the list so we can remove entries.
+ for (var i = this.delegations_.length - 1; i >= 0; --i) {
+ if (this.delegations_[i].getName().equals(name)) {
+ wasRemoved = true;
+ this.delegations_.splice(i, 1);
+ }
+ }
+
+ return wasRemoved;
+};
+
+/**
+ * Clear the list of delegations.
+ */
+DelegationSet.prototype.clear = function() { this.delegations_ = []; };
+
+/**
+ * Get the number of delegation entries.
+ * @return {number} The number of delegation entries.
+ */
+DelegationSet.prototype.size = function() { return this.delegations_.length; };
+
+/**
+ * Get the delegation at the given index, according to the ordering described
+ * in add().
+ * @param {number} i The index of the component, starting from 0.
+ * @return {DelegationSet.Delegation} The delegation at the index.
+ */
+DelegationSet.prototype.get = function(i) { return this.delegations_[i]; };
+
+/**
+ * Find the first delegation with the given name and return its index.
+ * @param {Name} name Then name of the delegation to find.
+ * @return {number} The index of the delegation, or -1 if not found.
+ */
+DelegationSet.prototype.find = function(name)
+{
+ for (var i = 0; i < this.delegations_.length; ++i) {
+ if (this.delegations_[i].getName().equals(name))
+ return i;
+ }
+
+ return -1;
+};
+
+/**
+ * Encode this DelegationSet for a particular wire format.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {Blob} The encoded buffer in a Blob object.
+ */
+DelegationSet.prototype.wireEncode = function(wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ return wireFormat.encodeDelegationSet(this);
+};
+
+/**
+ * Decode the input using a particular wire format and update this DelegationSet.
+ * @param {Blob|Buffer} input The buffer with the bytes to decode.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to decode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ */
+DelegationSet.prototype.wireDecode = function(input, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ if (typeof input === 'object' && input instanceof Blob)
+ // Input is a blob, so get its buf() and set copy false.
+ wireFormat.decodeDelegationSet(this, input.buf(), false);
+ else
+ wireFormat.decodeDelegationSet(this, input, true);
+};
+/**
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var DelegationSet = require('./delegation-set.js').DelegationSet; /** @ignore */
+var ContentType = require('./meta-info.js').ContentType; /** @ignore */
+var WireFormat = require('./encoding/wire-format.js').WireFormat; /** @ignore */
+var Data = require('./data.js').Data;
+
+/**
+ * The Link class extends Data and represents a Link instance where the Data
+ * content is an encoded delegation set. The format is defined in "link.pdf"
+ * attached to Redmine issue http://redmine.named-data.net/issues/2587 .
+ *
+ * Create a new Link with the optional values. There are 3 forms of the constructor:
+ * Link(name);
+ * Link(data);
+ * Link();
+ * @param {Name} name The name for constructing the base Data.
+ * @param {Data} data The Data object to copy values from. If the content can be
+ * decoded using the default wire encoding, then update the list of delegations.
+ * @constructor
+ */
+var Link = function Link(value)
+{
+ this.delegations_ = new DelegationSet();
+
+ if (value instanceof Data) {
+ // Call the base constructor.
+ Data.call(this, value);
+
+ if (!this.getContent().isNull()) {
+ try {
+ this.delegations_.wireDecode(this.getContent());
+ this.getMetaInfo().setType(ContentType.LINK);
+ }
+ catch (ex) {
+ this.delegations_.clear();
+ }
+ }
+ }
+ else {
+ if (value != undefined)
+ // value is a Name.
+ Data.call(this, value);
+ else
+ Data.call(this);
+
+ this.getMetaInfo().setType(ContentType.LINK);
+ }
+};
+
+Link.prototype = new Data();
+Link.prototype.name = "Link";
+
+exports.Link = Link;
+
+/**
+ * Override to call the base class wireDecode then populate the list of
+ * delegations from the content.
+ * @param {Blob|Buffer} input The buffer with the bytes to decode.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to decode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ */
+Link.prototype.wireDecode = function(input, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ Data.prototype.wireDecode.call(this, input, wireFormat);
+ if (this.getMetaInfo().getType() != ContentType.LINK)
+ throw new Error
+ ("Link.wireDecode: MetaInfo ContentType is not LINK.");
+
+ this.delegations_.wireDecode(this.getContent());
+};
+
+/**
+ * Add a new delegation to the list of delegations, sorted by preference number
+ * then by name. Re-encode this object's content using the optional wireFormat.
+ * @param {number} preference The preference number.
+ * @param {Name} name The delegation name. This makes a copy of the name. If
+ * there is already a delegation with the same name, this updates its preference.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the DelegationSet. If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {Link} This Link so that you can chain calls to update values.
+ */
+Link.prototype.addDelegation = function(preference, name, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ this.delegations_.add(preference, name);
+ this.encodeContent(wireFormat);
+
+ return this;
+};
+
+/**
+ * Remove every delegation with the given name. Re-encode this object's content
+ * using the optional wireFormat.
+ * @param {Name} name Then name to match the name of the delegation(s) to be
+ * removed.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the DelegationSet. If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {boolean} True if a delegation was removed, otherwise false.
+ */
+Link.prototype.removeDelegation = function(name, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ var wasRemoved = this.delegations_.remove(name);
+ if (wasRemoved)
+ this.encodeContent(wireFormat);
+
+ return wasRemoved;
+};
+
+/**
+ * Get the list of delegation for read only.
+ * @return {DelegationSet} The list of delegation, which you should treat as
+ * read-only. To modify it, call Link.addDelegation, etc.
+ */
+Link.prototype.getDelegations = function() { return this.delegations_; };
+
+/**
+ * A private method to encode the delegations_ and set this object's content.
+ * Also set the meta info content type to LINK.
+ * @param {WireFormat} wireFormat A WireFormat object used to encode the
+ * DelegationSet.
+ */
+Link.prototype.encodeContent = function(wireFormat)
+{
+ this.setContent(this.delegations_.wireEncode(wireFormat));
+ this.getMetaInfo().setType(ContentType.LINK);
+};
+/**
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-cxx nack.hpp https://github.com/named-data/ndn-cxx/blob/master/src/lp/nack.hpp
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * NetworkNack represents a network Nack packet and includes a Nack reason.
+ * @constructor
+ */
+var NetworkNack = function NetworkNack()
+{
+ this.reason_ = NetworkNack.Reason.NONE;
+ this.otherReasonCode_ = -1;
+};
+
+exports.NetworkNack = NetworkNack;
+
+/**
+ * A NetworkNack.Reason specifies the reason in a NetworkNack packet. If the
+ * reason code in the packet is not a recognized enum value, then we use
+ * Reason.OTHER_CODE and you can call getOtherReasonCode(). We do this to keep
+ * the recognized reason values independent of packet encoding formats.
+ */
+NetworkNack.Reason = {
+ NONE: 0,
+ CONGESTION: 50,
+ DUPLICATE: 100,
+ NO_ROUTE: 150,
+ OTHER_CODE: 0x7fff
+};
+
+/**
+ * Get the network Nack reason.
+ * @return {number} The reason enum value from NetworkNack.Reason. If this is
+ * Reason.OTHER_CODE, then call getOtherReasonCode() to get the unrecognized
+ * reason code.
+ */
+NetworkNack.prototype.getReason = function() { return this.reason_; };
+
+/**
+ * Get the reason code from the packet which is other than a recognized
+ * Reason enum value. This is only meaningful if getReason() is
+ * Reason.OTHER_CODE.
+ * @return {number} The reason code.
+ */
+NetworkNack.prototype.getOtherReasonCode = function()
+{
+ return this.otherReasonCode_;
+};
+
+/**
+ * Set the network Nack reason.
+ * @param {number} reason The network Nack reason enum value from
+ * NetworkNack.Reason. If the packet's reason code is not a recognized Reason
+ * enum value, use Reason.OTHER_CODE and call setOtherReasonCode().
+ */
+NetworkNack.prototype.setReason = function(reason) { this.reason_ = reason; };
+
+/**
+ * Set the packet's reason code to use when the reason enum is
+ * Reason.OTHER_CODE. If the packet's reason code is a recognized enum value,
+ * just call setReason().
+ * @param {number} otherReasonCode The packet's unrecognized reason code, which
+ * must be non-negative.
+ */
+NetworkNack.prototype.setOtherReasonCode = function(otherReasonCode)
+{
+ if (otherReasonCode < 0)
+ throw new Error("NetworkNack other reason code must be non-negative");
+ this.otherReasonCode_ = otherReasonCode;
+};
+
+/**
+ * Get the first header field in lpPacket which is a NetworkNack. This is
+ * an internal method which the application normally would not use.
+ * @param {LpPacket} lpPacket The LpPacket with the header fields to search.
+ * @return {NetworkNack} The first NetworkNack header field, or null if not
+ * found.
+ */
+NetworkNack.getFirstHeader = function(lpPacket)
+{
+ for (var i = 0; i < lpPacket.countHeaderFields(); ++i) {
+ var field = lpPacket.getHeaderField(i);
+ if (field instanceof NetworkNack)
+ return field;
+ }
+
+ return null;
+};
+/**
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Crypto = require('../crypto.js'); /** @ignore */
+var Blob = require('../util/blob.js').Blob; /** @ignore */
+var Name = require('../name.js').Name; /** @ignore */
+var ForwardingFlags = require('../forwarding-flags').ForwardingFlags; /** @ignore */
+var Tlv = require('./tlv/tlv.js').Tlv; /** @ignore */
+var TlvEncoder = require('./tlv/tlv-encoder.js').TlvEncoder; /** @ignore */
+var TlvDecoder = require('./tlv/tlv-decoder.js').TlvDecoder; /** @ignore */
+var WireFormat = require('./wire-format.js').WireFormat; /** @ignore */
+var Exclude = require('../exclude.js').Exclude; /** @ignore */
+var ContentType = require('../meta-info.js').ContentType; /** @ignore */
+var KeyLocatorType = require('../key-locator.js').KeyLocatorType; /** @ignore */
+var Sha256WithRsaSignature = require('../sha256-with-rsa-signature.js').Sha256WithRsaSignature; /** @ignore */
+var Sha256WithEcdsaSignature = require('../sha256-with-ecdsa-signature.js').Sha256WithEcdsaSignature; /** @ignore */
+var GenericSignature = require('../generic-signature.js').GenericSignature; /** @ignore */
+var HmacWithSha256Signature = require('../hmac-with-sha256-signature.js').HmacWithSha256Signature; /** @ignore */
+var DigestSha256Signature = require('../digest-sha256-signature.js').DigestSha256Signature; /** @ignore */
+var ControlParameters = require('../control-parameters.js').ControlParameters; /** @ignore */
+var ForwardingFlags = require('../forwarding-flags.js').ForwardingFlags; /** @ignore */
+var NetworkNack = require('../network-nack.js').NetworkNack; /** @ignore */
+var IncomingFaceId = require('../lp/incoming-face-id.js').IncomingFaceId; /** @ignore */
+var DecodingException = require('./decoding-exception.js').DecodingException;
+
+/**
+ * A Tlv0_2WireFormat implements the WireFormat interface for encoding and
+ * decoding with the NDN-TLV wire format, version 0.2.
+ * @constructor
+ */
+var Tlv0_2WireFormat = function Tlv0_2WireFormat()
+{
+ // Inherit from WireFormat.
+ WireFormat.call(this);
+};
+
+Tlv0_2WireFormat.prototype = new WireFormat();
+Tlv0_2WireFormat.prototype.name = "Tlv0_2WireFormat";
+
+exports.Tlv0_2WireFormat = Tlv0_2WireFormat;
+
+// Default object.
+Tlv0_2WireFormat.instance = null;
+
+/**
+ * Encode name as an NDN-TLV Name and return the encoding.
+ * @param {Name} name The Name to encode.
+ * @return {Blobl} A Blob containing the encoding.
+ */
+Tlv0_2WireFormat.prototype.encodeName = function(name)
+{
+ var encoder = new TlvEncoder();
+ Tlv0_2WireFormat.encodeName(name, encoder);
+ return new Blob(encoder.getOutput(), false);
+};
+
+/**
+ * Decode input as a NDN-TLV name and set the fields of the Name object.
+ * @param {Name} name The Name object whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ */
+Tlv0_2WireFormat.prototype.decodeName = function(name, input, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var decoder = new TlvDecoder(input);
+ Tlv0_2WireFormat.decodeName(name, decoder, copy);
+};
+
+/**
+ * Encode the interest using NDN-TLV and return a Buffer.
+ * @param {Interest} interest The Interest object to encode.
+ * @return {object} An associative array with fields
+ * (encoding, signedPortionBeginOffset, signedPortionEndOffset) where encoding
+ * is a Blob containing the encoding, signedPortionBeginOffset is the offset in
+ * the encoding of the beginning of the signed portion, and
+ * signedPortionEndOffset is the offset in the encoding of the end of the signed
+ * portion. The signed portion starts from the first name component and ends
+ * just before the final name component (which is assumed to be a signature for
+ * a signed interest).
+ */
+Tlv0_2WireFormat.prototype.encodeInterest = function(interest)
+{
+ var encoder = new TlvEncoder(256);
+ var saveLength = encoder.getLength();
+
+ // Encode backwards.
+ encoder.writeOptionalNonNegativeIntegerTlv
+ (Tlv.SelectedDelegation, interest.getSelectedDelegationIndex());
+ var linkWireEncoding = interest.getLinkWireEncoding(this);
+ if (!linkWireEncoding.isNull())
+ // Encode the entire link as is.
+ encoder.writeBuffer(linkWireEncoding.buf());
+
+ encoder.writeOptionalNonNegativeIntegerTlv
+ (Tlv.InterestLifetime, interest.getInterestLifetimeMilliseconds());
+
+ // Encode the Nonce as 4 bytes.
+ if (interest.getNonce().isNull() || interest.getNonce().size() == 0)
+ // This is the most common case. Generate a nonce.
+ encoder.writeBlobTlv(Tlv.Nonce, Crypto.randomBytes(4));
+ else if (interest.getNonce().size() < 4) {
+ var nonce = Buffer(4);
+ // Copy existing nonce bytes.
+ interest.getNonce().buf().copy(nonce);
+
+ // Generate random bytes for remaining bytes in the nonce.
+ for (var i = interest.getNonce().size(); i < 4; ++i)
+ nonce[i] = Crypto.randomBytes(1)[0];
+
+ encoder.writeBlobTlv(Tlv.Nonce, nonce);
+ }
+ else if (interest.getNonce().size() == 4)
+ // Use the nonce as-is.
+ encoder.writeBlobTlv(Tlv.Nonce, interest.getNonce().buf());
+ else
+ // Truncate.
+ encoder.writeBlobTlv(Tlv.Nonce, interest.getNonce().buf().slice(0, 4));
+
+ Tlv0_2WireFormat.encodeSelectors(interest, encoder);
+ var tempOffsets = Tlv0_2WireFormat.encodeName(interest.getName(), encoder);
+ var signedPortionBeginOffsetFromBack =
+ encoder.getLength() - tempOffsets.signedPortionBeginOffset;
+ var signedPortionEndOffsetFromBack =
+ encoder.getLength() - tempOffsets.signedPortionEndOffset;
+
+ encoder.writeTypeAndLength(Tlv.Interest, encoder.getLength() - saveLength);
+ var signedPortionBeginOffset =
+ encoder.getLength() - signedPortionBeginOffsetFromBack;
+ var signedPortionEndOffset =
+ encoder.getLength() - signedPortionEndOffsetFromBack;
+
+ return { encoding: new Blob(encoder.getOutput(), false),
+ signedPortionBeginOffset: signedPortionBeginOffset,
+ signedPortionEndOffset: signedPortionEndOffset };
+};
+
+/**
+ * Decode input as an NDN-TLV interest and set the fields of the interest
+ * object.
+ * @param {Interest} interest The Interest object whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @return {object} An associative array with fields
+ * (signedPortionBeginOffset, signedPortionEndOffset) where
+ * signedPortionBeginOffset is the offset in the encoding of the beginning of
+ * the signed portion, and signedPortionEndOffset is the offset in the encoding
+ * of the end of the signed portion. The signed portion starts from the first
+ * name component and ends just before the final name component (which is
+ * assumed to be a signature for a signed interest).
+ */
+Tlv0_2WireFormat.prototype.decodeInterest = function(interest, input, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var decoder = new TlvDecoder(input);
+
+ var endOffset = decoder.readNestedTlvsStart(Tlv.Interest);
+ var offsets = Tlv0_2WireFormat.decodeName(interest.getName(), decoder, copy);
+ if (decoder.peekType(Tlv.Selectors, endOffset))
+ Tlv0_2WireFormat.decodeSelectors(interest, decoder, copy);
+ // Require a Nonce, but don't force it to be 4 bytes.
+ var nonce = decoder.readBlobTlv(Tlv.Nonce);
+ interest.setInterestLifetimeMilliseconds
+ (decoder.readOptionalNonNegativeIntegerTlv(Tlv.InterestLifetime, endOffset));
+
+ if (decoder.peekType(Tlv.Data, endOffset)) {
+ // Get the bytes of the Link TLV.
+ var linkBeginOffset = decoder.getOffset();
+ var linkEndOffset = decoder.readNestedTlvsStart(Tlv.Data);
+ decoder.seek(linkEndOffset);
+
+ interest.setLinkWireEncoding
+ (new Blob(decoder.getSlice(linkBeginOffset, linkEndOffset), copy), this);
+ }
+ else
+ interest.unsetLink();
+ interest.setSelectedDelegationIndex
+ (decoder.readOptionalNonNegativeIntegerTlv(Tlv.SelectedDelegation, endOffset));
+ if (interest.getSelectedDelegationIndex() != null &&
+ interest.getSelectedDelegationIndex() >= 0 && !interest.hasLink())
+ throw new Error("Interest has a selected delegation, but no link object");
+
+ // Set the nonce last because setting other interest fields clears it.
+ interest.setNonce(new Blob(nonce, copy));
+
+ decoder.finishNestedTlvs(endOffset);
+ return offsets;
+};
+
+/**
+ * Encode data as NDN-TLV and return the encoding and signed offsets.
+ * @param {Data} data The Data object to encode.
+ * @return {object} An associative array with fields
+ * (encoding, signedPortionBeginOffset, signedPortionEndOffset) where encoding
+ * is a Blob containing the encoding, signedPortionBeginOffset is the offset in
+ * the encoding of the beginning of the signed portion, and
+ * signedPortionEndOffset is the offset in the encoding of the end of the
+ * signed portion.
+ */
+Tlv0_2WireFormat.prototype.encodeData = function(data)
+{
+ var encoder = new TlvEncoder(1500);
+ var saveLength = encoder.getLength();
+
+ // Encode backwards.
+ encoder.writeBlobTlv(Tlv.SignatureValue, data.getSignature().getSignature().buf());
+ var signedPortionEndOffsetFromBack = encoder.getLength();
+
+ Tlv0_2WireFormat.encodeSignatureInfo_(data.getSignature(), encoder);
+ encoder.writeBlobTlv(Tlv.Content, data.getContent().buf());
+ Tlv0_2WireFormat.encodeMetaInfo(data.getMetaInfo(), encoder);
+ Tlv0_2WireFormat.encodeName(data.getName(), encoder);
+ var signedPortionBeginOffsetFromBack = encoder.getLength();
+
+ encoder.writeTypeAndLength(Tlv.Data, encoder.getLength() - saveLength);
+ var signedPortionBeginOffset =
+ encoder.getLength() - signedPortionBeginOffsetFromBack;
+ var signedPortionEndOffset = encoder.getLength() - signedPortionEndOffsetFromBack;
+
+ return { encoding: new Blob(encoder.getOutput(), false),
+ signedPortionBeginOffset: signedPortionBeginOffset,
+ signedPortionEndOffset: signedPortionEndOffset };
+};
+
+/**
+ * Decode input as an NDN-TLV data packet, set the fields in the data object,
+ * and return the signed offsets.
+ * @param {Data} data The Data object whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @return {object} An associative array with fields
+ * (signedPortionBeginOffset, signedPortionEndOffset) where
+ * signedPortionBeginOffset is the offset in the encoding of the beginning of
+ * the signed portion, and signedPortionEndOffset is the offset in the encoding
+ * of the end of the signed portion.
+ */
+Tlv0_2WireFormat.prototype.decodeData = function(data, input, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var decoder = new TlvDecoder(input);
+
+ var endOffset = decoder.readNestedTlvsStart(Tlv.Data);
+ var signedPortionBeginOffset = decoder.getOffset();
+
+ Tlv0_2WireFormat.decodeName(data.getName(), decoder, copy);
+ Tlv0_2WireFormat.decodeMetaInfo(data.getMetaInfo(), decoder, copy);
+ data.setContent(new Blob(decoder.readBlobTlv(Tlv.Content), copy));
+ Tlv0_2WireFormat.decodeSignatureInfo(data, decoder, copy);
+
+ var signedPortionEndOffset = decoder.getOffset();
+ data.getSignature().setSignature
+ (new Blob(decoder.readBlobTlv(Tlv.SignatureValue), copy));
+
+ decoder.finishNestedTlvs(endOffset);
+ return { signedPortionBeginOffset: signedPortionBeginOffset,
+ signedPortionEndOffset: signedPortionEndOffset };
+};
+
+/**
+ * Encode controlParameters as NDN-TLV and return the encoding.
+ * @param {ControlParameters} controlParameters The ControlParameters object to
+ * encode.
+ * @return {Blob} A Blob containing the encoding.
+ */
+Tlv0_2WireFormat.prototype.encodeControlParameters = function(controlParameters)
+{
+ var encoder = new TlvEncoder(256);
+ Tlv0_2WireFormat.encodeControlParameters(controlParameters, encoder);
+ return new Blob(encoder.getOutput(), false);
+};
+
+/**
+ * Decode controlParameters in NDN-TLV and return the encoding.
+ * @param {ControlParameters} controlParameters The ControlParameters object to
+ * encode.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @throws DecodingException For invalid encoding
+ */
+Tlv0_2WireFormat.prototype.decodeControlParameters = function
+ (controlParameters, input, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var decoder = new TlvDecoder(input);
+ Tlv0_2WireFormat.decodeControlParameters(controlParameters, decoder, copy);
+};
+
+/**
+ * Encode controlResponse as NDN-TLV and return the encoding.
+ * @param {ControlResponse} controlResponse The ControlResponse object to
+ * encode.
+ * @return {Blob} A Blob containing the encoding.
+ */
+Tlv0_2WireFormat.prototype.encodeControlResponse = function(controlResponse)
+{
+ var encoder = new TlvEncoder(256);
+ var saveLength = encoder.getLength();
+
+ // Encode backwards.
+
+ // Encode the body.
+ if (controlResponse.getBodyAsControlParameters() != null)
+ Tlv0_2WireFormat.encodeControlParameters
+ (controlResponse.getBodyAsControlParameters(), encoder);
+
+ encoder.writeBlobTlv
+ (Tlv.NfdCommand_StatusText, new Blob(controlResponse.getStatusText()).buf());
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.NfdCommand_StatusCode, controlResponse.getStatusCode());
+
+ encoder.writeTypeAndLength
+ (Tlv.NfdCommand_ControlResponse, encoder.getLength() - saveLength);
+
+ return new Blob(encoder.getOutput(), false);
+};
+
+/**
+ * Decode controlResponse in NDN-TLV and return the encoding.
+ * @param {ControlResponse} controlResponse The ControlResponse object to
+ * encode.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @throws DecodingException For invalid encoding
+ */
+Tlv0_2WireFormat.prototype.decodeControlResponse = function
+ (controlResponse, input, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var decoder = new TlvDecoder(input);
+ var endOffset = decoder.readNestedTlvsStart(Tlv.NfdCommand_ControlResponse);
+
+ controlResponse.setStatusCode(decoder.readNonNegativeIntegerTlv
+ (Tlv.NfdCommand_StatusCode));
+ // Set copy false since we just immediately get a string.
+ var statusText = new Blob
+ (decoder.readBlobTlv(Tlv.NfdCommand_StatusText), false);
+ controlResponse.setStatusText(statusText.toString());
+
+ // Decode the body.
+ if (decoder.peekType(Tlv.ControlParameters_ControlParameters, endOffset)) {
+ controlResponse.setBodyAsControlParameters(new ControlParameters());
+ // Decode into the existing ControlParameters to avoid copying.
+ Tlv0_2WireFormat.decodeControlParameters
+ (controlResponse.getBodyAsControlParameters(), decoder, copy);
+ }
+ else
+ controlResponse.setBodyAsControlParameters(null);
+
+ decoder.finishNestedTlvs(endOffset);
+};
+
+/**
+ * Encode signature as an NDN-TLV SignatureInfo and return the encoding.
+ * @param {Signature} signature An object of a subclass of Signature to encode.
+ * @return {Blob} A Blob containing the encoding.
+ */
+Tlv0_2WireFormat.prototype.encodeSignatureInfo = function(signature)
+{
+ var encoder = new TlvEncoder(256);
+ Tlv0_2WireFormat.encodeSignatureInfo_(signature, encoder);
+
+ return new Blob(encoder.getOutput(), false);
+};
+
+// SignatureHolder is used by decodeSignatureInfoAndValue.
+Tlv0_2WireFormat.SignatureHolder = function Tlv0_2WireFormatSignatureHolder()
+{
+};
+
+Tlv0_2WireFormat.SignatureHolder.prototype.setSignature = function(signature)
+{
+ this.signature = signature;
+};
+
+Tlv0_2WireFormat.SignatureHolder.prototype.getSignature = function()
+{
+ return this.signature;
+};
+
+/**
+ * Decode signatureInfo as an NDN-TLV SignatureInfo and signatureValue as the
+ * related SignatureValue, and return a new object which is a subclass of Signature.
+ * @param {Buffer} signatureInfo The buffer with the signature info bytes to
+ * decode.
+ * @param {Buffer} signatureValue The buffer with the signature value to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @return {Signature} A new object which is a subclass of Signature.
+ */
+Tlv0_2WireFormat.prototype.decodeSignatureInfoAndValue = function
+ (signatureInfo, signatureValue, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ // Use a SignatureHolder to imitate a Data object for decodeSignatureInfo.
+ var signatureHolder = new Tlv0_2WireFormat.SignatureHolder();
+ var decoder = new TlvDecoder(signatureInfo);
+ Tlv0_2WireFormat.decodeSignatureInfo(signatureHolder, decoder, copy);
+
+ decoder = new TlvDecoder(signatureValue);
+ signatureHolder.getSignature().setSignature
+ (new Blob(decoder.readBlobTlv(Tlv.SignatureValue), copy));
+
+ return signatureHolder.getSignature();
+};
+
+/**
+ * Encode the signatureValue in the Signature object as an NDN-TLV
+ * SignatureValue (the signature bits) and return the encoding.
+ * @param {Signature} signature An object of a subclass of Signature with the
+ * signature value to encode.
+ * @return {Blob} A Blob containing the encoding.
+ */
+Tlv0_2WireFormat.prototype.encodeSignatureValue = function(signature)
+{
+ var encoder = new TlvEncoder(256);
+ encoder.writeBlobTlv(Tlv.SignatureValue, signature.getSignature().buf());
+
+ return new Blob(encoder.getOutput(), false);
+};
+
+/**
+ * Decode input as an NDN-TLV LpPacket and set the fields of the lpPacket object.
+ * @param {LpPacket} lpPacket The LpPacket object whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ */
+Tlv0_2WireFormat.prototype.decodeLpPacket = function(lpPacket, input, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ lpPacket.clear();
+
+ var decoder = new TlvDecoder(input);
+ var endOffset = decoder.readNestedTlvsStart(Tlv.LpPacket_LpPacket);
+
+ while (decoder.getOffset() < endOffset) {
+ // Imitate TlvDecoder.readTypeAndLength.
+ var fieldType = decoder.readVarNumber();
+ var fieldLength = decoder.readVarNumber();
+ var fieldEndOffset = decoder.getOffset() + fieldLength;
+ if (fieldEndOffset > input.length)
+ throw new DecodingException(new Error("TLV length exceeds the buffer length"));
+
+ if (fieldType == Tlv.LpPacket_Fragment) {
+ // Set the fragment to the bytes of the TLV value.
+ lpPacket.setFragmentWireEncoding
+ (new Blob(decoder.getSlice(decoder.getOffset(), fieldEndOffset), copy));
+ decoder.seek(fieldEndOffset);
+
+ // The fragment is supposed to be the last field.
+ break;
+ }
+ else if (fieldType == Tlv.LpPacket_Nack) {
+ var networkNack = new NetworkNack();
+ var code = decoder.readOptionalNonNegativeIntegerTlv
+ (Tlv.LpPacket_NackReason, fieldEndOffset);
+ var reason;
+ // The enum numeric values are the same as this wire format, so use as is.
+ if (code < 0 || code == NetworkNack.Reason.NONE)
+ // This includes an omitted NackReason.
+ networkNack.setReason(NetworkNack.Reason.NONE);
+ else if (code == NetworkNack.Reason.CONGESTION ||
+ code == NetworkNack.Reason.DUPLICATE ||
+ code == NetworkNack.Reason.NO_ROUTE)
+ networkNack.setReason(code);
+ else {
+ // Unrecognized reason.
+ networkNack.setReason(NetworkNack.Reason.OTHER_CODE);
+ networkNack.setOtherReasonCode(code);
+ }
+
+ lpPacket.addHeaderField(networkNack);
+ }
+ else if (fieldType == Tlv.LpPacket_IncomingFaceId) {
+ var incomingFaceId = new IncomingFaceId();
+ incomingFaceId.setFaceId(decoder.readNonNegativeInteger(fieldLength));
+ lpPacket.addHeaderField(incomingFaceId);
+ }
+ else {
+ // Unrecognized field type. The conditions for ignoring are here:
+ // http://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
+ var canIgnore =
+ (fieldType >= Tlv.LpPacket_IGNORE_MIN &&
+ fieldType <= Tlv.LpPacket_IGNORE_MAX &&
+ (fieldType & 0x01) === 1);
+ if (!canIgnore)
+ throw new DecodingException(new Error("Did not get the expected TLV type"));
+
+ // Ignore.
+ decoder.seek(fieldEndOffset);
+ }
+
+ decoder.finishNestedTlvs(fieldEndOffset);
+ }
+
+ decoder.finishNestedTlvs(endOffset);
+};
+
+/**
+ * Encode delegationSet as a sequence of NDN-TLV Delegation, and return the
+ * encoding. Note that the sequence of Delegation does not have an outer TLV
+ * type and length because it is intended to use the type and length of a Data
+ * packet's Content.
+ * @param {DelegationSet} delegationSet The DelegationSet object to encode.
+ * @return {Blob} A Blob containing the encoding.
+ */
+Tlv0_2WireFormat.prototype.encodeDelegationSet = function(delegationSet)
+{
+ var encoder = new TlvEncoder(256);
+
+ // Encode backwards.
+ for (var i = delegationSet.size() - 1; i >= 0; --i) {
+ var saveLength = encoder.getLength();
+
+ Tlv0_2WireFormat.encodeName(delegationSet.get(i).getName(), encoder);
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.Link_Preference, delegationSet.get(i).getPreference());
+
+ encoder.writeTypeAndLength
+ (Tlv.Link_Delegation, encoder.getLength() - saveLength);
+ }
+
+ return new Blob(encoder.getOutput(), false);
+};
+
+/**
+ * Decode input as a sequence of NDN-TLV Delegation and set the fields of the
+ * delegationSet object. Note that the sequence of Delegation does not have an
+ * outer TLV type and length because it is intended to use the type and length
+ * of a Data packet's Content. This ignores any elements after the sequence
+ * of Delegation.
+ * @param {DelegationSet} delegationSet The DelegationSet object
+ * whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ */
+Tlv0_2WireFormat.prototype.decodeDelegationSet = function
+ (delegationSet, input, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var decoder = new TlvDecoder(input);
+ var endOffset = input.length;
+
+ delegationSet.clear();
+ while (decoder.getOffset() < endOffset) {
+ decoder.readTypeAndLength(Tlv.Link_Delegation);
+ var preference = decoder.readNonNegativeIntegerTlv(Tlv.Link_Preference);
+ var name = new Name();
+ Tlv0_2WireFormat.decodeName(name, decoder, copy);
+
+ // Add unsorted to preserve the order so that Interest selected delegation
+ // index will work.
+ delegationSet.addUnsorted(preference, name);
+ }
+};
+
+/**
+ * Encode the EncryptedContent in NDN-TLV and return the encoding.
+ * @param {EncryptedContent} encryptedContent The EncryptedContent object to
+ * encode.
+ * @return {Blob} A Blob containing the encoding.
+ */
+Tlv0_2WireFormat.prototype.encodeEncryptedContent = function(encryptedContent)
+{
+ var encoder = new TlvEncoder(256);
+ var saveLength = encoder.getLength();
+
+ // Encode backwards.
+ encoder.writeBlobTlv
+ (Tlv.Encrypt_EncryptedPayload, encryptedContent.getPayload().buf());
+ encoder.writeOptionalBlobTlv
+ (Tlv.Encrypt_InitialVector, encryptedContent.getInitialVector().buf());
+ // Assume the algorithmType value is the same as the TLV type.
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.Encrypt_EncryptionAlgorithm, encryptedContent.getAlgorithmType());
+ Tlv0_2WireFormat.encodeKeyLocator
+ (Tlv.KeyLocator, encryptedContent.getKeyLocator(), encoder);
+
+ encoder.writeTypeAndLength
+ (Tlv.Encrypt_EncryptedContent, encoder.getLength() - saveLength);
+
+ return new Blob(encoder.getOutput(), false);
+};
+
+/**
+ * Decode input as an EncryptedContent in NDN-TLV and set the fields of the
+ * encryptedContent object.
+ * @param {EncryptedContent} encryptedContent The EncryptedContent object
+ * whose fields are updated.
+ * @param {Buffer} input The buffer with the bytes to decode.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ */
+Tlv0_2WireFormat.prototype.decodeEncryptedContent = function
+ (encryptedContent, input, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var decoder = new TlvDecoder(input);
+ var endOffset = decoder.
+ readNestedTlvsStart(Tlv.Encrypt_EncryptedContent);
+
+ Tlv0_2WireFormat.decodeKeyLocator
+ (Tlv.KeyLocator, encryptedContent.getKeyLocator(), decoder, copy);
+ encryptedContent.setAlgorithmType
+ (decoder.readNonNegativeIntegerTlv(Tlv.Encrypt_EncryptionAlgorithm));
+ encryptedContent.setInitialVector
+ (new Blob(decoder.readOptionalBlobTlv
+ (Tlv.Encrypt_InitialVector, endOffset), copy));
+ encryptedContent.setPayload
+ (new Blob(decoder.readBlobTlv(Tlv.Encrypt_EncryptedPayload), copy));
+
+ decoder.finishNestedTlvs(endOffset);
+};
+
+/**
+ * Get a singleton instance of a Tlv0_2WireFormat. To always use the
+ * preferred version NDN-TLV, you should use TlvWireFormat.get().
+ * @return {Tlv0_2WireFormat} The singleton instance.
+ */
+Tlv0_2WireFormat.get = function()
+{
+ if (Tlv0_2WireFormat.instance === null)
+ Tlv0_2WireFormat.instance = new Tlv0_2WireFormat();
+ return Tlv0_2WireFormat.instance;
+};
+
+/**
+ * Encode the name component to the encoder as NDN-TLV. This handles different
+ * component types such as ImplicitSha256DigestComponent.
+ * @param {Name.Component} component The name component to encode.
+ * @param {TlvEncoder} encoder The encoder to receive the encoding.
+ */
+Tlv0_2WireFormat.encodeNameComponent = function(component, encoder)
+{
+ var type = component.isImplicitSha256Digest() ?
+ Tlv.ImplicitSha256DigestComponent : Tlv.NameComponent;
+ encoder.writeBlobTlv(type, component.getValue().buf());
+};
+
+/**
+ * Decode the name component as NDN-TLV and return the component. This handles
+ * different component types such as ImplicitSha256DigestComponent.
+ * @param {TlvDecoder} decoder The decoder with the input.
+ * @param {boolean} copy (optional) If true, copy from the input when making new
+ * Blob values. If false, then Blob values share memory with the input, which
+ * must remain unchanged while the Blob values are used. If omitted, use true.
+ * @return {Name.Component} A new Name.Component.
+ */
+Tlv0_2WireFormat.decodeNameComponent = function(decoder, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var savePosition = decoder.getOffset();
+ var type = decoder.readVarNumber();
+ // Restore the position.
+ decoder.seek(savePosition);
+
+ var value = new Blob(decoder.readBlobTlv(type), copy);
+ if (type === Tlv.ImplicitSha256DigestComponent)
+ return Name.Component.fromImplicitSha256Digest(value);
+ else
+ return new Name.Component(value);
+};
+
+/**
+ * Encode the name to the encoder.
+ * @param {Name} name The name to encode.
+ * @param {TlvEncoder} encoder The encoder to receive the encoding.
+ * @return {object} An associative array with fields
+ * (signedPortionBeginOffset, signedPortionEndOffset) where
+ * signedPortionBeginOffset is the offset in the encoding of the beginning of
+ * the signed portion, and signedPortionEndOffset is the offset in the encoding
+ * of the end of the signed portion. The signed portion starts from the first
+ * name component and ends just before the final name component (which is
+ * assumed to be a signature for a signed interest).
+ */
+Tlv0_2WireFormat.encodeName = function(name, encoder)
+{
+ var saveLength = encoder.getLength();
+
+ // Encode the components backwards.
+ var signedPortionEndOffsetFromBack;
+ for (var i = name.size() - 1; i >= 0; --i) {
+ Tlv0_2WireFormat.encodeNameComponent(name.get(i), encoder);
+ if (i == name.size() - 1)
+ signedPortionEndOffsetFromBack = encoder.getLength();
+ }
+
+ var signedPortionBeginOffsetFromBack = encoder.getLength();
+ encoder.writeTypeAndLength(Tlv.Name, encoder.getLength() - saveLength);
+
+ var signedPortionBeginOffset =
+ encoder.getLength() - signedPortionBeginOffsetFromBack;
+ var signedPortionEndOffset;
+ if (name.size() == 0)
+ // There is no "final component", so set signedPortionEndOffset arbitrarily.
+ signedPortionEndOffset = signedPortionBeginOffset;
+ else
+ signedPortionEndOffset = encoder.getLength() - signedPortionEndOffsetFromBack;
+
+ return { signedPortionBeginOffset: signedPortionBeginOffset,
+ signedPortionEndOffset: signedPortionEndOffset };
+};
+
+/**
+ * Clear the name, decode a Name from the decoder and set the fields of the name
+ * object.
+ * @param {Name} name The name object whose fields are updated.
+ * @param {TlvDecoder} decoder The decoder with the input.
+ * @return {object} An associative array with fields
+ * (signedPortionBeginOffset, signedPortionEndOffset) where
+ * signedPortionBeginOffset is the offset in the encoding of the beginning of
+ * the signed portion, and signedPortionEndOffset is the offset in the encoding
+ * of the end of the signed portion. The signed portion starts from the first
+ * name component and ends just before the final name component (which is
+ * assumed to be a signature for a signed interest).
+ */
+Tlv0_2WireFormat.decodeName = function(name, decoder, copy)
+{
+ name.clear();
+
+ var endOffset = decoder.readNestedTlvsStart(Tlv.Name);
+ var signedPortionBeginOffset = decoder.getOffset();
+ // In case there are no components, set signedPortionEndOffset arbitrarily.
+ var signedPortionEndOffset = signedPortionBeginOffset;
+
+ while (decoder.getOffset() < endOffset) {
+ signedPortionEndOffset = decoder.getOffset();
+ name.append(Tlv0_2WireFormat.decodeNameComponent(decoder, copy));
+ }
+
+ decoder.finishNestedTlvs(endOffset);
+
+ return { signedPortionBeginOffset: signedPortionBeginOffset,
+ signedPortionEndOffset: signedPortionEndOffset };
+};
+
+/**
+ * Encode the interest selectors. If no selectors are written, do not output a
+ * Selectors TLV.
+ */
+Tlv0_2WireFormat.encodeSelectors = function(interest, encoder)
+{
+ var saveLength = encoder.getLength();
+
+ // Encode backwards.
+ if (interest.getMustBeFresh())
+ encoder.writeTypeAndLength(Tlv.MustBeFresh, 0);
+ encoder.writeOptionalNonNegativeIntegerTlv(
+ Tlv.ChildSelector, interest.getChildSelector());
+ if (interest.getExclude().size() > 0)
+ Tlv0_2WireFormat.encodeExclude(interest.getExclude(), encoder);
+
+ if (interest.getKeyLocator().getType() != null)
+ Tlv0_2WireFormat.encodeKeyLocator
+ (Tlv.PublisherPublicKeyLocator, interest.getKeyLocator(), encoder);
+
+ encoder.writeOptionalNonNegativeIntegerTlv(
+ Tlv.MaxSuffixComponents, interest.getMaxSuffixComponents());
+ encoder.writeOptionalNonNegativeIntegerTlv(
+ Tlv.MinSuffixComponents, interest.getMinSuffixComponents());
+
+ // Only output the type and length if values were written.
+ if (encoder.getLength() != saveLength)
+ encoder.writeTypeAndLength(Tlv.Selectors, encoder.getLength() - saveLength);
+};
+
+Tlv0_2WireFormat.decodeSelectors = function(interest, decoder, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var endOffset = decoder.readNestedTlvsStart(Tlv.Selectors);
+
+ interest.setMinSuffixComponents(decoder.readOptionalNonNegativeIntegerTlv
+ (Tlv.MinSuffixComponents, endOffset));
+ interest.setMaxSuffixComponents(decoder.readOptionalNonNegativeIntegerTlv
+ (Tlv.MaxSuffixComponents, endOffset));
+
+ if (decoder.peekType(Tlv.PublisherPublicKeyLocator, endOffset))
+ Tlv0_2WireFormat.decodeKeyLocator
+ (Tlv.PublisherPublicKeyLocator, interest.getKeyLocator(), decoder, copy);
+ else
+ interest.getKeyLocator().clear();
+
+ if (decoder.peekType(Tlv.Exclude, endOffset))
+ Tlv0_2WireFormat.decodeExclude(interest.getExclude(), decoder, copy);
+ else
+ interest.getExclude().clear();
+
+ interest.setChildSelector(decoder.readOptionalNonNegativeIntegerTlv
+ (Tlv.ChildSelector, endOffset));
+ interest.setMustBeFresh(decoder.readBooleanTlv(Tlv.MustBeFresh, endOffset));
+
+ decoder.finishNestedTlvs(endOffset);
+};
+
+Tlv0_2WireFormat.encodeExclude = function(exclude, encoder)
+{
+ var saveLength = encoder.getLength();
+
+ // TODO: Do we want to order the components (except for ANY)?
+ // Encode the entries backwards.
+ for (var i = exclude.size() - 1; i >= 0; --i) {
+ var entry = exclude.get(i);
+
+ if (entry == Exclude.ANY)
+ encoder.writeTypeAndLength(Tlv.Any, 0);
+ else
+ Tlv0_2WireFormat.encodeNameComponent(entry, encoder);
+ }
+
+ encoder.writeTypeAndLength(Tlv.Exclude, encoder.getLength() - saveLength);
+};
+
+Tlv0_2WireFormat.decodeExclude = function(exclude, decoder, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var endOffset = decoder.readNestedTlvsStart(Tlv.Exclude);
+
+ exclude.clear();
+ while (decoder.getOffset() < endOffset) {
+ if (decoder.peekType(Tlv.Any, endOffset)) {
+ // Read past the Any TLV.
+ decoder.readBooleanTlv(Tlv.Any, endOffset);
+ exclude.appendAny();
+ }
+ else
+ exclude.appendComponent(Tlv0_2WireFormat.decodeNameComponent(decoder, copy));
+ }
+
+ decoder.finishNestedTlvs(endOffset);
+};
+
+Tlv0_2WireFormat.encodeKeyLocator = function(type, keyLocator, encoder)
+{
+ var saveLength = encoder.getLength();
+
+ // Encode backwards.
+ if (keyLocator.getType() != null) {
+ if (keyLocator.getType() == KeyLocatorType.KEYNAME)
+ Tlv0_2WireFormat.encodeName(keyLocator.getKeyName(), encoder);
+ else if (keyLocator.getType() == KeyLocatorType.KEY_LOCATOR_DIGEST &&
+ keyLocator.getKeyData().size() > 0)
+ encoder.writeBlobTlv(Tlv.KeyLocatorDigest, keyLocator.getKeyData().buf());
+ else
+ throw new Error("Unrecognized KeyLocatorType " + keyLocator.getType());
+ }
+
+ encoder.writeTypeAndLength(type, encoder.getLength() - saveLength);
+};
+
+Tlv0_2WireFormat.decodeKeyLocator = function
+ (expectedType, keyLocator, decoder, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var endOffset = decoder.readNestedTlvsStart(expectedType);
+
+ keyLocator.clear();
+
+ if (decoder.getOffset() == endOffset)
+ // The KeyLocator is omitted, so leave the fields as none.
+ return;
+
+ if (decoder.peekType(Tlv.Name, endOffset)) {
+ // KeyLocator is a Name.
+ keyLocator.setType(KeyLocatorType.KEYNAME);
+ Tlv0_2WireFormat.decodeName(keyLocator.getKeyName(), decoder, copy);
+ }
+ else if (decoder.peekType(Tlv.KeyLocatorDigest, endOffset)) {
+ // KeyLocator is a KeyLocatorDigest.
+ keyLocator.setType(KeyLocatorType.KEY_LOCATOR_DIGEST);
+ keyLocator.setKeyData
+ (new Blob(decoder.readBlobTlv(Tlv.KeyLocatorDigest), copy));
+ }
+ else
+ throw new DecodingException(new Error
+ ("decodeKeyLocator: Unrecognized key locator type"));
+
+ decoder.finishNestedTlvs(endOffset);
+};
+
+/**
+ * An internal method to encode signature as the appropriate form of
+ * SignatureInfo in NDN-TLV.
+ * @param {Signature} signature An object of a subclass of Signature to encode.
+ * @param {TlvEncoder} encoder The encoder.
+ */
+Tlv0_2WireFormat.encodeSignatureInfo_ = function(signature, encoder)
+{
+ if (signature instanceof GenericSignature) {
+ // Handle GenericSignature separately since it has the entire encoding.
+ var encoding = signature.getSignatureInfoEncoding();
+
+ // Do a test decoding to sanity check that it is valid TLV.
+ try {
+ var decoder = new TlvDecoder(encoding.buf());
+ var endOffset = decoder.readNestedTlvsStart(Tlv.SignatureInfo);
+ decoder.readNonNegativeIntegerTlv(Tlv.SignatureType);
+ decoder.finishNestedTlvs(endOffset);
+ } catch (ex) {
+ throw new Error
+ ("The GenericSignature encoding is not a valid NDN-TLV SignatureInfo: " +
+ ex.message);
+ }
+
+ encoder.writeBuffer(encoding.buf());
+ return;
+ }
+
+ var saveLength = encoder.getLength();
+
+ // Encode backwards.
+ if (signature instanceof Sha256WithRsaSignature) {
+ Tlv0_2WireFormat.encodeKeyLocator
+ (Tlv.KeyLocator, signature.getKeyLocator(), encoder);
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.SignatureType, Tlv.SignatureType_SignatureSha256WithRsa);
+ }
+ else if (signature instanceof Sha256WithEcdsaSignature) {
+ Tlv0_2WireFormat.encodeKeyLocator
+ (Tlv.KeyLocator, signature.getKeyLocator(), encoder);
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.SignatureType, Tlv.SignatureType_SignatureSha256WithEcdsa);
+ }
+ else if (signature instanceof HmacWithSha256Signature) {
+ Tlv0_2WireFormat.encodeKeyLocator
+ (Tlv.KeyLocator, signature.getKeyLocator(), encoder);
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.SignatureType, Tlv.SignatureType_SignatureHmacWithSha256);
+ }
+ else if (signature instanceof DigestSha256Signature)
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.SignatureType, Tlv.SignatureType_DigestSha256);
+ else
+ throw new Error("encodeSignatureInfo: Unrecognized Signature object type");
+
+ encoder.writeTypeAndLength(Tlv.SignatureInfo, encoder.getLength() - saveLength);
+};
+
+Tlv0_2WireFormat.decodeSignatureInfo = function(data, decoder, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var beginOffset = decoder.getOffset();
+ var endOffset = decoder.readNestedTlvsStart(Tlv.SignatureInfo);
+
+ var signatureType = decoder.readNonNegativeIntegerTlv(Tlv.SignatureType);
+ if (signatureType == Tlv.SignatureType_SignatureSha256WithRsa) {
+ data.setSignature(new Sha256WithRsaSignature());
+ // Modify data's signature object because if we create an object
+ // and set it, then data will have to copy all the fields.
+ var signatureInfo = data.getSignature();
+ Tlv0_2WireFormat.decodeKeyLocator
+ (Tlv.KeyLocator, signatureInfo.getKeyLocator(), decoder, copy);
+ }
+ else if (signatureType == Tlv.SignatureType_SignatureSha256WithEcdsa) {
+ data.setSignature(new Sha256WithEcdsaSignature());
+ var signatureInfo = data.getSignature();
+ Tlv0_2WireFormat.decodeKeyLocator
+ (Tlv.KeyLocator, signatureInfo.getKeyLocator(), decoder, copy);
+ }
+ else if (signatureType == Tlv.SignatureType_SignatureHmacWithSha256) {
+ data.setSignature(new HmacWithSha256Signature());
+ var signatureInfo = data.getSignature();
+ Tlv0_2WireFormat.decodeKeyLocator
+ (Tlv.KeyLocator, signatureInfo.getKeyLocator(), decoder, copy);
+ }
+ else if (signatureType == Tlv.SignatureType_DigestSha256)
+ data.setSignature(new DigestSha256Signature());
+ else {
+ data.setSignature(new GenericSignature());
+ var signatureInfo = data.getSignature();
+
+ // Get the bytes of the SignatureInfo TLV.
+ signatureInfo.setSignatureInfoEncoding
+ (new Blob(decoder.getSlice(beginOffset, endOffset), copy), signatureType);
+ }
+
+ decoder.finishNestedTlvs(endOffset);
+};
+
+Tlv0_2WireFormat.encodeMetaInfo = function(metaInfo, encoder)
+{
+ var saveLength = encoder.getLength();
+
+ // Encode backwards.
+ var finalBlockIdBuf = metaInfo.getFinalBlockId().getValue().buf();
+ if (finalBlockIdBuf != null && finalBlockIdBuf.length > 0) {
+ // FinalBlockId has an inner NameComponent.
+ var finalBlockIdSaveLength = encoder.getLength();
+ Tlv0_2WireFormat.encodeNameComponent(metaInfo.getFinalBlockId(), encoder);
+ encoder.writeTypeAndLength
+ (Tlv.FinalBlockId, encoder.getLength() - finalBlockIdSaveLength);
+ }
+
+ encoder.writeOptionalNonNegativeIntegerTlv
+ (Tlv.FreshnessPeriod, metaInfo.getFreshnessPeriod());
+ if (metaInfo.getType() != ContentType.BLOB) {
+ // Not the default, so we need to encode the type.
+ if (metaInfo.getType() == ContentType.LINK ||
+ metaInfo.getType() == ContentType.KEY ||
+ metaInfo.getType() == ContentType.NACK)
+ // The ContentType enum is set up with the correct integer for
+ // each NDN-TLV ContentType.
+ encoder.writeNonNegativeIntegerTlv(Tlv.ContentType, metaInfo.getType());
+ else if (metaInfo.getType() == ContentType.OTHER_CODE)
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.ContentType, metaInfo.getOtherTypeCode());
+ else
+ // We don't expect this to happen.
+ throw new Error("unrecognized TLV ContentType");
+ }
+
+ encoder.writeTypeAndLength(Tlv.MetaInfo, encoder.getLength() - saveLength);
+};
+
+Tlv0_2WireFormat.decodeMetaInfo = function(metaInfo, decoder, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ var endOffset = decoder.readNestedTlvsStart(Tlv.MetaInfo);
+
+ var type = decoder.readOptionalNonNegativeIntegerTlv
+ (Tlv.ContentType, endOffset);
+ if (type == null || type < 0 || type === ContentType.BLOB)
+ metaInfo.setType(ContentType.BLOB);
+ else if (type === ContentType.LINK ||
+ type === ContentType.KEY ||
+ type === ContentType.NACK)
+ // The ContentType enum is set up with the correct integer for each NDN-TLV
+ // ContentType.
+ metaInfo.setType(type);
+ else {
+ // Unrecognized content type.
+ metaInfo.setType(ContentType.OTHER_CODE);
+ metaInfo.setOtherTypeCode(type);
+ }
+
+ metaInfo.setFreshnessPeriod
+ (decoder.readOptionalNonNegativeIntegerTlv(Tlv.FreshnessPeriod, endOffset));
+ if (decoder.peekType(Tlv.FinalBlockId, endOffset)) {
+ var finalBlockIdEndOffset = decoder.readNestedTlvsStart(Tlv.FinalBlockId);
+ metaInfo.setFinalBlockId(Tlv0_2WireFormat.decodeNameComponent(decoder, copy));
+ decoder.finishNestedTlvs(finalBlockIdEndOffset);
+ }
+ else
+ metaInfo.setFinalBlockId(null);
+
+ decoder.finishNestedTlvs(endOffset);
+};
+
+Tlv0_2WireFormat.encodeControlParameters = function(controlParameters, encoder)
+{
+ var saveLength = encoder.getLength();
+
+ // Encode backwards.
+ encoder.writeOptionalNonNegativeIntegerTlv
+ (Tlv.ControlParameters_ExpirationPeriod,
+ controlParameters.getExpirationPeriod());
+
+ if (controlParameters.getStrategy().size() > 0){
+ var strategySaveLength = encoder.getLength();
+ Tlv0_2WireFormat.encodeName(controlParameters.getStrategy(), encoder);
+ encoder.writeTypeAndLength(Tlv.ControlParameters_Strategy,
+ encoder.getLength() - strategySaveLength);
+ }
+
+ var flags = controlParameters.getForwardingFlags().getNfdForwardingFlags();
+ if (flags != new ForwardingFlags().getNfdForwardingFlags())
+ // The flags are not the default value.
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.ControlParameters_Flags, flags);
+
+ encoder.writeOptionalNonNegativeIntegerTlv
+ (Tlv.ControlParameters_Cost, controlParameters.getCost());
+ encoder.writeOptionalNonNegativeIntegerTlv
+ (Tlv.ControlParameters_Origin, controlParameters.getOrigin());
+ encoder.writeOptionalNonNegativeIntegerTlv
+ (Tlv.ControlParameters_LocalControlFeature,
+ controlParameters.getLocalControlFeature());
+
+ if (controlParameters.getUri().length != 0)
+ encoder.writeBlobTlv
+ (Tlv.ControlParameters_Uri, new Blob(controlParameters.getUri()).buf());
+
+ encoder.writeOptionalNonNegativeIntegerTlv
+ (Tlv.ControlParameters_FaceId, controlParameters.getFaceId());
+ if (controlParameters.getName() != null)
+ Tlv0_2WireFormat.encodeName(controlParameters.getName(), encoder);
+
+ encoder.writeTypeAndLength
+ (Tlv.ControlParameters_ControlParameters, encoder.getLength() - saveLength);
+};
+
+Tlv0_2WireFormat.decodeControlParameters = function
+ (controlParameters, decoder, copy)
+{
+ if (copy == null)
+ copy = true;
+
+ controlParameters.clear();
+ var endOffset = decoder.
+ readNestedTlvsStart(Tlv.ControlParameters_ControlParameters);
+
+ // decode name
+ if (decoder.peekType(Tlv.Name, endOffset)) {
+ var name = new Name();
+ Tlv0_2WireFormat.decodeName(name, decoder, copy);
+ controlParameters.setName(name);
+ }
+
+ // decode face ID
+ controlParameters.setFaceId(decoder.readOptionalNonNegativeIntegerTlv
+ (Tlv.ControlParameters_FaceId, endOffset));
+
+ // decode URI
+ if (decoder.peekType(Tlv.ControlParameters_Uri, endOffset)) {
+ // Set copy false since we just immediately get the string.
+ var uri = new Blob
+ (decoder.readOptionalBlobTlv(Tlv.ControlParameters_Uri, endOffset), false);
+ controlParameters.setUri(uri.toString());
+ }
+
+ // decode integers
+ controlParameters.setLocalControlFeature(decoder.
+ readOptionalNonNegativeIntegerTlv(
+ Tlv.ControlParameters_LocalControlFeature, endOffset));
+ controlParameters.setOrigin(decoder.
+ readOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_Origin,
+ endOffset));
+ controlParameters.setCost(decoder.readOptionalNonNegativeIntegerTlv(
+ Tlv.ControlParameters_Cost, endOffset));
+
+ // set forwarding flags
+ if (decoder.peekType(Tlv.ControlParameters_Flags, endOffset)) {
+ var flags = new ForwardingFlags();
+ flags.setNfdForwardingFlags(decoder.
+ readNonNegativeIntegerTlv(Tlv.ControlParameters_Flags, endOffset));
+ controlParameters.setForwardingFlags(flags);
+ }
+
+ // decode strategy
+ if (decoder.peekType(Tlv.ControlParameters_Strategy, endOffset)) {
+ var strategyEndOffset = decoder.readNestedTlvsStart(Tlv.ControlParameters_Strategy);
+ Tlv0_2WireFormat.decodeName(controlParameters.getStrategy(), decoder, copy);
+ decoder.finishNestedTlvs(strategyEndOffset);
+ }
+
+ // decode expiration period
+ controlParameters.setExpirationPeriod(
+ decoder.readOptionalNonNegativeIntegerTlv(
+ Tlv.ControlParameters_ExpirationPeriod, endOffset));
+
+ decoder.finishNestedTlvs(endOffset);
+};
+/**
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Tlv0_2WireFormat = require('./tlv-0_2-wire-format.js').Tlv0_2WireFormat;
+
+/**
+ * A Tlv0_1_1WireFormat extends Tlv0_2WireFormat so that it is an alias in case
+ * any applications use Tlv0_1_1WireFormat directly. These two wire formats are
+ * the same except that Tlv0_2WireFormat adds support for the name component
+ * type ImplicitSha256Digest.
+ * @constructor
+ */
+var Tlv0_1_1WireFormat = function Tlv0_1_1WireFormat()
+{
+ // Inherit from Tlv0_2WireFormat.
+ Tlv0_2WireFormat.call(this);
+};
+
+Tlv0_1_1WireFormat.prototype = new Tlv0_2WireFormat();
+Tlv0_1_1WireFormat.prototype.name = "Tlv0_1_1WireFormat";
+
+exports.Tlv0_1_1WireFormat = Tlv0_1_1WireFormat;
+
+// Default object.
+Tlv0_1_1WireFormat.instance = null;
+
+/**
+ * Get a singleton instance of a Tlv0_1_1WireFormat.
+ * @return {Tlv0_1_1WireFormat} The singleton instance.
+ */
+Tlv0_1_1WireFormat.get = function()
+{
+ if (Tlv0_1_1WireFormat.instance === null)
+ Tlv0_1_1WireFormat.instance = new Tlv0_1_1WireFormat();
+ return Tlv0_1_1WireFormat.instance;
+};
+/**
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var WireFormat = require('./wire-format.js').WireFormat; /** @ignore */
+var Tlv0_1_1WireFormat = require('./tlv-0_1_1-wire-format.js').Tlv0_1_1WireFormat;
+
+/**
+ * A Tlv0_1WireFormat extends Tlv0_1_1WireFormat so that it is an alias in case
+ * any applications use Tlv0_1WireFormat directly. These two wire formats are
+ * the same except that Tlv0_1_1WireFormat adds support for
+ * Sha256WithEcdsaSignature.
+ * @constructor
+ */
+var Tlv0_1WireFormat = function Tlv0_1WireFormat()
+{
+ // Inherit from Tlv0_1_1WireFormat.
+ Tlv0_1_1WireFormat.call(this);
+};
+
+Tlv0_1WireFormat.prototype = new Tlv0_1_1WireFormat();
+Tlv0_1WireFormat.prototype.name = "Tlv0_1WireFormat";
+
+exports.Tlv0_1WireFormat = Tlv0_1WireFormat;
+
+// Default object.
+Tlv0_1WireFormat.instance = null;
+
+/**
+ * Get a singleton instance of a Tlv0_1WireFormat.
+ * @return {Tlv0_1WireFormat} The singleton instance.
+ */
+Tlv0_1WireFormat.get = function()
+{
+ if (Tlv0_1WireFormat.instance === null)
+ Tlv0_1WireFormat.instance = new Tlv0_1WireFormat();
+ return Tlv0_1WireFormat.instance;
+};
+/**
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var WireFormat = require('./wire-format.js').WireFormat; /** @ignore */
+var Tlv0_2WireFormat = require('./tlv-0_2-wire-format.js').Tlv0_2WireFormat;
+
+/**
+ * A TlvWireFormat extends WireFormat to override its methods to
+ * implement encoding and decoding using the preferred implementation of NDN-TLV.
+ * @constructor
+ */
+var TlvWireFormat = function TlvWireFormat()
+{
+ // Inherit from Tlv0_2WireFormat.
+ Tlv0_2WireFormat.call(this);
+};
+
+TlvWireFormat.prototype = new Tlv0_2WireFormat();
+TlvWireFormat.prototype.name = "TlvWireFormat";
+
+exports.TlvWireFormat = TlvWireFormat;
+
+// Default object.
+TlvWireFormat.instance = null;
+
+/**
+ * Get a singleton instance of a TlvWireFormat. Assuming that the default
+ * wire format was set with WireFormat.setDefaultWireFormat(TlvWireFormat.get()),
+ * you can check if this is the default wire encoding with
+ * if WireFormat.getDefaultWireFormat() == TlvWireFormat.get().
+ * @return {TlvWireFormat} The singleton instance.
+ */
+TlvWireFormat.get = function()
+{
+ if (TlvWireFormat.instance === null)
+ TlvWireFormat.instance = new TlvWireFormat();
+ return TlvWireFormat.instance;
+};
+
+// On loading this module, make this the default wire format.
+// This module will be loaded because WireFormat loads it.
+WireFormat.setDefaultWireFormat(TlvWireFormat.get());
+/**
+ * This file contains utilities to help encode and decode NDN objects.
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * author: Meki Cheraoui
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var DataUtils = require('./data-utils.js').DataUtils; /** @ignore */
+var KeyLocatorType = require('../key-locator.js').KeyLocatorType; /** @ignore */
+var Interest = require('../interest.js').Interest; /** @ignore */
+var Data = require('../data.js').Data; /** @ignore */
+var Sha256WithRsaSignature = require('../sha256-with-rsa-signature.js').Sha256WithRsaSignature; /** @ignore */
+var Sha256WithEcdsaSignature = require('../sha256-with-ecdsa-signature.js').Sha256WithEcdsaSignature; /** @ignore */
+var HmacWithSha256Signature = require('../hmac-with-sha256-signature.js').HmacWithSha256Signature; /** @ignore */
+var DigestSha256Signature = require('../digest-sha256-signature.js').DigestSha256Signature; /** @ignore */
+var ContentType = require('../meta-info.js').ContentType; /** @ignore */
+var WireFormat = require('./wire-format.js').WireFormat;
+
+/**
+ * An EncodingUtils has static methods for encoding data.
+ * @constructor
+ */
+var EncodingUtils = function EncodingUtils()
+{
+};
+
+exports.EncodingUtils = EncodingUtils;
+
+EncodingUtils.encodeToHexInterest = function(interest, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ return DataUtils.toHex(interest.wireEncode(wireFormat).buf());
+};
+
+EncodingUtils.encodeToHexData = function(data, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ return DataUtils.toHex(data.wireEncode(wireFormat).buf());
+};
+
+EncodingUtils.decodeHexInterest = function(input, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ var interest = new Interest();
+ interest.wireDecode(DataUtils.toNumbers(input), wireFormat);
+ return interest;
+};
+
+EncodingUtils.decodeHexData = function(input, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ var data = new Data();
+ data.wireDecode(DataUtils.toNumbers(input), wireFormat);
+ return data;
+};
+
+/**
+ * Decode the Buffer array which holds SubjectPublicKeyInfo and return an RSAKey.
+ */
+EncodingUtils.decodeSubjectPublicKeyInfo = function(array)
+{
+ var hex = DataUtils.toHex(array).toLowerCase();
+ var a = _x509_getPublicKeyHexArrayFromCertHex(hex, _x509_getSubjectPublicKeyPosFromCertHex(hex, 0));
+ var rsaKey = new RSAKey();
+ rsaKey.setPublic(a[0], a[1]);
+ return rsaKey;
+}
+
+/**
+ * Return a user friendly HTML string with the contents of data.
+ */
+EncodingUtils.dataToHtml = function(/* Data */ data)
+{
+ if (data == -1)
+ return "NO CONTENT FOUND";
+ if (data == -2)
+ return "CONTENT NAME IS EMPTY";
+
+ var output = "";
+ function append(message) {
+ message = message.replace(/&/g, "&");
+ message = message.replace(/</g, "<");
+
+ output += message;
+ output += "<br/>";
+ }
+
+ // Imitate dumpData in examples/node/test-encode-decode-data.js
+
+ append("name: " + data.getName().toUri());
+ if (data.getContent().size() > 0) {
+ append("content (raw): " + data.getContent().buf().toString('binary'));
+ append("content (hex): " + data.getContent().toHex());
+ }
+ else
+ append("content: <empty>");
+
+ if (!(data.getMetaInfo().getType() == ContentType.BLOB)) {
+ if (data.getMetaInfo().getType() == ContentType.KEY)
+ append("metaInfo.type: KEY");
+ else if (data.getMetaInfo().getType() == ContentType.LINK)
+ append("metaInfo.type: LINK");
+ else if (data.getMetaInfo().getType() == ContentType.NACK)
+ append("metaInfo.type: NACK");
+ else if (data.getMetaInfo().getType() == ContentType.OTHER_CODE)
+ append("metaInfo.type: other code " + data.getMetaInfo().getOtherTypeCode());
+ }
+ append("metaInfo.freshnessPeriod (milliseconds): " +
+ (data.getMetaInfo().getFreshnessPeriod() >= 0 ?
+ "" + data.getMetaInfo().getFreshnessPeriod() : "<none>"));
+ append("metaInfo.finalBlockId: " +
+ (data.getMetaInfo().getFinalBlockId().getValue().size() > 0 ?
+ data.getMetaInfo().getFinalBlockId().getValue().toHex() : "<none>"));
+
+ var keyLocator = null;
+ var signature = data.getSignature();
+ if (signature instanceof Sha256WithRsaSignature) {
+ var signature = data.getSignature();
+ append("Sha256WithRsa signature.signature: " +
+ (signature.getSignature().size() > 0 ?
+ signature.getSignature().toHex() : "<none>"));
+ keyLocator = signature.getKeyLocator();
+ }
+ else if (signature instanceof Sha256WithEcdsaSignature) {
+ var signature = data.getSignature();
+ append("Sha256WithEcdsa signature.signature: " +
+ (signature.getSignature().size() > 0 ?
+ signature.getSignature().toHex() : "<none>"));
+ keyLocator = signature.getKeyLocator();
+ }
+ else if (signature instanceof HmacWithSha256Signature) {
+ var signature = data.getSignature();
+ append("HmacWithSha256 signature.signature: " +
+ (signature.getSignature().size() > 0 ?
+ signature.getSignature().toHex() : "<none>"));
+ keyLocator = signature.getKeyLocator();
+ }
+ else if (signature instanceof DigestSha256Signature) {
+ var signature = data.getSignature();
+ append("DigestSha256 signature.signature: " +
+ (signature.getSignature().size() > 0 ?
+ signature.getSignature().toHex() : "<none>"));
+ }
+ if (keyLocator !== null) {
+ if (keyLocator.getType() == null)
+ append("signature.keyLocator: <none>");
+ else if (keyLocator.getType() == KeyLocatorType.KEY_LOCATOR_DIGEST)
+ append("signature.keyLocator: KeyLocatorDigest: " + keyLocator.getKeyData().toHex());
+ else if (keyLocator.getType() == KeyLocatorType.KEYNAME)
+ append("signature.keyLocator: KeyName: " + keyLocator.getKeyName().toUri());
+ else
+ append("signature.keyLocator: <unrecognized ndn_KeyLocatorType>");
+ }
+
+ return output;
+};
+
+//
+// Deprecated: For the browser, define these in the global scope. Applications should access as member of EncodingUtils.
+//
+
+var encodeToHexInterest = function(interest) { return EncodingUtils.encodeToHexInterest(interest); }
+var decodeHexInterest = function(input) { return EncodingUtils.decodeHexInterest(input); }
+var decodeSubjectPublicKeyInfo = function(input) { return EncodingUtils.decodeSubjectPublicKeyInfo(input); }
+
+/**
+ * @deprecated Use interest.wireEncode().
+ */
+function encodeToBinaryInterest(interest) { return interest.wireEncode().buf(); }
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/algo/aes https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// (This is ported from ndn::gep::algo::Aes, and named AesAlgorithm because
+// "Aes" is very short and not all the Common Client Libraries have namespaces.)
+
+/** @ignore */
+var Crypto = require('../../crypto.js'); /** @ignore */
+var Blob = require('../../util/blob.js').Blob; /** @ignore */
+var DecryptKey = require('../decrypt-key.js').DecryptKey; /** @ignore */
+var EncryptKey = require('../encrypt-key.js').EncryptKey; /** @ignore */
+var EncryptAlgorithmType = require('./encrypt-params.js').EncryptAlgorithmType; /** @ignore */
+var UseSubtleCrypto = require('../../use-subtle-crypto-node.js').UseSubtleCrypto; /** @ignore */
+var SyncPromise = require('../../util/sync-promise.js').SyncPromise;
+
+/**
+ * The AesAlgorithm class provides static methods to manipulate keys, encrypt
+ * and decrypt using the AES symmetric key cipher.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var AesAlgorithm = function AesAlgorithm()
+{
+};
+
+exports.AesAlgorithm = AesAlgorithm;
+
+/**
+ * Generate a new random decrypt key for AES based on the given params.
+ * @param {AesKeyParams} params The key params with the key size (in bits).
+ * @return {DecryptKey} The new decrypt key.
+ */
+AesAlgorithm.generateKey = function(params)
+{
+ // Convert the key bit size to bytes.
+ var key = Crypto.randomBytes(params.getKeySize() / 8);
+
+ var decryptKey = new DecryptKey(new Blob(key, false));
+ return decryptKey;
+};
+
+/**
+ * Derive a new encrypt key from the given decrypt key value.
+ * @param {Blob} keyBits The key value of the decrypt key.
+ * @return {EncryptKey} The new encrypt key.
+ */
+AesAlgorithm.deriveEncryptKey = function(keyBits)
+{
+ return new EncryptKey(keyBits);
+};
+
+/**
+ * Decrypt the encryptedData using the keyBits according the encrypt params.
+ * @param {Blob} keyBits The key value.
+ * @param {Blob} encryptedData The data to decrypt.
+ * @param {EncryptParams} params This decrypts according to
+ * params.getAlgorithmType() and other params as needed such as
+ * params.getInitialVector().
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the decrypted Blob.
+ */
+AesAlgorithm.decryptPromise = function(keyBits, encryptedData, params, useSync)
+{
+ if (UseSubtleCrypto() && !useSync &&
+ // Crypto.subtle doesn't implement ECB.
+ params.getAlgorithmType() != EncryptAlgorithmType.AesEcb) {
+ if (params.getAlgorithmType() == EncryptAlgorithmType.AesCbc) {
+ return crypto.subtle.importKey
+ ("raw", keyBits.buf(), { name: "AES-CBC" }, false,
+ ["encrypt", "decrypt"])
+ .then(function(key) {
+ return crypto.subtle.decrypt
+ ({ name: "AES-CBC", iv: params.getInitialVector().buf() },
+ key, encryptedData.buf());
+ })
+ .then(function(result) {
+ return Promise.resolve(new Blob(new Uint8Array(result), false));
+ });
+ }
+ else
+ return Promise.reject(new Error("unsupported encryption mode"));
+ }
+ else {
+ if (params.getAlgorithmType() == EncryptAlgorithmType.AesEcb) {
+ try {
+ // ECB ignores the initial vector.
+ var cipher = Crypto.createDecipheriv("aes-128-ecb", keyBits.buf(), "");
+ return SyncPromise.resolve(new Blob
+ (Buffer.concat([cipher.update(encryptedData.buf()), cipher.final()]),
+ false));
+ } catch (err) {
+ return SyncPromise.reject(err);
+ }
+ }
+ else if (params.getAlgorithmType() == EncryptAlgorithmType.AesCbc) {
+ try {
+ var cipher = Crypto.createDecipheriv
+ ("aes-128-cbc", keyBits.buf(), params.getInitialVector().buf());
+ return SyncPromise.resolve(new Blob
+ (Buffer.concat([cipher.update(encryptedData.buf()), cipher.final()]),
+ false));
+ } catch (err) {
+ return SyncPromise.reject(err);
+ }
+ }
+ else
+ return SyncPromise.reject(new Error("unsupported encryption mode"));
+ }
+};
+
+/**
+ * Decrypt the encryptedData using the keyBits according the encrypt params.
+ * @param {Blob} keyBits The key value.
+ * @param {Blob} encryptedData The data to decrypt.
+ * @param {EncryptParams} params This decrypts according to
+ * params.getAlgorithmType() and other params as needed such as
+ * params.getInitialVector().
+ * @return {Blob} The decrypted data.
+ * @throws Error If decryptPromise doesn't return a SyncPromise which is
+ * already fulfilled.
+ */
+AesAlgorithm.decrypt = function(keyBits, encryptedData, params)
+{
+ return SyncPromise.getValue(this.decryptPromise
+ (keyBits, encryptedData, params, true));
+};
+
+/**
+ * Encrypt the plainData using the keyBits according the encrypt params.
+ * @param {Blob} keyBits The key value.
+ * @param {Blob} plainData The data to encrypt.
+ * @param {EncryptParams} params This encrypts according to
+ * params.getAlgorithmType() and other params as needed such as
+ * params.getInitialVector().
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the encrypted Blob.
+ */
+AesAlgorithm.encryptPromise = function(keyBits, plainData, params, useSync)
+{
+ if (params.getAlgorithmType() == EncryptAlgorithmType.AesCbc) {
+ if (params.getInitialVector().size() != AesAlgorithm.BLOCK_SIZE)
+ return SyncPromise.reject(new Error("incorrect initial vector size"));
+ }
+
+ if (UseSubtleCrypto() && !useSync &&
+ // Crypto.subtle doesn't implement ECB.
+ params.getAlgorithmType() != EncryptAlgorithmType.AesEcb) {
+ if (params.getAlgorithmType() == EncryptAlgorithmType.AesCbc) {
+ return crypto.subtle.importKey
+ ("raw", keyBits.buf(), { name: "AES-CBC" }, false,
+ ["encrypt", "decrypt"])
+ .then(function(key) {
+ return crypto.subtle.encrypt
+ ({ name: "AES-CBC", iv: params.getInitialVector().buf() },
+ key, plainData.buf());
+ })
+ .then(function(result) {
+ return Promise.resolve(new Blob(new Uint8Array(result), false));
+ });
+ }
+ else
+ return Promise.reject(new Error("unsupported encryption mode"));
+ }
+ else {
+ if (params.getAlgorithmType() == EncryptAlgorithmType.AesEcb) {
+ // ECB ignores the initial vector.
+ var cipher = Crypto.createCipheriv("aes-128-ecb", keyBits.buf(), "");
+ return SyncPromise.resolve(new Blob
+ (Buffer.concat([cipher.update(plainData.buf()), cipher.final()]),
+ false));
+ }
+ else if (params.getAlgorithmType() == EncryptAlgorithmType.AesCbc) {
+ var cipher = Crypto.createCipheriv
+ ("aes-128-cbc", keyBits.buf(), params.getInitialVector().buf());
+ return SyncPromise.resolve(new Blob
+ (Buffer.concat([cipher.update(plainData.buf()), cipher.final()]),
+ false));
+ }
+ else
+ return SyncPromise.reject(new Error("unsupported encryption mode"));
+ }
+};
+
+/**
+ * Encrypt the plainData using the keyBits according the encrypt params.
+ * @param {Blob} keyBits The key value.
+ * @param {Blob} plainData The data to encrypt.
+ * @param {EncryptParams} params This encrypts according to
+ * params.getAlgorithmType() and other params as needed such as
+ * params.getInitialVector().
+ * @return {Blob} The encrypted data.
+ * @throws Error If encryptPromise doesn't return a SyncPromise which is
+ * already fulfilled.
+ */
+AesAlgorithm.encrypt = function(keyBits, plainData, params)
+{
+ return SyncPromise.getValue(this.encryptPromise
+ (keyBits, plainData, params, true));
+};
+
+AesAlgorithm.BLOCK_SIZE = 16;
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/encrypt-params https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Crypto = require('../../crypto.js'); /** @ignore */
+var Blob = require('../../util/blob.js').Blob;
+
+var EncryptAlgorithmType = function EncryptAlgorithmType()
+{
+}
+
+exports.EncryptAlgorithmType = EncryptAlgorithmType;
+
+// These correspond to the TLV codes.
+EncryptAlgorithmType.AesEcb = 0;
+EncryptAlgorithmType.AesCbc = 1;
+EncryptAlgorithmType.RsaPkcs = 2;
+EncryptAlgorithmType.RsaOaep = 3;
+
+/**
+ * An EncryptParams holds an algorithm type and other parameters used to
+ * encrypt and decrypt. Create an EncryptParams with the given parameters.
+ * @param {number} algorithmType The algorithm type from EncryptAlgorithmType,
+ * or null if not specified.
+ * @param {number} initialVectorLength (optional) The initial vector length, or
+ * 0 if the initial vector is not specified. If ommitted, the initial vector is
+ * not specified.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var EncryptParams = function EncryptParams(algorithmType, initialVectorLength)
+{
+ this.algorithmType_ = algorithmType;
+
+ if (initialVectorLength != null && initialVectorLength > 0) {
+ var initialVector = Crypto.randomBytes(initialVectorLength);
+ this.initialVector_ = new Blob(initialVector, false);
+ }
+ else
+ this.initialVector_ = new Blob();
+};
+
+exports.EncryptParams = EncryptParams;
+
+/**
+ * Get the algorithmType.
+ * @return {number} The algorithm type from EncryptAlgorithmType, or null if not
+ * specified.
+ */
+EncryptParams.prototype.getAlgorithmType = function()
+{
+ return this.algorithmType_;
+};
+
+/**
+ * Get the initial vector.
+ * @return {Blob} The initial vector. If not specified, isNull() is true.
+ */
+EncryptParams.prototype.getInitialVector = function()
+{
+ return this.initialVector_;
+};
+
+/**
+ * Set the algorithm type.
+ * @param {number} algorithmType The algorithm type from EncryptAlgorithmType.
+ * If not specified, set to null.
+ * @return {EncryptParams} This EncryptParams so that you can chain calls to
+ * update values.
+ */
+EncryptParams.prototype.setAlgorithmType = function(algorithmType)
+{
+ this.algorithmType_ = algorithmType;
+ return this;
+};
+
+/**
+ * Set the initial vector.
+ * @param {Blob} initialVector The initial vector. If not specified, set to the
+ * default Blob() where isNull() is true.
+ * @return {EncryptParams} This EncryptParams so that you can chain calls to
+ * update values.
+ */
+EncryptParams.prototype.setInitialVector = function(initialVector)
+{
+ this.initialVector_ =
+ typeof initialVector === 'object' && initialVector instanceof Blob ?
+ initialVector : new Blob(initialVector);
+ return this;
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/encryptor https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Crypto = require('../../crypto.js'); /** @ignore */
+var Name = require('../../name.js').Name; /** @ignore */
+var KeyLocator = require('../../key-locator.js').KeyLocator; /** @ignore */
+var KeyLocatorType = require('../../key-locator.js').KeyLocatorType; /** @ignore */
+var TlvWireFormat = require('../../encoding/tlv-wire-format.js').TlvWireFormat; /** @ignore */
+var Blob = require('../../util/blob.js').Blob; /** @ignore */
+var AesAlgorithm = require('./aes-algorithm.js').AesAlgorithm; /** @ignore */
+var RsaAlgorithm = require('./rsa-algorithm.js').RsaAlgorithm; /** @ignore */
+var EncryptParams = require('./encrypt-params.js').EncryptParams; /** @ignore */
+var EncryptAlgorithmType = require('./encrypt-params.js').EncryptAlgorithmType; /** @ignore */
+var EncryptedContent = require('../encrypted-content.js').EncryptedContent; /** @ignore */
+var SyncPromise = require('../../util/sync-promise.js').SyncPromise;
+
+/**
+ * Encryptor has static constants and utility methods for encryption, such as
+ * encryptData.
+ * @constructor
+ */
+var Encryptor = function Encryptor(value)
+{
+};
+
+exports.Encryptor = Encryptor;
+
+Encryptor.NAME_COMPONENT_FOR = new Name.Component("FOR");
+Encryptor.NAME_COMPONENT_READ = new Name.Component("READ");
+Encryptor.NAME_COMPONENT_SAMPLE = new Name.Component("SAMPLE");
+Encryptor.NAME_COMPONENT_ACCESS = new Name.Component("ACCESS");
+Encryptor.NAME_COMPONENT_E_KEY = new Name.Component("E-KEY");
+Encryptor.NAME_COMPONENT_D_KEY = new Name.Component("D-KEY");
+Encryptor.NAME_COMPONENT_C_KEY = new Name.Component("C-KEY");
+
+/**
+ * Prepare an encrypted data packet by encrypting the payload using the key
+ * according to the params. In addition, this prepares the encoded
+ * EncryptedContent with the encryption result using keyName and params. The
+ * encoding is set as the content of the data packet. If params defines an
+ * asymmetric encryption algorithm and the payload is larger than the maximum
+ * plaintext size, this encrypts the payload with a symmetric key that is
+ * asymmetrically encrypted and provided as a nonce in the content of the data
+ * packet. The packet's /<dataName>/ is updated to be <dataName>/FOR/<keyName>.
+ * @param {Data} data The data packet which is updated.
+ * @param {Blob} payload The payload to encrypt.
+ * @param {Name} keyName The key name for the EncryptedContent.
+ * @param {Blob} key The encryption key value.
+ * @param {EncryptParams} params The parameters for encryption.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which fulfills when the data packet
+ * is updated.
+ */
+Encryptor.encryptDataPromise = function
+ (data, payload, keyName, key, params, useSync)
+{
+ data.getName().append(Encryptor.NAME_COMPONENT_FOR).append(keyName);
+
+ var algorithmType = params.getAlgorithmType();
+
+ if (algorithmType == EncryptAlgorithmType.AesCbc ||
+ algorithmType == EncryptAlgorithmType.AesEcb) {
+ return Encryptor.encryptSymmetricPromise_
+ (payload, key, keyName, params, useSync)
+ .then(function(content) {
+ data.setContent(content.wireEncode(TlvWireFormat.get()));
+ return SyncPromise.resolve();
+ });
+ }
+ else if (algorithmType == EncryptAlgorithmType.RsaPkcs ||
+ algorithmType == EncryptAlgorithmType.RsaOaep) {
+ // Node.js doesn't have a direct way to get the maximum plain text size, so
+ // try to encrypt the payload first and catch the error if it is too big.
+ return Encryptor.encryptAsymmetricPromise_
+ (payload, key, keyName, params, useSync)
+ .then(function(content) {
+ data.setContent(content.wireEncode(TlvWireFormat.get()));
+ return SyncPromise.resolve();
+ }, function(err) {
+ if (err.message.indexOf("data too large for key size") < 0)
+ // Not the expected error.
+ throw err;
+
+ // The payload is larger than the maximum plaintext size.
+ // 128-bit nonce.
+ var nonceKeyBuffer = Crypto.randomBytes(16);
+ var nonceKey = new Blob(nonceKeyBuffer, false);
+
+ var nonceKeyName = new Name(keyName);
+ nonceKeyName.append("nonce");
+
+ var symmetricParams = new EncryptParams
+ (EncryptAlgorithmType.AesCbc, AesAlgorithm.BLOCK_SIZE);
+
+ var nonceContent;
+ return Encryptor.encryptSymmetricPromise_
+ (payload, nonceKey, nonceKeyName, symmetricParams, useSync)
+ .then(function(localNonceContent) {
+ nonceContent = localNonceContent;
+ return Encryptor.encryptAsymmetricPromise_
+ (nonceKey, key, keyName, params, useSync);
+ })
+ .then(function(payloadContent) {
+ var nonceContentEncoding = nonceContent.wireEncode();
+ var payloadContentEncoding = payloadContent.wireEncode();
+ var content = new Buffer
+ (nonceContentEncoding.size() + payloadContentEncoding.size());
+ payloadContentEncoding.buf().copy(content, 0);
+ nonceContentEncoding.buf().copy(content, payloadContentEncoding.size());
+
+ data.setContent(new Blob(content, false));
+ return SyncPromise.resolve();
+ });
+ });
+ }
+ else
+ return SyncPromise.reject(new Error("Unsupported encryption method"));
+};
+
+/**
+ * Prepare an encrypted data packet by encrypting the payload using the key
+ * according to the params. In addition, this prepares the encoded
+ * EncryptedContent with the encryption result using keyName and params. The
+ * encoding is set as the content of the data packet. If params defines an
+ * asymmetric encryption algorithm and the payload is larger than the maximum
+ * plaintext size, this encrypts the payload with a symmetric key that is
+ * asymmetrically encrypted and provided as a nonce in the content of the data
+ * packet.
+ * @param {Data} data The data packet which is updated.
+ * @param {Blob} payload The payload to encrypt.
+ * @param {Name} keyName The key name for the EncryptedContent.
+ * @param {Blob} key The encryption key value.
+ * @param {EncryptParams} params The parameters for encryption.
+ * @throws Error If encryptPromise doesn't return a SyncPromise which is
+ * already fulfilled.
+ */
+Encryptor.encryptData = function(data, payload, keyName, key, params)
+{
+ return SyncPromise.getValue(Encryptor.encryptDataPromise
+ (data, payload, keyName, key, params, true));
+};
+
+/**
+ * Encrypt the payload using the symmetric key according to params, and return
+ * an EncryptedContent.
+ * @param {Blob} payload The data to encrypt.
+ * @param {Blob} key The key value.
+ * @param {Name} keyName The key name for the EncryptedContent key locator.
+ * @param {EncryptParams} params The parameters for encryption.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns a new EncryptedContent.
+ */
+Encryptor.encryptSymmetricPromise_ = function
+ (payload, key, keyName, params, useSync)
+{
+ var algorithmType = params.getAlgorithmType();
+ var initialVector = params.getInitialVector();
+ var keyLocator = new KeyLocator();
+ keyLocator.setType(KeyLocatorType.KEYNAME);
+ keyLocator.setKeyName(keyName);
+
+ if (algorithmType == EncryptAlgorithmType.AesCbc ||
+ algorithmType == EncryptAlgorithmType.AesEcb) {
+ if (algorithmType == EncryptAlgorithmType.AesCbc) {
+ if (initialVector.size() != AesAlgorithm.BLOCK_SIZE)
+ return SyncPromise.reject(new Error("incorrect initial vector size"));
+ }
+
+ return AesAlgorithm.encryptPromise(key, payload, params, useSync)
+ .then(function(encryptedPayload) {
+ var result = new EncryptedContent();
+ result.setAlgorithmType(algorithmType);
+ result.setKeyLocator(keyLocator);
+ result.setPayload(encryptedPayload);
+ result.setInitialVector(initialVector);
+ return SyncPromise.resolve(result);
+ });
+ }
+ else
+ return SyncPromise.reject(new Error("Unsupported encryption method"));
+};
+
+/**
+ * Encrypt the payload using the asymmetric key according to params, and
+ * return an EncryptedContent.
+ * @param {Blob} payload The data to encrypt. The size should be within range of
+ * the key.
+ * @param {Blob} key The key value.
+ * @param {Name} keyName The key name for the EncryptedContent key locator.
+ * @param {EncryptParams} params The parameters for encryption.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns a new EncryptedContent.
+ */
+Encryptor.encryptAsymmetricPromise_ = function
+ (payload, key, keyName, params, useSync)
+{
+ var algorithmType = params.getAlgorithmType();
+ var keyLocator = new KeyLocator();
+ keyLocator.setType(KeyLocatorType.KEYNAME);
+ keyLocator.setKeyName(keyName);
+
+ if (algorithmType == EncryptAlgorithmType.RsaPkcs ||
+ algorithmType == EncryptAlgorithmType.RsaOaep) {
+ return RsaAlgorithm.encryptPromise(key, payload, params, useSync)
+ .then(function(encryptedPayload) {
+ var result = new EncryptedContent();
+ result.setAlgorithmType(algorithmType);
+ result.setKeyLocator(keyLocator);
+ result.setPayload(encryptedPayload);
+ return SyncPromise.resolve(result);
+ });
+ }
+ else
+ return SyncPromise.reject(new Error("Unsupported encryption method"));
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/algo/rsa https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// (This is ported from ndn::gep::algo::Rsa, and named RsaAlgorithm because
+// "Rsa" is very short and not all the Common Client Libraries have namespaces.)
+
+/** @ignore */
+var constants = require('constants'); /** @ignore */
+var Crypto = require('../../crypto.js'); /** @ignore */
+var Blob = require('../../util/blob.js').Blob; /** @ignore */
+var DecryptKey = require('../decrypt-key.js').DecryptKey; /** @ignore */
+var EncryptKey = require('../encrypt-key.js').EncryptKey; /** @ignore */
+var EncryptAlgorithmType = require('./encrypt-params.js').EncryptAlgorithmType; /** @ignore */
+var DerNode = require('../../encoding/der/der-node.js').DerNode; /** @ignore */
+var OID = require('../../encoding/oid.js').OID; /** @ignore */
+var PrivateKeyStorage = require('../../security/identity/private-key-storage.js').PrivateKeyStorage; /** @ignore */
+var UseSubtleCrypto = require('../../use-subtle-crypto-node.js').UseSubtleCrypto; /** @ignore */
+var SyncPromise = require('../../util/sync-promise.js').SyncPromise; /** @ignore */
+var rsaKeygen = null;
+try {
+ // This should be installed with: sudo npm install rsa-keygen
+ rsaKeygen = require('rsa-keygen');
+}
+catch (e) {}
+
+/**
+ * The RsaAlgorithm class provides static methods to manipulate keys, encrypt
+ * and decrypt using RSA.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var RsaAlgorithm = function RsaAlgorithm()
+{
+};
+
+exports.RsaAlgorithm = RsaAlgorithm;
+
+/**
+ * Generate a new random decrypt key for RSA based on the given params.
+ * @param {RsaKeyParams} params The key params with the key size (in bits).
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the new DecryptKey
+ * (containing a PKCS8-encoded private key).
+ */
+RsaAlgorithm.generateKeyPromise = function(params, useSync)
+{
+ if (UseSubtleCrypto() && !useSync) {
+ return crypto.subtle.generateKey
+ ({ name: "RSASSA-PKCS1-v1_5", modulusLength: params.getKeySize(),
+ publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
+ hash: {name: "SHA-256"} },
+ true, ["sign", "verify"])
+ .then(function(key) {
+ // Export the private key to DER.
+ return crypto.subtle.exportKey("pkcs8", key.privateKey);
+ })
+ .then(function(pkcs8Der) {
+ return Promise.resolve(new DecryptKey
+ (new Blob(new Uint8Array(pkcs8Der), false)));
+ });
+ }
+ else {
+ if (!rsaKeygen)
+ return SyncPromise.reject(new Error
+ ("Need to install rsa-keygen: sudo npm install rsa-keygen"));
+
+ try {
+ var keyPair = rsaKeygen.generate(params.getKeySize());
+ // Get the PKCS1 private key DER from the PEM string and encode as PKCS8.
+ var privateKeyBase64 = keyPair.private_key.toString().replace
+ ("-----BEGIN RSA PRIVATE KEY-----", "").replace
+ ("-----END RSA PRIVATE KEY-----", "");
+ var pkcs1PrivateKeyDer = new Buffer(privateKeyBase64, 'base64');
+ var privateKey = PrivateKeyStorage.encodePkcs8PrivateKey
+ (pkcs1PrivateKeyDer, new OID(PrivateKeyStorage.RSA_ENCRYPTION_OID),
+ new DerNode.DerNull()).buf();
+
+ return SyncPromise.resolve(new DecryptKey(privateKey));
+ } catch (err) {
+ return SyncPromise.reject(err);
+ }
+ }
+};
+
+/**
+ * Generate a new random decrypt key for RSA based on the given params.
+ * @param {RsaKeyParams} params The key params with the key size (in bits).
+ * @return {DecryptKey} The new decrypt key (containing a PKCS8-encoded private
+ * key).
+ * @throws Error If generateKeyPromise doesn't return a SyncPromise which is
+ * already fulfilled.
+ */
+RsaAlgorithm.generateKey = function(params)
+{
+ return SyncPromise.getValue(this.generateKeyPromise(params, true));
+};
+
+/**
+ * Derive a new encrypt key from the given decrypt key value.
+ * @param {Blob} keyBits The key value of the decrypt key (PKCS8-encoded private
+ * key).
+ * @return {EncryptKey} The new encrypt key (DER-encoded public key).
+ */
+RsaAlgorithm.deriveEncryptKey = function(keyBits)
+{
+ var rsaPrivateKeyDer = RsaAlgorithm.getRsaPrivateKeyDer(keyBits);
+
+ // Decode the PKCS #1 RSAPrivateKey.
+ var parsedNode = DerNode.parse(rsaPrivateKeyDer.buf(), 0);
+ var rsaPrivateKeyChildren = parsedNode.getChildren();
+ var modulus = rsaPrivateKeyChildren[1];
+ var publicExponent = rsaPrivateKeyChildren[2];
+
+ // Encode the PKCS #1 RSAPublicKey.
+ var rsaPublicKey = new DerNode.DerSequence();
+ rsaPublicKey.addChild(modulus);
+ rsaPublicKey.addChild(publicExponent);
+ var rsaPublicKeyDer = rsaPublicKey.encode();
+
+ // Encode the SubjectPublicKeyInfo.
+ var algorithmIdentifier = new DerNode.DerSequence();
+ algorithmIdentifier.addChild(new DerNode.DerOid(new OID
+ (PrivateKeyStorage.RSA_ENCRYPTION_OID)));
+ algorithmIdentifier.addChild(new DerNode.DerNull());
+ var publicKey = new DerNode.DerSequence();
+ publicKey.addChild(algorithmIdentifier);
+ publicKey.addChild(new DerNode.DerBitString(rsaPublicKeyDer.buf(), 0));
+
+ return new EncryptKey(publicKey.encode());
+};
+
+/**
+ * Decrypt the encryptedData using the keyBits according the encrypt params.
+ * @param {Blob} keyBits The key value (PKCS8-encoded private key).
+ * @param {Blob} encryptedData The data to decrypt.
+ * @param {EncryptParams} params This decrypts according to
+ * params.getAlgorithmType().
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the decrypted Blob.
+ */
+RsaAlgorithm.decryptPromise = function(keyBits, encryptedData, params, useSync)
+{
+ if (UseSubtleCrypto() && !useSync &&
+ // Crypto.subtle doesn't implement PKCS1 padding.
+ params.getAlgorithmType() != EncryptAlgorithmType.RsaPkcs) {
+ if (params.getAlgorithmType() == EncryptAlgorithmType.RsaOaep) {
+ return crypto.subtle.importKey
+ ("pkcs8", keyBits.buf(), { name: "RSA-OAEP", hash: {name: "SHA-1"} },
+ false, ["decrypt"])
+ .then(function(privateKey) {
+ return crypto.subtle.decrypt
+ ({ name: "RSA-OAEP" }, privateKey, encryptedData.buf());
+ })
+ .then(function(result) {
+ return Promise.resolve(new Blob(new Uint8Array(result), false));
+ });
+ }
+ else
+ return Promise.reject(new Error("unsupported padding scheme"));
+ }
+ else {
+ // keyBits is PKCS #8 but we need the inner RSAPrivateKey.
+ var rsaPrivateKeyDer = RsaAlgorithm.getRsaPrivateKeyDer(keyBits);
+
+ // Encode the key DER as a PEM private key as needed by Crypto.
+ var keyBase64 = rsaPrivateKeyDer.buf().toString('base64');
+ var keyPem = "-----BEGIN RSA PRIVATE KEY-----\n";
+ for (var i = 0; i < keyBase64.length; i += 64)
+ keyPem += (keyBase64.substr(i, 64) + "\n");
+ keyPem += "-----END RSA PRIVATE KEY-----";
+
+ var padding;
+ if (params.getAlgorithmType() == EncryptAlgorithmType.RsaPkcs)
+ padding = constants.RSA_PKCS1_PADDING;
+ else if (params.getAlgorithmType() == EncryptAlgorithmType.RsaOaep)
+ padding = constants.RSA_PKCS1_OAEP_PADDING;
+ else
+ return SyncPromise.reject(new Error("unsupported padding scheme"));
+
+ try {
+ // In Node.js, privateDecrypt requires version v0.12.
+ return SyncPromise.resolve(new Blob
+ (Crypto.privateDecrypt({ key: keyPem, padding: padding }, encryptedData.buf()),
+ false));
+ } catch (err) {
+ return SyncPromise.reject(err);
+ }
+ }
+};
+
+/**
+ * Decrypt the encryptedData using the keyBits according the encrypt params.
+ * @param {Blob} keyBits The key value (PKCS8-encoded private key).
+ * @param {Blob} encryptedData The data to decrypt.
+ * @param {EncryptParams} params This decrypts according to
+ * params.getAlgorithmType().
+ * @return {Blob} The decrypted data.
+ * @throws Error If decryptPromise doesn't return a SyncPromise which is
+ * already fulfilled.
+ */
+RsaAlgorithm.decrypt = function(keyBits, encryptedData, params)
+{
+ return SyncPromise.getValue(this.decryptPromise
+ (keyBits, encryptedData, params, true));
+};
+
+/**
+ * Encrypt the plainData using the keyBits according the encrypt params.
+ * @param {Blob} keyBits The key value (DER-encoded public key).
+ * @param {Blob} plainData The data to encrypt.
+ * @param {EncryptParams} params This encrypts according to
+ * params.getAlgorithmType().
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise which returns the encrypted Blob.
+ */
+RsaAlgorithm.encryptPromise = function(keyBits, plainData, params, useSync)
+{
+ if (UseSubtleCrypto() && !useSync &&
+ // Crypto.subtle doesn't implement PKCS1 padding.
+ params.getAlgorithmType() != EncryptAlgorithmType.RsaPkcs) {
+ if (params.getAlgorithmType() == EncryptAlgorithmType.RsaOaep) {
+ return crypto.subtle.importKey
+ ("spki", keyBits.buf(), { name: "RSA-OAEP", hash: {name: "SHA-1"} },
+ false, ["encrypt"])
+ .then(function(publicKey) {
+ return crypto.subtle.encrypt
+ ({ name: "RSA-OAEP" }, publicKey, plainData.buf());
+ })
+ .then(function(result) {
+ return Promise.resolve(new Blob(new Uint8Array(result), false));
+ });
+ }
+ else
+ return Promise.reject(new Error("unsupported padding scheme"));
+ }
+ else {
+ // Encode the key DER as a PEM public key as needed by Crypto.
+ var keyBase64 = keyBits.buf().toString('base64');
+ var keyPem = "-----BEGIN PUBLIC KEY-----\n";
+ for (var i = 0; i < keyBase64.length; i += 64)
+ keyPem += (keyBase64.substr(i, 64) + "\n");
+ keyPem += "-----END PUBLIC KEY-----";
+
+ var padding;
+ if (params.getAlgorithmType() == EncryptAlgorithmType.RsaPkcs)
+ padding = constants.RSA_PKCS1_PADDING;
+ else if (params.getAlgorithmType() == EncryptAlgorithmType.RsaOaep)
+ padding = constants.RSA_PKCS1_OAEP_PADDING;
+ else
+ return SyncPromise.reject(new Error("unsupported padding scheme"));
+
+ try {
+ // In Node.js, publicEncrypt requires version v0.12.
+ return SyncPromise.resolve(new Blob
+ (Crypto.publicEncrypt({ key: keyPem, padding: padding }, plainData.buf()),
+ false));
+ } catch (err) {
+ return SyncPromise.reject(err);
+ }
+ }
+};
+
+/**
+ * Encrypt the plainData using the keyBits according the encrypt params.
+ * @param {Blob} keyBits The key value (DER-encoded public key).
+ * @param {Blob} plainData The data to encrypt.
+ * @param {EncryptParams} params This encrypts according to
+ * params.getAlgorithmType().
+ * @return {Blob} The encrypted data.
+ * @throws Error If encryptPromise doesn't return a SyncPromise which is
+ * already fulfilled.
+ */
+RsaAlgorithm.encrypt = function(keyBits, plainData, params)
+{
+ return SyncPromise.getValue(this.encryptPromise
+ (keyBits, plainData, params, true));
+};
+
+/**
+ * Decode the PKCS #8 private key, check that the algorithm is RSA, and return
+ * the inner RSAPrivateKey DER.
+ * @param {Blob} The DER-encoded PKCS #8 private key.
+ * @param {Blob} The DER-encoded RSAPrivateKey.
+ */
+RsaAlgorithm.getRsaPrivateKeyDer = function(pkcs8PrivateKeyDer)
+{
+ var parsedNode = DerNode.parse(pkcs8PrivateKeyDer.buf(), 0);
+ var pkcs8Children = parsedNode.getChildren();
+ var algorithmIdChildren = DerNode.getSequence(pkcs8Children, 1).getChildren();
+ var oidString = algorithmIdChildren[0].toVal();
+
+ if (oidString != PrivateKeyStorage.RSA_ENCRYPTION_OID)
+ throw new Error("The PKCS #8 private key is not RSA_ENCRYPTION");
+
+ return pkcs8Children[2].getPayload();
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/consumer-db https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var SyncPromise = require('../util/sync-promise.js').SyncPromise;
+
+/**
+ * ConsumerDb is a base class the storage of decryption keys for the consumer. A
+ * subclass must implement the methods. For example, see Sqlite3ConsumerDb (for
+ * Nodejs) or IndexedDbConsumerDb (for the browser).
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var ConsumerDb = function ConsumerDb()
+{
+};
+
+exports.ConsumerDb = ConsumerDb;
+
+/**
+ * Create a new ConsumerDb.Error to report an error using ConsumerDb
+ * methods, wrapping the given error object.
+ * Call with: throw new ConsumerDb.Error(new Error("message")).
+ * @constructor
+ * @param {Error} error The exception created with new Error.
+ */
+ConsumerDb.Error = function ConsumerDbError(error)
+{
+ if (error) {
+ error.__proto__ = ConsumerDb.Error.prototype;
+ return error;
+ }
+}
+
+ConsumerDb.Error.prototype = new Error();
+ConsumerDb.Error.prototype.name = "ConsumerDbError";
+
+/**
+ * Get the key with keyName from the database.
+ * @param {Name} keyName The key name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns a Blob with the encoded
+ * key (or an isNull Blob if cannot find the key with keyName), or that is
+ * rejected with ConsumerDb.Error for a database error.
+ */
+ConsumerDb.prototype.getKeyPromise = function(keyName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("ConsumerDb.getKeyPromise is not implemented"));
+};
+
+/**
+ * Add the key with keyName and keyBlob to the database.
+ * @param {Name} keyName The key name.
+ * @param {Blob} keyBlob The encoded key.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the key is added,
+ * or that is rejected with ConsumerDb.Error if a key with the same keyName
+ * already exists, or other database error.
+ */
+ConsumerDb.prototype.addKeyPromise = function(keyName, keyBlob, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("ConsumerDb.addKeyPromise is not implemented"));
+};
+
+/**
+ * Delete the key with keyName from the database. If there is no key with
+ * keyName, do nothing.
+ * @param {Name} keyName The key name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the key is deleted
+ * (or there is no such key), or that is rejected with ConsumerDb.Error for a
+ * database error.
+ */
+ConsumerDb.prototype.deleteKeyPromise = function(keyName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("ConsumerDb.addKeyPromise is not implemented"));
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/consumer https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('../util/blob.js').Blob; /** @ignore */
+var Name = require('../name.js').Name; /** @ignore */
+var Interest = require('../interest.js').Interest; /** @ignore */
+var NetworkNack = require('../network-nack.js').NetworkNack; /** @ignore */
+var Link = require('../link.js').Link; /** @ignore */
+var EncryptedContent = require('./encrypted-content.js').EncryptedContent; /** @ignore */
+var EncryptError = require('./encrypt-error.js').EncryptError; /** @ignore */
+var EncryptParams = require('./algo/encrypt-params.js').EncryptParams; /** @ignore */
+var EncryptAlgorithmType = require('./algo/encrypt-params.js').EncryptAlgorithmType; /** @ignore */
+var RsaAlgorithm = require('./algo/rsa-algorithm.js').RsaAlgorithm; /** @ignore */
+var AesAlgorithm = require('./algo/aes-algorithm.js').AesAlgorithm; /** @ignore */
+var Encryptor = require('./algo/encryptor.js').Encryptor; /** @ignore */
+var SyncPromise = require('../util/sync-promise.js').SyncPromise; /** @ignore */
+var NdnCommon = require('../util/ndn-common.js').NdnCommon;
+
+/**
+ * A Consumer manages fetched group keys used to decrypt a data packet in the
+ * group-based encryption protocol.
+ * Create a Consumer to use the given ConsumerDb, Face and other values.
+ * @param {Face} face The face used for data packet and key fetching.
+ * @param {KeyChain} keyChain The keyChain used to verify data packets.
+ * @param {Name} groupName The reading group name that the consumer belongs to.
+ * This makes a copy of the Name.
+ * @param {Name} consumerName The identity of the consumer. This makes a copy of
+ * the Name.
+ * @param {ConsumerDb} database The ConsumerDb database for storing decryption
+ * keys.
+ * @param {Link} cKeyLink (optional) The Link object to use in Interests for
+ * C-KEY retrieval. This makes a copy of the Link object. If the Link object's
+ * getDelegations().size() is zero, don't use it. If omitted, don't use a Link
+ * object.
+ * @param {Link} dKeyLink (optional) The Link object to use in Interests for
+ * D-KEY retrieval. This makes a copy of the Link object. If the Link object's
+ * getDelegations().size() is zero, don't use it. If omitted, don't use a Link
+ * object.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var Consumer = function Consumer
+ (face, keyChain, groupName, consumerName, database, cKeyLink, dKeyLink)
+{
+ this.database_ = database;
+ this.keyChain_ = keyChain;
+ this.face_ = face;
+ this.groupName_ = new Name(groupName);
+ this.consumerName_ = new Name(consumerName);
+ this.cKeyLink_ =
+ (cKeyLink == undefined ? Consumer.NO_LINK : new Link(cKeyLink));
+ this.dKeyLink_ =
+ (dKeyLink == undefined ? Consumer.NO_LINK : new Link(dKeyLink));
+
+ // The map key is the C-KEY name URI string. The value is the encoded key Blob.
+ // (Use a string because we can't use the Name object as the key in JavaScript.)
+ this.cKeyMap_ = {};
+ // The map key is the D-KEY name URI string. The value is the encoded key Blob.
+ this.dKeyMap_ = {};
+};
+
+exports.Consumer = Consumer;
+
+/**
+ * Express an Interest to fetch the content packet with contentName, and
+ * decrypt it, fetching keys as needed.
+ * @param {Name} contentName The name of the content packet.
+ * @param {function} onConsumeComplete When the content packet is fetched and
+ * decrypted, this calls onConsumeComplete(contentData, result) where
+ * contentData is the fetched Data packet and result is the decrypted plain
+ * text Blob.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError This calls onError(errorCode, message) for an error,
+ * where errorCode is an error code from EncryptError.ErrorCode.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param link {Link} (optional) The Link object to use in Interests for data
+ * retrieval. This makes a copy of the Link object. If the Link object's
+ * getDelegations().size() is zero, don't use it. If omitted, don't use a Link
+ * object.
+ */
+Consumer.prototype.consume = function
+ (contentName, onConsumeComplete, onError, link)
+{
+ if (link == undefined)
+ link = Consumer.NO_LINK;
+
+ var interest = new Interest(contentName);
+ var thisConsumer = this;
+ // Copy the Link object since the passed link may become invalid.
+ this.sendInterest_
+ (interest, 1, new Link(link),
+ function(validData) {
+ // Decrypt the content.
+ thisConsumer.decryptContent_(validData, function(plainText) {
+ try {
+ onConsumeComplete(validData, plainText);
+ } catch (ex) {
+ console.log("Error in onConsumeComplete: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }, onError);
+ },
+ onError);
+};
+
+/**
+ * Set the group name.
+ * @param {Name} groupName The reading group name that the consumer belongs to.
+ * This makes a copy of the Name.
+ */
+Consumer.prototype.setGroup = function(groupName)
+{
+ this.groupName_ = new Name(groupName);
+};
+
+/**
+ * Add a new decryption key with keyName and keyBlob to the database.
+ * @param {Name} keyName The key name.
+ * @param {Blob} keyBlob The encoded key.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the key is added,
+ * or that is rejected with Error if the consumer name is not a prefix of the
+ * key name, or ConsumerDb.Error if a key with the same keyName already exists,
+ * or other database error.
+ */
+Consumer.prototype.addDecryptionKeyPromise = function(keyName, keyBlob, useSync)
+{
+ if (!(this.consumerName_.match(keyName)))
+ return SyncPromise.reject(new Error
+ ("addDecryptionKey: The consumer name must be a prefix of the key name"));
+
+ return this.database_.addKeyPromise(keyName, keyBlob, useSync);
+};
+
+/**
+ * Add a new decryption key with keyName and keyBlob to the database.
+ * @param {Name} keyName The key name.
+ * @param {Blob} keyBlob The encoded key.
+ * @param {function} onComplete (optional) This calls onComplete() when the key
+ * is added. (Some database libraries only use a callback, so onComplete is
+ * required to use these.)
+ * @param {function} onError (optional) If defined, then onComplete must be
+ * defined and if there is an exception, then this calls onError(exception)
+ * where exception is Error if the consumer name is not a prefix of the key
+ * name, or ConsumerDb.Error if a key with the same keyName already exists,
+ * or other database error. If onComplete is defined but onError is undefined,
+ * then this will log any thrown exception. (Some database libraries only use a
+ * callback, so onError is required to be notified of an exception.)
+ */
+Consumer.prototype.addDecryptionKey = function
+ (keyName, keyBlob, onComplete, onError)
+{
+ return SyncPromise.complete(onComplete, onError,
+ this.addDecryptionKeyPromise(keyName, keyBlob, !onComplete));
+};
+
+/**
+ * Consume.Error is used internally from promised-based methods to reject with
+ * an error object that has the errorCode and message returned through the
+ * onError callback.
+ * @param {number} errorCode An error code from EncryptError.ErrorCode.
+ * @param {string} message The error message.
+ */
+Consumer.Error = function ConsumerError(errorCode, message)
+{
+ this.errorCode = errorCode;
+ this.message = message;
+};
+
+/**
+ * If exception is a ConsumerError, then call onError with the errorCode and
+ * message, otherwise call onError with ErrorCode.General.
+ */
+Consumer.Error.callOnError = function(onError, exception, messagePrefix)
+{
+ if (!messagePrefix)
+ messagePrefix = "";
+
+ if (exception instanceof Consumer.Error) {
+ try {
+ onError(exception.errorCode, exception.message);
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ else {
+ try {
+ onError(EncryptError.ErrorCode.General, messagePrefix + exception);
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+}
+
+/**
+ * Decrypt encryptedContent using keyBits.
+ * @param {Blob|EncryptedContent} encryptedContent The EncryptedContent to
+ * decrypt, or a Blob which is first decoded as an EncryptedContent.
+ * @param {Blob} keyBits The key value.
+ * @return {Promise|SyncPromise} A promise that returns the decrypted Blob, or
+ * that is rejected with Consumer.Error or other error.
+ */
+Consumer.decryptPromise_ = function(encryptedContent, keyBits)
+{
+ return SyncPromise.resolve()
+ .then(function() {
+ if (typeof encryptedContent == 'object' && encryptedContent instanceof Blob) {
+ // Decode as EncryptedContent.
+ var encryptedBlob = encryptedContent;
+ encryptedContent = new EncryptedContent();
+ encryptedContent.wireDecode(encryptedBlob);
+ }
+
+ var payload = encryptedContent.getPayload();
+
+ if (encryptedContent.getAlgorithmType() == EncryptAlgorithmType.AesCbc) {
+ // Prepare the parameters.
+ var decryptParams = new EncryptParams(EncryptAlgorithmType.AesCbc);
+ decryptParams.setInitialVector(encryptedContent.getInitialVector());
+
+ // Decrypt the content.
+ return AesAlgorithm.decryptPromise(keyBits, payload, decryptParams);
+ }
+ else if (encryptedContent.getAlgorithmType() == EncryptAlgorithmType.RsaOaep) {
+ // Prepare the parameters.
+ var decryptParams = new EncryptParams(EncryptAlgorithmType.RsaOaep);
+
+ // Decrypt the content.
+ return RsaAlgorithm.decryptPromise(keyBits, payload, decryptParams);
+ }
+ else
+ return SyncPromise.reject(new Consumer.Error
+ (EncryptError.ErrorCode.UnsupportedEncryptionScheme,
+ "" + encryptedContent.getAlgorithmType()));
+ });
+};
+
+/**
+ * Decrypt encryptedContent using keyBits.
+ * @param {Blob|EncryptedContent} encryptedContent The EncryptedContent to
+ * decrypt, or a Blob which is first decoded as an EncryptedContent.
+ * @param {Blob} keyBits The key value.
+ * @param {function} onPlainText When the data packet is decrypted, this calls
+ * onPlainText(decryptedBlob) with the decrypted Blob.
+ * @param {function} onError This calls onError(errorCode, message) for an error,
+ * where errorCode is an error code from EncryptError.ErrorCode.
+ */
+Consumer.decrypt_ = function(encryptedContent, keyBits, onPlainText, onError)
+{
+ Consumer.decryptPromise_(encryptedContent, keyBits)
+ .then(function(decryptedBlob) {
+ onPlainText(decryptedBlob);
+ }, function(ex) {
+ Consumer.Error.callOnError(onError, ex);
+ });
+};
+
+/**
+ * Decrypt the data packet.
+ * @param {Data} data The data packet. This does not verify the packet.
+ * @param {function} onPlainText When the data packet is decrypted, this calls
+ * onPlainText(decryptedBlob) with the decrypted Blob.
+ * @param {function} onError This calls onError(errorCode, message) for an error,
+ * where errorCode is an error code from EncryptError.ErrorCode.
+ */
+Consumer.prototype.decryptContent_ = function(data, onPlainText, onError)
+{
+ // Get the encrypted content.
+ var dataEncryptedContent = new EncryptedContent();
+ try {
+ dataEncryptedContent.wireDecode(data.getContent());
+ } catch (ex) {
+ Consumer.Error.callOnError(onError, ex, "Error decoding EncryptedContent: ");
+ return;
+ }
+ var cKeyName = dataEncryptedContent.getKeyLocator().getKeyName();
+
+ // Check if the content key is already in the store.
+ var cKey = this.cKeyMap_[cKeyName.toUri()];
+ if (cKey)
+ this.decrypt_(dataEncryptedContent, cKey, onPlainText, onError);
+ else {
+ // Retrieve the C-KEY Data from the network.
+ var interestName = new Name(cKeyName);
+ interestName.append(Encryptor.NAME_COMPONENT_FOR).append(this.groupName_);
+ var interest = new Interest(interestName);
+ var thisConsumer = this;
+ this.sendInterest_
+ (interest, 1, this.cKeyLink_,
+ function(validCKeyData) {
+ thisConsumer.decryptCKey_(validCKeyData, function(cKeyBits) {
+ thisConsumer.cKeyMap_[cKeyName.toUri()] = cKeyBits;
+ Consumer.decrypt_
+ (dataEncryptedContent, cKeyBits, onPlainText, onError);
+ }, onError);
+ },
+ onError);
+ }
+};
+
+/**
+ * Decrypt cKeyData.
+ * @param {Data} cKeyData The C-KEY data packet.
+ * @param {function} onPlainText When the data packet is decrypted, this calls
+ * onPlainText(decryptedBlob) with the decrypted Blob.
+ * @param {function} onError This calls onError(errorCode, message) for an error,
+ * where errorCode is an error code from EncryptError.ErrorCode.
+ */
+Consumer.prototype.decryptCKey_ = function(cKeyData, onPlainText, onError)
+{
+ // Get the encrypted content.
+ var cKeyContent = cKeyData.getContent();
+ var cKeyEncryptedContent = new EncryptedContent();
+ try {
+ cKeyEncryptedContent.wireDecode(cKeyContent);
+ } catch (ex) {
+ Consumer.Error.callOnError(onError, ex, "Error decoding EncryptedContent: ");
+ return;
+ }
+ var eKeyName = cKeyEncryptedContent.getKeyLocator().getKeyName();
+ var dKeyName = eKeyName.getPrefix(-3);
+ dKeyName.append(Encryptor.NAME_COMPONENT_D_KEY).append(eKeyName.getSubName(-2));
+
+ // Check if the decryption key is already in the store.
+ var dKey = this.dKeyMap_[dKeyName.toUri()];
+ if (dKey)
+ this.decrypt_(cKeyEncryptedContent, dKey, onPlainText, onError);
+ else {
+ // Get the D-Key Data.
+ var interestName = new Name(dKeyName);
+ interestName.append(Encryptor.NAME_COMPONENT_FOR).append(this.consumerName_);
+ var interest = new Interest(interestName);
+ var thisConsumer = this;
+ this.sendInterest_
+ (interest, 1, this.dKeyLink_,
+ function(validDKeyData) {
+ thisConsumer.decryptDKeyPromise_(validDKeyData)
+ .then(function(dKeyBits) {
+ thisConsumer.dKeyMap_[dKeyName.toUri()] = dKeyBits;
+ Consumer.decrypt_
+ (cKeyEncryptedContent, dKeyBits, onPlainText, onError);
+ }, function(ex) {
+ Consumer.Error.callOnError(onError, ex, "decryptDKey error: ");
+ });
+ },
+ onError);
+ }
+};
+
+/**
+ * Decrypt dKeyData.
+ * @param {Data} dKeyData The D-KEY data packet.
+ * @return {Promise|SyncPromise} A promise that returns the decrypted Blob, or
+ * that is rejected with Consumer.Error or other error.
+ */
+Consumer.prototype.decryptDKeyPromise_ = function(dKeyData)
+{
+ var dataContent;
+ var encryptedNonce;
+ var encryptedPayloadBlob;
+ var thisConsumer = this;
+
+ return SyncPromise.resolve()
+ .then(function() {
+ // Get the encrypted content.
+ dataContent = dKeyData.getContent();
+
+ // Process the nonce.
+ // dataContent is a sequence of the two EncryptedContent.
+ encryptedNonce = new EncryptedContent();
+ encryptedNonce.wireDecode(dataContent);
+ var consumerKeyName = encryptedNonce.getKeyLocator().getKeyName();
+
+ // Get consumer decryption key.
+ return thisConsumer.getDecryptionKeyPromise_(consumerKeyName);
+ })
+ .then(function(consumerKeyBlob) {
+ if (consumerKeyBlob.size() == 0)
+ return SyncPromise.reject(new Consumer.Error
+ (EncryptError.ErrorCode.NoDecryptKey,
+ "The desired consumer decryption key in not in the database"));
+
+ // Process the D-KEY.
+ // Use the size of encryptedNonce to find the start of encryptedPayload.
+ var encryptedPayloadBuffer = dataContent.buf().slice
+ (encryptedNonce.wireEncode().size());
+ encryptedPayloadBlob = new Blob(encryptedPayloadBuffer, false);
+ if (encryptedPayloadBlob.size() == 0)
+ return SyncPromise.reject(new Consumer.Error
+ (EncryptError.ErrorCode.InvalidEncryptedFormat,
+ "The data packet does not satisfy the D-KEY packet format"));
+
+ // Decrypt the D-KEY.
+ return Consumer.decryptPromise_(encryptedNonce, consumerKeyBlob);
+ })
+ .then(function(nonceKeyBits) {
+ return Consumer.decryptPromise_(encryptedPayloadBlob, nonceKeyBits);
+ });
+};
+
+/**
+ * Express the interest, call verifyData for the fetched Data packet and call
+ * onVerified if verify succeeds. If verify fails, call
+ * onError(EncryptError.ErrorCode.Validation, "verifyData failed"). If the
+ * interest times out, re-express nRetrials times. If the interest times out
+ * nRetrials times, or for a network Nack, call
+ * onError(EncryptError.ErrorCode.DataRetrievalFailure, interest.getName().toUri()).
+ * @param {Interest} interest The Interest to express.
+ * @param {number} nRetrials The number of retrials left after a timeout.
+ * @param {Link} link The Link object to use in the Interest. This does not make
+ * a copy of the Link object. If the Link object's getDelegations().size() is
+ * zero, don't use it.
+ * @param {function} onVerified When the fetched Data packet validation
+ * succeeds, this calls onVerified(data).
+ * @param {function} onError This calls onError(errorCode, message) for an error,
+ * where errorCode is an error code from EncryptError.ErrorCode.
+ */
+Consumer.prototype.sendInterest_ = function
+ (interest, nRetrials, link, onVerified, onError)
+{
+ // Prepare the callback functions.
+ var thisConsumer = this;
+ var onData = function(contentInterest, contentData) {
+ try {
+ thisConsumer.keyChain_.verifyData
+ (contentData, onVerified,
+ function(d, reason) {
+ try {
+ onError
+ (EncryptError.ErrorCode.Validation, "verifyData failed. Reason: " +
+ reason);
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ });
+ } catch (ex) {
+ Consumer.Error.callOnError(onError, ex, "verifyData error: ");
+ }
+ };
+
+ function onNetworkNack(interest, networkNack) {
+ // We have run out of options. Report a retrieval failure.
+ try {
+ onError(EncryptError.ErrorCode.DataRetrievalFailure,
+ interest.getName().toUri());
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+
+ var onTimeout = function(interest) {
+ if (nRetrials > 0)
+ thisConsumer.sendInterest_(interest, nRetrials - 1, link, onVerified, onError);
+ else
+ onNetworkNack(interest, new NetworkNack());
+ };
+
+ var request;
+ if (link.getDelegations().size() === 0)
+ // We can use the supplied interest without copying.
+ request = interest;
+ else {
+ // Copy the supplied interest and add the Link.
+ request = new Interest(interest);
+ // This will use a cached encoding if available.
+ request.setLinkWireEncoding(link.wireEncode());
+ }
+
+ try {
+ this.face_.expressInterest(request, onData, onTimeout, onNetworkNack);
+ } catch (ex) {
+ Consumer.Error.callOnError(onError, ex, "expressInterest error: ");
+ }
+};
+
+/**
+ * Get the encoded blob of the decryption key with decryptionKeyName from the
+ * database.
+ * @param {Name} decryptionKeyName The key name.
+ * @return {Promise|SyncPromise} A promise that returns a Blob with the encoded
+ * key (or an isNull Blob if cannot find the key with decryptionKeyName), or
+ * that is rejected with ConsumerDb.Error for a database error.
+ */
+Consumer.prototype.getDecryptionKeyPromise_ = function(decryptionKeyName)
+{
+ return this.database_.getKeyPromise(decryptionKeyName);
+};
+
+Consumer.NO_LINK = new Link();
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/decrypt-key https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('../util/blob.js').Blob;
+
+/**
+ * A DecryptKey supplies the key for decrypt.
+ * Create a DecryptKey with the given key value.
+ * @param {Blob|DecryptKey} value If value is another DecryptKey then copy it.
+ * Otherwise, value is the key value.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var DecryptKey = function DecryptKey(value)
+{
+ if (typeof value === 'object' && value instanceof DecryptKey) {
+ // Make a deep copy.
+ this.keyBits_ = value.keyBits_;
+ }
+ else {
+ var keyBits = value;
+ this.keyBits_ = typeof keyBits === 'object' && keyBits instanceof Blob ?
+ keyBits : new Blob(keyBits);
+ }
+};
+
+exports.DecryptKey = DecryptKey;
+
+/**
+ * Get the key value.
+ * @return {Blob} The key value.
+ */
+DecryptKey.prototype.getKeyBits = function() { return this.keyBits_; };
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/error-code https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * EncryptError holds the ErrorCode enum for errors from the encrypt library.
+ */
+var EncryptError = function EncryptError()
+{
+};
+
+exports.EncryptError = EncryptError;
+
+EncryptError.ErrorCode = {
+ Timeout: 1,
+ Validation: 2,
+ UnsupportedEncryptionScheme: 32,
+ InvalidEncryptedFormat: 33,
+ NoDecryptKey: 34,
+ EncryptionFailure: 35,
+ DataRetrievalFailure: 36,
+ General: 100
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/encrypt-key https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('../util/blob.js').Blob;
+
+/**
+ * An EncryptKey supplies the key for encrypt.
+ * Create an EncryptKey with the given key value.
+ * @param {Blob|EncryptKey} value If value is another EncryptKey then copy it.
+ * Otherwise, value is the key value.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var EncryptKey = function EncryptKey(value)
+{
+ if (typeof value === 'object' && value instanceof EncryptKey) {
+ // Make a deep copy.
+ this.keyBits_ = value.keyBits_;
+ }
+ else {
+ var keyBits = value;
+ this.keyBits_ = typeof keyBits === 'object' && keyBits instanceof Blob ?
+ keyBits : new Blob(keyBits);
+ }
+};
+
+exports.EncryptKey = EncryptKey;
+
+/**
+ * Get the key value.
+ * @return {Blob} The key value.
+ */
+EncryptKey.prototype.getKeyBits = function() { return this.keyBits_; };
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/encrypted-content https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var KeyLocator = require('../key-locator.js').KeyLocator; /** @ignore */
+var WireFormat = require('../encoding/wire-format.js').WireFormat; /** @ignore */
+var Blob = require('../util/blob.js').Blob;
+
+/**
+ * An EncryptedContent holds an encryption type, a payload and other fields
+ * representing encrypted content.
+ * @param {EncryptedContent} (optional) If value is another EncryptedContent
+ * then copy it. If value is omitted then create an EncryptedContent with
+ * unspecified values.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var EncryptedContent = function EncryptedContent(value)
+{
+ if (typeof value === 'object' && value instanceof EncryptedContent) {
+ // Make a deep copy.
+ this.algorithmType_ = value.algorithmType_;
+ this.keyLocator_ = new KeyLocator(value.keyLocator_);
+ this.initialVector_ = value.initialVector_;
+ this.payload_ = value.payload_;
+ }
+ else {
+ this.algorithmType_ = null;
+ this.keyLocator_ = new KeyLocator();
+ this.initialVector_ = new Blob();
+ this.payload_ = new Blob();
+ }
+};
+
+exports.EncryptedContent = EncryptedContent;
+
+/**
+ * Get the algorithm type from EncryptAlgorithmType.
+ * @return {number} The algorithm type from EncryptAlgorithmType, or null if
+ * not specified.
+ */
+EncryptedContent.prototype.getAlgorithmType = function()
+{
+ return this.algorithmType_;
+};
+
+/**
+ * Get the key locator.
+ * @return {KeyLocator} The key locator. If not specified, getType() is null.
+ */
+EncryptedContent.prototype.getKeyLocator = function()
+{
+ return this.keyLocator_;
+};
+
+/**
+ * Get the initial vector.
+ * @return {Blob} The initial vector. If not specified, isNull() is true.
+ */
+EncryptedContent.prototype.getInitialVector = function()
+{
+ return this.initialVector_;
+};
+
+/**
+ * Get the payload.
+ * @return {Blob} The payload. If not specified, isNull() is true.
+ */
+EncryptedContent.prototype.getPayload = function()
+{
+ return this.payload_;
+};
+
+/**
+ * Set the algorithm type.
+ * @param {number} algorithmType The algorithm type from EncryptAlgorithmType.
+ * If not specified, set to null.
+ * @return {EncryptedContent} This EncryptedContent so that you can chain calls
+ * to update values.
+ */
+EncryptedContent.prototype.setAlgorithmType = function(algorithmType)
+{
+ this.algorithmType_ = algorithmType;
+ return this;
+};
+
+/**
+ * Set the key locator.
+ * @param {KeyLocator} keyLocator The key locator. This makes a copy of the
+ * object. If not specified, set to the default KeyLocator().
+ * @return {EncryptedContent} This EncryptedContent so that you can chain calls
+ * to update values.
+ */
+EncryptedContent.prototype.setKeyLocator = function(keyLocator)
+{
+ this.keyLocator_ = typeof keyLocator === 'object' &&
+ keyLocator instanceof KeyLocator ?
+ new KeyLocator(keyLocator) : new KeyLocator();
+ return this;
+};
+
+/**
+ * Set the initial vector.
+ * @param {Blob} initialVector The initial vector. If not specified, set to the
+ * default Blob() where isNull() is true.
+ * @return {EncryptedContent} This EncryptedContent so that you can chain calls
+ * to update values.
+ */
+EncryptedContent.prototype.setInitialVector = function(initialVector)
+{
+ this.initialVector_ =
+ typeof initialVector === 'object' && initialVector instanceof Blob ?
+ initialVector : new Blob(initialVector);
+ return this;
+};
+
+/**
+ * Set the encrypted payload.
+ * @param {Blob} payload The payload. If not specified, set to the default Blob()
+ * where isNull() is true.
+ * @return {EncryptedContent} This EncryptedContent so that you can chain calls
+ * to update values.
+ */
+EncryptedContent.prototype.setPayload = function(payload)
+{
+ this.payload_ = typeof payload === 'object' && payload instanceof Blob ?
+ payload : new Blob(payload);
+ return this;
+};
+
+/**
+ * Encode this EncryptedContent for a particular wire format.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {Blob} The encoded buffer in a Blob object.
+ */
+EncryptedContent.prototype.wireEncode = function(wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ return wireFormat.encodeEncryptedContent(this);
+};
+
+/**
+ * Decode the input using a particular wire format and update this
+ * EncryptedContent.
+ * @param {Blob|Buffer} input The buffer with the bytes to decode.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to decode
+ * this object. If omitted, use WireFormat.getDefaultWireFormat().
+ */
+EncryptedContent.prototype.wireDecode = function(input, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+ if (typeof input === 'object' && input instanceof Blob)
+ // Input is a blob, so get its buf() and set copy false.
+ wireFormat.decodeEncryptedContent(this, input.buf(), false);
+ else
+ wireFormat.decodeEncryptedContent(this, input, true);
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/group-manager-db https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var SyncPromise = require('../util/sync-promise.js').SyncPromise;
+
+/**
+ * GroupManagerDb is a base class for the storage of data used by the
+ * GroupManager. It contains two tables to store Schedules and Members.
+ * This is an abstract base class. A subclass must implement the methods.
+ * For example, see Sqlite3GroupManagerDb (for Nodejs) or IndexedDbGroupManagerDb
+ * (for the browser).
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var GroupManagerDb = function GroupManagerDb()
+{
+};
+
+exports.GroupManagerDb = GroupManagerDb;
+
+/**
+ * Create a new GroupManagerDb.Error to report an error using GroupManagerDb
+ * methods, wrapping the given error object.
+ * Call with: throw new GroupManagerDb.Error(new Error("message")).
+ * @constructor
+ * @param {Error} error The exception created with new Error.
+ */
+GroupManagerDb.Error = function GroupManagerDbError(error)
+{
+ if (error) {
+ error.__proto__ = GroupManagerDb.Error.prototype;
+ return error;
+ }
+}
+
+GroupManagerDb.Error.prototype = new Error();
+GroupManagerDb.Error.prototype.name = "GroupManagerDbError";
+
+////////////////////////////////////////////////////// Schedule management.
+
+/**
+ * Check if there is a schedule with the given name.
+ * @param {string} name The name of the schedule.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns true if there is a
+ * schedule (else false), or that is rejected with GroupManagerDb.Error for a
+ * database error.
+ */
+GroupManagerDb.prototype.hasSchedulePromise = function(name, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.hasSchedulePromise is not implemented"));
+};
+
+/**
+ * List all the names of the schedules.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns a new array of string
+ * with the names of all schedules, or that is rejected with
+ * GroupManagerDb.Error for a database error.
+ */
+GroupManagerDb.prototype.listAllScheduleNamesPromise = function(useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.listAllScheduleNamesPromise is not implemented"));
+};
+
+/**
+ * Get a schedule with the given name.
+ * @param {string} name The name of the schedule.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns a new Schedule object,
+ * or that is rejected with GroupManagerDb.Error if the schedule does not exist
+ * or other database error.
+ */
+GroupManagerDb.prototype.getSchedulePromise = function(name, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.getSchedulePromise is not implemented"));
+};
+
+/**
+ * For each member using the given schedule, get the name and public key DER
+ * of the member's key.
+ * @param {string} name The name of the schedule.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns a new array of object
+ * (where "keyName" is the Name of the public key and "publicKey" is the Blob of
+ * the public key DER), or that is rejected with GroupManagerDb.Error for a
+ * database error. Note that the member's identity name is keyName.getPrefix(-1).
+ * If the schedule name is not found, the list is empty.
+ */
+GroupManagerDb.prototype.getScheduleMembersPromise = function
+ (name, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.getScheduleMembersPromise is not implemented"));
+};
+
+/**
+ * Add a schedule with the given name.
+ * @param {string} name The name of the schedule. The name cannot be empty.
+ * @param {Schedule} schedule The Schedule to add.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the schedule is
+ * added, or that is rejected with GroupManagerDb.Error if a schedule with the
+ * same name already exists, if the name is empty, or other database error.
+ */
+GroupManagerDb.prototype.addSchedulePromise = function(name, schedule, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.addSchedulePromise is not implemented"));
+};
+
+/**
+ * Delete the schedule with the given name. Also delete members which use this
+ * schedule. If there is no schedule with the name, then do nothing.
+ * @param {string} name The name of the schedule.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the schedule is
+ * deleted (or there is no such schedule), or that is rejected with
+ * GroupManagerDb.Error for a database error.
+ */
+GroupManagerDb.prototype.deleteSchedulePromise = function(name, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.deleteSchedulePromise is not implemented"));
+};
+
+/**
+ * Rename a schedule with oldName to newName.
+ * @param {string} oldName The name of the schedule to be renamed.
+ * @param {string} newName The new name of the schedule. The name cannot be empty.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the schedule is
+ * renamed, or that is rejected with GroupManagerDb.Error if a schedule with
+ * newName already exists, if the schedule with oldName does not exist, if
+ * newName is empty, or other database error.
+ */
+GroupManagerDb.prototype.renameSchedulePromise = function
+ (oldName, newName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.renameSchedulePromise is not implemented"));
+};
+
+/**
+ * Update the schedule with name and replace the old object with the given
+ * schedule. Otherwise, if no schedule with name exists, a new schedule
+ * with name and the given schedule will be added to database.
+ * @param {string} name The name of the schedule. The name cannot be empty.
+ * @param {Schedule} schedule The Schedule to update or add.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the schedule is
+ * updated, or that is rejected with GroupManagerDb.Error if the name is empty,
+ * or other database error.
+ */
+GroupManagerDb.prototype.updateSchedulePromise = function
+ (name, schedule, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.updateSchedulePromise is not implemented"));
+};
+
+////////////////////////////////////////////////////// Member management.
+
+/**
+ * Check if there is a member with the given identity name.
+ * @param {Name} identity The member's identity name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns true if there is a
+ * member (else false), or that is rejected with GroupManagerDb.Error for a
+ * database error.
+ */
+GroupManagerDb.prototype.hasMemberPromise = function(identity, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.hasMemberPromise is not implemented"));
+};
+
+/**
+ * List all the members.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns a new array of Name with
+ * the names of all members, or that is rejected with GroupManagerDb.Error for a
+ * database error.
+ */
+GroupManagerDb.prototype.listAllMembersPromise = function(useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.listAllMembersPromise is not implemented"));
+};
+
+/**
+ * Get the name of the schedule for the given member's identity name.
+ * @param {Name} identity The member's identity name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns the string schedule name,
+ * or that is rejected with GroupManagerDb.Error if there's no member with the
+ * given identity name in the database, or other database error.
+ */
+GroupManagerDb.prototype.getMemberSchedulePromise = function(identity, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.getMemberSchedulePromise is not implemented"));
+};
+
+/**
+ * Add a new member with the given key named keyName into a schedule named
+ * scheduleName. The member's identity name is keyName.getPrefix(-1).
+ * @param {string} scheduleName The schedule name.
+ * @param {Name} keyName The name of the key.
+ * @param {Blob} key A Blob of the public key DER.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the member is
+ * added, or that is rejected with GroupManagerDb.Error if there's no schedule
+ * named scheduleName, if the member's identity name already exists, or other
+ * database error.
+ */
+GroupManagerDb.prototype.addMemberPromise = function
+ (scheduleName, keyName, key, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.addMemberPromise is not implemented"));
+};
+
+/**
+ * Change the name of the schedule for the given member's identity name.
+ * @param {Name} identity The member's identity name.
+ * @param {string} scheduleName The new schedule name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the member is
+ * updated, or that is rejected with GroupManagerDb.Error if there's no member
+ * with the given identity name in the database, or there's no schedule named
+ * scheduleName, or other database error.
+ */
+GroupManagerDb.prototype.updateMemberSchedulePromise = function
+ (identity, scheduleName, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.updateMemberSchedulePromise is not implemented"));
+};
+
+/**
+ * Delete a member with the given identity name. If there is no member with
+ * the identity name, then do nothing.
+ * @param {Name} identity The member's identity name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the member is
+ * deleted (or there is no such member), or that is rejected with
+ * GroupManagerDb.Error for a database error.
+ */
+GroupManagerDb.prototype.deleteMemberPromise = function(identity, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("GroupManagerDb.deleteMemberPromise is not implemented"));
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/group-manager https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Name = require('../name.js').Name; /** @ignore */
+var Data = require('../data.js').Data; /** @ignore */
+var SyncPromise = require('../util/sync-promise.js').SyncPromise; /** @ignore */
+var IdentityCertificate = require('../security/certificate/identity-certificate.js').IdentityCertificate; /** @ignore */
+var SecurityException = require('../security/security-exception.js').SecurityException; /** @ignore */
+var RsaKeyParams = require('../security/key-params.js').RsaKeyParams; /** @ignore */
+var EncryptParams = require('./algo/encrypt-params.js').EncryptParams; /** @ignore */
+var EncryptAlgorithmType = require('./algo/encrypt-params.js').EncryptAlgorithmType; /** @ignore */
+var Encryptor = require('./algo/encryptor.js').Encryptor; /** @ignore */
+var RsaAlgorithm = require('./algo/rsa-algorithm.js').RsaAlgorithm; /** @ignore */
+var Interval = require('./interval.js').Interval; /** @ignore */
+var Schedule = require('./schedule.js').Schedule;
+
+/**
+ * A GroupManager manages keys and schedules for group members in a particular
+ * namespace.
+ * Create a group manager with the given values. The group manager namespace
+ * is <prefix>/read/<dataType> .
+ * @param {Name} prefix The prefix for the group manager namespace.
+ * @param {Name} dataType The data type for the group manager namespace.
+ * @param {GroupManagerDb} database The GroupManagerDb for storing the group
+ * management information (including user public keys and schedules).
+ * @param {number} keySize The group key will be an RSA key with keySize bits.
+ * @param {number} freshnessHours The number of hours of the freshness period of
+ * data packets carrying the keys.
+ * @param {KeyChain} keyChain The KeyChain to use for signing data packets. This
+ * signs with the default identity.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var GroupManager = function GroupManager
+ (prefix, dataType, database, keySize, freshnessHours, keyChain)
+{
+ this.namespace_ = new Name(prefix).append(Encryptor.NAME_COMPONENT_READ)
+ .append(dataType);
+ this.database_ = database;
+ this.keySize_ = keySize;
+ this.freshnessHours_ = freshnessHours;
+
+ this.keyChain_ = keyChain;
+};
+
+exports.GroupManager = GroupManager;
+
+/**
+ * Create a group key for the interval into which timeSlot falls. This creates
+ * a group key if it doesn't exist, and encrypts the key using the public key of
+ * each eligible member.
+ * @param {number} timeSlot The time slot to cover as milliseconds since
+ * Jan 1, 1970 UTC.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns a List of Data packets
+ * (where the first is the E-KEY data packet with the group's public key and the
+ * rest are the D-KEY data packets with the group's private key encrypted with
+ * the public key of each eligible member), or that is rejected with
+ * GroupManagerDb.Error for a database error or SecurityException for an error
+ * using the security KeyChain.
+ */
+GroupManager.prototype.getGroupKeyPromise = function(timeSlot, useSync)
+{
+ var memberKeys = [];
+ var result = [];
+ var thisManager = this;
+ var privateKeyBlob;
+ var publicKeyBlob;
+ var startTimeStamp;
+ var endTimeStamp;
+
+ // Get the time interval.
+ return this.calculateIntervalPromise_(timeSlot, memberKeys, useSync)
+ .then(function(finalInterval) {
+ if (finalInterval.isValid() == false)
+ return SyncPromise.resolve(result);
+
+ startTimeStamp = Schedule.toIsoString(finalInterval.getStartTime());
+ endTimeStamp = Schedule.toIsoString(finalInterval.getEndTime());
+
+ // Generate the private and public keys.
+ return thisManager.generateKeyPairPromise_(useSync)
+ .then(function(keyPair) {
+ privateKeyBlob = keyPair.privateKeyBlob;
+ publicKeyBlob = keyPair.publicKeyBlob;
+
+ // Add the first element to the result.
+ // The E-KEY (public key) data packet name convention is:
+ // /<data_type>/E-KEY/[start-ts]/[end-ts]
+ return thisManager.createEKeyDataPromise_
+ (startTimeStamp, endTimeStamp, publicKeyBlob, useSync);
+ })
+ .then(function(data) {
+ result.push(data);
+
+ // Encrypt the private key with the public key from each member's certificate.
+
+ // Process the memberKeys entry at i, and recursively call to process the
+ // next entry. Return a promise which is resolved when all are processed.
+ // (We have to make a recursive function to use Promises.)
+ function processMemberKey(i) {
+ if (i >= memberKeys.length)
+ // Finished.
+ return SyncPromise.resolve();
+
+ var keyName = memberKeys[i].keyName;
+ var certificateKey = memberKeys[i].publicKey;
+
+ return thisManager.createDKeyDataPromise_
+ (startTimeStamp, endTimeStamp, keyName, privateKeyBlob, certificateKey,
+ useSync)
+ .then(function(data) {
+ result.push(data);
+
+ return processMemberKey(i + 1);
+ });
+ }
+
+ return processMemberKey(0);
+ })
+ .then(function() {
+ return SyncPromise.resolve(result);
+ });
+ });
+};
+
+/**
+ * Add a schedule with the given scheduleName.
+ * @param {string} scheduleName The name of the schedule. The name cannot be
+ * empty.
+ * @param {Schedule} schedule The Schedule to add.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the schedule is
+ * added, or that is rejected with GroupManagerDb.Error if a schedule with the
+ * same name already exists, if the name is empty, or other database error.
+ */
+GroupManager.prototype.addSchedulePromise = function
+ (scheduleName, schedule, useSync)
+{
+ return this.database_.addSchedulePromise(scheduleName, schedule, useSync);
+};
+
+/**
+ * Delete the schedule with the given scheduleName. Also delete members which
+ * use this schedule. If there is no schedule with the name, then do nothing.
+ * @param {string} scheduleName The name of the schedule.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the schedule is
+ * deleted (or there is no such schedule), or that is rejected with
+ * GroupManagerDb.Error for a database error.
+ */
+GroupManager.prototype.deleteSchedulePromise = function(scheduleName, useSync)
+{
+ return this.database_.deleteSchedulePromise(scheduleName, useSync);
+};
+
+/**
+ * Update the schedule with scheduleName and replace the old object with the
+ * given schedule. Otherwise, if no schedule with name exists, a new schedule
+ * with name and the given schedule will be added to database.
+ * @param {string} scheduleName The name of the schedule. The name cannot be
+ * empty.
+ * @param {Schedule} schedule The Schedule to update or add.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the schedule is
+ * updated, or that is rejected with GroupManagerDb.Error if the name is empty,
+ * or other database error.
+ */
+GroupManager.prototype.updateSchedulePromise = function
+ (name, scheduleName, useSync)
+{
+ return this.database_.updateSchedulePromise(scheduleName, schedule, useSync);
+};
+
+/**
+ * Add a new member with the given memberCertificate into a schedule named
+ * scheduleName. If cert is an IdentityCertificate made from memberCertificate,
+ * then the member's identity name is cert.getPublicKeyName().getPrefix(-1).
+ * @param {string} scheduleName The schedule name.
+ * @param {Data} memberCertificate The member's certificate.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the member is
+ * added, or that is rejected with GroupManagerDb.Error if there's no schedule
+ * named scheduleName, if the member's identity name already exists, or other
+ * database error. Or a promise that is rejected with DerDecodingException for
+ * an error decoding memberCertificate as a certificate.
+ */
+GroupManager.prototype.addMemberPromise = function
+ (scheduleName, memberCertificate, useSync)
+{
+ var cert = new IdentityCertificate(memberCertificate);
+ return this.database_.addMemberPromise
+ (scheduleName, cert.getPublicKeyName(), cert.getPublicKeyInfo().getKeyDer(),
+ useSync);
+};
+
+/**
+ * Remove a member with the given identity name. If there is no member with
+ * the identity name, then do nothing.
+ * @param {Name} identity The member's identity name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the member is
+ * removed (or there is no such member), or that is rejected with
+ * GroupManagerDb.Error for a database error.
+ */
+GroupManager.prototype.removeMemberPromise = function(identity, useSync)
+{
+ return this.database_.deleteMemberPromise(identity, useSync);
+};
+
+/**
+ * Change the name of the schedule for the given member's identity name.
+ * @param {Name} identity The member's identity name.
+ * @param {string} scheduleName The new schedule name.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the member is
+ * updated, or that is rejected with GroupManagerDb.Error if there's no member
+ * with the given identity name in the database, or there's no schedule named
+ * scheduleName.
+ */
+GroupManager.prototype.updateMemberSchedulePromise = function
+ (identity, scheduleName, useSync)
+{
+ return this.database_.updateMemberSchedulePromise
+ (identity, scheduleName, useSync);
+};
+
+/**
+ * Calculate an Interval that covers the timeSlot.
+ * @param {number} timeSlot The time slot to cover as milliseconds since
+ * Jan 1, 1970 UTC.
+ * @param {Array<object>} memberKeys First clear memberKeys then fill it with
+ * the info of members who are allowed to access the interval. memberKeys is an
+ * array of object where "keyName" is the Name of the public key and "publicKey"
+ * is the Blob of the public key DER. The memberKeys entries are sorted by
+ * the entry keyName.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns a new nterval covering
+ * the time slot, or that is rejected with GroupManagerDb.Error for a database
+ * error.
+ */
+GroupManager.prototype.calculateIntervalPromise_ = function
+ (timeSlot, memberKeys, useSync)
+{
+ // Prepare.
+ var positiveResult = new Interval();
+ var negativeResult = new Interval();
+ // Clear memberKeys.
+ memberKeys.splice(0, memberKeys.length);
+ var thisManager = this;
+
+ // Get the all intervals from the schedules.
+ return this.database_.listAllScheduleNamesPromise(useSync)
+ .then(function(scheduleNames) {
+ // Process the scheduleNames entry at i, and recursively call to process the
+ // next entry. Return a promise which is resolved when all are processed.
+ // (We have to make a recursive function to use Promises.)
+ function processSchedule(i) {
+ if (i >= scheduleNames.length)
+ // Finished.
+ return SyncPromise.resolve();
+
+ var scheduleName = scheduleNames[i];
+
+ return thisManager.database_.getSchedulePromise(scheduleName, useSync)
+ .then(function(schedule) {
+ var result = schedule.getCoveringInterval(timeSlot);
+ var tempInterval = result.interval;
+
+ if (result.isPositive) {
+ if (!positiveResult.isValid())
+ positiveResult = tempInterval;
+ positiveResult.intersectWith(tempInterval);
+
+ return thisManager.database_.getScheduleMembersPromise
+ (scheduleName, useSync)
+ .then(function(map) {
+ // Add each entry in map to memberKeys.
+ for (var iMap = 0; iMap < map.length; ++iMap)
+ GroupManager.memberKeysAdd_(memberKeys, map[iMap]);
+
+ return processSchedule(i + 1);
+ });
+ }
+ else {
+ if (!negativeResult.isValid())
+ negativeResult = tempInterval;
+ negativeResult.intersectWith(tempInterval);
+
+ return processSchedule(i + 1);
+ }
+ });
+ }
+
+ return processSchedule(0);
+ })
+ .then(function() {
+ if (!positiveResult.isValid())
+ // Return an invalid interval when there is no member which has an
+ // interval covering the time slot.
+ return SyncPromise.resolve(new Interval(false));
+
+ // Get the final interval result.
+ var finalInterval;
+ if (negativeResult.isValid())
+ finalInterval = positiveResult.intersectWith(negativeResult);
+ else
+ finalInterval = positiveResult;
+
+ return SyncPromise.resolve(finalInterval);
+ });
+};
+
+/**
+ * Add entry to memberKeys, sorted by entry.keyName. If there is already an
+ * entry with keyName, then don't add.
+ */
+GroupManager.memberKeysAdd_ = function(memberKeys, entry)
+{
+ // Find the index of the first node where the keyName is not less than
+ // entry.keyName.
+ var i = 0;
+ while (i < memberKeys.length) {
+ var comparison = memberKeys[i].keyName.compare(entry.keyName);
+ if (comparison == 0)
+ // A duplicate, so don't add.
+ return;
+
+ if (comparison > 0)
+ break;
+ i += 1;
+ }
+
+ memberKeys.splice(i, 0, entry);
+};
+
+/**
+ * Generate an RSA key pair according to keySize_.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns an object where
+ * "privateKeyBlob" is the encoding Blob of the private key and "publicKeyBlob"
+ * is the encoding Blob of the public key.
+ */
+GroupManager.prototype.generateKeyPairPromise_ = function(useSync)
+{
+ var params = new RsaKeyParams(this.keySize_);
+
+ return RsaAlgorithm.generateKeyPromise(params)
+ .then(function(privateKey) {
+ var privateKeyBlob = privateKey.getKeyBits();
+ var publicKey = RsaAlgorithm.deriveEncryptKey(privateKeyBlob);
+ var publicKeyBlob = publicKey.getKeyBits();
+
+ return SyncPromise.resolve
+ ({ privateKeyBlob: privateKeyBlob, publicKeyBlob: publicKeyBlob });
+ });
+};
+
+/**
+ * Create an E-KEY Data packet for the given public key.
+ * @param {string} startTimeStamp The start time stamp string to put in the name.
+ * @param {string} endTimeStamp The end time stamp string to put in the name.
+ * @param {Blob} publicKeyBlob A Blob of the public key DER.
+ * @return The Data packet.
+ * @throws SecurityException for an error using the security KeyChain.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns the Data packet, or that
+ * is rejected with SecurityException for an error using the security KeyChain.
+ */
+GroupManager.prototype.createEKeyDataPromise_ = function
+ (startTimeStamp, endTimeStamp, publicKeyBlob, useSync)
+{
+ var name = new Name(this.namespace_);
+ name.append(Encryptor.NAME_COMPONENT_E_KEY).append(startTimeStamp)
+ .append(endTimeStamp);
+
+ var data = new Data(name);
+ data.getMetaInfo().setFreshnessPeriod
+ (this.freshnessHours_ * GroupManager.MILLISECONDS_IN_HOUR);
+ data.setContent(publicKeyBlob);
+
+ return this.keyChain_.signPromise(data);
+};
+
+/**
+ * Create a D-KEY Data packet with an EncryptedContent for the given private
+ * key, encrypted with the certificate key.
+ * @param {string} startTimeStamp The start time stamp string to put in the name.
+ * @param {string} endTimeStamp The end time stamp string to put in the name.
+ * @param {Name} keyName The key name to put in the data packet name and the
+ * EncryptedContent key locator.
+ * @param {Blob} privateKeyBlob A Blob of the encoded private key.
+ * @param {Blob} certificateKey The certificate key encoding, used to encrypt
+ * the private key.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns the Data packet, or that
+ * is rejected with SecurityException for an error using the security KeyChain.
+ */
+GroupManager.prototype.createDKeyDataPromise_ = function
+ (startTimeStamp, endTimeStamp, keyName, privateKeyBlob, certificateKey,
+ useSync)
+{
+ var name = new Name(this.namespace_);
+ name.append(Encryptor.NAME_COMPONENT_D_KEY);
+ name.append(startTimeStamp).append(endTimeStamp);
+ var data = new Data(name);
+ data.getMetaInfo().setFreshnessPeriod
+ (this.freshnessHours_ * GroupManager.MILLISECONDS_IN_HOUR);
+ var encryptParams = new EncryptParams(EncryptAlgorithmType.RsaOaep);
+ var thisManager = this;
+
+ return Encryptor.encryptDataPromise
+ (data, privateKeyBlob, keyName, certificateKey, encryptParams, useSync)
+ .catch(function(ex) {
+ // Consolidate errors such as InvalidKeyException.
+ return SyncPromise.reject(SecurityException(new Error
+ ("createDKeyData: Error in encryptData: " + ex)));
+ })
+ .then(function() {
+ return thisManager.keyChain_.signPromise(data);
+ });
+};
+
+GroupManager.MILLISECONDS_IN_HOUR = 3600 * 1000;
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/interval https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * An Interval defines a time duration which contains a start timestamp and an
+ * end timestamp. Create an Interval with one of these forms:
+ * Interval(isValid).
+ * Interval(startTime, endTime).
+ * Interval(interval).
+ * @param {boolean} isValid True to create a valid empty interval, false to
+ * create an invalid interval.
+ * @param {number} startTime The start time as milliseconds since Jan 1, 1970 UTC.
+ * The start time must be less than the end time. To create an empty interval
+ * (start time equals end time), use the constructor Interval(true).
+ * @param {number} endTime The end time as milliseconds since Jan 1, 1970 UTC.
+ * @param {Interval} interval The other interval with values to copy.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var Interval = function Interval(value, endTime)
+{
+ if (typeof value === 'object' && value instanceof Interval) {
+ // Make a copy.
+ this.startTime_ = value.startTime_;
+ this.endTime_ = value.endTime_;
+ this.isValid_ = value.isValid_;
+ }
+ else if (typeof value === 'number') {
+ var startTime = value;
+
+ if (!(startTime < endTime))
+ throw new Error("Interval start time must be less than the end time");
+
+ this.startTime_ = startTime;
+ this.endTime_ = endTime;
+ this.isValid_ = true;
+ }
+ else {
+ var isValid = (value ? true : false);
+
+ this.startTime_ = -Number.MAX_VALUE;
+ this.endTime_ = -Number.MAX_VALUE;
+ this.isValid_ = isValid;
+ }
+};
+
+exports.Interval = Interval;
+
+/**
+ * Set this interval to have the same values as the other interval.
+ * @param {Interval} other The other Interval with values to copy.
+ */
+Interval.prototype.set = function(other)
+{
+ this.startTime_ = other.startTime_;
+ this.endTime_ = other.endTime_;
+ this.isValid_ = other.isValid_;
+};
+
+/**
+ * Check if the time point is in this interval.
+ * @param {number} timePoint The time point to check as milliseconds since
+ * Jan 1, 1970 UTC.
+ * @return {boolean} True if timePoint is in this interval.
+ * @throws Error if this Interval is invalid.
+ */
+Interval.prototype.covers = function(timePoint)
+{
+ if (!this.isValid_)
+ throw new Error("Interval.covers: This Interval is invalid");
+
+ if (this.isEmpty())
+ return false;
+ else
+ return this.startTime_ <= timePoint && timePoint < this.endTime_;
+};
+
+/**
+ * Set this Interval to the intersection of this and the other interval.
+ * This and the other interval should be valid but either can be empty.
+ * @param {Interval} interval The other Interval to intersect with.
+ * @return {Interval} This Interval.
+ * @throws Error if this Interval or the other interval is invalid.
+ */
+Interval.prototype.intersectWith = function(interval)
+{
+ if (!this.isValid_)
+ throw new Error("Interval.intersectWith: This Interval is invalid");
+ if (!interval.isValid_)
+ throw new Error("Interval.intersectWith: The other Interval is invalid");
+
+ if (this.isEmpty() || interval.isEmpty()) {
+ // If either is empty, the result is empty.
+ this.startTime_ = this.endTime_;
+ return this;
+ }
+
+ if (this.startTime_ >= interval.endTime_ || this.endTime_ <= interval.startTime_) {
+ // The two intervals don't have an intersection, so the result is empty.
+ this.startTime_ = this.endTime_;
+ return this;
+ }
+
+ // Get the start time.
+ if (this.startTime_ <= interval.startTime_)
+ this.startTime_ = interval.startTime_;
+
+ // Get the end time.
+ if (this.endTime_ > interval.endTime_)
+ this.endTime_ = interval.endTime_;
+
+ return this;
+};
+
+/**
+ * Set this Interval to the union of this and the other interval.
+ * This and the other interval should be valid but either can be empty.
+ * This and the other interval should have an intersection. (Contiguous
+ * intervals are not allowed.)
+ * @param {Interval} interval The other Interval to union with.
+ * @return {Interval} This Interval.
+ * @throws Error if this Interval or the other interval is invalid, or if the
+ * two intervals do not have an intersection.
+ */
+Interval.prototype.unionWith = function(interval)
+{
+ if (!this.isValid_)
+ throw new Error("Interval.intersectWith: This Interval is invalid");
+ if (!interval.isValid_)
+ throw new Error("Interval.intersectWith: The other Interval is invalid");
+
+ if (this.isEmpty()) {
+ // This interval is empty, so use the other.
+ this.startTime_ = interval.startTime_;
+ this.endTime_ = interval.endTime_;
+ return this;
+ }
+
+ if (interval.isEmpty())
+ // The other interval is empty, so keep using this one.
+ return this;
+
+ if (this.startTime_ >= interval.endTime_ || this.endTime_ <= interval.startTime_)
+ throw new Error
+ ("Interval.unionWith: The two intervals do not have an intersection");
+
+ // Get the start time.
+ if (this.startTime_ > interval.startTime_)
+ this.startTime_ = interval.startTime_;
+
+ // Get the end time.
+ if (this.endTime_ < interval.endTime_)
+ this.endTime_ = interval.endTime_;
+
+ return this;
+};
+
+/**
+ * Get the start time.
+ * @return {number} The start time as milliseconds since Jan 1, 1970 UTC.
+ * @throws Error if this Interval is invalid.
+ */
+Interval.prototype.getStartTime = function()
+{
+ if (!this.isValid_)
+ throw new Error("Interval.getStartTime: This Interval is invalid");
+ return this.startTime_;
+};
+
+/**
+ * Get the end time.
+ * @return {number} The end time as milliseconds since Jan 1, 1970 UTC.
+ * @throws Error if this Interval is invalid.
+ */
+Interval.prototype.getEndTime = function()
+{
+ if (!this.isValid_)
+ throw new Error("Interval.getEndTime: This Interval is invalid");
+ return this.endTime_;
+};
+
+/**
+ * Check if this Interval is valid.
+ * @return {boolean} True if this interval is valid, false if invalid.
+ */
+Interval.prototype.isValid = function() { return this.isValid_; };
+
+/**
+ * Check if this Interval is empty.
+ * @return {boolean} True if this Interval is empty (start time equals end time),
+ * false if not.
+ * @throws Error if this Interval is invalid.
+ */
+Interval.prototype.isEmpty = function()
+{
+ if (!this.isValid_)
+ throw new Error("Interval.isEmpty: This Interval is invalid");
+ return this.startTime_ == this.endTime_;
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/producer-db https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var SyncPromise = require('../util/sync-promise.js').SyncPromise;
+
+/**
+ * ProducerDb is a base class for the storage of keys for the producer. It contains
+ * one table that maps time slots (to the nearest hour) to the content key
+ * created for that time slot. A subclass must implement the methods. For
+ * example, see Sqlite3ProducerDb (for Nodejs) or IndexedDbProducerDb (for the
+ * browser).
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var ProducerDb = function ProducerDb()
+{
+};
+
+exports.ProducerDb = ProducerDb;
+
+/**
+ * Create a new ProducerDb.Error to report an error using ProducerDb
+ * methods, wrapping the given error object.
+ * Call with: throw new ProducerDb.Error(new Error("message")).
+ * @constructor
+ * @param {Error} error The exception created with new Error.
+ */
+ProducerDb.Error = function ProducerDbError(error)
+{
+ if (error) {
+ error.__proto__ = ProducerDb.Error.prototype;
+ return error;
+ }
+}
+
+ProducerDb.Error.prototype = new Error();
+ProducerDb.Error.prototype.name = "ProducerDbError";
+
+/**
+ * Check if a content key exists for the hour covering timeSlot.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns true if there is a
+ * content key for timeSlot (else false), or that is rejected with
+ * ProducerDb.Error for a database error.
+ */
+ProducerDb.prototype.hasContentKeyPromise = function(timeSlot, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("ProducerDb.hasContentKeyPromise is not implemented"));
+};
+
+/**
+ * Get the content key for the hour covering timeSlot.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that returns a Blob with the encoded
+ * key, or that is rejected with ProducerDb.Error if there is no key covering
+ * timeSlot, or other database error
+ */
+ProducerDb.prototype.getContentKeyPromise = function(timeSlot, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("ProducerDb.getContentKeyPromise is not implemented"));
+};
+
+/**
+ * Add key as the content key for the hour covering timeSlot.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {Blob} key The encoded key.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the key is added,
+ * or that is rejected with ProducerDb.Error if a key for the same hour already
+ * exists in the database, or other database error.
+ */
+ProducerDb.prototype.addContentKeyPromise = function
+ (timeSlot, key, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("ProducerDb.addContentKeyPromise is not implemented"));
+};
+
+/**
+ * Delete the content key for the hour covering timeSlot. If there is no key for
+ * the time slot, do nothing.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {boolean} useSync (optional) If true then return a SyncPromise which
+ * is already fulfilled. If omitted or false, this may return a SyncPromise or
+ * an async Promise.
+ * @return {Promise|SyncPromise} A promise that fulfills when the key is deleted
+ * (or there is no such key), or that is rejected with ProducerDb.Error for a
+ * database error.
+ */
+ProducerDb.prototype.deleteContentKeyPromise = function(timeSlot, useSync)
+{
+ return SyncPromise.reject(new Error
+ ("ProducerDb.deleteContentKeyPromise is not implemented"));
+};
+
+/**
+ * Get the hour-based time slot.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @return {number} The hour-based time slot as hours since Jan 1, 1970 UTC.
+ */
+ProducerDb.getFixedTimeSlot = function(timeSlot)
+{
+ return Math.floor(Math.round(timeSlot) / 3600000.0);
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/producer https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Name = require('../name.js').Name; /** @ignore */
+var Interest = require('../interest.js').Interest; /** @ignore */
+var Data = require('../data.js').Data; /** @ignore */
+var Link = require('../link.js').Link; /** @ignore */
+var NetworkNack = require('../network-nack.js').NetworkNack; /** @ignore */
+var Exclude = require('../exclude.js').Exclude; /** @ignore */
+var Encryptor = require('./algo/encryptor.js').Encryptor; /** @ignore */
+var EncryptParams = require('./algo/encrypt-params.js').EncryptParams; /** @ignore */
+var EncryptAlgorithmType = require('./algo/encrypt-params.js').EncryptAlgorithmType; /** @ignore */
+var AesKeyParams = require('../security/key-params.js').AesKeyParams; /** @ignore */
+var AesAlgorithm = require('./algo/aes-algorithm.js').AesAlgorithm; /** @ignore */
+var Schedule = require('./schedule.js').Schedule; /** @ignore */
+var EncryptError = require('./encrypt-error.js').EncryptError; /** @ignore */
+var NdnCommon = require('../util/ndn-common.js').NdnCommon; /** @ignore */
+var SyncPromise = require('../util/sync-promise.js').SyncPromise;
+
+/**
+ * A Producer manages content keys used to encrypt a data packet in the
+ * group-based encryption protocol.
+ * Create a Producer to use the given ProducerDb, Face and other values.
+ *
+ * A producer can produce data with a naming convention:
+ * /<prefix>/SAMPLE/<dataType>/[timestamp]
+ *
+ * The produced data packet is encrypted with a content key,
+ * which is stored in the ProducerDb database.
+ *
+ * A producer also needs to produce data containing a content key
+ * encrypted with E-KEYs. A producer can retrieve E-KEYs through the face,
+ * and will re-try for at most repeatAttemps times when E-KEY retrieval fails.
+ *
+ * @param {Name} prefix The producer name prefix. This makes a copy of the Name.
+ * @param {Name} dataType The dataType portion of the producer name. This makes
+ * a copy of the Name.
+ * @param {Face} face The face used to retrieve keys.
+ * @param {KeyChain} keyChain The keyChain used to sign data packets.
+ * @param {ProducerDb} database The ProducerDb database for storing keys.
+ * @param {number} repeatAttempts (optional) The maximum retry for retrieving
+ * keys. If omitted, use a default value of 3.
+ * @param {Link} keyRetrievalLink (optional) The Link object to use in Interests
+ * for key retrieval. This makes a copy of the Link object. If the Link object's
+ * getDelegations().size() is zero, don't use it. If omitted, don't use a Link
+ * object.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var Producer = function Producer
+ (prefix, dataType, face, keyChain, database, repeatAttempts, keyRetrievalLink)
+{
+ this.face_ = face;
+ this.keyChain_ = keyChain;
+ this.database_ = database;
+ this.maxRepeatAttempts_ = (repeatAttempts == undefined ? 3 : repeatAttempts);
+ this.keyRetrievalLink_ =
+ (keyRetrievalLink == undefined ? Producer.NO_LINK : new Link(keyRetrievalLink));
+
+ // The map key is the key name URI string. The value is an object with fields
+ // "keyName" and "keyInfo" where "keyName" is the same Name used for the key
+ // name URI string, and "keyInfo" is the Producer.KeyInfo_.
+ // (Use a string because we can't use the Name object as the key in JavaScript.)
+ // (Also put the original Name in the value because we need to iterate over
+ // eKeyInfo_ and we don't want to rebuild the Name from the name URI string.)
+ this.eKeyInfo_ = {};
+ // The map key is the time stamp. The value is a Producer.KeyRequest_.
+ this.keyRequests_ = {};
+
+ var fixedPrefix = new Name(prefix);
+ var fixedDataType = new Name(dataType);
+
+ // Fill ekeyInfo_ with all permutations of dataType, including the 'E-KEY'
+ // component of the name. This will be used in createContentKey to send
+ // interests without reconstructing names every time.
+ fixedPrefix.append(Encryptor.NAME_COMPONENT_READ);
+ while (fixedDataType.size() > 0) {
+ var nodeName = new Name(fixedPrefix);
+ nodeName.append(fixedDataType);
+ nodeName.append(Encryptor.NAME_COMPONENT_E_KEY);
+
+ this.eKeyInfo_[nodeName.toUri()] =
+ { keyName: nodeName, keyInfo: new Producer.KeyInfo_() };
+ fixedDataType = fixedDataType.getPrefix(-1);
+ }
+ fixedPrefix.append(dataType);
+ this.namespace_ = new Name(prefix);
+ this.namespace_.append(Encryptor.NAME_COMPONENT_SAMPLE);
+ this.namespace_.append(dataType);
+};
+
+exports.Producer = Producer;
+
+/**
+ * Create the content key corresponding to the timeSlot. This first checks if
+ * the content key exists. For an existing content key, this returns the
+ * content key name directly. If the key does not exist, this creates one and
+ * encrypts it using the corresponding E-KEYs. The encrypted content keys are
+ * passed to the onEncryptedKeys callback.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {function} onEncryptedKeys If this creates a content key, then this
+ * calls onEncryptedKeys(keys) where keys is a list of encrypted content key
+ * Data packets. If onEncryptedKeys is null, this does not use it.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onContentKeyName This calls onContentKeyName(contentKeyName)
+ * with the content key name for the time slot. If onContentKeyName is null,
+ * this does not use it. (A callback is needed because of async database
+ * operations.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) This calls onError(errorCode, message)
+ * for an error, where errorCode is from EncryptError.ErrorCode and message is a
+ * string. If omitted, use a default callback which does nothing.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ */
+Producer.prototype.createContentKey = function
+ (timeSlot, onEncryptedKeys, onContentKeyName, onError)
+{
+ if (!onError)
+ onError = Producer.defaultOnError;
+
+ var hourSlot = Producer.getRoundedTimeSlot_(timeSlot);
+
+ // Create the content key name.
+ var contentKeyName = new Name(this.namespace_);
+ contentKeyName.append(Encryptor.NAME_COMPONENT_C_KEY);
+ contentKeyName.append(Schedule.toIsoString(hourSlot));
+
+ var contentKeyBits;
+ var thisProducer = this;
+
+ // Check if we have created the content key before.
+ this.database_.hasContentKeyPromise(timeSlot)
+ .then(function(exists) {
+ if (exists) {
+ if (onContentKeyName != null)
+ onContentKeyName(contentKeyName);
+ return;
+ }
+
+ // We haven't created the content key. Create one and add it into the database.
+ var aesParams = new AesKeyParams(128);
+ contentKeyBits = AesAlgorithm.generateKey(aesParams).getKeyBits();
+ thisProducer.database_.addContentKeyPromise(timeSlot, contentKeyBits)
+ .then(function() {
+ // Now we need to retrieve the E-KEYs for content key encryption.
+ var timeCount = Math.round(timeSlot);
+ thisProducer.keyRequests_[timeCount] =
+ new Producer.KeyRequest_(thisProducer.getEKeyInfoSize_());
+ var keyRequest = thisProducer.keyRequests_[timeCount];
+
+ // Check if the current E-KEYs can cover the content key.
+ var timeRange = new Exclude();
+ Producer.excludeAfter
+ (timeRange, new Name.Component(Schedule.toIsoString(timeSlot)));
+ for (var keyNameUri in thisProducer.eKeyInfo_) {
+ // For each current E-KEY.
+ var entry = thisProducer.eKeyInfo_[keyNameUri];
+ var keyInfo = entry.keyInfo;
+ if (timeSlot < keyInfo.beginTimeSlot || timeSlot >= keyInfo.endTimeSlot) {
+ // The current E-KEY cannot cover the content key, so retrieve one.
+ keyRequest.repeatAttempts[keyNameUri] = 0;
+ thisProducer.sendKeyInterest_
+ (new Interest(entry.keyName).setExclude(timeRange).setChildSelector(1),
+ timeSlot, onEncryptedKeys, onError);
+ }
+ else {
+ // The current E-KEY can cover the content key.
+ // Encrypt the content key directly.
+ var eKeyName = new Name(entry.keyName);
+ eKeyName.append(Schedule.toIsoString(keyInfo.beginTimeSlot));
+ eKeyName.append(Schedule.toIsoString(keyInfo.endTimeSlot));
+ thisProducer.encryptContentKeyPromise_
+ (keyInfo.keyBits, eKeyName, timeSlot, onEncryptedKeys, onError);
+ }
+ }
+
+ if (onContentKeyName != null)
+ onContentKeyName(contentKeyName);
+ });
+ });
+};
+
+/**
+ * Encrypt the given content with the content key that covers timeSlot, and
+ * update the data packet with the encrypted content and an appropriate data
+ * name.
+ * @param {Data} data An empty Data object which is updated.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {Blob} content The content to encrypt.
+ * @param {function} onComplete This calls onComplete() when the data packet has
+ * been updated.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onError (optional) This calls onError(errorCode, message)
+ * for an error, where errorCode is from EncryptError.ErrorCode and message is a
+ * string. If omitted, use a default callback which does nothing.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ */
+Producer.prototype.produce = function
+ (data, timeSlot, content, onComplete, onError)
+{
+ if (!onError)
+ onError = Producer.defaultOnError;
+
+ var thisProducer = this;
+
+ // Get a content key.
+ this.createContentKey(timeSlot, null, function(contentKeyName) {
+ thisProducer.database_.getContentKeyPromise(timeSlot)
+ .then(function(contentKey) {
+ // Produce data.
+ var dataName = new Name(thisProducer.namespace_);
+ dataName.append(Schedule.toIsoString(timeSlot));
+
+ data.setName(dataName);
+ var params = new EncryptParams(EncryptAlgorithmType.AesCbc, 16);
+ return Encryptor.encryptData
+ (data, content, contentKeyName, contentKey, params);
+ })
+ .then(function() {
+ return thisProducer.keyChain_.signPromise(data);
+ })
+ .then(function() {
+ try {
+ onComplete();
+ } catch (ex) {
+ console.log("Error in onComplete: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }, function(error) {
+ try {
+ onError(EncryptError.ErrorCode.General, "" + error);
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ });
+ }, onError);
+};
+
+/**
+ * The default onError callback which does nothing.
+ */
+Producer.defaultOnError = function(errorCode, message)
+{
+ // Do nothing.
+};
+
+Producer.KeyInfo_ = function ProducerKeyInfo()
+{
+ this.beginTimeSlot = 0.0;
+ this.endTimeSlot = 0.0;
+ this.keyBits = null; // Blob
+};
+
+Producer.KeyRequest_ = function ProducerKeyRequest(interests)
+{
+ this.interestCount = interests; // number
+ // The map key is the name URI string. The value is an int count.
+ // (Use a string because we can't use the Name object as the key in JavaScript.)
+ this.repeatAttempts = {};
+ this.encryptedKeys = []; // of Data
+};
+
+/**
+ * Round timeSlot to the nearest whole hour, so that we can store content keys
+ * uniformly (by start of the hour).
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @return {number} The start of the hour as milliseconds since Jan 1, 1970 UTC.
+ */
+Producer.getRoundedTimeSlot_ = function(timeSlot)
+{
+ return Math.round(Math.floor(Math.round(timeSlot) / 3600000.0) * 3600000.0);
+}
+
+/**
+ * Send an interest with the given name through the face with callbacks to
+ * handleCoveringKey_, handleTimeout_ and handleNetworkNack_.
+ * @param {Interest} interest The interest to send.
+ * @param {number} timeSlot The time slot, passed to handleCoveringKey_,
+ * handleTimeout_ and handleNetworkNack_.
+ * @param {function} onEncryptedKeys The OnEncryptedKeys callback, passed to
+ * handleCoveringKey_, handleTimeout_ and handleNetworkNack_.
+ * @param {function} onError This calls onError(errorCode, message) for an error.
+ */
+Producer.prototype.sendKeyInterest_ = function
+ (interest, timeSlot, onEncryptedKeys, onError)
+{
+ var thisProducer = this;
+
+ function onKey(interest, data) {
+ thisProducer.handleCoveringKey_
+ (interest, data, timeSlot, onEncryptedKeys, onError);
+ }
+
+ function onTimeout(interest) {
+ thisProducer.handleTimeout_(interest, timeSlot, onEncryptedKeys, onError);
+ }
+
+ function onNetworkNack(interest, networkNack) {
+ thisProducer.handleNetworkNack_
+ (interest, networkNack, timeSlot, onEncryptedKeys, onError);
+ }
+
+ var request;
+ if (this.keyRetrievalLink_.getDelegations().size() === 0)
+ // We can use the supplied interest without copying.
+ request = interest;
+ else {
+ // Copy the supplied interest and add the Link.
+ request = new Interest(interest);
+ // This will use a cached encoding if available.
+ request.setLinkWireEncoding(this.keyRetrievalLink_.wireEncode());
+ }
+
+ this.face_.expressInterest(request, onKey, onTimeout, onNetworkNack);
+};
+
+/**
+ * This is called from an expressInterest timeout to update the state of
+ * keyRequest. Re-express the interest if the number of retrials is less than
+ * the max limit.
+ * @param {Interest} interest The timed-out interest.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {function} onEncryptedKeys When there are no more interests to process,
+ * this calls onEncryptedKeys(keys) where keys is a list of encrypted content
+ * key Data packets. If onEncryptedKeys is null, this does not use it.
+ * @param {function} onError This calls onError(errorCode, message) for an error.
+ */
+Producer.prototype.handleTimeout_ = function
+ (interest, timeSlot, onEncryptedKeys, onError)
+{
+ var timeCount = Math.round(timeSlot);
+ var keyRequest = this.keyRequests_[timeCount];
+
+ var interestName = interest.getName();
+ var interestNameUri = interestName.toUri();
+
+ if (keyRequest.repeatAttempts[interestNameUri] < this.maxRepeatAttempts_) {
+ // Increase the retrial count.
+ ++keyRequest.repeatAttempts[interestNameUri];
+ this.sendKeyInterest_(interest, timeSlot, onEncryptedKeys, onError);
+ }
+ else
+ // Treat an eventual timeout as a network Nack.
+ this.handleNetworkNack_
+ (interest, new NetworkNack(), timeSlot, onEncryptedKeys, onError);
+};
+
+/**
+ * This is called from an expressInterest OnNetworkNack to handle a network
+ * Nack for the E-KEY requested through the Interest. Decrease the outstanding
+ * E-KEY interest count for the C-KEY corresponding to the timeSlot.
+ * @param {Interest} interest The interest given to expressInterest.
+ * @param {NetworkNack} networkNack The returned NetworkNack (unused).
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {function} onEncryptedKeys When there are no more interests to process,
+ * this calls onEncryptedKeys(keys) where keys is a list of encrypted content
+ * key Data packets. If onEncryptedKeys is null, this does not use it.
+ */
+Producer.prototype.handleNetworkNack_ = function
+ (interest, networkNack, timeSlot, onEncryptedKeys, onError)
+{
+ // We have run out of options....
+ var timeCount = Math.round(timeSlot);
+ this.updateKeyRequest_
+ (this.keyRequests_[timeCount], timeCount, onEncryptedKeys);
+};
+
+/**
+ * Decrease the count of outstanding E-KEY interests for the C-KEY for
+ * timeCount. If the count decreases to 0, invoke onEncryptedKeys.
+ * @param {Producer.KeyRequest_} keyRequest The KeyRequest with the
+ * interestCount to update.
+ * @param {number} timeCount The time count for indexing keyRequests_.
+ * @param {function} onEncryptedKeys When there are no more interests to
+ * process, this calls onEncryptedKeys(keys) where keys is a list of encrypted
+ * content key Data packets. If onEncryptedKeys is null, this does not use it.
+ */
+Producer.prototype.updateKeyRequest_ = function
+ (keyRequest, timeCount, onEncryptedKeys)
+{
+ --keyRequest.interestCount;
+ if (keyRequest.interestCount == 0 && onEncryptedKeys != null) {
+ try {
+ onEncryptedKeys(keyRequest.encryptedKeys);
+ } catch (ex) {
+ console.log("Error in onEncryptedKeys: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ delete this.keyRequests_[timeCount];
+ }
+};
+
+/**
+ * This is called from an expressInterest OnData to check that the encryption
+ * key contained in data fits the timeSlot. This sends a refined interest if
+ * required.
+ * @param {Interest} interest The interest given to expressInterest.
+ * @param {Data} data The fetched Data packet.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {function} onEncryptedKeys When there are no more interests to process,
+ * this calls onEncryptedKeys(keys) where keys is a list of encrypted content
+ * key Data packets. If onEncryptedKeys is null, this does not use it.
+ * @param {function} onError This calls onError(errorCode, message) for an error.
+ */
+Producer.prototype.handleCoveringKey_ = function
+ (interest, data, timeSlot, onEncryptedKeys, onError)
+{
+ var timeCount = Math.round(timeSlot);
+ var keyRequest = this.keyRequests_[timeCount];
+
+ var interestName = interest.getName();
+ var interestNameUrl = interestName.toUri();
+ var keyName = data.getName();
+
+ var begin = Schedule.fromIsoString
+ (keyName.get(Producer.START_TIME_STAMP_INDEX).getValue().toString());
+ var end = Schedule.fromIsoString
+ (keyName.get(Producer.END_TIME_STAMP_INDEX).getValue().toString());
+
+ if (timeSlot >= end) {
+ // If the received E-KEY covers some earlier period, try to retrieve an
+ // E-KEY covering a later one.
+ var timeRange = new Exclude(interest.getExclude());
+ Producer.excludeBefore(timeRange, keyName.get(Producer.START_TIME_STAMP_INDEX));
+ keyRequest.repeatAttempts[interestNameUrl] = 0;
+ this.sendKeyInterest_
+ (new Interest(interestName).setExclude(timeRange).setChildSelector(1),
+ timeSlot, onEncryptedKeys, onError);
+ }
+ else {
+ // If the received E-KEY covers the content key, encrypt the content.
+ var encryptionKey = data.getContent();
+ var thisProducer = this;
+ this.encryptContentKeyPromise_
+ (encryptionKey, keyName, timeSlot, onEncryptedKeys, onError)
+ .then(function(success) {
+ if (success) {
+ var keyInfo = thisProducer.eKeyInfo_[interestNameUrl].keyInfo;
+ keyInfo.beginTimeSlot = begin;
+ keyInfo.endTimeSlot = end;
+ keyInfo.keyBits = encryptionKey;
+ }
+ });
+ }
+};
+
+/**
+ * Get the content key from the database_ and encrypt it for the timeSlot
+ * using encryptionKey.
+ * @param {Blob} encryptionKey The encryption key value.
+ * @param {Name} eKeyName The key name for the EncryptedContent.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {function} onEncryptedKeys When there are no more interests to process,
+ * this calls onEncryptedKeys(keys) where keys is a list of encrypted content
+ * key Data packets. If onEncryptedKeys is null, this does not use it.
+ * @param {function} onError This calls onError(errorCode, message) for an error.
+ * @return {Promise} A promise that returns true if encryption succeeds,
+ * otherwise false.
+ */
+Producer.prototype.encryptContentKeyPromise_ = function
+ (encryptionKey, eKeyName, timeSlot, onEncryptedKeys, onError)
+{
+ var timeCount = Math.round(timeSlot);
+ var keyRequest = this.keyRequests_[timeCount];
+
+ var keyName = new Name(this.namespace_);
+ keyName.append(Encryptor.NAME_COMPONENT_C_KEY);
+ keyName.append(Schedule.toIsoString(Producer.getRoundedTimeSlot_(timeSlot)));
+
+ var cKeyData;
+ var thisProducer = this;
+
+ return this.database_.getContentKeyPromise(timeSlot)
+ .then(function(contentKey) {
+ cKeyData = new Data();
+ cKeyData.setName(keyName);
+ var params = new EncryptParams(EncryptAlgorithmType.RsaOaep);
+ return Encryptor.encryptDataPromise
+ (cKeyData, contentKey, eKeyName, encryptionKey, params);
+ })
+ .then(function() {
+ return SyncPromise.resolve(true);
+ }, function(error) {
+ try {
+ onError(EncryptError.ErrorCode.EncryptionFailure,
+ "encryptData failed: " + error);
+ } catch (ex) {
+ console.log("Error in onError: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ return SyncPromise.resolve(false);
+ })
+ .then(function(success) {
+ if (success) {
+ return thisProducer.keyChain_.signPromise(cKeyData)
+ .then(function() {
+ keyRequest.encryptedKeys.push(cKeyData);
+ thisProducer.updateKeyRequest_(keyRequest, timeCount, onEncryptedKeys);
+ return SyncPromise.resolve(true);
+ });
+ }
+ else
+ return SyncPromise.resolve(false);
+ });
+};
+
+Producer.prototype.getEKeyInfoSize_ = function()
+{
+ // Note: This is really a method to find the key count in any object, but we
+ // don't want to claim that it is a tested and general utility method.
+ var size = 0;
+ for (key in this.eKeyInfo_) {
+ if (this.eKeyInfo_.hasOwnProperty(key))
+ ++size;
+ }
+
+ return size;
+};
+
+// TODO: Move this to be the main representation inside the Exclude object.
+/**
+ * Create a new ExcludeEntry.
+ * @param {Name.Component} component
+ * @param {boolean} anyFollowsComponent
+ */
+Producer.ExcludeEntry = function ExcludeEntry(component, anyFollowsComponent)
+{
+ this.component_ = component;
+ this.anyFollowsComponent_ = anyFollowsComponent;
+};
+
+/**
+ * Create a list of ExcludeEntry from the Exclude object.
+ * @param {Exclude} exclude The Exclude object to read.
+ * @return {Array<ExcludeEntry>} A new array of ExcludeEntry.
+ */
+Producer.getExcludeEntries = function(exclude)
+{
+ var entries = [];
+
+ for (var i = 0; i < exclude.size(); ++i) {
+ if (exclude.get(i) == Exclude.ANY) {
+ if (entries.length == 0)
+ // Add a "beginning ANY".
+ entries.push(new Producer.ExcludeEntry(new Name.Component(), true));
+ else
+ // Set anyFollowsComponent of the final component.
+ entries[entries.length - 1].anyFollowsComponent_ = true;
+ }
+ else
+ entries.push(new Producer.ExcludeEntry(exclude.get(i), false));
+ }
+
+ return entries;
+};
+
+/**
+ * Set the Exclude object from the array of ExcludeEntry.
+ * @param {Exclude} exclude The Exclude object to update.
+ * @param {Array<ExcludeEntry>} entries The array of ExcludeEntry.
+ */
+Producer.setExcludeEntries = function(exclude, entries)
+{
+ exclude.clear();
+
+ for (var i = 0; i < entries.length; ++i) {
+ var entry = entries[i];
+
+ if (i == 0 && entry.component_.getValue().size() == 0 &&
+ entry.anyFollowsComponent_)
+ // This is a "beginning ANY".
+ exclude.appendAny();
+ else {
+ exclude.appendComponent(entry.component_);
+ if (entry.anyFollowsComponent_)
+ exclude.appendAny();
+ }
+ }
+};
+
+/**
+ * Get the latest entry in the array whose component_ is less than or equal to
+ * component.
+ * @param {Array<ExcludeEntry>} entries The array of ExcludeEntry.
+ * @param {Name.Component} component The component to compare.
+ * @return {number} The index of the found entry, or -1 if not found.
+ */
+Producer.findEntryBeforeOrAt = function(entries, component)
+{
+ var i = entries.length - 1;
+ while (i >= 0) {
+ if (entries[i].component_.compare(component) <= 0)
+ break;
+ --i;
+ }
+
+ return i;
+};
+
+/**
+ * Exclude all components in the range beginning at "from".
+ * @param {Exclude} exclude The Exclude object to update.
+ * @param {Name.Component} from The first component in the exclude range.
+ */
+Producer.excludeAfter = function(exclude, from)
+{
+ var entries = Producer.getExcludeEntries(exclude);
+
+ var iNewFrom;
+ var iFoundFrom = Producer.findEntryBeforeOrAt(entries, from);
+ if (iFoundFrom < 0) {
+ // There is no entry before "from" so insert at the beginning.
+ entries.splice(0, 0, new Producer.ExcludeEntry(from, true));
+ iNewFrom = 0;
+ }
+ else {
+ var foundFrom = entries[iFoundFrom];
+
+ if (!foundFrom.anyFollowsComponent_) {
+ if (foundFrom.component_.equals(from)) {
+ // There is already an entry with "from", so just set the "ANY" flag.
+ foundFrom.anyFollowsComponent_ = true;
+ iNewFrom = iFoundFrom;
+ }
+ else {
+ // Insert following the entry before "from".
+ entries.splice(iFoundFrom + 1, 0, new Producer.ExcludeEntry(from, true));
+ iNewFrom = iFoundFrom + 1;
+ }
+ }
+ else
+ // The entry before "from" already has an "ANY" flag, so do nothing.
+ iNewFrom = iFoundFrom;
+ }
+
+ // Remove intermediate entries since they are inside the range.
+ var iRemoveBegin = iNewFrom + 1;
+ var nRemoveNeeded = entries.length - iRemoveBegin;
+ entries.splice(iRemoveBegin, nRemoveNeeded);
+
+ Producer.setExcludeEntries(exclude, entries);
+};
+
+/**
+ * Exclude all components in the range ending at "to".
+ * @param {Exclude} exclude The Exclude object to update.
+ * @param {Name.Component} to The last component in the exclude range.
+ */
+Producer.excludeBefore = function(exclude, to)
+{
+ Producer.excludeRange(exclude, new Name.Component(), to);
+};
+
+/**
+ * Exclude all components in the range beginning at "from" and ending at "to".
+ * @param {Exclude} exclude The Exclude object to update.
+ * @param {Name.Component} from The first component in the exclude range.
+ * @param {Name.Component} to The last component in the exclude range.
+ */
+Producer.excludeRange = function(exclude, from, to)
+{
+ if (from.compare(to) >= 0) {
+ if (from.compare(to) == 0)
+ throw new Error
+ ("excludeRange: from == to. To exclude a single component, sue excludeOne.");
+ else
+ throw new Error
+ ("excludeRange: from must be less than to. Invalid range: [" +
+ from.toEscapedString() + ", " + to.toEscapedString() + "]");
+ }
+
+ var entries = Producer.getExcludeEntries(exclude);
+
+ var iNewFrom;
+ var iFoundFrom = Producer.findEntryBeforeOrAt(entries, from);
+ if (iFoundFrom < 0) {
+ // There is no entry before "from" so insert at the beginning.
+ entries.splice(0, 0, new Producer.ExcludeEntry(from, true));
+ iNewFrom = 0;
+ }
+ else {
+ var foundFrom = entries[iFoundFrom];
+
+ if (!foundFrom.anyFollowsComponent_) {
+ if (foundFrom.component_.equals(from)) {
+ // There is already an entry with "from", so just set the "ANY" flag.
+ foundFrom.anyFollowsComponent_ = true;
+ iNewFrom = iFoundFrom;
+ }
+ else {
+ // Insert following the entry before "from".
+ entries.splice(iFoundFrom + 1, 0, new Producer.ExcludeEntry(from, true));
+ iNewFrom = iFoundFrom + 1;
+ }
+ }
+ else
+ // The entry before "from" already has an "ANY" flag, so do nothing.
+ iNewFrom = iFoundFrom;
+ }
+
+ // We have at least one "from" before "to", so we know this will find an entry.
+ var iFoundTo = Producer.findEntryBeforeOrAt(entries, to);
+ var foundTo = entries[iFoundTo];
+ if (iFoundTo == iNewFrom)
+ // Insert the "to" immediately after the "from".
+ entries.splice(iNewFrom + 1, 0, new Producer.ExcludeEntry(to, false));
+ else {
+ var iRemoveEnd;
+ if (!foundTo.anyFollowsComponent_) {
+ if (foundTo.component_.equals(to))
+ // The "to" entry already exists. Remove up to it.
+ iRemoveEnd = iFoundTo;
+ else {
+ // Insert following the previous entry, which will be removed.
+ entries.splice(iFoundTo + 1, 0, new Producer.ExcludeEntry(to, false));
+ iRemoveEnd = iFoundTo + 1;
+ }
+ }
+ else
+ // "to" follows a component which is already followed by "ANY", meaning
+ // the new range now encompasses it, so remove the component.
+ iRemoveEnd = iFoundTo + 1;
+
+ // Remove intermediate entries since they are inside the range.
+ var iRemoveBegin = iNewFrom + 1;
+ var nRemoveNeeded = iRemoveEnd - iRemoveBegin;
+ entries.splice(iRemoveBegin, nRemoveNeeded);
+ }
+
+ Producer.setExcludeEntries(exclude, entries);
+};
+
+Producer.START_TIME_STAMP_INDEX = -2;
+Producer.END_TIME_STAMP_INDEX = -1;
+Producer.NO_LINK = new Link();
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/repetitive-interval https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Interval = require('./interval.js').Interval;
+
+/**
+ * A RepetitiveInterval is an advanced interval which can repeat and can be used
+ * to find a simple Interval that a time point falls in. Create a
+ * RepetitiveInterval with one of these forms:
+ * RepetitiveInterval() A RepetitiveInterval with one day duration, non-repeating..
+ * RepetitiveInterval(startDate, endDate, intervalStartHour, intervalEndHour, nRepeats, repeatUnit).
+ * RepetitiveInterval(repetitiveInterval).
+ * @param {number} startDate The start date as milliseconds since Jan 1, 1970 UTC.
+ * startDate must be earlier than or same as endDate. Or if repeatUnit is
+ * RepetitiveInterval.RepeatUnit.NONE, then it must equal endDate.
+ * @param {number} endDate The end date as milliseconds since Jan 1, 1970 UTC.
+ * @param {number} intervalStartHour The start hour in the day, from 0 to 23.
+ * intervalStartHour must be less than intervalEndHour.
+ * @param {number} intervalEndHour The end hour in the day from 1 to 24.
+ * @param {number} nRepeats (optional) Repeat the interval nRepeats repetitions,
+ * every unit, until endDate. If ommitted, use 0.
+ * @param {number} repeatUnit (optional) The unit of the repetition, from
+ * RepetitiveInterval.RepeatUnit. If ommitted, use NONE. If this is NONE or
+ * ommitted, then startDate must equal endDate.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var RepetitiveInterval = function RepetitiveInterval
+ (startDate, endDate, intervalStartHour, intervalEndHour, nRepeats, repeatUnit)
+{
+ if (typeof startDate === 'object' && startDate instanceof RepetitiveInterval) {
+ // Make a copy.
+ repetitiveInterval = startDate;
+
+ this.startDate_ = repetitiveInterval.startDate_;
+ this.endDate_ = repetitiveInterval.endDate_;
+ this.intervalStartHour_ = repetitiveInterval.intervalStartHour_;
+ this.intervalEndHour_ = repetitiveInterval.intervalEndHour_;
+ this.nRepeats_ = repetitiveInterval.nRepeats_;
+ this.repeatUnit_ = repetitiveInterval.repeatUnit_;
+ }
+ else if (typeof startDate === 'number') {
+ if (nRepeats == undefined)
+ nRepeats = 0;
+ if (repeatUnit == undefined)
+ repeatUnit = RepetitiveInterval.RepeatUnit.NONE;
+
+ this.startDate_ = RepetitiveInterval.toDateOnlyMilliseconds_(startDate);
+ this.endDate_ = RepetitiveInterval.toDateOnlyMilliseconds_(endDate);
+ this.intervalStartHour_ = Math.round(intervalStartHour);
+ this.intervalEndHour_ = Math.round(intervalEndHour);
+ this.nRepeats_ = Math.round(nRepeats);
+ this.repeatUnit_ = repeatUnit;
+
+ // Validate.
+ if (!(this.intervalStartHour_ < this.intervalEndHour_))
+ throw new Error("ReptitiveInterval: startHour must be less than endHour");
+ if (!(this.startDate_ <= this.endDate_))
+ throw new Error
+ ("ReptitiveInterval: startDate must be earlier than or same as endDate");
+ if (!(this.intervalStartHour_ >= 0))
+ throw new Error("ReptitiveInterval: intervalStartHour must be non-negative");
+ if (!(this.intervalEndHour_ >= 1 && this.intervalEndHour_ <= 24))
+ throw new Error("ReptitiveInterval: intervalEndHour must be from 1 to 24");
+ if (this.repeatUnit_ == RepetitiveInterval.RepeatUnit.NONE) {
+ if (!(this.startDate_ == this.endDate_))
+ throw new Error
+ ("ReptitiveInterval: With RepeatUnit.NONE, startDate must equal endDate");
+ }
+ }
+ else {
+ // The default constructor.
+ this.startDate_ = -Number.MAX_VALUE;
+ this.endDate_ = -Number.MAX_VALUE;
+ this.intervalStartHour_ = 0;
+ this.intervalEndHour_ = 24;
+ this.nRepeats_ = 0;
+ this.repeatUnit_ = RepetitiveInterval.RepeatUnit.NONE;
+ }
+};
+
+exports.RepetitiveInterval = RepetitiveInterval;
+
+RepetitiveInterval.RepeatUnit = {
+ NONE: 0,
+ DAY: 1,
+ MONTH: 2,
+ YEAR: 3
+};
+
+/**
+ * Get an interval that covers the time point. If there is no interval
+ * covering the time point, this returns false for isPositive and returns a
+ * negative interval.
+ * @param {number} timePoint The time point as milliseconds since Jan 1, 1970 UTC.
+ * @return {object} An associative array with fields
+ * (isPositive, interval) where
+ * isPositive is true if the returned interval is
+ * positive or false if negative, and interval is the Interval covering the time
+ * point or a negative interval if not found.
+ */
+RepetitiveInterval.prototype.getInterval = function(timePoint)
+{
+ var isPositive;
+ var startTime;
+ var endTime;
+
+ if (!this.hasIntervalOnDate_(timePoint)) {
+ // There is no interval on the date of timePoint.
+ startTime = RepetitiveInterval.toDateOnlyMilliseconds_(timePoint);
+ endTime = RepetitiveInterval.toDateOnlyMilliseconds_(timePoint) +
+ 24 * RepetitiveInterval.MILLISECONDS_IN_HOUR;
+ isPositive = false;
+ }
+ else {
+ // There is an interval on the date of timePoint.
+ startTime = RepetitiveInterval.toDateOnlyMilliseconds_(timePoint) +
+ this.intervalStartHour_ * RepetitiveInterval.MILLISECONDS_IN_HOUR;
+ endTime = RepetitiveInterval.toDateOnlyMilliseconds_(timePoint) +
+ this.intervalEndHour_ * RepetitiveInterval.MILLISECONDS_IN_HOUR;
+
+ // check if in the time duration
+ if (timePoint < startTime) {
+ endTime = startTime;
+ startTime = RepetitiveInterval.toDateOnlyMilliseconds_(timePoint);
+ isPositive = false;
+ }
+ else if (timePoint > endTime) {
+ startTime = endTime;
+ endTime = RepetitiveInterval.toDateOnlyMilliseconds_(timePoint) +
+ RepetitiveInterval.MILLISECONDS_IN_DAY;
+ isPositive = false;
+ }
+ else
+ isPositive = true;
+ }
+
+ return { isPositive: isPositive, interval: new Interval(startTime, endTime) };
+};
+
+/**
+ * Compare this to the other RepetitiveInterval.
+ * @param {RepetitiveInterval} other The other RepetitiveInterval to compare to.
+ * @return {number} -1 if this is less than the other, 1 if greater and 0 if equal.
+ */
+RepetitiveInterval.prototype.compare = function(other)
+{
+ if (this.startDate_ < other.startDate_)
+ return -1;
+ if (this.startDate_ > other.startDate_)
+ return 1;
+
+ if (this.endDate_ < other.endDate_)
+ return -1;
+ if (this.endDate_ > other.endDate_)
+ return 1;
+
+ if (this.intervalStartHour_ < other.intervalStartHour_)
+ return -1;
+ if (this.intervalStartHour_ > other.intervalStartHour_)
+ return 1;
+
+ if (this.intervalEndHour_ < other.intervalEndHour_)
+ return -1;
+ if (this.intervalEndHour_ > other.intervalEndHour_)
+ return 1;
+
+ if (this.nRepeats_ < other.nRepeats_)
+ return -1;
+ if (this.nRepeats_ > other.nRepeats_)
+ return 1;
+
+ if (this.repeatUnit_ < other.repeatUnit_)
+ return -1;
+ if (this.repeatUnit_ > other.repeatUnit_)
+ return 1;
+
+ return 0;
+};
+
+/**
+ * Get the start date.
+ * @return {number} The start date as milliseconds since Jan 1, 1970 UTC.
+ */
+RepetitiveInterval.prototype.getStartDate = function()
+{
+ return this.startDate_;
+};
+
+/**
+ * Get the end date.
+ * @return {number} The end date as milliseconds since Jan 1, 1970 UTC.
+ */
+RepetitiveInterval.prototype.getEndDate = function()
+{
+ return this.endDate_;
+};
+
+/**
+ * Get the interval start hour.
+ * @return {number} The interval start hour.
+ */
+RepetitiveInterval.prototype.getIntervalStartHour = function()
+{
+ return this.intervalStartHour_;
+}
+
+/**
+ * Get the interval end hour.
+ * @return {number} The interval end hour.
+ */
+RepetitiveInterval.prototype.getIntervalEndHour = function()
+{
+ return this.intervalEndHour_;
+};
+
+/**
+ * Get the number of repeats.
+ * @return {number} The number of repeats.
+ */
+RepetitiveInterval.prototype.getNRepeats = function()
+{
+ return this.nRepeats_;
+};
+
+/**
+ * Get the repeat unit.
+ * @return {number} The repeat unit, from RepetitiveInterval.RepeatUnit.
+ */
+RepetitiveInterval.prototype.getRepeatUnit = function()
+{
+ return this.repeatUnit_;
+};
+
+/**
+ * Check if the date of the time point is in any interval.
+ * @param {number} timePoint The time point as milliseconds since Jan 1, 1970 UTC.
+ * @return {boolean} True if the date of the time point is in any interval.
+ */
+RepetitiveInterval.prototype.hasIntervalOnDate_ = function(timePoint)
+{
+ var timePointDateMilliseconds = RepetitiveInterval.toDateOnlyMilliseconds_(timePoint);
+
+ if (timePointDateMilliseconds < this.startDate_ ||
+ timePointDateMilliseconds > this.endDate_)
+ return false;
+
+ if (this.repeatUnit_ == RepetitiveInterval.RepeatUnit.NONE)
+ return true;
+ else if (this.repeatUnit_ == RepetitiveInterval.RepeatUnit.DAY) {
+ var durationDays = (timePointDateMilliseconds - this.startDate_) /
+ RepetitiveInterval.MILLISECONDS_IN_DAY;
+ if (durationDays % this.nRepeats_ == 0)
+ return true;
+ }
+ else {
+ var timePointDate = new Date(timePointDateMilliseconds);
+ var startDate = new Date(this.startDate_);
+
+ if (this.repeatUnit_ == RepetitiveInterval.RepeatUnit.MONTH &&
+ timePointDate.getUTCDate() == startDate.getUTCDate()) {
+ var yearDifference =
+ timePointDate.getUTCFullYear() - startDate.getUTCFullYear();
+ var monthDifference = 12 * yearDifference +
+ timePointDate.getUTCMonth() - startDate.getUTCMonth();
+ if (monthDifference % this.nRepeats_ == 0)
+ return true;
+ }
+ else if (this.repeatUnit_ == RepetitiveInterval.RepeatUnit.YEAR &&
+ timePointDate.getUTCDate() == startDate.getUTCDate() &&
+ timePointDate.getUTCMonth() == startDate.getUTCMonth()) {
+ var difference = timePointDate.getUTCFullYear() - startDate.getUTCFullYear();
+ if (difference % this.nRepeats_ == 0)
+ return true;
+ }
+ }
+
+ return false;
+};
+
+/**
+ * Return a time point on the beginning of the date (without hours, minutes, etc.)
+ * @param {number} timePoint The time point as milliseconds since Jan 1, 1970 UTC.
+ * @return {number} A time point as milliseconds since Jan 1, 1970 UTC.
+ */
+RepetitiveInterval.toDateOnlyMilliseconds_ = function(timePoint)
+{
+ var result = Math.round(timePoint);
+ result -= result % RepetitiveInterval.MILLISECONDS_IN_DAY;
+ return result;
+};
+
+RepetitiveInterval.MILLISECONDS_IN_HOUR = 3600 * 1000;
+RepetitiveInterval.MILLISECONDS_IN_DAY = 24 * 3600 * 1000;
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-group-encrypt src/schedule https://github.com/named-data/ndn-group-encrypt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Interval = require('./interval.js').Interval; /** @ignore */
+var RepetitiveInterval = require('./repetitive-interval.js').RepetitiveInterval; /** @ignore */
+var Tlv = require('../encoding/tlv/tlv.js').Tlv; /** @ignore */
+var TlvEncoder = require('../encoding/tlv/tlv-encoder.js').TlvEncoder; /** @ignore */
+var TlvDecoder = require('../encoding/tlv/tlv-decoder.js').TlvDecoder; /** @ignore */
+var Blob = require('../util/blob.js').Blob;
+
+/**
+ * Schedule is used to manage the times when a member can access data using two
+ * sets of RepetitiveInterval as follows. whiteIntervalList is an ordered
+ * set for the times a member is allowed to access to data, and
+ * blackIntervalList is for the times a member is not allowed.
+ * Create a Schedule with one of these forms:
+ * Schedule() A Schedule with empty whiteIntervalList and blackIntervalList.
+ * Schedule(schedule). A copy of the given schedule.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var Schedule = function Schedule(value)
+{
+ if (typeof value === 'object' && value instanceof Schedule) {
+ // Make a copy.
+ var schedule = value;
+
+ // RepetitiveInterval is immutable, so we don't need to make a deep copy.
+ this.whiteIntervalList_ = schedule.whiteIntervalList_.slice(0);
+ this.blackIntervalList_ = schedule.blackIntervalList_.slice(0);
+ }
+ else {
+ // The default constructor.
+ this.whiteIntervalList_ = [];
+ this.blackIntervalList_ = [];
+ }
+};
+
+exports.Schedule = Schedule;
+
+/**
+ * Add the repetitiveInterval to the whiteIntervalList.
+ * @param {RepetitiveInterval} repetitiveInterval The RepetitiveInterval to add.
+ * If the list already contains the same RepetitiveInterval, this does nothing.
+ * @return {Schedule} This Schedule so you can chain calls to add.
+ */
+Schedule.prototype.addWhiteInterval = function(repetitiveInterval)
+{
+ // RepetitiveInterval is immutable, so we don't need to make a copy.
+ Schedule.sortedSetAdd_(this.whiteIntervalList_, repetitiveInterval);
+ return this;
+};
+
+/**
+ * Add the repetitiveInterval to the blackIntervalList.
+ * @param {RepetitiveInterval} repetitiveInterval The RepetitiveInterval to add.
+ * If the list already contains the same RepetitiveInterval, this does nothing.
+ * @return {Schedule} This Schedule so you can chain calls to add.
+ */
+Schedule.prototype.addBlackInterval = function(repetitiveInterval)
+{
+ // RepetitiveInterval is immutable, so we don't need to make a copy.
+ Schedule.sortedSetAdd_(this.blackIntervalList_, repetitiveInterval);
+ return this;
+};
+
+/**
+ * Get the interval that covers the time stamp. This iterates over the two
+ * repetitive interval sets and find the shortest interval that allows a group
+ * member to access the data. If there is no interval covering the time stamp,
+ * this returns false for isPositive and a negative interval.
+ * @param {number} timeStamp The time stamp as milliseconds since Jan 1, 1970 UTC.
+ * @return {object} An associative array with fields
+ * (isPositive, interval) where
+ * isPositive is true if the returned interval is positive or false if negative,
+ * and interval is the Interval covering the time stamp, or a negative interval
+ * if not found.
+ */
+Schedule.prototype.getCoveringInterval = function(timeStamp)
+{
+ var blackPositiveResult = new Interval(true);
+ var whitePositiveResult = new Interval(true);
+
+ var blackNegativeResult = new Interval();
+ var whiteNegativeResult = new Interval();
+
+ // Get the black result.
+ Schedule.calculateIntervalResult_
+ (this.blackIntervalList_, timeStamp, blackPositiveResult, blackNegativeResult);
+
+ // If the black positive result is not empty, then isPositive must be false.
+ if (!blackPositiveResult.isEmpty())
+ return { isPositive: false, interval: blackPositiveResult };
+
+ // Get the whiteResult.
+ Schedule.calculateIntervalResult_
+ (this.whiteIntervalList_, timeStamp, whitePositiveResult, whiteNegativeResult);
+
+ if (whitePositiveResult.isEmpty() && !whiteNegativeResult.isValid()) {
+ // There is no white interval covering the time stamp.
+ // Return false and a 24-hour interval.
+ var timeStampDateOnly =
+ RepetitiveInterval.toDateOnlyMilliseconds_(timeStamp);
+ return { isPositive: false,
+ interval: new Interval
+ (timeStampDateOnly,
+ timeStampDateOnly + RepetitiveInterval.MILLISECONDS_IN_DAY) };
+ }
+
+ if (!whitePositiveResult.isEmpty()) {
+ // There is white interval covering the time stamp.
+ // Return true and calculate the intersection.
+ if (blackNegativeResult.isValid())
+ return { isPositive: true,
+ interval: whitePositiveResult.intersectWith(blackNegativeResult) };
+ else
+ return { isPositive: true, interval: whitePositiveResult };
+ }
+ else
+ // There is no white interval covering the time stamp.
+ // Return false.
+ return { isPositive: false, interval: whiteNegativeResult };
+};
+
+/**
+ * Encode this Schedule.
+ * @return {Blob} The encoded buffer.
+ */
+Schedule.prototype.wireEncode = function()
+{
+ // For now, don't use WireFormat and hardcode to use TLV since the encoding
+ // doesn't go out over the wire, only into the local SQL database.
+ var encoder = new TlvEncoder(256);
+ var saveLength = encoder.getLength();
+
+ // Encode backwards.
+ // Encode the blackIntervalList.
+ var saveLengthForList = encoder.getLength();
+ for (var i = this.blackIntervalList_.length - 1; i >= 0; i--)
+ Schedule.encodeRepetitiveInterval_(this.blackIntervalList_[i], encoder);
+ encoder.writeTypeAndLength
+ (Tlv.Encrypt_BlackIntervalList, encoder.getLength() - saveLengthForList);
+
+ // Encode the whiteIntervalList.
+ saveLengthForList = encoder.getLength();
+ for (var i = this.whiteIntervalList_.length - 1; i >= 0; i--)
+ Schedule.encodeRepetitiveInterval_(this.whiteIntervalList_[i], encoder);
+ encoder.writeTypeAndLength
+ (Tlv.Encrypt_WhiteIntervalList, encoder.getLength() - saveLengthForList);
+
+ encoder.writeTypeAndLength
+ (Tlv.Encrypt_Schedule, encoder.getLength() - saveLength);
+
+ return new Blob(encoder.getOutput(), false);
+};
+
+/**
+ * Decode the input and update this Schedule object.
+ * @param {Blob|Buffer} input The input buffer to decode. For Buffer, this reads
+ * from position() to limit(), but does not change the position.
+ * @throws DecodingException For invalid encoding.
+ */
+Schedule.prototype.wireDecode = function(input)
+{
+ // If input is a blob, get its buf().
+ var decodeBuffer = typeof input === 'object' && input instanceof Blob ?
+ input.buf() : input;
+
+ // For now, don't use WireFormat and hardcode to use TLV since the encoding
+ // doesn't go out over the wire, only into the local SQL database.
+ var decoder = new TlvDecoder(decodeBuffer);
+
+ var endOffset = decoder.readNestedTlvsStart(Tlv.Encrypt_Schedule);
+
+ // Decode the whiteIntervalList.
+ this.whiteIntervalList_ = [];
+ var listEndOffset = decoder.readNestedTlvsStart(Tlv.Encrypt_WhiteIntervalList);
+ while (decoder.getOffset() < listEndOffset)
+ Schedule.sortedSetAdd_
+ (this.whiteIntervalList_, Schedule.decodeRepetitiveInterval_(decoder));
+ decoder.finishNestedTlvs(listEndOffset);
+
+ // Decode the blackIntervalList.
+ this.blackIntervalList_ = [];
+ listEndOffset = decoder.readNestedTlvsStart(Tlv.Encrypt_BlackIntervalList);
+ while (decoder.getOffset() < listEndOffset)
+ Schedule.sortedSetAdd_
+ (this.blackIntervalList_, Schedule.decodeRepetitiveInterval_(decoder));
+ decoder.finishNestedTlvs(listEndOffset);
+
+ decoder.finishNestedTlvs(endOffset);
+};
+
+/**
+ * Insert element into the list, sorted using element.compare(). If it is a
+ * duplicate of an existing list element, don't add it.
+ */
+Schedule.sortedSetAdd_ = function(list, element)
+{
+ // Find the index of the first element where it is not less than element.
+ var i = 0;
+ while (i < list.length) {
+ var comparison = list[i].compare(element);
+ if (comparison == 0)
+ // Don't add a duplicate.
+ return;
+ if (!(comparison < 0))
+ break;
+
+ ++i;
+ }
+
+ list.splice(i, 0, element);
+};
+
+/**
+ * Encode the RepetitiveInterval as NDN-TLV to the encoder.
+ * @param {RepetitiveInterval} repetitiveInterval The RepetitiveInterval to encode.
+ * @param {TlvEncoder} encoder The TlvEncoder to receive the encoding.
+ */
+Schedule.encodeRepetitiveInterval_ = function(repetitiveInterval, encoder)
+{
+ var saveLength = encoder.getLength();
+
+ // Encode backwards.
+ // The RepeatUnit enum has the same values as the encoding.
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.Encrypt_RepeatUnit, repetitiveInterval.getRepeatUnit());
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.Encrypt_NRepeats, repetitiveInterval.getNRepeats());
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.Encrypt_IntervalEndHour, repetitiveInterval.getIntervalEndHour());
+ encoder.writeNonNegativeIntegerTlv
+ (Tlv.Encrypt_IntervalStartHour, repetitiveInterval.getIntervalStartHour());
+ // Use Blob to convert the string to UTF8 encoding.
+ encoder.writeBlobTlv(Tlv.Encrypt_EndDate,
+ new Blob(Schedule.toIsoString(repetitiveInterval.getEndDate())).buf());
+ encoder.writeBlobTlv(Tlv.Encrypt_StartDate,
+ new Blob(Schedule.toIsoString(repetitiveInterval.getStartDate())).buf());
+
+ encoder.writeTypeAndLength
+ (Tlv.Encrypt_RepetitiveInterval, encoder.getLength() - saveLength);
+};
+
+/**
+ * Decode the input as an NDN-TLV RepetitiveInterval.
+ * @param {TlvDecoder} decoder The decoder with the input to decode.
+ * @return {RepetitiveInterval} A new RepetitiveInterval with the decoded result.
+ */
+Schedule.decodeRepetitiveInterval_ = function(decoder)
+{
+ var endOffset = decoder.readNestedTlvsStart(Tlv.Encrypt_RepetitiveInterval);
+
+ // Use Blob to convert UTF8 to a string.
+ var startDate = Schedule.fromIsoString
+ (new Blob(decoder.readBlobTlv(Tlv.Encrypt_StartDate), true).toString());
+ var endDate = Schedule.fromIsoString
+ (new Blob(decoder.readBlobTlv(Tlv.Encrypt_EndDate), true).toString());
+ var startHour = decoder.readNonNegativeIntegerTlv(Tlv.Encrypt_IntervalStartHour);
+ var endHour = decoder.readNonNegativeIntegerTlv(Tlv.Encrypt_IntervalEndHour);
+ var nRepeats = decoder.readNonNegativeIntegerTlv(Tlv.Encrypt_NRepeats);
+
+ // The RepeatUnit enum has the same values as the encoding.
+ var repeatUnit = decoder.readNonNegativeIntegerTlv(Tlv.Encrypt_RepeatUnit);
+
+ decoder.finishNestedTlvs(endOffset);
+ return new RepetitiveInterval
+ (startDate, endDate, startHour, endHour, nRepeats, repeatUnit);
+};
+
+/**
+ * A helper function to calculate black interval results or white interval
+ * results.
+ * @param {Array} list The set of RepetitiveInterval, which can be the white
+ * list or the black list.
+ * @param {number} timeStamp The time stamp as milliseconds since Jan 1, 1970 UTC.
+ * @param {Interval} positiveResult The positive result which is updated.
+ * @param {Interval} negativeResult The negative result which is updated.
+ */
+Schedule.calculateIntervalResult_ = function
+ (list, timeStamp, positiveResult, negativeResult)
+{
+ for (var i = 0; i < list.length; ++i) {
+ var element = list[i];
+
+ var result = element.getInterval(timeStamp);
+ var tempInterval = result.interval;
+ if (result.isPositive == true)
+ positiveResult.unionWith(tempInterval);
+ else {
+ if (!negativeResult.isValid())
+ negativeResult.set(tempInterval);
+ else
+ negativeResult.intersectWith(tempInterval);
+ }
+ }
+};
+
+/**
+ * Convert a UNIX timestamp to ISO time representation with the "T" in the middle.
+ * @param {number} msSince1970 Timestamp as milliseconds since Jan 1, 1970 UTC.
+ * @return {string} The string representation.
+ */
+Schedule.toIsoString = function(msSince1970)
+{
+ var utcTime = new Date(Math.round(msSince1970));
+ return utcTime.getUTCFullYear() +
+ Schedule.to2DigitString(utcTime.getUTCMonth() + 1) +
+ Schedule.to2DigitString(utcTime.getUTCDate()) +
+ "T" +
+ Schedule.to2DigitString(utcTime.getUTCHours()) +
+ Schedule.to2DigitString(utcTime.getUTCMinutes()) +
+ Schedule.to2DigitString(utcTime.getUTCSeconds());
+};
+
+/**
+ * A private method to zero pad an integer to 2 digits.
+ * @param {number} x The number to pad. Assume it is a non-negative integer.
+ * @return {string} The padded string.
+ */
+Schedule.to2DigitString = function(x)
+{
+ var result = x.toString();
+ return result.length === 1 ? "0" + result : result;
+};
+
+/**
+ * Convert an ISO time representation with the "T" in the middle to a UNIX
+ * timestamp.
+ * @param {string} timeString The ISO time representation.
+ * @return {number} The timestamp as milliseconds since Jan 1, 1970 UTC.
+ */
+Schedule.fromIsoString = function(timeString)
+{
+ if (timeString.length != 15 || timeString.substr(8, 1) != 'T')
+ throw new Error("fromIsoString: Format is not the expected yyyymmddThhmmss");
+
+ return Date.UTC
+ (parseInt(timeString.substr(0, 4)),
+ parseInt(timeString.substr(4, 2) - 1),
+ parseInt(timeString.substr(6, 2)),
+ parseInt(timeString.substr(9, 2)),
+ parseInt(timeString.substr(11, 2)),
+ parseInt(timeString.substr(13, 2)));
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// Don't require modules since this is meant for the browser, not Node.js.
+
+/**
+ * IndexedDbConsumerDb extends ConsumerDb to implement the storage of decryption
+ * keys for the consumer using the browser's IndexedDB service.
+ * Create an IndexedDbConsumerDb to use the given IndexedDB database name.
+ * @param {string} databaseName IndexedDB database name.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var IndexedDbConsumerDb = function IndexedDbConsumerDb(databaseName)
+{
+ ConsumerDb.call(this);
+
+ this.database = new Dexie(databaseName);
+ this.database.version(1).stores({
+ // "keyName" is the key name URI // string
+ // (Note: In SQLite3, the key name is the TLV encoded bytes, but we can't
+ // index on a byte array in IndexedDb.)
+ // "key" is the key bytes // Uint8Array
+ decryptionKeys: "keyName"
+ });
+ this.database.open();
+};
+
+IndexedDbConsumerDb.prototype = new ConsumerDb();
+IndexedDbConsumerDb.prototype.name = "IndexedDbConsumerDb";
+
+/**
+ * Get the key with keyName from the database.
+ * @param {Name} keyName The key name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that returns a Blob with the encoded key (or an
+ * isNull Blob if cannot find the key with keyName), or that is
+ * rejected with ConsumerDb.Error for a database error.
+ */
+IndexedDbConsumerDb.prototype.getKeyPromise = function(keyName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new ConsumerDb.Error(new Error
+ ("IndexedDbConsumerDb.getKeyPromise is only supported for async")));
+
+ return this.database.decryptionKeys.get(keyName.toUri())
+ .then(function(decryptionKeysEntry) {
+ if (decryptionKeysEntry)
+ return Promise.resolve(new Blob(decryptionKeysEntry.key));
+ else
+ return Promise.resolve(new Blob());
+ })
+ .catch(function(ex) {
+ return Promise.reject(new ConsumerDb.Error(new Error
+ ("IndexedDbConsumerDb.getKeyPromise: Error: " + ex)));
+ });
+};
+
+/**
+ * Add the key with keyName and keyBlob to the database.
+ * @param {Name} keyName The key name.
+ * @param {Blob} keyBlob The encoded key.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the key is added, or that
+ * is rejected with ConsumerDb.Error if a key with the same keyName already
+ * exists, or other database error.
+ */
+IndexedDbConsumerDb.prototype.addKeyPromise = function(keyName, keyBlob, useSync)
+{
+ if (useSync)
+ return Promise.reject(new ConsumerDb.Error(new Error
+ ("IndexedDbConsumerDb.addKeyPromise is only supported for async")));
+
+ // Add rejects if the primary key already exists.
+ return this.database.decryptionKeys.add
+ ({ keyName: keyName.toUri(), key: keyBlob.buf() })
+ .catch(function(ex) {
+ return Promise.reject(new ConsumerDb.Error(new Error
+ ("IndexedDbConsumerDb.addKeyPromise: Error: " + ex)));
+ });
+};
+
+/**
+ * Delete the key with keyName from the database. If there is no key with
+ * keyName, do nothing.
+ * @param {Name} keyName The key name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the key is deleted (or there
+ * is no such key), or that is rejected with ConsumerDb.Error for a database
+ * error.
+ */
+IndexedDbConsumerDb.prototype.deleteKeyPromise = function(keyName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new ConsumerDb.Error(new Error
+ ("IndexedDbConsumerDb.deleteKeyPromise is only supported for async")));
+
+ return this.database.decryptionKeys.delete(keyName.toUri())
+ .catch(function(ex) {
+ return Promise.reject(new ConsumerDb.Error(new Error
+ ("IndexedDbConsumerDb.deleteKeyPromise: Error: " + ex)));
+ });
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// Don't require modules since this is meant for the browser, not Node.js.
+
+/**
+ * IndexedDbGroupManagerDb extends GroupManagerDb to implement the storage of
+ * data used by the GroupManager using the browser's IndexedDB service.
+ * Create an IndexedDbGroupManagerDb to use the given IndexedDB database name.
+ * @param {string} databaseName IndexedDB database name.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var IndexedDbGroupManagerDb = function IndexedDbGroupManagerDb(databaseName)
+{
+ GroupManagerDb.call(this);
+
+ this.database = new Dexie(databaseName);
+ this.database.version(1).stores({
+ // "scheduleId" is the schedule ID, auto incremented // number
+ // "scheduleName" is the schedule name, unique // string
+ // "schedule" is the TLV-encoded schedule // Uint8Array
+ schedules: "++scheduleId, &scheduleName",
+
+ // "memberNameUri" is the member name URI // string
+ // (Note: In SQLite3, the member name index is the TLV encoded bytes, but
+ // we can't index on a byte array in IndexedDb.)
+ // (Note: The SQLite3 table also has an auto-incremented member ID primary
+ // key, but is not used so we omit it to simplify.)
+ // "memberName" is the TLV-encoded member name (same as memberNameUri // Uint8Array
+ // "scheduleId" is the schedule ID, linked to the schedules table // number
+ // (Note: The SQLite3 table has a foreign key to the schedules table with
+ // cascade update and delete, but we have to handle it manually.)
+ // "keyName" is the TLV-encoded key name // Uint8Array
+ // "publicKey" is the encoded key bytes // Uint8Array
+ members: "memberNameUri, scheduleId"
+ });
+ this.database.open();
+};
+
+IndexedDbGroupManagerDb.prototype = new GroupManagerDb();
+IndexedDbGroupManagerDb.prototype.name = "IndexedDbGroupManagerDb";
+
+////////////////////////////////////////////////////// Schedule management.
+
+/**
+ * Check if there is a schedule with the given name.
+ * @param {string} name The name of the schedule.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that returns true if there is a schedule (else
+ * false), or that is rejected with GroupManagerDb.Error for a database error.
+ */
+IndexedDbGroupManagerDb.prototype.hasSchedulePromise = function(name, useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.hasSchedulePromise is only supported for async")));
+
+ return this.getScheduleIdPromise_(name)
+ .then(function(scheduleId) {
+ return Promise.resolve(scheduleId != -1);
+ });
+};
+
+/**
+ * List all the names of the schedules.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that returns a new array of string with the names
+ * of all schedules, or that is rejected with GroupManagerDb.Error for a
+ * database error.
+ */
+IndexedDbGroupManagerDb.prototype.listAllScheduleNamesPromise = function(useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.listAllScheduleNamesPromise is only supported for async")));
+
+ var list = [];
+ return this.database.schedules.each(function(entry) {
+ list.push(entry.scheduleName);
+ })
+ .then(function() {
+ return Promise.resolve(list);
+ })
+ .catch(function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.listAllScheduleNamesPromise: Error: " + ex)));
+ });
+};
+
+/**
+ * Get a schedule with the given name.
+ * @param {string} name The name of the schedule.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that returns a new Schedule object, or that is
+ * rejected with GroupManagerDb.Error if the schedule does not exist or other
+ * database error.
+ */
+IndexedDbGroupManagerDb.prototype.getSchedulePromise = function(name, useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.getSchedulePromise is only supported for async")));
+
+ var thisManager = this;
+ // Use getScheduleIdPromise_ to handle the search on the non-primary key.
+ return this.getScheduleIdPromise_(name)
+ .then(function(scheduleId) {
+ if (scheduleId != -1) {
+ return thisManager.database.schedules.get(scheduleId)
+ .then(function(entry) {
+ // We expect entry to be found, and don't expect an error decoding.
+ var schedule = new Schedule();
+ schedule.wireDecode(new Blob(entry.schedule, false));
+ return Promise.resolve(schedule);
+ })
+ .catch(function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.getSchedulePromise: Error: " + ex)));
+ });
+ }
+ else
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.getSchedulePromise: Cannot get the result from the database")));
+ });
+};
+
+/**
+ * For each member using the given schedule, get the name and public key DER
+ * of the member's key.
+ * @param {string} name The name of the schedule.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that returns a new array of object (where
+ * "keyName" is the Name of the public key and "publicKey" is the Blob of the
+ * public key DER), or that is rejected with GroupManagerDb.Error for a database
+ * error. Note that the member's identity name is keyName.getPrefix(-1). If the
+ * schedule name is not found, the list is empty.
+ */
+IndexedDbGroupManagerDb.prototype.getScheduleMembersPromise = function
+ (name, useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.getScheduleMembersPromise is only supported for async")));
+
+ var list = [];
+ var thisManager = this;
+ // There is only one matching schedule ID, so we can just look it up instead
+ // of doing a more complicated join.
+ return this.getScheduleIdPromise_(name)
+ .then(function(scheduleId) {
+ if (scheduleId == -1)
+ // Return the empty list.
+ return Promise.resolve(list);
+
+ var onEntryError = null;
+ return thisManager.database.members.where("scheduleId").equals(scheduleId)
+ .each(function(entry) {
+ try {
+ var keyName = new Name();
+ keyName.wireDecode(new Blob(entry.keyName, false), TlvWireFormat.get());
+
+ list.push({ keyName: keyName, publicKey: new Blob(entry.publicKey, false) });
+ } catch (ex) {
+ // We don't expect this to happen.
+ onEntryError = new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.getScheduleMembersPromise: Error decoding name: " + ex));
+ }
+ })
+ .then(function() {
+ if (onEntryError)
+ // We got an error decoding.
+ return Promise.reject(onEntryError);
+ else
+ return Promise.resolve(list);
+ }, function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.getScheduleMembersPromise: Error: " + ex)));
+ });
+ });
+};
+
+/**
+ * Add a schedule with the given name.
+ * @param {string} name The name of the schedule. The name cannot be empty.
+ * @param {Schedule} schedule The Schedule to add.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the schedule is added, or that
+ * is rejected with GroupManagerDb.Error if a schedule with the same name
+ * already exists, if the name is empty, or other database error.
+ */
+IndexedDbGroupManagerDb.prototype.addSchedulePromise = function
+ (name, schedule, useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.addSchedulePromise is only supported for async")));
+
+ if (name.length == 0)
+ return Promise.reject(new GroupManagerDb.Error
+ ("IndexedDbGroupManagerDb.addSchedulePromise: The schedule name cannot be empty"));
+
+ // Add rejects if the primary key already exists.
+ return this.database.schedules.add
+ ({ scheduleName: name, schedule: schedule.wireEncode().buf() })
+ .catch(function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.addContentKeyPromise: Error: " + ex)));
+ });
+};
+
+/**
+ * Delete the schedule with the given name. Also delete members which use this
+ * schedule. If there is no schedule with the name, then do nothing.
+ * @param {string} name The name of the schedule.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the schedule is deleted (or
+ * there is no such schedule), or that is rejected with GroupManagerDb.Error for
+ * a database error.
+ */
+IndexedDbGroupManagerDb.prototype.deleteSchedulePromise = function
+ (name, useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.deleteSchedulePromise is only supported for async")));
+
+ var scheduleId;
+ var thisManager = this;
+ return this.getScheduleIdPromise_(name)
+ .then(function(localScheduleId) {
+ scheduleId = localScheduleId;
+
+ // Get the members which use this schedule.
+ return thisManager.database.members.where("scheduleId").equals(scheduleId).toArray();
+ })
+ .then(function(membersEntries) {
+ // Delete the members.
+ var promises = membersEntries.map(function(entry) {
+ return thisManager.database.members.delete(entry.memberNameUri);
+ });
+ return Promise.all(promises);
+ })
+ .then(function() {
+ // Now delete the schedule.
+ return thisManager.database.schedules.delete(scheduleId);
+ })
+ .catch(function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.deleteSchedulePromise: Error: " + ex)));
+ });
+};
+
+/**
+ * Rename a schedule with oldName to newName.
+ * @param {string} oldName The name of the schedule to be renamed.
+ * @param {string} newName The new name of the schedule. The name cannot be empty.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the schedule is renamed, or
+ * that is rejected with GroupManagerDb.Error if a schedule with newName already
+ * exists, if the schedule with oldName does not exist, if newName is empty, or
+ * other database error.
+ */
+IndexedDbGroupManagerDb.prototype.renameSchedulePromise = function
+ (oldName, newName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.renameSchedulePromise is only supported for async")));
+
+ if (newName.length == 0)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.renameSchedule: The schedule newName cannot be empty")));
+
+ var thisManager = this;
+ return this.getScheduleIdPromise_(oldName)
+ .then(function(scheduleId) {
+ if (scheduleId == -1)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.renameSchedule: The schedule oldName does not exist")));
+
+ return thisManager.database.schedules.update
+ (scheduleId, { scheduleName: newName })
+ .catch(function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.renameSchedulePromise: Error: " + ex)));
+ });
+ });
+};
+
+/**
+ * Update the schedule with name and replace the old object with the given
+ * schedule. Otherwise, if no schedule with name exists, a new schedule
+ * with name and the given schedule will be added to database.
+ * @param {string} name The name of the schedule. The name cannot be empty.
+ * @param {Schedule} schedule The Schedule to update or add.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the schedule is updated, or
+ * that is rejected with GroupManagerDb.Error if the name is empty, or other
+ * database error.
+ */
+IndexedDbGroupManagerDb.prototype.updateSchedulePromise = function
+ (name, schedule, useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.updateSchedulePromise is only supported for async")));
+
+ var thisManager = this;
+ return this.getScheduleIdPromise_(name)
+ .then(function(scheduleId) {
+ if (scheduleId == -1)
+ return thisManager.addSchedulePromise(name, schedule);
+
+ return thisManager.database.schedules.update
+ (scheduleId, { schedule: schedule.wireEncode().buf() })
+ .catch(function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.updateSchedulePromise: Error: " + ex)));
+ });
+ });
+};
+
+////////////////////////////////////////////////////// Member management.
+
+/**
+ * Check if there is a member with the given identity name.
+ * @param {Name} identity The member's identity name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that returns true if there is a member (else
+ * false), or that is rejected with GroupManagerDb.Error for a database error.
+ */
+IndexedDbGroupManagerDb.prototype.hasMemberPromise = function(identity, useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.hasMemberPromise is only supported for async")));
+
+ return this.database.members.get(identity.toUri())
+ .then(function(entry) {
+ return Promise.resolve(entry != undefined);
+ })
+ .catch(function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.hasMemberPromise: Error: " + ex)));
+ });
+};
+
+/**
+ * List all the members.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that returns a new array of Name with the names
+ * of all members, or that is rejected with GroupManagerDb.Error for a
+ * database error.
+ */
+IndexedDbGroupManagerDb.prototype.listAllMembersPromise = function(useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.listAllMembersPromise is only supported for async")));
+
+ var list = [];
+ var onEntryError = null;
+ return this.database.members.each(function(entry) {
+ try {
+ var identity = new Name();
+ identity.wireDecode(new Blob(entry.memberName, false), TlvWireFormat.get());
+ list.push(identity);
+ } catch (ex) {
+ // We don't expect this to happen.
+ onEntryError = new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.listAllMembersPromise: Error decoding name: " + ex));
+ }
+ })
+ .then(function() {
+ if (onEntryError)
+ // We got an error decoding.
+ return Promise.reject(onEntryError);
+ else
+ return Promise.resolve(list);
+ }, function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.listAllMembersPromise: Error: " + ex)));
+ });
+};
+
+/**
+ * Get the name of the schedule for the given member's identity name.
+ * @param {Name} identity The member's identity name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that returns the string schedule name, or that is
+ * rejected with GroupManagerDb.Error if there's no member with the given
+ * identity name in the database, or other database error.
+ */
+IndexedDbGroupManagerDb.prototype.getMemberSchedulePromise = function
+ (identity, useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.getMemberSchedulePromise is only supported for async")));
+
+ var thisManager = this;
+ return this.database.members.get(identity.toUri())
+ .then(function(membersEntry) {
+ if (!membersEntry)
+ throw new Error("The member identity name does not exist in the database");
+
+ return thisManager.database.schedules.get(membersEntry.scheduleId);
+ })
+ .then(function(schedulesEntry) {
+ if (!schedulesEntry)
+ throw new Error
+ ("The schedule ID for the member identity name does not exist in the database");
+
+ return Promise.resolve(schedulesEntry.scheduleName);
+ })
+ .catch(function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.getScheduleIdPromise_: Error: " + ex)));
+ });
+};
+
+/**
+ * Add a new member with the given key named keyName into a schedule named
+ * scheduleName. The member's identity name is keyName.getPrefix(-1).
+ * @param {string} scheduleName The schedule name.
+ * @param {Name} keyName The name of the key.
+ * @param {Blob} key A Blob of the public key DER.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the member is added, or that
+ * is rejected with GroupManagerDb.Error if there's no schedule named
+ * scheduleName, if the member's identity name already exists, or other database
+ * error.
+ */
+IndexedDbGroupManagerDb.prototype.addMemberPromise = function
+ (scheduleName, keyName, key, useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.addMemberPromise is only supported for async")));
+
+ var thisManager = this;
+ return this.getScheduleIdPromise_(scheduleName)
+ .then(function(scheduleId) {
+ if (scheduleId == -1)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.addMemberPromise: The schedule does not exist")));
+
+ // Needs to be changed in the future.
+ var memberName = keyName.getPrefix(-1);
+
+ // Add rejects if the primary key already exists.
+ return thisManager.database.members.add
+ ({ memberNameUri: memberName.toUri(),
+ memberName: memberName.wireEncode(TlvWireFormat.get()).buf(),
+ scheduleId: scheduleId,
+ keyName: keyName.wireEncode(TlvWireFormat.get()).buf(),
+ publicKey: key.buf() })
+ .catch(function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.addMemberPromise: Error: " + ex)));
+ });
+ });
+};
+
+/**
+ * Change the name of the schedule for the given member's identity name.
+ * @param {Name} identity The member's identity name.
+ * @param {string} scheduleName The new schedule name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the member is updated, or that
+ * is rejected with GroupManagerDb.Error if there's no member with the given
+ * identity name in the database, or there's no schedule named scheduleName, or
+ * other database error.
+ */
+IndexedDbGroupManagerDb.prototype.updateMemberSchedulePromise = function
+ (identity, scheduleName, useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.updateMemberSchedulePromise is only supported for async")));
+
+ var thisManager = this;
+ return this.getScheduleIdPromise_(scheduleName)
+ .then(function(scheduleId) {
+ if (scheduleId == -1)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.updateMemberSchedulePromise: The schedule does not exist")));
+
+ return thisManager.database.members.update
+ (identity.toUri(), { scheduleId: scheduleId })
+ .catch(function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.updateMemberSchedulePromise: Error: " + ex)));
+ });
+ });
+};
+
+/**
+ * Delete a member with the given identity name. If there is no member with
+ * the identity name, then do nothing.
+ * @param {Name} identity The member's identity name.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the member is deleted (or
+ * there is no such member), or that is rejected with GroupManagerDb.Error for a
+ * database error.
+ */
+IndexedDbGroupManagerDb.prototype.deleteMemberPromise = function
+ (identity, useSync)
+{
+ if (useSync)
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.deleteMemberPromise is only supported for async")));
+
+ return this.database.members.delete(identity.toUri())
+ .catch(function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.deleteMemberPromise: Error: " + ex)));
+ });
+};
+
+/**
+ * Get the ID for the schedule.
+ * @param {string} name The schedule name.
+ * @return {Promise} A promise that returns the ID (or -1 if not found), or that
+ * is rejected with GroupManagerDb.Error for a database error.
+ */
+IndexedDbGroupManagerDb.prototype.getScheduleIdPromise_ = function(name)
+{
+ // The scheduleName is not the primary key, so use 'where' instead of 'get'.
+ var id = -1;
+ return this.database.schedules.where("scheduleName").equals(name)
+ .each(function(entry) {
+ id = entry.scheduleId;
+ })
+ .then(function() {
+ return Promise.resolve(id);
+ })
+ .catch(function(ex) {
+ return Promise.reject(new GroupManagerDb.Error(new Error
+ ("IndexedDbGroupManagerDb.getScheduleIdPromise_: Error: " + ex)));
+ });
+};
+/**
+ * Copyright (C) 2015-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// Don't require modules since this is meant for the browser, not Node.js.
+
+/**
+ * IndexedDbProducerDb extends ProducerDb to implement storage of keys for the
+ * producer using the browser's IndexedDB service. It contains one table that
+ * maps time slots (to the nearest hour) to the content key created for that
+ * time slot.
+ * Create an IndexedDbProducerDb to use the given IndexedDB database name.
+ * @param {string} databaseName IndexedDB database name.
+ * @note This class is an experimental feature. The API may change.
+ * @constructor
+ */
+var IndexedDbProducerDb = function IndexedDbProducerDb(databaseName)
+{
+ ProducerDb.call(this);
+
+ this.database = new Dexie(databaseName);
+ this.database.version(1).stores({
+ // "timeSlot" is the hour-based time slot as hours since Jan 1, 1970 UTC. // number
+ // "key" is the encoded key // Uint8Array
+ contentKeys: "timeSlot"
+ });
+ this.database.open();
+};
+
+IndexedDbProducerDb.prototype = new ProducerDb();
+IndexedDbProducerDb.prototype.name = "IndexedDbProducerDb";
+
+/**
+ * Check if a content key exists for the hour covering timeSlot.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that returns true if there is a content key for
+ * timeSlot (else false), or that is rejected with ProducerDb.Error for a
+ * database error.
+ */
+IndexedDbProducerDb.prototype.hasContentKeyPromise = function(timeSlot, useSync)
+{
+ if (useSync)
+ return Promise.reject(new ProducerDb.Error(new Error
+ ("IndexedDbProducerDb.hasContentKeyPromise is only supported for async")));
+
+ var fixedTimeSlot = ProducerDb.getFixedTimeSlot(timeSlot);
+
+ return this.database.contentKeys.get(fixedTimeSlot)
+ .then(function(contentKeysEntry) {
+ return Promise.resolve(contentKeysEntry != undefined);
+ })
+ .catch(function(ex) {
+ return Promise.reject(new ProducerDb.Error(new Error
+ ("IndexedDbProducerDb.hasContentKeyPromise: Error: " + ex)));
+ });
+};
+
+/**
+ * Get the content key for the hour covering timeSlot.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that returns a Blob with the encoded key, or that
+ * is rejected with ProducerDb.Error if there is no key covering timeSlot, or
+ * other database error
+ */
+IndexedDbProducerDb.prototype.getContentKeyPromise = function(timeSlot, useSync)
+{
+ if (useSync)
+ return Promise.reject(new ProducerDb.Error(new Error
+ ("IndexedDbProducerDb.getContentKeyPromise is only supported for async")));
+
+ var fixedTimeSlot = ProducerDb.getFixedTimeSlot(timeSlot);
+
+ return this.database.contentKeys.get(fixedTimeSlot)
+ .then(function(contentKeysEntry) {
+ if (contentKeysEntry)
+ return Promise.resolve(new Blob(contentKeysEntry.key));
+ else
+ return Promise.reject(new ProducerDb.Error(new Error
+ ("IndexedDbProducerDb.getContentKeyPromise: Cannot get the key from the database")));
+ }, function(ex) {
+ return Promise.reject(new ProducerDb.Error(new Error
+ ("IndexedDbProducerDb.getContentKeyPromise: Error: " + ex)));
+ });
+};
+
+/**
+ * Add key as the content key for the hour covering timeSlot.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {Blob} key The encoded key.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the key is added, or that
+ * is rejected with ProducerDb.Error if a key for the same hour already exists
+ * in the database, or other database error.
+ */
+IndexedDbProducerDb.prototype.addContentKeyPromise = function
+ (timeSlot, key, useSync)
+{
+ if (useSync)
+ return Promise.reject(new ProducerDb.Error(new Error
+ ("IndexedDbProducerDb.addContentKeyPromise is only supported for async")));
+
+ var fixedTimeSlot = ProducerDb.getFixedTimeSlot(timeSlot);
+
+ // Add rejects if the primary key already exists.
+ return this.database.contentKeys.add
+ ({ timeSlot: fixedTimeSlot, key: key.buf() })
+ .catch(function(ex) {
+ return Promise.reject(new ProducerDb.Error(new Error
+ ("IndexedDbProducerDb.addContentKeyPromise: Error: " + ex)));
+ });
+};
+
+/**
+ * Delete the content key for the hour covering timeSlot. If there is no key for
+ * the time slot, do nothing.
+ * @param {number} timeSlot The time slot as milliseconds since Jan 1, 1970 UTC.
+ * @param {boolean} useSync (optional) If true then return a rejected promise
+ * since this only supports async code.
+ * @return {Promise} A promise that fulfills when the key is deleted (or there
+ * is no such key), or that is rejected with ProducerDb.Error for a database
+ * error.
+ */
+IndexedDbProducerDb.prototype.deleteContentKeyPromise = function(timeSlot, useSync)
+{
+ if (useSync)
+ return Promise.reject(new ProducerDb.Error(new Error
+ ("IndexedDbProducerDb.deleteContentKeyPromise is only supported for async")));
+
+ var fixedTimeSlot = ProducerDb.getFixedTimeSlot(timeSlot);
+
+ return this.database.contentKeys.delete(fixedTimeSlot)
+ .catch(function(ex) {
+ return Promise.reject(new ProducerDb.Error(new Error
+ ("IndexedDbProducerDb.deleteContentKeyPromise: Error: " + ex)));
+ });
+};
+/**
+ * This class represents the digest tree for chrono-sync2013.
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Zhehao Wang, based on Jeff T.'s implementation in ndn-cpp
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var DigestTree = require('./digest-tree.js').DigestTree; /** @ignore */
+var Interest = require('../interest.js').Interest; /** @ignore */
+var Data = require('../data.js').Data; /** @ignore */
+var Name = require('../name.js').Name; /** @ignore */
+var Blob = require('../util/blob.js').Blob; /** @ignore */
+var MemoryContentCache = require('../util/memory-content-cache.js').MemoryContentCache; /** @ignore */
+var SyncStateProto = require('./sync-state.js').SyncStateProto; /** @ignore */
+var NdnCommon = require('../util/ndn-common.js').NdnCommon;
+
+/**
+ * ChronoSync2013 implements the NDN ChronoSync protocol as described in the
+ * 2013 paper "Let's ChronoSync: Decentralized Dataset State Synchronization in
+ * Named Data Networking". http://named-data.net/publications/chronosync .
+ * @note The support for ChronoSync is experimental and the API is not finalized.
+ * See the API docs for more detail at
+ * http://named-data.net/doc/ndn-ccl-api/chrono-sync2013.html .
+ *
+ * Create a new ChronoSync2013 to communicate using the given face. Initialize
+ * the digest log with a digest of "00" and and empty content. Register the
+ * applicationBroadcastPrefix to receive interests for sync state messages and
+ * express an interest for the initial root digest "00".
+ * @param {function} onReceivedSyncState When ChronoSync receives a sync state message,
+ * this calls onReceivedSyncState(syncStates, isRecovery) where syncStates is the
+ * list of SyncState messages and isRecovery is true if this is the initial
+ * list of SyncState messages or from a recovery interest. (For example, if
+ * isRecovery is true, a chat application would not want to re-display all
+ * the associated chat messages.) The callback should send interests to fetch
+ * the application data for the sequence numbers in the sync state.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onInitialized This calls onInitialized() when the first sync data
+ * is received (or the interest times out because there are no other
+ * publishers yet).
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {Name} applicationDataPrefix The prefix used by this application instance
+ * for application data. For example, "/my/local/prefix/ndnchat4/0K4wChff2v".
+ * This is used when sending a sync message for a new sequence number.
+ * In the sync message, this uses applicationDataPrefix.toUri().
+ * @param {Name} applicationBroadcastPrefix The broadcast name prefix including the
+ * application name. For example, "/ndn/broadcast/ChronoChat-0.3/ndnchat1".
+ * This makes a copy of the name.
+ * @param {int} sessionNo The session number used with the applicationDataPrefix in
+ * sync state messages.
+ * @param {Face} face The Face for calling registerPrefix and expressInterest. The
+ * Face object must remain valid for the life of this ChronoSync2013 object.
+ * @param {KeyChain} keyChain To sign a data packet containing a sync state message, this
+ * calls keyChain.sign(data, certificateName).
+ * @param {Name} certificateName The certificate name of the key to use for signing a
+ * data packet containing a sync state message.
+ * @param {Milliseconds} syncLifetime The interest lifetime in milliseconds for sending
+ * sync interests.
+ * @param {function} onRegisterFailed If failed to register the prefix to receive
+ * interests for the applicationBroadcastPrefix, this calls
+ * onRegisterFailed(applicationBroadcastPrefix).
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @constructor
+ */
+var ChronoSync2013 = function ChronoSync2013
+ (onReceivedSyncState, onInitialized, applicationDataPrefix,
+ applicationBroadcastPrefix, sessionNo, face, keyChain, certificateName,
+ syncLifetime, onRegisterFailed)
+{
+ // assigning function pointers
+ this.onReceivedSyncState = onReceivedSyncState;
+ this.onInitialized = onInitialized;
+ this.applicationDataPrefixUri = applicationDataPrefix.toUri();
+ this.applicationBroadcastPrefix = applicationBroadcastPrefix;
+ this.session = sessionNo;
+ this.face = face;
+ this.keyChain = keyChain;
+ this.certificateName = certificateName;
+ this.sync_lifetime = syncLifetime;
+ this.usrseq = -1;
+
+ this.digest_tree = new DigestTree();
+ this.contentCache = new MemoryContentCache(face);
+
+ this.digest_log = new Array();
+ this.digest_log.push(new ChronoSync2013.DigestLogEntry("00",[]));
+
+ this.contentCache.registerPrefix
+ (this.applicationBroadcastPrefix, onRegisterFailed,
+ this.onInterest.bind(this));
+ this.enabled = true;
+
+ var interest = new Interest(this.applicationBroadcastPrefix);
+ interest.getName().append("00");
+
+ interest.setInterestLifetimeMilliseconds(1000);
+
+ var Sync;
+ try {
+ // Using protobuf.min.js in the browser.
+ Sync = dcodeIO.ProtoBuf.newBuilder().import(SyncStateProto).build("Sync");
+ }
+ catch (ex) {
+ // Using protobufjs in node.
+ Sync = require("protobufjs").newBuilder().import(SyncStateProto).build("Sync");
+ }
+ this.SyncStateMsg = Sync.SyncStateMsg;
+ this.SyncState = Sync.SyncState;
+
+ this.face.expressInterest(interest, this.onData.bind(this), this.initialTimeOut.bind(this));
+};
+
+exports.ChronoSync2013 = ChronoSync2013;
+
+ChronoSync2013.prototype.getProducerSequenceNo = function(dataPrefix, sessionNo)
+{
+ var index = this.digest_tree.find(dataPrefix, sessionNo);
+ if (index < 0)
+ return -1;
+ else
+ return this.digest_tree.get(index).getSequenceNo();
+};
+
+/**
+ * Increment the sequence number, create a sync message with the new sequence number,
+ * and publish a data packet where the name is applicationBroadcastPrefix + root
+ * digest of current digest tree. Then add the sync message to digest tree and digest
+ * log which creates a new root digest. Finally, express an interest for the next sync
+ * update with the name applicationBroadcastPrefix + the new root digest.
+ * After this, application should publish the content for the new sequence number.
+ * Get the new sequence number with getSequenceNo().
+ */
+ChronoSync2013.prototype.publishNextSequenceNo = function()
+{
+ this.usrseq ++;
+ var content = [new this.SyncState({ name:this.applicationDataPrefixUri,
+ type:'UPDATE',
+ seqno:{
+ seq:this.usrseq,
+ session:this.session
+ }
+ })];
+ var content_t = new this.SyncStateMsg({ss:content});
+ this.broadcastSyncState(this.digest_tree.getRoot(), content_t);
+
+ if (!this.update(content))
+ console.log("Warning: ChronoSync: update did not create a new digest log entry");
+
+ var interest = new Interest(this.applicationBroadcastPrefix);
+ interest.getName().append(this.digest_tree.getRoot());
+ interest.setInterestLifetimeMilliseconds(this.sync_lifetime);
+
+ this.face.expressInterest(interest, this.onData.bind(this), this.syncTimeout.bind(this));
+};
+
+/**
+ * Get the sequence number of the latest data published by this application instance.
+ * @return {int} the sequence number
+ */
+ChronoSync2013.prototype.getSequenceNo = function()
+{
+ return this.usrseq;
+};
+
+// DigestLogEntry class
+
+ChronoSync2013.DigestLogEntry = function ChronoSync2013DisgestLogEntry(digest, data)
+{
+ this.digest = digest;
+ this.data = data;
+};
+
+ChronoSync2013.DigestLogEntry.prototype.getDigest = function()
+{
+ return this.digest;
+};
+
+ChronoSync2013.DigestLogEntry.prototype.getData = function()
+{
+ return this.data;
+};
+
+/**
+ * Unregister callbacks so that this does not respond to interests anymore.
+ * If you will dispose this ChronoSync2013 object while your application is
+ * still running, you should call shutdown() first. After calling this, you
+ * should not call publishNextSequenceNo() again since the behavior will be
+ * undefined.
+ */
+ChronoSync2013.prototype.shutdown = function()
+{
+ this.enabled = false;
+ this.contentCache.unregisterAll();
+};
+
+// SyncState class
+/**
+ * A SyncState holds the values of a sync state message which is passed to the
+ * onReceivedSyncState callback which was given to the ChronoSyn2013
+ * constructor. Note: this has the same info as the Protobuf class
+ * Sync::SyncState, but we make a separate class so that we don't need the
+ * Protobuf definition in the ChronoSync API.
+ */
+ChronoSync2013.SyncState = function ChronoSync2013SyncState(dataPrefixUri, sessionNo, sequenceNo)
+{
+ this.dataPrefixUri_ = dataPrefixUri;
+ this.sessionNo_ = sessionNo;
+ this.sequenceNo_ = sequenceNo;
+};
+
+/**
+ * Get the application data prefix for this sync state message.
+ * @return The application data prefix as a Name URI string.
+ */
+ChronoSync2013.SyncState.prototype.getDataPrefix = function()
+{
+ return this.dataPrefixUri_;
+}
+
+/**
+ * Get the session number associated with the application data prefix for
+ * this sync state message.
+ * @return The session number.
+ */
+ChronoSync2013.SyncState.prototype.getSessionNo = function()
+{
+ return this.sessionNo_;
+}
+
+/**
+ * Get the sequence number for this sync state message.
+ * @return The sequence number.
+ */
+ChronoSync2013.SyncState.prototype.getSequenceNo = function()
+{
+ return this.sequenceNo_;
+}
+
+// Private methods for ChronoSync2013 class,
+/**
+ * Make a data packet with the syncMessage and with name applicationBroadcastPrefix_ + digest.
+ * Sign and send.
+ * @param {string} The root digest as a hex string for the data packet name.
+ * @param {SyncStateMsg} The syncMessage updates the digest tree state with the given digest.
+ */
+ChronoSync2013.prototype.broadcastSyncState = function(digest, syncMessage)
+{
+ var array = new Uint8Array(syncMessage.toArrayBuffer());
+ var data = new Data(this.applicationBroadcastPrefix);
+ data.getName().append(digest);
+ data.setContent(new Blob(array, false));
+ var thisChronoSync = this;
+ this.keyChain.sign(data, this.certificateName, function() {
+ thisChronoSync.contentCache.add(data);
+ });
+};
+
+/**
+ * Update the digest tree with the messages in content. If the digest tree root is not in
+ * the digest log, also add a log entry with the content.
+ * @param {SyncStates[]} The sync state messages
+ * @return {bool} True if added a digest log entry (because the updated digest tree root
+ * was not in the log), false if didn't add a log entry.
+ */
+ // Whatever's received by ondata, is pushed into digest log as its data directly
+ChronoSync2013.prototype.update = function(content)
+{
+ for (var i = 0; i < content.length; i++) {
+ if (content[i].type == 0) {
+ if (this.digest_tree.update(content[i].name, content[i].seqno.session, content[i].seqno.seq)) {
+ if (this.applicationDataPrefixUri == content[i].name)
+ this.usrseq = content[i].seqno.seq;
+ }
+ }
+ }
+
+ if (this.logfind(this.digest_tree.getRoot()) == -1) {
+ var newlog = new ChronoSync2013.DigestLogEntry(this.digest_tree.getRoot(), content);
+ this.digest_log.push(newlog);
+ return true;
+ }
+ else
+ return false;
+};
+
+ChronoSync2013.prototype.logfind = function(digest)
+{
+ for (var i = 0; i < this.digest_log.length; i++) {
+ if(digest == this.digest_log[i].digest)
+ return i;
+ }
+ return -1;
+};
+
+/**
+ * Process the sync interest from the applicationBroadcastPrefix. If we can't
+ * satisfy the interest, add it to the pending interest table in
+ * this.contentCache so that a future call to contentCacheAdd may satisfy it.
+ */
+ChronoSync2013.prototype.onInterest = function
+ (prefix, interest, face, interestFilterId, filter)
+{
+ if (!this.enabled)
+ // Ignore callbacks after the application calls shutdown().
+ return;
+
+ //search if the digest is already exist in the digest log
+
+ var syncdigest = interest.getName().get(this.applicationBroadcastPrefix.size()).toEscapedString();
+ if (interest.getName().size() == this.applicationBroadcastPrefix.size() + 2) {
+ syncdigest = interest.getName().get(this.applicationBroadcastPrefix.size() + 1).toEscapedString();
+ }
+ if (interest.getName().size() == this.applicationBroadcastPrefix.size() + 2 || syncdigest == "00") {
+ this.processRecoveryInst(interest, syncdigest, face);
+ }
+ else {
+ this.contentCache.storePendingInterest(interest, face);
+
+ if (syncdigest != this.digest_tree.getRoot()) {
+ var index = this.logfind(syncdigest);
+ var content = [];
+ if(index == -1) {
+ var self = this;
+ // Are we sure that using a "/local/timeout" interest is the best future call approach?
+ var timeout = new Interest(new Name("/local/timeout"));
+ timeout.setInterestLifetimeMilliseconds(2000);
+ this.face.expressInterest
+ (timeout, this.dummyOnData,
+ this.judgeRecovery.bind(this, timeout, syncdigest, face));
+ }
+ else {
+ //common interest processing
+ this.processSyncInst(index, syncdigest, face);
+ }
+ }
+ }
+};
+
+/**
+ * Process sync/recovery data.
+ * @param {Interest}
+ * @param {Data}
+ */
+ChronoSync2013.prototype.onData = function(interest, co)
+{
+ if (!this.enabled)
+ // Ignore callbacks after the application calls shutdown().
+ return;
+
+ var arr = new Uint8Array(co.getContent().size());
+ arr.set(co.getContent().buf());
+ var content_t = this.SyncStateMsg.decode(arr.buffer);
+ var content = content_t.ss;
+
+ var isRecovery = false;
+
+ if (this.digest_tree.getRoot() == "00") {
+ isRecovery = true;
+ this.initialOndata(content);
+ }
+ else {
+ this.update(content);
+ if (interest.getName().size() == this.applicationBroadcastPrefix.size() + 2)
+ // Assume this is a recovery interest.
+ isRecovery = true;
+ else
+ isRecovery = false;
+ }
+
+ var syncStates = [];
+
+ for (var i = 0; i < content.length; i++) {
+ if (content[i].type == 0) {
+ syncStates.push(new ChronoSync2013.SyncState
+ (content[i].name, content[i].seqno.session, content[i].seqno.seq));
+ }
+ }
+
+ // Instead of using Protobuf, use our own definition of SyncStates to pass to onReceivedSyncState.
+ try {
+ this.onReceivedSyncState(syncStates, isRecovery);
+ } catch (ex) {
+ console.log("Error in onReceivedSyncState: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+
+ var n = new Name(this.applicationBroadcastPrefix);
+ n.append(this.digest_tree.getRoot());
+
+ var interest = new Interest(n);
+ interest.setInterestLifetimeMilliseconds(this.sync_lifetime);
+
+ this.face.expressInterest(interest, this.onData.bind(this), this.syncTimeout.bind(this));
+};
+
+/**
+ * Interest variable not actually in use here
+ */
+ChronoSync2013.prototype.initialTimeOut = function(interest)
+{
+ if (!this.enabled)
+ // Ignore callbacks after the application calls shutdown().
+ return;
+
+ console.log("no other people");
+
+ this.usrseq++;
+ try {
+ this.onInitialized();
+ } catch (ex) {
+ console.log("Error in onInitialized: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ var content = [new this.SyncState({ name:this.applicationDataPrefixUri,
+ type:'UPDATE',
+ seqno: {
+ seq:this.usrseq,
+ session:this.session
+ }
+ })];
+ this.update(content);
+ var n = new Name(this.applicationBroadcastPrefix);
+ n.append(this.digest_tree.getRoot());
+ var retryInterest = new Interest(n);
+ retryInterest.setInterestLifetimeMilliseconds(this.sync_lifetime);
+
+ this.face.expressInterest(retryInterest, this.onData.bind(this), this.syncTimeout.bind(this));
+};
+
+ChronoSync2013.prototype.processRecoveryInst = function(interest, syncdigest, face)
+{
+ if (this.logfind(syncdigest) != -1) {
+ var content = [];
+
+ for(var i = 0; i < this.digest_tree.digestnode.length; i++) {
+ content[i] = new this.SyncState({ name:this.digest_tree.digestnode[i].getDataPrefix(),
+ type:'UPDATE',
+ seqno:{
+ seq:this.digest_tree.digestnode[i].getSequenceNo(),
+ session:this.digest_tree.digestnode[i].getSessionNo()
+ }
+ });
+ }
+
+ if (content.length != 0) {
+ var content_t = new this.SyncStateMsg({ss:content});
+ var str = new Uint8Array(content_t.toArrayBuffer());
+ var co = new Data(interest.getName());
+ co.setContent(new Blob(str, false));
+ if (interest.getName().get(-1).toEscapedString() == "00")
+ // Limit the lifetime of replies to interest for "00" since they can be different.
+ co.getMetaInfo().setFreshnessPeriod(1000);
+
+ this.keyChain.sign(co, this.certificateName, function() {
+ try {
+ face.putData(co);
+ } catch (e) {
+ console.log(e.toString());
+ }
+ });
+ }
+ }
+};
+
+/**
+ * Common interest processing, using digest log to find the difference after syncdigest_t
+ * @return True if sent a data packet to satisfy the interest.
+ */
+ChronoSync2013.prototype.processSyncInst = function(index, syncdigest_t, face)
+{
+ var content = [];
+ var data_name = [];
+ var data_seq = [];
+ var data_ses = [];
+
+ for (var j = index + 1; j < this.digest_log.length; j++) {
+ var temp = this.digest_log[j].getData();
+ for (var i = 0 ; i < temp.length ; i++) {
+ if (temp[i].type != 0) {
+ continue;
+ }
+ if (this.digest_tree.find(temp[i].name, temp[i].seqno.session) != -1) {
+ var n = data_name.indexOf(temp[i].name);
+ if (n == -1) {
+ data_name.push(temp[i].name);
+ data_seq.push(temp[i].seqno.seq);
+ data_ses.push(temp[i].seqno.session);
+ }
+ else {
+ data_seq[n] = temp[i].seqno.seq;
+ data_ses[n] = temp[i].seqno.session;
+ }
+ }
+ }
+ }
+
+ for(var i = 0; i < data_name.length; i++) {
+ content[i] = new this.SyncState({ name:data_name[i],
+ type:'UPDATE',
+ seqno: {
+ seq:data_seq[i],
+ session:data_ses[i]
+ }
+ });
+ }
+ if (content.length != 0) {
+ var content_t = new this.SyncStateMsg({ss:content});
+ var str = new Uint8Array(content_t.toArrayBuffer());
+ var n = new Name(this.prefix)
+ n.append(this.chatroom).append(syncdigest_t);
+
+ var co = new Data(n);
+ co.setContent(new Blob(str, false));
+ this.keyChain.sign(co, this.certificateName, function() {
+ try {
+ face.putData(co);
+ }
+ catch (e) {
+ console.log(e.toString());
+ }
+ });
+ }
+};
+
+/**
+ * Send recovery interset.
+ * @param {string} syncdigest_t
+ */
+ChronoSync2013.prototype.sendRecovery = function(syncdigest_t)
+{
+ var n = new Name(this.applicationBroadcastPrefix);
+ n.append("recovery").append(syncdigest_t);
+
+ var interest = new Interest(n);
+
+ interest.setInterestLifetimeMilliseconds(this.sync_lifetime);
+
+ this.face.expressInterest(interest, this.onData.bind(this), this.syncTimeout.bind(this));
+};
+
+/**
+ * This is called by onInterest after a timeout to check if a recovery is needed.
+ * This method has an interest argument because we use it as the onTimeout for
+ * Face.expressInterest.
+ * @param {Interest}
+ * @param {string}
+ * @param {Face}
+ */
+ChronoSync2013.prototype.judgeRecovery = function(interest, syncdigest_t, face)
+{
+ //console.log("*** judgeRecovery interest " + interest.getName().toUri() + " times out. Digest: " + syncdigest_t + " ***");
+ var index = this.logfind(syncdigest_t);
+ if (index != -1) {
+ if (syncdigest_t != this.digest_tree.root)
+ this.processSyncInst(index, syncdigest_t, face);
+ }
+ else
+ this.sendRecovery(syncdigest_t);
+};
+
+ChronoSync2013.prototype.syncTimeout = function(interest)
+{
+ if (!this.enabled)
+ // Ignore callbacks after the application calls shutdown().
+ return;
+
+ var component = interest.getName().get(4).toEscapedString();
+ if (component == this.digest_tree.root) {
+ var n = new Name(interest.getName());
+ var newInterest = new Interest(n);
+
+ interest.setInterestLifetimeMilliseconds(this.sync_lifetime);
+ this.face.expressInterest(newInterest, this.onData.bind(this), this.syncTimeout.bind(this));
+ }
+};
+
+ChronoSync2013.prototype.initialOndata = function(content)
+{
+ this.update(content);
+
+ var digest_t = this.digest_tree.getRoot();
+ for (var i = 0; i < content.length; i++) {
+ if (content[i].name == this.applicationDataPrefixUri && content[i].seqno.session == this.session) {
+ //if the user was an old comer, after add the static log he need to increase his seqno by 1
+ var content_t = [new this.SyncState({ name:this.applicationDataPrefixUri,
+ type:'UPDATE',
+ seqno: {
+ seq:content[i].seqno.seq + 1,
+ session:this.session
+ }
+ })];
+ if (this.update(content_t)) {
+ var newlog = new ChronoSync2013.DigestLogEntry(this.digest_tree.getRoot(), content_t);
+ this.digest_log.push(newlog);
+ try {
+ this.onInitialized();
+ } catch (ex) {
+ console.log("Error in onInitialized: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ }
+ }
+
+ var content_t;
+ if (this.usrseq >= 0) {
+ //send the data packet with new seqno back
+ content_t = new this.SyncState({ name:this.applicationDataPrefixUri,
+ type:'UPDATE',
+ seqno: {
+ seq:this.usrseq,
+ session:this.session
+ }
+ });
+ }
+ else
+ content_t = new this.SyncState({ name:this.applicationDataPrefixUri,
+ type:'UPDATE',
+ seqno: {
+ seq:0,
+ session:this.session
+ }
+ });
+ var content_tt = new this.SyncStateMsg({ss:content_t});
+ this.broadcastSyncState(digest_t, content_tt);
+
+ if (this.digest_tree.find(this.applicationDataPrefixUri, this.session) == -1) {
+ //the user haven't put himself in the digest tree
+ this.usrseq++;
+ var content = [new this.SyncState({ name:this.applicationDataPrefixUri,
+ type:'UPDATE',
+ seqno: {
+ seq:this.usrseq,
+ session:this.session
+ }
+ })];
+ if (this.update(content)) {
+ try {
+ this.onInitialized();
+ } catch (ex) {
+ console.log("Error in onInitialized: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ }
+};
+
+ChronoSync2013.prototype.dummyOnData = function(interest, data)
+{
+ console.log("*** dummyOnData called. ***");
+};/**
+ * This class represents the digest tree for chrono-sync2013.
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Zhehao Wang, based on Jeff T.'s implementation in ndn-cpp
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// Use capitalized Crypto to not clash with the browser's crypto.subtle.
+/** @ignore */
+var Crypto = require('../crypto.js');
+
+/**
+ * @constructor
+ */
+var DigestTree = function DigestTree()
+{
+ this.root = "00";
+ this.digestnode = [];
+};
+
+exports.DigestTree = DigestTree;
+
+// The meaning of a session is explained here:
+// http://named-data.net/doc/ndn-ccl-api/chrono-sync2013.html
+// DigestTree.Node works with seqno_seq and seqno_session, without protobuf definition,
+DigestTree.Node = function DigestTreeNode(dataPrefix, seqno_session, seqno_seq)
+{
+ // In this context, this should mean DigestTree.Node instead
+ this.dataPrefix = dataPrefix;
+ this.seqno_session = seqno_session;
+ this.seqno_seq = seqno_seq;
+
+ this.recomputeDigest();
+};
+
+DigestTree.Node.prototype.getDataPrefix = function()
+{
+ return this.dataPrefix;
+};
+
+DigestTree.Node.prototype.getSessionNo = function()
+{
+ return this.seqno_session;
+};
+
+DigestTree.Node.prototype.getSequenceNo = function()
+{
+ return this.seqno_seq;
+};
+
+DigestTree.Node.prototype.getDigest = function()
+{
+ return this.digest;
+};
+
+DigestTree.Node.prototype.setSequenceNo = function(sequenceNo)
+{
+ this.seqno_seq = sequenceNo;
+ this.recomputeDigest();
+};
+
+// Using Node.JS buffer, as documented here http://nodejs.org/api/buffer.html.
+DigestTree.Node.prototype.Int32ToBuffer = function(value) {
+ var result = new Buffer(4);
+ for (var i = 0; i < 4; i++) {
+ result[i] = value % 256;
+ value = Math.floor(value / 256);
+ }
+ return result;
+}
+
+DigestTree.Node.prototype.recomputeDigest = function()
+{
+ var seqHash = Crypto.createHash('sha256');
+
+ seqHash.update(this.Int32ToBuffer(this.seqno_session));
+ seqHash.update(this.Int32ToBuffer(this.seqno_seq));
+
+ var digest_seq = seqHash.digest();
+
+ var nameHash = Crypto.createHash('sha256');
+ nameHash.update(this.dataPrefix);
+ var digest_name = nameHash.digest();
+
+ var hash = Crypto.createHash('sha256');
+ hash.update(digest_name);
+ hash.update(digest_seq);
+
+ this.digest = hash.digest('hex');
+};
+
+// Do the work of string and then sequence number compare
+DigestTree.Node.Compare = function(node1, node2)
+{
+ if (node1.dataPrefix != node2.dataPrefix)
+ return node1.dataPrefix < node2.dataPrefix;
+ return node1.seqno_session < node2.seqno_session;
+};
+
+/**
+ * Update the digest tree and recompute the root digest. If the combination of dataPrefix
+ * and sessionNo already exists in the tree then update its sequenceNo (only if the given
+ * sequenceNo is newer), otherwise add a new node.
+ * @param {string} The name prefix.
+ * @param {int} sessionNo The session number.
+ * @param {int} sequenceNo The sequence number.
+ * @return True if the digest tree is updated, false if not
+ */
+DigestTree.prototype.update = function(dataPrefix, sessionNo, sequenceNo)
+{
+ var n_index = this.find(dataPrefix, sessionNo);
+ if (n_index >= 0) {
+ if (this.digestnode[n_index].getSequenceNo() < sequenceNo)
+ this.digestnode[n_index].setSequenceNo(sequenceNo);
+ else
+ return false;
+ }
+ else {
+ var temp = new DigestTree.Node(dataPrefix, sessionNo, sequenceNo);
+ this.digestnode.push(temp);
+ this.digestnode.sort(this.sortNodes);
+ }
+ this.recomputeRoot();
+ return true;
+};
+
+// Need to confirm this sort works with the insertion in ndn-cpp.
+DigestTree.prototype.sortNodes = function()
+{
+ var temp;
+ for (var i = this.digestnode.length; i > 0; i--) {
+ for (var j = 0; j < i - 1; j++) {
+ if (this.digestnode[j].getDataPrefix() > this.digestnode[j + 1].getDataPrefix()) {
+ temp = this.digestnode[j];
+ this.digestnode[j] = this.digestnode[j + 1];
+ this.digestnode[j + 1] = temp;
+ }
+ }
+ }
+};
+
+DigestTree.prototype.sortNodes = function (node1, node2)
+{
+ if (node1.getDataPrefix() == node2.getDataPrefix() &&
+ node1.getSessionNo() == node2.getSessionNo())
+ return 0;
+
+ if ((node1.getDataPrefix() > node2.getDataPrefix()) ||
+ ((node1.getDataPrefix() == node2.getDataPrefix()) &&
+ (node1.getSessionNo() >node2.getSessionNo())))
+ return 1;
+ else
+ return -1;
+}
+
+DigestTree.prototype.find = function(dataPrefix, sessionNo)
+{
+ for (var i = 0; i < this.digestnode.length; ++i) {
+ if (this.digestnode[i].getDataPrefix() == dataPrefix &&
+ this.digestnode[i].getSessionNo() == sessionNo)
+ return i;
+ }
+ return -1;
+};
+
+DigestTree.prototype.size = function()
+{
+ return this.digestnode.size();
+};
+
+// Not really used
+DigestTree.prototype.get = function(i)
+{
+ return this.digestnode[i];
+};
+
+DigestTree.prototype.getRoot = function()
+{
+ return this.root;
+};
+
+DigestTree.prototype.recomputeRoot = function()
+{
+ var md = Crypto.createHash('sha256');
+ // The result of updateHex is related with the sequence of participants,
+ // I don't think that should be the case.
+ for (var i = 0; i < this.digestnode.length; i++) {
+ md.update(new Buffer(this.digestnode[i].digest, 'hex'));
+ }
+ this.root = md.digest('hex');
+};
+// Just define the SyncStateProto object. We do a Protobuf import dynamically
+// when we need it so that protobufjs is optional.
+var SyncStateProto = {
+ "package": "Sync",
+ "messages": [
+ {
+ "name": "SyncState",
+ "fields": [
+ {
+ "rule": "required",
+ "type": "string",
+ "name": "name",
+ "id": 1,
+ "options": {}
+ },
+ {
+ "rule": "required",
+ "type": "ActionType",
+ "name": "type",
+ "id": 2,
+ "options": {}
+ },
+ {
+ "rule": "optional",
+ "type": "SeqNo",
+ "name": "seqno",
+ "id": 3,
+ "options": {}
+ }
+ ],
+ "enums": [
+ {
+ "name": "ActionType",
+ "values": [
+ {
+ "name": "UPDATE",
+ "id": 0
+ },
+ {
+ "name": "DELETE",
+ "id": 1
+ },
+ {
+ "name": "OTHER",
+ "id": 2
+ }
+ ],
+ "options": {}
+ }
+ ],
+ "messages": [
+ {
+ "name": "SeqNo",
+ "fields": [
+ {
+ "rule": "required",
+ "type": "uint32",
+ "name": "seq",
+ "id": 1,
+ "options": {}
+ },
+ {
+ "rule": "required",
+ "type": "uint32",
+ "name": "session",
+ "id": 2,
+ "options": {}
+ }
+ ],
+ "enums": [],
+ "messages": [],
+ "options": {}
+ }
+ ],
+ "options": {}
+ },
+ {
+ "name": "SyncStateMsg",
+ "fields": [
+ {
+ "rule": "repeated",
+ "type": "SyncState",
+ "name": "ss",
+ "id": 1,
+ "options": {}
+ }
+ ],
+ "enums": [],
+ "messages": [],
+ "options": {}
+ }
+ ],
+ "enums": [],
+ "imports": [],
+ "options": {}
+};
+
+exports.SyncStateProto = SyncStateProto;
+/**
+ * Copyright (C) 2014-2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+// Use capitalized Crypto to not clash with the browser's crypto.subtle.
+/** @ignore */
+var Crypto = require('../crypto.js'); /** @ignore */
+var WireFormat = require('../encoding/wire-format.js').WireFormat; /** @ignore */
+var TlvEncoder = require('../encoding/tlv/tlv-encoder.js').TlvEncoder; /** @ignore */
+var Blob = require('./blob.js').Blob;
+
+/**
+ * A CommandInterestGenerator keeps track of a timestamp and generates command
+ * interests according to the NFD Signed Command Interests protocol:
+ * http://redmine.named-data.net/projects/nfd/wiki/Command_Interests
+ *
+ * Create a new CommandInterestGenerator and initialize the timestamp to now.
+ * @constructor
+ */
+var CommandInterestGenerator = function CommandInterestGenerator()
+{
+ this.lastTimestamp = Math.round(new Date().getTime());
+};
+
+exports.CommandInterestGenerator = CommandInterestGenerator;
+
+/**
+ * Append a timestamp component and a random value component to interest's name.
+ * This ensures that the timestamp is greater than the timestamp used in the
+ * previous call. Then use keyChain to sign the interest which appends a
+ * SignatureInfo component and a component with the signature bits. If the
+ * interest lifetime is not set, this sets it.
+ * @param {Interest} interest The interest whose name is append with components.
+ * @param {KeyChain} keyChain The KeyChain for calling sign.
+ * @param {Name} certificateName The certificate name of the key to use for
+ * signing.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the SignatureInfo and to encode interest name for signing. If omitted, use
+ * WireFormat.getDefaultWireFormat().
+ * @param {function} onComplete (optional) This calls onComplete() when complete.
+ * (Some crypto/database libraries only use a callback, so onComplete is
+ * required to use these.)
+ */
+CommandInterestGenerator.prototype.generate = function
+ (interest, keyChain, certificateName, wireFormat, onComplete)
+{
+ onComplete = (typeof wireFormat === "function") ? wireFormat : onComplete;
+ wireFormat = (typeof wireFormat === "function" || !wireFormat) ?
+ WireFormat.getDefaultWireFormat() : wireFormat;
+
+ var timestamp = Math.round(new Date().getTime());
+ while (timestamp <= this.lastTimestamp)
+ timestamp += 1.0;
+
+ // The timestamp is encoded as a TLV nonNegativeInteger.
+ var encoder = new TlvEncoder(8);
+ encoder.writeNonNegativeInteger(timestamp);
+ interest.getName().append(new Blob(encoder.getOutput(), false));
+
+ // The random value is a TLV nonNegativeInteger too, but we know it is 8
+ // bytes, so we don't need to call the nonNegativeInteger encoder.
+ interest.getName().append(new Blob(Crypto.randomBytes(8), false));
+
+ // Update the timestamp before calling async sign.
+ this.lastTimestamp = timestamp;
+
+ keyChain.sign(interest, certificateName, wireFormat, function() {
+ if (interest.getInterestLifetimeMilliseconds() == null ||
+ interest.getInterestLifetimeMilliseconds() < 0)
+ // The caller has not set the interest lifetime, so set it here.
+ interest.setInterestLifetimeMilliseconds(1000.0);
+
+ if (onComplete)
+ onComplete();
+ });
+};
+/**
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var LOG = require('../log.js').Log.LOG;
+
+/**
+ * An InterestFilterTable is an internal class to hold a list of entries with
+ * an interest Filter and its OnInterestCallback.
+ * @constructor
+ */
+var InterestFilterTable = function InterestFilterTable()
+{
+ this.table_ = []; // of Entry
+};
+
+exports.InterestFilterTable = InterestFilterTable;
+
+/**
+ * InterestFilterTable.Entry holds an interestFilterId, an InterestFilter and
+ * the OnInterestCallback with its related Face.
+ * Create a new Entry with the given values.
+ * @param {number} interestFilterId The ID from getNextEntryId().
+ * @param {InterestFilter} filter The InterestFilter for this entry.
+ * @param {function} onInterest The callback to call.
+ * @param {Face} face The face on which was called registerPrefix or
+ * setInterestFilter which is passed to the onInterest callback.
+ * @constructor
+ */
+InterestFilterTable.Entry = function InterestFilterTableEntry
+ (interestFilterId, filter, onInterest, face)
+{
+ this.interestFilterId_ = interestFilterId;
+ this.filter_ = filter;
+ this.onInterest_ = onInterest;
+ this.face_ = face;
+};
+
+/**
+ * Get the interestFilterId given to the constructor.
+ * @return {number} The interestFilterId.
+ */
+InterestFilterTable.Entry.prototype.getInterestFilterId = function()
+{
+ return this.interestFilterId_;
+};
+
+/**
+ * Get the InterestFilter given to the constructor.
+ * @return {InterestFilter} The InterestFilter.
+ */
+InterestFilterTable.Entry.prototype.getFilter = function()
+{
+ return this.filter_;
+};
+
+/**
+ * Get the onInterest callback given to the constructor.
+ * @return {function} The onInterest callback.
+ */
+InterestFilterTable.Entry.prototype.getOnInterest = function()
+{
+ return this.onInterest_;
+};
+
+/**
+ * Get the Face given to the constructor.
+ * @return {Face} The Face.
+ */
+InterestFilterTable.Entry.prototype.getFace = function()
+{
+ return this.face_;
+};
+
+/**
+ * Add a new entry to the table.
+ * @param {number} interestFilterId The ID from Node.getNextEntryId().
+ * @param {InterestFilter} filter The InterestFilter for this entry.
+ * @param {function} onInterest The callback to call.
+ * @param {Face} face The face on which was called registerPrefix or
+ * setInterestFilter which is passed to the onInterest callback.
+ */
+InterestFilterTable.prototype.setInterestFilter = function
+ (interestFilterId, filter, onInterest, face)
+{
+ this.table_.push(new InterestFilterTable.Entry
+ (interestFilterId, filter, onInterest, face));
+};
+
+/**
+ * Find all entries from the interest filter table where the interest conforms
+ * to the entry's filter, and add to the matchedFilters list.
+ * @param {Interest} interest The interest which may match the filter in
+ * multiple entries.
+ * @param {Array<InterestFilterTable.Entry>} matchedFilters Add each matching
+ * InterestFilterTable.Entry from the interest filter table. The caller should
+ * pass in an empty array.
+ */
+InterestFilterTable.prototype.getMatchedFilters = function
+ (interest, matchedFilters)
+{
+ for (var i = 0; i < this.table_.length; ++i) {
+ var entry = this.table_[i];
+ if (entry.getFilter().doesMatch(interest.getName()))
+ matchedFilters.push(entry);
+ }
+};
+
+/**
+ * Remove the interest filter entry which has the interestFilterId from the
+ * interest filter table. This does not affect another interest filter with a
+ * different interestFilterId, even if it has the same prefix name. If there is
+ * no entry with the interestFilterId, do nothing.
+ * @param {number} interestFilterId The ID returned from setInterestFilter.
+ */
+InterestFilterTable.prototype.unsetInterestFilter = function(interestFilterId)
+{
+ // Go backwards through the list so we can erase entries.
+ // Remove all entries even though interestFilterId should be unique.
+ var count = 0;
+ for (var i = this.table_.length - 1; i >= 0; --i) {
+ if (this.table_[i].getInterestFilterId() == interestFilterId) {
+ ++count;
+ this.table_.splice(i, 1);
+ }
+ }
+
+ if (count === 0)
+ if (LOG > 0) console.log
+ ("unsetInterestFilter: Didn't find interestFilterId " + interestFilterId);
+};
+/**
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var NdnCommon = require('../util/ndn-common.js').NdnCommon; /** @ignore */
+var LOG = require('../log.js').Log.LOG;
+
+/**
+ * A PendingInterestTable is an internal class to hold a list of pending
+ * interests with their callbacks.
+ * @constructor
+ */
+var PendingInterestTable = function PendingInterestTable()
+{
+ this.table_ = []; // of Entry
+ this.removeRequests_ = []; // of number
+};
+
+exports.PendingInterestTable = PendingInterestTable;
+
+/**
+ * PendingInterestTable.Entry holds the callbacks and other fields for an entry
+ * in the pending interest table.
+ * Create a new Entry with the given fields. Note: You should not call this
+ * directly but call PendingInterestTable.add.
+ * @constructor
+ */
+PendingInterestTable.Entry = function PendingInterestTableEntry
+ (pendingInterestId, interest, onData, onTimeout, onNetworkNack)
+{
+ this.pendingInterestId_ = pendingInterestId;
+ this.interest_ = interest;
+ this.onData_ = onData;
+ this.onTimeout_ = onTimeout;
+ this.onNetworkNack_ = onNetworkNack;
+ this.timerId_ = -1;
+};
+
+/**
+ * Get the pendingInterestId given to the constructor.
+ * @return {number} The pendingInterestId.
+ */
+PendingInterestTable.Entry.prototype.getPendingInterestId = function()
+{
+ return this.pendingInterestId_;
+};
+
+/**
+ * Get the interest given to the constructor (from Face.expressInterest).
+ * @return {Interest} The interest. NOTE: You must not change the interest
+ * object - if you need to change it then make a copy.
+ */
+PendingInterestTable.Entry.prototype.getInterest = function()
+{
+ return this.interest_;
+};
+
+/**
+ * Get the OnData callback given to the constructor.
+ * @return {function} The OnData callback.
+ */
+PendingInterestTable.Entry.prototype.getOnData = function()
+{
+ return this.onData_;
+};
+
+/**
+ * Get the OnNetworkNack callback given to the constructor.
+ * @return {function} The OnNetworkNack callback.
+ */
+PendingInterestTable.Entry.prototype.getOnNetworkNack = function()
+{
+ return this.onNetworkNack_;
+};
+
+/**
+* Call onTimeout_ (if defined). This ignores exceptions from the call to
+* onTimeout_.
+*/
+PendingInterestTable.Entry.prototype.callTimeout = function()
+{
+ if (this.onTimeout_) {
+ try {
+ this.onTimeout_(this.interest_);
+ } catch (ex) {
+ console.log("Error in onTimeout: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+};
+
+/**
+ * Call setTimeout(callback, milliseconds) and remember the timer ID. If the
+ * timer ID has already been set on a prevous call, do nothing.
+ */
+PendingInterestTable.Entry.prototype.setTimeout = function(callback, milliseconds)
+{
+ if (this.timerId_ !== -1)
+ // Already set a timeout.
+ return;
+ this.timerId_ = setTimeout(callback, milliseconds);
+};
+
+/**
+ * Clear the timeout timer and reset the timer ID.
+ */
+PendingInterestTable.Entry.prototype.clearTimeout = function()
+{
+ if (this.timerId_ !== -1) {
+ clearTimeout(this.timerId_);
+ this.timerId_ = -1;
+ }
+};
+
+/**
+ * Add a new entry to the pending interest table. Also set a timer to call the
+ * timeout. However, if removePendingInterest was already called with the
+ * pendingInterestId, don't add an entry and return null.
+ * @param {number} pendingInterestId
+ * @param {Interest} interestCopy
+ * @param {function} onData
+ * @param {function} onTimeout
+ * @param {function} onNetworkNack
+ * @return {PendingInterestTable.Entry} The new PendingInterestTable.Entry, or
+ * null if removePendingInterest was already called with the pendingInterestId.
+ */
+PendingInterestTable.prototype.add = function
+ (pendingInterestId, interestCopy, onData, onTimeout, onNetworkNack)
+{
+ var removeRequestIndex = this.removeRequests_.indexOf(pendingInterestId);
+ if (removeRequestIndex >= 0) {
+ // removePendingInterest was called with the pendingInterestId returned by
+ // expressInterest before we got here, so don't add a PIT entry.
+ this.removeRequests_.splice(removeRequestIndex, 1);
+ return null;
+ }
+
+ var entry = new PendingInterestTable.Entry
+ (pendingInterestId, interestCopy, onData, onTimeout, onNetworkNack);
+ this.table_.push(entry);
+
+ // Set interest timer.
+ var timeoutMilliseconds = (interestCopy.getInterestLifetimeMilliseconds() || 4000);
+ var thisTable = this;
+ var timeoutCallback = function() {
+ if (LOG > 1) console.log("Interest time out: " + interestCopy.getName().toUri());
+
+ // Remove the entry from the table.
+ var index = thisTable.table_.indexOf(entry);
+ if (index >= 0)
+ thisTable.table_.splice(index, 1);
+
+ entry.callTimeout();
+ };
+
+ entry.setTimeout(timeoutCallback, timeoutMilliseconds);
+ return entry;
+};
+
+/**
+ * Find all entries from the pending interest table where data conforms to
+ * the entry's interest selectors, remove the entries from the table, and add to
+ * the entries list.
+ * @param {Data} data The incoming Data packet to find the interest for.
+ * @param {Array<PendingInterestTable.Entry>} entries Add matching
+ * PendingInterestTable.Entry from the pending interest table. The caller should
+ * pass in an empty array.
+ */
+PendingInterestTable.prototype.extractEntriesForExpressedInterest = function
+ (data, entries)
+{
+ // Go backwards through the list so we can erase entries.
+ for (var i = this.table_.length - 1; i >= 0; --i) {
+ var pendingInterest = this.table_[i];
+ if (pendingInterest.getInterest().matchesData(data)) {
+ pendingInterest.clearTimeout();
+ entries.push(pendingInterest);
+ this.table_.splice(i, 1);
+ }
+ }
+};
+
+/**
+ * Find all entries from the pending interest table where the OnNetworkNack
+ * callback is not null and the entry's interest is the same as the given
+ * interest, remove the entries from the table, and add to the entries list.
+ * (We don't remove the entry if the OnNetworkNack callback is null so that
+ * OnTimeout will be called later.) The interests are the same if their default
+ * wire encoding is the same (which has everything including the name, nonce,
+ * link object and selectors).
+ * @param {Interest} interest The Interest to search for (typically from a Nack
+ * packet).
+ * @param {Array<PendingInterestTable.Entry>} entries Add matching
+ * PendingInterestTable.Entry from the pending interest table. The caller should
+ * pass in an empty array.
+ */
+PendingInterestTable.prototype.extractEntriesForNackInterest = function
+ (interest, entries)
+{
+ var encoding = interest.wireEncode();
+
+ // Go backwards through the list so we can erase entries.
+ for (var i = this.table_.length - 1; i >= 0; --i) {
+ var pendingInterest = this.table_[i];
+ if (pendingInterest.getOnNetworkNack() == null)
+ continue;
+
+ // wireEncode returns the encoding cached when the interest was sent (if
+ // it was the default wire encoding).
+ if (pendingInterest.getInterest().wireEncode().equals(encoding)) {
+ pendingInterest.clearTimeout();
+ entries.push(pendingInterest);
+ this.table_.splice(i, 1);
+ }
+ }
+};
+
+/**
+ * Remove the pending interest entry with the pendingInterestId from the pending
+ * interest table. This does not affect another pending interest with a
+ * different pendingInterestId, even if it has the same interest name.
+ * If there is no entry with the pendingInterestId, do nothing.
+ * @param {number} pendingInterestId The ID returned from expressInterest.
+ */
+PendingInterestTable.prototype.removePendingInterest = function
+ (pendingInterestId)
+{
+ if (pendingInterestId == null)
+ return;
+
+ // Go backwards through the list so we can erase entries.
+ // Remove all entries even though pendingInterestId should be unique.
+ var count = 0;
+ for (var i = this.table_.length - 1; i >= 0; --i) {
+ var entry = this.table_[i];
+ if (entry.getPendingInterestId() == pendingInterestId) {
+ entry.clearTimeout();
+ this.table_.splice(i, 1);
+ ++count;
+ }
+ }
+
+ if (count === 0)
+ if (LOG > 0) console.log
+ ("removePendingInterest: Didn't find pendingInterestId " + pendingInterestId);
+
+ if (count === 0) {
+ // The pendingInterestId was not found. Perhaps this has been called before
+ // the callback in expressInterest can add to the PIT. Add this
+ // removal request which will be checked before adding to the PIT.
+ if (this.removeRequests_.indexOf(pendingInterestId) < 0)
+ // Not already requested, so add the request.
+ this.removeRequests_.push(pendingInterestId);
+ }
+};
+/**
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var LOG = require('../log.js').Log.LOG;
+
+/**
+ * A RegisteredPrefixTable is an internal class to hold a list of registered
+ * prefixes with information necessary to remove the registration later.
+ * @param {InterestFilterTable} interestFilterTable See removeRegisteredPrefix(),
+ * which may call interestFilterTable.unsetInterestFilter().
+ * @constructor
+ */
+var RegisteredPrefixTable = function RegisteredPrefixTable(interestFilterTable)
+{
+ this.interestFilterTable_ = interestFilterTable;
+ this.table_ = []; // of Entry
+ this.removeRequests_ = []; // of number
+};
+
+exports.RegisteredPrefixTable = RegisteredPrefixTable;
+
+/**
+ * Add a new entry to the table. However, if removeRegisteredPrefix was already
+ * called with the registeredPrefixId, don't add an entry and return false.
+ * @param {number} registeredPrefixId The ID from Node.getNextEntryId().
+ * @param {Name} prefix The name prefix.
+ * @param {number} relatedInterestFilterId (optional) The related
+ * interestFilterId for the filter set in the same registerPrefix operation. If
+ * omitted, set to 0.
+ * return {boolean} True if added an entry, false if removeRegisteredPrefix was
+ * already called with the registeredPrefixId.
+ */
+RegisteredPrefixTable.prototype.add = function
+ (registeredPrefixId, prefix, relatedInterestFilterId)
+{
+ var removeRequestIndex = this.removeRequests_.indexOf(registeredPrefixId);
+ if (removeRequestIndex >= 0) {
+ // removeRegisteredPrefix was called with the registeredPrefixId returned by
+ // registerPrefix before we got here, so don't add a registered prefix
+ // table entry.
+ this.removeRequests_.splice(removeRequestIndex, 1);
+ return false;
+ }
+
+ this.table_.push(new RegisteredPrefixTable._Entry
+ (registeredPrefixId, prefix, relatedInterestFilterId));
+ return true;
+};
+
+/**
+ * Remove the registered prefix entry with the registeredPrefixId from the
+ * registered prefix table. This does not affect another registered prefix with
+ * a different registeredPrefixId, even if it has the same prefix name. If an
+ * interest filter was automatically created by registerPrefix, also call
+ * interestFilterTable_.unsetInterestFilter to remove it.
+ * If there is no entry with the registeredPrefixId, do nothing.
+ * @param {number} registeredPrefixId The ID returned from registerPrefix.
+ */
+RegisteredPrefixTable.prototype.removeRegisteredPrefix = function
+ (registeredPrefixId)
+{
+ // Go backwards through the list so we can erase entries.
+ // Remove all entries even though registeredPrefixId should be unique.
+ var count = 0;
+ for (var i = this.table_.length - 1; i >= 0; --i) {
+ var entry = this.table_[i];
+ if (entry.getRegisteredPrefixId() == registeredPrefixId) {
+ ++count;
+
+ if (entry.getRelatedInterestFilterId() > 0)
+ // Remove the related interest filter.
+ this.interestFilterTable_.unsetInterestFilter
+ (entry.getRelatedInterestFilterId());
+
+ this.table_.splice(i, 1);
+ }
+ }
+
+ if (count === 0)
+ if (LOG > 0) console.log
+ ("removeRegisteredPrefix: Didn't find registeredPrefixId " + registeredPrefixId);
+
+ if (count === 0) {
+ // The registeredPrefixId was not found. Perhaps this has been called before
+ // the callback in registerPrefix can add to the registered prefix table.
+ // Add this removal request which will be checked before adding to the
+ // registered prefix table.
+ if (this.removeRequests_.indexOf(registeredPrefixId) < 0)
+ // Not already requested, so add the request.
+ this.removeRequests_.push(registeredPrefixId);
+ }
+};
+
+/**
+ * RegisteredPrefixTable._Entry holds a registeredPrefixId and information
+ * necessary to remove the registration later. It optionally holds a related
+ * interestFilterId if the InterestFilter was set in the same registerPrefix
+ * operation.
+ * Create a RegisteredPrefixTable.Entry with the given values.
+ * @param {number} registeredPrefixId The ID from Node.getNextEntryId().
+ * @param {Name} prefix The name prefix.
+ * @param {number} relatedInterestFilterId (optional) The related
+ * interestFilterId for the filter set in the same registerPrefix operation. If
+ * omitted, set to 0.
+ * @constructor
+ */
+RegisteredPrefixTable._Entry = function RegisteredPrefixTableEntry
+ (registeredPrefixId, prefix, relatedInterestFilterId)
+{
+ this.registeredPrefixId_ = registeredPrefixId;
+ this.prefix_ = prefix;
+ this.relatedInterestFilterId_ = relatedInterestFilterId;
+};
+
+/**
+ * Get the registeredPrefixId given to the constructor.
+ * @return {number} The registeredPrefixId.
+ */
+RegisteredPrefixTable._Entry.prototype.getRegisteredPrefixId = function()
+{
+ return this.registeredPrefixId;
+};
+
+/**
+ * Get the name prefix given to the constructor.
+ * @return {Name} The name prefix.
+ */
+RegisteredPrefixTable._Entry.prototype.getPrefix = function()
+{
+ return this.prefix;
+};
+
+/**
+ * Get the related interestFilterId given to the constructor.
+ * @return {number} The related interestFilterId.
+ */
+RegisteredPrefixTable._Entry.prototype.getRelatedInterestFilterId = function()
+{
+ return this.relatedInterestFilterId;
+};
+/**
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-cxx fields.hpp https://github.com/named-data/ndn-cxx/blob/master/src/lp/fields.hpp
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/**
+ * IncomingFaceId represents the incoming face ID header field in an NDNLPv2 packet.
+ * http://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
+ * @constructor
+ */
+var IncomingFaceId = function IncomingFaceId()
+{
+ this.faceId_ = null;
+};
+
+exports.IncomingFaceId = IncomingFaceId;
+
+/**
+ * Get the incoming face ID value.
+ * @return {number} The face ID value.
+ */
+IncomingFaceId.prototype.getFaceId = function() { return this.faceId_; };
+
+/**
+ * Set the face ID value.
+ * @param {number} faceId The incoming face ID value.
+ */
+IncomingFaceId.prototype.setFaceId = function(faceId)
+{
+ this.faceId_ = faceId;
+};
+
+/**
+ * Get the first header field in lpPacket which is an IncomingFaceId. This is
+ * an internal method which the application normally would not use.
+ * @param {LpPacket} lpPacket The LpPacket with the header fields to search.
+ * @return {IncomingFaceId} The first IncomingFaceId header field, or null if
+ * not found.
+ */
+IncomingFaceId.getFirstHeader = function(lpPacket)
+{
+ for (var i = 0; i < lpPacket.countHeaderFields(); ++i) {
+ var field = lpPacket.getHeaderField(i);
+ if (field instanceof IncomingFaceId)
+ return field;
+ }
+
+ return null;
+};
+/**
+ * Copyright (C) 2016 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * @author: From ndn-cxx packet.hpp https://github.com/named-data/ndn-cxx/blob/master/src/lp/packet.hpp
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var Blob = require('../util/blob.js').Blob;
+
+/**
+ * An LpPacket represents an NDNLPv2 packet including header fields an an
+ * optional fragment. This is an internal class which the application normally
+ * would not use.
+ * http://redmine.named-data.net/projects/nfd/wiki/NDNLPv2
+ * @constructor
+ */
+var LpPacket = function LpPacket()
+{
+ this.headerFields_ = [];
+ this.fragmentWireEncoding_ = new Blob();
+};
+
+exports.LpPacket = LpPacket;
+
+/**
+ * Get the fragment wire encoding.
+ * @return {Blob} The wire encoding, or an isNull Blob if not specified.
+ */
+LpPacket.prototype.getFragmentWireEncoding = function()
+{
+ return this.fragmentWireEncoding_;
+};
+
+/**
+ * Get the number of header fields. This does not include the fragment.
+ * @return {number} The number of header fields.
+ */
+LpPacket.prototype.countHeaderFields = function()
+{
+ return this.headerFields_.length;
+};
+
+/**
+ * Get the header field at the given index.
+ * @param {number} index The index, starting from 0. It is an error if index is
+ * greater to or equal to countHeaderFields().
+ * @return {object} The header field at the index.
+ */
+LpPacket.prototype.getHeaderField = function(index)
+{
+ return this.headerFields_[index];
+};
+
+/**
+ * Remove all header fields and set the fragment to an isNull Blob.
+ */
+LpPacket.prototype.clear = function()
+{
+ this.headerFields_ = [];
+ this.fragmentWireEncoding_ = new Blob();
+};
+
+/**
+ * Set the fragment wire encoding.
+ * @param {Blob} fragmentWireEncoding The fragment wire encoding or an isNull
+ * Blob if not specified.
+ */
+LpPacket.prototype.setFragmentWireEncoding = function(fragmentWireEncoding)
+{
+ this.fragmentWireEncoding_ =
+ typeof fragmentWireEncoding === 'object' && fragmentWireEncoding instanceof Blob ?
+ fragmentWireEncoding : new Blob(fragmentWireEncoding);
+};
+
+/**
+ * Add a header field. To add the fragment, use setFragmentWireEncoding().
+ * @param {object} headerField The header field to add.
+ */
+LpPacket.prototype.addHeaderField = function(headerField)
+{
+ this.headerFields_.push(headerField);
+};
+/**
+ * This class represents the top-level object for communicating with an NDN host.
+ * Copyright (C) 2013-2016 Regents of the University of California.
+ * @author: Meki Cherkaoui, Jeff Thompson <jefft0@remap.ucla.edu>, Wentao Shang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * A copy of the GNU Lesser General Public License is in the file COPYING.
+ */
+
+/** @ignore */
+var DataUtils = require('./encoding/data-utils.js').DataUtils; /** @ignore */
+var Name = require('./name.js').Name; /** @ignore */
+var Interest = require('./interest.js').Interest; /** @ignore */
+var Data = require('./data.js').Data; /** @ignore */
+var ControlParameters = require('./control-parameters.js').ControlParameters; /** @ignore */
+var ControlResponse = require('./control-response.js').ControlResponse; /** @ignore */
+var InterestFilter = require('./interest-filter.js').InterestFilter; /** @ignore */
+var WireFormat = require('./encoding/wire-format.js').WireFormat; /** @ignore */
+var TlvWireFormat = require('./encoding/tlv-wire-format.js').TlvWireFormat; /** @ignore */
+var Tlv = require('./encoding/tlv/tlv.js').Tlv; /** @ignore */
+var TlvDecoder = require('./encoding/tlv/tlv-decoder.js').TlvDecoder; /** @ignore */
+var ForwardingFlags = require('./forwarding-flags.js').ForwardingFlags; /** @ignore */
+var Transport = require('./transport/transport.js').Transport; /** @ignore */
+var TcpTransport = require('./transport/tcp-transport.js').TcpTransport; /** @ignore */
+var UnixTransport = require('./transport/unix-transport.js').UnixTransport; /** @ignore */
+var CommandInterestGenerator = require('./util/command-interest-generator.js').CommandInterestGenerator; /** @ignore */
+var Blob = require('./util/blob.js').Blob; /** @ignore */
+var NdnCommon = require('./util/ndn-common.js').NdnCommon; /** @ignore */
+var NetworkNack = require('./network-nack.js').NetworkNack; /** @ignore */
+var LpPacket = require('./lp/lp-packet.js').LpPacket; /** @ignore */
+var InterestFilterTable = require('./impl/interest-filter-table.js').InterestFilterTable; /** @ignore */
+var PendingInterestTable = require('./impl/pending-interest-table.js').PendingInterestTable; /** @ignore */
+var RegisteredPrefixTable = require('./impl/registered-prefix-table.js').RegisteredPrefixTable; /** @ignore */
+var fs = require('fs'); /** @ignore */
+var LOG = require('./log.js').Log.LOG;
+
+/**
+ * Create a new Face with the given settings.
+ * This throws an exception if Face.supported is false.
+ * There are two forms of the constructor. The first form takes the transport and connectionInfo:
+ * Face(transport, connectionInfo). The second form takes an optional settings object:
+ * Face([settings]).
+ * @constructor
+ * @param {Transport} transport An object of a subclass of Transport to use for
+ * communication.
+ * @param {Transport.ConnectionInfo} connectionInfo This must be a ConnectionInfo
+ * from the same subclass of Transport as transport. If omitted and transport is
+ * a new UnixTransport() then attempt to create to the Unix socket for the local
+ * forwarder.
+ * @param {Object} settings (optional) An associative array with the following defaults:
+ * {
+ * getTransport: function() { return new WebSocketTransport(); }, // If in the browser.
+ * OR function() { return new TcpTransport(); }, // If in Node.js.
+ * // If getTransport creates a UnixTransport and connectionInfo is null,
+ * // then connect to the local forwarder's Unix socket.
+ * getConnectionInfo: transport.defaultGetConnectionInfo, // a function, on each call it returns a new Transport.ConnectionInfo or null if there are no more hosts.
+ * // If connectionInfo or host is not null, getConnectionInfo is ignored.
+ * connectionInfo: null,
+ * host: null, // If null and connectionInfo is null, use getConnectionInfo when connecting.
+ * // However, if connectionInfo is not null, use it instead.
+ * port: 9696, // If in the browser.
+ * OR 6363, // If in Node.js.
+ * // However, if connectionInfo is not null, use it instead.
+ * onopen: function() { if (LOG > 3) console.log("NDN connection established."); },
+ * onclose: function() { if (LOG > 3) console.log("NDN connection closed."); },
+ * }
+ */
+var Face = function Face(transportOrSettings, connectionInfo)
+{
+ if (!Face.supported)
+ throw new Error("The necessary JavaScript support is not available on this platform.");
+
+ var settings;
+ if (typeof transportOrSettings == 'object' && transportOrSettings instanceof Transport) {
+ this.getConnectionInfo = null;
+ this.transport = transportOrSettings;
+ this.connectionInfo = (connectionInfo || null);
+ // Use defaults for other settings.
+ settings = {};
+
+ if (this.connectionInfo == null) {
+ if (this.transport && this.transport.__proto__ &&
+ this.transport.__proto__.name == "UnixTransport") {
+ // Try to create the default connectionInfo for UnixTransport.
+ var filePath = Face.getUnixSocketFilePathForLocalhost();
+ if (filePath != null)
+ this.connectionInfo = new UnixTransport.ConnectionInfo(filePath);
+ else
+ console.log
+ ("Face constructor: Cannot determine the default Unix socket file path for UnixTransport");
+ if (LOG > 0)
+ console.log("Using " + this.connectionInfo.toString());
+ }
+ }
+ }
+ else {
+ settings = (transportOrSettings || {});
+ // For the browser, browserify-tcp-transport.js replaces TcpTransport with WebSocketTransport.
+ var getTransport = (settings.getTransport || function() { return new TcpTransport(); });
+ this.transport = getTransport();
+ this.getConnectionInfo = (settings.getConnectionInfo || this.transport.defaultGetConnectionInfo);
+
+ this.connectionInfo = (settings.connectionInfo || null);
+ if (this.connectionInfo == null) {
+ var host = (settings.host !== undefined ? settings.host : null);
+
+ if (this.transport && this.transport.__proto__ &&
+ this.transport.__proto__.name == "UnixTransport") {
+ // We are using UnixTransport on Node.js. There is no IP-style host and port.
+ if (host != null)
+ // Assume the host is the local Unix socket path.
+ this.connectionInfo = new UnixTransport.ConnectionInfo(host);
+ else {
+ // If getConnectionInfo is not null, it will be used instead so no
+ // need to set this.connectionInfo.
+ if (this.getConnectionInfo == null) {
+ var filePath = Face.getUnixSocketFilePathForLocalhost();
+ if (filePath != null)
+ this.connectionInfo = new UnixTransport.ConnectionInfo(filePath);
+ else
+ console.log
+ ("Face constructor: Cannot determine the default Unix socket file path for UnixTransport");
+ }
+ }
+ }
+ else {
+ if (host != null) {
+ if (typeof WebSocketTransport != 'undefined')
+ this.connectionInfo = new WebSocketTransport.ConnectionInfo
+ (host, settings.port || 9696);
+ else
+ this.connectionInfo = new TcpTransport.ConnectionInfo
+ (host, settings.port || 6363);
+ }
+ }
+ }
+ }
+
+ // Deprecated: Set this.host and this.port for backwards compatibility.
+ if (this.connectionInfo == null) {
+ this.host = null;
+ this.host = null;
+ }
+ else {
+ this.host = this.connectionInfo.host;
+ this.host = this.connectionInfo.port;
+ }
+
+ this.readyStatus = Face.UNOPEN;
+
+ // Event handler
+ this.onopen = (settings.onopen || function() { if (LOG > 3) console.log("Face connection established."); });
+ this.onclose = (settings.onclose || function() { if (LOG > 3) console.log("Face connection closed."); });
+ // This is used by reconnectAndExpressInterest.
+ this.onConnectedCallbacks = [];
+ this.commandKeyChain = null;
+ this.commandCertificateName = new Name();
+ this.commandInterestGenerator = new CommandInterestGenerator();
+ this.timeoutPrefix = new Name("/local/timeout");
+
+ this.pendingInterestTable_ = new PendingInterestTable();
+ this.interestFilterTable_ = new InterestFilterTable();
+ this.registeredPrefixTable_ = new RegisteredPrefixTable(this.interestFilterTable_);
+ this.lastEntryId = 0;
+};
+
+exports.Face = Face;
+
+Face.UNOPEN = 0; // the Face is created but not opened yet
+Face.OPEN_REQUESTED = 1; // requested to connect but onopen is not called.
+Face.OPENED = 2; // connection to the forwarder opened
+Face.CLOSED = 3; // connection to the forwarder closed
+
+TcpTransport.importFace(Face);
+
+/**
+ * If the forwarder's Unix socket file path exists, then return the file path.
+ * Otherwise return an empty string. This uses Node.js blocking file system
+ * utilities.
+ * @return The Unix socket file path to use, or an empty string.
+ */
+Face.getUnixSocketFilePathForLocalhost = function()
+{
+ var filePath = "/var/run/nfd.sock";
+ if (fs.existsSync(filePath))
+ return filePath;
+ else {
+ filePath = "/tmp/.ndnd.sock";
+ if (fs.existsSync(filePath))
+ return filePath;
+ else
+ return "";
+ }
+}
+
+/**
+ * Return true if necessary JavaScript support is available, else log an error and return false.
+ */
+Face.getSupported = function()
+{
+ try {
+ var dummy = new Buffer(1).slice(0, 1);
+ }
+ catch (ex) {
+ console.log("NDN not available: Buffer not supported. " + ex);
+ return false;
+ }
+
+ return true;
+};
+
+Face.supported = Face.getSupported();
+
+Face.prototype.createRoute = function(hostOrConnectionInfo, port)
+{
+ if (hostOrConnectionInfo instanceof Transport.ConnectionInfo)
+ this.connectionInfo = hostOrConnectionInfo;
+ else
+ this.connectionInfo = new TcpTransport.ConnectionInfo(hostOrConnectionInfo, port);
+
+ // Deprecated: Set this.host and this.port for backwards compatibility.
+ this.host = this.connectionInfo.host;
+ this.host = this.connectionInfo.port;
+};
+
+Face.prototype.close = function()
+{
+ if (this.readyStatus != Face.OPENED)
+ return;
+
+ this.readyStatus = Face.CLOSED;
+ this.transport.close();
+};
+
+/**
+ * An internal method to get the next unique entry ID for the pending interest
+ * table, interest filter table, etc. Most entry IDs are for the pending
+ * interest table (there usually are not many interest filter table entries) so
+ * we use a common pool to only have to have one method which is called by Face.
+ *
+ * @return {number} The next entry ID.
+ */
+Face.prototype.getNextEntryId = function()
+{
+ return ++this.lastEntryId;
+};
+
+/**
+ * Return a function that selects a host at random from hostList and returns
+ * makeConnectionInfo(host, port), and if no more hosts remain, return null.
+ * @param {Array<string>} hostList An array of host names.
+ * @param {number} port The port for the connection.
+ * @param {function} makeConnectionInfo This calls makeConnectionInfo(host, port)
+ * to make the Transport.ConnectionInfo. For example:
+ * function(host, port) { return new TcpTransport.ConnectionInfo(host, port); }
+ * @return {function} A function which returns a Transport.ConnectionInfo.
+ */
+Face.makeShuffledHostGetConnectionInfo = function(hostList, port, makeConnectionInfo)
+{
+ // Make a copy.
+ hostList = hostList.slice(0, hostList.length);
+ DataUtils.shuffle(hostList);
+
+ return function() {
+ if (hostList.length == 0)
+ return null;
+
+ return makeConnectionInfo(hostList.splice(0, 1)[0], port);
+ };
+};
+
+/**
+ * Send the interest through the transport, read the entire response and call
+ * onData, onTimeout or onNetworkNack as described below.
+ * There are two forms of expressInterest. The first form takes the exact
+ * interest (including lifetime):
+ * expressInterest(interest, onData [, onTimeout] [, onNetworkNack] [, wireFormat]).
+ * The second form creates the interest from a name and optional interest template:
+ * expressInterest(name [, template], onData [, onTimeout] [, onNetworkNack] [, wireFormat]).
+ * @param {Interest} interest The Interest to send which includes the interest lifetime for the timeout.
+ * @param {function} onData When a matching data packet is received, this calls onData(interest, data) where
+ * interest is the interest given to expressInterest and data is the received
+ * Data object. NOTE: You must not change the interest object - if you need to
+ * change it then make a copy.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onTimeout (optional) If the interest times out according to the interest lifetime,
+ * this calls onTimeout(interest) where:
+ * interest is the interest given to expressInterest.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onNetworkNack (optional) When a network Nack packet for the
+ * interest is received and onNetworkNack is not null, this calls
+ * onNetworkNack(interest, networkNack) and does not call onTimeout. interest is
+ * the sent Interest and networkNack is the received NetworkNack. If
+ * onNetworkNack is supplied, then onTimeout must be supplied too. However, if a
+ * network Nack is received and onNetworkNack is null, do nothing and wait for
+ * the interest to time out. (Therefore, an application which does not yet
+ * process a network Nack reason treats a Nack the same as a timeout.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {Name} name The Name for the interest. (only used for the second form of expressInterest).
+ * @param {Interest} template (optional) If not omitted, copy the interest selectors from this Interest.
+ * If omitted, use a default interest lifetime. (only used for the second form of expressInterest).
+ * @param {WireFormat} (optional) A WireFormat object used to encode the message.
+ * If omitted, use WireFormat.getDefaultWireFormat().
+ * @return {number} The pending interest ID which can be used with removePendingInterest.
+ * @throws Error If the encoded interest size exceeds Face.getMaxNdnPacketSize().
+ */
+Face.prototype.expressInterest = function
+ (interestOrName, arg2, arg3, arg4, arg5, arg6)
+{
+ var interest;
+ if (typeof interestOrName === 'object' && interestOrName instanceof Interest)
+ // Just use a copy of the interest.
+ interest = new Interest(interestOrName);
+ else {
+ // The first argument is a name. Make the interest from the name and possible template.
+ if (arg2 && typeof arg2 === 'object' && arg2 instanceof Interest) {
+ var template = arg2;
+ // Copy the template.
+ interest = new Interest(template);
+ interest.setName(interestOrName);
+
+ // Shift the remaining args to be processed below.
+ arg2 = arg3;
+ arg3 = arg4;
+ arg4 = arg5;
+ arg5 = arg6;
+ }
+ else {
+ // No template.
+ interest = new Interest(interestOrName);
+ interest.setInterestLifetimeMilliseconds(4000); // default interest timeout
+ }
+ }
+
+ var onData = arg2;
+ var onTimeout;
+ var onNetworkNack;
+ var wireFormat;
+ // arg3, arg4, arg5 may be:
+ // OnTimeout, OnNetworkNack, WireFormat
+ // OnTimeout, OnNetworkNack, null
+ // OnTimeout, WireFormat, null
+ // OnTimeout, null, null
+ // WireFormat, null, null
+ // null, null, null
+ if (typeof arg3 === "function")
+ onTimeout = arg3;
+ else
+ onTimeout = function() {};
+
+ if (typeof arg4 === "function")
+ onNetworkNack = arg4;
+ else
+ onNetworkNack = null;
+
+ if (arg3 instanceof WireFormat)
+ wireFormat = arg3;
+ else if (arg4 instanceof WireFormat)
+ wireFormat = arg4;
+ else if (arg5 instanceof WireFormat)
+ wireFormat = arg5;
+ else
+ wireFormat = WireFormat.getDefaultWireFormat();
+
+ var pendingInterestId = this.getNextEntryId();
+
+ // Set the nonce in our copy of the Interest so it is saved in the PIT.
+ interest.setNonce(Face.nonceTemplate_);
+ interest.refreshNonce();
+
+ if (this.connectionInfo == null) {
+ if (this.getConnectionInfo == null)
+ console.log('ERROR: connectionInfo is NOT SET');
+ else {
+ var thisFace = this;
+ this.connectAndExecute(function() {
+ thisFace.reconnectAndExpressInterest
+ (pendingInterestId, interest, onData, onTimeout, onNetworkNack,
+ wireFormat);
+ });
+ }
+ }
+ else
+ this.reconnectAndExpressInterest
+ (pendingInterestId, interest, onData, onTimeout, onNetworkNack, wireFormat);
+
+ return pendingInterestId;
+};
+
+/**
* If the host and port are different than the ones in this.transport, then call
* this.transport.connect to change the connection (or connect for the first time).
* Then call expressInterestHelper.
*/
-NDN.prototype.reconnectAndExpressInterest = function(interest, closure) {
- if (this.transport.connectedHost != this.host || this.transport.connectedPort != this.port) {
- var thisNDN = this;
- this.transport.connect(thisNDN, function() { thisNDN.expressInterestHelper(interest, closure); });
- }
- else
- this.expressInterestHelper(interest, closure);
-};
+Face.prototype.reconnectAndExpressInterest = function
+ (pendingInterestId, interest, onData, onTimeout, onNetworkNack, wireFormat)
+{
+ var thisFace = this;
+ if (!this.connectionInfo.equals(this.transport.connectionInfo) || this.readyStatus === Face.UNOPEN) {
+ this.readyStatus = Face.OPEN_REQUESTED;
+ this.onConnectedCallbacks.push
+ (function() {
+ thisFace.expressInterestHelper
+ (pendingInterestId, interest, onData, onTimeout, onNetworkNack,
+ wireFormat);
+ });
-/*
- * Do the work of reconnectAndExpressInterest once we know we are connected. Set the PITTable and call
- * this.transport.send to send the interest.
- */
-NDN.prototype.expressInterestHelper = function(interest, closure) {
- var binaryInterest = encodeToBinaryInterest(interest);
- var thisNDN = this;
- //TODO: check local content store first
- if (closure != null) {
- var pitEntry = new PITEntry(interest, closure);
- // TODO: This needs to be a single thread-safe transaction on a global object.
- NDN.PITTable.push(pitEntry);
- closure.pitEntry = pitEntry;
+ this.transport.connect
+ (this.connectionInfo, this,
+ function() {
+ thisFace.readyStatus = Face.OPENED;
- // Set interest timer.
- var timeoutMilliseconds = (interest.interestLifetime || 4000);
- var timeoutCallback = function() {
- if (LOG > 3) console.log("Interest time out: " + interest.name.to_uri());
-
- // Remove PIT entry from NDN.PITTable, even if we add it again later to re-express
- // the interest because we don't want to match it in the mean time.
- // TODO: Make this a thread-safe operation on the global PITTable.
- var index = NDN.PITTable.indexOf(pitEntry);
- if (index >= 0)
- NDN.PITTable.splice(index, 1);
-
- // Raise closure callback
- if (closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT,
- new UpcallInfo(thisNDN, interest, 0, null)) == Closure.RESULT_REEXPRESS) {
- if (LOG > 3) console.log("Re-express interest: " + interest.name.to_uri());
- pitEntry.timerID = setTimeout(timeoutCallback, timeoutMilliseconds);
- NDN.PITTable.push(pitEntry);
- thisNDN.transport.send(binaryInterest);
- }
- };
- pitEntry.timerID = setTimeout(timeoutCallback, timeoutMilliseconds);
- }
-
- this.transport.send(binaryInterest);
-};
-
-NDN.prototype.registerPrefix = function(name, closure, flag) {
- var thisNDN = this;
- var onConnected = function() {
- if (thisNDN.ndndid == null) {
- // Fetch ndndid first, then register.
- var interest = new Interest(NDN.ndndIdFetcher);
- interest.interestLifetime = 4000; // milliseconds
- if (LOG>3) console.log('Expressing interest for ndndid from ndnd.');
- thisNDN.reconnectAndExpressInterest
- (interest, new NDN.FetchNdndidClosure(thisNDN, name, closure, flag));
+ // Execute each action requested while the connection was opening.
+ while (thisFace.onConnectedCallbacks.length > 0) {
+ try {
+ thisFace.onConnectedCallbacks.shift()();
+ } catch (ex) {
+ console.log("Face.reconnectAndExpressInterest: ignoring exception from onConnectedCallbacks: " + ex);
+ }
}
- else
- thisNDN.registerPrefixHelper(name, closure, flag);
- };
- if (this.host == null || this.port == null) {
- if (this.getHostAndPort == null)
- console.log('ERROR: host OR port NOT SET');
- else
- this.connectAndExecute(onConnected);
- }
+ if (thisFace.onopen)
+ // Call Face.onopen after success
+ thisFace.onopen();
+ },
+ function() { thisFace.closeByTransport(); });
+ }
+ else {
+ if (this.readyStatus === Face.OPEN_REQUESTED)
+ // The connection is still opening, so add to the interests to express.
+ this.onConnectedCallbacks.push
+ (function() {
+ thisFace.expressInterestHelper
+ (pendingInterestId, interest, onData, onTimeout, onNetworkNack,
+ wireFormat);
+ });
+ else if (this.readyStatus === Face.OPENED)
+ this.expressInterestHelper
+ (pendingInterestId, interest, onData, onTimeout, onNetworkNack,
+ wireFormat);
else
- onConnected();
+ throw new Error
+ ("reconnectAndExpressInterest: unexpected connection is not opened");
+ }
};
-/*
- * This is a closure to receive the ContentObject for NDN.ndndIdFetcher and call
- * registerPrefixHelper(name, callerClosure, flag).
+/**
+ * Do the work of reconnectAndExpressInterest once we know we are connected.
+ * Add the PendingInterest and call this.transport.send to send the interest.
*/
-NDN.FetchNdndidClosure = function FetchNdndidClosure(ndn, name, callerClosure, flag) {
- // Inherit from Closure.
- Closure.call(this);
+Face.prototype.expressInterestHelper = function
+ (pendingInterestId, interest, onData, onTimeout, onNetworkNack, wireFormat)
+{
+ if (this.pendingInterestTable_.add
+ (pendingInterestId, interest, onData, onTimeout, onNetworkNack) == null)
+ // removePendingInterest was already called with the pendingInterestId.
+ return;
- this.ndn = ndn;
- this.name = name;
- this.callerClosure = callerClosure;
- this.flag = flag;
+ // Special case: For timeoutPrefix we don't actually send the interest.
+ if (!this.timeoutPrefix.match(interest.getName())) {
+ var binaryInterest = interest.wireEncode(wireFormat);
+ if (binaryInterest.size() > Face.getMaxNdnPacketSize())
+ throw new Error
+ ("The encoded interest size exceeds the maximum limit getMaxNdnPacketSize()");
+
+ this.transport.send(binaryInterest.buf());
+ }
};
-NDN.FetchNdndidClosure.prototype.upcall = function(kind, upcallInfo) {
- if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
- console.log("Timeout while requesting the ndndid. Cannot registerPrefix for " +
- this.name.to_uri() + " .");
- return Closure.RESULT_OK;
- }
- if (!(kind == Closure.UPCALL_CONTENT ||
- kind == Closure.UPCALL_CONTENT_UNVERIFIED))
- // The upcall is not for us.
- return Closure.RESULT_ERR;
+/**
+ * Remove the pending interest entry with the pendingInterestId from the pending
+ * interest table. This does not affect another pending interest with a
+ * different pendingInterestId, even if it has the same interest name.
+ * If there is no entry with the pendingInterestId, do nothing.
+ * @param {number} pendingInterestId The ID returned from expressInterest.
+ */
+Face.prototype.removePendingInterest = function(pendingInterestId)
+{
+ this.pendingInterestTable_.removePendingInterest(pendingInterestId);
+};
- var co = upcallInfo.contentObject;
- if (!co.signedInfo || !co.signedInfo.publisher
- || !co.signedInfo.publisher.publisherPublicKeyDigest)
- console.log
- ("ContentObject doesn't have a publisherPublicKeyDigest. Cannot set ndndid and registerPrefix for "
- + this.name.to_uri() + " .");
+/**
+ * Set the KeyChain and certificate name used to sign command interests (e.g.
+ * for registerPrefix).
+ * @param {KeyChain} keyChain The KeyChain object for signing interests, which
+ * must remain valid for the life of this Face. You must create the KeyChain
+ * object and pass it in. You can create a default KeyChain for your system with
+ * the default KeyChain constructor.
+ * @param {Name} certificateName The certificate name for signing interests.
+ * This makes a copy of the Name. You can get the default certificate name with
+ * keyChain.getDefaultCertificateName() .
+ */
+Face.prototype.setCommandSigningInfo = function(keyChain, certificateName)
+{
+ this.commandKeyChain = keyChain;
+ this.commandCertificateName = new Name(certificateName);
+};
+
+/**
+ * Set the certificate name used to sign command interest (e.g. for
+ * registerPrefix), using the KeyChain that was set with setCommandSigningInfo.
+ * @param {Name} certificateName The certificate name for signing interest. This
+ * makes a copy of the Name.
+ */
+Face.prototype.setCommandCertificateName = function(certificateName)
+{
+ this.commandCertificateName = new Name(certificateName);
+};
+
+/**
+ * Append a timestamp component and a random value component to interest's name.
+ * Then use the keyChain and certificateName from setCommandSigningInfo to sign
+ * the interest. If the interest lifetime is not set, this sets it.
+ * @note This method is an experimental feature. See the API docs for more
+ * detail at
+ * http://named-data.net/doc/ndn-ccl-api/face.html#face-makecommandinterest-method .
+ * @param {Interest} interest The interest whose name is appended with
+ * components.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the SignatureInfo and to encode the interest name for signing. If omitted,
+ * use WireFormat.getDefaultWireFormat().
+ * @param {function} onComplete (optional) This calls onComplete() when complete.
+ * If omitted, block until complete. (Some crypto/database libraries only use a
+ * callback, so onComplete is required to use these.)
+ */
+Face.prototype.makeCommandInterest = function(interest, wireFormat, onComplete)
+{
+ onComplete = (typeof wireFormat === "function") ? wireFormat : onComplete;
+ wireFormat = (typeof wireFormat === "function" || !wireFormat) ?
+ WireFormat.getDefaultWireFormat() : wireFormat;
+ this.nodeMakeCommandInterest
+ (interest, this.commandKeyChain, this.commandCertificateName, wireFormat,
+ onComplete);
+};
+
+/**
+ * Append a timestamp component and a random value component to interest's name.
+ * Then use the keyChain and certificateName from setCommandSigningInfo to sign
+ * the interest. If the interest lifetime is not set, this sets it.
+ * @param {Interest} interest The interest whose name is appended with
+ * components.
+ * @param {KeyChain} keyChain The KeyChain for calling sign.
+ * @param {Name} certificateName The certificate name of the key to use for
+ * signing.
+ * @param {WireFormat} wireFormat A WireFormat object used to encode
+ * the SignatureInfo and to encode the interest name for signing.
+ * @param {function} onComplete (optional) This calls onComplete() when complete.
+ * (Some crypto/database libraries only use a callback, so onComplete is
+ * required to use these.)
+ */
+Face.prototype.nodeMakeCommandInterest = function
+ (interest, keyChain, certificateName, wireFormat, onComplete)
+{
+ this.commandInterestGenerator.generate
+ (interest, keyChain, certificateName, wireFormat, onComplete);
+};
+
+/**
+ * Register prefix with the connected NDN hub and call onInterest when a
+ * matching interest is received. To register a prefix with NFD, you must
+ * first call setCommandSigningInfo.
+ * This uses the form:
+ * @param {Name} prefix The Name prefix.
+ * @param {function} onInterest (optional) If not null, this creates an interest
+ * filter from prefix so that when an Interest is received which matches the
+ * filter, this calls
+ * onInterest(prefix, interest, face, interestFilterId, filter).
+ * NOTE: You must not change the prefix object - if you need to change it then
+ * make a copy. If onInterest is null, it is ignored and you must call
+ * setInterestFilter.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onRegisterFailed If register prefix fails for any reason,
+ * this calls onRegisterFailed(prefix) where:
+ * prefix is the prefix given to registerPrefix.
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {function} onRegisterSuccess (optional) When this receives a success
+ * message, this calls onRegisterSuccess(prefix, registeredPrefixId) where
+ * prefix is the prefix given to registerPrefix and registeredPrefixId is
+ * the value retured by registerPrefix. If onRegisterSuccess is null or omitted,
+ * this does not use it. (The onRegisterSuccess parameter comes after
+ * onRegisterFailed because it can be null or omitted, unlike onRegisterFailed.)
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
+ * @param {ForwardingFlags} flags (optional) The ForwardingFlags object for
+ * finer control of which interests are forward to the application. If omitted,
+ * use the default flags defined by the default ForwardingFlags constructor.
+ * @return {number} The registered prefix ID which can be used with
+ * removeRegisteredPrefix.
+ */
+Face.prototype.registerPrefix = function
+ (prefix, onInterest, onRegisterFailed, onRegisterSuccess, flags, wireFormat)
+{
+ // Temporarlity reassign to resolve the different overloaded forms.
+ var arg4 = onRegisterSuccess;
+ var arg5 = flags;
+ var arg6 = wireFormat;
+ // arg4, arg5, arg6 may be:
+ // OnRegisterSuccess, ForwardingFlags, WireFormat
+ // OnRegisterSuccess, ForwardingFlags, null
+ // OnRegisterSuccess, WireFormat, null
+ // OnRegisterSuccess, null, null
+ // ForwardingFlags, WireFormat, null
+ // ForwardingFlags, null, null
+ // WireFormat, null, null
+ // null, null, null
+ if (typeof arg4 === "function")
+ onRegisterSuccess = arg4;
+ else
+ onRegisterSuccess = null;
+
+ if (arg4 instanceof ForwardingFlags)
+ flags = arg4;
+ else if (arg5 instanceof ForwardingFlags)
+ flags = arg5;
+ else
+ flags = new ForwardingFlags();
+
+ if (arg4 instanceof WireFormat)
+ wireFormat = arg4;
+ else if (arg5 instanceof WireFormat)
+ wireFormat = arg5;
+ else if (arg6 instanceof WireFormat)
+ wireFormat = arg6;
+ else
+ wireFormat = WireFormat.getDefaultWireFormat();
+
+ if (!onRegisterFailed)
+ onRegisterFailed = function() {};
+
+ var registeredPrefixId = this.getNextEntryId();
+ var thisFace = this;
+ var onConnected = function() {
+ thisFace.nfdRegisterPrefix
+ (registeredPrefixId, prefix, onInterest, flags, onRegisterFailed,
+ onRegisterSuccess, thisFace.commandKeyChain,
+ thisFace.commandCertificateName, wireFormat);
+ };
+
+ if (this.connectionInfo == null) {
+ if (this.getConnectionInfo == null)
+ console.log('ERROR: connectionInfo is NOT SET');
+ else
+ this.connectAndExecute(onConnected);
+ }
+ else
+ onConnected();
+
+ return registeredPrefixId;
+};
+
+/**
+ * Get the practical limit of the size of a network-layer packet. If a packet
+ * is larger than this, the library or application MAY drop it.
+ * @return {number} The maximum NDN packet size.
+ */
+Face.getMaxNdnPacketSize = function() { return NdnCommon.MAX_NDN_PACKET_SIZE; };
+
+/**
+ * A RegisterResponse has onData to receive the response Data packet from the
+ * register prefix interest sent to the connected NDN hub. If this gets a bad
+ * response or onTimeout is called, then call onRegisterFailed.
+ */
+Face.RegisterResponse = function RegisterResponse
+ (prefix, onRegisterFailed, onRegisterSuccess, registeredPrefixId, parent,
+ onInterest)
+{
+ this.prefix = prefix;
+ this.onRegisterFailed = onRegisterFailed;
+ this.onRegisterSuccess= onRegisterSuccess;
+ this.registeredPrefixId = registeredPrefixId;
+ this.parent = parent;
+ this.onInterest = onInterest;
+};
+
+Face.RegisterResponse.prototype.onData = function(interest, responseData)
+{
+ // Decode responseData.getContent() and check for a success code.
+ var controlResponse = new ControlResponse();
+ try {
+ controlResponse.wireDecode(responseData.getContent(), TlvWireFormat.get());
+ }
+ catch (e) {
+ // Error decoding the ControlResponse.
+ if (LOG > 0)
+ console.log("Register prefix failed: Error decoding the NFD response: " + e);
+ if (this.onRegisterFailed) {
+ try {
+ this.onRegisterFailed(this.prefix);
+ } catch (ex) {
+ console.log("Error in onRegisterFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ return;
+ }
+
+ // Status code 200 is "OK".
+ if (controlResponse.getStatusCode() != 200) {
+ if (LOG > 0)
+ console.log("Register prefix failed: Expected NFD status code 200, got: " +
+ controlResponse.getStatusCode());
+ if (this.onRegisterFailed) {
+ try {
+ this.onRegisterFailed(this.prefix);
+ } catch (ex) {
+ console.log("Error in onRegisterFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ return;
+ }
+
+ // Success, so we can add to the registered prefix table.
+ if (this.registeredPrefixId != 0) {
+ var interestFilterId = 0;
+ if (this.onInterest != null)
+ // registerPrefix was called with the "combined" form that includes the
+ // callback, so add an InterestFilterEntry.
+ interestFilterId = this.parent.setInterestFilter
+ (new InterestFilter(this.prefix), this.onInterest);
+
+ if (!this.parent.registeredPrefixTable_.add
+ (this.registeredPrefixId, this.prefix, interestFilterId)) {
+ // removeRegisteredPrefix was already called with the registeredPrefixId.
+ if (interestFilterId > 0)
+ // Remove the related interest filter we just added.
+ this.parent.unsetInterestFilter(interestFilterId);
+
+ return;
+ }
+ }
+
+ if (LOG > 2)
+ console.log("Register prefix succeeded with the NFD forwarder for prefix " +
+ this.prefix.toUri());
+ if (this.onRegisterSuccess != null) {
+ try {
+ this.onRegisterSuccess(this.prefix, this.registeredPrefixId);
+ } catch (ex) {
+ console.log("Error in onRegisterSuccess: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+};
+
+/**
+ * We timed out waiting for the response.
+ */
+Face.RegisterResponse.prototype.onTimeout = function(interest)
+{
+ if (LOG > 2)
+ console.log("Timeout for NFD register prefix command.");
+ if (this.onRegisterFailed) {
+ try {
+ this.onRegisterFailed(this.prefix);
+ } catch (ex) {
+ console.log("Error in onRegisterFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+};
+
+/**
+ * Do the work of registerPrefix to register with NFD.
+ * @param {number} registeredPrefixId The Face.getNextEntryId() which
+ * registerPrefix got so it could return it to the caller. If this is 0, then
+ * don't add to registeredPrefixTable (assuming it has already been done).
+ * @param {Name} prefix
+ * @param {function} onInterest
+ * @param {ForwardingFlags} flags
+ * @param {function} onRegisterFailed
+ * @param {function} onRegisterSuccess
+ * @param {KeyChain} commandKeyChain
+ * @param {Name} commandCertificateName
+ * @param {WireFormat} wireFormat
+ */
+Face.prototype.nfdRegisterPrefix = function
+ (registeredPrefixId, prefix, onInterest, flags, onRegisterFailed,
+ onRegisterSuccess, commandKeyChain, commandCertificateName, wireFormat)
+{
+ if (commandKeyChain == null)
+ throw new Error
+ ("registerPrefix: The command KeyChain has not been set. You must call setCommandSigningInfo.");
+ if (commandCertificateName.size() == 0)
+ throw new Error
+ ("registerPrefix: The command certificate name has not been set. You must call setCommandSigningInfo.");
+
+ var controlParameters = new ControlParameters();
+ controlParameters.setName(prefix);
+ controlParameters.setForwardingFlags(flags);
+
+ // Make the callback for this.isLocal().
+ var thisFace = this;
+ var onIsLocalResult = function(isLocal) {
+ var commandInterest = new Interest();
+ if (isLocal) {
+ commandInterest.setName(new Name("/localhost/nfd/rib/register"));
+ // The interest is answered by the local host, so set a short timeout.
+ commandInterest.setInterestLifetimeMilliseconds(2000.0);
+ }
else {
- if (LOG>3) console.log('Got ndndid from ndnd.');
- this.ndn.ndndid = co.signedInfo.publisher.publisherPublicKeyDigest;
- if (LOG>3) console.log(this.ndn.ndndid);
+ commandInterest.setName(new Name("/localhop/nfd/rib/register"));
+ // The host is remote, so set a longer timeout.
+ commandInterest.setInterestLifetimeMilliseconds(4000.0);
+ }
+ // NFD only accepts TlvWireFormat packets.
+ commandInterest.getName().append
+ (controlParameters.wireEncode(TlvWireFormat.get()));
+ thisFace.nodeMakeCommandInterest
+ (commandInterest, commandKeyChain, commandCertificateName,
+ TlvWireFormat.get(), function() {
+ // Send the registration interest.
+ var response = new Face.RegisterResponse
+ (prefix, onRegisterFailed, onRegisterSuccess, registeredPrefixId,
+ thisFace, onInterest);
+ thisFace.reconnectAndExpressInterest
+ (null, commandInterest, response.onData.bind(response),
+ response.onTimeout.bind(response), null, wireFormat);
+ });
+ };
- this.ndn.registerPrefixHelper(this.name, this.callerClosure, this.flag);
- }
-
- return Closure.RESULT_OK;
+ this.isLocal
+ (onIsLocalResult,
+ function(message) {
+ if (LOG > 0)
+ console.log("Error in Transport.isLocal: " + message);
+ if (onRegisterFailed) {
+ try {
+ onRegisterFailed(prefix);
+ } catch (ex) {
+ console.log("Error in onRegisterFailed: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ });
};
-/*
- * Do the work of registerPrefix once we know we are connected with a ndndid.
+/**
+ * Remove the registered prefix entry with the registeredPrefixId from the
+ * registered prefix table. This does not affect another registered prefix with
+ * a different registeredPrefixId, even if it has the same prefix name. If an
+ * interest filter was automatically created by registerPrefix, also remove it.
+ * If there is no entry with the registeredPrefixId, do nothing.
+ *
+ * @param {number} registeredPrefixId The ID returned from registerPrefix.
*/
-NDN.prototype.registerPrefixHelper = function(name, closure, flag) {
- var fe = new ForwardingEntry('selfreg', name, null, null, 3, 2147483647);
- var bytes = encodeForwardingEntry(fe);
-
- var si = new SignedInfo();
- si.setFields();
-
- var co = new ContentObject(new Name(), si, bytes, new Signature());
- co.sign();
- var coBinary = encodeToBinaryContentObject(co);
-
- //var nodename = unescape('%00%88%E2%F4%9C%91%16%16%D6%21%8E%A0c%95%A5%A6r%11%E0%A0%82%89%A6%A9%85%AB%D6%E2%065%DB%AF');
- var nodename = this.ndndid;
- var interestName = new Name(['ndnx', nodename, 'selfreg', coBinary]);
-
- var interest = new Interest(interestName);
- interest.scope = 1;
- if (LOG > 3) console.log('Send Interest registration packet.');
-
- var csEntry = new CSEntry(name.getName(), closure);
- NDN.CSTable.push(csEntry);
-
- this.transport.send(encodeToBinaryInterest(interest));
+Face.prototype.removeRegisteredPrefix = function(registeredPrefixId)
+{
+ this.registeredPrefixTable_.removeRegisteredPrefix(registeredPrefixId);
};
-/*
- * This is called when an entire binary XML element is received, such as a ContentObject or Interest.
- * Look up in the PITTable and call the closure callback.
+/**
+ * Add an entry to the local interest filter table to call the onInterest
+ * callback for a matching incoming Interest. This method only modifies the
+ * library's local callback table and does not register the prefix with the
+ * forwarder. It will always succeed. To register a prefix with the forwarder,
+ * use registerPrefix. There are two forms of setInterestFilter.
+ * The first form uses the exact given InterestFilter:
+ * setInterestFilter(filter, onInterest).
+ * The second form creates an InterestFilter from the given prefix Name:
+ * setInterestFilter(prefix, onInterest).
+ * @param {InterestFilter} filter The InterestFilter with a prefix and optional
+ * regex filter used to match the name of an incoming Interest. This makes a
+ * copy of filter.
+ * @param {Name} prefix The Name prefix used to match the name of an incoming
+ * Interest.
+ * @param {function} onInterest When an Interest is received which matches the
+ * filter, this calls onInterest(prefix, interest, face, interestFilterId, filter).
+ * NOTE: The library will log any exceptions thrown by this callback, but for
+ * better error handling the callback should catch and properly handle any
+ * exceptions.
*/
-NDN.prototype.onReceivedElement = function(element) {
- if (LOG>3) console.log('Complete element received. Length ' + element.length + '. Start decoding.');
- var decoder = new BinaryXMLDecoder(element);
- // Dispatch according to packet type
- if (decoder.peekStartElement(NDNProtocolDTags.Interest)) { // Interest packet
- if (LOG > 3) console.log('Interest packet received.');
-
- var interest = new Interest();
- interest.from_ndnb(decoder);
- if (LOG > 3) console.log(interest);
- var nameStr = escape(interest.name.getName());
- if (LOG > 3) console.log(nameStr);
-
- var entry = getEntryForRegisteredPrefix(nameStr);
- if (entry != null) {
- //console.log(entry);
- var info = new UpcallInfo(this, interest, 0, null);
- var ret = entry.closure.upcall(Closure.UPCALL_INTEREST, info);
- if (ret == Closure.RESULT_INTEREST_CONSUMED && info.contentObject != null)
- this.transport.send(encodeToBinaryContentObject(info.contentObject));
- }
- } else if (decoder.peekStartElement(NDNProtocolDTags.ContentObject)) { // Content packet
- if (LOG > 3) console.log('ContentObject packet received.');
-
- var co = new ContentObject();
- co.from_ndnb(decoder);
-
- var pitEntry = NDN.getEntryForExpressedInterest(co.name);
- if (pitEntry != null) {
- // Cancel interest timer
- clearTimeout(pitEntry.timerID);
-
- // Remove PIT entry from NDN.PITTable
- var index = NDN.PITTable.indexOf(pitEntry);
- if (index >= 0)
- NDN.PITTable.splice(index, 1);
-
- var currentClosure = pitEntry.closure;
-
- if (this.verify == false) {
- // Pass content up without verifying the signature
- currentClosure.upcall(Closure.UPCALL_CONTENT_UNVERIFIED, new UpcallInfo(this, pitEntry.interest, 0, co));
- return;
- }
-
- // Key verification
-
- // Recursive key fetching & verification closure
- var KeyFetchClosure = function KeyFetchClosure(content, closure, key, sig, wit) {
- this.contentObject = content; // unverified content object
- this.closure = closure; // closure corresponding to the contentObject
- this.keyName = key; // name of current key to be fetched
- this.sigHex = sig; // hex signature string to be verified
- this.witness = wit;
-
- Closure.call(this);
- };
-
- var thisNDN = this;
- KeyFetchClosure.prototype.upcall = function(kind, upcallInfo) {
- if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
- console.log("In KeyFetchClosure.upcall: interest time out.");
- console.log(this.keyName.contentName.getName());
- } else if (kind == Closure.UPCALL_CONTENT) {
- //console.log("In KeyFetchClosure.upcall: signature verification passed");
-
- var rsakey = decodeSubjectPublicKeyInfo(upcallInfo.contentObject.content);
- var verified = rsakey.verifyByteArray(this.contentObject.rawSignatureData, this.witness, this.sigHex);
-
- var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
- //console.log("raise encapsulated closure");
- this.closure.upcall(flag, new UpcallInfo(thisNDN, null, 0, this.contentObject));
-
- // Store key in cache
- var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
- NDN.addKeyEntry(keyEntry);
- //console.log(NDN.KeyStore);
- } else if (kind == Closure.UPCALL_CONTENT_BAD) {
- console.log("In KeyFetchClosure.upcall: signature verification failed");
- }
- };
-
- if (co.signedInfo && co.signedInfo.locator && co.signature) {
- if (LOG > 3) console.log("Key verification...");
- var sigHex = DataUtils.toHex(co.signature.signature).toLowerCase();
-
- var wit = null;
- if (co.signature.Witness != null) {
- wit = new Witness();
- wit.decode(co.signature.Witness);
- }
-
- var keylocator = co.signedInfo.locator;
- if (keylocator.type == KeyLocatorType.KEYNAME) {
- if (LOG > 3) console.log("KeyLocator contains KEYNAME");
- //var keyname = keylocator.keyName.contentName.getName();
- //console.log(nameStr);
- //console.log(keyname);
-
- if (keylocator.keyName.contentName.match(co.name)) {
- if (LOG > 3) console.log("Content is key itself");
-
- var rsakey = decodeSubjectPublicKeyInfo(co.content);
- var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
- var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-
- currentClosure.upcall(flag, new UpcallInfo(this, pitEntry.interest, 0, co));
-
- // SWT: We don't need to store key here since the same key will be
- // stored again in the closure.
- //var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
- //NDN.addKeyEntry(keyEntry);
- //console.log(NDN.KeyStore);
- } else {
- // Check local key store
- var keyEntry = NDN.getKeyByName(keylocator.keyName);
- if (keyEntry) {
- // Key found, verify now
- if (LOG > 3) console.log("Local key cache hit");
- var rsakey = keyEntry.rsaKey;
- var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
- var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-
- // Raise callback
- currentClosure.upcall(flag, new UpcallInfo(this, pitEntry.interest, 0, co));
- } else {
- // Not found, fetch now
- if (LOG > 3) console.log("Fetch key according to keylocator");
- var nextClosure = new KeyFetchClosure(co, currentClosure, keylocator.keyName, sigHex, wit);
- this.expressInterest(keylocator.keyName.contentName.getPrefix(4), nextClosure);
- }
- }
- } else if (keylocator.type == KeyLocatorType.KEY) {
- if (LOG > 3) console.log("Keylocator contains KEY");
-
- var rsakey = decodeSubjectPublicKeyInfo(co.signedInfo.locator.publicKey);
- var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
-
- var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
- // Raise callback
- currentClosure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(this, pitEntry.interest, 0, co));
-
- // Since KeyLocator does not contain key name for this key,
- // we have no way to store it as a key entry in KeyStore.
- } else {
- var cert = keylocator.certificate;
- console.log("KeyLocator contains CERT");
- console.log(cert);
-
- // TODO: verify certificate
- }
- }
- }
- } else
- console.log('Incoming packet is not Interest or ContentObject. Discard now.');
+Face.prototype.setInterestFilter = function(filterOrPrefix, onInterest)
+{
+ var interestFilterId = this.getNextEntryId();
+ this.interestFilterTable_.setInterestFilter
+ (interestFilterId, new InterestFilter(filterOrPrefix), onInterest, this);
+ return interestFilterId;
};
-/*
- * Assume this.getHostAndPort is not null. This is called when this.host is null or its host
- * is not alive. Get a host and port, connect, then execute onConnected().
+/**
+ * Remove the interest filter entry which has the interestFilterId from the
+ * interest filter table. This does not affect another interest filter with a
+ * different interestFilterId, even if it has the same prefix name. If there is
+ * no entry with the interestFilterId, do nothing.
+ * @param {number} interestFilterId The ID returned from setInterestFilter.
*/
-NDN.prototype.connectAndExecute = function(onConnected) {
- var hostAndPort = this.getHostAndPort();
- if (hostAndPort == null) {
- console.log('ERROR: No more hosts from getHostAndPort');
- this.host = null;
+Face.prototype.unsetInterestFilter = function(interestFilterId)
+{
+ this.interestFilterTable_.unsetInterestFilter(interestFilterId);
+};
+
+/**
+ * The OnInterest callback calls this to put a Data packet which satisfies an
+ * Interest.
+ * @param {Data} data The Data packet which satisfies the interest.
+ * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
+ * the Data packet. If omitted, use WireFormat.getDefaultWireFormat().
+ * @throws Error If the encoded Data packet size exceeds getMaxNdnPacketSize().
+ */
+Face.prototype.putData = function(data, wireFormat)
+{
+ wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());
+
+ var encoding = data.wireEncode(wireFormat);
+ if (encoding.size() > Face.getMaxNdnPacketSize())
+ throw new Error
+ ("The encoded Data packet size exceeds the maximum limit getMaxNdnPacketSize()");
+
+ this.transport.send(encoding.buf());
+};
+
+/**
+ * Send the encoded packet out through the transport.
+ * @param {Buffer} encoding The Buffer with the encoded packet to send.
+ * @throws Error If the encoded packet size exceeds getMaxNdnPacketSize().
+ */
+Face.prototype.send = function(encoding)
+{
+ if (encoding.length > Face.getMaxNdnPacketSize())
+ throw new Error
+ ("The encoded packet size exceeds the maximum limit getMaxNdnPacketSize()");
+
+ this.transport.send(encoding);
+};
+
+/**
+ * Check if the face is local based on the current connection through the
+ * Transport; some Transport may cause network I/O (e.g. an IP host name lookup).
+ * @param {function} onResult On success, this calls onResult(isLocal) where
+ * isLocal is true if the host is local, false if not. We use callbacks because
+ * this may need to do network I/O (e.g. an IP host name lookup).
+ * @param {function} onError On failure for DNS lookup or other error, this
+ * calls onError(message) where message is an error string.
+ */
+Face.prototype.isLocal = function(onResult, onError)
+{
+ // TODO: How to call transport.isLocal when this.connectionInfo is null? (This
+ // happens when the application does not supply a host but relies on the
+ // getConnectionInfo function to select a host.) For now return true to keep
+ // the same behavior from before we added Transport.isLocal.
+ if (this.connectionInfo == null)
+ onResult(false);
+ else
+ this.transport.isLocal(this.connectionInfo, onResult, onError);
+};
+
+/**
+ * This is called when an entire element is received, such as a Data or Interest.
+ */
+Face.prototype.onReceivedElement = function(element)
+{
+ if (LOG > 3) console.log('Complete element received. Length ' + element.length + '. Start decoding.');
+
+ var lpPacket = null;
+ if (element[0] == Tlv.LpPacket_LpPacket) {
+ // Decode the LpPacket and replace element with the fragment.
+ lpPacket = new LpPacket();
+ // Set copy false so that the fragment is a slice which will be copied below.
+ // The header fields are all integers and don't need to be copied.
+ TlvWireFormat.get().decodeLpPacket(lpPacket, element, false);
+ element = lpPacket.getFragmentWireEncoding().buf();
+ }
+
+ // First, decode as Interest or Data.
+ var interest = null;
+ var data = null;
+ if (element[0] == Tlv.Interest || element[0] == Tlv.Data) {
+ var decoder = new TlvDecoder (element);
+ if (decoder.peekType(Tlv.Interest, element.length)) {
+ interest = new Interest();
+ interest.wireDecode(element, TlvWireFormat.get());
+
+ if (lpPacket != null)
+ interest.setLpPacket(lpPacket);
+ }
+ else if (decoder.peekType(Tlv.Data, element.length)) {
+ data = new Data();
+ data.wireDecode(element, TlvWireFormat.get());
+
+ if (lpPacket != null)
+ data.setLpPacket(lpPacket);
+ }
+ }
+
+ if (lpPacket !== null) {
+ // We have decoded the fragment, so remove the wire encoding to save memory.
+ lpPacket.setFragmentWireEncoding(new Blob());
+
+ var networkNack = NetworkNack.getFirstHeader(lpPacket);
+ if (networkNack != null) {
+ if (interest == null)
+ // We got a Nack but not for an Interest, so drop the packet.
return;
+
+ var pitEntries = [];
+ this.pendingInterestTable_.extractEntriesForNackInterest(interest, pitEntries);
+ for (var i = 0; i < pitEntries.length; ++i) {
+ var pendingInterest = pitEntries[i];
+ try {
+ pendingInterest.getOnNetworkNack()(pendingInterest.getInterest(), networkNack);
+ } catch (ex) {
+ console.log("Error in onNetworkNack: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+
+ // We have processed the network Nack packet.
+ return;
}
+ }
- if (hostAndPort.host == this.host && hostAndPort.port == this.port) {
- console.log('ERROR: The host returned by getHostAndPort is not alive: ' +
- this.host + ":" + this.port);
- return;
+ // Now process as Interest or Data.
+ if (interest !== null) {
+ if (LOG > 3) console.log('Interest packet received.');
+
+ // Call all interest filter callbacks which match.
+ var matchedFilters = [];
+ this.interestFilterTable_.getMatchedFilters(interest, matchedFilters);
+ for (var i = 0; i < matchedFilters.length; ++i) {
+ var entry = matchedFilters[i];
+ if (LOG > 3)
+ console.log("Found interest filter for " + interest.getName().toUri());
+ try {
+ entry.getOnInterest()
+ (entry.getFilter().getPrefix(), interest, this,
+ entry.getInterestFilterId(), entry.getFilter());
+ } catch (ex) {
+ console.log("Error in onInterest: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
}
+ }
+ else if (data !== null) {
+ if (LOG > 3) console.log('Data packet received.');
- this.host = hostAndPort.host;
- this.port = hostAndPort.port;
- if (LOG>3) console.log("Connect: trying host from getHostAndPort: " + this.host);
-
- // Fetch any content.
- var interest = new Interest(new Name("/"));
- interest.interestLifetime = 4000; // milliseconds
-
- var thisNDN = this;
- var timerID = setTimeout(function() {
- if (LOG>3) console.log("Connect: timeout waiting for host " + thisNDN.host);
- // Try again.
- thisNDN.connectAndExecute(onConnected);
- }, 3000);
-
- this.reconnectAndExpressInterest
- (interest, new NDN.ConnectClosure(this, onConnected, timerID));
+ var pendingInterests = [];
+ this.pendingInterestTable_.extractEntriesForExpressedInterest
+ (data, pendingInterests);
+ // Process each matching PIT entry (if any).
+ for (var i = 0; i < pendingInterests.length; ++i) {
+ var pendingInterest = pendingInterests[i];
+ try {
+ pendingInterest.getOnData()(pendingInterest.getInterest(), data);
+ } catch (ex) {
+ console.log("Error in onData: " + NdnCommon.getErrorWithStackTrace(ex));
+ }
+ }
+ }
};
-NDN.ConnectClosure = function ConnectClosure(ndn, onConnected, timerID) {
- // Inherit from Closure.
- Closure.call(this);
-
- this.ndn = ndn;
- this.onConnected = onConnected;
- this.timerID = timerID;
-};
-
-NDN.ConnectClosure.prototype.upcall = function(kind, upcallInfo) {
- if (!(kind == Closure.UPCALL_CONTENT ||
- kind == Closure.UPCALL_CONTENT_UNVERIFIED))
- // The upcall is not for us.
- return Closure.RESULT_ERR;
-
- // The host is alive, so cancel the timeout and continue with onConnected().
- clearTimeout(this.timerID);
-
- // Call NDN.onopen after success
- this.ndn.readyStatus = NDN.OPENED;
- this.ndn.onopen();
-
- this.onConnected();
-
- return Closure.RESULT_OK;
-};
-
-/*
- * A BinaryXmlElementReader lets you call onReceivedData multiple times which uses a
- * BinaryXMLStructureDecoder to detect the end of a binary XML element and calls
- * elementListener.onReceivedElement(element) with the element.
- * This handles the case where a single call to onReceivedData may contain multiple elements.
+/**
+ * Assume this.getConnectionInfo is not null. This is called when
+ * this.connectionInfo is null or its host is not alive.
+ * Get a connectionInfo, connect, then execute onConnected().
*/
-var BinaryXmlElementReader = function BinaryXmlElementReader(elementListener) {
- this.elementListener = elementListener;
- this.dataParts = [];
- this.structureDecoder = new BinaryXMLStructureDecoder();
+Face.prototype.connectAndExecute = function(onConnected)
+{
+ var connectionInfo = this.getConnectionInfo();
+ if (connectionInfo == null) {
+ console.log('ERROR: No more connectionInfo from getConnectionInfo');
+ this.connectionInfo = null;
+ // Deprecated: Set this.host and this.port for backwards compatibility.
+ this.host = null;
+ this.host = null;
+
+ return;
+ }
+
+ if (connectionInfo.equals(this.connectionInfo)) {
+ console.log
+ ('ERROR: The host returned by getConnectionInfo is not alive: ' +
+ this.connectionInfo.toString());
+ return;
+ }
+
+ this.connectionInfo = connectionInfo;
+ if (LOG>0) console.log("connectAndExecute: trying host from getConnectionInfo: " +
+ this.connectionInfo.toString());
+ // Deprecated: Set this.host and this.port for backwards compatibility.
+ this.host = this.connectionInfo.host;
+ this.host = this.connectionInfo.port;
+
+ // Fetch any content.
+ var interest = new Interest(new Name("/"));
+ interest.setInterestLifetimeMilliseconds(4000);
+
+ var thisFace = this;
+ var timerID = setTimeout(function() {
+ if (LOG>0) console.log("connectAndExecute: timeout waiting for host " + thisFace.host);
+ // Try again.
+ thisFace.connectAndExecute(onConnected);
+ }, 3000);
+
+ this.reconnectAndExpressInterest
+ (null, interest,
+ function(localInterest, localData) {
+ // The host is alive, so cancel the timeout and continue with onConnected().
+ clearTimeout(timerID);
+
+ if (LOG>0)
+ console.log("connectAndExecute: connected to host " + thisFace.host);
+ onConnected();
+ },
+ function(localInterest) { /* Ignore timeout */ },
+ null, WireFormat.getDefaultWireFormat());
};
-BinaryXmlElementReader.prototype.onReceivedData = function(/* Uint8Array */ rawData) {
- // Process multiple objects in the data.
- while(true) {
- // Scan the input to check if a whole ndnb object has been read.
- this.structureDecoder.seek(0);
- if (this.structureDecoder.findElementEnd(rawData)) {
- // Got the remainder of an object. Report to the caller.
- this.dataParts.push(rawData.subarray(0, this.structureDecoder.offset));
- var element = DataUtils.concatArrays(this.dataParts);
- this.dataParts = [];
- try {
- this.elementListener.onReceivedElement(element);
- } catch (ex) {
- console.log("BinaryXmlElementReader: ignoring exception from onReceivedElement: " + ex);
- }
+/**
+ * This is called by the Transport when the connection is closed by the remote host.
+ */
+Face.prototype.closeByTransport = function()
+{
+ this.readyStatus = Face.CLOSED;
+ this.onclose();
+};
- // Need to read a new object.
- rawData = rawData.subarray(this.structureDecoder.offset, rawData.length);
- this.structureDecoder = new BinaryXMLStructureDecoder();
- if (rawData.length == 0)
- // No more data in the packet.
- return;
-
- // else loop back to decode.
- }
- else {
- // Save for a later call to concatArrays so that we only copy data once.
- this.dataParts.push(rawData);
- if (LOG>3) console.log('Incomplete packet received. Length ' + rawData.length + '. Wait for more input.');
- return;
- }
- }
-}
\ No newline at end of file
+Face.nonceTemplate_ = new Blob(new Buffer(4), false);
+(function(n,t,i){"use strict";function s(n,t){return typeof t!="object"&&(t=t()),Object.keys(t).forEach(function(i){n[i]=t[i]}),n}function y(n){return{from:function(t){return n.prototype=Object.create(t.prototype),n.prototype.constructor=n,{extend:function(i){s(n.prototype,typeof i!="object"?i(t.prototype):i)}}}}}function p(n,t){return t(n)}function u(n,t){function cr(){w.on("versionchange",function(){w.close();w.on("error").fire(new g("Database version changed by other database connection."))})}function di(n){this._cfg={version:n,storesSource:null,dbschema:{},tables:{},contentUpgrade:null};this.stores({})}function lr(n,t,i,u){var e,f,s,h,l,c;if(n===0)Object.keys(st).forEach(function(n){gi(t,n,st[n].primKey,st[n].indexes)}),e=w._createTransaction(yt,ui,st),e.idbtrans=t,e.idbtrans.onerror=o(i,["populating database"]),e.on("error").subscribe(i),r.newPSD(function(){r.PSD.trans=e;try{w.on("populate").fire(e)}catch(n){u.onerror=t.onerror=function(n){n.preventDefault()};try{t.abort()}catch(f){}t.db.close();i(n)}});else{if(f=[],s=ri.filter(function(t){return t._cfg.version===n})[0],!s)throw new g("Dexie specification of currently installed DB version is missing");st=w._dbSchema=s._cfg.dbschema;h=!1;l=ri.filter(function(t){return t._cfg.version>n});l.forEach(function(n){var e=st,r=n._cfg.dbschema,u;fr(e,t);fr(r,t);st=w._dbSchema=r;u=ar(e,r);u.add.forEach(function(n){f.push(function(t,i){gi(t,n[0],n[1].primKey,n[1].indexes);i()})});u.change.forEach(function(n){if(n.recreate)throw new g("Not yet support for changing primary key");else f.push(function(t,i){var r=t.objectStore(n.name);n.add.forEach(function(n){nr(r,n)});n.change.forEach(function(n){r.deleteIndex(n.name);nr(r,n)});n.del.forEach(function(n){r.deleteIndex(n)});i()})});n._cfg.contentUpgrade&&f.push(function(t,u){var f,e;h=!0;f=w._createTransaction(yt,[].slice.call(t.db.objectStoreNames,0),r);f.idbtrans=t;e=0;f._promise=p(f._promise,function(n){return function(t,i,r){function f(n){return function(){n.apply(this,arguments);--e==0&&u()}}return++e,n.call(this,t,function(n,t){arguments[0]=f(n);arguments[1]=f(t);i.apply(this,arguments)},r)}});t.onerror=o(i,["running upgrader function for version",n._cfg.version]);f.on("error").subscribe(i);n._cfg.contentUpgrade(f);e===0&&u()});h&&dr()||f.push(function(n,t){yr(r,n);t()})});c=function(){try{f.length?f.shift()(t,c):vr(st,t)}catch(n){u.onerror=t.onerror=function(n){n.preventDefault()};try{t.abort()}catch(r){}t.db.close();i(n)}};c()}}function ar(n,t){var f={del:[],add:[],change:[]},r,e,o,i,c,s,u,l,h;for(r in n)t[r]||f.del.push(r);for(r in t)if(e=n[r],o=t[r],e)if(i={name:r,def:t[r],recreate:!1,del:[],add:[],change:[]},e.primKey.src!==o.primKey.src)i.recreate=!0,f.change.push(i);else{c=e.indexes.reduce(function(n,t){return n[t.name]=t,n},{});s=o.indexes.reduce(function(n,t){return n[t.name]=t,n},{});for(u in c)s[u]||i.del.push(u);for(u in s)l=c[u],h=s[u],l?l.src!==h.src&&i.change.push(h):i.add.push(h);(i.recreate||i.del.length>0||i.add.length>0||i.change.length>0)&&f.change.push(i)}else f.add.push([r,o]);return f}function gi(n,t,i,r){var u=n.db.createObjectStore(t,i.keyPath?{keyPath:i.keyPath,autoIncrement:i.auto}:{autoIncrement:i.auto});return r.forEach(function(n){nr(u,n)}),u}function vr(n,t){Object.keys(n).forEach(function(i){t.db.objectStoreNames.contains(i)||gi(t,i,n[i].primKey,n[i].indexes)})}function yr(n,t){for(var u,r=0;r<t.db.objectStoreNames.length;++r)u=t.db.objectStoreNames[r],(n[u]===null||n[u]===i)&&t.db.deleteObjectStore(u)}function nr(n,t){n.createIndex(t.name,t.keyPath,{unique:t.unique,multiEntry:t.multi})}function pr(n,t){throw new g("Table "+t[0]+" not part of transaction. Original Scope Function Source: "+u.Promise.PSD.trans.scopeFunc.toString());}function ei(n,t,i,r){this.name=n;this.schema=i;this.hook=ni[n]?ni[n].hook:v(null,{creating:[at,f],reading:[lt,nt],updating:[vt,f],deleting:[wt,f]});this._tpf=t;this._collClass=r||li}function tr(n,t,i,r){ei.call(this,n,t,i,r||rr)}function ir(n,t,i,r){function o(n,t,i,r){return s._promise(n,i,r)}var s=this,f,u,e;for(this.db=w,this.mode=n,this.storeNames=t,this.idbtrans=null,this.on=v(this,["complete","error"],"abort"),this._reculock=0,this._blockedFuncs=[],this._psd=null,this.active=!0,this._dbschema=i,r&&(this.parent=r),this._tpf=o,this.tables=Object.create(ki),f=t.length-1;f!==-1;--f)u=t[f],e=w._tableFactory(n,i[u],o),this.tables[u]=e,this[u]||(this[u]=e)}function ci(n,t,i){this._ctx={table:n,index:t===":id"?null:t,collClass:n._collClass,or:i}}function li(n,t){var r=null,u=null,i;if(t)try{r=t()}catch(f){u=f}i=n._ctx;this._ctx={table:i.table,index:i.index,isPrimKey:!i.index||i.table.schema.primKey.keyPath&&i.index===i.table.schema.primKey.name,range:r,op:"openCursor",dir:"next",unique:"",algorithm:null,filter:null,isMatch:null,offset:0,limit:Infinity,error:u,or:i.or}}function rr(){li.apply(this,arguments)}function wr(n,t){return n._cfg.version-t._cfg.version}function ur(n,t,i,u,f,e){i.forEach(function(i){var o=w._tableFactory(u,f[i],t);n.forEach(function(n){n[i]||(e?Object.defineProperty(n,i,{configurable:!0,enumerable:!0,get:function(){var n=r.PSD&&r.PSD.trans;return n&&n.db===w?n.tables[i]:o}}):n[i]=o)})})}function br(n){n.forEach(function(n){for(var t in n)n[t]instanceof ei&&delete n[t]})}function pi(n,t,i,u,f,e){var s=r.PSD;e=e||nt;n.onerror||(n.onerror=o(f));n.onsuccess=t?k(function(){var r=n.result,o;r?(o=function(){r.continue()},t(r,function(n){o=n},u,f)&&i(e(r.value),r,function(n){o=n}),o()):u()},f,s):k(function(){var t=n.result,r;t?(r=function(){t.continue()},i(e(t.value),t,function(n){r=n}),r()):u()},f,s)}function kr(n){var t=[];return n.split(",").forEach(function(n){n=n.trim();var i=n.replace("&","").replace("++","").replace("*",""),r=i.indexOf("[")!==0?i:n.substring(n.indexOf("[")+1,n.indexOf("]")).split("+");t.push(new a(i,r||null,n.indexOf("&")!==-1,n.indexOf("*")!==-1,n.indexOf("++")!==-1,Array.isArray(r),r.indexOf(".")!==-1))}),t}function wi(n,t){return n<t?-1:n>t?1:0}function or(n,t){return n<t?1:n>t?-1:0}function sr(n){return function(t,i){for(var r=0,u;;){if(u=n(t[r],i[r]),u!==0)return u;if(++r,r===t.length||r===i.length)return n(t.length,i.length)}}}function bi(n,t){return n?t?function(){return n.apply(this,arguments)&&t.apply(this,arguments)}:n:t}function dr(){return navigator.userAgent.indexOf("Trident")>=0||navigator.userAgent.indexOf("MSIE")>=0}function gr(){if(w.verno=et.version/10,w._dbSchema=st={},ui=[].slice.call(et.objectStoreNames,0),ui.length!==0){var n=et.transaction(ft(ui),"readonly");ui.forEach(function(t){for(var u,s,r=n.objectStore(t),i=r.keyPath,f=i&&typeof i=="string"&&i.indexOf(".")!==-1,h=new a(i,i||"",!1,!1,!!r.autoIncrement,i&&typeof i!="string",f),o=[],e=0;e<r.indexNames.length;++e)u=r.index(r.indexNames[e]),i=u.keyPath,f=i&&typeof i=="string"&&i.indexOf(".")!==-1,s=new a(u.name,i,!!u.unique,!!u.multiEntry,!1,i&&typeof i!="string",f),o.push(s);st[t]=new ut(t,h,o,{})});ur([ni],w._transPromiseFactory,Object.keys(st),yt,st)}}function fr(n,t){for(var i,r,u,o,s=t.db.objectStoreNames,f=0;f<s.length;++f)for(i=s[f],r=t.objectStore(i),u=0;u<r.indexNames.length;++u){var h=r.indexNames[u],e=r.index(h).keyPath,c=typeof e=="string"?e:"["+[].slice.call(e).join("+")+"]";n[i]&&(o=n[i].idxByName[c],o&&(o.name=h))}}var hr=t&&t.addons||u.addons,oi=u.dependencies,ai=oi.indexedDB,kt=oi.IDBKeyRange,nu=oi.IDBTransaction,tu=oi.DOMError,yi=oi.TypeError,g=oi.Error,st=this._dbSchema={},ri=[],ui=[],ni={},ki={},et=null,si=!0,fi=null,vi=!1,ti="readonly",yt="readwrite",w=this,ii=[],hi=!1,er=!!ct();this.version=function(n){if(et)throw new g("Cannot add version when database is open");this.verno=Math.max(this.verno,n);var t=ri.filter(function(t){return t._cfg.version===n})[0];return t?t:(t=new di(n),ri.push(t),ri.sort(wr),t)};s(di.prototype,{stores:function(n){var i,t;return this._cfg.storesSource=this._cfg.storesSource?s(this._cfg.storesSource,n):n,i={},ri.forEach(function(n){s(i,n._cfg.storesSource)}),t=this._cfg.dbschema={},this._parseStoresSpec(i,t),st=w._dbSchema=t,br([ni,w,ki]),ur([ki],pr,Object.keys(t),yt,t),ur([ni,w,this._cfg.tables],w._transPromiseFactory,Object.keys(t),yt,t,!0),ui=Object.keys(t),this},upgrade:function(n){var t=this;return e(function(){n(w._createTransaction(yt,Object.keys(t._cfg.dbschema),t._cfg.dbschema))}),this._cfg.contentUpgrade=n,this},_parseStoresSpec:function(n,t){Object.keys(n).forEach(function(i){if(n[i]!==null){var u={},f=kr(n[i]),r=f.shift();if(r.multi)throw new g("Primary key cannot be multi-valued");r.keyPath&&r.auto&&h(u,r.keyPath,0);f.forEach(function(n){if(n.auto)throw new g("Only primary key can be marked as autoIncrement (++)");if(!n.keyPath)throw new g("Index must have a name and cannot be an empty string");h(u,n.keyPath,n.compound?n.keyPath.map(function(){return""}):"")});t[i]=new ut(i,r,f,u)}})}});this._allTables=ni;this._tableFactory=function(n,t,i){return n===ti?new ei(t.name,i,t,li):new tr(t.name,i,t)};this._createTransaction=function(n,t,i,r){return new ir(n,t,i,r)};this._transPromiseFactory=function(n,t,i){var f,u;return!si||r.PSD&&r.PSD.letThrough?(u=w._createTransaction(n,t,st),u._promise(n,function(n,t){u.error(function(n){w.on("error").fire(n)});i(function(t){u.complete(function(){n(t)})},t,u)})):f=new r(function(r,u){ii.push({resume:function(){var e=w._transPromiseFactory(n,t,i);f.onuncatched=e.onuncatched;e.then(r,u)}})})};this._whenReady=function(n){return si&&(!r.PSD||!r.PSD.letThrough)?new r(function(t,i){e(function(){new r(function(){n(t,i)})});ii.push({resume:function(){n(t,i)}})}):new r(n)};this.verno=0;this.open=function(){return new r(function(t,i){function f(n){try{u.transaction.abort()}catch(t){}vi=!1;fi=n;si=!1;i(fi);ii.forEach(function(n){n.resume()});ii=[]}if(et||vi)throw new g("Database already opened or being opened");var u,e=!1;try{if(fi=null,vi=!0,ri.length===0&&(hi=!0),!ai)throw new g("indexedDB API not found. If using IE10+, make sure to run your code on a server URL (not locally). If using Safari, make sure to include indexedDB polyfill.");u=hi?ai.open(n):ai.open(n,Math.round(w.verno*10));u.onerror=o(f,["opening database",n]);u.onblocked=function(n){w.on("blocked").fire(n)};u.onupgradeneeded=k(function(t){var i,r;hi&&!w._allowEmptyDB?(u.onerror=function(n){n.preventDefault()},u.transaction.abort(),u.result.close(),i=ai.deleteDatabase(n),i.onsuccess=i.onerror=function(){f(new g("Database '"+n+"' doesnt exist"))}):(t.oldVersion===0&&(e=!0),u.transaction.onerror=o(f),r=t.oldVersion>Math.pow(2,62)?0:t.oldVersion,lr(r/10,u.transaction,f,u))},f);u.onsuccess=k(function(){vi=!1;et=u.result;hi?gr():et.objectStoreNames.length>0&&fr(st,et.transaction(ft(et.objectStoreNames),ti));et.onversionchange=w.on("versionchange").fire;er||rt(function(t){if(t.indexOf(n)===-1)return t.push(n)});r.newPSD(function(){function i(){si=!1;ii.forEach(function(n){n.resume()});ii=[];t()}r.PSD.letThrough=!0;try{var n=w.on.ready.fire();n&&typeof n.then=="function"?n.then(i,function(n){et.close();et=null;f(n)}):b(i)}catch(u){f(u)}})},f)}catch(s){f(s)}})};this.close=function(){et&&(et.close(),et=null,si=!0,fi=null)};this.delete=function(){var t=arguments;return new r(function(i,r){function u(){w.close();var t=ai.deleteDatabase(n);t.onsuccess=function(){er||rt(function(t){var i=t.indexOf(n);if(i>=0)return t.splice(i,1)});i()};t.onerror=o(r,["deleting",n]);t.onblocked=function(){w.on("blocked").fire()}}if(t.length>0)throw new g("Arguments not allowed in db.delete()");vi?ii.push({resume:u}):u()})};this.backendDB=function(){return et};this.isOpen=function(){return et!==null};this.hasFailed=function(){return fi!==null};this.dynamicallyOpened=function(){return hi};this.name=n;Object.defineProperty(this,"tables",{get:function(){return Object.keys(ni).map(function(n){return ni[n]})}});this.on=v(this,"error","populate","blocked",{ready:[bt,f],versionchange:[pt,f]});this.on.ready.subscribe=p(this.on.ready.subscribe,function(n){return function(t,i){function r(){return i||w.on.ready.unsubscribe(r),t.apply(this,arguments)}n.call(this,r);w.isOpen()&&(si?ii.push({resume:r}):r())}});e(function(){w.on("populate").fire(w._createTransaction(yt,ui,st));w.on("error").fire(new g)});this.transaction=function(n,t,i){function s(t,e){var s=null,c,a,h;try{if(f)throw f;s=w._createTransaction(n,o,st,u);c=o.map(function(n){return s.tables[n]});c.push(s);h=0;r.newPSD(function(){r.PSD.trans=s;s.scopeFunc=i;u&&(s.idbtrans=u.idbtrans,s._promise=p(s._promise,function(n){return function(t,i,u){function f(n){return function(t){var i;return r._rootExec(function(){i=n(t);r._tickFinalize(function(){--h==0&&s.active&&(s.active=!1,s.on.complete.fire())})}),i}}return++h,n.call(this,t,function(n,t,r){return i(f(n),f(t),r)},u)}}));s.complete(function(){t(a)});s.error(function(n){s.idbtrans&&(s.idbtrans.onerror=ht);try{s.abort()}catch(i){}u&&(u.active=!1,u.on.error.fire(n));var t=e(n);u||t||w.on.error.fire(n)});r._rootExec(function(){a=i.apply(s,c)})});(!s.idbtrans||u&&h===0)&&s._nop()}catch(l){s&&s.idbtrans&&(s.idbtrans.onerror=ht);s&&s.abort();u&&u.on.error.fire(l);b(function(){e(l)||w.on("error").fire(l)})}}var u,e;t=[].slice.call(arguments,1,arguments.length-1);i=arguments[arguments.length-1];u=r.PSD&&r.PSD.trans;u&&u.db===w&&n.indexOf("!")===-1||(u=null);e=n.indexOf("?")!==-1;n=n.replace("!","").replace("?","");var h=Array.isArray(t[0])?t.reduce(function(n,t){return n.concat(t)}):t,f=null,o=h.map(function(n){return typeof n=="string"?n:(n instanceof ei||(f=f||new yi("Invalid type. Arguments following mode must be instances of Table or String")),n.name)});return n=="r"||n==ti?n=ti:n=="rw"||n==yt?n=yt:f=new g("Invalid transaction mode: "+n),u&&(f||(u&&u.mode===ti&&n===yt&&(e?u=null:f=f||new g("Cannot enter a sub-transaction with READWRITE mode when parent transaction is READONLY")),u&&o.forEach(function(n){u.tables.hasOwnProperty(n)||(e?u=null:f=f||new g("Table "+n+" not included in parent transaction. Parent Transaction function: "+u.scopeFunc.toString()))}))),u?u._promise(n,s,"lock"):w._whenReady(s)};this.table=function(n){if(!hi&&!ni.hasOwnProperty(n))throw new g("Table does not exist");return ni[n]};s(ei.prototype,function(){function n(){throw new g("Current Transaction is READONLY");}return{_trans:function(n,t,i){return this._tpf(n,[this.name],t,i)},_idbstore:function(n,t,i){var r=this;return this._tpf(n,[this.name],function(n,i,u){t(n,i,u.idbtrans.objectStore(r.name),u)},i)},get:function(n,t){var i=this;return e(function(){t(i.schema.instanceTemplate)}),this._idbstore(ti,function(t,r,u){var f=u.get(n);f.onerror=o(r,["getting",n,"from",i.name]);f.onsuccess=function(){t(i.hook.reading.fire(f.result))}}).then(t)},where:function(n){return new ci(this,n)},count:function(n){return this.toCollection().count(n)},offset:function(n){return this.toCollection().offset(n)},limit:function(n){return this.toCollection().limit(n)},reverse:function(){return this.toCollection().reverse()},filter:function(n){return this.toCollection().and(n)},each:function(n){var t=this;return e(function(){n(t.schema.instanceTemplate)}),this._idbstore(ti,function(i,r,u){var f=u.openCursor();f.onerror=o(r,["calling","Table.each()","on",t.name]);pi(f,null,n,i,r,t.hook.reading.fire)})},toArray:function(n){var t=this;return e(function(){n([t.schema.instanceTemplate])}),this._idbstore(ti,function(n,i,r){var u=[],f=r.openCursor();f.onerror=o(i,["calling","Table.toArray()","on",t.name]);pi(f,null,function(n){u.push(n)},function(){n(u)},i,t.hook.reading.fire)}).then(n)},orderBy:function(n){return new this._collClass(new ci(this,n))},toCollection:function(){return new this._collClass(new ci(this))},mapToClass:function(n,t){var i,r;return this.schema.mappedClass=n,i=Object.create(n.prototype),this.schema.primKey.keyPath&&(h(i,this.schema.primKey.keyPath,this.schema.primKey.auto?0:""),ot(n.prototype,this.schema.primKey.keyPath)),t&&it(i,t),this.schema.instanceTemplate=i,r=Object.setPrototypeOf?function(t){return t?(Object.setPrototypeOf(t,n.prototype),t):t}:function(t){var r,i;if(!t)return t;r=Object.create(n.prototype);for(i in t)t.hasOwnProperty(i)&&(r[i]=t[i]);return r},this.schema.readHook&&this.hook.reading.unsubscribe(this.schema.readHook),this.schema.readHook=r,this.hook("reading",r),n},defineClass:function(n){return this.mapToClass(u.defineClass(n),n)},add:n,put:n,"delete":n,clear:n,update:n}});y(tr).from(ei).extend(function(){return{add:function(n,t){var u=this,r=this.hook.creating.fire;return this._idbstore(yt,function(e,s,l,a){var v={},w,y,p;r!==f&&(w=t||(l.keyPath?c(n,l.keyPath):i),y=r.call(v,w,n,a),w===i&&y!==i&&(l.keyPath?h(n,l.keyPath,y):t=y));p=t?l.add(n,t):l.add(n);p.onerror=o(function(n){if(v.onerror)v.onerror(n);return s(n)},["adding",n,"into",u.name]);p.onsuccess=function(t){var i=l.keyPath;if(i&&h(n,i,t.target.result),v.onsuccess)v.onsuccess(t.target.result);e(p.result)}})},put:function(n,t){var r=this,u=this.hook.creating.fire,e=this.hook.updating.fire;return u!==f||e!==f?this._trans(yt,function(u,f,e){var o=t||r.schema.primKey.keyPath&&c(n,r.schema.primKey.keyPath);o===i?e.tables[r.name].add(n).then(u,f):(e._lock(),n=l(n),e.tables[r.name].where(":id").equals(o).modify(function(){this.value=n}).then(function(i){return i===0?e.tables[r.name].add(n,t):o}).finally(function(){e._unlock()}).then(u,f))}):this._idbstore(yt,function(i,u,f){var e=t?f.put(n,t):f.put(n);e.onerror=o(u,["putting",n,"into",r.name]);e.onsuccess=function(t){var r=f.keyPath;r&&h(n,r,t.target.result);i(e.result)}})},"delete":function(n){return this.hook.deleting.subscribers.length?this.where(":id").equals(n).delete():this._idbstore(yt,function(t,i,r){var u=r.delete(n);u.onerror=o(i,["deleting",n,"from",r.name]);u.onsuccess=function(){t(u.result)}})},clear:function(){return this.hook.deleting.subscribers.length?this.toCollection().delete():this._idbstore(yt,function(n,t,i){var r=i.clear();r.onerror=o(t,["clearing",i.name]);r.onsuccess=function(){n(r.result)}})},update:function(n,t){if(typeof t!="object"||Array.isArray(t))throw new g("db.update(keyOrObject, modifications). modifications must be an object.");if(typeof n!="object"||Array.isArray(n))return this.where(":id").equals(n).modify(t);Object.keys(t).forEach(function(i){h(n,i,t[i])});var u=c(n,this.schema.primKey.keyPath);return u===i&&r.reject(new g("Object does not contain its primary key")),this.where(":id").equals(u).modify(t)}}});s(ir.prototype,{_lock:function(){return++this._reculock,this._reculock===1&&r.PSD&&(r.PSD.lockOwnerFor=this),this},_unlock:function(){if(--this._reculock==0)for(r.PSD&&(r.PSD.lockOwnerFor=null);this._blockedFuncs.length>0&&!this._locked();){var n=this._blockedFuncs.shift();try{n()}catch(t){}}return this},_locked:function(){return this._reculock&&(!r.PSD||r.PSD.lockOwnerFor!==this)},_nop:function(n){this.tables[this.storeNames[0]].get(0).then(n)},_promise:function(n,t,i){var f=this;return r.newPSD(function(){var e;return f._locked()?e=new r(function(r,u){f._blockedFuncs.push(function(){f._promise(n,t,i).then(r,u)})}):(e=f.active?new r(function(r,e){if(!f.idbtrans&&n){if(!et)throw fi?new g("Database not open. Following error in populate, ready or upgrade function made Dexie.open() fail: "+fi):new g("Database not open");var o=f.idbtrans=et.transaction(ft(f.storeNames),f.mode);o.onerror=function(n){f.on("error").fire(n&&n.target.error);n.preventDefault();f.abort()};o.onabort=function(n){f.active=!1;f.on("abort").fire(n)};o.oncomplete=function(n){f.active=!1;f.on("complete").fire(n)}}i&&f._lock();try{t(r,e,f)}catch(s){u.ignoreTransaction(function(){f.on("error").fire(s)});f.abort();e(s)}}):r.reject(gt(new g("Transaction is inactive. Original Scope Function Source: "+f.scopeFunc.toString()))),f.active&&i&&e.finally(function(){f._unlock()})),e.onuncatched=function(n){u.ignoreTransaction(function(){f.on("error").fire(n)});f.abort()},e})},complete:function(n){return this.on("complete",n)},error:function(n){return this.on("error",n)},abort:function(){if(this.idbtrans&&this.active)try{this.active=!1;this.idbtrans.abort();this.on.error.fire(new g("Transaction Aborted"))}catch(n){}},table:function(n){if(!this.tables.hasOwnProperty(n))throw new g("Table "+n+" not in transaction");return this.tables[n]}});s(ci.prototype,function(){function n(n,t){try{throw t;}catch(i){n._ctx.error=i}return n}function i(n){return Array.prototype.slice.call(n.length===1&&Array.isArray(n[0])?n[0]:n)}function r(n){return n==="next"?function(n){return n.toUpperCase()}:function(n){return n.toLowerCase()}}function u(n){return n==="next"?function(n){return n.toLowerCase()}:function(n){return n.toUpperCase()}}function f(n,t,i,r,u,f){for(var h,s=Math.min(n.length,r.length),o=-1,e=0;e<s;++e){if(h=t[e],h!==r[e])return u(n[e],i[e])<0?n.substr(0,e)+i[e]+i.substr(e+1):u(n[e],r[e])<0?n.substr(0,e)+r[e]+i.substr(e+1):o>=0?n.substr(0,o)+t[o]+i.substr(o+1):null;u(n[e],h)<0&&(o=e)}return s<r.length&&f==="next"?n+i.substr(n.length):s<n.length&&f==="prev"?n.substr(0,i.length):o<0?null:n.substr(0,o)+r[o]+i.substr(o+1)}function t(n,t,i){function a(n){s=r(n);e=u(n);h=n==="next"?wi:or;c=s(i);o=e(i);l=n}var s,e,h,c,o,l;a("next");n._ondirectionchange=function(n){a(n)};n._addAlgorithm(function(n,i,r){var u=n.key,s,a;return typeof u!="string"?!1:(s=e(u),t(s,o)?(i(function(){n.continue()}),!0):(a=f(u,s,c,o,h,l),a?i(function(){n.continue(a)}):i(r),!1))})}return{between:function(n,t,i,r){return(i=i!==!1,r=r===!0,n>t||n===t&&(i||r)&&!(i&&r))?new this._ctx.collClass(this,function(){return kt.only(n)}).limit(0):new this._ctx.collClass(this,function(){return kt.bound(n,t,!i,!r)})},equals:function(n){return new this._ctx.collClass(this,function(){return kt.only(n)})},above:function(n){return new this._ctx.collClass(this,function(){return kt.lowerBound(n,!0)})},aboveOrEqual:function(n){return new this._ctx.collClass(this,function(){return kt.lowerBound(n)})},below:function(n){return new this._ctx.collClass(this,function(){return kt.upperBound(n,!0)})},belowOrEqual:function(n){return new this._ctx.collClass(this,function(){return kt.upperBound(n)})},startsWith:function(t){return typeof t!="string"?n(new this._ctx.collClass(this),new yi("String expected")):this.between(t,t+String.fromCharCode(65535),!0,!0)},startsWithIgnoreCase:function(i){if(typeof i!="string")return n(new this._ctx.collClass(this),new yi("String expected"));if(i==="")return this.startsWith(i);var r=new this._ctx.collClass(this,function(){return kt.bound(i.toUpperCase(),i.toLowerCase()+String.fromCharCode(65535))});return t(r,function(n,t){return n.indexOf(t)===0},i),r._ondirectionchange=function(){n(r,new g("reverse() not supported with WhereClause.startsWithIgnoreCase()"))},r},equalsIgnoreCase:function(i){if(typeof i!="string")return n(new this._ctx.collClass(this),new yi("String expected"));var r=new this._ctx.collClass(this,function(){return kt.bound(i.toUpperCase(),i.toLowerCase())});return t(r,function(n,t){return n===t},i),r},anyOf:function(){var f=this._ctx,e=f.table.schema,o=f.index?e.idxByName[f.index]:e.primKey,s=o&&o.compound,n=i(arguments),t=s?sr(wi):wi,u,r;return(n.sort(t),n.length===0)?new this._ctx.collClass(this,function(){return kt.only("")}).limit(0):(u=new this._ctx.collClass(this,function(){return kt.bound(n[0],n[n.length-1])}),u._ondirectionchange=function(i){t=i==="next"?wi:or;s&&(t=sr(t));n.sort(t)},r=0,u._addAlgorithm(function(i,u,f){for(var e=i.key;t(e,n[r])>0;)if(++r,r===n.length)return u(f),!1;return t(e,n[r])===0?(u(function(){i.continue()}),!0):(u(function(){i.continue(n[r])}),!1)}),u)}}});s(li.prototype,function(){function t(n,t){n.filter=bi(n.filter,t)}function f(n,t){n.isMatch=bi(n.isMatch,t)}function r(n,t){if(n.isPrimKey)return t;var i=n.table.schema.idxByName[n.index];if(!i)throw new g("KeyPath "+n.index+" on object store "+t.name+" is not indexed");return n.isPrimKey?t:t.index(i.name)}function u(n,t){return r(n,t)[n.op](n.range||null,n.dir+n.unique)}function i(n,t,i,r,f){n.or?function(){function e(){++c==2&&i()}function h(n,i,u){if(!o||o(i,u,e,r)){var f=i.primaryKey.toString();s.hasOwnProperty(f)||(s[f]=!0,t(n,i,u))}}var o=n.filter,s={},l=n.table.schema.primKey.keyPath,c=0;n.or._iterate(h,e,r,f);pi(u(n,f),n.algorithm,h,e,r,n.table.hook.reading.fire)}():pi(u(n,f),bi(n.algorithm,n.filter),t,i,r,n.table.hook.reading.fire)}function n(n){return n.table.schema.instanceTemplate}return{_read:function(n,t){var i=this._ctx;return i.error?i.table._trans(null,function(n,t){t(i.error)}):i.table._idbstore(ti,n).then(t)},_write:function(n){var t=this._ctx;return t.error?t.table._trans(null,function(n,i){i(t.error)}):t.table._idbstore(yt,n,"locked")},_addAlgorithm:function(n){var t=this._ctx;t.algorithm=bi(t.algorithm,n)},_iterate:function(n,t,r,u){return i(this._ctx,n,t,r,u)},each:function(t){var r=this._ctx;return e(function(){t(n(r))}),this._read(function(n,u,f){i(r,t,n,u,f)})},count:function(n){var f,t,u;return e(function(){n(0)}),f=this,t=this._ctx,t.filter||t.algorithm||t.or?(u=0,this._read(function(n,r,f){i(t,function(){return++u,!1},function(){n(u)},r,f)},n)):this._read(function(n,i,u){var e=r(t,u),s=t.range?e.count(t.range):e.count();s.onerror=o(i,["calling","count()","on",f.name]);s.onsuccess=function(i){n(Math.min(i.target.result,Math.max(0,t.limit-t.offset)))}},n)},sortBy:function(t,i){function u(n,t){return t?u(n[r[t]],t-1):n[h]}function c(n,t){var i=u(n,o),r=u(t,o);return i<r?-f:i>r?f:0}var s=this._ctx,f;e(function(){i([n(s)])});var r=t.split(".").reverse(),h=r[0],o=r.length-1;return f=this._ctx.dir==="next"?1:-1,this.toArray(function(n){return n.sort(c)}).then(i)},toArray:function(t){var r=this._ctx;return e(function(){t([n(r)])}),this._read(function(n,t,u){var f=[];i(r,function(n){f.push(n)},function(){n(f)},t,u)},t)},offset:function(n){var i=this._ctx;return n<=0?this:(i.offset+=n,i.or||i.algorithm||i.filter?t(i,function(){return--n<0}):t(i,function(t,i){return n===0?!0:n===1?(--n,!1):(i(function(){t.advance(n);n=0}),!1)}),this)},limit:function(n){return this._ctx.limit=Math.min(this._ctx.limit,n),t(this._ctx,function(t,i,r){return--n<=0&&i(r),n>=0}),this},until:function(i,r){var u=this._ctx;return e(function(){i(n(u))}),t(this._ctx,function(n,t,u){return i(n.value)?(t(u),r):!0}),this},first:function(t){var i=this;return e(function(){t(n(i._ctx))}),this.limit(1).toArray(function(n){return n[0]}).then(t)},last:function(n){return this.reverse().first(n)},and:function(i){var r=this;return e(function(){i(n(r._ctx))}),t(this._ctx,function(n){return i(n.value)}),f(this._ctx,i),this},or:function(n){return new ci(this._ctx.table,n,this)},reverse:function(){return this._ctx.dir=this._ctx.dir==="prev"?"next":"prev",this._ondirectionchange&&this._ondirectionchange(this._ctx.dir),this},desc:function(){return this.reverse()},eachKey:function(t){var i=this,r=this._ctx;return e(function(){t(n(i._ctx)[i._ctx.index])}),r.isPrimKey||(r.op="openKeyCursor"),this.each(function(n,i){t(i.key,i)})},eachUniqueKey:function(n){return this._ctx.unique="unique",this.eachKey(n)},keys:function(t){var u,i,r;return e(function(){t([n(i)[u._ctx.index]])}),u=this,i=this._ctx,i.isPrimKey||(i.op="openKeyCursor"),r=[],this.each(function(n,t){r.push(t.key)}).then(function(){return r}).then(t)},uniqueKeys:function(n){return this._ctx.unique="unique",this.keys(n)},firstKey:function(n){var t=this;return this.limit(1).keys(function(n){return n[0]}).then(n)},lastKey:function(n){return this.reverse().firstKey(n)},distinct:function(){var n={};return t(this._ctx,function(t){var i=t.primaryKey.toString(),r=n.hasOwnProperty(i);return n[i]=!0,!r}),this}}});y(rr).from(li).extend({modify:function(n){var a=this,t=this._ctx,r=t.table.hook,i=r.updating.fire,u=r.deleting.fire;return e(function(){typeof n=="function"&&n.call({value:t.table.schema.instanceTemplate},t.table.schema.instanceTemplate)}),this._write(function(r,e,v,y){function st(n,i){var r,u,f;if(et=i.primaryKey,r={primKey:i.primaryKey,value:n},w.call(r,n)!==!1)u=!r.hasOwnProperty("value"),f=u?i.delete():i.update(r.value),++ut,f.onerror=o(function(n){if(p.push(n),nt.push(r.primKey),r.onerror)r.onerror(n);return it(),!0},u?["deleting",n,"from",t.table.name]:["modifying",n,"on",t.table.name]),f.onsuccess=function(){if(r.onsuccess)r.onsuccess(r.value);++b;it()};else if(r.onsuccess)r.onsuccess(r.value)}function ot(n){return n&&(p.push(n),nt.push(et)),e(new d("Error modifying one or more objects",p,b,nt))}function it(){ft&&b+p.length===ut&&(p.length>0?ot():r(b))}var w,k,rt,g;typeof n=="function"?w=i===f&&u===f?n:function(t){var f=l(t),e,r;if(n.call(this,t)===!1)return!1;this.hasOwnProperty("value")?(e=dt(f,this.value),r=i.call(this,e,this.primKey,f,y),r&&(t=this.value,Object.keys(r).forEach(function(n){h(t,n,r[n])}))):u.call(this,this.primKey,t,y)}:i===f?(k=Object.keys(n),rt=k.length,w=function(t){for(var i,u,f=!1,r=0;r<rt;++r)i=k[r],u=n[i],c(t,i)!==u&&(h(t,i,u),f=!0);return f}):(g=n,n=tt(g),w=function(t){var u=!1,r=i.call(this,n,this.primKey,l(t),y);return r&&s(n,r),Object.keys(n).forEach(function(i){var r=n[i];c(t,i)!==r&&(h(t,i,r),u=!0)}),r&&(n=tt(g)),u});var ut=0,b=0,ft=!1,p=[],nt=[],et=null;a._iterate(st,function(){ft=!0;it()},ot,v)})},"delete":function(){return this.modify(function(){delete this.value})}});s(this,{Collection:li,Table:ei,Transaction:ir,Version:di,WhereClause:ci,WriteableCollection:rr,WriteableTable:tr});cr();hr.forEach(function(n){n(w)})}function f(){}function nt(n){return n}function lt(n,t){return n===nt?t:function(i){return t(n(i))}}function w(n,t){return function(){n.apply(this,arguments);t.apply(this,arguments)}}function at(n,t){return n===f?t:function(){var f=n.apply(this,arguments),r,u,e;return f!==i&&(arguments[0]=f),r=this.onsuccess,u=this.onerror,delete this.onsuccess,delete this.onerror,e=t.apply(this,arguments),r&&(this.onsuccess=this.onsuccess?w(r,this.onsuccess):r),u&&(this.onerror=this.onerror?w(u,this.onerror):u),e!==i?e:f}}function vt(n,t){return n===f?t:function(){var r=n.apply(this,arguments),f,e,u;return r!==i&&s(arguments[0],r),f=this.onsuccess,e=this.onerror,delete this.onsuccess,delete this.onerror,u=t.apply(this,arguments),f&&(this.onsuccess=this.onsuccess?w(f,this.onsuccess):f),e&&(this.onerror=this.onerror?w(e,this.onerror):e),r===i?u===i?i:u:u===i?r:s(r,u)}}function yt(n,t){return n===f?t:function(){return n.apply(this,arguments)===!1?!1:t.apply(this,arguments)}}function pt(n,t){return n===f?t:function(){return t.apply(this,arguments)===!1?!1:n.apply(this,arguments)}}function wt(n,t){return n===f?t:function(){n.apply(this,arguments);t.apply(this,arguments)}}function bt(n,t){return n===f?t:function(){var i=n.apply(this,arguments),r,u;return i&&typeof i.then=="function"?(r=this,u=arguments,i.then(function(){return t.apply(r,u)})):t.apply(this,arguments)}}function v(t){function i(n,t,i){if(Array.isArray(n))return c(n);if(typeof n=="object")return h(n);t||(t=yt);i||(i=f);var r={subscribers:[],fire:i,subscribe:function(n){r.subscribers.push(n);r.fire=t(r.fire,n)},unsubscribe:function(n){r.subscribers=r.subscribers.filter(function(t){return t!==n});r.fire=r.subscribers.reduce(t,i)}};return u[n]=e[n]=r,r}function h(t){Object.keys(t).forEach(function(r){var f=t[r],u;if(Array.isArray(f))i(r,t[r][0],t[r][1]);else if(f==="asap")u=i(r,null,function(){var t=arguments;u.subscribers.forEach(function(i){b(function(){i.apply(n,t)})})}),u.subscribe=function(n){u.subscribers.indexOf(n)===-1&&u.subscribers.push(n)},u.unsubscribe=function(n){var t=u.subscribers.indexOf(n);t!==-1&&u.subscribers.splice(t,1)};else throw new Error("Invalid event config");})}function c(n){function r(){if(t)return!1;t=!0}var t=!1;n.forEach(function(n){i(n).subscribe(r)})}var o=arguments,u={},e=function(n,i){if(i){var f=[].slice.call(arguments,1),r=u[n];return r.subscribe.apply(r,f),t}if(typeof n=="string")return u[n]},r,s;for(e.addEventType=i,r=1,s=o.length;r<s;++r)i(o[r]);return e}function kt(n){if(!n)throw new Error("Assertion failed");}function b(t){n.setImmediate?setImmediate(t):setTimeout(t,0)}function et(n){var t=setTimeout(n,1e3);clearTimeout(t)}function k(n,t,i){return function(){var u=r.PSD;r.PSD=i;try{n.apply(this,arguments)}catch(f){t(f)}finally{r.PSD=u}}}function c(n,t){var f,r,o,s,u,e;if(n.hasOwnProperty(t))return n[t];if(!t)return n;if(typeof t!="string"){for(f=[],r=0,o=t.length;r<o;++r)s=c(n,t[r]),f.push(s);return f}return(u=t.indexOf("."),u!==-1)?(e=n[t.substr(0,u)],e===i?i:c(e,t.substr(u+1))):i}function h(n,t,r){var u,c,e,f,s,o;if(n&&t!==i)if(typeof t!="string"&&"length"in t)for(kt(typeof r!="string"&&"length"in r),u=0,c=t.length;u<c;++u)h(n,t[u],r[u]);else e=t.indexOf("."),e!==-1?(f=t.substr(0,e),s=t.substr(e+1),s===""?r===i?delete n[f]:n[f]=r:(o=n[f],o||(o=n[f]={}),h(o,s,r))):r===i?delete n[t]:n[t]=r}function ot(n,t){h(n,t,i)}function tt(n){var i={};for(var t in n)n.hasOwnProperty(t)&&(i[t]=n[t]);return i}function l(n){var t,i,u,r;if(!n||typeof n!="object")return n;if(Array.isArray(n))for(t=[],i=0,u=n.length;i<u;++i)t.push(l(n[i]));else if(n instanceof Date)t=new Date,t.setTime(n.getTime());else{t=n.constructor?Object.create(n.constructor.prototype):{};for(r in n)n.hasOwnProperty(r)&&(t[r]=l(n[r]))}return t}function dt(n,t){var u={};for(var r in n)n.hasOwnProperty(r)&&(t.hasOwnProperty(r)?n[r]!==t[r]&&JSON.stringify(n[r])!=JSON.stringify(t[r])&&(u[r]=t[r]):u[r]=i);for(r in t)t.hasOwnProperty(r)&&!n.hasOwnProperty(r)&&(u[r]=t[r]);return u}function st(n){if(typeof n=="function")return new n;if(Array.isArray(n))return[st(n[0])];if(n&&typeof n=="object"){var t={};return it(t,n),t}return n}function it(n,t){Object.keys(t).forEach(function(i){var r=st(t[i]);n[i]=r})}function o(n,t){return function(i){var r=i&&i.target.error||new Error,u;return t&&(u=" occurred when "+t.map(function(n){switch(typeof n){case"function":return n();case"string":return n;default:return JSON.stringify(n)}}).join(" "),r.name?r.toString=function(){return r.name+u+(r.message?". "+r.message:"")}:r=r+u),n(r),i&&(i.stopPropagation&&i.stopPropagation(),i.preventDefault&&i.preventDefault()),!1}}function gt(n){try{throw n;}catch(t){return t}}function ht(n){n.preventDefault()}function rt(n){var t,i=u.dependencies.localStorage;if(!i)return n([]);try{t=JSON.parse(i.getItem("Dexie.DatabaseNames")||"[]")}catch(r){t=[]}n(t)&&i.setItem("Dexie.DatabaseNames",JSON.stringify(t))}function a(n,t,i,r,u,f,e){this.name=n;this.keyPath=t;this.unique=i;this.multi=r;this.auto=u;this.compound=f;this.dotted=e;var o=typeof t=="string"?t:t&&"["+[].join.call(t,"+")+"]";this.src=(i?"&":"")+(r?"*":"")+(u?"++":"")+o}function ut(n,t,i,r){this.name=n;this.primKey=t||new a;this.indexes=i||[new a];this.instanceTemplate=r;this.mappedClass=null;this.idxByName=i.reduce(function(n,t){return n[t.name]=t,n},{})}function d(n,t,i,r){this.name="ModifyError";this.failures=t;this.failedKeys=r;this.successCount=i;this.message=t.join("\n")}function ft(n){return n.length===1?n[0]:n}function ct(){var n=u.dependencies.indexedDB,t=n&&(n.getDatabaseNames||n.webkitGetDatabaseNames);return t&&t.bind(n)}var r=function(){function y(n){u.push([n,a.call(arguments,1)])}function p(){var r=u,t,f,i;for(u=[],t=0,f=r.length;t<f;++t)i=r[t],i[0].apply(n,i[1])}function t(n){if(typeof this!="object")throw new TypeError("Promises must be constructed via new");if(typeof n!="function")throw new TypeError("not a function");this._state=null;this._value=null;this._deferreds=[];this._catched=!1;var r=this,u=!0;this._PSD=t.PSD;try{k(this,n,function(n){u?i(c,r,n):c(r,n)},function(n){return u?(i(s,r,n),!1):s(r,n)})}finally{u=!1}}function o(n,f){var s,o,l,a,b,c;if(n._state===null){n._deferreds.push(f);return}if(s=n._state?f.onFulfilled:f.onRejected,s===null)return(n._state?f.resolve:f.reject)(n._value);l=r;r=!1;i=y;try{a=t.PSD;t.PSD=n._PSD;o=s(n._value);n._state||o&&typeof o.then=="function"&&o._state===!1||w(n);f.resolve(o)}catch(v){if(b=f.reject(v),!b&&n.onuncatched)try{n.onuncatched(v)}catch(v){}}finally{if(t.PSD=a,l){do{while(u.length>0)p();if(c=e.pop(),c)try{c()}catch(v){}}while(e.length>0||u.length>0);i=h;r=!0}}}function d(n){var f=r,t;r=!1;i=y;try{n()}finally{if(f){do{while(u.length>0)p();if(t=e.pop(),t)try{t()}catch(o){}}while(e.length>0||u.length>0);i=h;r=!0}}}function w(n){n._catched=!0;n._parent&&w(n._parent)}function c(n,i){var r=t.PSD;t.PSD=n._PSD;try{if(i===n)throw new TypeError("A promise cannot be resolved with itself.");if(i&&(typeof i=="object"||typeof i=="function")&&typeof i.then=="function"){k(n,function(n,t){i.then(n,t)},function(t){c(n,t)},function(t){s(n,t)});return}n._state=!0;n._value=i;b.call(n)}catch(u){s(u)}finally{t.PSD=r}}function s(n,i){var r=t.PSD;if(t.PSD=n._PSD,n._state=!1,n._value=i,b.call(n),!n._catched)try{if(n.onuncatched)n.onuncatched(n._value);t.on.error.fire(n._value)}catch(u){}return t.PSD=r,n._catched}function b(){for(var n=0,t=this._deferreds.length;n<t;n++)o(this,this._deferreds[n]);this._deferreds=[]}function l(n,t,i,r){this.onFulfilled=typeof n=="function"?n:null;this.onRejected=typeof t=="function"?t:null;this.resolve=i;this.reject=r}function k(n,t,i,r){var u=!1;try{t(function(n){u||(u=!0,i(n))},function(t){return u?n._catched:(u=!0,r(t))})}catch(f){return u?void 0:r(f)}}var a=[].slice,h=typeof setImmediate=="undefined"?function(t){var i=arguments;setTimeout(function(){t.apply(n,a.call(i,1))},0)}:setImmediate,i=h,r=!0,u=[],e=[];return t.on=v(null,"error"),t.all=function(){var n=Array.prototype.slice.call(arguments.length===1&&Array.isArray(arguments[0])?arguments[0]:arguments);return new t(function(t,i){function f(r,e){try{if(e&&(typeof e=="object"||typeof e=="function")){var o=e.then;if(typeof o=="function"){o.call(e,function(n){f(r,n)},i);return}}n[r]=e;--u==0&&t(n)}catch(s){i(s)}}var u,r;if(n.length===0)return t([]);for(u=n.length,r=0;r<n.length;r++)f(r,n[r])})},t.prototype.then=function(n,r){var f=this,u=new t(function(t,u){f._state===null?o(f,new l(n,r,t,u)):i(o,f,new l(n,r,t,u))});return u._PSD=this._PSD,u.onuncatched=this.onuncatched,u._parent=this,u},t.prototype._then=function(n,t){o(this,new l(n,t,f,f))},t.prototype["catch"]=function(n){if(arguments.length===1)return this.then(null,n);var i=arguments[0],r=arguments[1];return typeof i=="function"?this.then(null,function(n){return n instanceof i?r(n):t.reject(n)}):this.then(null,function(n){return n&&n.name===i?r(n):t.reject(n)})},t.prototype["finally"]=function(n){return this.then(function(t){return n(),t},function(i){return n(),t.reject(i)})},t.prototype.onuncatched=null,t.resolve=function(n){var i=new t(function(){});return i._state=!0,i._value=n,i},t.reject=function(n){var i=new t(function(){});return i._state=!1,i._value=n,i},t.race=function(n){return new t(function(t,i){n.map(function(n){n.then(t,i)})})},t.PSD=null,t.newPSD=function(n){var i=t.PSD;t.PSD=i?Object.create(i):{};try{return n()}finally{t.PSD=i}},t._rootExec=d,t._tickFinalize=function(n){if(r)throw new Error("Not in a virtual tick");e.push(n)},t}(),e=function(){},g;y(d).from(Error);u.delete=function(n){var t=new u(n),i=t.delete();return i.onblocked=function(n){t.on("blocked",n);return this},i};u.getDatabaseNames=function(n){return new r(function(n,t){var r=ct(),i;r?(i=r(),i.onsuccess=function(t){n([].slice.call(t.target.result,0))},i.onerror=o(t)):rt(function(t){return n(t),!1})}).then(n)};u.defineClass=function(n){function t(n){n&&s(this,n)}return it(t.prototype,n),t};u.ignoreTransaction=function(n){return r.newPSD(function(){return r.PSD.trans=null,n()})};u.spawn=function(){return n.console&&console.warn("Dexie.spawn() is deprecated. Use Dexie.ignoreTransaction() instead."),u.ignoreTransaction.apply(this,arguments)};u.vip=function(n){return r.newPSD(function(){return r.PSD.letThrough=!0,n()})};Object.defineProperty(u,"currentTransaction",{get:function(){return r.PSD&&r.PSD.trans||null}});u.Promise=r;u.derive=y;u.extend=s;u.override=p;u.events=v;u.getByKeyPath=c;u.setByKeyPath=h;u.delByKeyPath=ot;u.shallowClone=tt;u.deepClone=l;u.addons=[];u.fakeAutoComplete=e;u.asap=b;u.ModifyError=d;u.MultiModifyError=d;u.IndexSpec=a;u.TableSchema=ut;g=n.idbModules&&n.idbModules.shimIndexedDB?n.idbModules:{};u.dependencies={indexedDB:g.shimIndexedDB||n.indexedDB||n.mozIndexedDB||n.webkitIndexedDB||n.msIndexedDB,IDBKeyRange:g.IDBKeyRange||n.IDBKeyRange||n.webkitIDBKeyRange,IDBTransaction:g.IDBTransaction||n.IDBTransaction||n.webkitIDBTransaction,Error:n.Error||String,SyntaxError:n.SyntaxError||String,TypeError:n.TypeError||String,DOMError:n.DOMError||String,localStorage:(typeof chrome!="undefined"&&chrome!==null?chrome.storage:void 0)!=null?null:n.localStorage};u.version=1.1;t("Dexie",u);et(function(){e=et})}).apply(null,typeof define=="function"&&define.amd?[self||window,function(n,t){define(n,function(){return t})}]:typeof global!="undefined"&&typeof module!="undefined"&&module.exports?[global,function(n,t){module.exports=t}]:[self||window,function(n,t){(self||window)[n]=t}]);
+//# sourceMappingURL=Dexie.min.js.map
diff --git a/gui/html/js/ndn-js.min.js b/gui/html/js/ndn-js.min.js
index 092e995..c73b511 100644
--- a/gui/html/js/ndn-js.min.js
+++ b/gui/html/js/ndn-js.min.js
@@ -1,224 +1,165 @@
-var Closure=function(){this.ndn_data=null;this.ndn_data_dirty=!1};Closure.RESULT_ERR=-1;Closure.RESULT_OK=0;Closure.RESULT_REEXPRESS=1;Closure.RESULT_INTEREST_CONSUMED=2;Closure.RESULT_VERIFY=3;Closure.RESULT_FETCHKEY=4;Closure.UPCALL_FINAL=0;Closure.UPCALL_INTEREST=1;Closure.UPCALL_CONSUMED_INTEREST=2;Closure.UPCALL_CONTENT=3;Closure.UPCALL_INTEREST_TIMED_OUT=4;Closure.UPCALL_CONTENT_UNVERIFIED=5;Closure.UPCALL_CONTENT_BAD=6;Closure.prototype.upcall=function(){return Closure.RESULT_OK};
-var UpcallInfo=function(a,b,c,d){this.ndn=a;this.interest=b;this.matchedComps=c;this.contentObject=d};UpcallInfo.prototype.toString=function(){var a="ndn = "+this.ndn,a=a+("\nInterest = "+this.interest),a=a+("\nmatchedComps = "+this.matchedComps);return a+="\nContentObject: "+this.contentObject};
-var WebSocketTransport=function(){if(!WebSocket)throw Error("WebSocket support is not available on this platform.");this.elementReader=this.connectedPort=this.connectedHost=this.ws=null;this.defaultGetHostAndPort=NDN.makeShuffledGetHostAndPort(["A.ws.ndn.ucla.edu","B.ws.ndn.ucla.edu","C.ws.ndn.ucla.edu","D.ws.ndn.ucla.edu","E.ws.ndn.ucla.edu"],9696)};
-WebSocketTransport.prototype.connect=function(a,b){null!=this.ws&&delete this.ws;this.ws=new WebSocket("ws://"+a.host+":"+a.port);0<LOG&&console.log("ws connection created.");this.connectedHost=a.host;this.connectedPort=a.port;this.ws.binaryType="arraybuffer";this.elementReader=new BinaryXmlElementReader(a);var c=this;this.ws.onmessage=function(a){a=a.data;if(null==a||void 0==a||""==a)console.log("INVALID ANSWER");else if(a instanceof ArrayBuffer){a=new Uint8Array(a);3<LOG&&console.log("BINARY RESPONSE IS "+
-DataUtils.toHex(a));try{c.elementReader.onReceivedData(a)}catch(b){console.log("NDN.ws.onmessage exception: "+b)}}};this.ws.onopen=function(a){3<LOG&&console.log(a);3<LOG&&console.log("ws.onopen: WebSocket connection opened.");3<LOG&&console.log("ws.onopen: ReadyState: "+this.readyState);b()};this.ws.onerror=function(a){console.log("ws.onerror: ReadyState: "+this.readyState);console.log(a);console.log("ws.onerror: WebSocket error: "+a.data)};this.ws.onclose=function(){console.log("ws.onclose: WebSocket connection closed.");
-c.ws=null;a.readyStatus=NDN.CLOSED;a.onclose()}};WebSocketTransport.prototype.send=function(a){if(null!=this.ws){var b=new Uint8Array(a.length);b.set(a);this.ws.send(b.buffer);3<LOG&&console.log("ws.send() returned.")}else console.log("WebSocket connection is not established.")};
-var NDNProtocolDTags={Any:13,Name:14,Component:15,Certificate:16,Collection:17,CompleteName:18,Content:19,SignedInfo:20,ContentDigest:21,ContentHash:22,Count:24,Header:25,Interest:26,Key:27,KeyLocator:28,KeyName:29,Length:30,Link:31,LinkAuthenticator:32,NameComponentCount:33,RootDigest:36,Signature:37,Start:38,Timestamp:39,Type:40,Nonce:41,Scope:42,Exclude:43,Bloom:44,BloomSeed:45,AnswerOriginKind:47,InterestLifetime:48,Witness:53,SignatureBits:54,DigestAlgorithm:55,BlockSize:56,FreshnessSeconds:58,
-FinalBlockID:59,PublisherPublicKeyDigest:60,PublisherCertificateDigest:61,PublisherIssuerKeyDigest:62,PublisherIssuerCertificateDigest:63,ContentObject:64,WrappedKey:65,WrappingKeyIdentifier:66,WrapAlgorithm:67,KeyAlgorithm:68,Label:69,EncryptedKey:70,EncryptedNonceKey:71,WrappingKeyName:72,Action:73,FaceID:74,IPProto:75,Host:76,Port:77,MulticastInterface:78,ForwardingFlags:79,FaceInstance:80,ForwardingEntry:81,MulticastTTL:82,MinSuffixComponents:83,MaxSuffixComponents:84,ChildSelector:85,RepositoryInfo:86,
-Version:87,RepositoryVersion:88,GlobalPrefix:89,LocalName:90,Policy:91,Namespace:92,GlobalPrefixName:93,PolicyVersion:94,KeyValueSet:95,KeyValuePair:96,IntegerValue:97,DecimalValue:98,StringValue:99,BinaryValue:100,NameValue:101,Entry:102,ACL:103,ParameterizedName:104,Prefix:105,Suffix:106,Root:107,ProfileName:108,Parameters:109,InfoString:110,StatusResponse:112,StatusCode:113,StatusText:114,SyncNode:115,SyncNodeKind:116,SyncNodeElement:117,SyncVersion:118,SyncNodeElements:119,SyncContentHash:120,
-SyncLeafCount:121,SyncTreeDepth:122,SyncByteCount:123,ConfigSlice:124,ConfigSliceList:125,ConfigSliceOp:126,NDNProtocolDataUnit:17702112,NDNPROTOCOL_DATA_UNIT:"NDNProtocolDataUnit"},NDNProtocolDTagsStrings=[null,null,null,null,null,null,null,null,null,null,null,null,null,"Any","Name","Component","Certificate","Collection","CompleteName","Content","SignedInfo","ContentDigest","ContentHash",null,"Count","Header","Interest","Key","KeyLocator","KeyName","Length","Link","LinkAuthenticator","NameComponentCount",
-null,null,"RootDigest","Signature","Start","Timestamp","Type","Nonce","Scope","Exclude","Bloom","BloomSeed",null,"AnswerOriginKind","InterestLifetime",null,null,null,null,"Witness","SignatureBits","DigestAlgorithm","BlockSize",null,"FreshnessSeconds","FinalBlockID","PublisherPublicKeyDigest","PublisherCertificateDigest","PublisherIssuerKeyDigest","PublisherIssuerCertificateDigest","ContentObject","WrappedKey","WrappingKeyIdentifier","WrapAlgorithm","KeyAlgorithm","Label","EncryptedKey","EncryptedNonceKey",
-"WrappingKeyName","Action","FaceID","IPProto","Host","Port","MulticastInterface","ForwardingFlags","FaceInstance","ForwardingEntry","MulticastTTL","MinSuffixComponents","MaxSuffixComponents","ChildSelector","RepositoryInfo","Version","RepositoryVersion","GlobalPrefix","LocalName","Policy","Namespace","GlobalPrefixName","PolicyVersion","KeyValueSet","KeyValuePair","IntegerValue","DecimalValue","StringValue","BinaryValue","NameValue","Entry","ACL","ParameterizedName","Prefix","Suffix","Root","ProfileName",
-"Parameters","InfoString",null,"StatusResponse","StatusCode","StatusText","SyncNode","SyncNodeKind","SyncNodeElement","SyncVersion","SyncNodeElements","SyncContentHash","SyncLeafCount","SyncTreeDepth","SyncByteCount","ConfigSlice","ConfigSliceList","ConfigSliceOp"],NDNTime=function(a){this.NANOS_MAX=999877929;"number"==typeof a?this.msec=a:1<LOG&&console.log("UNRECOGNIZED TYPE FOR TIME")};NDNTime.prototype.getJavascriptDate=function(){var a=new Date;a.setTime(this.msec);return a};
-var ExponentialReExpressClosure=function(a,b){Closure.call(this);this.callerClosure=a;b=b||{};this.maxInterestLifetime=b.maxInterestLifetime||16E3};
-ExponentialReExpressClosure.prototype.upcall=function(a,b){try{if(a==Closure.UPCALL_INTEREST_TIMED_OUT){var c=b.interest.interestLifetime;if(null==c)return this.callerClosure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT,b);c*=2;if(c>this.maxInterestLifetime)return this.callerClosure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT,b);var d=b.interest.clone();d.interestLifetime=c;b.ndn.expressInterest(d.name,this,d);return Closure.RESULT_OK}return this.callerClosure.upcall(a,b)}catch(e){return console.log("ExponentialReExpressClosure.upcall exception: "+
-e),Closure.RESULT_ERR}};var Name=function Name(b){if("string"==typeof b)3<LOG&&console.log("Content Name String "+b),this.components=Name.createNameArray(b);else if("object"===typeof b)if(this.components=[],b instanceof Name)this.add(b);else for(var c=0;c<b.length;++c)this.add(b[c]);else null==b?this.components=[]:1<LOG&&console.log("NO CONTENT NAME GIVEN")};Name.prototype.getName=function(){return this.to_uri()};
-Name.createNameArray=function(a){a=a.trim();if(0>=a.length)return[];var b=a.indexOf(":");if(0<=b){var c=a.indexOf("/");if(0>c||b<c)a=a.substr(b+1,a.length-b-1).trim()}if("/"==a[0])if(2<=a.length&&"/"==a[1]){b=a.indexOf("/",2);if(0>b)return[];a=a.substr(b+1,a.length-b-1).trim()}else a=a.substr(1,a.length-1).trim();a=a.split("/");for(b=0;b<a.length;++b)c=Name.fromEscapedString(a[b]),null==c?(a.splice(b,1),--b):a[b]=c;return a};
-Name.prototype.from_ndnb=function(a){a.readStartElement(this.getElementLabel());for(this.components=[];a.peekStartElement(NDNProtocolDTags.Component);)this.add(a.readBinaryElement(NDNProtocolDTags.Component));a.readEndElement()};Name.prototype.to_ndnb=function(a){if(null==this.components)throw Error("CANNOT ENCODE EMPTY CONTENT NAME");a.writeStartElement(this.getElementLabel());for(var b=this.components.length,c=0;c<b;c++)a.writeElement(NDNProtocolDTags.Component,this.components[c]);a.writeEndElement()};
-Name.prototype.getElementLabel=function(){return NDNProtocolDTags.Name};
-Name.prototype.add=function(a){var b;if("string"==typeof a)b=DataUtils.stringToUtf8Array(a);else if("object"==typeof a&&a instanceof Uint8Array)b=new Uint8Array(a);else if("object"==typeof a&&a instanceof ArrayBuffer)b=new Uint8Array(new ArrayBuffer(a.byteLength)),b.set(new Uint8Array(a));else{if("object"==typeof a&&a instanceof Name){a=a==this?this.components.slice(0,this.components.length):a.components;for(b=0;b<a.length;++b)this.components.push(new Uint8Array(a[b]));return this}if("object"==typeof a)b=
-new Uint8Array(a);else throw Error("Cannot add Name element at index "+this.components.length+": Invalid type");}this.components.push(b);return this};Name.prototype.addSegment=function(a){for(var b=1,c=a;0<c;)b++,c>>=8;b=new Uint8Array(b);c=0;b[c]=0;for(c++;0<a;)b[c]=a&255,a>>=8,c++;this.components.push(b);return this};Name.prototype.to_uri=function(){if(0==this.components.length)return"/";for(var a="",b=0;b<this.components.length;++b)a+="/"+Name.toEscapedString(this.components[b]);return a};
-Name.prototype.addSegment=function(a){a=DataUtils.nonNegativeIntToBigEndian(a);var b=new Uint8Array(a.length+1);b.set(a,1);this.components.push(b);return this};Name.prototype.getPrefix=function(a){return new Name(this.components.slice(0,a))};Name.prototype.cut=function(){return new Name(this.components.slice(0,this.components.length-1))};Name.prototype.getComponent=function(a){var b=new ArrayBuffer(this.components[a].length);(new Uint8Array(b)).set(this.components[a]);return b};
-Name.prototype.indexOfFileName=function(){for(var a=this.components.length-1;0<=a;--a){var b=this.components[a];if(!(0>=b.length)&&!(0==b[0]||192==b[0]||193==b[0]||245<=b[0]&&255>=b[0]))return a}return-1};Name.prototype.equalsName=function(a){if(this.components.length!=a.components.length)return!1;for(var b=this.components.length-1;0<=b;--b)if(!DataUtils.arraysEqual(this.components[b],a.components[b]))return!1;return!0};
-Name.prototype.getContentDigestValue=function(){for(var a=this.components.length-1;0<=a;--a){var b=Name.getComponentContentDigestValue(this.components[a]);if(null!=b)return b}return null};
-Name.getComponentContentDigestValue=function(a){return a.length==Name.ContentDigestPrefix.length+32+Name.ContentDigestSuffix.length&&DataUtils.arraysEqual(a.subarray(0,Name.ContentDigestPrefix.length),Name.ContentDigestPrefix)&&DataUtils.arraysEqual(a.subarray(a.length-Name.ContentDigestSuffix.length,a.length),Name.ContentDigestSuffix)?a.subarray(Name.ContentDigestPrefix.length,Name.ContentDigestPrefix.length+32):null};Name.ContentDigestPrefix=new Uint8Array([193,46,77,46,71,193,1,170,2,133]);
-Name.ContentDigestSuffix=new Uint8Array([0]);Name.toEscapedString=function(a){for(var b="",c=!1,d=0;d<a.length;++d)if(46!=a[d]){c=!0;break}if(c)for(d=0;d<a.length;++d)c=a[d],b=48<=c&&57>=c||65<=c&&90>=c||97<=c&&122>=c||43==c||45==c||46==c||95==c?b+String.fromCharCode(c):b+("%"+(16>c?"0":"")+c.toString(16).toUpperCase());else{b="...";for(d=0;d<a.length;++d)b+="."}return b};
-Name.fromEscapedString=function(a){a=unescape(a.trim());return null==a.match(/[^.]/)?2>=a.length?null:DataUtils.toNumbersFromString(a.substr(3,a.length-3)):DataUtils.toNumbersFromString(a)};Name.prototype.match=function(a){var b=this.components;a=a.components;if(b.length>a.length)return!1;for(var c=0;c<b.length;++c)if(!DataUtils.arraysEqual(b[c],a[c]))return!1;return!0};
-var ContentObject=function(a,b,c,d){this.name="string"==typeof a?new Name(a):a;this.signedInfo=b;this.content="string"==typeof c?DataUtils.toNumbersFromString(c):c;this.signature=d;this.rawSignatureData=this.endContent=this.endSIG=this.startSIG=null};
-ContentObject.prototype.sign=function(){var a=this.encodeObject(this.name),b=this.encodeObject(this.signedInfo),c=this.encodeContent(),d=new ArrayBuffer(a.length+b.length+c.length),d=new Uint8Array(d);d.set(a,0);d.set(b,a.length);d.set(c,a.length+b.length);4<LOG&&console.log("Signature Data is (binary) "+d);4<LOG&&console.log("Signature Data is (RawString)");4<LOG&&console.log(DataUtils.toString(d));a=new RSAKey;a.readPrivateKeyFromPEMString(globalKeyManager.privateKey);a=a.signByteArrayWithSHA256(d);
-4<LOG&&console.log("SIGNATURE SAVED IS");4<LOG&&console.log(a);4<LOG&&console.log(DataUtils.toNumbers(a.trim()));this.signature.signature=DataUtils.toNumbers(a.trim())};ContentObject.prototype.encodeObject=function(a){var b=new BinaryXMLEncoder;a.to_ndnb(b);return b.getReducedOstream()};ContentObject.prototype.encodeContent=function(){var a=new BinaryXMLEncoder;a.writeElement(NDNProtocolDTags.Content,this.content);return a.getReducedOstream()};
-ContentObject.prototype.saveRawData=function(a){this.rawSignatureData=a.subarray(this.startSIG,this.endSIG)};
-ContentObject.prototype.from_ndnb=function(a){a.readStartElement(this.getElementLabel());a.peekStartElement(NDNProtocolDTags.Signature)&&(this.signature=new Signature,this.signature.from_ndnb(a));this.startSIG=a.offset;this.name=new Name;this.name.from_ndnb(a);a.peekStartElement(NDNProtocolDTags.SignedInfo)&&(this.signedInfo=new SignedInfo,this.signedInfo.from_ndnb(a));this.content=a.readBinaryElement(NDNProtocolDTags.Content);this.endSIG=a.offset;a.readEndElement();this.saveRawData(a.istream)};
-ContentObject.prototype.to_ndnb=function(a){a.writeStartElement(this.getElementLabel());null!=this.signature&&this.signature.to_ndnb(a);this.startSIG=a.offset;null!=this.name&&this.name.to_ndnb(a);null!=this.signedInfo&&this.signedInfo.to_ndnb(a);a.writeElement(NDNProtocolDTags.Content,this.content);this.endSIG=a.offset;a.writeEndElement();this.saveRawData(a.ostream)};ContentObject.prototype.getElementLabel=function(){return NDNProtocolDTags.ContentObject};
-var Signature=function(a,b,c){this.Witness=a;this.signature=b;this.digestAlgorithm=c};
-Signature.prototype.from_ndnb=function(a){a.readStartElement(this.getElementLabel());4<LOG&&console.log("STARTED DECODING SIGNATURE");a.peekStartElement(NDNProtocolDTags.DigestAlgorithm)&&(4<LOG&&console.log("DIGIEST ALGORITHM FOUND"),this.digestAlgorithm=a.readUTF8Element(NDNProtocolDTags.DigestAlgorithm));a.peekStartElement(NDNProtocolDTags.Witness)&&(4<LOG&&console.log("WITNESS FOUND"),this.Witness=a.readBinaryElement(NDNProtocolDTags.Witness));4<LOG&&console.log("SIGNATURE FOUND");this.signature=
-a.readBinaryElement(NDNProtocolDTags.SignatureBits);a.readEndElement()};
-Signature.prototype.to_ndnb=function(a){if(!this.validate())throw Error("Cannot encode: field values missing.");a.writeStartElement(this.getElementLabel());null!=this.digestAlgorithm&&!this.digestAlgorithm.equals(NDNDigestHelper.DEFAULT_DIGEST_ALGORITHM)&&a.writeElement(NDNProtocolDTags.DigestAlgorithm,OIDLookup.getDigestOID(this.DigestAlgorithm));null!=this.Witness&&a.writeElement(NDNProtocolDTags.Witness,this.Witness);a.writeElement(NDNProtocolDTags.SignatureBits,this.signature);a.writeEndElement()};
-Signature.prototype.getElementLabel=function(){return NDNProtocolDTags.Signature};Signature.prototype.validate=function(){return null!=this.signature};
-var ContentType={DATA:0,ENCR:1,GONE:2,KEY:3,LINK:4,NACK:5},ContentTypeValue={"0":787648,1:1101969,2:1631044,3:2639423,4:2917194,5:3408010},ContentTypeValueReverse={787648:0,1101969:1,1631044:2,2639423:3,2917194:4,3408010:5},SignedInfo=function(a,b,c,d,e,f){this.publisher=a;this.timestamp=b;this.type=c;this.locator=d;this.freshnessSeconds=e;this.finalBlockID=f;this.setFields()};
-SignedInfo.prototype.setFields=function(){var a=globalKeyManager.publicKey;4<LOG&&console.log("PUBLIC KEY TO WRITE TO CONTENT OBJECT IS ");4<LOG&&console.log(a);var a=DataUtils.toNumbers(globalKeyManager.publicKey),b=hex_sha256_from_bytes(a);this.publisher=new PublisherPublicKeyDigest(DataUtils.toNumbers(b));b=(new Date).getTime();this.timestamp=new NDNTime(b);4<LOG&&console.log("TIME msec is");4<LOG&&console.log(this.timestamp.msec);this.type=0;4<LOG&&console.log("PUBLIC KEY TO WRITE TO CONTENT OBJECT IS ");
-4<LOG&&console.log(a);this.locator=new KeyLocator(a,KeyLocatorType.KEY)};
-SignedInfo.prototype.from_ndnb=function(a){a.readStartElement(this.getElementLabel());a.peekStartElement(NDNProtocolDTags.PublisherPublicKeyDigest)&&(4<LOG&&console.log("DECODING PUBLISHER KEY"),this.publisher=new PublisherPublicKeyDigest,this.publisher.from_ndnb(a));a.peekStartElement(NDNProtocolDTags.Timestamp)&&(4<LOG&&console.log("DECODING TIMESTAMP"),this.timestamp=a.readDateTime(NDNProtocolDTags.Timestamp));if(a.peekStartElement(NDNProtocolDTags.Type)){var b=a.readBinaryElement(NDNProtocolDTags.Type);
-4<LOG&&console.log("Binary Type of of Signed Info is "+b);this.type=b;if(null==this.type)throw Error("Cannot parse signedInfo type: bytes.");}else this.type=ContentType.DATA;a.peekStartElement(NDNProtocolDTags.FreshnessSeconds)&&(this.freshnessSeconds=a.readIntegerElement(NDNProtocolDTags.FreshnessSeconds),4<LOG&&console.log("FRESHNESS IN SECONDS IS "+this.freshnessSeconds));a.peekStartElement(NDNProtocolDTags.FinalBlockID)&&(4<LOG&&console.log("DECODING FINAL BLOCKID"),this.finalBlockID=a.readBinaryElement(NDNProtocolDTags.FinalBlockID));
-a.peekStartElement(NDNProtocolDTags.KeyLocator)&&(4<LOG&&console.log("DECODING KEY LOCATOR"),this.locator=new KeyLocator,this.locator.from_ndnb(a));a.readEndElement()};
-SignedInfo.prototype.to_ndnb=function(a){if(!this.validate())throw Error("Cannot encode : field values missing.");a.writeStartElement(this.getElementLabel());null!=this.publisher&&(3<LOG&&console.log("ENCODING PUBLISHER KEY"+this.publisher.publisherPublicKeyDigest),this.publisher.to_ndnb(a));null!=this.timestamp&&a.writeDateTime(NDNProtocolDTags.Timestamp,this.timestamp);null!=this.type&&0!=this.type&&a.writeElement(NDNProtocolDTags.type,this.type);null!=this.freshnessSeconds&&a.writeElement(NDNProtocolDTags.FreshnessSeconds,
-this.freshnessSeconds);null!=this.finalBlockID&&a.writeElement(NDNProtocolDTags.FinalBlockID,this.finalBlockID);null!=this.locator&&this.locator.to_ndnb(a);a.writeEndElement()};SignedInfo.prototype.valueToType=function(){return null};SignedInfo.prototype.getElementLabel=function(){return NDNProtocolDTags.SignedInfo};SignedInfo.prototype.validate=function(){return null==this.publisher||null==this.timestamp||null==this.locator?!1:!0};
-var DateFormat=function(){var a=/d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,b=/\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,c=/[^-+\dA-Z]/g,d=function(a,b){a=String(a);for(b=b||2;a.length<b;)a="0"+a;return a};return function(e,f,g){var h=dateFormat;1==arguments.length&&("[object String]"==Object.prototype.toString.call(e)&&!/\d/.test(e))&&(f=e,e=void 0);e=e?new Date(e):new Date;if(isNaN(e))throw SyntaxError("invalid date");
-f=String(h.masks[f]||f||h.masks["default"]);"UTC:"==f.slice(0,4)&&(f=f.slice(4),g=!0);var j=g?"getUTC":"get",l=e[j+"Date"](),n=e[j+"Day"](),m=e[j+"Month"](),p=e[j+"FullYear"](),k=e[j+"Hours"](),q=e[j+"Minutes"](),s=e[j+"Seconds"](),j=e[j+"Milliseconds"](),r=g?0:e.getTimezoneOffset(),t={d:l,dd:d(l),ddd:h.i18n.dayNames[n],dddd:h.i18n.dayNames[n+7],m:m+1,mm:d(m+1),mmm:h.i18n.monthNames[m],mmmm:h.i18n.monthNames[m+12],yy:String(p).slice(2),yyyy:p,h:k%12||12,hh:d(k%12||12),H:k,HH:d(k),M:q,MM:d(q),s:s,
-ss:d(s),l:d(j,3),L:d(99<j?Math.round(j/10):j),t:12>k?"a":"p",tt:12>k?"am":"pm",T:12>k?"A":"P",TT:12>k?"AM":"PM",Z:g?"UTC":(String(e).match(b)||[""]).pop().replace(c,""),o:(0<r?"-":"+")+d(100*Math.floor(Math.abs(r)/60)+Math.abs(r)%60,4),S:["th","st","nd","rd"][3<l%10?0:(10!=l%100-l%10)*l%10]};return f.replace(a,function(a){return a in t?t[a]:a.slice(1,a.length-1)})}}();
-DateFormat.masks={"default":"ddd mmm dd yyyy HH:MM:ss",shortDate:"m/d/yy",mediumDate:"mmm d, yyyy",longDate:"mmmm d, yyyy",fullDate:"dddd, mmmm d, yyyy",shortTime:"h:MM TT",mediumTime:"h:MM:ss TT",longTime:"h:MM:ss TT Z",isoDate:"yyyy-mm-dd",isoTime:"HH:MM:ss",isoDateTime:"yyyy-mm-dd'T'HH:MM:ss",isoUtcDateTime:"UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"};DateFormat.i18n={dayNames:"Sun Mon Tue Wed Thu Fri Sat Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),monthNames:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec January February March April May June July August September October November December".split(" ")};
-Date.prototype.format=function(a,b){return dateFormat(this,a,b)};var Interest=function(a,b,c,d,e,f,g,h,j,l,n){this.name=a;this.faceInstance=b;this.maxSuffixComponents=d;this.minSuffixComponents=c;this.publisherPublicKeyDigest=e;this.exclude=f;this.childSelector=g;this.answerOriginKind=h;this.scope=j;this.interestLifetime=l;this.nonce=n};Interest.RECURSIVE_POSTFIX="*";Interest.CHILD_SELECTOR_LEFT=0;Interest.CHILD_SELECTOR_RIGHT=1;Interest.ANSWER_CONTENT_STORE=1;Interest.ANSWER_GENERATED=2;
-Interest.ANSWER_STALE=4;Interest.MARK_STALE=16;Interest.DEFAULT_ANSWER_ORIGIN_KIND=Interest.ANSWER_CONTENT_STORE|Interest.ANSWER_GENERATED;
-Interest.prototype.from_ndnb=function(a){a.readStartElement(NDNProtocolDTags.Interest);this.name=new Name;this.name.from_ndnb(a);a.peekStartElement(NDNProtocolDTags.MinSuffixComponents)&&(this.minSuffixComponents=a.readIntegerElement(NDNProtocolDTags.MinSuffixComponents));a.peekStartElement(NDNProtocolDTags.MaxSuffixComponents)&&(this.maxSuffixComponents=a.readIntegerElement(NDNProtocolDTags.MaxSuffixComponents));a.peekStartElement(NDNProtocolDTags.PublisherPublicKeyDigest)&&(this.publisherPublicKeyDigest=
-new PublisherPublicKeyDigest,this.publisherPublicKeyDigest.from_ndnb(a));a.peekStartElement(NDNProtocolDTags.Exclude)&&(this.exclude=new Exclude,this.exclude.from_ndnb(a));a.peekStartElement(NDNProtocolDTags.ChildSelector)&&(this.childSelector=a.readIntegerElement(NDNProtocolDTags.ChildSelector));a.peekStartElement(NDNProtocolDTags.AnswerOriginKind)&&(this.answerOriginKind=a.readIntegerElement(NDNProtocolDTags.AnswerOriginKind));a.peekStartElement(NDNProtocolDTags.Scope)&&(this.scope=a.readIntegerElement(NDNProtocolDTags.Scope));
-a.peekStartElement(NDNProtocolDTags.InterestLifetime)&&(this.interestLifetime=1E3*DataUtils.bigEndianToUnsignedInt(a.readBinaryElement(NDNProtocolDTags.InterestLifetime))/4096);a.peekStartElement(NDNProtocolDTags.Nonce)&&(this.nonce=a.readBinaryElement(NDNProtocolDTags.Nonce));a.readEndElement()};
-Interest.prototype.to_ndnb=function(a){a.writeStartElement(NDNProtocolDTags.Interest);this.name.to_ndnb(a);null!=this.minSuffixComponents&&a.writeElement(NDNProtocolDTags.MinSuffixComponents,this.minSuffixComponents);null!=this.maxSuffixComponents&&a.writeElement(NDNProtocolDTags.MaxSuffixComponents,this.maxSuffixComponents);null!=this.publisherPublicKeyDigest&&this.publisherPublicKeyDigest.to_ndnb(a);null!=this.exclude&&this.exclude.to_ndnb(a);null!=this.childSelector&&a.writeElement(NDNProtocolDTags.ChildSelector,
-this.childSelector);this.DEFAULT_ANSWER_ORIGIN_KIND!=this.answerOriginKind&&null!=this.answerOriginKind&&a.writeElement(NDNProtocolDTags.AnswerOriginKind,this.answerOriginKind);null!=this.scope&&a.writeElement(NDNProtocolDTags.Scope,this.scope);null!=this.interestLifetime&&a.writeElement(NDNProtocolDTags.InterestLifetime,DataUtils.nonNegativeIntToBigEndian(4096*(this.interestLifetime/1E3)));null!=this.nonce&&a.writeElement(NDNProtocolDTags.Nonce,this.nonce);a.writeEndElement()};
-Interest.prototype.matches_name=function(a){return!this.name.match(a)||null!=this.minSuffixComponents&&!(a.components.length+1-this.name.components.length>=this.minSuffixComponents)||null!=this.maxSuffixComponents&&!(a.components.length+1-this.name.components.length<=this.maxSuffixComponents)||null!=this.exclude&&a.components.length>this.name.components.length&&this.exclude.matches(a.components[this.name.components.length])?!1:!0};
-Interest.prototype.clone=function(){return new Interest(this.name,this.faceInstance,this.minSuffixComponents,this.maxSuffixComponents,this.publisherPublicKeyDigest,this.exclude,this.childSelector,this.answerOriginKind,this.scope,this.interestLifetime,this.nonce)};var Exclude=function(a){this.values=a||[]};Exclude.ANY="*";
-Exclude.prototype.from_ndnb=function(a){for(a.readStartElement(NDNProtocolDTags.Exclude);;)if(a.peekStartElement(NDNProtocolDTags.Component))this.values.push(a.readBinaryElement(NDNProtocolDTags.Component));else if(a.peekStartElement(NDNProtocolDTags.Any))a.readStartElement(NDNProtocolDTags.Any),a.readEndElement(),this.values.push(Exclude.ANY);else if(a.peekStartElement(NDNProtocolDTags.Bloom))a.readBinaryElement(NDNProtocolDTags.Bloom),this.values.push(Exclude.ANY);else break;a.readEndElement()};
-Exclude.prototype.to_ndnb=function(a){if(!(null==this.values||0==this.values.length)){a.writeStartElement(NDNProtocolDTags.Exclude);for(var b=0;b<this.values.length;++b)this.values[b]==Exclude.ANY?(a.writeStartElement(NDNProtocolDTags.Any),a.writeEndElement()):a.writeElement(NDNProtocolDTags.Component,this.values[b]);a.writeEndElement()}};
-Exclude.prototype.to_uri=function(){if(null==this.values||0==this.values.length)return"";for(var a="",b=0;b<this.values.length;++b)0<b&&(a+=","),a=this.values[b]==Exclude.ANY?a+"*":a+Name.toEscapedString(this.values[b]);return a};
-Exclude.prototype.matches=function(a){for(var b=0;b<this.values.length;++b)if(this.values[b]==Exclude.ANY){var c=null;0<b&&(c=this.values[b-1]);var d,e=null;for(d=b+1;d<this.values.length;++d)if(this.values[d]!=Exclude.ANY){e=this.values[d];break}if(null!=e){if(null!=c){if(0<Exclude.compareComponents(a,c)&&0>Exclude.compareComponents(a,e))return!0}else if(0>Exclude.compareComponents(a,e))return!0;b=d-1}else if(null!=c){if(0<Exclude.compareComponents(a,c))return!0}else return!0}else if(DataUtils.arraysEqual(a,
-this.values[b]))return!0;return!1};Exclude.compareComponents=function(a,b){if(a.length<b.length)return-1;if(a.length>b.length)return 1;for(var c=0;c<a.length;++c){if(a[c]<b[c])return-1;if(a[c]>b[c])return 1}return 0};
-var Key=function(){},KeyLocatorType={KEY:1,CERTIFICATE:2,KEYNAME:3},KeyLocator=function(a,b){this.type=b;b==KeyLocatorType.KEYNAME?(3<LOG&&console.log("KeyLocator: SET KEYNAME"),this.keyName=a):b==KeyLocatorType.KEY?(3<LOG&&console.log("KeyLocator: SET KEY"),this.publicKey=a):b==KeyLocatorType.CERTIFICATE&&(3<LOG&&console.log("KeyLocator: SET CERTIFICATE"),this.certificate=a)};
-KeyLocator.prototype.from_ndnb=function(a){a.readStartElement(this.getElementLabel());if(a.peekStartElement(NDNProtocolDTags.Key)){try{this.publicKey=a.readBinaryElement(NDNProtocolDTags.Key),this.type=KeyLocatorType.KEY,4<LOG&&console.log("PUBLIC KEY FOUND: "+this.publicKey)}catch(b){throw Error("Cannot parse key: ",b);}if(null==this.publicKey)throw Error("Cannot parse key: ");}else if(a.peekStartElement(NDNProtocolDTags.Certificate)){try{this.certificate=a.readBinaryElement(NDNProtocolDTags.Certificate),
-this.type=KeyLocatorType.CERTIFICATE,4<LOG&&console.log("CERTIFICATE FOUND: "+this.certificate)}catch(c){throw Error("Cannot decode certificate: "+c);}if(null==this.certificate)throw Error("Cannot parse certificate! ");}else this.type=KeyLocatorType.KEYNAME,this.keyName=new KeyName,this.keyName.from_ndnb(a);a.readEndElement()};
-KeyLocator.prototype.to_ndnb=function(a){4<LOG&&console.log("type is is "+this.type);if(!this.validate())throw new ContentEncodingException("Cannot encode "+this.getClass().getName()+": field values missing.");a.writeStartElement(this.getElementLabel());if(this.type==KeyLocatorType.KEY)5<LOG&&console.log("About to encode a public key"+this.publicKey),a.writeElement(NDNProtocolDTags.Key,this.publicKey);else if(this.type==KeyLocatorType.CERTIFICATE)try{a.writeElement(NDNProtocolDTags.Certificate,this.certificate)}catch(b){throw Error("CertificateEncodingException attempting to write key locator: "+
-b);}else this.type==KeyLocatorType.KEYNAME&&this.keyName.to_ndnb(a);a.writeEndElement()};KeyLocator.prototype.getElementLabel=function(){return NDNProtocolDTags.KeyLocator};KeyLocator.prototype.validate=function(){return null!=this.keyName||null!=this.publicKey||null!=this.certificate};var KeyName=function(){this.contentName=this.contentName;this.publisherID=this.publisherID};
-KeyName.prototype.from_ndnb=function(a){a.readStartElement(this.getElementLabel());this.contentName=new Name;this.contentName.from_ndnb(a);4<LOG&&console.log("KEY NAME FOUND: ");PublisherID.peek(a)&&(this.publisherID=new PublisherID,this.publisherID.from_ndnb(a));a.readEndElement()};
-KeyName.prototype.to_ndnb=function(a){if(!this.validate())throw Error("Cannot encode : field values missing.");a.writeStartElement(this.getElementLabel());this.contentName.to_ndnb(a);null!=this.publisherID&&this.publisherID.to_ndnb(a);a.writeEndElement()};KeyName.prototype.getElementLabel=function(){return NDNProtocolDTags.KeyName};KeyName.prototype.validate=function(){return null!=this.contentName};
-var PublisherType=function(a){this.KEY=NDNProtocolDTags.PublisherPublicKeyDigest;this.CERTIFICATE=NDNProtocolDTags.PublisherCertificateDigest;this.ISSUER_KEY=NDNProtocolDTags.PublisherIssuerKeyDigest;this.ISSUER_CERTIFICATE=NDNProtocolDTags.PublisherIssuerCertificateDigest;this.Tag=a},isTypeTagVal=function(a){return a==NDNProtocolDTags.PublisherPublicKeyDigest||a==NDNProtocolDTags.PublisherCertificateDigest||a==NDNProtocolDTags.PublisherIssuerKeyDigest||a==NDNProtocolDTags.PublisherIssuerCertificateDigest?
-!0:!1},PublisherID=function(){this.PUBLISHER_ID_DIGEST_ALGORITHM="SHA-256";this.PUBLISHER_ID_LEN=32;this.publisherType=this.publisherID=null};
-PublisherID.prototype.from_ndnb=function(a){var b=a.peekStartElementAsLong();if(null==b)throw Error("Cannot parse publisher ID.");this.publisherType=new PublisherType(b);if(!isTypeTagVal(b))throw Error("Invalid publisher ID, got unexpected type: "+b);this.publisherID=a.readBinaryElement(b);if(null==this.publisherID)throw new ContentDecodingException(Error("Cannot parse publisher ID of type : "+b+"."));};
-PublisherID.prototype.to_ndnb=function(a){if(!this.validate())throw Error("Cannot encode "+this.getClass().getName()+": field values missing.");a.writeElement(this.getElementLabel(),this.publisherID)};PublisherID.peek=function(a){a=a.peekStartElementAsLong();return null==a?!1:isTypeTagVal(a)};PublisherID.prototype.getElementLabel=function(){return this.publisherType.Tag};PublisherID.prototype.validate=function(){return null!=id()&&null!=type()};
-var PublisherPublicKeyDigest=function(a){this.PUBLISHER_ID_LEN=64;this.publisherPublicKeyDigest=a};
-PublisherPublicKeyDigest.prototype.from_ndnb=function(a){this.publisherPublicKeyDigest=a.readBinaryElement(this.getElementLabel());4<LOG&&console.log("Publisher public key digest is "+this.publisherPublicKeyDigest);if(null==this.publisherPublicKeyDigest)throw Error("Cannot parse publisher key digest.");this.publisherPublicKeyDigest.length!=this.PUBLISHER_ID_LEN&&0<LOG&&console.log("LENGTH OF PUBLISHER ID IS WRONG! Expected "+this.PUBLISHER_ID_LEN+", got "+this.publisherPublicKeyDigest.length)};
-PublisherPublicKeyDigest.prototype.to_ndnb=function(a){if(!this.validate())throw Error("Cannot encode : field values missing.");3<LOG&&console.log("PUBLISHER KEY DIGEST IS"+this.publisherPublicKeyDigest);a.writeElement(this.getElementLabel(),this.publisherPublicKeyDigest)};PublisherPublicKeyDigest.prototype.getElementLabel=function(){return NDNProtocolDTags.PublisherPublicKeyDigest};PublisherPublicKeyDigest.prototype.validate=function(){return null!=this.publisherPublicKeyDigest};
-var NetworkProtocol={TCP:6,UDP:17},FaceInstance=function(a,b,c,d,e,f,g,h,j){this.action=a;this.publisherPublicKeyDigest=b;this.faceID=c;this.ipProto=d;this.host=e;this.Port=f;this.multicastInterface=g;this.multicastTTL=h;this.freshnessSeconds=j};
-FaceInstance.prototype.from_ndnb=function(a){a.readStartElement(this.getElementLabel());a.peekStartElement(NDNProtocolDTags.Action)&&(this.action=a.readUTF8Element(NDNProtocolDTags.Action));a.peekStartElement(NDNProtocolDTags.PublisherPublicKeyDigest)&&(this.publisherPublicKeyDigest=new PublisherPublicKeyDigest,this.publisherPublicKeyDigest.from_ndnb(a));a.peekStartElement(NDNProtocolDTags.FaceID)&&(this.faceID=a.readIntegerElement(NDNProtocolDTags.FaceID));if(a.peekStartElement(NDNProtocolDTags.IPProto)){var b=
-a.readIntegerElement(NDNProtocolDTags.IPProto);this.ipProto=null;if(NetworkProtocol.TCP==b)this.ipProto=NetworkProtocol.TCP;else if(NetworkProtocol.UDP==b)this.ipProto=NetworkProtocol.UDP;else throw Error("FaceInstance.decoder. Invalid "+NDNProtocolDTags.tagToString(NDNProtocolDTags.IPProto)+" field: "+b);}a.peekStartElement(NDNProtocolDTags.Host)&&(this.host=a.readUTF8Element(NDNProtocolDTags.Host));a.peekStartElement(NDNProtocolDTags.Port)&&(this.Port=a.readIntegerElement(NDNProtocolDTags.Port));
-a.peekStartElement(NDNProtocolDTags.MulticastInterface)&&(this.multicastInterface=a.readUTF8Element(NDNProtocolDTags.MulticastInterface));a.peekStartElement(NDNProtocolDTags.MulticastTTL)&&(this.multicastTTL=a.readIntegerElement(NDNProtocolDTags.MulticastTTL));a.peekStartElement(NDNProtocolDTags.FreshnessSeconds)&&(this.freshnessSeconds=a.readIntegerElement(NDNProtocolDTags.FreshnessSeconds));a.readEndElement()};
-FaceInstance.prototype.to_ndnb=function(a){a.writeStartElement(this.getElementLabel());null!=this.action&&0!=this.action.length&&a.writeElement(NDNProtocolDTags.Action,this.action);null!=this.publisherPublicKeyDigest&&this.publisherPublicKeyDigest.to_ndnb(a);null!=this.faceID&&a.writeElement(NDNProtocolDTags.FaceID,this.faceID);null!=this.ipProto&&a.writeElement(NDNProtocolDTags.IPProto,this.ipProto);null!=this.host&&0!=this.host.length&&a.writeElement(NDNProtocolDTags.Host,this.host);null!=this.Port&&
-a.writeElement(NDNProtocolDTags.Port,this.Port);null!=this.multicastInterface&&0!=this.multicastInterface.length&&a.writeElement(NDNProtocolDTags.MulticastInterface,this.multicastInterface);null!=this.multicastTTL&&a.writeElement(NDNProtocolDTags.MulticastTTL,this.multicastTTL);null!=this.freshnessSeconds&&a.writeElement(NDNProtocolDTags.FreshnessSeconds,this.freshnessSeconds);a.writeEndElement()};FaceInstance.prototype.getElementLabel=function(){return NDNProtocolDTags.FaceInstance};
-var ForwardingEntry=function(a,b,c,d,e,f){this.action=a;this.prefixName=b;this.ndndID=c;this.faceID=d;this.flags=e;this.lifetime=f};
-ForwardingEntry.prototype.from_ndnb=function(a){a.readStartElement(this.getElementLabel());a.peekStartElement(NDNProtocolDTags.Action)&&(this.action=a.readUTF8Element(NDNProtocolDTags.Action));a.peekStartElement(NDNProtocolDTags.Name)&&(this.prefixName=new Name,this.prefixName.from_ndnb(a));a.peekStartElement(NDNProtocolDTags.PublisherPublicKeyDigest)&&(this.NdndId=new PublisherPublicKeyDigest,this.NdndId.from_ndnb(a));a.peekStartElement(NDNProtocolDTags.FaceID)&&(this.faceID=a.readIntegerElement(NDNProtocolDTags.FaceID));
-a.peekStartElement(NDNProtocolDTags.ForwardingFlags)&&(this.flags=a.readIntegerElement(NDNProtocolDTags.ForwardingFlags));a.peekStartElement(NDNProtocolDTags.FreshnessSeconds)&&(this.lifetime=a.readIntegerElement(NDNProtocolDTags.FreshnessSeconds));a.readEndElement()};
-ForwardingEntry.prototype.to_ndnb=function(a){a.writeStartElement(this.getElementLabel());null!=this.action&&0!=this.action.length&&a.writeElement(NDNProtocolDTags.Action,this.action);null!=this.prefixName&&this.prefixName.to_ndnb(a);null!=this.NdndId&&this.NdndId.to_ndnb(a);null!=this.faceID&&a.writeElement(NDNProtocolDTags.FaceID,this.faceID);null!=this.flags&&a.writeElement(NDNProtocolDTags.ForwardingFlags,this.flags);null!=this.lifetime&&a.writeElement(NDNProtocolDTags.FreshnessSeconds,this.lifetime);
-a.writeEndElement()};ForwardingEntry.prototype.getElementLabel=function(){return NDNProtocolDTags.ForwardingEntry};var DynamicUint8Array=function(a){a||(a=16);this.array=new Uint8Array(a);this.length=a};DynamicUint8Array.prototype.ensureLength=function(a){if(!(this.array.length>=a)){var b=2*this.array.length;a>b&&(b=a);a=new Uint8Array(b);a.set(this.array);this.array=a;this.length=b}};DynamicUint8Array.prototype.set=function(a,b){this.ensureLength(a.length+b);this.array.set(a,b)};
-DynamicUint8Array.prototype.subarray=function(a,b){return this.array.subarray(a,b)};
-var XML_EXT=0,XML_TAG=1,XML_DTAG=2,XML_ATTR=3,XML_DATTR=4,XML_BLOB=5,XML_UDATA=6,XML_CLOSE=0,XML_SUBTYPE_PROCESSING_INSTRUCTIONS=16,XML_TT_BITS=3,XML_TT_MASK=(1<<XML_TT_BITS)-1,XML_TT_VAL_BITS=XML_TT_BITS+1,XML_TT_VAL_MASK=(1<<XML_TT_VAL_BITS)-1,XML_REG_VAL_BITS=7,XML_REG_VAL_MASK=(1<<XML_REG_VAL_BITS)-1,XML_TT_NO_MORE=1<<XML_REG_VAL_BITS,BYTE_MASK=255,LONG_BYTES=8,LONG_BITS=64,bits_11=2047,bits_18=262143,bits_32=4294967295,BinaryXMLEncoder=function(){this.ostream=new DynamicUint8Array(100);this.offset=
-0;this.CODEC_NAME="Binary"};BinaryXMLEncoder.prototype.writeUString=function(a){this.encodeUString(a,XML_UDATA)};BinaryXMLEncoder.prototype.writeBlob=function(a){3<LOG&&console.log(a);this.encodeBlob(a,a.length)};BinaryXMLEncoder.prototype.writeStartElement=function(a,b){null==a?this.encodeUString(a,XML_TAG):this.encodeTypeAndVal(XML_DTAG,a);null!=b&&this.writeAttributes(b)};
-BinaryXMLEncoder.prototype.writeEndElement=function(){this.ostream.ensureLength(this.offset+1);this.ostream.array[this.offset]=XML_CLOSE;this.offset+=1};BinaryXMLEncoder.prototype.writeAttributes=function(a){if(null!=a)for(var b=0;b<a.length;b++){var c=a[b].k,d=a[b].v,e=stringToTag(c);null==e?this.encodeUString(c,XML_ATTR):this.encodeTypeAndVal(XML_DATTR,e);this.encodeUString(d)}};
-stringToTag=function(a){return 0<=a&&a<NDNProtocolDTagsStrings.length?NDNProtocolDTagsStrings[a]:a==NDNProtocolDTags.NDNProtocolDataUnit?NDNProtocolDTags.NDNPROTOCOL_DATA_UNIT:null};tagToString=function(a){for(var b=0;b<NDNProtocolDTagsStrings.length;++b)if(null!=NDNProtocolDTagsStrings[b]&&NDNProtocolDTagsStrings[b]==a)return b;return NDNProtocolDTags.NDNPROTOCOL_DATA_UNIT==a?NDNProtocolDTags.NDNProtocolDataUnit:null};
-BinaryXMLEncoder.prototype.writeElement=function(a,b,c){this.writeStartElement(a,c);"number"===typeof b?(4<LOG&&console.log("GOING TO WRITE THE NUMBER .charCodeAt(0) "+b.toString().charCodeAt(0)),4<LOG&&console.log("GOING TO WRITE THE NUMBER "+b.toString()),4<LOG&&console.log("type of number is "+typeof b.toString()),this.writeUString(b.toString())):"string"===typeof b?(4<LOG&&console.log("GOING TO WRITE THE STRING "+b),4<LOG&&console.log("type of STRING is "+typeof b),this.writeUString(b)):(4<LOG&&
-console.log("GOING TO WRITE A BLOB "+b),this.writeBlob(b));this.writeEndElement()};var TypeAndVal=function(a,b){this.type=a;this.val=b};
-BinaryXMLEncoder.prototype.encodeTypeAndVal=function(a,b){4<LOG&&console.log("Encoding type "+a+" and value "+b);4<LOG&&console.log("OFFSET IS "+this.offset);if(a>XML_UDATA||0>a||0>b)throw Error("Tag and value must be positive, and tag valid.");var c=this.numEncodingBytes(b);this.ostream.ensureLength(this.offset+c);this.ostream.array[this.offset+c-1]=BYTE_MASK&(XML_TT_MASK&a|(XML_TT_VAL_MASK&b)<<XML_TT_BITS)|XML_TT_NO_MORE;b>>>=XML_TT_VAL_BITS;for(var d=this.offset+c-2;0!=b&&d>=this.offset;)this.ostream.array[d]=
-BYTE_MASK&b&XML_REG_VAL_MASK,b>>>=XML_REG_VAL_BITS,--d;if(0!=b)throw Error("This should not happen: miscalculated encoding");this.offset+=c;return c};
-BinaryXMLEncoder.prototype.encodeUString=function(a,b){if(null!=a&&!(b==XML_TAG||b==XML_ATTR&&0==a.length)){3<LOG&&console.log("The string to write is ");3<LOG&&console.log(a);var c=DataUtils.stringToUtf8Array(a);this.encodeTypeAndVal(b,b==XML_TAG||b==XML_ATTR?c.length-1:c.length);3<LOG&&console.log("THE string to write is ");3<LOG&&console.log(c);this.writeString(c);this.offset+=c.length}};
-BinaryXMLEncoder.prototype.encodeBlob=function(a,b){null!=a&&(4<LOG&&console.log("LENGTH OF XML_BLOB IS "+b),this.encodeTypeAndVal(XML_BLOB,b),this.writeBlobArray(a),this.offset+=b)};var ENCODING_LIMIT_1_BYTE=(1<<XML_TT_VAL_BITS)-1,ENCODING_LIMIT_2_BYTES=(1<<XML_TT_VAL_BITS+XML_REG_VAL_BITS)-1,ENCODING_LIMIT_3_BYTES=(1<<XML_TT_VAL_BITS+2*XML_REG_VAL_BITS)-1;
-BinaryXMLEncoder.prototype.numEncodingBytes=function(a){if(a<=ENCODING_LIMIT_1_BYTE)return 1;if(a<=ENCODING_LIMIT_2_BYTES)return 2;if(a<=ENCODING_LIMIT_3_BYTES)return 3;var b=1;for(a>>>=XML_TT_VAL_BITS;0!=a;)b++,a>>>=XML_REG_VAL_BITS;return b};
-BinaryXMLEncoder.prototype.writeDateTime=function(a,b){4<LOG&&console.log("ENCODING DATE with LONG VALUE");4<LOG&&console.log(b.msec);var c=Math.round(4096*(b.msec/1E3)).toString(16),c=DataUtils.toNumbers("0".concat(c,"0"));4<LOG&&console.log("ENCODING DATE with BINARY VALUE");4<LOG&&console.log(c);4<LOG&&console.log("ENCODING DATE with BINARY VALUE(HEX)");4<LOG&&console.log(DataUtils.toHex(c));this.writeElement(a,c)};
-BinaryXMLEncoder.prototype.writeString=function(a){if("string"===typeof a){4<LOG&&console.log("GOING TO WRITE A STRING");4<LOG&&console.log(a);this.ostream.ensureLength(this.offset+a.length);for(var b=0;b<a.length;b++)4<LOG&&console.log("input.charCodeAt(i)="+a.charCodeAt(b)),this.ostream.array[this.offset+b]=a.charCodeAt(b)}else 4<LOG&&console.log("GOING TO WRITE A STRING IN BINARY FORM"),4<LOG&&console.log(a),this.writeBlobArray(a)};
-BinaryXMLEncoder.prototype.writeBlobArray=function(a){4<LOG&&console.log("GOING TO WRITE A BLOB");this.ostream.set(a,this.offset)};BinaryXMLEncoder.prototype.getReducedOstream=function(){return this.ostream.subarray(0,this.offset)};XML_EXT=0;XML_TAG=1;XML_DTAG=2;XML_ATTR=3;XML_DATTR=4;XML_BLOB=5;XML_UDATA=6;XML_CLOSE=0;XML_SUBTYPE_PROCESSING_INSTRUCTIONS=16;XML_TT_BITS=3;XML_TT_MASK=(1<<XML_TT_BITS)-1;XML_TT_VAL_BITS=XML_TT_BITS+1;XML_TT_VAL_MASK=(1<<XML_TT_VAL_BITS)-1;XML_REG_VAL_BITS=7;
-XML_REG_VAL_MASK=(1<<XML_REG_VAL_BITS)-1;XML_TT_NO_MORE=1<<XML_REG_VAL_BITS;BYTE_MASK=255;LONG_BYTES=8;LONG_BITS=64;bits_11=2047;bits_18=262143;bits_32=4294967295;tagToString=function(a){return 0<=a&&a<NDNProtocolDTagsStrings.length?NDNProtocolDTagsStrings[a]:a==NDNProtocolDTags.NDNProtocolDataUnit?NDNProtocolDTags.NDNPROTOCOL_DATA_UNIT:null};
-stringToTag=function(a){for(var b=0;b<NDNProtocolDTagsStrings.length;++b)if(null!=NDNProtocolDTagsStrings[b]&&NDNProtocolDTagsStrings[b]==a)return b;return NDNProtocolDTags.NDNPROTOCOL_DATA_UNIT==a?NDNProtocolDTags.NDNProtocolDataUnit:null};var BinaryXMLDecoder=function(a){this.istream=a;this.offset=0};BinaryXMLDecoder.prototype.initializeDecoding=function(){};BinaryXMLDecoder.prototype.readStartDocument=function(){};BinaryXMLDecoder.prototype.readEndDocument=function(){};
-BinaryXMLDecoder.prototype.readStartElement=function(a,b){var c=this.decodeTypeAndVal();if(null==c)throw new ContentDecodingException(Error("Expected start element: "+a+" got something not a tag."));var d=null;c.type()==XML_TAG?(d="string"==typeof c.val()?parseInt(c.val())+1:c.val()+1,d=this.decodeUString(d)):c.type()==XML_DTAG&&(d=c.val());if(null==d||d!=a)throw console.log("expecting "+a+" but got "+d),new ContentDecodingException(Error("Expected start element: "+a+" got: "+d+"("+c.val()+")"));
-null!=b&&readAttributes(b)};
-BinaryXMLDecoder.prototype.readAttributes=function(a){if(null!=a)try{for(var b=this.peekTypeAndVal();null!=b&&(XML_ATTR==b.type()||XML_DATTR==b.type());){var c=this.decodeTypeAndVal(),d=null;if(XML_ATTR==c.type()){var e;e="string"==typeof c.val()?parseInt(c.val())+1:c.val()+1;d=this.decodeUString(e)}else if(XML_DATTR==c.type()&&(d=tagToString(c.val()),null==d))throw new ContentDecodingException(Error("Unknown DATTR value"+c.val()));var f=this.decodeUString();a.push([d,f]);b=this.peekTypeAndVal()}}catch(g){throw new ContentDecodingException(Error("readStartElement",
-g));}};BinaryXMLDecoder.prototype.peekStartElementAsString=function(){var a=null,b=this.offset;try{var c=this.decodeTypeAndVal();if(null!=c)if(c.type()==XML_TAG){var d;d="string"==typeof c.val()?parseInt(c.val())+1:c.val()+1;a=this.decodeUString(d)}else c.type()==XML_DTAG&&(a=tagToString(c.val()))}catch(e){}finally{try{this.offset=b}catch(f){throw Log.logStackTrace(Log.FAC_ENCODING,Level.WARNING,f),new ContentDecodingException(Error("Cannot reset stream! "+f.getMessage(),f));}}return a};
-BinaryXMLDecoder.prototype.peekStartElement=function(a){if("string"==typeof a){var b=this.peekStartElementAsString();return null!=b&&b==a?!0:!1}if("number"==typeof a)return b=this.peekStartElementAsLong(),null!=b&&b==a?!0:!1;throw new ContentDecodingException(Error("SHOULD BE STRING OR NUMBER"));};
-BinaryXMLDecoder.prototype.peekStartElementAsLong=function(){var a=null,b=this.offset;try{var c=this.decodeTypeAndVal();if(null!=c)if(c.type()==XML_TAG){if(c.val()+1>DEBUG_MAX_LEN)throw new ContentDecodingException(Error("Decoding error: length "+c.val()+1+" longer than expected maximum length!"));var d;d="string"==typeof c.val()?parseInt(c.val())+1:c.val()+1;var e=this.decodeUString(d),a=stringToTag(e)}else c.type()==XML_DTAG&&(a=c.val())}catch(f){}finally{try{this.offset=b}catch(g){throw Log.logStackTrace(Log.FAC_ENCODING,
-Level.WARNING,g),Error("Cannot reset stream! "+g.getMessage(),g);}}return a};BinaryXMLDecoder.prototype.readBinaryElement=function(a,b){var c=null;this.readStartElement(a,b);return c=this.readBlob()};
-BinaryXMLDecoder.prototype.readEndElement=function(){4<LOG&&console.log("this.offset is "+this.offset);var a=this.istream[this.offset];this.offset++;4<LOG&&console.log("XML_CLOSE IS "+XML_CLOSE);4<LOG&&console.log("next is "+a);if(a!=XML_CLOSE)throw console.log("Expected end element, got: "+a),new ContentDecodingException(Error("Expected end element, got: "+a));};BinaryXMLDecoder.prototype.readUString=function(){var a=this.decodeUString();this.readEndElement();return a};
-BinaryXMLDecoder.prototype.readBlob=function(){var a=this.decodeBlob();this.readEndElement();return a};BinaryXMLDecoder.prototype.readDateTime=function(a){a=this.readBinaryElement(a);a=DataUtils.toHex(a);a=parseInt(a,16);var b=1E3*(a/4096);3<LOG&&console.log("DECODED DATE WITH VALUE");3<LOG&&console.log(b);b=new NDNTime(b);if(null==b)throw new ContentDecodingException(Error("Cannot parse timestamp: "+DataUtils.printHexBytes(a)));return b};
-BinaryXMLDecoder.prototype.decodeTypeAndVal=function(){var a=-1,b=0,c=!0;do{var d=this.istream[this.offset];if(0>d||0==d&&0==b)return null;(c=0==(d&XML_TT_NO_MORE))?(b<<=XML_REG_VAL_BITS,b|=d&XML_REG_VAL_MASK):(a=d&XML_TT_MASK,b<<=XML_TT_VAL_BITS,b|=d>>>XML_TT_BITS&XML_TT_VAL_MASK);this.offset++}while(c);3<LOG&&console.log("TYPE is "+a+" VAL is "+b);return new TypeAndVal(a,b)};BinaryXMLDecoder.peekTypeAndVal=function(){var a=null,b=this.offset;try{a=this.decodeTypeAndVal()}finally{this.offset=b}return a};
-BinaryXMLDecoder.prototype.decodeBlob=function(a){if(null==a)return a=this.decodeTypeAndVal(),a="string"==typeof a.val()?parseInt(a.val()):a.val(),this.decodeBlob(a);var b=this.istream.subarray(this.offset,this.offset+a);this.offset+=a;return b};
-BinaryXMLDecoder.prototype.decodeUString=function(a){if(null==a){a=this.offset;var b=this.decodeTypeAndVal();3<LOG&&console.log("TV is "+b);3<LOG&&console.log(b);3<LOG&&console.log("Type of TV is "+typeof b);return null==b||XML_UDATA!=b.type()?(this.offset=a,""):this.decodeUString(b.val())}a=this.decodeBlob(a);return DataUtils.toString(a)};TypeAndVal=function(a,b){this.t=a;this.v=b};TypeAndVal.prototype.type=function(){return this.t};TypeAndVal.prototype.val=function(){return this.v};
-BinaryXMLDecoder.prototype.readIntegerElement=function(a){4<LOG&&console.log("READING INTEGER "+a);4<LOG&&console.log("TYPE OF "+typeof a);a=this.readUTF8Element(a);return parseInt(a)};BinaryXMLDecoder.prototype.readUTF8Element=function(a,b){this.readStartElement(a,b);return this.readUString()};BinaryXMLDecoder.prototype.seek=function(a){this.offset=a};function ContentDecodingException(a){this.message=a.message;for(var b in a)this[b]=a[b]}ContentDecodingException.prototype=Error();
-ContentDecodingException.prototype.name="ContentDecodingException";var BinaryXMLStructureDecoder=function(){this.gotElementEnd=!1;this.level=this.offset=0;this.state=BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE;this.headerLength=0;this.useHeaderBuffer=!1;this.headerBuffer=new DynamicUint8Array(5);this.nBytesToRead=0};BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE=0;BinaryXMLStructureDecoder.READ_BYTES=1;
-BinaryXMLStructureDecoder.prototype.findElementEnd=function(a){if(this.gotElementEnd)return!0;for(var b=new BinaryXMLDecoder(a);;){if(this.offset>=a.length)return!1;switch(this.state){case BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE:if(0==this.headerLength&&a[this.offset]==XML_CLOSE){++this.offset;--this.level;if(0==this.level)return!0;if(0>this.level)throw Error("BinaryXMLStructureDecoder: Unexepected close tag at offset "+(this.offset-1));this.startHeader();break}for(var c=this.headerLength;;){if(this.offset>=
-a.length){this.useHeaderBuffer=!0;var d=this.headerLength-c;this.headerBuffer.set(a.subarray(this.offset-d,d),c);return!1}d=a[this.offset++];++this.headerLength;if(d&XML_TT_NO_MORE)break}this.useHeaderBuffer?(d=this.headerLength-c,this.headerBuffer.set(a.subarray(this.offset-d,d),c),c=(new BinaryXMLDecoder(this.headerBuffer.array)).decodeTypeAndVal()):(b.seek(this.offset-this.headerLength),c=b.decodeTypeAndVal());if(null==c)throw Error("BinaryXMLStructureDecoder: Can't read header starting at offset "+
-(this.offset-this.headerLength));d=c.t;if(d==XML_DATTR)this.startHeader();else if(d==XML_DTAG||d==XML_EXT)++this.level,this.startHeader();else if(d==XML_TAG||d==XML_ATTR)d==XML_TAG&&++this.level,this.nBytesToRead=c.v+1,this.state=BinaryXMLStructureDecoder.READ_BYTES;else if(d==XML_BLOB||d==XML_UDATA)this.nBytesToRead=c.v,this.state=BinaryXMLStructureDecoder.READ_BYTES;else throw Error("BinaryXMLStructureDecoder: Unrecognized header type "+d);break;case BinaryXMLStructureDecoder.READ_BYTES:c=a.length-
-this.offset;if(c<this.nBytesToRead)return this.offset+=c,this.nBytesToRead-=c,!1;this.offset+=this.nBytesToRead;this.startHeader();break;default:throw Error("BinaryXMLStructureDecoder: Unrecognized state "+this.state);}}};BinaryXMLStructureDecoder.prototype.startHeader=function(){this.headerLength=0;this.useHeaderBuffer=!1;this.state=BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE};BinaryXMLStructureDecoder.prototype.seek=function(a){this.offset=a};var DataUtils=function(){};DataUtils.keyStr="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
-DataUtils.stringtoBase64=function(a){a=escape(a);var b="",c,d,e="",f,g,h="",j=0;do c=a.charCodeAt(j++),d=a.charCodeAt(j++),e=a.charCodeAt(j++),f=c>>2,c=(c&3)<<4|d>>4,g=(d&15)<<2|e>>6,h=e&63,isNaN(d)?g=h=64:isNaN(e)&&(h=64),b=b+DataUtils.keyStr.charAt(f)+DataUtils.keyStr.charAt(c)+DataUtils.keyStr.charAt(g)+DataUtils.keyStr.charAt(h);while(j<a.length);return b};
-DataUtils.base64toString=function(a){var b="",c,d,e="",f,g="",h=0;/[^A-Za-z0-9\+\/\=]/g.exec(a)&&alert("There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\nExpect errors in decoding.");a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");do c=DataUtils.keyStr.indexOf(a.charAt(h++)),d=DataUtils.keyStr.indexOf(a.charAt(h++)),f=DataUtils.keyStr.indexOf(a.charAt(h++)),g=DataUtils.keyStr.indexOf(a.charAt(h++)),c=c<<2|d>>4,d=(d&15)<<4|f>>2,e=(f&3)<<6|g,
-b+=String.fromCharCode(c),64!=f&&(b+=String.fromCharCode(d)),64!=g&&(b+=String.fromCharCode(e));while(h<a.length);return unescape(b)};DataUtils.toHex=function(a){4<LOG&&console.log("ABOUT TO CONVERT "+a);for(var b="",c=0;c<a.length;c++)b+=(16>a[c]?"0":"")+a[c].toString(16);4<LOG&&console.log("Converted to: "+b);return b};DataUtils.stringToHex=function(a){for(var b="",c=0;c<a.length;++c)var d=a.charCodeAt(c),b=b+((16>d?"0":"")+d.toString(16));return b};
-DataUtils.toString=function(a){for(var b="",c=0;c<a.length;c++)b+=String.fromCharCode(a[c]);return b};DataUtils.toNumbers=function(a){if("string"==typeof a){var b=new Uint8Array(Math.floor(a.length/2)),c=0;a.replace(/(..)/g,function(a){b[c++]=parseInt(a,16)});return b}};DataUtils.hexToRawString=function(a){if("string"==typeof a){var b="";a.replace(/(..)/g,function(a){b+=String.fromCharCode(parseInt(a,16))});return b}};
-DataUtils.toNumbersFromString=function(a){for(var b=new Uint8Array(a.length),c=0;c<a.length;c++)b[c]=a.charCodeAt(c);return b};DataUtils.stringToUtf8Array=function(a){return DataUtils.toNumbersFromString(str2rstr_utf8(a))};DataUtils.concatArrays=function(a){for(var b=0,c=0;c<a.length;++c)b+=a[c].length;for(var b=new Uint8Array(b),d=0,c=0;c<a.length;++c)b.set(a[c],d),d+=a[c].length;return b};
-DataUtils.decodeUtf8=function(a){for(var b="",c=0,d=0,e=0;c<a.length;)if(d=a.charCodeAt(c),128>d)b+=String.fromCharCode(d),c++;else if(191<d&&224>d)e=a.charCodeAt(c+1),b+=String.fromCharCode((d&31)<<6|e&63),c+=2;else var e=a.charCodeAt(c+1),f=a.charCodeAt(c+2),b=b+String.fromCharCode((d&15)<<12|(e&63)<<6|f&63),c=c+3;return b};DataUtils.arraysEqual=function(a,b){if(a.length!=b.length)return!1;for(var c=0;c<a.length;++c)if(a[c]!=b[c])return!1;return!0};
-DataUtils.bigEndianToUnsignedInt=function(a){for(var b=0,c=0;c<a.length;++c)b<<=8,b+=a[c];return b};DataUtils.nonNegativeIntToBigEndian=function(a){a=Math.round(a);if(0>=a)return new Uint8Array(0);for(var b=new Uint8Array(8),c=0;0!=a;)++c,b[8-c]=a&255,a>>=8;return b.subarray(8-c,8)};DataUtils.shuffle=function(a){for(var b=a.length-1;1<=b;--b){var c=Math.floor(Math.random()*(b+1)),d=a[b];a[b]=a[c];a[c]=d}};function encodeToHexInterest(a){return DataUtils.toHex(encodeToBinaryInterest(a))}
-function encodeToBinaryInterest(a){var b=new BinaryXMLEncoder;a.to_ndnb(b);return b.getReducedOstream()}function encodeToHexContentObject(a){return DataUtils.toHex(encodeToBinaryContentObject(a))}function encodeToBinaryContentObject(a){var b=new BinaryXMLEncoder;a.to_ndnb(b);return b.getReducedOstream()}function encodeForwardingEntry(a){var b=new BinaryXMLEncoder;a.to_ndnb(b);return b.getReducedOstream()}
-function decodeHexFaceInstance(a){var b=DataUtils.toNumbers(a);a=new BinaryXMLDecoder(b);3<LOG&&console.log("DECODING HEX FACE INSTANCE \n"+b);b=new FaceInstance;b.from_ndnb(a);return b}function decodeHexInterest(a){var b=DataUtils.toNumbers(a);a=new BinaryXMLDecoder(b);3<LOG&&console.log("DECODING HEX INTERST \n"+b);b=new Interest;b.from_ndnb(a);return b}
-function decodeHexContentObject(a){var b=DataUtils.toNumbers(a);a=new BinaryXMLDecoder(b);3<LOG&&console.log("DECODED HEX CONTENT OBJECT \n"+b);b=new ContentObject;b.from_ndnb(a);return b}function decodeHexForwardingEntry(a){var b=DataUtils.toNumbers(a);a=new BinaryXMLDecoder(b);3<LOG&&console.log("DECODED HEX FORWARDING ENTRY \n"+b);b=new ForwardingEntry;b.from_ndnb(a);return b}
-function decodeSubjectPublicKeyInfo(a){a=DataUtils.toHex(a).toLowerCase();a=_x509_getPublicKeyHexArrayFromCertHex(a,_x509_getSubjectPublicKeyPosFromCertHex(a,0));var b=new RSAKey;b.setPublic(a[0],a[1]);return b}
-function contentObjectToHtml(a){var b="";if(-1==a)b+="NO CONTENT FOUND";else if(-2==a)b+="CONTENT NAME IS EMPTY";else{null!=a.name&&null!=a.name.components&&(b+="NAME: "+a.name.to_uri(),b+="<br /><br />");null!=a.content&&(b+="CONTENT(ASCII): "+DataUtils.toString(a.content),b+="<br />",b+="<br />");null!=a.content&&(b+="CONTENT(hex): "+DataUtils.toHex(a.content),b+="<br />",b+="<br />");null!=a.signature&&null!=a.signature.signature&&(b+="SIGNATURE(hex): "+DataUtils.toHex(a.signature.signature),b+=
-"<br />",b+="<br />");null!=a.signedInfo&&(null!=a.signedInfo.publisher&&null!=a.signedInfo.publisher.publisherPublicKeyDigest)&&(b+="Publisher Public Key Digest(hex): "+DataUtils.toHex(a.signedInfo.publisher.publisherPublicKeyDigest),b+="<br />",b+="<br />");if(null!=a.signedInfo&&null!=a.signedInfo.timestamp){var c=new Date;c.setTime(a.signedInfo.timestamp.msec);b+="TimeStamp: "+c;b+="<br />";b+="TimeStamp(number): "+a.signedInfo.timestamp.msec;b+="<br />"}null!=a.signedInfo&&null!=a.signedInfo.finalBlockID&&
-(b+="FinalBlockID: "+DataUtils.toHex(a.signedInfo.finalBlockID),b+="<br />");if(null!=a.signedInfo&&null!=a.signedInfo.locator&&null!=a.signedInfo.locator.certificate){var d=DataUtils.toHex(a.signedInfo.locator.certificate).toLowerCase(),c=DataUtils.toHex(a.signature.signature).toLowerCase(),e=DataUtils.toString(a.rawSignatureData),b=b+("Hex Certificate: "+d),b=b+"<br />",b=b+"<br />",e=new X509;e.readCertHex(d);b+="Public key (hex) modulus: "+e.subjectPublicKeyRSA.n.toString(16)+"<br/>";b+="exponent: "+
-e.subjectPublicKeyRSA.e.toString(16)+"<br/>";b+="<br/>";c=e.subjectPublicKeyRSA.verifyByteArray(a.rawSignatureData,null,c);2<LOG&&console.log("result is "+c);d=e.subjectPublicKeyRSA.n;e=e.subjectPublicKeyRSA.e;2<LOG&&console.log("PUBLIC KEY n after is ");2<LOG&&console.log(d);2<LOG&&console.log("EXPONENT e after is ");2<LOG&&console.log(e);b=c?b+"SIGNATURE VALID":b+"SIGNATURE INVALID";b+="<br />";b+="<br />"}if(null!=a.signedInfo&&null!=a.signedInfo.locator&&null!=a.signedInfo.locator.publicKey){var f=
-DataUtils.toHex(a.signedInfo.locator.publicKey).toLowerCase(),g=DataUtils.toString(a.signedInfo.locator.publicKey),c=DataUtils.toHex(a.signature.signature).toLowerCase(),e=DataUtils.toString(a.rawSignatureData),d=null,h="";null!=a.signature.Witness&&(d=new Witness,d.decode(a.signature.Witness),h=DataUtils.toHex(a.signature.Witness));b+="Public key: "+f;b+="<br />";b+="<br />";2<LOG&&console.log(" ContentName + SignedInfo + Content = "+e);2<LOG&&console.log(" PublicKeyHex = "+f);2<LOG&&console.log(" PublicKeyString = "+
-g);2<LOG&&console.log(" Signature "+c);2<LOG&&console.log(" Witness "+h);2<LOG&&console.log(" Signature NOW IS");2<LOG&&console.log(a.signature.signature);e=decodeSubjectPublicKeyInfo(a.signedInfo.locator.publicKey);b+="Public key (hex) modulus: "+e.n.toString(16)+"<br/>";b+="exponent: "+e.e.toString(16)+"<br/>";b+="<br/>";c=e.verifyByteArray(a.rawSignatureData,d,c);2<LOG&&console.log("PUBLIC KEY n after is ");2<LOG&&console.log(e.n);2<LOG&&console.log("EXPONENT e after is ");2<LOG&&console.log(e.e);
-b=c?b+"SIGNATURE VALID":b+"SIGNATURE INVALID";b+="<br />";b+="<br />"}}return b}
-var KeyManager=function(){this.certificate="MIIBmzCCAQQCCQC32FyQa61S7jANBgkqhkiG9w0BAQUFADASMRAwDgYDVQQDEwdheGVsY2R2MB4XDTEyMDQyODIzNDQzN1oXDTEyMDUyODIzNDQzN1owEjEQMA4GA1UEAxMHYXhlbGNkdjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4X0wp9goqxuECxdULcr2IHr9Ih4Iaypg0Wy39URIup8/CLzQmdsh3RYqd55hqonu5VTTpH3iMLx6xZDVJAZ8OJi7pvXcQ2C4Re2kjL2c8SanI0RfDhlS1zJadfr1VhRPmpivcYawJ4aFuOLAi+qHFxtN7lhcGCgpW1OV60oXd58CAwEAATANBgkqhkiG9w0BAQUFAAOBgQDLOrA1fXzSrpftUB5Ro6DigX1Bjkf7F5Bkd69hSVp+jYeJFBBlsILQAfSxUZPQtD+2Yc3iCmSYNyxqu9PcufDRJlnvB7PG29+L3y9lR37tetzUV9eTscJ7rdp8Wt6AzpW32IJ/54yKNfP7S6ZIoIG+LP6EIxq6s8K1MXRt8uBJKw==";this.publicKey=
-"30819F300D06092A864886F70D010101050003818D0030818902818100E17D30A7D828AB1B840B17542DCAF6207AFD221E086B2A60D16CB7F54448BA9F3F08BCD099DB21DD162A779E61AA89EEE554D3A47DE230BC7AC590D524067C3898BBA6F5DC4360B845EDA48CBD9CF126A723445F0E1952D7325A75FAF556144F9A98AF7186B0278685B8E2C08BEA87171B4DEE585C1828295B5395EB4A17779F0203010001";this.privateKey="MIICXQIBAAKBgQDhfTCn2CirG4QLF1QtyvYgev0iHghrKmDRbLf1REi6nz8IvNCZ2yHdFip3nmGqie7lVNOkfeIwvHrFkNUkBnw4mLum9dxDYLhF7aSMvZzxJqcjRF8OGVLXMlp1+vVWFE+amK9xhrAnhoW44sCL6ocXG03uWFwYKClbU5XrShd3nwIDAQABAoGAGkv6T6jC3WmhFZYL6CdCWvlc6gysmKrhjarrLTxgavtFY6R5g2ft5BXAsCCVbUkWxkIFSKqxpVNl0gKZCNGEzPDN6mHJOQI/h0rlxNIHAuGfoAbCzALnqmyZivhJAPGijAyKuU9tczsst5+Kpn+bn7ehzHQuj7iwJonS5WbojqECQQD851K8TpW2GrRizNgG4dx6orZxAaon/Jnl8lS7soXhllQty7qG+oDfzznmdMsiznCqEABzHUUKOVGE9RWPN3aRAkEA5D/w9N55d0ibnChFJlc8cUAoaqH+w+U3oQP2Lb6AZHJpLptN4y4b/uf5d4wYU5/i/gC7SSBH3wFhh9bjRLUDLwJAVOx8vN0Kqt7myfKNbCo19jxjVSlA8TKCn1Oznl/BU1I+rC4oUaEW25DjmX6IpAR8kq7S59ThVSCQPjxqY/A08QJBAIRaF2zGPITQk3r/VumemCvLWiRK/yG0noc9dtibqHOWbCtcXtOm/xDWjq+lis2i3ssOvYrvrv0/HcDY+Dv1An0CQQCLJtMsfSg4kvG/FRY5UMhtMuwo8ovYcMXt4Xv/LWaMhndD67b2UGawQCRqr5ghRTABWdDD/HuuMBjrkPsX0861"};
-KeyManager.prototype.verify=function(a,b){var c=this.certificate,d=new X509;d.readCertPEM(c);return d.subjectPublicKeyRSA.verifyString(a,b)};KeyManager.prototype.sign=function(a){var b=this.privateKey,c=new RSAKey;c.readPrivateKeyFromPEMString(b);return c.signString(a,"sha256")};var globalKeyManager=new KeyManager,MerklePath=function(){this.index=null;this.digestList=[]},Witness=function(){this.oid=null;this.path=new MerklePath};
-function parseOID(a,b,c){for(var d,e=0,f=0;b<c;++b){var g=a[b],e=e<<7|g&127,f=f+7;g&128||(d=void 0==d?parseInt(e/40)+"."+e%40:d+("."+(31<=f?"bigint":e)),e=f=0);d+=String.fromCharCode()}return d}function parseInteger(a,b,c){for(var d=0;b<c;++b)d=d<<8|a[b];return d}
-Witness.prototype.decode=function(a){for(var b=0,c=0;b<a.length;){var d=0;if(48==a[b])0!=(a[b+1]&128)&&(d=a[b+1]&127),c++;else if(6==a[b])d=a[b+1],this.oid=parseOID(a,b+2,b+2+d);else if(2==a[b])d=a[b+1],this.path.index=parseInteger(a,b+2,b+2+d);else if(4==a[b]&&(0!=(a[b+1]&128)&&(d=a[b+1]&127),4==c)){var d=a[b+1],e=DataUtils.toHex(a.subarray(b+2,b+2+d));this.path.digestList.push(e)}b=b+2+d}};var hexcase=0,b64pad="";
-function hex_sha256_from_bytes(a){return rstr2hex(binb2rstr(binb_sha256(byteArray2binb(a),8*a.length)))}function hex_sha256(a){return rstr2hex(rstr_sha256(str2rstr_utf8(a)))}function b64_sha256(a){return rstr2b64(rstr_sha256(str2rstr_utf8(a)))}function any_sha256(a,b){return rstr2any(rstr_sha256(str2rstr_utf8(a)),b)}function hex_hmac_sha256(a,b){return rstr2hex(rstr_hmac_sha256(str2rstr_utf8(a),str2rstr_utf8(b)))}
-function b64_hmac_sha256(a,b){return rstr2b64(rstr_hmac_sha256(str2rstr_utf8(a),str2rstr_utf8(b)))}function any_hmac_sha256(a,b,c){return rstr2any(rstr_hmac_sha256(str2rstr_utf8(a),str2rstr_utf8(b)),c)}function sha256_vm_test(){return"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"==hex_sha256("abc").toLowerCase()}function rstr_sha256(a){return binb2rstr(binb_sha256(rstr2binb(a),8*a.length))}
-function rstr_hmac_sha256(a,b){var c=rstr2binb(a);16<c.length&&(c=binb_sha256(c,8*a.length));for(var d=Array(16),e=Array(16),f=0;16>f;f++)d[f]=c[f]^909522486,e[f]=c[f]^1549556828;c=binb_sha256(d.concat(rstr2binb(b)),512+8*b.length);return binb2rstr(binb_sha256(e.concat(c),768))}function rstr2hex(a){try{hexcase}catch(b){hexcase=0}for(var c=hexcase?"0123456789ABCDEF":"0123456789abcdef",d="",e,f=0;f<a.length;f++)e=a.charCodeAt(f),d+=c.charAt(e>>>4&15)+c.charAt(e&15);return d}
-function rstr2b64(a){try{b64pad}catch(b){b64pad=""}for(var c="",d=a.length,e=0;e<d;e+=3)for(var f=a.charCodeAt(e)<<16|(e+1<d?a.charCodeAt(e+1)<<8:0)|(e+2<d?a.charCodeAt(e+2):0),g=0;4>g;g++)c=8*e+6*g>8*a.length?c+b64pad:c+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(f>>>6*(3-g)&63);return c}
-function rstr2any(a,b){var c=b.length,d=[],e,f,g,h,j=Array(Math.ceil(a.length/2));for(e=0;e<j.length;e++)j[e]=a.charCodeAt(2*e)<<8|a.charCodeAt(2*e+1);for(;0<j.length;){h=[];for(e=g=0;e<j.length;e++)if(g=(g<<16)+j[e],f=Math.floor(g/c),g-=f*c,0<h.length||0<f)h[h.length]=f;d[d.length]=g;j=h}c="";for(e=d.length-1;0<=e;e--)c+=b.charAt(d[e]);d=Math.ceil(8*a.length/(Math.log(b.length)/Math.log(2)));for(e=c.length;e<d;e++)c=b[0]+c;return c}
-function str2rstr_utf8(a){for(var b="",c=-1,d,e;++c<a.length;)d=a.charCodeAt(c),e=c+1<a.length?a.charCodeAt(c+1):0,55296<=d&&(56319>=d&&56320<=e&&57343>=e)&&(d=65536+((d&1023)<<10)+(e&1023),c++),127>=d?b+=String.fromCharCode(d):2047>=d?b+=String.fromCharCode(192|d>>>6&31,128|d&63):65535>=d?b+=String.fromCharCode(224|d>>>12&15,128|d>>>6&63,128|d&63):2097151>=d&&(b+=String.fromCharCode(240|d>>>18&7,128|d>>>12&63,128|d>>>6&63,128|d&63));return b}
-function str2rstr_utf16le(a){for(var b="",c=0;c<a.length;c++)b+=String.fromCharCode(a.charCodeAt(c)&255,a.charCodeAt(c)>>>8&255);return b}function str2rstr_utf16be(a){for(var b="",c=0;c<a.length;c++)b+=String.fromCharCode(a.charCodeAt(c)>>>8&255,a.charCodeAt(c)&255);return b}function rstr2binb(a){for(var b=Array(a.length>>2),c=0;c<8*a.length;c+=8)b[c>>5]|=(a.charCodeAt(c/8)&255)<<24-c%32;return b}
-function byteArray2binb(a){for(var b=Array(a.length>>2),c=0;c<8*a.length;c+=8)b[c>>5]|=(a[c/8]&255)<<24-c%32;return b}function binb2rstr(a){for(var b="",c=0;c<32*a.length;c+=8)b+=String.fromCharCode(a[c>>5]>>>24-c%32&255);return b}function sha256_S(a,b){return a>>>b|a<<32-b}function sha256_R(a,b){return a>>>b}function sha256_Ch(a,b,c){return a&b^~a&c}function sha256_Maj(a,b,c){return a&b^a&c^b&c}function sha256_Sigma0256(a){return sha256_S(a,2)^sha256_S(a,13)^sha256_S(a,22)}
-function sha256_Sigma1256(a){return sha256_S(a,6)^sha256_S(a,11)^sha256_S(a,25)}function sha256_Gamma0256(a){return sha256_S(a,7)^sha256_S(a,18)^sha256_R(a,3)}function sha256_Gamma1256(a){return sha256_S(a,17)^sha256_S(a,19)^sha256_R(a,10)}function sha256_Sigma0512(a){return sha256_S(a,28)^sha256_S(a,34)^sha256_S(a,39)}function sha256_Sigma1512(a){return sha256_S(a,14)^sha256_S(a,18)^sha256_S(a,41)}function sha256_Gamma0512(a){return sha256_S(a,1)^sha256_S(a,8)^sha256_R(a,7)}
-function sha256_Gamma1512(a){return sha256_S(a,19)^sha256_S(a,61)^sha256_R(a,6)}
-var sha256_K=[1116352408,1899447441,-1245643825,-373957723,961987163,1508970993,-1841331548,-1424204075,-670586216,310598401,607225278,1426881987,1925078388,-2132889090,-1680079193,-1046744716,-459576895,-272742522,264347078,604807628,770255983,1249150122,1555081692,1996064986,-1740746414,-1473132947,-1341970488,-1084653625,-958395405,-710438585,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,-2117940946,-1838011259,-1564481375,-1474664885,-1035236496,-949202525,
--778901479,-694614492,-200395387,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,-2067236844,-1933114872,-1866530822,-1538233109,-1090935817,-965641998];function binb_sha256(a,b){var c=[1779033703,-1150833019,1013904242,-1521486534,1359893119,-1694144372,528734635,1541459225],d=Array(64);a[b>>5]|=128<<24-b%32;a[(b+64>>9<<4)+15]=b;for(var e=0;e<a.length;e+=16)processBlock_sha256(a,e,c,d);return c}
-function processBlock_sha256(a,b,c,d){var e,f,g,h,j,l,n,m,p,k,q;e=c[0];f=c[1];g=c[2];h=c[3];j=c[4];l=c[5];n=c[6];m=c[7];for(p=0;64>p;p++)d[p]=16>p?a[p+b]:safe_add(safe_add(safe_add(sha256_Gamma1256(d[p-2]),d[p-7]),sha256_Gamma0256(d[p-15])),d[p-16]),k=safe_add(safe_add(safe_add(safe_add(m,sha256_Sigma1256(j)),sha256_Ch(j,l,n)),sha256_K[p]),d[p]),q=safe_add(sha256_Sigma0256(e),sha256_Maj(e,f,g)),m=n,n=l,l=j,j=safe_add(h,k),h=g,g=f,f=e,e=safe_add(k,q);c[0]=safe_add(e,c[0]);c[1]=safe_add(f,c[1]);c[2]=
-safe_add(g,c[2]);c[3]=safe_add(h,c[3]);c[4]=safe_add(j,c[4]);c[5]=safe_add(l,c[5]);c[6]=safe_add(n,c[6]);c[7]=safe_add(m,c[7])}function safe_add(a,b){var c=(a&65535)+(b&65535);return(a>>16)+(b>>16)+(c>>16)<<16|c&65535}var Sha256=function(){this.W=Array(64);this.hash=[1779033703,-1150833019,1013904242,-1521486534,1359893119,-1694144372,528734635,1541459225];this.nTotalBytes=0;this.buffer=new Uint8Array(64);this.nBufferBytes=0};
-Sha256.prototype.update=function(a){this.nTotalBytes+=a.length;if(0<this.nBufferBytes){var b=this.buffer.length-this.nBufferBytes;if(a.length<b){this.buffer.set(a,this.nBufferBytes);this.nBufferBytes+=a.length;return}this.buffer.set(a.subarray(0,b),this.nBufferBytes);processBlock_sha256(byteArray2binb(this.buffer),0,this.hash,this.W);this.nBufferBytes=0;a=a.subarray(b,a.length);if(0==a.length)return}b=a.length>>6;if(0<b){for(var b=64*b,c=byteArray2binb(a.subarray(0,b)),d=0;d<c.length;d+=16)processBlock_sha256(c,
-d,this.hash,this.W);a=a.subarray(b,a.length)}0<a.length&&(this.buffer.set(a),this.nBufferBytes=a.length)};Sha256.prototype.finalize=function(){var a=byteArray2binb(this.buffer.subarray(0,this.nBufferBytes)),b=8*this.nBufferBytes;a[b>>5]|=128<<24-b%32;a[(b+64>>9<<4)+15]=8*this.nTotalBytes;for(b=0;b<a.length;b+=16)processBlock_sha256(a,b,this.hash,this.W);return Sha256.binb2Uint8Array(this.hash)};
-Sha256.binb2Uint8Array=function(a){for(var b=new Uint8Array(4*a.length),c=0,d=0;d<32*a.length;d+=8)b[c++]=a[d>>5]>>>24-d%32&255;return b};var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",b64pad="=";
-function hex2b64(a){var b,c,d="";for(b=0;b+3<=a.length;b+=3)c=parseInt(a.substring(b,b+3),16),d+=b64map.charAt(c>>6)+b64map.charAt(c&63);b+1==a.length?(c=parseInt(a.substring(b,b+1),16),d+=b64map.charAt(c<<2)):b+2==a.length&&(c=parseInt(a.substring(b,b+2),16),d+=b64map.charAt(c>>2)+b64map.charAt((c&3)<<4));for(;0<(d.length&3);)d+=b64pad;return d}
-function b64tohex(a){var b="",c,d=0,e;for(c=0;c<a.length&&a.charAt(c)!=b64pad;++c){var f=b64map.indexOf(a.charAt(c));0>f||(0==d?(b+=int2char(f>>2),e=f&3,d=1):1==d?(b+=int2char(e<<2|f>>4),e=f&15,d=2):2==d?(b+=int2char(e),b+=int2char(f>>2),e=f&3,d=3):(b+=int2char(e<<2|f>>4),b+=int2char(f&15),d=0))}1==d&&(b+=int2char(e<<2));return b}function b64toBA(a){a=b64tohex(a);var b,c=[];for(b=0;2*b<a.length;++b)c[b]=parseInt(a.substring(2*b,2*b+2),16);return c}
-function parseBigInt(a,b){return new BigInteger(a,b)}function linebrk(a,b){for(var c="",d=0;d+b<a.length;)c+=a.substring(d,d+b)+"\n",d+=b;return c+a.substring(d,a.length)}function byte2Hex(a){return 16>a?"0"+a.toString(16):a.toString(16)}
-function pkcs1pad2(a,b){if(b<a.length+11)return alert("Message too long for RSA"),null;for(var c=[],d=a.length-1;0<=d&&0<b;){var e=a.charCodeAt(d--);128>e?c[--b]=e:127<e&&2048>e?(c[--b]=e&63|128,c[--b]=e>>6|192):(c[--b]=e&63|128,c[--b]=e>>6&63|128,c[--b]=e>>12|224)}c[--b]=0;d=new SecureRandom;for(e=[];2<b;){for(e[0]=0;0==e[0];)d.nextBytes(e);c[--b]=e[0]}c[--b]=2;c[--b]=0;return new BigInteger(c)}function RSAKey(){this.n=null;this.e=0;this.coeff=this.dmq1=this.dmp1=this.q=this.p=this.d=null}
-function RSASetPublic(a,b){null!=a&&null!=b&&0<a.length&&0<b.length?(this.n=parseBigInt(a,16),this.e=parseInt(b,16)):alert("Invalid RSA public key")}function RSADoPublic(a){return a.modPowInt(this.e,this.n)}function RSAEncrypt(a){a=pkcs1pad2(a,this.n.bitLength()+7>>3);if(null==a)return null;a=this.doPublic(a);if(null==a)return null;a=a.toString(16);return 0==(a.length&1)?a:"0"+a}RSAKey.prototype.doPublic=RSADoPublic;RSAKey.prototype.setPublic=RSASetPublic;RSAKey.prototype.encrypt=RSAEncrypt;
+/*
+ <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
+ <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
+ <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
+ MIT
+ <a href="https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/LICENSE">MIT License</a>
+ <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
+ <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
+*/
+(function(a,b){"object"===typeof exports?module.exports.printStackTrace=b():"function"===typeof define&&define.amd?define(b):a.printStackTrace=b()})(this,function(){function a(b){b=b||{guess:!0};var c=b.e||null,d=!!b.guess,e=b.mode||null;b=new a.implementation;c=b.run(c,e);return d?b.guessAnonymousFunctions(c):c}a.implementation=function(){};a.implementation.prototype={run:function(a,c){a=a||this.createException();c=c||this.mode(a);return"other"===c?this.other(arguments.callee):this[c](a)},createException:function(){try{this.undef()}catch(a){return a}},
+mode:function(a){return"undefined"!==typeof window&&-1<window.navigator.userAgent.indexOf("PhantomJS")?"phantomjs":a.arguments&&a.stack?"chrome":a.stack&&a.sourceURL?"safari":a.stack&&a.number?"ie":a.stack&&a.fileName?"firefox":a.message&&a["opera#sourceloc"]?!a.stacktrace||-1<a.message.indexOf("\n")&&a.message.split("\n").length>a.stacktrace.split("\n").length?"opera9":"opera10a":a.message&&a.stack&&a.stacktrace?0>a.stacktrace.indexOf("called from line")?"opera10b":"opera11":a.stack&&!a.fileName?
+"chrome":"other"},instrumentFunction:function(b,c,d){b=b||window;var e=b[c];b[c]=function(){d.call(this,a().slice(4));return b[c]._instrumented.apply(this,arguments)};b[c]._instrumented=e},deinstrumentFunction:function(a,c){a[c].constructor===Function&&a[c]._instrumented&&a[c]._instrumented.constructor===Function&&(a[c]=a[c]._instrumented)},chrome:function(a){return(a.stack+"\n").replace(/^[\s\S]+?\s+at\s+/," at ").replace(/^\s+(at eval )?at\s+/gm,"").replace(/^([^\(]+?)([\n$])/gm,"{anonymous}() ($1)$2").replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm,
+"{anonymous}() ($1)").replace(/^(.+) \((.+)\)$/gm,"$1@$2").split("\n").slice(0,-1)},safari:function(a){return a.stack.replace(/\[native code\]\n/m,"").replace(/^(?=\w+Error\:).*$\n/m,"").replace(/^@/gm,"{anonymous}()@").split("\n")},ie:function(a){return a.stack.replace(/^\s*at\s+(.*)$/gm,"$1").replace(/^Anonymous function\s+/gm,"{anonymous}() ").replace(/^(.+)\s+\((.+)\)$/gm,"$1@$2").split("\n").slice(1)},firefox:function(a){return a.stack.replace(/(?:\n@:0)?\s+$/m,"").replace(/^(?:\((\S*)\))?@/gm,
+"{anonymous}($1)@").split("\n")},opera11:function(a){var c=/^.*line (\d+), column (\d+)(?: in (.+))? in (\S+):$/;a=a.stacktrace.split("\n");for(var d=[],e=0,f=a.length;e<f;e+=2){var g=c.exec(a[e]);if(g){var h=g[4]+":"+g[1]+":"+g[2],g=g[3]||"global code",g=g.replace(/<anonymous function: (\S+)>/,"$1").replace(/<anonymous function>/,"{anonymous}");d.push(g+"@"+h+" -- "+a[e+1].replace(/^\s+/,""))}}return d},opera10b:function(a){var c=/^(.*)@(.+):(\d+)$/;a=a.stacktrace.split("\n");for(var d=[],e=0,f=
+a.length;e<f;e++){var g=c.exec(a[e]);g&&d.push((g[1]?g[1]+"()":"global code")+"@"+g[2]+":"+g[3])}return d},opera10a:function(a){var c=/Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;a=a.stacktrace.split("\n");for(var d=[],e=0,f=a.length;e<f;e+=2){var g=c.exec(a[e]);g&&d.push((g[3]||"{anonymous}")+"()@"+g[2]+":"+g[1]+" -- "+a[e+1].replace(/^\s+/,""))}return d},opera9:function(a){var c=/Line (\d+).*script (?:in )?(\S+)/i;a=a.message.split("\n");for(var d=[],e=2,f=a.length;e<f;e+=2){var g=
+c.exec(a[e]);g&&d.push("{anonymous}()@"+g[2]+":"+g[1]+" -- "+a[e+1].replace(/^\s+/,""))}return d},phantomjs:function(a){var c=/(\S+) \((\S+)\)/i;a=a.stack.split("\n");for(var d=[],e=1,f=a.length;e<f;e++){a[e]=a[e].replace(/^\s+at\s+/gm,"");var g=c.exec(a[e]);g?d.push(g[1]+"()@"+g[2]):d.push("{anonymous}()@"+a[e])}return d},other:function(a){for(var c=/function(?:\s+([\w$]+))?\s*\(/,d=[],e,f,g=Array.prototype.slice;a&&10>d.length;){e=c.test(a.toString())?RegExp.$1||"{anonymous}":"{anonymous}";try{f=
+g.call(a.arguments||[])}catch(h){f=["Cannot access arguments: "+h]}d[d.length]=e+"("+this.stringifyArguments(f)+")";try{a=a.caller}catch(k){d[d.length]="Cannot access caller: "+k;break}}return d},stringifyArguments:function(a){for(var c=[],d=Array.prototype.slice,e=0;e<a.length;++e){var f=a[e];void 0===f?c[e]="undefined":null===f?c[e]="null":f.constructor&&(c[e]=f.constructor===Array?3>f.length?"["+this.stringifyArguments(f)+"]":"["+this.stringifyArguments(d.call(f,0,1))+"..."+this.stringifyArguments(d.call(f,
+-1))+"]":f.constructor===Object?"#object":f.constructor===Function?"#function":f.constructor===String?'"'+f+'"':f.constructor===Number?f:"?")}return c.join(",")},sourceCache:{},ajax:function(a){var c=this.createXMLHTTPObject();if(c)try{return c.open("GET",a,!1),c.send(null),c.responseText}catch(d){}return""},createXMLHTTPObject:function(){for(var a,c=[function(){return new XMLHttpRequest},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Msxml3.XMLHTTP")},
+function(){return new ActiveXObject("Microsoft.XMLHTTP")}],d=0;d<c.length;d++)try{return a=c[d](),this.createXMLHTTPObject=c[d],a}catch(e){}},isSameDomain:function(a){return"undefined"!==typeof location&&-1!==a.indexOf(location.hostname)},getSource:function(a){a in this.sourceCache||(this.sourceCache[a]=this.ajax(a).split("\n"));return this.sourceCache[a]},guessAnonymousFunctions:function(a){for(var c=0;c<a.length;++c){var d=/^(.*?)(?::(\d+))(?::(\d+))?(?: -- .+)?$/,e=a[c],f=/\{anonymous\}\(.*\)@(.*)/.exec(e);
+if(f){var g=d.exec(f[1]);g&&(d=g[1],f=g[2],g=g[3]||0,d&&this.isSameDomain(d)&&f&&(d=this.guessAnonymousFunction(d,f,g),a[c]=e.replace("{anonymous}",d)))}}return a},guessAnonymousFunction:function(a,c,d){var e;try{e=this.findFunctionName(this.getSource(a),c)}catch(f){e="getSource failed with url: "+a+", exception: "+f.toString()}return e},findFunctionName:function(a,c){for(var d=/function\s+([^(]*?)\s*\(([^)]*)\)/,e=/['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/,f=/['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/,
+g="",h,k=Math.min(c,20),l,m=0;m<k;++m)if(h=a[c-m-1],l=h.indexOf("//"),0<=l&&(h=h.substr(0,l)),h)if(g=h+g,(h=e.exec(g))&&h[1]||(h=d.exec(g))&&h[1]||(h=f.exec(g))&&h[1])return h[1];return"(?)"}};return a});var ndn=ndn||{},exports=ndn,module={};function require(){return ndn}exports.printStackTrace=printStackTrace;
+function DetectSubtleCrypto(){var a=!1;if("undefined"!==typeof crypto&&crypto&&crypto.subtle){var b={name:"RSASSA-PKCS1-v1_5",modulusLength:2048,hash:{name:"SHA-256"},publicExponent:new Uint8Array([1,0,1])},c;crypto.subtle.generateKey(b,!0,["sign","verify"]).then(function(a){c=a;return crypto.subtle.sign(b,a.privateKey,new Uint8Array([1,2,3,4,5]))}).then(function(a){return crypto.subtle.verify(b,c.publicKey,a,new Uint8Array([1,2,3,4,5]))}).then(function(a){return crypto.subtle.exportKey("pkcs8",c.privateKey)}).then(function(a){return crypto.subtle.importKey("pkcs8",
+a,b,!0,["sign"])}).then(function(a){return crypto.subtle.exportKey("spki",c.publicKey)}).then(function(a){return crypto.subtle.importKey("spki",a,b,!0,["verify"])}).then(function(a){a=new Uint8Array([1,2,3,4,5]);return crypto.subtle.digest({name:"SHA-256"},a.buffer)}).then(function(b){a=!0},function(a){console.log("DetectSubtleCrypto encountered error, not using crypto.subtle: ",a)})}return function(){return a}}var UseSubtleCrypto=DetectSubtleCrypto();exports.UseSubtleCrypto=UseSubtleCrypto;
+var CryptoJS=CryptoJS||function(a,b){var c={},d=c.lib={},e=d.Base=function(){function a(){}return{extend:function(b){a.prototype=this;var c=new a;b&&c.mixIn(b);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var b in a)a.hasOwnProperty(b)&&(this[b]=a[b]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},
+clone:function(){return this.init.prototype.extend(this)}}}(),f=d.WordArray=e.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=b?c:4*a.length},toString:function(a){return(a||h).stringify(this)},concat:function(a){var b=this.words,c=a.words,d=this.sigBytes;a=a.sigBytes;this.clamp();if(d%4)for(var e=0;e<a;e++)b[d+e>>>2]|=(c[e>>>2]>>>24-e%4*8&255)<<24-(d+e)%4*8;else for(e=0;e<a;e+=4)b[d+e>>>2]=c[e>>>2];this.sigBytes+=a;return this},clamp:function(){var b=this.words,c=this.sigBytes;b[c>>>
+2]&=4294967295<<32-c%4*8;b.length=a.ceil(c/4)},clone:function(){var a=e.clone.call(this);a.words=this.words.slice(0);return a},random:function(b){for(var c=[],d=0;d<b;d+=4)c.push(4294967296*a.random()|0);return new f.init(c,b)}}),g=c.enc={},h=g.Hex={stringify:function(a){var b=a.words;a=a.sigBytes;for(var c=[],d=0;d<a;d++){var e=b[d>>>2]>>>24-d%4*8&255;c.push((e>>>4).toString(16));c.push((e&15).toString(16))}return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;d<b;d+=2)c[d>>>3]|=parseInt(a.substr(d,
+2),16)<<24-d%8*4;return new f.init(c,b/2)}},k=g.Latin1={stringify:function(a){var b=a.words;a=a.sigBytes;for(var c=[],d=0;d<a;d++)c.push(String.fromCharCode(b[d>>>2]>>>24-d%4*8&255));return c.join("")},parse:function(a){for(var b=a.length,c=[],d=0;d<b;d++)c[d>>>2]|=(a.charCodeAt(d)&255)<<24-d%4*8;return new f.init(c,b)}},l=g.Utf8={stringify:function(a){try{return decodeURIComponent(escape(k.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data");}},parse:function(a){return k.parse(unescape(encodeURIComponent(a)))}},
+m=d.BufferedBlockAlgorithm=e.extend({reset:function(){this._data=new f.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=l.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(b){var c=this._data,d=c.words,e=c.sigBytes,g=this.blockSize,h=e/(4*g),h=b?a.ceil(h):a.max((h|0)-this._minBufferSize,0);b=h*g;e=a.min(4*b,e);if(b){for(var k=0;k<b;k+=g)this._doProcessBlock(d,k);k=d.splice(0,b);c.sigBytes-=e}return new f.init(k,e)},clone:function(){var a=e.clone.call(this);
+a._data=this._data.clone();return a},_minBufferSize:0});d.Hasher=m.extend({cfg:e.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){m.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,c){return(new a.init(c)).finalize(b)}},_createHmacHelper:function(a){return function(b,c){return(new q.HMAC.init(a,
+c)).finalize(b)}}});var q=c.algo={};return c}(Math);exports.CryptoJS=CryptoJS;module.exports=exports;var C=require("./core.js").CryptoJS;
+(function(a){var b=C.lib,c=b.WordArray,d=b.Hasher,b=C.algo,e=[],f=[];(function(){function b(c){for(var d=a.sqrt(c),e=2;e<=d;e++)if(!(c%e))return!1;return!0}function c(a){return 4294967296*(a-(a|0))|0}for(var d=2,g=0;64>g;)b(d)&&(8>g&&(e[g]=c(a.pow(d,0.5))),f[g]=c(a.pow(d,1/3)),g++),d++})();var g=[],b=b.SHA256=d.extend({_doReset:function(){this._hash=new c.init(e.slice(0))},_doProcessBlock:function(a,b){for(var c=this._hash.words,d=c[0],e=c[1],p=c[2],s=c[3],w=c[4],M=c[5],O=c[6],K=c[7],x=0;64>x;x++){if(16>
+x)g[x]=a[b+x]|0;else{var A=g[x-15],I=g[x-2];g[x]=((A<<25|A>>>7)^(A<<14|A>>>18)^A>>>3)+g[x-7]+((I<<15|I>>>17)^(I<<13|I>>>19)^I>>>10)+g[x-16]}A=K+((w<<26|w>>>6)^(w<<21|w>>>11)^(w<<7|w>>>25))+(w&M^~w&O)+f[x]+g[x];I=((d<<30|d>>>2)^(d<<19|d>>>13)^(d<<10|d>>>22))+(d&e^d&p^e&p);K=O;O=M;M=w;w=s+A|0;s=p;p=e;e=d;d=A+I|0}c[0]=c[0]+d|0;c[1]=c[1]+e|0;c[2]=c[2]+p|0;c[3]=c[3]+s|0;c[4]=c[4]+w|0;c[5]=c[5]+M|0;c[6]=c[6]+O|0;c[7]=c[7]+K|0},_doFinalize:function(){var b=this._data,c=b.words,d=8*this._nDataBytes,e=8*b.sigBytes;
+c[e>>>5]|=128<<24-e%32;c[(e+64>>>9<<4)+14]=a.floor(d/4294967296);c[(e+64>>>9<<4)+15]=d;b.sigBytes=4*c.length;this._process();return this._hash},clone:function(){var a=d.clone.call(this);a._hash=this._hash.clone();return a}});C.SHA256=d._createHelper(b);C.HmacSHA256=d._createHmacHelper(b)})(Math);exports.CryptoJS=C;module.exports=exports;
+(function(){var a=CryptoJS,b=a.enc.Utf8;a.algo.HMAC=a.lib.Base.extend({init:function(a,d){a=this._hasher=new a.init;"string"==typeof d&&(d=b.parse(d));var e=a.blockSize,f=4*e;d.sigBytes>f&&(d=a.finalize(d));d.clamp();for(var g=this._oKey=d.clone(),h=this._iKey=d.clone(),k=g.words,l=h.words,m=0;m<e;m++)k[m]^=1549556828,l[m]^=909522486;g.sigBytes=h.sigBytes=f;this.reset()},reset:function(){var a=this._hasher;a.reset();a.update(this._iKey)},update:function(a){this._hasher.update(a);return this},finalize:function(a){var b=
+this._hasher;a=b.finalize(a);b.reset();return b.finalize(this._oKey.clone().concat(a))}})})();var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",b64pad="=";
+function hex2b64(a){var b,c,d="";for(b=0;b+3<=a.length;b+=3)c=parseInt(a.substring(b,b+3),16),d+=b64map.charAt(c>>6)+b64map.charAt(c&63);b+1==a.length?(c=parseInt(a.substring(b,b+1),16),d+=b64map.charAt(c<<2)):b+2==a.length&&(c=parseInt(a.substring(b,b+2),16),d+=b64map.charAt(c>>2)+b64map.charAt((c&3)<<4));if(b64pad)for(;0<(d.length&3);)d+=b64pad;return d}
+function b64tohex(a){var b="",c,d=0,e,f;for(c=0;c<a.length&&a.charAt(c)!=b64pad;++c)f=b64map.indexOf(a.charAt(c)),0>f||(0==d?(b+=int2char(f>>2),e=f&3,d=1):1==d?(b+=int2char(e<<2|f>>4),e=f&15,d=2):2==d?(b+=int2char(e),b+=int2char(f>>2),e=f&3,d=3):(b+=int2char(e<<2|f>>4),b+=int2char(f&15),d=0));1==d&&(b+=int2char(e<<2));return b}function b64toBA(a){a=b64tohex(a);var b,c=[];for(b=0;2*b<a.length;++b)c[b]=parseInt(a.substring(2*b,2*b+2),16);return c}exports.b64tohex=b64tohex;exports.b64toBA=b64toBA;
+exports.hex2b64=hex2b64;module.exports=exports;function Arcfour(){this.j=this.i=0;this.S=[]}function ARC4init(a){var b,c,d;for(b=0;256>b;++b)this.S[b]=b;for(b=c=0;256>b;++b)c=c+this.S[b]+a[b%a.length]&255,d=this.S[b],this.S[b]=this.S[c],this.S[c]=d;this.j=this.i=0}function ARC4next(){var a;this.i=this.i+1&255;this.j=this.j+this.S[this.i]&255;a=this.S[this.i];this.S[this.i]=this.S[this.j];this.S[this.j]=a;return this.S[a+this.S[this.i]&255]}Arcfour.prototype.init=ARC4init;Arcfour.prototype.next=ARC4next;
+function prng_newstate(){return new Arcfour}var rng_psize=256,rng_state,rng_pool,rng_pptr;function rng_seed_int(a){rng_pool[rng_pptr++]^=a&255;rng_pool[rng_pptr++]^=a>>8&255;rng_pool[rng_pptr++]^=a>>16&255;rng_pool[rng_pptr++]^=a>>24&255;rng_pptr>=rng_psize&&(rng_pptr-=rng_psize)}function rng_seed_time(){rng_seed_int((new Date).getTime())}
+if(null==rng_pool){rng_pool=[];rng_pptr=0;var t;if("Netscape"==navigator.appName&&"5">navigator.appVersion&&window.crypto){var z=window.crypto.random(32);for(t=0;t<z.length;++t)rng_pool[rng_pptr++]=z.charCodeAt(t)&255}for(;rng_pptr<rng_psize;)t=Math.floor(65536*Math.random()),rng_pool[rng_pptr++]=t>>>8,rng_pool[rng_pptr++]=t&255;rng_pptr=0;rng_seed_time()}
+function rng_get_byte(){if(null==rng_state){rng_seed_time();rng_state=prng_newstate();rng_state.init(rng_pool);for(rng_pptr=0;rng_pptr<rng_pool.length;++rng_pptr)rng_pool[rng_pptr]=0;rng_pptr=0}return rng_state.next()}function rng_get_bytes(a){var b;for(b=0;b<a.length;++b)a[b]=rng_get_byte()}function SecureRandom(){}SecureRandom.prototype.nextBytes=rng_get_bytes;var intShim=require("jsbn");function parseBigInt(a,b){return new BigInteger(a,b)}
+function linebrk(a,b){for(var c="",d=0;d+b<a.length;)c+=a.substring(d,d+b)+"\n",d+=b;return c+a.substring(d,a.length)}function byte2Hex(a){return 16>a?"0"+a.toString(16):a.toString(16)}
+function pkcs1pad2(a,b){if(b<a.length+11)return alert("Message too long for RSA"),null;for(var c=[],d=a.length-1;0<=d&&0<b;){var e=a.charCodeAt(d--);128>e?c[--b]=e:127<e&&2048>e?(c[--b]=e&63|128,c[--b]=e>>6|192):(c[--b]=e&63|128,c[--b]=e>>6&63|128,c[--b]=e>>12|224)}c[--b]=0;d=new SecureRandom;for(e=[];2<b;){for(e[0]=0;0==e[0];)d.nextBytes(e);c[--b]=e[0]}c[--b]=2;c[--b]=0;return new BigInteger(c)}
+function oaep_mgf1_arr(a,b,c){for(var d="",e=0;d.length<b;)d+=c(String.fromCharCode.apply(String,a.concat([(e&4278190080)>>24,(e&16711680)>>16,(e&65280)>>8,e&255]))),e+=1;return d}var SHA1_SIZE=20;
+function oaep_pad(a,b,c){if(a.length+2*SHA1_SIZE+2>b)throw"Message too long for RSA";var d="",e;for(e=0;e<b-a.length-2*SHA1_SIZE-2;e+=1)d+="\x00";b=rstr_sha1("")+d+"\u0001"+a;a=Array(SHA1_SIZE);(new SecureRandom).nextBytes(a);d=oaep_mgf1_arr(a,b.length,c||rstr_sha1);c=[];for(e=0;e<b.length;e+=1)c[e]=b.charCodeAt(e)^d.charCodeAt(e);b=oaep_mgf1_arr(c,a.length,rstr_sha1);d=[0];for(e=0;e<a.length;e+=1)d[e+1]=a[e]^b.charCodeAt(e);return new BigInteger(d.concat(c))}
+function RSAKey(){this.n=null;this.e=0;this.coeff=this.dmq1=this.dmp1=this.q=this.p=this.d=null}function RSASetPublic(a,b){this.isPublic=!0;"string"!==typeof a?(this.n=a,this.e=b):null!=a&&null!=b&&0<a.length&&0<b.length?(this.n=parseBigInt(a,16),this.e=parseInt(b,16)):alert("Invalid RSA public key")}function RSADoPublic(a){return a.modPowInt(this.e,this.n)}
+function RSAEncrypt(a){a=pkcs1pad2(a,this.n.bitLength()+7>>3);if(null==a)return null;a=this.doPublic(a);if(null==a)return null;a=a.toString(16);return 0==(a.length&1)?a:"0"+a}function RSAEncryptOAEP(a,b){var c=oaep_pad(a,this.n.bitLength()+7>>3,b);if(null==c)return null;c=this.doPublic(c);if(null==c)return null;c=c.toString(16);return 0==(c.length&1)?c:"0"+c}RSAKey.prototype.doPublic=RSADoPublic;RSAKey.prototype.setPublic=RSASetPublic;RSAKey.prototype.encrypt=RSAEncrypt;
+RSAKey.prototype.encryptOAEP=RSAEncryptOAEP;RSAKey.prototype.type="RSA";exports.RSAKey=RSAKey;module.exports=exports;var intShim=require("jsbn"),BigInteger=intShim.BigInteger?intShim.BigInteger:intShim,RSAKey=require("./rsa.js").RSAKey;
function pkcs1unpad2(a,b){for(var c=a.toByteArray(),d=0;d<c.length&&0==c[d];)++d;if(c.length-d!=b-1||2!=c[d])return null;for(++d;0!=c[d];)if(++d>=c.length)return null;for(var e="";++d<c.length;){var f=c[d]&255;128>f?e+=String.fromCharCode(f):191<f&&224>f?(e+=String.fromCharCode((f&31)<<6|c[d+1]&63),++d):(e+=String.fromCharCode((f&15)<<12|(c[d+1]&63)<<6|c[d+2]&63),d+=2)}return e}
-function RSASetPrivate(a,b,c){null!=a&&null!=b&&0<a.length&&0<b.length?(this.n=parseBigInt(a,16),this.e=parseInt(b,16),this.d=parseBigInt(c,16)):alert("Invalid RSA private key")}
-function RSASetPrivateEx(a,b,c,d,e,f,g,h){null!=a&&null!=b&&0<a.length&&0<b.length?(this.n=parseBigInt(a,16),this.e=parseInt(b,16),this.d=parseBigInt(c,16),this.p=parseBigInt(d,16),this.q=parseBigInt(e,16),this.dmp1=parseBigInt(f,16),this.dmq1=parseBigInt(g,16),this.coeff=parseBigInt(h,16)):alert("Invalid RSA private key")}
-function RSAGenerate(a,b){var c=new SecureRandom,d=a>>1;this.e=parseInt(b,16);for(var e=new BigInteger(b,16);;){for(;!(this.p=new BigInteger(a-d,1,c),0==this.p.subtract(BigInteger.ONE).gcd(e).compareTo(BigInteger.ONE)&&this.p.isProbablePrime(10)););for(;!(this.q=new BigInteger(d,1,c),0==this.q.subtract(BigInteger.ONE).gcd(e).compareTo(BigInteger.ONE)&&this.q.isProbablePrime(10)););if(0>=this.p.compareTo(this.q)){var f=this.p;this.p=this.q;this.q=f}var f=this.p.subtract(BigInteger.ONE),g=this.q.subtract(BigInteger.ONE),
-h=f.multiply(g);if(0==h.gcd(e).compareTo(BigInteger.ONE)){this.n=this.p.multiply(this.q);this.d=e.modInverse(h);this.dmp1=this.d.mod(f);this.dmq1=this.d.mod(g);this.coeff=this.q.modInverse(this.p);break}}}function RSADoPrivate(a){if(null==this.p||null==this.q)return a.modPow(this.d,this.n);var b=a.mod(this.p).modPow(this.dmp1,this.p);for(a=a.mod(this.q).modPow(this.dmq1,this.q);0>b.compareTo(a);)b=b.add(this.p);return b.subtract(a).multiply(this.coeff).mod(this.p).multiply(this.q).add(a)}
-function RSADecrypt(a){a=parseBigInt(a,16);a=this.doPrivate(a);return null==a?null:pkcs1unpad2(a,this.n.bitLength()+7>>3)}RSAKey.prototype.doPrivate=RSADoPrivate;RSAKey.prototype.setPrivate=RSASetPrivate;RSAKey.prototype.setPrivateEx=RSASetPrivateEx;RSAKey.prototype.generate=RSAGenerate;RSAKey.prototype.decrypt=RSADecrypt;function _rsapem_pemToBase64(a){a=a.replace("-----BEGIN RSA PRIVATE KEY-----","");a=a.replace("-----END RSA PRIVATE KEY-----","");return a=a.replace(/[ \n]+/g,"")}
-function _rsapem_getPosArrayOfChildrenFromHex(a){var b=[],c=ASN1HEX.getStartPosOfV_AtObj(a,0),d=ASN1HEX.getPosOfNextSibling_AtObj(a,c),e=ASN1HEX.getPosOfNextSibling_AtObj(a,d),f=ASN1HEX.getPosOfNextSibling_AtObj(a,e),g=ASN1HEX.getPosOfNextSibling_AtObj(a,f),h=ASN1HEX.getPosOfNextSibling_AtObj(a,g),j=ASN1HEX.getPosOfNextSibling_AtObj(a,h),l=ASN1HEX.getPosOfNextSibling_AtObj(a,j);a=ASN1HEX.getPosOfNextSibling_AtObj(a,l);b.push(c,d,e,f,g,h,j,l,a);return b}
-function _rsapem_getHexValueArrayOfChildrenFromHex(a){var b=_rsapem_getPosArrayOfChildrenFromHex(a),c=ASN1HEX.getHexOfV_AtObj(a,b[0]),d=ASN1HEX.getHexOfV_AtObj(a,b[1]),e=ASN1HEX.getHexOfV_AtObj(a,b[2]),f=ASN1HEX.getHexOfV_AtObj(a,b[3]),g=ASN1HEX.getHexOfV_AtObj(a,b[4]),h=ASN1HEX.getHexOfV_AtObj(a,b[5]),j=ASN1HEX.getHexOfV_AtObj(a,b[6]),l=ASN1HEX.getHexOfV_AtObj(a,b[7]);a=ASN1HEX.getHexOfV_AtObj(a,b[8]);b=[];b.push(c,d,e,f,g,h,j,l,a);return b}
-function _rsapem_readPrivateKeyFromPEMString(a){a=_rsapem_pemToBase64(a);a=b64tohex(a);a=_rsapem_getHexValueArrayOfChildrenFromHex(a);this.setPrivateEx(a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8])}RSAKey.prototype.readPrivateKeyFromPEMString=_rsapem_readPrivateKeyFromPEMString;var _RSASIGN_DIHEAD=[];_RSASIGN_DIHEAD.sha1="3021300906052b0e03021a05000414";_RSASIGN_DIHEAD.sha256="3031300d060960864801650304020105000420";_RSASIGN_DIHEAD.sha384="3041300d060960864801650304020205000430";
-_RSASIGN_DIHEAD.sha512="3051300d060960864801650304020305000440";_RSASIGN_DIHEAD.md2="3020300c06082a864886f70d020205000410";_RSASIGN_DIHEAD.md5="3020300c06082a864886f70d020505000410";_RSASIGN_DIHEAD.ripemd160="3021300906052b2403020105000414";var _RSASIGN_HASHHEXFUNC=[];_RSASIGN_HASHHEXFUNC.sha1=function(a){return hex_sha1(a)};_RSASIGN_HASHHEXFUNC.sha256=function(a){return hex_sha256(a)};_RSASIGN_HASHHEXFUNC.sha512=function(a){return hex_sha512(a)};_RSASIGN_HASHHEXFUNC.md5=function(a){return hex_md5(a)};
-_RSASIGN_HASHHEXFUNC.ripemd160=function(a){return hex_rmd160(a)};var _RSASIGN_HASHBYTEFUNC=[];_RSASIGN_HASHBYTEFUNC.sha256=function(a){return hex_sha256_from_bytes(a)};var _RE_HEXDECONLY=RegExp("");_RE_HEXDECONLY.compile("[^0-9a-f]","gi");function _rsasign_getHexPaddedDigestInfoForString(a,b,c){b/=4;a=(0,_RSASIGN_HASHHEXFUNC[c])(a);c="00"+_RSASIGN_DIHEAD[c]+a;a="";b=b-4-c.length;for(var d=0;d<b;d+=2)a+="ff";return sPaddedMessageHex="0001"+a+c}
-function _rsasign_getHexPaddedDigestInfoForStringHEX(a,b,c){b/=4;a=(0,_RSASIGN_HASHHEXFUNC[c])(a);c="00"+_RSASIGN_DIHEAD[c]+a;a="";b=b-4-c.length;for(var d=0;d<b;d+=2)a+="ff";return sPaddedMessageHex="0001"+a+c}function _rsasign_getHexPaddedDigestInfoForByteArray(a,b,c){b/=4;a=(0,_RSASIGN_HASHBYTEFUNC[c])(a);c="00"+_RSASIGN_DIHEAD[c]+a;a="";b=b-4-c.length;for(var d=0;d<b;d+=2)a+="ff";return sPaddedMessageHex="0001"+a+c}
-function _zeroPaddingOfSignature(a,b){for(var c="",d=b/4-a.length,e=0;e<d;e++)c+="0";return c+a}function _rsasign_signString(a,b){var c=_rsasign_getHexPaddedDigestInfoForString(a,this.n.bitLength(),b),c=parseBigInt(c,16),c=this.doPrivate(c).toString(16);return _zeroPaddingOfSignature(c,this.n.bitLength())}
-function _rsasign_signStringHEX(a,b){var c=_rsasign_getHexPaddedDigestInfoForString(a,this.n.bitLength(),b),c=parseBigInt(c,16),c=this.doPrivate(c).toString(16);return _zeroPaddingOfSignature(c,this.n.bitLength())}function _rsasign_signByteArray(a,b,c){a=_rsasign_getHexPaddedDigestInfoForByteArray(a,c.n.bitLength(),b);a=parseBigInt(a,16);a=c.doPrivate(a).toString(16);return _zeroPaddingOfSignature(a,c.n.bitLength())}
-function _rsasign_signByteArrayWithSHA256(a){return _rsasign_signByteArray(a,"sha256",this)}function _rsasign_signStringWithSHA1(a){return _rsasign_signString(a,"sha1")}function _rsasign_signStringWithSHA256(a){return _rsasign_signString(a,"sha256")}function _rsasign_getDecryptSignatureBI(a,b,c){var d=new RSAKey;d.setPublic(b,c);return d.doPublic(a)}function _rsasign_getHexDigestInfoFromSig(a,b,c){return _rsasign_getDecryptSignatureBI(a,b,c).toString(16).replace(/^1f+00/,"")}
-function _rsasign_getAlgNameAndHashFromHexDisgestInfo(a){for(var b in _RSASIGN_DIHEAD){var c=_RSASIGN_DIHEAD[b],d=c.length;if(a.substring(0,d)==c)return[b,a.substring(d)]}return[]}function _rsasign_verifySignatureWithArgs(a,b,c,d){b=_rsasign_getHexDigestInfoFromSig(b,c,d);c=_rsasign_getAlgNameAndHashFromHexDisgestInfo(b);if(0==c.length)return!1;b=c[1];a=(0,_RSASIGN_HASHHEXFUNC[c[0]])(a);return b==a}
-function _rsasign_verifyHexSignatureForMessage(a,b){var c=parseBigInt(a,16);return _rsasign_verifySignatureWithArgs(b,c,this.n.toString(16),this.e.toString(16))}
-function _rsasign_verifyString(a,b){b=b.replace(_RE_HEXDECONLY,"");3<LOG&&console.log("n is "+this.n);3<LOG&&console.log("e is "+this.e);if(b.length!=this.n.bitLength()/4)return 0;b=b.replace(/[ \n]+/g,"");var c=parseBigInt(b,16),c=this.doPublic(c).toString(16).replace(/^1f+00/,""),d=_rsasign_getAlgNameAndHashFromHexDisgestInfo(c);if(0==d.length)return!1;c=d[1];d=(0,_RSASIGN_HASHHEXFUNC[d[0]])(a);return c==d}
-function _rsasign_verifyByteArray(a,b,c){c=c.replace(_RE_HEXDECONLY,"");3<LOG&&console.log("n is "+this.n);3<LOG&&console.log("e is "+this.e);if(c.length!=this.n.bitLength()/4)return 0;c=c.replace(/[ \n]+/g,"");c=parseBigInt(c,16);c=this.doPublic(c).toString(16).replace(/^1f+00/,"");c=_rsasign_getAlgNameAndHashFromHexDisgestInfo(c);if(0==c.length)return!1;var d=c[0];c=c[1];var e=null;if(null==b)e=(0,_RSASIGN_HASHBYTEFUNC[d])(a);else{a=hex_sha256_from_bytes(a);d=b.path.index;for(e=b.path.digestList.length-
-1;0<=e;e--){var f="",f=0==d%2?a+b.path.digestList[e]:b.path.digestList[e]+a;a=hex_sha256_from_bytes(DataUtils.toNumbers(f));d=Math.floor(d/2)}e=hex_sha256_from_bytes(DataUtils.toNumbers(a))}return c==e}RSAKey.prototype.signString=_rsasign_signString;RSAKey.prototype.signByteArray=_rsasign_signByteArray;RSAKey.prototype.signByteArrayWithSHA256=_rsasign_signByteArrayWithSHA256;RSAKey.prototype.signStringWithSHA1=_rsasign_signStringWithSHA1;RSAKey.prototype.signStringWithSHA256=_rsasign_signStringWithSHA256;
-RSAKey.prototype.sign=_rsasign_signString;RSAKey.prototype.signWithSHA1=_rsasign_signStringWithSHA1;RSAKey.prototype.signWithSHA256=_rsasign_signStringWithSHA256;RSAKey.prototype.verifyByteArray=_rsasign_verifyByteArray;RSAKey.prototype.verifyString=_rsasign_verifyString;RSAKey.prototype.verifyHexSignatureForMessage=_rsasign_verifyHexSignatureForMessage;RSAKey.prototype.verify=_rsasign_verifyString;RSAKey.prototype.verifyHexSignatureForByteArrayMessage=_rsasign_verifyHexSignatureForMessage;
-function _asnhex_getByteLengthOfL_AtObj(a,b){if("8"!=a.substring(b+2,b+3))return 1;var c=parseInt(a.substring(b+3,b+4));return 0==c?-1:0<c&&10>c?c+1:-2}function _asnhex_getHexOfL_AtObj(a,b){var c=_asnhex_getByteLengthOfL_AtObj(a,b);return 1>c?"":a.substring(b+2,b+2+2*c)}function _asnhex_getIntOfL_AtObj(a,b){var c=_asnhex_getHexOfL_AtObj(a,b);return""==c?-1:(8>parseInt(c.substring(0,1))?parseBigInt(c,16):parseBigInt(c.substring(2),16)).intValue()}
-function _asnhex_getStartPosOfV_AtObj(a,b){var c=_asnhex_getByteLengthOfL_AtObj(a,b);return 0>c?c:b+2*(c+1)}function _asnhex_getHexOfV_AtObj(a,b){var c=_asnhex_getStartPosOfV_AtObj(a,b),d=_asnhex_getIntOfL_AtObj(a,b);return a.substring(c,c+2*d)}function _asnhex_getHexOfTLV_AtObj(a,b){var c=a.substr(b,2),d=_asnhex_getHexOfL_AtObj(a,b),e=_asnhex_getHexOfV_AtObj(a,b);return c+d+e}
-function _asnhex_getPosOfNextSibling_AtObj(a,b){var c=_asnhex_getStartPosOfV_AtObj(a,b),d=_asnhex_getIntOfL_AtObj(a,b);return c+2*d}function _asnhex_getPosArrayOfChildren_AtObj(a,b){var c=[],d=_asnhex_getStartPosOfV_AtObj(a,b);c.push(d);for(var e=_asnhex_getIntOfL_AtObj(a,b),f=d,g=0;;){f=_asnhex_getPosOfNextSibling_AtObj(a,f);if(null==f||f-d>=2*e)break;if(200<=g)break;c.push(f);g++}return c}function _asnhex_getNthChildIndex_AtObj(a,b,c){return _asnhex_getPosArrayOfChildren_AtObj(a,b)[c]}
-function _asnhex_getDecendantIndexByNthList(a,b,c){if(0==c.length)return b;var d=c.shift();b=_asnhex_getPosArrayOfChildren_AtObj(a,b);return _asnhex_getDecendantIndexByNthList(a,b[d],c)}function _asnhex_getDecendantHexTLVByNthList(a,b,c){b=_asnhex_getDecendantIndexByNthList(a,b,c);return _asnhex_getHexOfTLV_AtObj(a,b)}function _asnhex_getDecendantHexVByNthList(a,b,c){b=_asnhex_getDecendantIndexByNthList(a,b,c);return _asnhex_getHexOfV_AtObj(a,b)}function ASN1HEX(){return ASN1HEX}
-ASN1HEX.getByteLengthOfL_AtObj=_asnhex_getByteLengthOfL_AtObj;ASN1HEX.getHexOfL_AtObj=_asnhex_getHexOfL_AtObj;ASN1HEX.getIntOfL_AtObj=_asnhex_getIntOfL_AtObj;ASN1HEX.getStartPosOfV_AtObj=_asnhex_getStartPosOfV_AtObj;ASN1HEX.getHexOfV_AtObj=_asnhex_getHexOfV_AtObj;ASN1HEX.getHexOfTLV_AtObj=_asnhex_getHexOfTLV_AtObj;ASN1HEX.getPosOfNextSibling_AtObj=_asnhex_getPosOfNextSibling_AtObj;ASN1HEX.getPosArrayOfChildren_AtObj=_asnhex_getPosArrayOfChildren_AtObj;ASN1HEX.getNthChildIndex_AtObj=_asnhex_getNthChildIndex_AtObj;
-ASN1HEX.getDecendantIndexByNthList=_asnhex_getDecendantIndexByNthList;ASN1HEX.getDecendantHexVByNthList=_asnhex_getDecendantHexVByNthList;ASN1HEX.getDecendantHexTLVByNthList=_asnhex_getDecendantHexTLVByNthList;function _x509_pemToBase64(a){a=a.replace("-----BEGIN CERTIFICATE-----","");a=a.replace("-----END CERTIFICATE-----","");return a=a.replace(/[ \n]+/g,"")}function _x509_pemToHex(a){a=_x509_pemToBase64(a);return b64tohex(a)}
-function _x509_getHexTbsCertificateFromCert(a){return ASN1HEX.getStartPosOfV_AtObj(a,0)}function _x509_getSubjectPublicKeyInfoPosFromCertHex(a){var b=ASN1HEX.getStartPosOfV_AtObj(a,0),b=ASN1HEX.getPosArrayOfChildren_AtObj(a,b);return 1>b.length?-1:"a003020102"==a.substring(b[0],b[0]+10)?6>b.length?-1:b[6]:5>b.length?-1:b[5]}
-function _x509_getSubjectPublicKeyPosFromCertHex(a,b){null==b&&(b=_x509_getSubjectPublicKeyInfoPosFromCertHex(a));if(-1==b)return-1;var c=ASN1HEX.getPosArrayOfChildren_AtObj(a,b);if(2!=c.length)return-1;c=c[1];if("03"!=a.substring(c,c+2))return-1;c=ASN1HEX.getStartPosOfV_AtObj(a,c);return"00"!=a.substring(c,c+2)?-1:c+2}
-function _x509_getPublicKeyHexArrayFromCertHex(a,b){null==b&&(b=_x509_getSubjectPublicKeyPosFromCertHex(a));var c=ASN1HEX.getPosArrayOfChildren_AtObj(a,b);4<LOG&&(console.log("a is now"),console.log(c));if(2>c.length)return[];var d=ASN1HEX.getHexOfV_AtObj(a,c[0]),c=ASN1HEX.getHexOfV_AtObj(a,c[1]);return null!=d&&null!=c?[d,c]:[]}function _x509_getPublicKeyHexArrayFromCertPEM(a){a=_x509_pemToHex(a);return _x509_getPublicKeyHexArrayFromCertHex(a)}
-function _x509_getSerialNumberHex(){return ASN1HEX.getDecendantHexVByNthList(this.hex,0,[0,1])}function _x509_getIssuerHex(){return ASN1HEX.getDecendantHexTLVByNthList(this.hex,0,[0,3])}function _x509_getIssuerString(){return _x509_hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex,0,[0,3]))}function _x509_getSubjectHex(){return ASN1HEX.getDecendantHexTLVByNthList(this.hex,0,[0,5])}function _x509_getSubjectString(){return _x509_hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex,0,[0,5]))}
-function _x509_getNotBefore(){var a=ASN1HEX.getDecendantHexVByNthList(this.hex,0,[0,4,0]),a=a.replace(/(..)/g,"%$1");return a=decodeURIComponent(a)}function _x509_getNotAfter(){var a=ASN1HEX.getDecendantHexVByNthList(this.hex,0,[0,4,1]),a=a.replace(/(..)/g,"%$1");return a=decodeURIComponent(a)}_x509_DN_ATTRHEX={"0603550406":"C","060355040a":"O","060355040b":"OU","0603550403":"CN","0603550405":"SN","0603550408":"ST","0603550407":"L"};
-function _x509_hex2dn(a){for(var b="",c=ASN1HEX.getPosArrayOfChildren_AtObj(a,0),d=0;d<c.length;d++)var e=ASN1HEX.getHexOfTLV_AtObj(a,c[d]),b=b+"/"+_x509_hex2rdn(e);return b}function _x509_hex2rdn(a){var b=ASN1HEX.getDecendantHexTLVByNthList(a,0,[0,0]),c=ASN1HEX.getDecendantHexVByNthList(a,0,[0,1]);a="";try{a=_x509_DN_ATTRHEX[b]}catch(d){a=b}c=c.replace(/(..)/g,"%$1");b=decodeURIComponent(c);return a+"="+b}
-function _x509_readCertPEM(a){a=_x509_pemToHex(a);var b=_x509_getPublicKeyHexArrayFromCertHex(a);4<LOG&&(console.log("HEX VALUE IS "+a),console.log("type of a"+typeof b),console.log("a VALUE IS "),console.log(b),console.log("a[0] VALUE IS "+b[0]),console.log("a[1] VALUE IS "+b[1]));var c=new RSAKey;c.setPublic(b[0],b[1]);this.subjectPublicKeyRSA=c;this.subjectPublicKeyRSA_hN=b[0];this.subjectPublicKeyRSA_hE=b[1];this.hex=a}
-function _x509_readCertHex(a){a=a.toLowerCase();var b=_x509_getPublicKeyHexArrayFromCertHex(a),c=new RSAKey;c.setPublic(b[0],b[1]);this.subjectPublicKeyRSA=c;this.subjectPublicKeyRSA_hN=b[0];this.subjectPublicKeyRSA_hE=b[1];this.hex=a}function _x509_readCertPEMWithoutRSAInit(a){a=_x509_pemToHex(a);var b=_x509_getPublicKeyHexArrayFromCertHex(a);this.subjectPublicKeyRSA.setPublic(b[0],b[1]);this.subjectPublicKeyRSA_hN=b[0];this.subjectPublicKeyRSA_hE=b[1];this.hex=a}
-function X509(){this.hex=this.subjectPublicKeyRSA_hE=this.subjectPublicKeyRSA_hN=this.subjectPublicKeyRSA=null}X509.prototype.readCertPEM=_x509_readCertPEM;X509.prototype.readCertHex=_x509_readCertHex;X509.prototype.readCertPEMWithoutRSAInit=_x509_readCertPEMWithoutRSAInit;X509.prototype.getSerialNumberHex=_x509_getSerialNumberHex;X509.prototype.getIssuerHex=_x509_getIssuerHex;X509.prototype.getSubjectHex=_x509_getSubjectHex;X509.prototype.getIssuerString=_x509_getIssuerString;
-X509.prototype.getSubjectString=_x509_getSubjectString;X509.prototype.getNotBefore=_x509_getNotBefore;X509.prototype.getNotAfter=_x509_getNotAfter;var dbits,canary=0xdeadbeefcafe,j_lm=15715070==(canary&16777215);function BigInteger(a,b,c){null!=a&&("number"==typeof a?this.fromNumber(a,b,c):null==b&&"string"!=typeof a?this.fromString(a,256):this.fromString(a,b))}function nbi(){return new BigInteger(null)}
-function am1(a,b,c,d,e,f){for(;0<=--f;){var g=b*this[a++]+c[d]+e;e=Math.floor(g/67108864);c[d++]=g&67108863}return e}function am2(a,b,c,d,e,f){var g=b&32767;for(b>>=15;0<=--f;){var h=this[a]&32767,j=this[a++]>>15,l=b*h+j*g,h=g*h+((l&32767)<<15)+c[d]+(e&1073741823);e=(h>>>30)+(l>>>15)+b*j+(e>>>30);c[d++]=h&1073741823}return e}
-function am3(a,b,c,d,e,f){var g=b&16383;for(b>>=14;0<=--f;){var h=this[a]&16383,j=this[a++]>>14,l=b*h+j*g,h=g*h+((l&16383)<<14)+c[d]+e;e=(h>>28)+(l>>14)+b*j;c[d++]=h&268435455}return e}j_lm&&"Microsoft Internet Explorer"==navigator.appName?(BigInteger.prototype.am=am2,dbits=30):j_lm&&"Netscape"!=navigator.appName?(BigInteger.prototype.am=am1,dbits=26):(BigInteger.prototype.am=am3,dbits=28);BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=(1<<dbits)-1;BigInteger.prototype.DV=1<<dbits;
+function oaep_mgf1_str(a,b,c){for(var d="",e=0;d.length<b;)d+=c(a+String.fromCharCode.apply(String,[(e&4278190080)>>24,(e&16711680)>>16,(e&65280)>>8,e&255])),e+=1;return d}SHA1_SIZE=20;
+function oaep_unpad(a,b,c){a=a.toByteArray();var d;for(d=0;d<a.length;d+=1)a[d]&=255;for(;a.length<b;)a.unshift(0);a=String.fromCharCode.apply(String,a);if(a.length<2*SHA1_SIZE+2)throw"Cipher too short";var e=a.substr(1,SHA1_SIZE);b=a.substr(SHA1_SIZE+1);c=oaep_mgf1_str(b,SHA1_SIZE,c||rstr_sha1);var f=[];for(d=0;d<e.length;d+=1)f[d]=e.charCodeAt(d)^c.charCodeAt(d);e=oaep_mgf1_str(String.fromCharCode.apply(String,f),a.length-SHA1_SIZE,rstr_sha1);a=[];for(d=0;d<b.length;d+=1)a[d]=b.charCodeAt(d)^e.charCodeAt(d);
+a=String.fromCharCode.apply(String,a);if(a.substr(0,SHA1_SIZE)!==rstr_sha1(""))throw"Hash mismatch";a=a.substr(SHA1_SIZE);d=a.indexOf("\u0001");if((-1!=d?a.substr(0,d).lastIndexOf("\x00"):-1)+1!=d)throw"Malformed data";return a.substr(d+1)}function RSASetPrivate(a,b,c){this.isPrivate=!0;"string"!==typeof a?(this.n=a,this.e=b,this.d=c):null!=a&&null!=b&&0<a.length&&0<b.length?(this.n=parseBigInt(a,16),this.e=parseInt(b,16),this.d=parseBigInt(c,16)):alert("Invalid RSA private key")}
+function RSASetPrivateEx(a,b,c,d,e,f,g,h){this.isPrivate=!0;if(null==a)throw"RSASetPrivateEx N == null";if(null==b)throw"RSASetPrivateEx E == null";if(0==a.length)throw"RSASetPrivateEx N.length == 0";if(0==b.length)throw"RSASetPrivateEx E.length == 0";null!=a&&null!=b&&0<a.length&&0<b.length?(this.n=parseBigInt(a,16),this.e=parseInt(b,16),this.d=parseBigInt(c,16),this.p=parseBigInt(d,16),this.q=parseBigInt(e,16),this.dmp1=parseBigInt(f,16),this.dmq1=parseBigInt(g,16),this.coeff=parseBigInt(h,16)):
+alert("Invalid RSA private key in RSASetPrivateEx")}
+function RSAGenerate(a,b){var c=new SecureRandom,d=a>>1;this.e=parseInt(b,16);for(var e=new BigInteger(b,16);;){for(;this.p=new BigInteger(a-d,1,c),0!=this.p.subtract(BigInteger.ONE).gcd(e).compareTo(BigInteger.ONE)||!this.p.isProbablePrime(10););for(;this.q=new BigInteger(d,1,c),0!=this.q.subtract(BigInteger.ONE).gcd(e).compareTo(BigInteger.ONE)||!this.q.isProbablePrime(10););if(0>=this.p.compareTo(this.q)){var f=this.p;this.p=this.q;this.q=f}var f=this.p.subtract(BigInteger.ONE),g=this.q.subtract(BigInteger.ONE),
+h=f.multiply(g);if(0==h.gcd(e).compareTo(BigInteger.ONE)){this.n=this.p.multiply(this.q);this.d=e.modInverse(h);this.dmp1=this.d.mod(f);this.dmq1=this.d.mod(g);this.coeff=this.q.modInverse(this.p);break}}this.isPrivate=!0}
+function RSADoPrivate(a){if(null==this.p||null==this.q)return a.modPow(this.d,this.n);var b=a.mod(this.p).modPow(this.dmp1,this.p);for(a=a.mod(this.q).modPow(this.dmq1,this.q);0>b.compareTo(a);)b=b.add(this.p);return b.subtract(a).multiply(this.coeff).mod(this.p).multiply(this.q).add(a)}function RSADecrypt(a){a=parseBigInt(a,16);a=this.doPrivate(a);return null==a?null:pkcs1unpad2(a,this.n.bitLength()+7>>3)}
+function RSADecryptOAEP(a,b){var c=parseBigInt(a,16),c=this.doPrivate(c);return null==c?null:oaep_unpad(c,this.n.bitLength()+7>>3,b)}RSAKey.prototype.doPrivate=RSADoPrivate;RSAKey.prototype.setPrivate=RSASetPrivate;RSAKey.prototype.setPrivateEx=RSASetPrivateEx;RSAKey.prototype.generate=RSAGenerate;RSAKey.prototype.decrypt=RSADecrypt;RSAKey.prototype.decryptOAEP=RSADecryptOAEP;exports.RSAKey=RSAKey;module.exports=exports;CryptoJS=require("./sha256.js").CryptoJS;intShim=require("jsbn");
+"undefined"!=typeof KJUR&&KJUR||(KJUR={});"undefined"!=typeof KJUR.crypto&&KJUR.crypto||(KJUR.crypto={});
+KJUR.crypto.Util=new function(){this.DIGESTINFOHEAD={sha1:"3021300906052b0e03021a05000414",sha224:"302d300d06096086480165030402040500041c",sha256:"3031300d060960864801650304020105000420",sha384:"3041300d060960864801650304020205000430",sha512:"3051300d060960864801650304020305000440",md2:"3020300c06082a864886f70d020205000410",md5:"3020300c06082a864886f70d020505000410",ripemd160:"3021300906052b2403020105000414"};this.DEFAULTPROVIDER={md5:"cryptojs",sha1:"cryptojs",sha224:"cryptojs",sha256:"cryptojs",
+sha384:"cryptojs",sha512:"cryptojs",ripemd160:"cryptojs",hmacmd5:"cryptojs",hmacsha1:"cryptojs",hmacsha224:"cryptojs",hmacsha256:"cryptojs",hmacsha384:"cryptojs",hmacsha512:"cryptojs",hmacripemd160:"cryptojs",MD5withRSA:"cryptojs/jsrsa",SHA1withRSA:"cryptojs/jsrsa",SHA224withRSA:"cryptojs/jsrsa",SHA256withRSA:"cryptojs/jsrsa",SHA384withRSA:"cryptojs/jsrsa",SHA512withRSA:"cryptojs/jsrsa",RIPEMD160withRSA:"cryptojs/jsrsa",MD5withECDSA:"cryptojs/jsrsa",SHA1withECDSA:"cryptojs/jsrsa",SHA224withECDSA:"cryptojs/jsrsa",
+SHA256withECDSA:"cryptojs/jsrsa",SHA384withECDSA:"cryptojs/jsrsa",SHA512withECDSA:"cryptojs/jsrsa",RIPEMD160withECDSA:"cryptojs/jsrsa",SHA1withDSA:"cryptojs/jsrsa",SHA224withDSA:"cryptojs/jsrsa",SHA256withDSA:"cryptojs/jsrsa",MD5withRSAandMGF1:"cryptojs/jsrsa",SHA1withRSAandMGF1:"cryptojs/jsrsa",SHA224withRSAandMGF1:"cryptojs/jsrsa",SHA256withRSAandMGF1:"cryptojs/jsrsa",SHA384withRSAandMGF1:"cryptojs/jsrsa",SHA512withRSAandMGF1:"cryptojs/jsrsa",RIPEMD160withRSAandMGF1:"cryptojs/jsrsa"};this.CRYPTOJSMESSAGEDIGESTNAME=
+{md5:"CryptoJS.algo.MD5",sha1:"CryptoJS.algo.SHA1",sha224:"CryptoJS.algo.SHA224",sha256:"CryptoJS.algo.SHA256",sha384:"CryptoJS.algo.SHA384",sha512:"CryptoJS.algo.SHA512",ripemd160:"CryptoJS.algo.RIPEMD160"};this.getDigestInfoHex=function(a,b){if("undefined"==typeof this.DIGESTINFOHEAD[b])throw"alg not supported in Util.DIGESTINFOHEAD: "+b;return this.DIGESTINFOHEAD[b]+a};this.getPaddedDigestInfoHex=function(a,b,c){var d=this.getDigestInfoHex(a,b);a=c/4;if(d.length+22>a)throw"key is too short for SigAlg: keylen="+
+c+","+b;b="00"+d;c="";a=a-4-b.length;for(d=0;d<a;d+=2)c+="ff";return"0001"+c+b};this.hashString=function(a,b){return(new KJUR.crypto.MessageDigest({alg:b})).digestString(a)};this.hashHex=function(a,b){return(new KJUR.crypto.MessageDigest({alg:b})).digestHex(a)};this.sha1=function(a){return(new KJUR.crypto.MessageDigest({alg:"sha1",prov:"cryptojs"})).digestString(a)};this.sha256=function(a){return(new KJUR.crypto.MessageDigest({alg:"sha256",prov:"cryptojs"})).digestString(a)};this.sha256Hex=function(a){return(new KJUR.crypto.MessageDigest({alg:"sha256",
+prov:"cryptojs"})).digestHex(a)};this.sha512=function(a){return(new KJUR.crypto.MessageDigest({alg:"sha512",prov:"cryptojs"})).digestString(a)};this.sha512Hex=function(a){return(new KJUR.crypto.MessageDigest({alg:"sha512",prov:"cryptojs"})).digestHex(a)};this.md5=function(a){return(new KJUR.crypto.MessageDigest({alg:"md5",prov:"cryptojs"})).digestString(a)};this.ripemd160=function(a){return(new KJUR.crypto.MessageDigest({alg:"ripemd160",prov:"cryptojs"})).digestString(a)};this.getCryptoJSMDByName=
+function(a){}};
+KJUR.crypto.MessageDigest=function(a){this.setAlgAndProvider=function(a,c){null!=a&&void 0===c&&(c=KJUR.crypto.Util.DEFAULTPROVIDER[a]);if(-1!=":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:".indexOf(a)&&"cryptojs"==c){try{this.md=eval(KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[a]).create()}catch(d){throw"setAlgAndProvider hash alg set fail alg="+a+"/"+d;}this.updateString=function(a){this.md.update(a)};this.updateHex=function(a){a=CryptoJS.enc.Hex.parse(a);this.md.update(a)};this.digest=function(){return this.md.finalize().toString(CryptoJS.enc.Hex)};
+this.digestString=function(a){this.updateString(a);return this.digest()};this.digestHex=function(a){this.updateHex(a);return this.digest()}}if(-1!=":sha256:".indexOf(a)&&"sjcl"==c){try{this.md=new sjcl.hash.sha256}catch(e){throw"setAlgAndProvider hash alg set fail alg="+a+"/"+e;}this.updateString=function(a){this.md.update(a)};this.updateHex=function(a){a=sjcl.codec.hex.toBits(a);this.md.update(a)};this.digest=function(){var a=this.md.finalize();return sjcl.codec.hex.fromBits(a)};this.digestString=
+function(a){this.updateString(a);return this.digest()};this.digestHex=function(a){this.updateHex(a);return this.digest()}}};this.updateString=function(a){throw"updateString(str) not supported for this alg/prov: "+this.algName+"/"+this.provName;};this.updateHex=function(a){throw"updateHex(hex) not supported for this alg/prov: "+this.algName+"/"+this.provName;};this.digest=function(){throw"digest() not supported for this alg/prov: "+this.algName+"/"+this.provName;};this.digestString=function(a){throw"digestString(str) not supported for this alg/prov: "+
+this.algName+"/"+this.provName;};this.digestHex=function(a){throw"digestHex(hex) not supported for this alg/prov: "+this.algName+"/"+this.provName;};void 0!==a&&void 0!==a.alg&&(this.algName=a.alg,void 0===a.prov&&(this.provName=KJUR.crypto.Util.DEFAULTPROVIDER[this.algName]),this.setAlgAndProvider(this.algName,this.provName))};
+KJUR.crypto.Mac=function(a){this.setAlgAndProvider=function(a,c){a=a.toLowerCase();null==a&&(a="hmacsha1");a=a.toLowerCase();if("hmac"!=a.substr(0,4))throw"setAlgAndProvider unsupported HMAC alg: "+a;void 0===c&&(c=KJUR.crypto.Util.DEFAULTPROVIDER[a]);this.algProv=a+"/"+c;var d=a.substr(4);if(-1!=":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:".indexOf(d)&&"cryptojs"==c){try{var e=eval(KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[d]);this.mac=CryptoJS.algo.HMAC.create(e,this.pass)}catch(f){throw"setAlgAndProvider hash alg set fail hashAlg="+
+d+"/"+f;}this.updateString=function(a){this.mac.update(a)};this.updateHex=function(a){a=CryptoJS.enc.Hex.parse(a);this.mac.update(a)};this.doFinal=function(){return this.mac.finalize().toString(CryptoJS.enc.Hex)};this.doFinalString=function(a){this.updateString(a);return this.doFinal()};this.doFinalHex=function(a){this.updateHex(a);return this.doFinal()}}};this.updateString=function(a){throw"updateString(str) not supported for this alg/prov: "+this.algProv;};this.updateHex=function(a){throw"updateHex(hex) not supported for this alg/prov: "+
+this.algProv;};this.doFinal=function(){throw"digest() not supported for this alg/prov: "+this.algProv;};this.doFinalString=function(a){throw"digestString(str) not supported for this alg/prov: "+this.algProv;};this.doFinalHex=function(a){throw"digestHex(hex) not supported for this alg/prov: "+this.algProv;};this.setPassword=function(a){if("string"==typeof a){var c=a;1!=a.length%2&&a.match(/^[0-9A-Fa-f]+$/)||(c=rstrtohex(a))}else{if("object"!=typeof a)throw"KJUR.crypto.Mac unsupported password type: "+
+a;c=null;if(void 0!==a.hex){if(0!=a.hex.length%2||!a.hex.match(/^[0-9A-Fa-f]+$/))throw"Mac: wrong hex password: "+a.hex;c=a.hex}void 0!==a.utf8&&(c=utf8tohex(a.utf8));void 0!==a.rstr&&(c=rstrtohex(a.rstr));void 0!==a.b64&&(c=b64tohex(a.b64));void 0!==a.b64u&&(c=b64utohex(a.b64u));if(null==c)throw"KJUR.crypto.Mac unsupported password type: "+a;}this.pass=CryptoJS.enc.Hex.parse(c)};void 0!==a&&(void 0!==a.pass&&this.setPassword(a.pass),void 0!==a.alg&&(this.algName=a.alg,void 0===a.prov&&(this.provName=
+KJUR.crypto.Util.DEFAULTPROVIDER[this.algName]),this.setAlgAndProvider(this.algName,this.provName)))};
+KJUR.crypto.Signature=function(a){var b=null;this._setAlgNames=function(){this.algName.match(/^(.+)with(.+)$/)&&(this.mdAlgName=RegExp.$1.toLowerCase(),this.pubkeyAlgName=RegExp.$2.toLowerCase())};this._zeroPaddingOfSignature=function(a,b){for(var c="",g=b/4-a.length,h=0;h<g;h++)c+="0";return c+a};this.setAlgAndProvider=function(a,b){this._setAlgNames();if("cryptojs/jsrsa"!=b)throw"provider not supported: "+b;if(-1!=":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:".indexOf(this.mdAlgName)){try{this.md=
+new KJUR.crypto.MessageDigest({alg:this.mdAlgName})}catch(c){throw"setAlgAndProvider hash alg set fail alg="+this.mdAlgName+"/"+c;}this.init=function(a,b){var c=null;try{c=void 0===b?KEYUTIL.getKey(a):KEYUTIL.getKey(a,b)}catch(d){throw"init failed:"+d;}if(!0===c.isPrivate)this.prvKey=c,this.state="SIGN";else if(!0===c.isPublic)this.pubKey=c,this.state="VERIFY";else throw"init failed.:"+c;};this.initSign=function(a){"string"==typeof a.ecprvhex&&"string"==typeof a.eccurvename?(this.ecprvhex=a.ecprvhex,
+this.eccurvename=a.eccurvename):this.prvKey=a;this.state="SIGN"};this.initVerifyByPublicKey=function(a){"string"==typeof a.ecpubhex&&"string"==typeof a.eccurvename?(this.ecpubhex=a.ecpubhex,this.eccurvename=a.eccurvename):a instanceof KJUR.crypto.ECDSA?this.pubKey=a:a instanceof RSAKey&&(this.pubKey=a);this.state="VERIFY"};this.initVerifyByCertificatePEM=function(a){var b=new X509;b.readCertPEM(a);this.pubKey=b.subjectPublicKeyRSA;this.state="VERIFY"};this.updateString=function(a){this.md.updateString(a)};
+this.updateHex=function(a){this.md.updateHex(a)};this.sign=function(){this.sHashHex=this.md.digest();if("undefined"!=typeof this.ecprvhex&&"undefined"!=typeof this.eccurvename)this.hSign=(new KJUR.crypto.ECDSA({curve:this.eccurvename})).signHex(this.sHashHex,this.ecprvhex);else if(this.prvKey instanceof RSAKey&&"rsaandmgf1"==this.pubkeyAlgName)this.hSign=this.prvKey.signWithMessageHashPSS(this.sHashHex,this.mdAlgName,this.pssSaltLen);else if(this.prvKey instanceof RSAKey&&"rsa"==this.pubkeyAlgName)this.hSign=
+this.prvKey.signWithMessageHash(this.sHashHex,this.mdAlgName);else if(this.prvKey instanceof KJUR.crypto.ECDSA)this.hSign=this.prvKey.signWithMessageHash(this.sHashHex);else if(this.prvKey instanceof KJUR.crypto.DSA)this.hSign=this.prvKey.signWithMessageHash(this.sHashHex);else throw"Signature: unsupported public key alg: "+this.pubkeyAlgName;return this.hSign};this.signString=function(a){this.updateString(a);return this.sign()};this.signHex=function(a){this.updateHex(a);return this.sign()};this.verify=
+function(a){this.sHashHex=this.md.digest();if("undefined"!=typeof this.ecpubhex&&"undefined"!=typeof this.eccurvename)return(new KJUR.crypto.ECDSA({curve:this.eccurvename})).verifyHex(this.sHashHex,a,this.ecpubhex);if(this.pubKey instanceof RSAKey&&"rsaandmgf1"==this.pubkeyAlgName)return this.pubKey.verifyWithMessageHashPSS(this.sHashHex,a,this.mdAlgName,this.pssSaltLen);if(this.pubKey instanceof RSAKey&&"rsa"==this.pubkeyAlgName||this.pubKey instanceof KJUR.crypto.ECDSA||this.pubKey instanceof KJUR.crypto.DSA)return this.pubKey.verifyWithMessageHash(this.sHashHex,
+a);throw"Signature: unsupported public key alg: "+this.pubkeyAlgName;}}};this.init=function(a,b){throw"init(key, pass) not supported for this alg:prov="+this.algProvName;};this.initVerifyByPublicKey=function(a){throw"initVerifyByPublicKey(rsaPubKeyy) not supported for this alg:prov="+this.algProvName;};this.initVerifyByCertificatePEM=function(a){throw"initVerifyByCertificatePEM(certPEM) not supported for this alg:prov="+this.algProvName;};this.initSign=function(a){throw"initSign(prvKey) not supported for this alg:prov="+
+this.algProvName;};this.updateString=function(a){throw"updateString(str) not supported for this alg:prov="+this.algProvName;};this.updateHex=function(a){throw"updateHex(hex) not supported for this alg:prov="+this.algProvName;};this.sign=function(){throw"sign() not supported for this alg:prov="+this.algProvName;};this.signString=function(a){throw"digestString(str) not supported for this alg:prov="+this.algProvName;};this.signHex=function(a){throw"digestHex(hex) not supported for this alg:prov="+this.algProvName;
+};this.verify=function(a){throw"verify(hSigVal) not supported for this alg:prov="+this.algProvName;};this.initParams=a;if(void 0!==a&&(void 0!==a.alg&&(this.algName=a.alg,this.provName=void 0===a.prov?KJUR.crypto.Util.DEFAULTPROVIDER[this.algName]:a.prov,this.algProvName=this.algName+":"+this.provName,this.setAlgAndProvider(this.algName,this.provName),this._setAlgNames()),void 0!==a.psssaltlen&&(this.pssSaltLen=a.psssaltlen),void 0!==a.prvkeypem)){if(void 0!==a.prvkeypas)throw"both prvkeypem and prvkeypas parameters not supported";
+try{b=new RSAKey,b.readPrivateKeyFromPEMString(a.prvkeypem),this.initSign(b)}catch(c){throw"fatal error to load pem private key: "+c;}}};
+KJUR.crypto.OID=new function(){this.oidhex2name={"2a864886f70d010101":"rsaEncryption","2a8648ce3d0201":"ecPublicKey","2a8648ce380401":"dsa","2a8648ce3d030107":"secp256r1","2b8104001f":"secp192k1","2b81040021":"secp224r1","2b8104000a":"secp256k1","2b81040023":"secp521r1","2b81040022":"secp384r1","2a8648ce380403":"SHA1withDSA","608648016503040301":"SHA224withDSA","608648016503040302":"SHA256withDSA"}};exports.KJUR=KJUR;module.exports=exports;
+var ASN1HEX=require("./asn1hex-1.1.js").ASN1HEX,b64tohex=require("./base64.js").b64tohex,RSAKey=require("./rsa2.js").RSAKey;function _rsapem_pemToBase64(a){a=a.replace("-----BEGIN RSA PRIVATE KEY-----","");a=a.replace("-----END RSA PRIVATE KEY-----","");return a=a.replace(/[ \n]+/g,"")}
+function _rsapem_getPosArrayOfChildrenFromHex(a){var b=[],c=ASN1HEX.getStartPosOfV_AtObj(a,0),d=ASN1HEX.getPosOfNextSibling_AtObj(a,c),e=ASN1HEX.getPosOfNextSibling_AtObj(a,d),f=ASN1HEX.getPosOfNextSibling_AtObj(a,e),g=ASN1HEX.getPosOfNextSibling_AtObj(a,f),h=ASN1HEX.getPosOfNextSibling_AtObj(a,g),k=ASN1HEX.getPosOfNextSibling_AtObj(a,h),l=ASN1HEX.getPosOfNextSibling_AtObj(a,k);a=ASN1HEX.getPosOfNextSibling_AtObj(a,l);b.push(c,d,e,f,g,h,k,l,a);return b}
+function _rsapem_getHexValueArrayOfChildrenFromHex(a){var b=_rsapem_getPosArrayOfChildrenFromHex(a),c=ASN1HEX.getHexOfV_AtObj(a,b[0]),d=ASN1HEX.getHexOfV_AtObj(a,b[1]),e=ASN1HEX.getHexOfV_AtObj(a,b[2]),f=ASN1HEX.getHexOfV_AtObj(a,b[3]),g=ASN1HEX.getHexOfV_AtObj(a,b[4]),h=ASN1HEX.getHexOfV_AtObj(a,b[5]),k=ASN1HEX.getHexOfV_AtObj(a,b[6]),l=ASN1HEX.getHexOfV_AtObj(a,b[7]);a=ASN1HEX.getHexOfV_AtObj(a,b[8]);b=[];b.push(c,d,e,f,g,h,k,l,a);return b}
+function _rsapem_readPrivateKeyFromASN1HexString(a){a=_rsapem_getHexValueArrayOfChildrenFromHex(a);this.setPrivateEx(a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8])}function _rsapem_readPrivateKeyFromPEMString(a){a=_rsapem_pemToBase64(a);a=b64tohex(a);a=_rsapem_getHexValueArrayOfChildrenFromHex(a);this.setPrivateEx(a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8])}RSAKey.prototype.readPrivateKeyFromPEMString=_rsapem_readPrivateKeyFromPEMString;RSAKey.prototype.readPrivateKeyFromASN1HexString=_rsapem_readPrivateKeyFromASN1HexString;
+exports.RSAKey=RSAKey;module.exports=exports;var intShim=require("jsbn"),BigInteger=intShim.BigInteger?intShim.BigInteger:intShim,RSAKey=require("./rsapem-1.1.js").RSAKey,_RE_HEXDECONLY=RegExp("");_RE_HEXDECONLY.compile("[^0-9a-f]","gi");function _rsasign_getHexPaddedDigestInfoForString(a,b,c){a=KJUR.crypto.Util.hashString(a,c);return KJUR.crypto.Util.getPaddedDigestInfoHex(a,c,b)}function _zeroPaddingOfSignature(a,b){for(var c="",d=b/4-a.length,e=0;e<d;e++)c+="0";return c+a}
+function _rsasign_signString(a,b){var c=KJUR.crypto.Util.hashString(a,b);return this.signWithMessageHash(c,b)}function _rsasign_signWithMessageHash(a,b){var c=KJUR.crypto.Util.getPaddedDigestInfoHex(a,b,this.n.bitLength()),c=parseBigInt(c,16),c=this.doPrivate(c).toString(16);return _zeroPaddingOfSignature(c,this.n.bitLength())}function _rsasign_signStringWithSHA1(a){return _rsasign_signString.call(this,a,"sha1")}
+function _rsasign_signStringWithSHA256(a){return _rsasign_signString.call(this,a,"sha256")}function pss_mgf1_str(a,b,c){for(var d="",e=0;d.length<b;)d+=hextorstr(c(rstrtohex(a+String.fromCharCode.apply(String,[(e&4278190080)>>24,(e&16711680)>>16,(e&65280)>>8,e&255])))),e+=1;return d}function _rsasign_signStringPSS(a,b,c){a=rstrtohex(a);a=KJUR.crypto.Util.hashHex(a,b);void 0===c&&(c=-1);return this.signWithMessageHashPSS(a,b,c)}
+function _rsasign_signWithMessageHashPSS(a,b,c){var d=hextorstr(a);a=d.length;var e=this.n.bitLength()-1,f=Math.ceil(e/8),g=function(a){return KJUR.crypto.Util.hashHex(a,b)};if(-1===c||void 0===c)c=a;else if(-2===c)c=f-a-2;else if(-2>c)throw"invalid salt length";if(f<a+c+2)throw"data too long";var h="";0<c&&(h=Array(c),(new SecureRandom).nextBytes(h),h=String.fromCharCode.apply(String,h));for(var k=hextorstr(g(rstrtohex("\x00\x00\x00\x00\x00\x00\x00\x00"+d+h))),l=[],d=0;d<f-c-a-2;d+=1)l[d]=0;c=String.fromCharCode.apply(String,
+l)+"\u0001"+h;g=pss_mgf1_str(k,c.length,g);h=[];for(d=0;d<c.length;d+=1)h[d]=c.charCodeAt(d)^g.charCodeAt(d);h[0]&=~(65280>>8*f-e&255);for(d=0;d<a;d++)h.push(k.charCodeAt(d));h.push(188);return _zeroPaddingOfSignature(this.doPrivate(new BigInteger(h)).toString(16),this.n.bitLength())}function _rsasign_getDecryptSignatureBI(a,b,c){var d=new RSAKey;d.setPublic(b,c);return d.doPublic(a)}
+function _rsasign_getHexDigestInfoFromSig(a,b,c){return _rsasign_getDecryptSignatureBI(a,b,c).toString(16).replace(/^1f+00/,"")}function _rsasign_getAlgNameAndHashFromHexDisgestInfo(a){for(var b in KJUR.crypto.Util.DIGESTINFOHEAD){var c=KJUR.crypto.Util.DIGESTINFOHEAD[b],d=c.length;if(a.substring(0,d)==c)return[b,a.substring(d)]}return[]}
+function _rsasign_verifySignatureWithArgs(a,b,c,d){b=_rsasign_getHexDigestInfoFromSig(b,c,d);c=_rsasign_getAlgNameAndHashFromHexDisgestInfo(b);if(0==c.length)return!1;b=c[1];a=KJUR.crypto.Util.hashString(a,c[0]);return b==a}function _rsasign_verifyHexSignatureForMessage(a,b){var c=parseBigInt(a,16);return _rsasign_verifySignatureWithArgs(b,c,this.n.toString(16),this.e.toString(16))}
+function _rsasign_verifyString(a,b){b=b.replace(_RE_HEXDECONLY,"");b=b.replace(/[ \n]+/g,"");var c=parseBigInt(b,16);if(c.bitLength()>this.n.bitLength())return 0;var c=this.doPublic(c).toString(16).replace(/^1f+00/,""),d=_rsasign_getAlgNameAndHashFromHexDisgestInfo(c);if(0==d.length)return!1;c=d[1];d=KJUR.crypto.Util.hashString(a,d[0]);return c==d}
+function _rsasign_verifyWithMessageHash(a,b){b=b.replace(_RE_HEXDECONLY,"");b=b.replace(/[ \n]+/g,"");var c=parseBigInt(b,16);if(c.bitLength()>this.n.bitLength())return 0;c=this.doPublic(c).toString(16).replace(/^1f+00/,"");c=_rsasign_getAlgNameAndHashFromHexDisgestInfo(c);return 0==c.length?!1:c[1]==a}function _rsasign_verifyStringPSS(a,b,c,d){a=rstrtohex(a);a=KJUR.crypto.Util.hashHex(a,c);void 0===d&&(d=-1);return this.verifyWithMessageHashPSS(a,b,c,d)}
+function _rsasign_verifyWithMessageHashPSS(a,b,c,d){var e=new BigInteger(b,16);if(e.bitLength()>this.n.bitLength())return!1;b=function(a){return KJUR.crypto.Util.hashHex(a,c)};a=hextorstr(a);var f=a.length,g=this.n.bitLength()-1,h=Math.ceil(g/8);if(-1===d||void 0===d)d=f;else if(-2===d)d=h-f-2;else if(-2>d)throw"invalid salt length";if(h<f+d+2)throw"data too long";for(var k=this.doPublic(e).toByteArray(),e=0;e<k.length;e+=1)k[e]&=255;for(;k.length<h;)k.unshift(0);if(188!==k[h-1])throw"encoded message does not end in 0xbc";
+var k=String.fromCharCode.apply(String,k),l=k.substr(0,h-f-1),k=k.substr(l.length,f),m=65280>>8*h-g&255;if(0!==(l.charCodeAt(0)&m))throw"bits beyond keysize not zero";for(var q=pss_mgf1_str(k,l.length,b),g=[],e=0;e<l.length;e+=1)g[e]=l.charCodeAt(e)^q.charCodeAt(e);g[0]&=~m;f=h-f-d-2;for(e=0;e<f;e+=1)if(0!==g[e])throw"leftmost octets not zero";if(1!==g[f])throw"0x01 marker not found";return k===hextorstr(b(rstrtohex("\x00\x00\x00\x00\x00\x00\x00\x00"+a+String.fromCharCode.apply(String,g.slice(-d)))))}
+RSAKey.prototype.signWithMessageHash=_rsasign_signWithMessageHash;RSAKey.prototype.signString=_rsasign_signString;RSAKey.prototype.signStringWithSHA1=_rsasign_signStringWithSHA1;RSAKey.prototype.signStringWithSHA256=_rsasign_signStringWithSHA256;RSAKey.prototype.sign=_rsasign_signString;RSAKey.prototype.signWithSHA1=_rsasign_signStringWithSHA1;RSAKey.prototype.signWithSHA256=_rsasign_signStringWithSHA256;RSAKey.prototype.signWithMessageHashPSS=_rsasign_signWithMessageHashPSS;
+RSAKey.prototype.signStringPSS=_rsasign_signStringPSS;RSAKey.prototype.signPSS=_rsasign_signStringPSS;RSAKey.SALT_LEN_HLEN=-1;RSAKey.SALT_LEN_MAX=-2;RSAKey.prototype.verifyWithMessageHash=_rsasign_verifyWithMessageHash;RSAKey.prototype.verifyString=_rsasign_verifyString;RSAKey.prototype.verifyHexSignatureForMessage=_rsasign_verifyHexSignatureForMessage;RSAKey.prototype.verify=_rsasign_verifyString;RSAKey.prototype.verifyHexSignatureForByteArrayMessage=_rsasign_verifyHexSignatureForMessage;
+RSAKey.prototype.verifyWithMessageHashPSS=_rsasign_verifyWithMessageHashPSS;RSAKey.prototype.verifyStringPSS=_rsasign_verifyStringPSS;RSAKey.prototype.verifyPSS=_rsasign_verifyStringPSS;RSAKey.SALT_LEN_RECOVER=-2;exports.RSAKey=RSAKey;module.exports=exports;"undefined"!=typeof KJUR&&KJUR||(KJUR={});"undefined"!=typeof KJUR.crypto&&KJUR.crypto||(KJUR.crypto={});
+KJUR.crypto.ECDSA=function(a){var b=new SecureRandom;this.type="EC";this.getBigRandom=function(a){return(new BigInteger(a.bitLength(),b)).mod(a.subtract(BigInteger.ONE)).add(BigInteger.ONE)};this.setNamedCurve=function(a){this.ecparams=KJUR.crypto.ECParameterDB.getByName(a);this.pubKeyHex=this.prvKeyHex=null;this.curveName=a};this.setPrivateKeyHex=function(a){this.isPrivate=!0;this.prvKeyHex=a};this.setPublicKeyHex=function(a){this.isPublic=!0;this.pubKeyHex=a};this.generateKeyPairHex=function(){var a=
+this.getBigRandom(this.ecparams.n),b=this.ecparams.G.multiply(a),e=b.getX().toBigInteger(),b=b.getY().toBigInteger(),f=this.ecparams.keylen/4,a=("0000000000"+a.toString(16)).slice(-f),e=("0000000000"+e.toString(16)).slice(-f),b=("0000000000"+b.toString(16)).slice(-f),e="04"+e+b;this.setPrivateKeyHex(a);this.setPublicKeyHex(e);return{ecprvhex:a,ecpubhex:e}};this.signWithMessageHash=function(a){return this.signHex(a,this.prvKeyHex)};this.signHex=function(a,b){var e=new BigInteger(b,16),f=this.ecparams.n,
+g=new BigInteger(a,16);do var h=this.getBigRandom(f),k=this.ecparams.G.multiply(h).getX().toBigInteger().mod(f);while(0>=k.compareTo(BigInteger.ZERO));e=h.modInverse(f).multiply(g.add(e.multiply(k))).mod(f);return KJUR.crypto.ECDSA.biRSSigToASN1Sig(k,e)};this.sign=function(a,b){var e=this.ecparams.n,f=BigInteger.fromByteArrayUnsigned(a);do var g=this.getBigRandom(e),h=this.ecparams.G.multiply(g).getX().toBigInteger().mod(e);while(0>=h.compareTo(BigInteger.ZERO));e=g.modInverse(e).multiply(f.add(b.multiply(h))).mod(e);
+return this.serializeSig(h,e)};this.verifyWithMessageHash=function(a,b){return this.verifyHex(a,b,this.pubKeyHex)};this.verifyHex=function(a,b,e){var f;f=KJUR.crypto.ECDSA.parseSigHex(b);b=f.r;f=f.s;e=ECPointFp.decodeFromHex(this.ecparams.curve,e);a=new BigInteger(a,16);return this.verifyRaw(a,b,f,e)};this.verify=function(a,b,e){var f;if(Bitcoin.Util.isArray(b))b=this.parseSig(b),f=b.r,b=b.s;else if("object"===typeof b&&b.r&&b.s)f=b.r,b=b.s;else throw"Invalid value for signature";if(!(e instanceof
+ECPointFp))if(Bitcoin.Util.isArray(e))e=ECPointFp.decodeFrom(this.ecparams.curve,e);else throw"Invalid format for pubkey value, must be byte array or ECPointFp";a=BigInteger.fromByteArrayUnsigned(a);return this.verifyRaw(a,f,b,e)};this.verifyRaw=function(a,b,e,f){var g=this.ecparams.n,h=this.ecparams.G;if(0>b.compareTo(BigInteger.ONE)||0<=b.compareTo(g)||0>e.compareTo(BigInteger.ONE)||0<=e.compareTo(g))return!1;e=e.modInverse(g);a=a.multiply(e).mod(g);e=b.multiply(e).mod(g);return h.multiply(a).add(f.multiply(e)).getX().toBigInteger().mod(g).equals(b)};
+this.serializeSig=function(a,b){var e=a.toByteArraySigned(),f=b.toByteArraySigned(),g=[];g.push(2);g.push(e.length);g=g.concat(e);g.push(2);g.push(f.length);g=g.concat(f);g.unshift(g.length);g.unshift(48);return g};this.parseSig=function(a){var b;if(48!=a[0])throw Error("Signature not a valid DERSequence");b=2;if(2!=a[b])throw Error("First element in signature must be a DERInteger");var e=a.slice(b+2,b+2+a[b+1]);b+=2+a[b+1];if(2!=a[b])throw Error("Second element in signature must be a DERInteger");
+a=a.slice(b+2,b+2+a[b+1]);e=BigInteger.fromByteArrayUnsigned(e);a=BigInteger.fromByteArrayUnsigned(a);return{r:e,s:a}};this.parseSigCompact=function(a){if(65!==a.length)throw"Signature has the wrong length";var b=a[0]-27;if(0>b||7<b)throw"Invalid signature type";var e=this.ecparams.n,f=BigInteger.fromByteArrayUnsigned(a.slice(1,33)).mod(e);a=BigInteger.fromByteArrayUnsigned(a.slice(33,65)).mod(e);return{r:f,s:a,i:b}};void 0!==a&&void 0!==a.curve&&(this.curveName=a.curve);void 0===this.curveName&&
+(this.curveName="secp256r1");this.setNamedCurve(this.curveName);void 0!==a&&(void 0!==a.prv&&this.setPrivateKeyHex(a.prv),void 0!==a.pub&&this.setPublicKeyHex(a.pub))};KJUR.crypto.ECDSA.parseSigHex=function(a){var b=KJUR.crypto.ECDSA.parseSigHexInHexRS(a);a=new BigInteger(b.r,16);b=new BigInteger(b.s,16);return{r:a,s:b}};
+KJUR.crypto.ECDSA.parseSigHexInHexRS=function(a){if("30"!=a.substr(0,2))throw"signature is not a ASN.1 sequence";var b=ASN1HEX.getPosArrayOfChildren_AtObj(a,0);if(2!=b.length)throw"number of signature ASN.1 sequence elements seem wrong";var c=b[0],b=b[1];if("02"!=a.substr(c,2))throw"1st item of sequene of signature is not ASN.1 integer";if("02"!=a.substr(b,2))throw"2nd item of sequene of signature is not ASN.1 integer";c=ASN1HEX.getHexOfV_AtObj(a,c);a=ASN1HEX.getHexOfV_AtObj(a,b);return{r:c,s:a}};
+KJUR.crypto.ECDSA.asn1SigToConcatSig=function(a){var b=KJUR.crypto.ECDSA.parseSigHexInHexRS(a);a=b.r;b=b.s;"00"==a.substr(0,2)&&8==a.length/2*8%128&&(a=a.substr(2));"00"==b.substr(0,2)&&8==b.length/2*8%128&&(b=b.substr(2));if(0!=a.length/2*8%128)throw"unknown ECDSA sig r length error";if(0!=b.length/2*8%128)throw"unknown ECDSA sig s length error";return a+b};
+KJUR.crypto.ECDSA.concatSigToASN1Sig=function(a){if(0!=a.length/2*8%128)throw"unknown ECDSA concatinated r-s sig length error";var b=a.substr(0,a.length/2);a=a.substr(a.length/2);return KJUR.crypto.ECDSA.hexRSSigToASN1Sig(b,a)};KJUR.crypto.ECDSA.hexRSSigToASN1Sig=function(a,b){var c=new BigInteger(a,16),d=new BigInteger(b,16);return KJUR.crypto.ECDSA.biRSSigToASN1Sig(c,d)};
+KJUR.crypto.ECDSA.biRSSigToASN1Sig=function(a,b){var c=new KJUR.asn1.DERInteger({bigint:a}),d=new KJUR.asn1.DERInteger({bigint:b});return(new KJUR.asn1.DERSequence({array:[c,d]})).getEncodedHex()};intShim=require("jsbn");
+ASN1HEX=new function(){this.getByteLengthOfL_AtObj=function(a,b){if("8"!=a.substring(b+2,b+3))return 1;var c=parseInt(a.substring(b+3,b+4));return 0==c?-1:0<c&&10>c?c+1:-2};this.getHexOfL_AtObj=function(a,b){var c=this.getByteLengthOfL_AtObj(a,b);return 1>c?"":a.substring(b+2,b+2+2*c)};this.getIntOfL_AtObj=function(a,b){var c=this.getHexOfL_AtObj(a,b);return""==c?-1:(8>parseInt(c.substring(0,1))?new BigInteger(c,16):new BigInteger(c.substring(2),16)).intValue()};this.getStartPosOfV_AtObj=function(a,
+b){var c=this.getByteLengthOfL_AtObj(a,b);return 0>c?c:b+2*(c+1)};this.getHexOfV_AtObj=function(a,b){var c=this.getStartPosOfV_AtObj(a,b),d=this.getIntOfL_AtObj(a,b);return a.substring(c,c+2*d)};this.getHexOfTLV_AtObj=function(a,b){var c=a.substr(b,2),d=this.getHexOfL_AtObj(a,b),e=this.getHexOfV_AtObj(a,b);return c+d+e};this.getPosOfNextSibling_AtObj=function(a,b){var c=this.getStartPosOfV_AtObj(a,b),d=this.getIntOfL_AtObj(a,b);return c+2*d};this.getPosArrayOfChildren_AtObj=function(a,b){var c=[],
+d=this.getStartPosOfV_AtObj(a,b);c.push(d);for(var e=this.getIntOfL_AtObj(a,b),f=d,g=0;;){f=this.getPosOfNextSibling_AtObj(a,f);if(null==f||f-d>=2*e)break;if(200<=g)break;c.push(f);g++}return c};this.getNthChildIndex_AtObj=function(a,b,c){return this.getPosArrayOfChildren_AtObj(a,b)[c]};this.getDecendantIndexByNthList=function(a,b,c){if(0==c.length)return b;var d=c.shift();b=this.getPosArrayOfChildren_AtObj(a,b);return this.getDecendantIndexByNthList(a,b[d],c)};this.getDecendantHexTLVByNthList=function(a,
+b,c){b=this.getDecendantIndexByNthList(a,b,c);return this.getHexOfTLV_AtObj(a,b)};this.getDecendantHexVByNthList=function(a,b,c){b=this.getDecendantIndexByNthList(a,b,c);return this.getHexOfV_AtObj(a,b)}};ASN1HEX.getVbyList=function(a,b,c,d){b=this.getDecendantIndexByNthList(a,b,c);if(void 0===b)throw"can't find nthList object";if(void 0!==d&&a.substr(b,2)!=d)throw"checking tag doesn't match: "+a.substr(b,2)+"!="+d;return this.getHexOfV_AtObj(a,b)};
+ASN1HEX.hextooidstr=function(a){var b=function(a,b){return a.length>=b?a:Array(b-a.length+1).join("0")+a},c=[],d=a.substr(0,2),d=parseInt(d,16);c[0]=new String(Math.floor(d/40));c[1]=new String(d%40);var e=a.substr(2);a=[];for(d=0;d<e.length/2;d++)a.push(parseInt(e.substr(2*d,2),16));for(var e=[],f="",d=0;d<a.length;d++)a[d]&128?f+=b((a[d]&127).toString(2),7):(f+=b((a[d]&127).toString(2),7),e.push(new String(parseInt(f,2))),f="");b=c.join(".");0<e.length&&(b=b+"."+e.join("."));return b};
+ASN1HEX.dump=function(a,b,c,d){var e=function(a,b){return a.length<=2*b?a:a.substr(0,b)+"..(total "+a.length/2+"bytes).."+a.substr(a.length-b,b)};void 0===b&&(b={ommit_long_octet:32});void 0===c&&(c=0);void 0===d&&(d="");var f=b.ommit_long_octet;if("01"==a.substr(c,2))return a=ASN1HEX.getHexOfV_AtObj(a,c),"00"==a?d+"BOOLEAN FALSE\n":d+"BOOLEAN TRUE\n";if("02"==a.substr(c,2))return a=ASN1HEX.getHexOfV_AtObj(a,c),d+"INTEGER "+e(a,f)+"\n";if("03"==a.substr(c,2))return a=ASN1HEX.getHexOfV_AtObj(a,c),
+d+"BITSTRING "+e(a,f)+"\n";if("04"==a.substr(c,2))return a=ASN1HEX.getHexOfV_AtObj(a,c),ASN1HEX.isASN1HEX(a)?e=d+"OCTETSTRING, encapsulates\n"+ASN1HEX.dump(a,b,0,d+" "):d+"OCTETSTRING "+e(a,f)+"\n";if("05"==a.substr(c,2))return d+"NULL\n";if("06"==a.substr(c,2)){a=ASN1HEX.getHexOfV_AtObj(a,c);var g=KJUR.asn1.ASN1Util.oidHexToInt(a),f=KJUR.asn1.x509.OID.oid2name(g);a=g.replace(/\./g," ");return""!=f?d+"ObjectIdentifier "+f+" ("+a+")\n":d+"ObjectIdentifier ("+a+")\n"}if("0c"==a.substr(c,2))return d+
+"UTF8String '"+hextoutf8(ASN1HEX.getHexOfV_AtObj(a,c))+"'\n";if("13"==a.substr(c,2))return d+"PrintableString '"+hextoutf8(ASN1HEX.getHexOfV_AtObj(a,c))+"'\n";if("14"==a.substr(c,2))return d+"TeletexString '"+hextoutf8(ASN1HEX.getHexOfV_AtObj(a,c))+"'\n";if("16"==a.substr(c,2))return d+"IA5String '"+hextoutf8(ASN1HEX.getHexOfV_AtObj(a,c))+"'\n";if("17"==a.substr(c,2))return d+"UTCTime "+hextoutf8(ASN1HEX.getHexOfV_AtObj(a,c))+"\n";if("18"==a.substr(c,2))return d+"GeneralizedTime "+hextoutf8(ASN1HEX.getHexOfV_AtObj(a,
+c))+"\n";if("30"==a.substr(c,2)){if("3000"==a.substr(c,4))return d+"SEQUENCE {}\n";e=d+"SEQUENCE\n";c=ASN1HEX.getPosArrayOfChildren_AtObj(a,c);f=b;2!=c.length&&3!=c.length||"06"!=a.substr(c[0],2)||"04"!=a.substr(c[c.length-1],2)||(f=ASN1HEX.getHexOfV_AtObj(a,c[0]),g=KJUR.asn1.ASN1Util.oidHexToInt(f),f=KJUR.asn1.x509.OID.oid2name(g),b=JSON.parse(JSON.stringify(b)),b.x509ExtName=f,f=b);for(g=0;g<c.length;g++)e+=ASN1HEX.dump(a,f,c[g],d+" ");return e}if("31"==a.substr(c,2)){e=d+"SET\n";c=ASN1HEX.getPosArrayOfChildren_AtObj(a,
+c);for(g=0;g<c.length;g++)e+=ASN1HEX.dump(a,b,c[g],d+" ");return e}f=parseInt(a.substr(c,2),16);if(0!=(f&128)){e=f&31;if(0!=(f&32))for(e=d+"["+e+"]\n",c=ASN1HEX.getPosArrayOfChildren_AtObj(a,c),g=0;g<c.length;g++)e+=ASN1HEX.dump(a,b,c[g],d+" ");else a=ASN1HEX.getHexOfV_AtObj(a,c),"68747470"==a.substr(0,8)&&(a=hextoutf8(a)),"subjectAltName"===b.x509ExtName&&2==e&&(a=hextoutf8(a)),e=d+"["+e+"] "+a+"\n";return e}return d+"UNKNOWN("+a.substr(c,2)+") "+ASN1HEX.getHexOfV_AtObj(a,c)+"\n"};
+ASN1HEX.isASN1HEX=function(a){if(1==a.length%2)return!1;var b=ASN1HEX.getIntOfL_AtObj(a,0),c=a.substr(0,2),d=ASN1HEX.getHexOfL_AtObj(a,0);return a.length-c.length-d.length==2*b?!0:!1};exports.ASN1HEX=ASN1HEX;module.exports=exports;b64tohex=require("./base64.js").b64tohex;RSAKey=require("./rsa.js").RSAKey;ASN1HEX=require("./asn1hex-1.1.js").ASN1HEX;
+function X509(){this.hex=this.subjectPublicKeyRSA_hE=this.subjectPublicKeyRSA_hN=this.subjectPublicKeyRSA=null;this.getSerialNumberHex=function(){return ASN1HEX.getDecendantHexVByNthList(this.hex,0,[0,1])};this.getIssuerHex=function(){return ASN1HEX.getDecendantHexTLVByNthList(this.hex,0,[0,3])};this.getIssuerString=function(){return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex,0,[0,3]))};this.getSubjectHex=function(){return ASN1HEX.getDecendantHexTLVByNthList(this.hex,0,[0,5])};this.getSubjectString=
+function(){return X509.hex2dn(ASN1HEX.getDecendantHexTLVByNthList(this.hex,0,[0,5]))};this.getNotBefore=function(){var a=ASN1HEX.getDecendantHexVByNthList(this.hex,0,[0,4,0]),a=a.replace(/(..)/g,"%$1");return a=decodeURIComponent(a)};this.getNotAfter=function(){var a=ASN1HEX.getDecendantHexVByNthList(this.hex,0,[0,4,1]),a=a.replace(/(..)/g,"%$1");return a=decodeURIComponent(a)};this.readCertPEM=function(a){a=X509.pemToHex(a);var b=X509.getPublicKeyHexArrayFromCertHex(a),c=new RSAKey;c.setPublic(b[0],
+b[1]);this.subjectPublicKeyRSA=c;this.subjectPublicKeyRSA_hN=b[0];this.subjectPublicKeyRSA_hE=b[1];this.hex=a};this.readCertPEMWithoutRSAInit=function(a){a=X509.pemToHex(a);var b=X509.getPublicKeyHexArrayFromCertHex(a);this.subjectPublicKeyRSA.setPublic(b[0],b[1]);this.subjectPublicKeyRSA_hN=b[0];this.subjectPublicKeyRSA_hE=b[1];this.hex=a}}X509.pemToBase64=function(a){a=a.replace("-----BEGIN CERTIFICATE-----","");a=a.replace("-----END CERTIFICATE-----","");return a=a.replace(/[ \n]+/g,"")};
+X509.pemToHex=function(a){a=X509.pemToBase64(a);return b64tohex(a)};X509.getSubjectPublicKeyPosFromCertHex=function(a){var b=X509.getSubjectPublicKeyInfoPosFromCertHex(a);if(-1==b)return-1;b=ASN1HEX.getPosArrayOfChildren_AtObj(a,b);if(2!=b.length)return-1;b=b[1];if("03"!=a.substring(b,b+2))return-1;b=ASN1HEX.getStartPosOfV_AtObj(a,b);return"00"!=a.substring(b,b+2)?-1:b+2};
+X509.getSubjectPublicKeyInfoPosFromCertHex=function(a){var b=ASN1HEX.getStartPosOfV_AtObj(a,0),b=ASN1HEX.getPosArrayOfChildren_AtObj(a,b);return 1>b.length?-1:"a003020102"==a.substring(b[0],b[0]+10)?6>b.length?-1:b[6]:5>b.length?-1:b[5]};X509.getPublicKeyHexArrayFromCertHex=function(a){var b=X509.getSubjectPublicKeyPosFromCertHex(a),c=ASN1HEX.getPosArrayOfChildren_AtObj(a,b);if(2!=c.length)return[];b=ASN1HEX.getHexOfV_AtObj(a,c[0]);a=ASN1HEX.getHexOfV_AtObj(a,c[1]);return null!=b&&null!=a?[b,a]:[]};
+X509.getHexTbsCertificateFromCert=function(a){return ASN1HEX.getStartPosOfV_AtObj(a,0)};X509.getPublicKeyHexArrayFromCertPEM=function(a){a=X509.pemToHex(a);return X509.getPublicKeyHexArrayFromCertHex(a)};X509.hex2dn=function(a){for(var b="",c=ASN1HEX.getPosArrayOfChildren_AtObj(a,0),d=0;d<c.length;d++)var e=ASN1HEX.getHexOfTLV_AtObj(a,c[d]),b=b+"/"+X509.hex2rdn(e);return b};
+X509.hex2rdn=function(a){var b=ASN1HEX.getDecendantHexTLVByNthList(a,0,[0,0]),c=ASN1HEX.getDecendantHexVByNthList(a,0,[0,1]);a="";try{a=X509.DN_ATTRHEX[b]}catch(d){a=b}c=c.replace(/(..)/g,"%$1");b=decodeURIComponent(c);return a+"="+b};X509.DN_ATTRHEX={"0603550406":"C","060355040a":"O","060355040b":"OU","0603550403":"CN","0603550405":"SN","0603550408":"ST","0603550407":"L"};
+X509.getPublicKeyFromCertPEM=function(a){var b=X509.getPublicKeyInfoPropOfCertPEM(a);if("2a864886f70d010101"==b.algoid){var c=KEYUTIL.parsePublicRawRSAKeyHex(b.keyhex);a=new RSAKey;a.setPublic(c.n,c.e);return a}if("2a8648ce3d0201"==b.algoid)return a=new KJUR.crypto.ECDSA({curve:KJUR.crypto.OID.oidhex2name[b.algparam],info:b.keyhex}),a.setPublicKeyHex(b.keyhex),a;if("2a8648ce380401"==b.algoid){var c=ASN1HEX.getVbyList(b.algparam,0,[0],"02"),d=ASN1HEX.getVbyList(b.algparam,0,[1],"02"),e=ASN1HEX.getVbyList(b.algparam,
+0,[2],"02"),b=ASN1HEX.getHexOfV_AtObj(b.keyhex,0),b=b.substr(2);a=new KJUR.crypto.DSA;a.setPublic(new BigInteger(c,16),new BigInteger(d,16),new BigInteger(e,16),new BigInteger(b,16));return a}throw"unsupported key";};
+X509.getPublicKeyInfoPropOfCertPEM=function(a){var b={algparam:null};a=X509.pemToHex(a);var c=ASN1HEX.getPosArrayOfChildren_AtObj(a,0);if(3!=c.length)throw"malformed X.509 certificate PEM (code:001)";if("30"!=a.substr(c[0],2))throw"malformed X.509 certificate PEM (code:002)";c=ASN1HEX.getPosArrayOfChildren_AtObj(a,c[0]);if(7>c.length)throw"malformed X.509 certificate PEM (code:003)";c=ASN1HEX.getPosArrayOfChildren_AtObj(a,c[6]);if(2!=c.length)throw"malformed X.509 certificate PEM (code:004)";var d=
+ASN1HEX.getPosArrayOfChildren_AtObj(a,c[0]);if(2!=d.length)throw"malformed X.509 certificate PEM (code:005)";b.algoid=ASN1HEX.getHexOfV_AtObj(a,d[0]);"06"==a.substr(d[1],2)?b.algparam=ASN1HEX.getHexOfV_AtObj(a,d[1]):"30"==a.substr(d[1],2)&&(b.algparam=ASN1HEX.getHexOfTLV_AtObj(a,d[1]));if("03"!=a.substr(c[1],2))throw"malformed X.509 certificate PEM (code:006)";a=ASN1HEX.getHexOfV_AtObj(a,c[1]);b.keyhex=a.substr(2);return b};
+X509.getPublicKeyInfoPosOfCertHEX=function(a){var b=ASN1HEX.getPosArrayOfChildren_AtObj(a,0);if(3!=b.length)throw"malformed X.509 certificate PEM (code:001)";if("30"!=a.substr(b[0],2))throw"malformed X.509 certificate PEM (code:002)";a=ASN1HEX.getPosArrayOfChildren_AtObj(a,b[0]);if(7>a.length)throw"malformed X.509 certificate PEM (code:003)";return a[6]};
+X509.getV3ExtInfoListOfCertHex=function(a){var b=ASN1HEX.getPosArrayOfChildren_AtObj(a,0);if(3!=b.length)throw"malformed X.509 certificate PEM (code:001)";if("30"!=a.substr(b[0],2))throw"malformed X.509 certificate PEM (code:002)";b=ASN1HEX.getPosArrayOfChildren_AtObj(a,b[0]);if(8>b.length)throw"malformed X.509 certificate PEM (code:003)";if("a3"!=a.substr(b[7],2))throw"malformed X.509 certificate PEM (code:004)";b=ASN1HEX.getPosArrayOfChildren_AtObj(a,b[7]);if(1!=b.length)throw"malformed X.509 certificate PEM (code:005)";
+if("30"!=a.substr(b[0],2))throw"malformed X.509 certificate PEM (code:006)";for(var b=ASN1HEX.getPosArrayOfChildren_AtObj(a,b[0]),c=b.length,d=Array(c),e=0;e<c;e++)d[e]=X509.getV3ExtItemInfo_AtObj(a,b[e]);return d};
+X509.getV3ExtItemInfo_AtObj=function(a,b){var c={};c.posTLV=b;var d=ASN1HEX.getPosArrayOfChildren_AtObj(a,b);if(2!=d.length&&3!=d.length)throw"malformed X.509v3 Ext (code:001)";if("06"!=a.substr(d[0],2))throw"malformed X.509v3 Ext (code:002)";var e=ASN1HEX.getHexOfV_AtObj(a,d[0]);c.oid=ASN1HEX.hextooidstr(e);c.critical=!1;3==d.length&&(c.critical=!0);d=d[d.length-1];if("04"!=a.substr(d,2))throw"malformed X.509v3 Ext (code:003)";c.posV=ASN1HEX.getStartPosOfV_AtObj(a,d);return c};
+X509.getHexOfTLV_V3ExtValue=function(a,b){var c=X509.getPosOfTLV_V3ExtValue(a,b);return-1==c?"":ASN1HEX.getHexOfTLV_AtObj(a,c)};X509.getHexOfV_V3ExtValue=function(a,b){var c=X509.getPosOfTLV_V3ExtValue(a,b);return-1==c?"":ASN1HEX.getHexOfV_AtObj(a,c)};X509.getPosOfTLV_V3ExtValue=function(a,b){var c=b;b.match(/^[0-9.]+$/)||(c=KJUR.asn1.x509.OID.name2oid(b));if(""==c)return-1;for(var d=X509.getV3ExtInfoListOfCertHex(a),e=0;e<d.length;e++){var f=d[e];if(f.oid==c)return f.posV}return-1};
+X509.KEYUSAGE_NAME="digitalSignature nonRepudiation keyEncipherment dataEncipherment keyAgreement keyCertSign cRLSign encipherOnly decipherOnly".split(" ");X509.getExtKeyUsageBin=function(a){var b=X509.getHexOfV_V3ExtValue(a,"keyUsage");if(""==b)return"";if(0!=b.length%2||2>=b.length)throw"malformed key usage value";a=parseInt(b.substr(0,2));b=parseInt(b.substr(2),16).toString(2);return b.substr(0,b.length-a)};
+X509.getExtKeyUsageString=function(a){a=X509.getExtKeyUsageBin(a);for(var b=[],c=0;c<a.length;c++)"1"==a.substr(c,1)&&b.push(X509.KEYUSAGE_NAME[c]);return b.join(",")};
+X509.getExtAIAInfo=function(a){var b={ocsp:[],caissuer:[]},c=X509.getPosOfTLV_V3ExtValue(a,"authorityInfoAccess");if(-1==c)return null;if("30"!=a.substr(c,2))throw"malformed AIA Extn Value";for(var c=ASN1HEX.getPosArrayOfChildren_AtObj(a,c),d=0;d<c.length;d++){var e=ASN1HEX.getPosArrayOfChildren_AtObj(a,c[d]);if(2!=e.length)throw"malformed AccessDescription of AIA Extn";var f=e[0],e=e[1];"2b06010505073001"==ASN1HEX.getHexOfV_AtObj(a,f)&&"86"==a.substr(e,2)&&b.ocsp.push(hextoutf8(ASN1HEX.getHexOfV_AtObj(a,
+e)));"2b06010505073002"==ASN1HEX.getHexOfV_AtObj(a,f)&&"86"==a.substr(e,2)&&b.caissuer.push(hextoutf8(ASN1HEX.getHexOfV_AtObj(a,e)))}return b};exports.X509=X509;module.exports=exports;var dbits,canary=0xdeadbeefcafe,j_lm=15715070==(canary&16777215),BigInteger=function(a,b,c){null!=a&&("number"==typeof a?this.fromNumber(a,b,c):null==b&&"string"!=typeof a?this.fromString(a,256):this.fromString(a,b))};function nbi(){return new BigInteger(null)}
+function am1(a,b,c,d,e,f){for(;0<=--f;){var g=b*this[a++]+c[d]+e;e=Math.floor(g/67108864);c[d++]=g&67108863}return e}function am2(a,b,c,d,e,f){var g=b&32767;for(b>>=15;0<=--f;){var h=this[a]&32767,k=this[a++]>>15,l=b*h+k*g,h=g*h+((l&32767)<<15)+c[d]+(e&1073741823);e=(h>>>30)+(l>>>15)+b*k+(e>>>30);c[d++]=h&1073741823}return e}
+function am3(a,b,c,d,e,f){var g=b&16383;for(b>>=14;0<=--f;){var h=this[a]&16383,k=this[a++]>>14,l=b*h+k*g,h=g*h+((l&16383)<<14)+c[d]+e;e=(h>>28)+(l>>14)+b*k;c[d++]=h&268435455}return e}j_lm&&"Microsoft Internet Explorer"==navigator.appName?(BigInteger.prototype.am=am2,dbits=30):j_lm&&"Netscape"!=navigator.appName?(BigInteger.prototype.am=am1,dbits=26):(BigInteger.prototype.am=am3,dbits=28);BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=(1<<dbits)-1;BigInteger.prototype.DV=1<<dbits;
var BI_FP=52;BigInteger.prototype.FV=Math.pow(2,BI_FP);BigInteger.prototype.F1=BI_FP-dbits;BigInteger.prototype.F2=2*dbits-BI_FP;var BI_RM="0123456789abcdefghijklmnopqrstuvwxyz",BI_RC=[],rr,vv;rr=48;for(vv=0;9>=vv;++vv)BI_RC[rr++]=vv;rr=97;for(vv=10;36>vv;++vv)BI_RC[rr++]=vv;rr=65;for(vv=10;36>vv;++vv)BI_RC[rr++]=vv;function int2char(a){return BI_RM.charAt(a)}function intAt(a,b){var c=BI_RC[a.charCodeAt(b)];return null==c?-1:c}
-function bnpCopyTo(a){for(var b=this.t-1;0<=b;--b)a[b]=this[b];a.t=this.t;a.s=this.s}function bnpFromInt(a){this.t=1;this.s=0>a?-1:0;0<a?this[0]=a:-1>a?this[0]=a+DV:this.t=0}function nbv(a){var b=nbi();b.fromInt(a);return b}
+function bnpCopyTo(a){for(var b=this.t-1;0<=b;--b)a[b]=this[b];a.t=this.t;a.s=this.s}function bnpFromInt(a){this.t=1;this.s=0>a?-1:0;0<a?this[0]=a:-1>a?this[0]=a+this.DV:this.t=0}function nbv(a){var b=nbi();b.fromInt(a);return b}
function bnpFromString(a,b){var c;if(16==b)c=4;else if(8==b)c=3;else if(256==b)c=8;else if(2==b)c=1;else if(32==b)c=5;else if(4==b)c=2;else{this.fromRadix(a,b);return}this.s=this.t=0;for(var d=a.length,e=!1,f=0;0<=--d;){var g=8==c?a[d]&255:intAt(a,d);0>g?"-"==a.charAt(d)&&(e=!0):(e=!1,0==f?this[this.t++]=g:f+c>this.DB?(this[this.t-1]|=(g&(1<<this.DB-f)-1)<<f,this[this.t++]=g>>this.DB-f):this[this.t-1]|=g<<f,f+=c,f>=this.DB&&(f-=this.DB))}8==c&&0!=(a[0]&128)&&(this.s=-1,0<f&&(this[this.t-1]|=(1<<this.DB-
f)-1<<f));this.clamp();e&&BigInteger.ZERO.subTo(this,this)}function bnpClamp(){for(var a=this.s&this.DM;0<this.t&&this[this.t-1]==a;)--this.t}
-function bnToString(a){if(0>this.s)return"-"+this.negate().toString(a);if(16==a)a=4;else if(8==a)a=3;else if(2==a)a=1;else if(32==a)a=5;else if(4==a)a=2;else return this.toRadix(a);var b=(1<<a)-1,c,d=!1,e="",f=this.t,g=this.DB-f*this.DB%a;if(0<f--){if(g<this.DB&&0<(c=this[f]>>g))d=!0,e=int2char(c);for(;0<=f;)g<a?(c=(this[f]&(1<<g)-1)<<a-g,c|=this[--f]>>(g+=this.DB-a)):(c=this[f]>>(g-=a)&b,0>=g&&(g+=this.DB,--f)),0<c&&(d=!0),d&&(e+=int2char(c))}return d?e:"0"}
-function bnNegate(){var a=nbi();BigInteger.ZERO.subTo(this,a);return a}function bnAbs(){return 0>this.s?this.negate():this}function bnCompareTo(a){var b=this.s-a.s;if(0!=b)return b;var c=this.t,b=c-a.t;if(0!=b)return b;for(;0<=--c;)if(0!=(b=this[c]-a[c]))return b;return 0}function nbits(a){var b=1,c;if(0!=(c=a>>>16))a=c,b+=16;if(0!=(c=a>>8))a=c,b+=8;if(0!=(c=a>>4))a=c,b+=4;if(0!=(c=a>>2))a=c,b+=2;0!=a>>1&&(b+=1);return b}
+function bnToString(a){if(0>this.s)return"-"+this.negate().toString(a);if(16==a)a=4;else if(8==a)a=3;else if(2==a)a=1;else if(32==a)a=5;else if(4==a)a=2;else return this.toRadix(a);var b=(1<<a)-1,c,d=!1,e="",f=this.t,g=this.DB-f*this.DB%a;if(0<f--)for(g<this.DB&&0<(c=this[f]>>g)&&(d=!0,e=int2char(c));0<=f;)g<a?(c=(this[f]&(1<<g)-1)<<a-g,c|=this[--f]>>(g+=this.DB-a)):(c=this[f]>>(g-=a)&b,0>=g&&(g+=this.DB,--f)),0<c&&(d=!0),d&&(e+=int2char(c));return d?e:"0"}
+function bnNegate(){var a=nbi();BigInteger.ZERO.subTo(this,a);return a}function bnAbs(){return 0>this.s?this.negate():this}function bnCompareTo(a){var b=this.s-a.s;if(0!=b)return b;var c=this.t,b=c-a.t;if(0!=b)return 0>this.s?-b:b;for(;0<=--c;)if(0!=(b=this[c]-a[c]))return b;return 0}function nbits(a){var b=1,c;0!=(c=a>>>16)&&(a=c,b+=16);0!=(c=a>>8)&&(a=c,b+=8);0!=(c=a>>4)&&(a=c,b+=4);0!=(c=a>>2)&&(a=c,b+=2);0!=a>>1&&(b+=1);return b}
function bnBitLength(){return 0>=this.t?0:this.DB*(this.t-1)+nbits(this[this.t-1]^this.s&this.DM)}function bnpDLShiftTo(a,b){var c;for(c=this.t-1;0<=c;--c)b[c+a]=this[c];for(c=a-1;0<=c;--c)b[c]=0;b.t=this.t+a;b.s=this.s}function bnpDRShiftTo(a,b){for(var c=a;c<this.t;++c)b[c-a]=this[c];b.t=Math.max(this.t-a,0);b.s=this.s}
function bnpLShiftTo(a,b){var c=a%this.DB,d=this.DB-c,e=(1<<d)-1,f=Math.floor(a/this.DB),g=this.s<<c&this.DM,h;for(h=this.t-1;0<=h;--h)b[h+f+1]=this[h]>>d|g,g=(this[h]&e)<<c;for(h=f-1;0<=h;--h)b[h]=0;b[f]=g;b.t=this.t+f+1;b.s=this.s;b.clamp()}
function bnpRShiftTo(a,b){b.s=this.s;var c=Math.floor(a/this.DB);if(c>=this.t)b.t=0;else{var d=a%this.DB,e=this.DB-d,f=(1<<d)-1;b[0]=this[c]>>d;for(var g=c+1;g<this.t;++g)b[g-c-1]|=(this[g]&f)<<e,b[g-c]=this[g]>>d;0<d&&(b[this.t-c-1]|=(this.s&f)<<e);b.t=this.t-c;b.clamp()}}
function bnpSubTo(a,b){for(var c=0,d=0,e=Math.min(a.t,this.t);c<e;)d+=this[c]-a[c],b[c++]=d&this.DM,d>>=this.DB;if(a.t<this.t){for(d-=a.s;c<this.t;)d+=this[c],b[c++]=d&this.DM,d>>=this.DB;d+=this.s}else{for(d+=this.s;c<a.t;)d-=a[c],b[c++]=d&this.DM,d>>=this.DB;d-=a.s}b.s=0>d?-1:0;-1>d?b[c++]=this.DV+d:0<d&&(b[c++]=d);b.t=c;b.clamp()}
-function bnpMultiplyTo(a,b){var c=this.abs(),d=a.abs(),e=c.t;for(b.t=e+d.t;0<=--e;)b[e]=0;for(e=0;e<d.t;++e)b[e+c.t]=c.am(0,d[e],b,e,0,c.t);b.s=0;b.clamp();this.s!=a.s&&BigInteger.ZERO.subTo(b,b)}function bnpSquareTo(a){for(var b=this.abs(),c=a.t=2*b.t;0<=--c;)a[c]=0;for(c=0;c<b.t-1;++c){var d=b.am(c,b[c],a,2*c,0,1);if((a[c+b.t]+=b.am(c+1,2*b[c],a,2*c+1,d,b.t-c-1))>=b.DV)a[c+b.t]-=b.DV,a[c+b.t+1]=1}0<a.t&&(a[a.t-1]+=b.am(c,b[c],a,2*c,0,1));a.s=0;a.clamp()}
-function bnpDivRemTo(a,b,c){var d=a.abs();if(!(0>=d.t)){var e=this.abs();if(e.t<d.t)null!=b&&b.fromInt(0),null!=c&&this.copyTo(c);else{null==c&&(c=nbi());var f=nbi(),g=this.s;a=a.s;var h=this.DB-nbits(d[d.t-1]);0<h?(d.lShiftTo(h,f),e.lShiftTo(h,c)):(d.copyTo(f),e.copyTo(c));d=f.t;e=f[d-1];if(0!=e){var j=e*(1<<this.F1)+(1<d?f[d-2]>>this.F2:0),l=this.FV/j,j=(1<<this.F1)/j,n=1<<this.F2,m=c.t,p=m-d,k=null==b?nbi():b;f.dlShiftTo(p,k);0<=c.compareTo(k)&&(c[c.t++]=1,c.subTo(k,c));BigInteger.ONE.dlShiftTo(d,
-k);for(k.subTo(f,f);f.t<d;)f[f.t++]=0;for(;0<=--p;){var q=c[--m]==e?this.DM:Math.floor(c[m]*l+(c[m-1]+n)*j);if((c[m]+=f.am(0,q,c,p,0,d))<q){f.dlShiftTo(p,k);for(c.subTo(k,c);c[m]<--q;)c.subTo(k,c)}}null!=b&&(c.drShiftTo(d,b),g!=a&&BigInteger.ZERO.subTo(b,b));c.t=d;c.clamp();0<h&&c.rShiftTo(h,c);0>g&&BigInteger.ZERO.subTo(c,c)}}}}function bnMod(a){var b=nbi();this.abs().divRemTo(a,null,b);0>this.s&&0<b.compareTo(BigInteger.ZERO)&&a.subTo(b,b);return b}function Classic(a){this.m=a}
+function bnpMultiplyTo(a,b){var c=this.abs(),d=a.abs(),e=c.t;for(b.t=e+d.t;0<=--e;)b[e]=0;for(e=0;e<d.t;++e)b[e+c.t]=c.am(0,d[e],b,e,0,c.t);b.s=0;b.clamp();this.s!=a.s&&BigInteger.ZERO.subTo(b,b)}function bnpSquareTo(a){for(var b=this.abs(),c=a.t=2*b.t;0<=--c;)a[c]=0;for(c=0;c<b.t-1;++c){var d=b.am(c,b[c],a,2*c,0,1);(a[c+b.t]+=b.am(c+1,2*b[c],a,2*c+1,d,b.t-c-1))>=b.DV&&(a[c+b.t]-=b.DV,a[c+b.t+1]=1)}0<a.t&&(a[a.t-1]+=b.am(c,b[c],a,2*c,0,1));a.s=0;a.clamp()}
+function bnpDivRemTo(a,b,c){var d=a.abs();if(!(0>=d.t)){var e=this.abs();if(e.t<d.t)null!=b&&b.fromInt(0),null!=c&&this.copyTo(c);else{null==c&&(c=nbi());var f=nbi(),g=this.s;a=a.s;var h=this.DB-nbits(d[d.t-1]);0<h?(d.lShiftTo(h,f),e.lShiftTo(h,c)):(d.copyTo(f),e.copyTo(c));d=f.t;e=f[d-1];if(0!=e){var k=e*(1<<this.F1)+(1<d?f[d-2]>>this.F2:0),l=this.FV/k,k=(1<<this.F1)/k,m=1<<this.F2,q=c.t,p=q-d,s=null==b?nbi():b;f.dlShiftTo(p,s);0<=c.compareTo(s)&&(c[c.t++]=1,c.subTo(s,c));BigInteger.ONE.dlShiftTo(d,
+s);for(s.subTo(f,f);f.t<d;)f[f.t++]=0;for(;0<=--p;){var w=c[--q]==e?this.DM:Math.floor(c[q]*l+(c[q-1]+m)*k);if((c[q]+=f.am(0,w,c,p,0,d))<w)for(f.dlShiftTo(p,s),c.subTo(s,c);c[q]<--w;)c.subTo(s,c)}null!=b&&(c.drShiftTo(d,b),g!=a&&BigInteger.ZERO.subTo(b,b));c.t=d;c.clamp();0<h&&c.rShiftTo(h,c);0>g&&BigInteger.ZERO.subTo(c,c)}}}}function bnMod(a){var b=nbi();this.abs().divRemTo(a,null,b);0>this.s&&0<b.compareTo(BigInteger.ZERO)&&a.subTo(b,b);return b}function Classic(a){this.m=a}
function cConvert(a){return 0>a.s||0<=a.compareTo(this.m)?a.mod(this.m):a}function cRevert(a){return a}function cReduce(a){a.divRemTo(this.m,null,a)}function cMulTo(a,b,c){a.multiplyTo(b,c);this.reduce(c)}function cSqrTo(a,b){a.squareTo(b);this.reduce(b)}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;
function bnpInvDigit(){if(1>this.t)return 0;var a=this[0];if(0==(a&1))return 0;var b=a&3,b=b*(2-(a&15)*b)&15,b=b*(2-(a&255)*b)&255,b=b*(2-((a&65535)*b&65535))&65535,b=b*(2-a*b%this.DV)%this.DV;return 0<b?this.DV-b:-b}function Montgomery(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<<a.DB-15)-1;this.mt2=2*a.t}
function montConvert(a){var b=nbi();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);0>a.s&&0<b.compareTo(BigInteger.ZERO)&&this.m.subTo(b,b);return b}function montRevert(a){var b=nbi();a.copyTo(b);this.reduce(b);return b}
@@ -228,56 +169,952 @@
BigInteger.prototype.multiplyTo=bnpMultiplyTo;BigInteger.prototype.squareTo=bnpSquareTo;BigInteger.prototype.divRemTo=bnpDivRemTo;BigInteger.prototype.invDigit=bnpInvDigit;BigInteger.prototype.isEven=bnpIsEven;BigInteger.prototype.exp=bnpExp;BigInteger.prototype.toString=bnToString;BigInteger.prototype.negate=bnNegate;BigInteger.prototype.abs=bnAbs;BigInteger.prototype.compareTo=bnCompareTo;BigInteger.prototype.bitLength=bnBitLength;BigInteger.prototype.mod=bnMod;BigInteger.prototype.modPowInt=bnModPowInt;
BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1);function bnClone(){var a=nbi();this.copyTo(a);return a}function bnIntValue(){if(0>this.s){if(1==this.t)return this[0]-this.DV;if(0==this.t)return-1}else{if(1==this.t)return this[0];if(0==this.t)return 0}return(this[1]&(1<<32-this.DB)-1)<<this.DB|this[0]}function bnByteValue(){return 0==this.t?this.s:this[0]<<24>>24}function bnShortValue(){return 0==this.t?this.s:this[0]<<16>>16}function bnpChunkSize(a){return Math.floor(Math.LN2*this.DB/Math.log(a))}
function bnSigNum(){return 0>this.s?-1:0>=this.t||1==this.t&&0>=this[0]?0:1}function bnpToRadix(a){null==a&&(a=10);if(0==this.signum()||2>a||36<a)return"0";var b=this.chunkSize(a),b=Math.pow(a,b),c=nbv(b),d=nbi(),e=nbi(),f="";for(this.divRemTo(c,d,e);0<d.signum();)f=(b+e.intValue()).toString(a).substr(1)+f,d.divRemTo(c,d,e);return e.intValue().toString(a)+f}
-function bnpFromRadix(a,b){this.fromInt(0);null==b&&(b=10);for(var c=this.chunkSize(b),d=Math.pow(b,c),e=!1,f=0,g=0,h=0;h<a.length;++h){var j=intAt(a,h);0>j?"-"==a.charAt(h)&&0==this.signum()&&(e=!0):(g=b*g+j,++f>=c&&(this.dMultiply(d),this.dAddOffset(g,0),g=f=0))}0<f&&(this.dMultiply(Math.pow(b,f)),this.dAddOffset(g,0));e&&BigInteger.ZERO.subTo(this,this)}
-function bnpFromNumber(a,b,c){if("number"==typeof b)if(2>a)this.fromInt(1);else{this.fromNumber(a,c);this.testBit(a-1)||this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);for(this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(b);)this.dAddOffset(2,0),this.bitLength()>a&&this.subTo(BigInteger.ONE.shiftLeft(a-1),this)}else{c=[];var d=a&7;c.length=(a>>3)+1;b.nextBytes(c);c[0]=0<d?c[0]&(1<<d)-1:0;this.fromString(c,256)}}
-function bnToByteArray(){var a=this.t,b=[];b[0]=this.s;var c=this.DB-a*this.DB%8,d,e=0;if(0<a--){if(c<this.DB&&(d=this[a]>>c)!=(this.s&this.DM)>>c)b[e++]=d|this.s<<this.DB-c;for(;0<=a;)if(8>c?(d=(this[a]&(1<<c)-1)<<8-c,d|=this[--a]>>(c+=this.DB-8)):(d=this[a]>>(c-=8)&255,0>=c&&(c+=this.DB,--a)),0!=(d&128)&&(d|=-256),0==e&&(this.s&128)!=(d&128)&&++e,0<e||d!=this.s)b[e++]=d}return b}function bnEquals(a){return 0==this.compareTo(a)}function bnMin(a){return 0>this.compareTo(a)?this:a}
+function bnpFromRadix(a,b){this.fromInt(0);null==b&&(b=10);for(var c=this.chunkSize(b),d=Math.pow(b,c),e=!1,f=0,g=0,h=0;h<a.length;++h){var k=intAt(a,h);0>k?"-"==a.charAt(h)&&0==this.signum()&&(e=!0):(g=b*g+k,++f>=c&&(this.dMultiply(d),this.dAddOffset(g,0),g=f=0))}0<f&&(this.dMultiply(Math.pow(b,f)),this.dAddOffset(g,0));e&&BigInteger.ZERO.subTo(this,this)}
+function bnpFromNumber(a,b,c){if("number"==typeof b)if(2>a)this.fromInt(1);else for(this.fromNumber(a,c),this.testBit(a-1)||this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this),this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(b);)this.dAddOffset(2,0),this.bitLength()>a&&this.subTo(BigInteger.ONE.shiftLeft(a-1),this);else{c=[];var d=a&7;c.length=(a>>3)+1;b.nextBytes(c);c[0]=0<d?c[0]&(1<<d)-1:0;this.fromString(c,256)}}
+function bnToByteArray(){var a=this.t,b=[];b[0]=this.s;var c=this.DB-a*this.DB%8,d,e=0;if(0<a--)for(c<this.DB&&(d=this[a]>>c)!=(this.s&this.DM)>>c&&(b[e++]=d|this.s<<this.DB-c);0<=a;)if(8>c?(d=(this[a]&(1<<c)-1)<<8-c,d|=this[--a]>>(c+=this.DB-8)):(d=this[a]>>(c-=8)&255,0>=c&&(c+=this.DB,--a)),0!=(d&128)&&(d|=-256),0==e&&(this.s&128)!=(d&128)&&++e,0<e||d!=this.s)b[e++]=d;return b}function bnEquals(a){return 0==this.compareTo(a)}function bnMin(a){return 0>this.compareTo(a)?this:a}
function bnMax(a){return 0<this.compareTo(a)?this:a}function bnpBitwiseTo(a,b,c){var d,e,f=Math.min(a.t,this.t);for(d=0;d<f;++d)c[d]=b(this[d],a[d]);if(a.t<this.t){e=a.s&this.DM;for(d=f;d<this.t;++d)c[d]=b(this[d],e);c.t=this.t}else{e=this.s&this.DM;for(d=f;d<a.t;++d)c[d]=b(e,a[d]);c.t=a.t}c.s=b(this.s,a.s);c.clamp()}function op_and(a,b){return a&b}function bnAnd(a){var b=nbi();this.bitwiseTo(a,op_and,b);return b}function op_or(a,b){return a|b}
function bnOr(a){var b=nbi();this.bitwiseTo(a,op_or,b);return b}function op_xor(a,b){return a^b}function bnXor(a){var b=nbi();this.bitwiseTo(a,op_xor,b);return b}function op_andnot(a,b){return a&~b}function bnAndNot(a){var b=nbi();this.bitwiseTo(a,op_andnot,b);return b}function bnNot(){for(var a=nbi(),b=0;b<this.t;++b)a[b]=this.DM&~this[b];a.t=this.t;a.s=~this.s;return a}function bnShiftLeft(a){var b=nbi();0>a?this.rShiftTo(-a,b):this.lShiftTo(a,b);return b}
function bnShiftRight(a){var b=nbi();0>a?this.lShiftTo(-a,b):this.rShiftTo(a,b);return b}function lbit(a){if(0==a)return-1;var b=0;0==(a&65535)&&(a>>=16,b+=16);0==(a&255)&&(a>>=8,b+=8);0==(a&15)&&(a>>=4,b+=4);0==(a&3)&&(a>>=2,b+=2);0==(a&1)&&++b;return b}function bnGetLowestSetBit(){for(var a=0;a<this.t;++a)if(0!=this[a])return a*this.DB+lbit(this[a]);return 0>this.s?this.t*this.DB:-1}function cbit(a){for(var b=0;0!=a;)a&=a-1,++b;return b}
function bnBitCount(){for(var a=0,b=this.s&this.DM,c=0;c<this.t;++c)a+=cbit(this[c]^b);return a}function bnTestBit(a){var b=Math.floor(a/this.DB);return b>=this.t?0!=this.s:0!=(this[b]&1<<a%this.DB)}function bnpChangeBit(a,b){var c=BigInteger.ONE.shiftLeft(a);this.bitwiseTo(c,b,c);return c}function bnSetBit(a){return this.changeBit(a,op_or)}function bnClearBit(a){return this.changeBit(a,op_andnot)}function bnFlipBit(a){return this.changeBit(a,op_xor)}
function bnpAddTo(a,b){for(var c=0,d=0,e=Math.min(a.t,this.t);c<e;)d+=this[c]+a[c],b[c++]=d&this.DM,d>>=this.DB;if(a.t<this.t){for(d+=a.s;c<this.t;)d+=this[c],b[c++]=d&this.DM,d>>=this.DB;d+=this.s}else{for(d+=this.s;c<a.t;)d+=a[c],b[c++]=d&this.DM,d>>=this.DB;d+=a.s}b.s=0>d?-1:0;0<d?b[c++]=d:-1>d&&(b[c++]=this.DV+d);b.t=c;b.clamp()}function bnAdd(a){var b=nbi();this.addTo(a,b);return b}function bnSubtract(a){var b=nbi();this.subTo(a,b);return b}
-function bnMultiply(a){var b=nbi();this.multiplyTo(a,b);return b}function bnDivide(a){var b=nbi();this.divRemTo(a,b,null);return b}function bnRemainder(a){var b=nbi();this.divRemTo(a,null,b);return b}function bnDivideAndRemainder(a){var b=nbi(),c=nbi();this.divRemTo(a,b,c);return[b,c]}function bnpDMultiply(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()}
+function bnMultiply(a){var b=nbi();this.multiplyTo(a,b);return b}function bnSquare(){var a=nbi();this.squareTo(a);return a}function bnDivide(a){var b=nbi();this.divRemTo(a,b,null);return b}function bnRemainder(a){var b=nbi();this.divRemTo(a,null,b);return b}function bnDivideAndRemainder(a){var b=nbi(),c=nbi();this.divRemTo(a,b,c);return[b,c]}function bnpDMultiply(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()}
function bnpDAddOffset(a,b){if(0!=a){for(;this.t<=b;)this[this.t++]=0;for(this[b]+=a;this[b]>=this.DV;)this[b]-=this.DV,++b>=this.t&&(this[this.t++]=0),++this[b]}}function NullExp(){}function nNop(a){return a}function nMulTo(a,b,c){a.multiplyTo(b,c)}function nSqrTo(a,b){a.squareTo(b)}NullExp.prototype.convert=nNop;NullExp.prototype.revert=nNop;NullExp.prototype.mulTo=nMulTo;NullExp.prototype.sqrTo=nSqrTo;function bnPow(a){return this.exp(a,new NullExp)}
function bnpMultiplyLowerTo(a,b,c){var d=Math.min(this.t+a.t,b);c.s=0;for(c.t=d;0<d;)c[--d]=0;var e;for(e=c.t-this.t;d<e;++d)c[d+this.t]=this.am(0,a[d],c,d,0,this.t);for(e=Math.min(a.t,b);d<e;++d)this.am(0,a[d],c,d,0,b-d);c.clamp()}function bnpMultiplyUpperTo(a,b,c){--b;var d=c.t=this.t+a.t-b;for(c.s=0;0<=--d;)c[d]=0;for(d=Math.max(b-this.t,0);d<a.t;++d)c[this.t+d-b]=this.am(b-d,a[d],c,0,0,this.t+d-b);c.clamp();c.drShiftTo(1,c)}
function Barrett(a){this.r2=nbi();this.q3=nbi();BigInteger.ONE.dlShiftTo(2*a.t,this.r2);this.mu=this.r2.divide(a);this.m=a}function barrettConvert(a){if(0>a.s||a.t>2*this.m.t)return a.mod(this.m);if(0>a.compareTo(this.m))return a;var b=nbi();a.copyTo(b);this.reduce(b);return b}function barrettRevert(a){return a}
function barrettReduce(a){a.drShiftTo(this.m.t-1,this.r2);a.t>this.m.t+1&&(a.t=this.m.t+1,a.clamp());this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);for(this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);0>a.compareTo(this.r2);)a.dAddOffset(1,this.m.t+1);for(a.subTo(this.r2,a);0<=a.compareTo(this.m);)a.subTo(this.m,a)}function barrettSqrTo(a,b){a.squareTo(b);this.reduce(b)}function barrettMulTo(a,b,c){a.multiplyTo(b,c);this.reduce(c)}Barrett.prototype.convert=barrettConvert;
Barrett.prototype.revert=barrettRevert;Barrett.prototype.reduce=barrettReduce;Barrett.prototype.mulTo=barrettMulTo;Barrett.prototype.sqrTo=barrettSqrTo;
-function bnModPow(a,b){var c=a.bitLength(),d,e=nbv(1),f;if(0>=c)return e;d=18>c?1:48>c?3:144>c?4:768>c?5:6;f=8>c?new Classic(b):b.isEven()?new Barrett(b):new Montgomery(b);var g=[],h=3,j=d-1,l=(1<<d)-1;g[1]=f.convert(this);if(1<d){c=nbi();for(f.sqrTo(g[1],c);h<=l;)g[h]=nbi(),f.mulTo(c,g[h-2],g[h]),h+=2}for(var n=a.t-1,m,p=!0,k=nbi(),c=nbits(a[n])-1;0<=n;){c>=j?m=a[n]>>c-j&l:(m=(a[n]&(1<<c+1)-1)<<j-c,0<n&&(m|=a[n-1]>>this.DB+c-j));for(h=d;0==(m&1);)m>>=1,--h;if(0>(c-=h))c+=this.DB,--n;if(p)g[m].copyTo(e),
-p=!1;else{for(;1<h;)f.sqrTo(e,k),f.sqrTo(k,e),h-=2;0<h?f.sqrTo(e,k):(h=e,e=k,k=h);f.mulTo(k,g[m],e)}for(;0<=n&&0==(a[n]&1<<c);)f.sqrTo(e,k),h=e,e=k,k=h,0>--c&&(c=this.DB-1,--n)}return f.revert(e)}
+function bnModPow(a,b){var c=a.bitLength(),d,e=nbv(1),f;if(0>=c)return e;d=18>c?1:48>c?3:144>c?4:768>c?5:6;f=8>c?new Classic(b):b.isEven()?new Barrett(b):new Montgomery(b);var g=[],h=3,k=d-1,l=(1<<d)-1;g[1]=f.convert(this);if(1<d)for(c=nbi(),f.sqrTo(g[1],c);h<=l;)g[h]=nbi(),f.mulTo(c,g[h-2],g[h]),h+=2;for(var m=a.t-1,q,p=!0,s=nbi(),c=nbits(a[m])-1;0<=m;){c>=k?q=a[m]>>c-k&l:(q=(a[m]&(1<<c+1)-1)<<k-c,0<m&&(q|=a[m-1]>>this.DB+c-k));for(h=d;0==(q&1);)q>>=1,--h;0>(c-=h)&&(c+=this.DB,--m);if(p)g[q].copyTo(e),
+p=!1;else{for(;1<h;)f.sqrTo(e,s),f.sqrTo(s,e),h-=2;0<h?f.sqrTo(e,s):(h=e,e=s,s=h);f.mulTo(s,g[q],e)}for(;0<=m&&0==(a[m]&1<<c);)f.sqrTo(e,s),h=e,e=s,s=h,0>--c&&(c=this.DB-1,--m)}return f.revert(e)}
function bnGCD(a){var b=0>this.s?this.negate():this.clone();a=0>a.s?a.negate():a.clone();if(0>b.compareTo(a)){var c=b,b=a;a=c}var c=b.getLowestSetBit(),d=a.getLowestSetBit();if(0>d)return b;c<d&&(d=c);0<d&&(b.rShiftTo(d,b),a.rShiftTo(d,a));for(;0<b.signum();)0<(c=b.getLowestSetBit())&&b.rShiftTo(c,b),0<(c=a.getLowestSetBit())&&a.rShiftTo(c,a),0<=b.compareTo(a)?(b.subTo(a,b),b.rShiftTo(1,b)):(a.subTo(b,a),a.rShiftTo(1,a));0<d&&a.lShiftTo(d,a);return a}
function bnpModInt(a){if(0>=a)return 0;var b=this.DV%a,c=0>this.s?a-1:0;if(0<this.t)if(0==b)c=this[0]%a;else for(var d=this.t-1;0<=d;--d)c=(b*c+this[d])%a;return c}
-function bnModInverse(a){var b=a.isEven();if(this.isEven()&&b||0==a.signum())return BigInteger.ZERO;for(var c=a.clone(),d=this.clone(),e=nbv(1),f=nbv(0),g=nbv(0),h=nbv(1);0!=c.signum();){for(;c.isEven();){c.rShiftTo(1,c);if(b){if(!e.isEven()||!f.isEven())e.addTo(this,e),f.subTo(a,f);e.rShiftTo(1,e)}else f.isEven()||f.subTo(a,f);f.rShiftTo(1,f)}for(;d.isEven();){d.rShiftTo(1,d);if(b){if(!g.isEven()||!h.isEven())g.addTo(this,g),h.subTo(a,h);g.rShiftTo(1,g)}else h.isEven()||h.subTo(a,h);h.rShiftTo(1,
-h)}0<=c.compareTo(d)?(c.subTo(d,c),b&&e.subTo(g,e),f.subTo(h,f)):(d.subTo(c,d),b&&g.subTo(e,g),h.subTo(f,h))}if(0!=d.compareTo(BigInteger.ONE))return BigInteger.ZERO;if(0<=h.compareTo(a))return h.subtract(a);if(0>h.signum())h.addTo(a,h);else return h;return 0>h.signum()?h.add(a):h}
-var lowprimes=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509],lplim=67108864/lowprimes[lowprimes.length-1];
+function bnModInverse(a){var b=a.isEven();if(this.isEven()&&b||0==a.signum())return BigInteger.ZERO;for(var c=a.clone(),d=this.clone(),e=nbv(1),f=nbv(0),g=nbv(0),h=nbv(1);0!=c.signum();){for(;c.isEven();)c.rShiftTo(1,c),b?(e.isEven()&&f.isEven()||(e.addTo(this,e),f.subTo(a,f)),e.rShiftTo(1,e)):f.isEven()||f.subTo(a,f),f.rShiftTo(1,f);for(;d.isEven();)d.rShiftTo(1,d),b?(g.isEven()&&h.isEven()||(g.addTo(this,g),h.subTo(a,h)),g.rShiftTo(1,g)):h.isEven()||h.subTo(a,h),h.rShiftTo(1,h);0<=c.compareTo(d)?
+(c.subTo(d,c),b&&e.subTo(g,e),f.subTo(h,f)):(d.subTo(c,d),b&&g.subTo(e,g),h.subTo(f,h))}if(0!=d.compareTo(BigInteger.ONE))return BigInteger.ZERO;if(0<=h.compareTo(a))return h.subtract(a);if(0>h.signum())h.addTo(a,h);else return h;return 0>h.signum()?h.add(a):h}
+var lowprimes=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,
+733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997],lplim=67108864/lowprimes[lowprimes.length-1];
function bnIsProbablePrime(a){var b,c=this.abs();if(1==c.t&&c[0]<=lowprimes[lowprimes.length-1]){for(b=0;b<lowprimes.length;++b)if(c[0]==lowprimes[b])return!0;return!1}if(c.isEven())return!1;for(b=1;b<lowprimes.length;){for(var d=lowprimes[b],e=b+1;e<lowprimes.length&&d<lplim;)d*=lowprimes[e++];for(d=c.modInt(d);b<e;)if(0==d%lowprimes[b++])return!1}return c.millerRabin(a)}
-function bnpMillerRabin(a){var b=this.subtract(BigInteger.ONE),c=b.getLowestSetBit();if(0>=c)return!1;var d=b.shiftRight(c);a=a+1>>1;a>lowprimes.length&&(a=lowprimes.length);for(var e=nbi(),f=0;f<a;++f){e.fromInt(lowprimes[f]);var g=e.modPow(d,this);if(0!=g.compareTo(BigInteger.ONE)&&0!=g.compareTo(b)){for(var h=1;h++<c&&0!=g.compareTo(b);)if(g=g.modPowInt(2,this),0==g.compareTo(BigInteger.ONE))return!1;if(0!=g.compareTo(b))return!1}}return!0}BigInteger.prototype.chunkSize=bnpChunkSize;
-BigInteger.prototype.toRadix=bnpToRadix;BigInteger.prototype.fromRadix=bnpFromRadix;BigInteger.prototype.fromNumber=bnpFromNumber;BigInteger.prototype.bitwiseTo=bnpBitwiseTo;BigInteger.prototype.changeBit=bnpChangeBit;BigInteger.prototype.addTo=bnpAddTo;BigInteger.prototype.dMultiply=bnpDMultiply;BigInteger.prototype.dAddOffset=bnpDAddOffset;BigInteger.prototype.multiplyLowerTo=bnpMultiplyLowerTo;BigInteger.prototype.multiplyUpperTo=bnpMultiplyUpperTo;BigInteger.prototype.modInt=bnpModInt;
-BigInteger.prototype.millerRabin=bnpMillerRabin;BigInteger.prototype.clone=bnClone;BigInteger.prototype.intValue=bnIntValue;BigInteger.prototype.byteValue=bnByteValue;BigInteger.prototype.shortValue=bnShortValue;BigInteger.prototype.signum=bnSigNum;BigInteger.prototype.toByteArray=bnToByteArray;BigInteger.prototype.equals=bnEquals;BigInteger.prototype.min=bnMin;BigInteger.prototype.max=bnMax;BigInteger.prototype.and=bnAnd;BigInteger.prototype.or=bnOr;BigInteger.prototype.xor=bnXor;
-BigInteger.prototype.andNot=bnAndNot;BigInteger.prototype.not=bnNot;BigInteger.prototype.shiftLeft=bnShiftLeft;BigInteger.prototype.shiftRight=bnShiftRight;BigInteger.prototype.getLowestSetBit=bnGetLowestSetBit;BigInteger.prototype.bitCount=bnBitCount;BigInteger.prototype.testBit=bnTestBit;BigInteger.prototype.setBit=bnSetBit;BigInteger.prototype.clearBit=bnClearBit;BigInteger.prototype.flipBit=bnFlipBit;BigInteger.prototype.add=bnAdd;BigInteger.prototype.subtract=bnSubtract;
-BigInteger.prototype.multiply=bnMultiply;BigInteger.prototype.divide=bnDivide;BigInteger.prototype.remainder=bnRemainder;BigInteger.prototype.divideAndRemainder=bnDivideAndRemainder;BigInteger.prototype.modPow=bnModPow;BigInteger.prototype.modInverse=bnModInverse;BigInteger.prototype.pow=bnPow;BigInteger.prototype.gcd=bnGCD;BigInteger.prototype.isProbablePrime=bnIsProbablePrime;
-var LOG=0,NDN=function NDN(b){if(!NDN.supported)throw Error("The necessary JavaScript support is not available on this platform.");b=b||{};this.transport=(b.getTransport||function(){return new WebSocketTransport})();this.getHostAndPort=b.getHostAndPort||this.transport.defaultGetHostAndPort;this.host=void 0!==b.host?b.host:null;this.port=b.port||9696;this.readyStatus=NDN.UNOPEN;this.verify=void 0!==b.verify?b.verify:!0;this.onopen=b.onopen||function(){3<LOG&&console.log("NDN connection established.")};
-this.onclose=b.onclose||function(){3<LOG&&console.log("NDN connection closed.")};this.ndndid=null};NDN.UNOPEN=0;NDN.OPENED=1;NDN.CLOSED=2;NDN.getSupported=function(){try{(new Uint8Array(1)).subarray(0,1)}catch(a){return console.log("NDN not available: Uint8Array not supported. "+a),!1}return!0};NDN.supported=NDN.getSupported();NDN.ndndIdFetcher=new Name("/%C1.M.S.localhost/%C1.M.SRV/ndnd/KEY");NDN.prototype.createRoute=function(a,b){this.host=a;this.port=b};NDN.KeyStore=[];
-var KeyStoreEntry=function(a,b,c){this.keyName=a;this.rsaKey=b;this.timeStamp=c};NDN.addKeyEntry=function(a){null==NDN.getKeyByName(a.keyName)&&NDN.KeyStore.push(a)};NDN.getKeyByName=function(a){for(var b=null,c=0;c<NDN.KeyStore.length;c++)if(NDN.KeyStore[c].keyName.contentName.match(a.contentName)&&(null==b||NDN.KeyStore[c].keyName.contentName.components.length>b.keyName.contentName.components.length))b=NDN.KeyStore[c];return b};NDN.PITTable=[];
-var PITEntry=function(a,b){this.interest=a;this.closure=b;this.timerID=-1};NDN.getEntryForExpressedInterest=function(a){for(var b=null,c=0;c<NDN.PITTable.length;c++)if(NDN.PITTable[c].interest.matches_name(a)&&(null==b||NDN.PITTable[c].interest.name.components.length>b.interest.name.components.length))b=NDN.PITTable[c];return b};NDN.CSTable=[];var CSEntry=function(a,b){this.name=a;this.closure=b};
-function getEntryForRegisteredPrefix(a){for(var b=0;b<NDN.CSTable.length;b++)if(null!=NDN.CSTable[b].name.match(a))return NDN.CSTable[b];return null}NDN.makeShuffledGetHostAndPort=function(a,b){a=a.slice(0,a.length);DataUtils.shuffle(a);return function(){return 0==a.length?null:{host:a.splice(0,1)[0],port:b}}};
-NDN.prototype.expressInterest=function(a,b,c){var d=new Interest(a);null!=c?(d.minSuffixComponents=c.minSuffixComponents,d.maxSuffixComponents=c.maxSuffixComponents,d.publisherPublicKeyDigest=c.publisherPublicKeyDigest,d.exclude=c.exclude,d.childSelector=c.childSelector,d.answerOriginKind=c.answerOriginKind,d.scope=c.scope,d.interestLifetime=c.interestLifetime):d.interestLifetime=4E3;if(null==this.host||null==this.port)if(null==this.getHostAndPort)console.log("ERROR: host OR port NOT SET");else{var e=
-this;this.connectAndExecute(function(){e.reconnectAndExpressInterest(d,b)})}else this.reconnectAndExpressInterest(d,b)};NDN.prototype.reconnectAndExpressInterest=function(a,b){if(this.transport.connectedHost!=this.host||this.transport.connectedPort!=this.port){var c=this;this.transport.connect(c,function(){c.expressInterestHelper(a,b)})}else this.expressInterestHelper(a,b)};
-NDN.prototype.expressInterestHelper=function(a,b){var c=encodeToBinaryInterest(a),d=this;if(null!=b){var e=new PITEntry(a,b);NDN.PITTable.push(e);b.pitEntry=e;var f=a.interestLifetime||4E3,g=function(){3<LOG&&console.log("Interest time out: "+a.name.to_uri());var h=NDN.PITTable.indexOf(e);0<=h&&NDN.PITTable.splice(h,1);b.upcall(Closure.UPCALL_INTEREST_TIMED_OUT,new UpcallInfo(d,a,0,null))==Closure.RESULT_REEXPRESS&&(3<LOG&&console.log("Re-express interest: "+a.name.to_uri()),e.timerID=setTimeout(g,
-f),NDN.PITTable.push(e),d.transport.send(c))};e.timerID=setTimeout(g,f)}this.transport.send(c)};
-NDN.prototype.registerPrefix=function(a,b,c){var d=this,e=function(){if(null==d.ndndid){var e=new Interest(NDN.ndndIdFetcher);e.interestLifetime=4E3;3<LOG&&console.log("Expressing interest for ndndid from ndnd.");d.reconnectAndExpressInterest(e,new NDN.FetchNdndidClosure(d,a,b,c))}else d.registerPrefixHelper(a,b,c)};null==this.host||null==this.port?null==this.getHostAndPort?console.log("ERROR: host OR port NOT SET"):this.connectAndExecute(e):e()};
-NDN.FetchNdndidClosure=function(a,b,c,d){Closure.call(this);this.ndn=a;this.name=b;this.callerClosure=c;this.flag=d};
-NDN.FetchNdndidClosure.prototype.upcall=function(a,b){if(a==Closure.UPCALL_INTEREST_TIMED_OUT)return console.log("Timeout while requesting the ndndid. Cannot registerPrefix for "+this.name.to_uri()+" ."),Closure.RESULT_OK;if(!(a==Closure.UPCALL_CONTENT||a==Closure.UPCALL_CONTENT_UNVERIFIED))return Closure.RESULT_ERR;var c=b.contentObject;!c.signedInfo||!c.signedInfo.publisher||!c.signedInfo.publisher.publisherPublicKeyDigest?console.log("ContentObject doesn't have a publisherPublicKeyDigest. Cannot set ndndid and registerPrefix for "+
-this.name.to_uri()+" ."):(3<LOG&&console.log("Got ndndid from ndnd."),this.ndn.ndndid=c.signedInfo.publisher.publisherPublicKeyDigest,3<LOG&&console.log(this.ndn.ndndid),this.ndn.registerPrefixHelper(this.name,this.callerClosure,this.flag));return Closure.RESULT_OK};
-NDN.prototype.registerPrefixHelper=function(a,b){var c=new ForwardingEntry("selfreg",a,null,null,3,2147483647),c=encodeForwardingEntry(c),d=new SignedInfo;d.setFields();c=new ContentObject(new Name,d,c,new Signature);c.sign();c=encodeToBinaryContentObject(c);c=new Name(["ndnx",this.ndndid,"selfreg",c]);c=new Interest(c);c.scope=1;3<LOG&&console.log("Send Interest registration packet.");d=new CSEntry(a.getName(),b);NDN.CSTable.push(d);this.transport.send(encodeToBinaryInterest(c))};
-NDN.prototype.onReceivedElement=function(a){3<LOG&&console.log("Complete element received. Length "+a.length+". Start decoding.");var b=new BinaryXMLDecoder(a);if(b.peekStartElement(NDNProtocolDTags.Interest))3<LOG&&console.log("Interest packet received."),a=new Interest,a.from_ndnb(b),3<LOG&&console.log(a),b=escape(a.name.getName()),3<LOG&&console.log(b),b=getEntryForRegisteredPrefix(b),null!=b&&(a=new UpcallInfo(this,a,0,null),b.closure.upcall(Closure.UPCALL_INTEREST,a)==Closure.RESULT_INTEREST_CONSUMED&&
-null!=a.contentObject&&this.transport.send(encodeToBinaryContentObject(a.contentObject)));else if(b.peekStartElement(NDNProtocolDTags.ContentObject)){if(3<LOG&&console.log("ContentObject packet received."),a=new ContentObject,a.from_ndnb(b),b=NDN.getEntryForExpressedInterest(a.name),null!=b){clearTimeout(b.timerID);var c=NDN.PITTable.indexOf(b);0<=c&&NDN.PITTable.splice(c,1);c=b.closure;if(!1==this.verify)c.upcall(Closure.UPCALL_CONTENT_UNVERIFIED,new UpcallInfo(this,b.interest,0,a));else{var d=function(a,
-b,c,d,e){this.contentObject=a;this.closure=b;this.keyName=c;this.sigHex=d;this.witness=e;Closure.call(this)},e=this;d.prototype.upcall=function(a,b){if(a==Closure.UPCALL_INTEREST_TIMED_OUT)console.log("In KeyFetchClosure.upcall: interest time out."),console.log(this.keyName.contentName.getName());else if(a==Closure.UPCALL_CONTENT){var c=decodeSubjectPublicKeyInfo(b.contentObject.content),d=!0==c.verifyByteArray(this.contentObject.rawSignatureData,this.witness,this.sigHex)?Closure.UPCALL_CONTENT:Closure.UPCALL_CONTENT_BAD;
-this.closure.upcall(d,new UpcallInfo(e,null,0,this.contentObject));c=new KeyStoreEntry(h.keyName,c,(new Date).getTime());NDN.addKeyEntry(c)}else a==Closure.UPCALL_CONTENT_BAD&&console.log("In KeyFetchClosure.upcall: signature verification failed")};if(a.signedInfo&&a.signedInfo.locator&&a.signature){3<LOG&&console.log("Key verification...");var f=DataUtils.toHex(a.signature.signature).toLowerCase(),g=null;null!=a.signature.Witness&&(g=new Witness,g.decode(a.signature.Witness));var h=a.signedInfo.locator;
-if(h.type==KeyLocatorType.KEYNAME)if(3<LOG&&console.log("KeyLocator contains KEYNAME"),h.keyName.contentName.match(a.name))3<LOG&&console.log("Content is key itself"),d=decodeSubjectPublicKeyInfo(a.content),f=d.verifyByteArray(a.rawSignatureData,g,f),f=!0==f?Closure.UPCALL_CONTENT:Closure.UPCALL_CONTENT_BAD,c.upcall(f,new UpcallInfo(this,b.interest,0,a));else{var j=NDN.getKeyByName(h.keyName);j?(3<LOG&&console.log("Local key cache hit"),d=j.rsaKey,f=d.verifyByteArray(a.rawSignatureData,g,f),f=!0==
-f?Closure.UPCALL_CONTENT:Closure.UPCALL_CONTENT_BAD,c.upcall(f,new UpcallInfo(this,b.interest,0,a))):(3<LOG&&console.log("Fetch key according to keylocator"),a=new d(a,c,h.keyName,f,g),this.expressInterest(h.keyName.contentName.getPrefix(4),a))}else h.type==KeyLocatorType.KEY?(3<LOG&&console.log("Keylocator contains KEY"),d=decodeSubjectPublicKeyInfo(a.signedInfo.locator.publicKey),f=d.verifyByteArray(a.rawSignatureData,g,f),f=!0==f?Closure.UPCALL_CONTENT:Closure.UPCALL_CONTENT_BAD,c.upcall(Closure.UPCALL_CONTENT,
-new UpcallInfo(this,b.interest,0,a))):(a=h.certificate,console.log("KeyLocator contains CERT"),console.log(a))}}}}else console.log("Incoming packet is not Interest or ContentObject. Discard now.")};
-NDN.prototype.connectAndExecute=function(a){var b=this.getHostAndPort();if(null==b)console.log("ERROR: No more hosts from getHostAndPort"),this.host=null;else if(b.host==this.host&&b.port==this.port)console.log("ERROR: The host returned by getHostAndPort is not alive: "+this.host+":"+this.port);else{this.host=b.host;this.port=b.port;3<LOG&&console.log("Connect: trying host from getHostAndPort: "+this.host);b=new Interest(new Name("/"));b.interestLifetime=4E3;var c=this,d=setTimeout(function(){3<LOG&&
-console.log("Connect: timeout waiting for host "+c.host);c.connectAndExecute(a)},3E3);this.reconnectAndExpressInterest(b,new NDN.ConnectClosure(this,a,d))}};NDN.ConnectClosure=function(a,b,c){Closure.call(this);this.ndn=a;this.onConnected=b;this.timerID=c};NDN.ConnectClosure.prototype.upcall=function(a){if(!(a==Closure.UPCALL_CONTENT||a==Closure.UPCALL_CONTENT_UNVERIFIED))return Closure.RESULT_ERR;clearTimeout(this.timerID);this.ndn.readyStatus=NDN.OPENED;this.ndn.onopen();this.onConnected();return Closure.RESULT_OK};
-var BinaryXmlElementReader=function(a){this.elementListener=a;this.dataParts=[];this.structureDecoder=new BinaryXMLStructureDecoder};
-BinaryXmlElementReader.prototype.onReceivedData=function(a){for(;;)if(this.structureDecoder.seek(0),this.structureDecoder.findElementEnd(a)){this.dataParts.push(a.subarray(0,this.structureDecoder.offset));var b=DataUtils.concatArrays(this.dataParts);this.dataParts=[];try{this.elementListener.onReceivedElement(b)}catch(c){console.log("BinaryXmlElementReader: ignoring exception from onReceivedElement: "+c)}a=a.subarray(this.structureDecoder.offset,a.length);this.structureDecoder=new BinaryXMLStructureDecoder;
-if(0==a.length)break}else{this.dataParts.push(a);3<LOG&&console.log("Incomplete packet received. Length "+a.length+". Wait for more input.");break}};
+function bnpMillerRabin(a){var b=this.subtract(BigInteger.ONE),c=b.getLowestSetBit();if(0>=c)return!1;var d=b.shiftRight(c);a=a+1>>1;a>lowprimes.length&&(a=lowprimes.length);for(var e=nbi(),f=0;f<a;++f){e.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);var g=e.modPow(d,this);if(0!=g.compareTo(BigInteger.ONE)&&0!=g.compareTo(b)){for(var h=1;h++<c&&0!=g.compareTo(b);)if(g=g.modPowInt(2,this),0==g.compareTo(BigInteger.ONE))return!1;if(0!=g.compareTo(b))return!1}}return!0}
+BigInteger.prototype.chunkSize=bnpChunkSize;BigInteger.prototype.toRadix=bnpToRadix;BigInteger.prototype.fromRadix=bnpFromRadix;BigInteger.prototype.fromNumber=bnpFromNumber;BigInteger.prototype.bitwiseTo=bnpBitwiseTo;BigInteger.prototype.changeBit=bnpChangeBit;BigInteger.prototype.addTo=bnpAddTo;BigInteger.prototype.dMultiply=bnpDMultiply;BigInteger.prototype.dAddOffset=bnpDAddOffset;BigInteger.prototype.multiplyLowerTo=bnpMultiplyLowerTo;BigInteger.prototype.multiplyUpperTo=bnpMultiplyUpperTo;
+BigInteger.prototype.modInt=bnpModInt;BigInteger.prototype.millerRabin=bnpMillerRabin;BigInteger.prototype.clone=bnClone;BigInteger.prototype.intValue=bnIntValue;BigInteger.prototype.byteValue=bnByteValue;BigInteger.prototype.shortValue=bnShortValue;BigInteger.prototype.signum=bnSigNum;BigInteger.prototype.toByteArray=bnToByteArray;BigInteger.prototype.equals=bnEquals;BigInteger.prototype.min=bnMin;BigInteger.prototype.max=bnMax;BigInteger.prototype.and=bnAnd;BigInteger.prototype.or=bnOr;
+BigInteger.prototype.xor=bnXor;BigInteger.prototype.andNot=bnAndNot;BigInteger.prototype.not=bnNot;BigInteger.prototype.shiftLeft=bnShiftLeft;BigInteger.prototype.shiftRight=bnShiftRight;BigInteger.prototype.getLowestSetBit=bnGetLowestSetBit;BigInteger.prototype.bitCount=bnBitCount;BigInteger.prototype.testBit=bnTestBit;BigInteger.prototype.setBit=bnSetBit;BigInteger.prototype.clearBit=bnClearBit;BigInteger.prototype.flipBit=bnFlipBit;BigInteger.prototype.add=bnAdd;BigInteger.prototype.subtract=bnSubtract;
+BigInteger.prototype.multiply=bnMultiply;BigInteger.prototype.divide=bnDivide;BigInteger.prototype.remainder=bnRemainder;BigInteger.prototype.divideAndRemainder=bnDivideAndRemainder;BigInteger.prototype.modPow=bnModPow;BigInteger.prototype.modInverse=bnModInverse;BigInteger.prototype.pow=bnPow;BigInteger.prototype.gcd=bnGCD;BigInteger.prototype.isProbablePrime=bnIsProbablePrime;BigInteger.prototype.square=bnSquare;exports.BigInteger=BigInteger;module.exports=exports;
+var ASN1HEX=require("../contrib/securityLib/asn1hex-1.1.js").ASN1HEX,KJUR=require("../contrib/securityLib/crypto-1.0.js").KJUR,RSAKey=require("../contrib/securityLib/rsasign-1.2.js").RSAKey,b64tohex=require("../contrib/securityLib/base64.js").b64tohex,exports=ndn=ndn||{};
+exports.createHash=function(a){if("sha256"!=a)throw Error("createHash: unsupported algorithm.");a={};a.md=new KJUR.crypto.MessageDigest({alg:"sha256",prov:"cryptojs"});a.update=function(a){this.md.updateHex(a.toString("hex"))};a.digest=function(a){var c=this.md.digest();return"hex"==a?c:"base64"==a?(new Buffer(c,"hex")).toString("base64"):new Buffer(c,"hex")};return a};
+exports.createHmac=function(a,b){if("sha256"!==a)throw Error("createHmac: unsupported algorithm.");var c={};c.md=new KJUR.crypto.Mac({alg:"HmacSHA256",pass:{hex:b.toString("hex")}});c.update=function(a){this.md.updateHex(a.toString("hex"))};c.digest=function(a){var b=this.md.doFinal();return"hex"==a?b:"base64"==a?(new Buffer(b,"hex")).toString("base64"):new Buffer(b,"hex")};return c};
+exports.createSign=function(a){if("RSA-SHA256"!=a)throw Error("createSign: unsupported algorithm.");return{arr:[],update:function(a){this.arr.push(a)},sign:function(a){var c=new RSAKey;c.readPrivateKeyFromPEMString(a);a=new KJUR.crypto.Signature({alg:"SHA256withRSA",prov:"cryptojs/jsrsa"});a.initSign(c);for(c=0;c<this.arr.length;++c)a.updateHex(this.arr[c].toString("hex"));return new Buffer(a.sign(),"hex")}}};
+exports.createVerify=function(a){if("RSA-SHA256"!=a)throw Error("createSign: unsupported algorithm.");return{arr:[],update:function(a){this.arr.push(a)},verify:function(a,c){for(var d=a.split("\n"),e="",f=1;f<d.length-1;f++)e+=d[f];d=(new Buffer(e,"base64")).toString("hex");e=ASN1HEX.getPosArrayOfChildren_AtObj(d,0);2!=e.length?e=-1:(e=e[1],"03"!=d.substring(e,e+2)?e=-1:(e=ASN1HEX.getStartPosOfV_AtObj(d,e),e="00"!=d.substring(e,e+2)?-1:e+2));f=ASN1HEX.getPosArrayOfChildren_AtObj(d,e);2!=f.length?
+e=null:(e=ASN1HEX.getHexOfV_AtObj(d,f[0]),d=ASN1HEX.getHexOfV_AtObj(d,f[1]),f=new RSAKey,f.setPublic(e,d),e=f);d=new KJUR.crypto.Signature({alg:"SHA256withRSA",prov:"cryptojs/jsrsa"});d.initVerifyByPublicKey(e);for(e=0;e<this.arr.length;e++)d.updateHex(this.arr[e].toString("hex"));e=c.toString("hex");return d.verify(e)}}};exports.randomBytes=function(a){for(var b=new Buffer(a),c=0;c<a;++c)b[c]=Math.floor(256*Math.random());return b};
+exports.toByteArray=function(a){var b=[];b64tohex(a).replace(/(..)/g,function(a){b.push(parseInt(a,16))});return b};module.exports=exports;var lookup="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",base64js={};
+(function(a){function b(a){a=a.charCodeAt(0);if(a===d)return 62;if(a===e)return 63;if(a<f)return-1;if(a<f+10)return a-f+52;if(a<h+26)return a-h;if(a<g+26)return a-g+26}var c="undefined"!==typeof Uint8Array?Uint8Array:Array,d=43,e=47,f=48,g=97,h=65;a.toByteArray=function(a){function d(a){w[M++]=a}var e,f,g,h,w;if(0<a.length%4)throw Error("Invalid string. Length must be a multiple of 4");e=a.length;h="="===a.charAt(e-2)?2:"="===a.charAt(e-1)?1:0;w=new c(3*a.length/4-h);f=0<h?a.length-4:a.length;var M=
+0;for(e=0;e<f;e+=4)g=b(a.charAt(e))<<18|b(a.charAt(e+1))<<12|b(a.charAt(e+2))<<6|b(a.charAt(e+3)),d((g&16711680)>>16),d((g&65280)>>8),d(g&255);2===h?(g=b(a.charAt(e))<<2|b(a.charAt(e+1))>>4,d(g&255)):1===h&&(g=b(a.charAt(e))<<10|b(a.charAt(e+1))<<4|b(a.charAt(e+2))>>2,d(g>>8&255),d(g&255));return w};a.fromByteArray=function(a){var b,c=a.length%3,d="",e,f;b=0;for(f=a.length-c;b<f;b+=3)e=(a[b]<<16)+(a[b+1]<<8)+a[b+2],e=lookup.charAt(e>>18&63)+lookup.charAt(e>>12&63)+lookup.charAt(e>>6&63)+lookup.charAt(e&
+63),d+=e;switch(c){case 1:e=a[a.length-1];d+=lookup.charAt(e>>2);d+=lookup.charAt(e<<4&63);d+="==";break;case 2:e=(a[a.length-2]<<8)+a[a.length-1],d+=lookup.charAt(e>>10),d+=lookup.charAt(e>>4&63),d+=lookup.charAt(e<<2&63),d+="="}return d}})(base64js);
+var ieee={read:function(a,b,c,d,e){var f;f=8*e-d-1;var g=(1<<f)-1,h=g>>1,k=-7;e=c?e-1:0;var l=c?-1:1,m=a[b+e];e+=l;c=m&(1<<-k)-1;m>>=-k;for(k+=f;0<k;c=256*c+a[b+e],e+=l,k-=8);f=c&(1<<-k)-1;c>>=-k;for(k+=d;0<k;f=256*f+a[b+e],e+=l,k-=8);if(0===c)c=1-h;else{if(c===g)return f?NaN:Infinity*(m?-1:1);f+=Math.pow(2,d);c-=h}return(m?-1:1)*f*Math.pow(2,c-d)},write:function(a,b,c,d,e,f){var g,h=8*f-e-1,k=(1<<h)-1,l=k>>1,m=23===e?Math.pow(2,-24)-Math.pow(2,-77):0;f=d?0:f-1;var q=d?1:-1,p=0>b||0===b&&0>1/b?1:
+0;b=Math.abs(b);isNaN(b)||Infinity===b?(b=isNaN(b)?1:0,d=k):(d=Math.floor(Math.log(b)/Math.LN2),1>b*(g=Math.pow(2,-d))&&(d--,g*=2),b=1<=d+l?b+m/g:b+m*Math.pow(2,1-l),2<=b*g&&(d++,g/=2),d+l>=k?(b=0,d=k):1<=d+l?(b=(b*g-1)*Math.pow(2,e),d+=l):(b=b*Math.pow(2,l-1)*Math.pow(2,e),d=0));for(;8<=e;a[c+f]=b&255,f+=q,b/=256,e-=8);d=d<<e|b;for(h+=e;0<h;a[c+f]=d&255,f+=q,d/=256,h-=8);a[c+f-q]|=128*p}};exports.ieee=ieee;var base64=base64js,ieee754=require("./ieee754.js").ieee;exports.Buffer=Buffer;
+exports.SlowBuffer=Buffer;exports.INSPECT_MAX_BYTES=50;Buffer.poolSize=8192;Buffer._useTypedArrays=function(){try{var a=new ArrayBuffer(0),b=new Uint8Array(a);b.foo=function(){return 42};return 42===b.foo()&&"function"===typeof b.subarray}catch(c){return!1}}();
+function Buffer(a,b,c){if(!(this instanceof Buffer))return new Buffer(a,b,c);var d=typeof a;if("base64"===b&&"string"===d)for(a=Buffer.stringtrim(a);0!==a.length%4;)a+="=";var e;if("number"===d)e=Buffer.coerce(a);else if("string"===d)e=Buffer.byteLength(a,b);else if("object"===d)e=Buffer.coerce(a.length);else throw Error("First argument needs to be a number, array or string.");var f;Buffer._useTypedArrays?f=Buffer._augment(new Uint8Array(e)):(f=this,f.length=e,f._isBuffer=!0);if(Buffer._useTypedArrays&&
+"number"===typeof a.byteLength)f._set(a);else if(Buffer.isArrayish(a))if(Buffer.isBuffer(a))for(b=0;b<e;b++)f[b]=a.readUInt8(b);else for(b=0;b<e;b++)f[b]=(a[b]%256+256)%256;else if("string"===d)f.write(a,0,b);else if("number"===d&&!Buffer._useTypedArrays&&!c)for(b=0;b<e;b++)f[b]=0;return f}
+Buffer.isEncoding=function(a){switch(String(a).toLowerCase()){case "hex":case "utf8":case "utf-8":case "ascii":case "binary":case "base64":case "raw":case "ucs2":case "ucs-2":case "utf16le":case "utf-16le":return!0;default:return!1}};Buffer.isBuffer=function(a){return!(null===a||void 0===a||!a._isBuffer)};
+Buffer.byteLength=function(a,b){var c;a=a.toString();switch(b||"utf8"){case "hex":c=a.length/2;break;case "utf8":case "utf-8":c=Buffer.utf8ToBytes(a).length;break;case "ascii":case "binary":case "raw":c=a.length;break;case "base64":c=Buffer.base64ToBytes(a).length;break;case "ucs2":case "ucs-2":case "utf16le":case "utf-16le":c=2*a.length;break;default:throw Error("Unknown encoding");}return c};
+Buffer.concat=function(a,b){Buffer.assert(Buffer.isArray(a),"Usage: Buffer.concat(list[, length])");if(0===a.length)return new Buffer(0);if(1===a.length)return a[0];var c;if(void 0===b)for(c=b=0;c<a.length;c++)b+=a[c].length;var d=new Buffer(b),e=0;for(c=0;c<a.length;c++){var f=a[c];f.copy(d,e);e+=f.length}return d};
+Buffer.compare=function(a,b){Buffer.assert(Buffer.isBuffer(a)&&Buffer.isBuffer(b),"Arguments must be Buffers");for(var c=a.length,d=b.length,e=0,f=Math.min(c,d);e<f&&a[e]===b[e];e++);e!==f&&(c=a[e],d=b[e]);return c<d?-1:d<c?1:0};
+Buffer.hexWrite=function(a,b,c,d){c=Number(c)||0;var e=a.length-c;d?(d=Number(d),d>e&&(d=e)):d=e;e=b.length;Buffer.assert(0===e%2,"Invalid hex string");d>e/2&&(d=e/2);for(e=0;e<d;e++){var f=parseInt(b.substr(2*e,2),16);Buffer.assert(!isNaN(f),"Invalid hex string");a[c+e]=f}return e};Buffer.utf8Write=function(a,b,c,d){return Buffer.blitBuffer(Buffer.utf8ToBytes(b),a,c,d)};Buffer.asciiWrite=function(a,b,c,d){return Buffer.blitBuffer(Buffer.asciiToBytes(b),a,c,d)};
+Buffer.binaryWrite=function(a,b,c,d){return Buffer.asciiWrite(a,b,c,d)};Buffer.base64Write=function(a,b,c,d){return Buffer.blitBuffer(Buffer.base64ToBytes(b),a,c,d)};Buffer.utf16leWrite=function(a,b,c,d){return Buffer.blitBuffer(Buffer.utf16leToBytes(b),a,c,d)};
+Buffer.prototype.write=function(a,b,c,d){if(isFinite(b))isFinite(c)||(d=c,c=void 0);else{var e=d;d=b;b=c;c=e}b=Number(b)||0;e=this.length-b;c?(c=Number(c),c>e&&(c=e)):c=e;d=String(d||"utf8").toLowerCase();switch(d){case "hex":a=Buffer.hexWrite(this,a,b,c);break;case "utf8":case "utf-8":a=Buffer.utf8Write(this,a,b,c);break;case "ascii":a=Buffer.asciiWrite(this,a,b,c);break;case "binary":a=Buffer.binaryWrite(this,a,b,c);break;case "base64":a=Buffer.base64Write(this,a,b,c);break;case "ucs2":case "ucs-2":case "utf16le":case "utf-16le":a=
+Buffer.utf16leWrite(this,a,b,c);break;default:throw Error("Unknown encoding");}return a};
+Buffer.prototype.toString=function(a,b,c){a=String(a||"utf8").toLowerCase();b=Number(b)||0;c=void 0===c?this.length:Number(c);if(c===b)return"";switch(a){case "hex":a=Buffer.hexSlice(this,b,c);break;case "utf8":case "utf-8":a=Buffer.utf8Slice(this,b,c);break;case "ascii":a=Buffer.asciiSlice(this,b,c);break;case "binary":a=Buffer.binarySlice(this,b,c);break;case "base64":a=Buffer.base64Slice(this,b,c);break;case "ucs2":case "ucs-2":case "utf16le":case "utf-16le":a=utf16leSlice(this,b,c);break;default:throw Error("Unknown encoding");
+}return a};Buffer.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};Buffer.prototype.equals=function(a){Buffer.assert(Buffer.isBuffer(a),"Argument must be a Buffer");return 0===Buffer.compare(this,a)};Buffer.prototype.compare=function(a){Buffer.assert(Buffer.isBuffer(a),"Argument must be a Buffer");return Buffer.compare(this,a)};
+Buffer.prototype.copy=function(a,b,c,d){c||(c=0);d||0===d||(d=this.length);b||(b=0);if(d!==c&&0!==a.length&&0!==this.length)if(Buffer.assert(d>=c,"sourceEnd < sourceStart"),Buffer.assert(0<=b&&b<a.length,"targetStart out of bounds"),Buffer.assert(0<=c&&c<this.length,"sourceStart out of bounds"),Buffer.assert(0<=d&&d<=this.length,"sourceEnd out of bounds"),d>this.length&&(d=this.length),a.length-b<d-c&&(d=a.length-b+c),d-=c,100>d||!Buffer._useTypedArrays)for(var e=0;e<d;e++)a[e+b]=this[e+c];else a._set(this.subarray(c,
+c+d),b)};Buffer.base64Slice=function(a,b,c){return 0===b&&c===a.length?base64.fromByteArray(a):base64.fromByteArray(a.slice(b,c))};Buffer.utf8Slice=function(a,b,c){var d="",e="";for(c=Math.min(a.length,c);b<c;b++)127>=a[b]?(d+=Buffer.decodeUtf8Char(e)+String.fromCharCode(a[b]),e=""):e+="%"+a[b].toString(16);return d+Buffer.decodeUtf8Char(e)};Buffer.asciiSlice=function(a,b,c){var d="";for(c=Math.min(a.length,c);b<c;b++)d+=String.fromCharCode(a[b]);return d};
+Buffer.binarySlice=function(a,b,c){return Buffer.asciiSlice(a,b,c)};Buffer.hexSlice=function(a,b,c){var d=a.length;if(!b||0>b)b=0;if(!c||0>c||c>d)c=d;for(d="";b<c;b++)d+=Buffer.toHex(a[b]);return d};function utf16leSlice(a,b,c){a=a.slice(b,c);b="";for(c=0;c<a.length;c+=2)b+=String.fromCharCode(a[c]+256*a[c+1]);return b}
+Buffer.prototype.slice=function(a,b){var c=this.length;a=Buffer.clamp(a,c,0);b=Buffer.clamp(b,c,c);if(Buffer._useTypedArrays)return Buffer._augment(this.subarray(a,b));for(var c=b-a,d=new Buffer(c,void 0,!0),e=0;e<c;e++)d[e]=this[e+a];return d};Buffer.prototype.get=function(a){console.log(".get() is deprecated. Access using array indexes instead.");return this.readUInt8(a)};
+Buffer.prototype.set=function(a,b){console.log(".set() is deprecated. Access using array indexes instead.");return this.writeUInt8(a,b)};Buffer.prototype.readUInt8=function(a,b){b||(Buffer.assert(void 0!==a&&null!==a,"missing offset"),Buffer.assert(a<this.length,"Trying to read beyond buffer length"));if(!(a>=this.length))return this[a]};
+Buffer.readUInt16=function(a,b,c,d){d||(Buffer.assert("boolean"===typeof c,"missing or invalid endian"),Buffer.assert(void 0!==b&&null!==b,"missing offset"),Buffer.assert(b+1<a.length,"Trying to read beyond buffer length"));d=a.length;if(!(b>=d))return c?(c=a[b],b+1<d&&(c|=a[b+1]<<8)):(c=a[b]<<8,b+1<d&&(c|=a[b+1])),c};Buffer.prototype.readUInt16LE=function(a,b){return Buffer.readUInt16(this,a,!0,b)};Buffer.prototype.readUInt16BE=function(a,b){return Buffer.readUInt16(this,a,!1,b)};
+Buffer.readUInt32=function(a,b,c,d){d||(Buffer.assert("boolean"===typeof c,"missing or invalid endian"),Buffer.assert(void 0!==b&&null!==b,"missing offset"),Buffer.assert(b+3<a.length,"Trying to read beyond buffer length"));d=a.length;if(!(b>=d)){var e;c?(b+2<d&&(e=a[b+2]<<16),b+1<d&&(e|=a[b+1]<<8),e|=a[b],b+3<d&&(e+=a[b+3]<<24>>>0)):(b+1<d&&(e=a[b+1]<<16),b+2<d&&(e|=a[b+2]<<8),b+3<d&&(e|=a[b+3]),e+=a[b]<<24>>>0);return e}};
+Buffer.prototype.readUInt32LE=function(a,b){return Buffer.readUInt32(this,a,!0,b)};Buffer.prototype.readUInt32BE=function(a,b){return Buffer.readUInt32(this,a,!1,b)};Buffer.prototype.readInt8=function(a,b){b||(Buffer.assert(void 0!==a&&null!==a,"missing offset"),Buffer.assert(a<this.length,"Trying to read beyond buffer length"));if(!(a>=this.length))return this[a]&128?-1*(255-this[a]+1):this[a]};
+Buffer.readInt16=function(a,b,c,d){d||(Buffer.assert("boolean"===typeof c,"missing or invalid endian"),Buffer.assert(void 0!==b&&null!==b,"missing offset"),Buffer.assert(b+1<a.length,"Trying to read beyond buffer length"));if(!(b>=a.length))return a=Buffer.readUInt16(a,b,c,!0),a&32768?-1*(65535-a+1):a};Buffer.prototype.readInt16LE=function(a,b){return Buffer.readInt16(this,a,!0,b)};Buffer.prototype.readInt16BE=function(a,b){return Buffer.readInt16(this,a,!1,b)};
+Buffer.readInt32=function(a,b,c,d){d||(Buffer.assert("boolean"===typeof c,"missing or invalid endian"),Buffer.assert(void 0!==b&&null!==b,"missing offset"),Buffer.assert(b+3<a.length,"Trying to read beyond buffer length"));if(!(b>=a.length))return a=Buffer.readUInt32(a,b,c,!0),a&2147483648?-1*(4294967295-a+1):a};Buffer.prototype.readInt32LE=function(a,b){return Buffer.readInt32(this,a,!0,b)};Buffer.prototype.readInt32BE=function(a,b){return Buffer.readInt32(this,a,!1,b)};
+Buffer.readFloat=function(a,b,c,d){d||(Buffer.assert("boolean"===typeof c,"missing or invalid endian"),Buffer.assert(b+3<a.length,"Trying to read beyond buffer length"));return ieee754.read(a,b,c,23,4)};Buffer.prototype.readFloatLE=function(a,b){return Buffer.readFloat(this,a,!0,b)};Buffer.prototype.readFloatBE=function(a,b){return Buffer.readFloat(this,a,!1,b)};
+Buffer.readDouble=function(a,b,c,d){d||(Buffer.assert("boolean"===typeof c,"missing or invalid endian"),Buffer.assert(b+7<a.length,"Trying to read beyond buffer length"));return ieee754.read(a,b,c,52,8)};Buffer.prototype.readDoubleLE=function(a,b){return Buffer.readDouble(this,a,!0,b)};Buffer.prototype.readDoubleBE=function(a,b){return Buffer.readDouble(this,a,!1,b)};
+Buffer.prototype.writeUInt8=function(a,b,c){c||(Buffer.assert(void 0!==a&&null!==a,"missing value"),Buffer.assert(void 0!==b&&null!==b,"missing offset"),Buffer.assert(b<this.length,"trying to write beyond buffer length"),Buffer.verifuint(a,255));if(!(b>=this.length))return this[b]=a,b+1};
+Buffer.writeUInt16=function(a,b,c,d,e){e||(Buffer.assert(void 0!==b&&null!==b,"missing value"),Buffer.assert("boolean"===typeof d,"missing or invalid endian"),Buffer.assert(void 0!==c&&null!==c,"missing offset"),Buffer.assert(c+1<a.length,"trying to write beyond buffer length"),Buffer.verifuint(b,65535));var f=a.length;if(!(c>=f)){e=0;for(f=Math.min(f-c,2);e<f;e++)a[c+e]=(b&255<<8*(d?e:1-e))>>>8*(d?e:1-e);return c+2}};
+Buffer.prototype.writeUInt16LE=function(a,b,c){return Buffer.writeUInt16(this,a,b,!0,c)};Buffer.prototype.writeUInt16BE=function(a,b,c){return Buffer.writeUInt16(this,a,b,!1,c)};
+Buffer.writeUInt32=function(a,b,c,d,e){e||(Buffer.assert(void 0!==b&&null!==b,"missing value"),Buffer.assert("boolean"===typeof d,"missing or invalid endian"),Buffer.assert(void 0!==c&&null!==c,"missing offset"),Buffer.assert(c+3<a.length,"trying to write beyond buffer length"),Buffer.verifuint(b,4294967295));var f=a.length;if(!(c>=f)){e=0;for(f=Math.min(f-c,4);e<f;e++)a[c+e]=b>>>8*(d?e:3-e)&255;return c+4}};Buffer.prototype.writeUInt32LE=function(a,b,c){return Buffer.writeUInt32(this,a,b,!0,c)};
+Buffer.prototype.writeUInt32BE=function(a,b,c){return Buffer.writeUInt32(this,a,b,!1,c)};Buffer.prototype.writeInt8=function(a,b,c){c||(Buffer.assert(void 0!==a&&null!==a,"missing value"),Buffer.assert(void 0!==b&&null!==b,"missing offset"),Buffer.assert(b<this.length,"Trying to write beyond buffer length"),Buffer.verifsint(a,127,-128));if(!(b>=this.length))return 0<=a?this.writeUInt8(a,b,c):this.writeUInt8(255+a+1,b,c),b+1};
+Buffer.writeInt16=function(a,b,c,d,e){e||(Buffer.assert(void 0!==b&&null!==b,"missing value"),Buffer.assert("boolean"===typeof d,"missing or invalid endian"),Buffer.assert(void 0!==c&&null!==c,"missing offset"),Buffer.assert(c+1<a.length,"Trying to write beyond buffer length"),Buffer.verifsint(b,32767,-32768));if(!(c>=a.length))return 0<=b?Buffer.writeUInt16(a,b,c,d,e):Buffer.writeUInt16(a,65535+b+1,c,d,e),c+2};Buffer.prototype.writeInt16LE=function(a,b,c){return Buffer.writeInt16(this,a,b,!0,c)};
+Buffer.prototype.writeInt16BE=function(a,b,c){return Buffer.writeInt16(this,a,b,!1,c)};
+Buffer.writeInt32=function(a,b,c,d,e){e||(Buffer.assert(void 0!==b&&null!==b,"missing value"),Buffer.assert("boolean"===typeof d,"missing or invalid endian"),Buffer.assert(void 0!==c&&null!==c,"missing offset"),Buffer.assert(c+3<a.length,"Trying to write beyond buffer length"),Buffer.verifsint(b,2147483647,-2147483648));if(!(c>=a.length))return 0<=b?Buffer.writeUInt32(a,b,c,d,e):Buffer.writeUInt32(a,4294967295+b+1,c,d,e),c+4};
+Buffer.prototype.writeInt32LE=function(a,b,c){return Buffer.writeInt32(this,a,b,!0,c)};Buffer.prototype.writeInt32BE=function(a,b,c){return Buffer.writeInt32(this,a,b,!1,c)};
+Buffer.writeFloat=function(a,b,c,d,e){e||(Buffer.assert(void 0!==b&&null!==b,"missing value"),Buffer.assert("boolean"===typeof d,"missing or invalid endian"),Buffer.assert(void 0!==c&&null!==c,"missing offset"),Buffer.assert(c+3<a.length,"Trying to write beyond buffer length"),Buffer.verifIEEE754(b,3.4028234663852886E38,-3.4028234663852886E38));if(!(c>=a.length))return ieee754.write(a,b,c,d,23,4),c+4};Buffer.prototype.writeFloatLE=function(a,b,c){return Buffer.writeFloat(this,a,b,!0,c)};
+Buffer.prototype.writeFloatBE=function(a,b,c){return Buffer.writeFloat(this,a,b,!1,c)};Buffer.writeDouble=function(a,b,c,d,e){e||(Buffer.assert(void 0!==b&&null!==b,"missing value"),Buffer.assert("boolean"===typeof d,"missing or invalid endian"),Buffer.assert(void 0!==c&&null!==c,"missing offset"),Buffer.assert(c+7<a.length,"Trying to write beyond buffer length"),Buffer.verifIEEE754(b,1.7976931348623157E308,-1.7976931348623157E308));if(!(c>=a.length))return ieee754.write(a,b,c,d,52,8),c+8};
+Buffer.prototype.writeDoubleLE=function(a,b,c){return Buffer.writeDouble(this,a,b,!0,c)};Buffer.prototype.writeDoubleBE=function(a,b,c){return Buffer.writeDouble(this,a,b,!1,c)};
+Buffer.prototype.fill=function(a,b,c){a||(a=0);b||(b=0);c||(c=this.length);Buffer.assert(c>=b,"end < start");if(c!==b&&0!==this.length){Buffer.assert(0<=b&&b<this.length,"start out of bounds");Buffer.assert(0<=c&&c<=this.length,"end out of bounds");if("number"===typeof a)for(;b<c;b++)this[b]=a;else{a=Buffer.utf8ToBytes(a.toString());for(var d=a.length;b<c;b++)this[b]=a[b%d]}return this}};
+Buffer.prototype.inspect=function(){for(var a=[],b=this.length,c=0;c<b;c++)if(a[c]=Buffer.toHex(this[c]),c===exports.INSPECT_MAX_BYTES){a[c+1]="...";break}return"<Buffer "+a.join(" ")+">"};Buffer.prototype.toArrayBuffer=function(){if("undefined"!==typeof Uint8Array){if(Buffer._useTypedArrays)return(new Buffer(this)).buffer;for(var a=new Uint8Array(this.length),b=0,c=a.length;b<c;b+=1)a[b]=this[b];return a.buffer}throw Error("Buffer.toArrayBuffer not supported in this browser");};var BP=Buffer.prototype;
+Buffer._augment=function(a){a._isBuffer=!0;a._get=a.get;a._set=a.set;a.get=BP.get;a.set=BP.set;a.write=BP.write;a.toString=BP.toString;a.toLocaleString=BP.toString;a.toJSON=BP.toJSON;a.equals=BP.equals;a.compare=BP.compare;a.copy=BP.copy;a.slice=BP.slice;a.readUInt8=BP.readUInt8;a.readUInt16LE=BP.readUInt16LE;a.readUInt16BE=BP.readUInt16BE;a.readUInt32LE=BP.readUInt32LE;a.readUInt32BE=BP.readUInt32BE;a.readInt8=BP.readInt8;a.readInt16LE=BP.readInt16LE;a.readInt16BE=BP.readInt16BE;a.readInt32LE=BP.readInt32LE;
+a.readInt32BE=BP.readInt32BE;a.readFloatLE=BP.readFloatLE;a.readFloatBE=BP.readFloatBE;a.readDoubleLE=BP.readDoubleLE;a.readDoubleBE=BP.readDoubleBE;a.writeUInt8=BP.writeUInt8;a.writeUInt16LE=BP.writeUInt16LE;a.writeUInt16BE=BP.writeUInt16BE;a.writeUInt32LE=BP.writeUInt32LE;a.writeUInt32BE=BP.writeUInt32BE;a.writeInt8=BP.writeInt8;a.writeInt16LE=BP.writeInt16LE;a.writeInt16BE=BP.writeInt16BE;a.writeInt32LE=BP.writeInt32LE;a.writeInt32BE=BP.writeInt32BE;a.writeFloatLE=BP.writeFloatLE;a.writeFloatBE=
+BP.writeFloatBE;a.writeDoubleLE=BP.writeDoubleLE;a.writeDoubleBE=BP.writeDoubleBE;a.fill=BP.fill;a.inspect=BP.inspect;a.toArrayBuffer=BP.toArrayBuffer;return a};Buffer.stringtrim=function(a){return a.trim?a.trim():a.replace(/^\s+|\s+$/g,"")};Buffer.clamp=function(a,b,c){if("number"!==typeof a)return c;a=~~a;if(a>=b)return b;if(0<=a)return a;a+=b;return 0<=a?a:0};Buffer.coerce=function(a){a=~~Math.ceil(+a);return 0>a?0:a};
+Buffer.isArray=function(a){return(Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)})(a)};Buffer.isArrayish=function(a){return Buffer.isArray(a)||Buffer.isBuffer(a)||a&&"object"===typeof a&&"number"===typeof a.length};Buffer.toHex=function(a){return 16>a?"0"+a.toString(16):a.toString(16)};
+Buffer.utf8ToBytes=function(a){for(var b=[],c=0;c<a.length;c++){var d=a.charCodeAt(c);if(127>=d)b.push(d);else{var e=c;55296<=d&&57343>=d&&c++;d=encodeURIComponent(a.slice(e,c+1)).substr(1).split("%");for(e=0;e<d.length;e++)b.push(parseInt(d[e],16))}}return b};Buffer.asciiToBytes=function(a){for(var b=[],c=0;c<a.length;c++)b.push(a.charCodeAt(c)&255);return b};Buffer.utf16leToBytes=function(a){for(var b,c,d=[],e=0;e<a.length;e++)b=a.charCodeAt(e),c=b>>8,b%=256,d.push(b),d.push(c);return d};
+Buffer.base64ToBytes=function(a){return base64.toByteArray(a)};Buffer.blitBuffer=function(a,b,c,d){for(var e=0;e<d&&!(e+c>=b.length||e>=a.length);e++)b[e+c]=a[e];return e};Buffer.decodeUtf8Char=function(a){try{return decodeURIComponent(a)}catch(b){return String.fromCharCode(65533)}};
+Buffer.verifuint=function(a,b){Buffer.assert("number"===typeof a,"cannot write a non-number as a number");Buffer.assert(0<=a,"specified a negative value for writing an unsigned value");Buffer.assert(a<=b,"value is larger than maximum value for type");Buffer.assert(Math.floor(a)===a,"value has a fractional component")};
+Buffer.verifsint=function(a,b,c){Buffer.assert("number"===typeof a,"cannot write a non-number as a number");Buffer.assert(a<=b,"value larger than maximum allowed value");Buffer.assert(a>=c,"value smaller than minimum allowed value");Buffer.assert(Math.floor(a)===a,"value has a fractional component")};Buffer.verifIEEE754=function(a,b,c){Buffer.assert("number"===typeof a,"cannot write a non-number as a number");Buffer.assert(a<=b,"value larger than maximum allowed value");Buffer.assert(a>=c,"value smaller than minimum allowed value")};
+Buffer.assert=function(a,b){if(!a)throw Error(b||"Failed assertion");};var Log=function(){};exports.Log=Log;Log.LOG=0;var printStackTrace=require("../../contrib/stacktrace/stacktrace.js").printStackTrace,NdnCommon={};exports.NdnCommon=NdnCommon;NdnCommon.MAX_NDN_PACKET_SIZE=8800;NdnCommon.getErrorWithStackTrace=function(a){return a+"\n"+printStackTrace({e:a}).join("\n")};
+NdnCommon.checkIndexedDb=function(a){try{var b=new Dexie("test-Dexie-support");b.version(1).stores({});b.open();setTimeout(function(){try{a(b.isOpen())}catch(c){a(!1)}},200)}catch(c){a(!1)}};var NdnCommon=require("./ndn-common.js").NdnCommon,ExponentialReExpress=function(a,b,c,d){d=d||{};this.face=a;this.callerOnData=b;this.callerOnTimeout=c;this.maxInterestLifetime=d.maxInterestLifetime||16E3};exports.ExponentialReExpress=ExponentialReExpress;
+ExponentialReExpress.makeOnTimeout=function(a,b,c,d){var e=new ExponentialReExpress(a,b,c,d);return function(a){e.onTimeout(a)}};
+ExponentialReExpress.prototype.onTimeout=function(a){var b=a.getInterestLifetimeMilliseconds();if(null==b){if(this.callerOnTimeout)try{this.callerOnTimeout(a)}catch(c){console.log("Error in onTimeout: "+NdnCommon.getErrorWithStackTrace(c))}}else if(b*=2,b>this.maxInterestLifetime){if(this.callerOnTimeout)try{this.callerOnTimeout(a)}catch(d){console.log("Error in onTimeout: "+NdnCommon.getErrorWithStackTrace(d))}}else{a=a.clone();a.setInterestLifetimeMilliseconds(b);var e=this;this.face.expressInterest(a,
+this.callerOnData,function(a){e.onTimeout(a)})}};var Blob=function Blob(b,c){null==c&&(c=!0);null==b?this.buffer=null:"object"===typeof b&&b instanceof Blob?this.buffer=b.buffer:"string"===typeof b?this.buffer=new Buffer(b,"utf8"):c?this.buffer=new Buffer(b):Buffer.isBuffer(b)?this.buffer=b:this.buffer=new Buffer(b);this.length=null!=this.buffer?this.buffer.length:0};exports.Blob=Blob;Blob.prototype.size=function(){return null!=this.buffer?this.buffer.length:0};Blob.prototype.buf=function(){return this.buffer};
+Blob.prototype.isNull=function(){return null==this.buffer};Blob.prototype.toHex=function(){return null==this.buffer?"":this.buffer.toString("hex")};Blob.prototype.toString=function(){return null==this.buffer?"":this.buffer.toString("utf8")};Blob.prototype.equals=function(a){if(this.isNull())return a.isNull();if(a.isNull()||this.buffer.length!=a.buffer.length)return!1;for(var b=0;b<this.buffer.length;++b)if(this.buffer[b]!=a.buffer[b])return!1;return!0};
+var Blob=require("./blob.js").Blob,SignedBlob=function SignedBlob(b,c,d){Blob.call(this,b);null==this.buffer?this.signedPortionEndOffset=this.signedPortionBeginOffset=0:"object"===typeof b&&b instanceof SignedBlob?(this.signedPortionBeginOffset=null==c?b.signedPortionBeginOffset:c,this.signedPortionEndOffset=null==d?b.signedPortionEndOffset:d):(this.signedPortionBeginOffset=c||0,this.signedPortionEndOffset=d||0);this.signedBuffer=null==this.buffer?null:this.buffer.slice(this.signedPortionBeginOffset,
+this.signedPortionEndOffset)};SignedBlob.prototype=new Blob;SignedBlob.prototype.name="SignedBlob";exports.SignedBlob=SignedBlob;SignedBlob.prototype.signedSize=function(){return null!=this.signedBuffer?this.signedBuffer.length:0};SignedBlob.prototype.signedBuf=function(){return this.signedBuffer};SignedBlob.prototype.getSignedPortionBeginOffset=function(){return this.signedPortionBeginOffset};SignedBlob.prototype.getSignedPortionEndOffset=function(){return this.signedPortionEndOffset};
+var DynamicBuffer=function(a){a||(a=16);this.array=new Buffer(a)};exports.DynamicBuffer=DynamicBuffer;DynamicBuffer.prototype.ensureLength=function(a){if(!(this.array.length>=a)){var b=2*this.array.length;a>b&&(b=a);a=new Buffer(b);this.array.copy(a);this.array=a}};DynamicBuffer.prototype.copy=function(a,b){this.ensureLength(a.length+b);Buffer.isBuffer(a)?a.copy(this.array,b):(new Buffer(a)).copy(this.array,b);return b+a.length};
+DynamicBuffer.prototype.ensureLengthFromBack=function(a){if(!(this.array.length>=a)){var b=2*this.array.length;a>b&&(b=a);a=new Buffer(b);this.array.copy(a,a.length-this.array.length);this.array=a}};DynamicBuffer.prototype.copyFromBack=function(a,b){this.ensureLengthFromBack(b);Buffer.isBuffer(a)?a.copy(this.array,this.array.length-b):(new Buffer(a)).copy(this.array,this.array.length-b)};DynamicBuffer.prototype.slice=function(a,b){return void 0==b?this.array.slice(a):this.array.slice(a,b)};
+var ChangeCounter=function(a){this.target=a;this.changeCount=null==a?0:a.getChangeCount()};exports.ChangeCounter=ChangeCounter;ChangeCounter.prototype.get=function(){return this.target};ChangeCounter.prototype.set=function(a){this.target=a;this.changeCount=null==a?0:a.getChangeCount()};ChangeCounter.prototype.checkChanged=function(){if(null==this.target)return!1;var a=this.target.getChangeCount();return this.changeCount!=a?(this.changeCount=a,!0):!1};
+var NdnCommon=require("./ndn-common.js").NdnCommon,SyncPromise=function(a,b){this.value=a;this.isRejected=b};exports.SyncPromise=SyncPromise;SyncPromise.prototype.then=function(a,b){if(this.isRejected)if(b)try{return b(this.value)}catch(c){return new SyncPromise(c,!0)}else return this;else if(a)try{return a(this.value)}catch(d){return new SyncPromise(d,!0)}else return this};SyncPromise.prototype["catch"]=function(a){return this.then(void 0,a)};
+SyncPromise.resolve=function(a){return new SyncPromise(a,!1)};SyncPromise.reject=function(a){return new SyncPromise(a,!0)};SyncPromise.getValue=function(a){if(a instanceof SyncPromise){if(a.isRejected)throw a.value;return a.value}throw Error("Cannot return immediately because promise is not a SyncPromise");};
+SyncPromise.complete=function(a,b,c){var d;c?d=b:(c=b,d=null);if(a)c.then(function(b){try{a(b)}catch(c){console.log("Error in onComplete: "+NdnCommon.getErrorWithStackTrace(c))}},function(a){if(d)try{d(a)}catch(b){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(b))}else{if(c instanceof SyncPromise)throw a;console.log("Uncaught exception from a Promise: "+NdnCommon.getErrorWithStackTrace(a))}});else return SyncPromise.getValue(c)};var DataUtils={};exports.DataUtils=DataUtils;
+DataUtils.keyStr="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";DataUtils.stringtoBase64=function(a){var b="",c,d,e="",f,g,h="",k=0;do c=a.charCodeAt(k++),d=a.charCodeAt(k++),e=a.charCodeAt(k++),f=c>>2,c=(c&3)<<4|d>>4,g=(d&15)<<2|e>>6,h=e&63,isNaN(d)?g=h=64:isNaN(e)&&(h=64),b=b+DataUtils.keyStr.charAt(f)+DataUtils.keyStr.charAt(c)+DataUtils.keyStr.charAt(g)+DataUtils.keyStr.charAt(h);while(k<a.length);return b};
+DataUtils.base64toString=function(a){var b="",c,d,e="",f,g="",h=0;/[^A-Za-z0-9\+\/\=]/g.exec(a)&&alert("There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\nExpect errors in decoding.");a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");do c=DataUtils.keyStr.indexOf(a.charAt(h++)),d=DataUtils.keyStr.indexOf(a.charAt(h++)),f=DataUtils.keyStr.indexOf(a.charAt(h++)),g=DataUtils.keyStr.indexOf(a.charAt(h++)),c=c<<2|d>>4,d=(d&15)<<4|f>>2,e=(f&3)<<6|g,
+b+=String.fromCharCode(c),64!=f&&(b+=String.fromCharCode(d)),64!=g&&(b+=String.fromCharCode(e));while(h<a.length);return b};DataUtils.toHex=function(a){return a.toString("hex")};DataUtils.stringToHex=function(a){for(var b="",c=0;c<a.length;++c)var d=a.charCodeAt(c),b=b+((16>d?"0":"")+d.toString(16));return b};DataUtils.toString=function(a){return a.toString("binary")};DataUtils.toNumbers=function(a){return new Buffer(a,"hex")};
+DataUtils.hexToRawString=function(a){if("string"==typeof a){var b="";a.replace(/(..)/g,function(a){b+=String.fromCharCode(parseInt(a,16))});return b}};DataUtils.toNumbersFromString=function(a){return new Buffer(a,"binary")};DataUtils.toNumbersIfString=function(a){return"string"===typeof a?new Buffer(a,"binary"):a};DataUtils.stringToUtf8Array=function(a){return new Buffer(a,"utf8")};DataUtils.concatArrays=function(a){return Buffer.concat(a)};
+DataUtils.decodeUtf8=function(a){for(var b="",c=0,d=0,e=0;c<a.length;)if(d=a.charCodeAt(c),128>d)b+=String.fromCharCode(d),c++;else if(191<d&&224>d)e=a.charCodeAt(c+1),b+=String.fromCharCode((d&31)<<6|e&63),c+=2;else var e=a.charCodeAt(c+1),f=a.charCodeAt(c+2),b=b+String.fromCharCode((d&15)<<12|(e&63)<<6|f&63),c=c+3;return b};
+DataUtils.arraysEqual=function(a,b){if(!a.slice)throw Error("DataUtils.arraysEqual: a1 is not an array");if(!b.slice)throw Error("DataUtils.arraysEqual: a2 is not an array");if(a.length!=b.length)return!1;for(var c=0;c<a.length;++c)if(a[c]!=b[c])return!1;return!0};DataUtils.bigEndianToUnsignedInt=function(a){for(var b=0,c=0;c<a.length;++c)b*=256,b+=a[c];return b};
+DataUtils.nonNegativeIntToBigEndian=function(a){a=Math.round(a);if(0>=a)return new Buffer(0);for(var b=new Buffer(8),c=0;0!=a;)++c,b[8-c]=a&255,a=Math.floor(a/256);return b.slice(8-c,8)};DataUtils.shuffle=function(a){for(var b=a.length-1;1<=b;--b){var c=Math.floor(Math.random()*(b+1)),d=a[b];a[b]=a[c];a[c]=d}};DataUtils.privateKeyPemToDer=function(a){a=a.split("\n");for(var b="",c=1;c<a.length-1;c++)b+=a[c];return new Buffer(b,"base64")};
+function DecodingException(a){if(a)return a.__proto__=DecodingException.prototype,a}DecodingException.prototype=Error();DecodingException.prototype.name="DecodingException";exports.DecodingException=DecodingException;var Tlv=function(){};exports.Tlv=Tlv;Tlv.Interest=5;Tlv.Data=6;Tlv.Name=7;Tlv.ImplicitSha256DigestComponent=1;Tlv.NameComponent=8;Tlv.Selectors=9;Tlv.Nonce=10;Tlv.InterestLifetime=12;Tlv.MinSuffixComponents=13;Tlv.MaxSuffixComponents=14;Tlv.PublisherPublicKeyLocator=15;Tlv.Exclude=16;
+Tlv.ChildSelector=17;Tlv.MustBeFresh=18;Tlv.Any=19;Tlv.MetaInfo=20;Tlv.Content=21;Tlv.SignatureInfo=22;Tlv.SignatureValue=23;Tlv.ContentType=24;Tlv.FreshnessPeriod=25;Tlv.FinalBlockId=26;Tlv.SignatureType=27;Tlv.KeyLocator=28;Tlv.KeyLocatorDigest=29;Tlv.SelectedDelegation=32;Tlv.FaceInstance=128;Tlv.ForwardingEntry=129;Tlv.StatusResponse=130;Tlv.Action=131;Tlv.FaceID=132;Tlv.IPProto=133;Tlv.Host=134;Tlv.Port=135;Tlv.MulticastInterface=136;Tlv.MulticastTTL=137;Tlv.ForwardingFlags=138;
+Tlv.StatusCode=139;Tlv.StatusText=140;Tlv.SignatureType_DigestSha256=0;Tlv.SignatureType_SignatureSha256WithRsa=1;Tlv.SignatureType_SignatureSha256WithEcdsa=3;Tlv.SignatureType_SignatureHmacWithSha256=4;Tlv.ContentType_Default=0;Tlv.ContentType_Link=1;Tlv.ContentType_Key=2;Tlv.NfdCommand_ControlResponse=101;Tlv.NfdCommand_StatusCode=102;Tlv.NfdCommand_StatusText=103;Tlv.ControlParameters_ControlParameters=104;Tlv.ControlParameters_FaceId=105;Tlv.ControlParameters_Uri=114;
+Tlv.ControlParameters_LocalControlFeature=110;Tlv.ControlParameters_Origin=111;Tlv.ControlParameters_Cost=106;Tlv.ControlParameters_Flags=108;Tlv.ControlParameters_Strategy=107;Tlv.ControlParameters_ExpirationPeriod=109;Tlv.LpPacket_LpPacket=100;Tlv.LpPacket_Fragment=80;Tlv.LpPacket_Sequence=81;Tlv.LpPacket_FragIndex=82;Tlv.LpPacket_FragCount=83;Tlv.LpPacket_Nack=800;Tlv.LpPacket_NackReason=801;Tlv.LpPacket_NextHopFaceId=816;Tlv.LpPacket_IncomingFaceId=817;Tlv.LpPacket_CachePolicy=820;
+Tlv.LpPacket_CachePolicyType=821;Tlv.LpPacket_IGNORE_MIN=800;Tlv.LpPacket_IGNORE_MAX=959;Tlv.Link_Preference=30;Tlv.Link_Delegation=31;Tlv.Encrypt_EncryptedContent=130;Tlv.Encrypt_EncryptionAlgorithm=131;Tlv.Encrypt_EncryptedPayload=132;Tlv.Encrypt_InitialVector=133;Tlv.Encrypt_StartDate=134;Tlv.Encrypt_EndDate=135;Tlv.Encrypt_IntervalStartHour=136;Tlv.Encrypt_IntervalEndHour=137;Tlv.Encrypt_NRepeats=138;Tlv.Encrypt_RepeatUnit=139;Tlv.Encrypt_RepetitiveInterval=140;Tlv.Encrypt_WhiteIntervalList=141;
+Tlv.Encrypt_BlackIntervalList=142;Tlv.Encrypt_Schedule=143;Tlv.getHighBytes=function(a){return(a-a%4294967296)/4294967296};var DynamicBuffer=require("../../util/dynamic-buffer.js").DynamicBuffer,Tlv=require("./tlv.js").Tlv,TlvEncoder=function(a){this.output=new DynamicBuffer(a||16);this.length=0};exports.TlvEncoder=TlvEncoder;TlvEncoder.prototype.getLength=function(){return this.length};
+TlvEncoder.prototype.writeVarNumber=function(a){if(253>a)this.length+=1,this.output.ensureLengthFromBack(this.length),this.output.array[this.output.array.length-this.length]=a&255;else if(65535>=a){this.length+=3;this.output.ensureLengthFromBack(this.length);var b=this.output.array.length-this.length;this.output.array[b]=253;this.output.array[b+1]=a>>8&255;this.output.array[b+2]=a&255}else if(4294967295>=a)this.length+=5,this.output.ensureLengthFromBack(this.length),b=this.output.array.length-this.length,
+this.output.array[b]=254,this.output.array[b+1]=a>>24&255,this.output.array[b+2]=a>>16&255,this.output.array[b+3]=a>>8&255,this.output.array[b+4]=a&255;else{this.length+=9;this.output.ensureLengthFromBack(this.length);b=this.output.array.length-this.length;this.output.array[b]=255;var c=Tlv.getHighBytes(a);this.output.array[b+1]=c>>24&255;this.output.array[b+2]=c>>16&255;this.output.array[b+3]=c>>8&255;this.output.array[b+4]=c&255;this.output.array[b+5]=a>>24&255;this.output.array[b+6]=a>>16&255;
+this.output.array[b+7]=a>>8&255;this.output.array[b+8]=a&255}};TlvEncoder.prototype.writeTypeAndLength=function(a,b){this.writeVarNumber(b);this.writeVarNumber(a)};
+TlvEncoder.prototype.writeNonNegativeInteger=function(a){if(0>a)throw Error("TLV integer value may not be negative");a=Math.round(a);if(255>=a)this.length+=1,this.output.ensureLengthFromBack(this.length),this.output.array[this.output.array.length-this.length]=a&255;else if(65535>=a){this.length+=2;this.output.ensureLengthFromBack(this.length);var b=this.output.array.length-this.length;this.output.array[b]=a>>8&255;this.output.array[b+1]=a&255}else if(4294967295>=a)this.length+=4,this.output.ensureLengthFromBack(this.length),
+b=this.output.array.length-this.length,this.output.array[b]=a>>24&255,this.output.array[b+1]=a>>16&255,this.output.array[b+2]=a>>8&255,this.output.array[b+3]=a&255;else{this.length+=8;this.output.ensureLengthFromBack(this.length);var b=this.output.array.length-this.length,c=Tlv.getHighBytes(a);this.output.array[b]=c>>24&255;this.output.array[b+1]=c>>16&255;this.output.array[b+2]=c>>8&255;this.output.array[b+3]=c&255;this.output.array[b+4]=a>>24&255;this.output.array[b+5]=a>>16&255;this.output.array[b+
+6]=a>>8&255;this.output.array[b+7]=a&255}};TlvEncoder.prototype.writeNonNegativeIntegerTlv=function(a,b){var c=this.length;this.writeNonNegativeInteger(b);this.writeTypeAndLength(a,this.length-c)};TlvEncoder.prototype.writeOptionalNonNegativeIntegerTlv=function(a,b){null!=b&&0<=b&&this.writeNonNegativeIntegerTlv(a,b)};TlvEncoder.prototype.writeBuffer=function(a){null!=a&&(this.length+=a.length,this.output.copyFromBack(a,this.length))};
+TlvEncoder.prototype.writeBlobTlv=function(a,b){null==b?this.writeTypeAndLength(a,0):(this.writeBuffer(b),this.writeTypeAndLength(a,b.length))};TlvEncoder.prototype.writeOptionalBlobTlv=function(a,b){null!=b&&0<b.length&&this.writeBlobTlv(a,b)};TlvEncoder.prototype.getOutput=function(){return this.output.array.slice(this.output.array.length-this.length)};var DecodingException=require("../decoding-exception.js").DecodingException,TlvDecoder=function(a){this.input=a;this.offset=0};
+exports.TlvDecoder=TlvDecoder;TlvDecoder.prototype.readVarNumber=function(){var a=this.input[this.offset];this.offset+=1;return 253>a?a:this.readExtendedVarNumber(a)};
+TlvDecoder.prototype.readExtendedVarNumber=function(a){253==a?(a=(this.input[this.offset]<<8)+this.input[this.offset+1],this.offset+=2):254==a?(a=Math.abs(this.input[this.offset]<<24)+(this.input[this.offset+1]<<16)+(this.input[this.offset+2]<<8)+this.input[this.offset+3],this.offset+=4):(a=4294967296*(Math.abs(this.input[this.offset]<<24)+(this.input[this.offset+1]<<16)+(this.input[this.offset+2]<<8)+this.input[this.offset+3])+(this.input[this.offset+4]<<24)+(this.input[this.offset+5]<<16)+(this.input[this.offset+
+6]<<8)+this.input[this.offset+7],this.offset+=8);return a};TlvDecoder.prototype.readTypeAndLength=function(a){if(this.readVarNumber()!=a)throw new DecodingException(Error("Did not get the expected TLV type"));a=this.readVarNumber();if(this.offset+a>this.input.length)throw new DecodingException(Error("TLV length exceeds the buffer length"));return a};TlvDecoder.prototype.readNestedTlvsStart=function(a){return this.readTypeAndLength(a)+this.offset};
+TlvDecoder.prototype.finishNestedTlvs=function(a){if(this.offset!=a){for(;this.offset<a;){this.readVarNumber();var b=this.readVarNumber();this.offset+=b;if(this.offset>this.input.length)throw new DecodingException(Error("TLV length exceeds the buffer length"));}if(this.offset!=a)throw new DecodingException(Error("TLV length does not equal the total length of the nested TLVs"));}};
+TlvDecoder.prototype.peekType=function(a,b){if(this.offset>=b)return!1;var c=this.offset,d=this.readVarNumber();this.offset=c;return d==a};
+TlvDecoder.prototype.readNonNegativeInteger=function(a){var b;if(1==a)b=this.input[this.offset];else if(2==a)b=(this.input[this.offset]<<8)+this.input[this.offset+1];else if(4==a)b=Math.abs(this.input[this.offset]<<24)+(this.input[this.offset+1]<<16)+(this.input[this.offset+2]<<8)+this.input[this.offset+3];else if(8==a)b=4294967296*(Math.abs(this.input[this.offset]<<24)+(this.input[this.offset+1]<<16)+(this.input[this.offset+2]<<8)+this.input[this.offset+3])+Math.abs(this.input[this.offset+4]<<24)+
+(this.input[this.offset+5]<<16)+(this.input[this.offset+6]<<8)+this.input[this.offset+7];else throw new DecodingException(Error("Invalid length for a TLV nonNegativeInteger"));this.offset+=a;return b};TlvDecoder.prototype.readNonNegativeIntegerTlv=function(a){a=this.readTypeAndLength(a);return this.readNonNegativeInteger(a)};TlvDecoder.prototype.readOptionalNonNegativeIntegerTlv=function(a,b){return this.peekType(a,b)?this.readNonNegativeIntegerTlv(a):null};
+TlvDecoder.prototype.readBlobTlv=function(a){a=this.readTypeAndLength(a);var b=this.input.slice(this.offset,this.offset+a);this.offset+=a;return b};TlvDecoder.prototype.readOptionalBlobTlv=function(a,b){return this.peekType(a,b)?this.readBlobTlv(a):null};TlvDecoder.prototype.readBooleanTlv=function(a,b){if(this.peekType(a,b)){var c=this.readTypeAndLength(a);this.offset+=c;return!0}return!1};TlvDecoder.prototype.getOffset=function(){return this.offset};
+TlvDecoder.prototype.seek=function(a){this.offset=a};TlvDecoder.prototype.getSlice=function(a,b){return this.input.slice(a,b)};var TlvDecoder=require("./tlv-decoder.js").TlvDecoder,TlvStructureDecoder=function TlvStructureDecoder(){this.gotElementEnd_=!1;this.offset_=0;this.state_=TlvStructureDecoder.READ_TYPE;this.headerLength_=0;this.useHeaderBuffer_=!1;this.headerBuffer_=new Buffer(8);this.nBytesToRead_=0};exports.TlvStructureDecoder=TlvStructureDecoder;TlvStructureDecoder.READ_TYPE=0;
+TlvStructureDecoder.READ_TYPE_BYTES=1;TlvStructureDecoder.READ_LENGTH=2;TlvStructureDecoder.READ_LENGTH_BYTES=3;TlvStructureDecoder.READ_VALUE_BYTES=4;
+TlvStructureDecoder.prototype.findElementEnd=function(a){if(this.gotElementEnd_)return!0;for(var b=new TlvDecoder(a);;){if(this.offset_>=a.length)return!1;if(this.state_==TlvStructureDecoder.READ_TYPE){var c=a[this.offset_];this.offset_+=1;253>c?this.state_=TlvStructureDecoder.READ_LENGTH:(this.nBytesToRead_=253==c?2:254==c?4:8,this.state_=TlvStructureDecoder.READ_TYPE_BYTES)}else if(this.state_==TlvStructureDecoder.READ_TYPE_BYTES){c=a.length-this.offset_;if(c<this.nBytesToRead_)return this.offset_+=
+c,this.nBytesToRead_-=c,!1;this.offset_+=this.nBytesToRead_;this.state_=TlvStructureDecoder.READ_LENGTH}else if(this.state_==TlvStructureDecoder.READ_LENGTH)if(c=a[this.offset_],this.offset_+=1,253>c){this.nBytesToRead_=c;if(0==this.nBytesToRead_)return this.gotElementEnd_=!0;this.state_=TlvStructureDecoder.READ_VALUE_BYTES}else this.nBytesToRead_=253==c?2:254==c?4:8,this.firstOctet_=c,this.state_=TlvStructureDecoder.READ_LENGTH_BYTES;else if(this.state_==TlvStructureDecoder.READ_LENGTH_BYTES){c=
+a.length-this.offset_;if(!this.useHeaderBuffer_&&c>=this.nBytesToRead_)b.seek(this.offset_),this.nBytesToRead_=b.readExtendedVarNumber(this.firstOctet_),this.offset_=b.getOffset();else{this.useHeaderBuffer_=!0;var d=this.nBytesToRead_-this.headerLength_;if(d>c){if(this.headerLength_+c>this.headerBuffer_.length)throw Error("Cannot store more header bytes than the size of headerBuffer");a.slice(this.offset_,this.offset_+c).copy(this.headerBuffer_,this.headerLength_);this.offset_+=c;this.headerLength_+=
+c;return!1}if(this.headerLength_+d>this.headerBuffer_.length)throw Error("Cannot store more header bytes than the size of headerBuffer");a.slice(this.offset_,this.offset_+d).copy(this.headerBuffer_,this.headerLength_);this.offset_+=d;this.nBytesToRead_=(new TlvDecoder(this.headerBuffer_)).readExtendedVarNumber(this.firstOctet_)}if(0==this.nBytesToRead_)return this.gotElementEnd_=!0;this.state_=TlvStructureDecoder.READ_VALUE_BYTES}else{if(this.state_==TlvStructureDecoder.READ_VALUE_BYTES){c=a.length-
+this.offset_;if(c<this.nBytesToRead_)return this.offset_+=c,this.nBytesToRead_-=c,!1;this.offset_+=this.nBytesToRead_;return this.gotElementEnd_=!0}throw Error("findElementEnd: unrecognized state");}}};TlvStructureDecoder.prototype.getOffset=function(){return this.offset_};TlvStructureDecoder.prototype.seek=function(a){this.offset_=a};
+var TlvEncoder=require("./tlv/tlv-encoder.js").TlvEncoder,TlvDecoder=require("./tlv/tlv-decoder.js").TlvDecoder,Blob=require("../util/blob.js").Blob,Name=require("../name.js").Name,ProtobufTlv=function(){};exports.ProtobufTlv=ProtobufTlv;ProtobufTlv._Field=null;ProtobufTlv.establishField=function(){if(null===ProtobufTlv._Field)try{ProtobufTlv._Field=dcodeIO.ProtoBuf.Reflect.Message.Field}catch(a){ProtobufTlv._Field=require("protobufjs").Reflect.Message.Field}};
+ProtobufTlv.encode=function(a,b){ProtobufTlv.establishField();a.encodeAB();var c=new TlvEncoder;ProtobufTlv._encodeMessageValue(a,b,c);return new Blob(c.getOutput(),!1)};ProtobufTlv.decode=function(a,b,c){ProtobufTlv.establishField();c="object"===typeof c&&c instanceof Blob?c.buf():c;var d=new TlvDecoder(c);ProtobufTlv._decodeMessageValue(a,b,d,c.length)};
+ProtobufTlv._encodeMessageValue=function(a,b,c){b=b.getChildren(ProtobufTlv._Field);for(var d=b.length-1;0<=d;--d){var e=b[d],f=e.id,g;if(e.repeated)g=a[e.name];else if(null!=a[e.name])g=[a[e.name]];else continue;for(var h=g.length-1;0<=h;--h){var k=g[h];if("message"==e.type.name){var l=c.getLength();ProtobufTlv._encodeMessageValue(k,e.resolvedType,c);c.writeTypeAndLength(f,c.getLength()-l)}else if("uint32"==e.type.name||"uint64"==e.type.name)c.writeNonNegativeIntegerTlv(f,k);else if("enum"==e.type.name){if(0>
+k)throw Error("ProtobufTlv::encode: ENUM value may not be negative");c.writeNonNegativeIntegerTlv(f,k)}else if("bytes"==e.type.name)l=k.toBuffer(),void 0==l.length&&(l=new Uint8Array(k.toArrayBuffer())),c.writeBlobTlv(f,l);else if("string"==e.type.name)c.writeBlobTlv(f,(new Blob(k,!1)).buf());else if("bool"==e.type.name)k&&c.writeTypeAndLength(f,0);else if("double"==e.type.name)l=new Buffer(8),l.writeDoubleLE(k,0),c.writeBlobTlv(f,l);else throw Error("ProtobufTlv::encode: Unknown field type");}}};
+ProtobufTlv._decodeMessageValue=function(a,b,c,d){b=b.getChildren(ProtobufTlv._Field);for(var e=0;e<b.length;++e){var f=b[e],g=f.id;if(f.required||c.peekType(g,d))if(f.repeated)for(;c.peekType(g,d);)if("message"==f.type.name){var h=c.readNestedTlvsStart(g),k=new (f.resolvedType.build());a.add(f.name,k);ProtobufTlv._decodeMessageValue(k,f.resolvedType,c,h);c.finishNestedTlvs(h)}else a.add(f.name,ProtobufTlv._decodeFieldValue(f,g,c,d));else"message"==f.type.name?(h=c.readNestedTlvsStart(g),k=new (f.resolvedType.build()),
+a.set(f.name,k),ProtobufTlv._decodeMessageValue(k,f.resolvedType,c,h),c.finishNestedTlvs(h)):a.set(f.name,ProtobufTlv._decodeFieldValue(f,g,c,d))}};
+ProtobufTlv._decodeFieldValue=function(a,b,c,d){if("uint32"==a.type.name||"uint64"==a.type.name||"enum"==a.type.name)return c.readNonNegativeIntegerTlv(b);if("bytes"==a.type.name)return c.readBlobTlv(b);if("string"==a.type.name)return c.readBlobTlv(b).toString();if("bool"==a.type.name)return c.readBooleanTlv(b,d);if("double"==a.type.name)return c.readBlobTlv(b).readDoubleLE(0);throw Error("ProtobufTlv.decode: Unknown field type");};
+ProtobufTlv.toName=function(a){for(var b=new Name,c=0;c<a.length;++c)b.append(new Blob(new Buffer(a[c].toBinary(),"binary")),!1);return b};var OID=function(a){if("string"===typeof a){a=a.split(".");this.oid=[];for(var b=0;b<a.length;++b)this.oid.push(parseInt(a[b]))}else this.oid=a.slice(0,a.length)};exports.OID=OID;OID.prototype.getIntegerList=function(){return this.oid};OID.prototype.setIntegerList=function(a){this.oid=a.slice(0,a.length)};
+OID.prototype.toString=function(){for(var a="",b=0;b<this.oid.length;++b)0!==b&&(a+="."),a+=this.oid[b];return a};OID.prototype.equals=function(a){if(!(a instanceof OID)||this.oid.length!==a.oid.length)return!1;for(var b=0;b<this.oid.length;++b)if(this.oid[b]!=a.oid[b])return!1;return!0};var WireFormat=function(){};exports.WireFormat=WireFormat;WireFormat.prototype.encodeName=function(a){throw Error("encodeName is unimplemented in the base WireFormat class. You should use a derived class.");};
+WireFormat.prototype.decodeName=function(a,b,c){throw Error("decodeName is unimplemented in the base WireFormat class. You should use a derived class.");};WireFormat.prototype.encodeInterest=function(a){throw Error("encodeInterest is unimplemented in the base WireFormat class. You should use a derived class.");};WireFormat.prototype.decodeInterest=function(a,b,c){throw Error("decodeInterest is unimplemented in the base WireFormat class. You should use a derived class.");};
+WireFormat.prototype.encodeData=function(a){throw Error("encodeData is unimplemented in the base WireFormat class. You should use a derived class.");};WireFormat.prototype.decodeData=function(a,b,c){throw Error("decodeData is unimplemented in the base WireFormat class. You should use a derived class.");};WireFormat.prototype.encodeControlParameters=function(a){throw Error("encodeControlParameters is unimplemented in the base WireFormat class. You should use a derived class.");};
+WireFormat.prototype.decodeControlParameters=function(a,b,c){throw Error("decodeControlParameters is unimplemented in the base WireFormat class. You should use a derived class.");};WireFormat.prototype.encodeControlResponse=function(a){throw Error("encodeControlResponse is unimplemented in the base WireFormat class. You should use a derived class.");};
+WireFormat.prototype.decodeControlResponse=function(a,b,c){throw Error("decodeControlResponse is unimplemented in the base WireFormat class. You should use a derived class.");};WireFormat.prototype.encodeSignatureInfo=function(a){throw Error("encodeSignatureInfo is unimplemented in the base WireFormat class. You should use a derived class.");};
+WireFormat.prototype.decodeSignatureInfoAndValue=function(a,b,c){throw Error("decodeSignatureInfoAndValue is unimplemented in the base WireFormat class. You should use a derived class.");};WireFormat.prototype.encodeSignatureValue=function(a){throw Error("encodeSignatureValue is unimplemented in the base WireFormat class. You should use a derived class.");};
+WireFormat.prototype.decodeLpPacket=function(a,b,c){throw Error("decodeLpPacket is unimplemented in the base WireFormat class. You should use a derived class.");};WireFormat.prototype.encodeDelegationSet=function(a){throw Error("encodeDelegationSet is unimplemented in the base WireFormat class. You should use a derived class.");};
+WireFormat.prototype.decodeDelegationSet=function(a,b,c){throw Error("decodeDelegationSet is unimplemented in the base WireFormat class. You should use a derived class.");};WireFormat.prototype.encodeEncryptedContent=function(a){throw Error("encodeEncryptedContent is unimplemented in the base WireFormat class. You should use a derived class.");};
+WireFormat.prototype.decodeEncryptedContent=function(a,b,c){throw Error("decodeEncryptedContent is unimplemented in the base WireFormat class. You should use a derived class.");};WireFormat.setDefaultWireFormat=function(a){WireFormat.defaultWireFormat=a};WireFormat.getDefaultWireFormat=function(){return WireFormat.defaultWireFormat};
+var TlvWireFormat=require("./tlv-wire-format.js").TlvWireFormat,DataUtils=require("./data-utils.js").DataUtils,Tlv=require("./tlv/tlv.js").Tlv,TlvStructureDecoder=require("./tlv/tlv-structure-decoder.js").TlvStructureDecoder,DecodingException=require("./decoding-exception.js").DecodingException,NdnCommon=require("../util/ndn-common.js").NdnCommon,LOG=require("../log.js").Log.LOG,ElementReader=function(a){this.elementListener_=a;this.dataParts_=[];this.tlvStructureDecoder_=new TlvStructureDecoder};
+exports.ElementReader=ElementReader;
+ElementReader.prototype.onReceivedData=function(a){for(;;){var b,c;try{if(0===this.dataParts_.length&&0>=a.length)break;this.tlvStructureDecoder_.seek(0);b=this.tlvStructureDecoder_.findElementEnd(a);c=this.tlvStructureDecoder_.getOffset()}catch(d){throw this.dataParts_=[],this.tlvStructureDecoder_=new TlvStructureDecoder,d;}if(b){var e;0===this.dataParts_.length?e=a.slice(0,c):(this.dataParts_.push(a.slice(0,c)),e=DataUtils.concatArrays(this.dataParts_),this.dataParts_=[]);a=a.slice(c,a.length);
+this.tlvStructureDecoder_=new TlvStructureDecoder;this.elementListener_.onReceivedElement(e);if(0==a.length)break}else{b=a.length;for(c=0;c<this.dataParts_.length;++c)b+=this.dataParts_[c].length;if(b>NdnCommon.MAX_NDN_PACKET_SIZE)throw this.dataParts_=[],this.tlvStructureDecoder_=new TlvStructureDecoder,new DecodingException(Error("The incoming packet exceeds the maximum limit Face.getMaxNdnPacketSize()"));this.dataParts_.push(new Buffer(a));3<LOG&&console.log("Incomplete packet received. Length "+
+a.length+". Wait for more input.");break}}};function DerDecodingException(a){if(a)return a.__proto__=DerDecodingException.prototype,a}DerDecodingException.prototype=Error();DerDecodingException.prototype.name="DerDecodingException";exports.DerDecodingException=DerDecodingException;function DerEncodingException(a){if(a)return a.__proto__=DerEncodingException.prototype,a}DerEncodingException.prototype=Error();DerEncodingException.prototype.name="DerEncodingException";exports.DerEncodingException=DerEncodingException;
+var DerNodeType=function(){};exports.DerNodeType=DerNodeType;DerNodeType.Eoc=0;DerNodeType.Boolean=1;DerNodeType.Integer=2;DerNodeType.BitString=3;DerNodeType.OctetString=4;DerNodeType.Null=5;DerNodeType.ObjectIdentifier=6;DerNodeType.ObjectDescriptor=7;DerNodeType.External=40;DerNodeType.Real=9;DerNodeType.Enumerated=10;DerNodeType.EmbeddedPdv=43;DerNodeType.Utf8String=12;DerNodeType.RelativeOid=13;DerNodeType.Sequence=48;DerNodeType.Set=49;DerNodeType.NumericString=18;
+DerNodeType.PrintableString=19;DerNodeType.T61String=20;DerNodeType.VideoTexString=21;DerNodeType.Ia5String=22;DerNodeType.UtcTime=23;DerNodeType.GeneralizedTime=24;DerNodeType.GraphicString=25;DerNodeType.VisibleString=26;DerNodeType.GeneralString=27;DerNodeType.UniversalString=28;DerNodeType.CharacterString=29;DerNodeType.BmpString=30;
+var DynamicBuffer=require("../../util/dynamic-buffer.js").DynamicBuffer,Blob=require("../../util/blob.js").Blob,DerDecodingException=require("./der-decoding-exception.js").DerDecodingException,DerEncodingException=require("./der-encoding-exception.js").DerEncodingException,DerNodeType=require("./der-node-type.js").DerNodeType,DerNode=function(a){this.nodeType_=a;this.parent_=null;this.header_=new Buffer(0);this.payload_=new DynamicBuffer(0);this.payloadPosition_=0};exports.DerNode=DerNode;
+DerNode.prototype.getSize=function(){return this.header_.length+this.payloadPosition_};
+DerNode.prototype.encodeHeader=function(a){var b=new DynamicBuffer(10),c=0;b.array[c++]=this.nodeType_;if(0>a)throw Error("encodeHeader: DER object has negative length");if(127>=a)b.array[c++]=a&255;else{var d=new DynamicBuffer(10),e=a;for(a=0;0!=e;)++a,d.ensureLengthFromBack(a),d.array[d.array.length-a]=e&255,e>>=8;e=a+1;d.ensureLengthFromBack(e);d.array[d.array.length-e]=(128|a)&255;b.copy(d.slice(d.array.length-e),c);c+=e}this.header_=b.slice(0,c)};
+DerNode.prototype.decodeHeader=function(a,b){var c=b,d=a[c]&255,c=c+1;this.nodeType_=d;var e=a[c]&255,c=c+1,f=new DynamicBuffer(10),g=0;f.array[g++]=d;d=f.array[g++]=e;if(0!=(e&128))for(e&=127,d=0;0<e;){var h=a[c],c=c+1;f.ensureLength(g+1);f.array[g++]=h;d=256*d+(h&255);e-=1}this.header_=f.slice(0,g);return d};DerNode.prototype.encode=function(){var a=new Buffer(this.getSize());this.header_.copy(a);this.payload_.slice(0,this.payloadPosition_).copy(a,this.header_.length);return new Blob(a,!1)};
+DerNode.prototype.decode=function(a,b){var c=b,d=this.decodeHeader(a,c),e=this.header_.length;0<d&&(c+=e,this.payloadAppend(a.slice(c,c+d)))};DerNode.prototype.payloadAppend=function(a){this.payloadPosition_=this.payload_.copy(a,this.payloadPosition_)};
+DerNode.parse=function(a,b){void 0==b&&(b=0);var c=a[b]&255;if(c===DerNodeType.Boolean)c=new DerNode.DerBoolean;else if(c===DerNodeType.Integer)c=new DerNode.DerInteger;else if(c===DerNodeType.BitString)c=new DerNode.DerBitString;else if(c===DerNodeType.OctetString)c=new DerNode.DerOctetString;else if(c===DerNodeType.Null)c=new DerNode.DerNull;else if(c===DerNodeType.ObjectIdentifier)c=new DerNode.DerOid;else if(c===DerNodeType.Sequence)c=new DerNode.DerSequence;else if(c===DerNodeType.PrintableString)c=
+new DerNode.DerPrintableString;else if(c===DerNodeType.GeneralizedTime)c=new DerNode.DerGeneralizedTime;else throw new DerDecodingException(Error("Unimplemented DER type "+c));c.decode(a,b);return c};DerNode.prototype.toVal=function(){return this.encode()};DerNode.prototype.getPayload=function(){return new Blob(this.payload_.slice(0,this.payloadPosition_),!0)};DerNode.prototype.getChildren=function(){throw new DerDecodingException(Error("getChildren: This DerNode is not DerSequence"));};
+DerNode.getSequence=function(a,b){if(0>b||b>=a.length)throw new DerDecodingException(Error("getSequence: Child index is out of bounds"));if(!(a[b]instanceof DerNode.DerSequence))throw new DerDecodingException(Error("getSequence: Child DerNode is not a DerSequence"));return a[b]};DerNode.DerStructure=function(a){DerNode.call(this,a);this.childChanged_=!1;this.nodeList_=[];this.size_=0};DerNode.DerStructure.prototype=new DerNode;DerNode.DerStructure.prototype.name="DerStructure";
+DerNode.DerStructure.prototype.getSize=function(){this.childChanged_&&(this.updateSize(),this.childChanged_=!1);this.encodeHeader(this.size_);return this.size_+this.header_.length};DerNode.DerStructure.prototype.getChildren=function(){return this.nodeList_};DerNode.DerStructure.prototype.updateSize=function(){for(var a=0,b=0;b<this.nodeList_.length;++b)a+=this.nodeList_[b].getSize();this.size_=a;this.childChanged_=!1};
+DerNode.DerStructure.prototype.addChild=function(a,b){a.parent_=this;this.nodeList_.push(a);b&&null!=this.parent_&&this.parent_.setChildChanged();this.childChanged_=!0};DerNode.DerStructure.prototype.setChildChanged=function(){null!=this.parent_&&this.parent_.setChildChanged();this.childChanged_=!0};
+DerNode.DerStructure.prototype.encode=function(){var a=new DynamicBuffer(10),b=0;this.updateSize();this.encodeHeader(this.size_);for(var b=a.copy(this.header_,b),c=0;c<this.nodeList_.length;++c)var d=this.nodeList_[c].encode(),b=a.copy(d.buf(),b);return new Blob(a.slice(0,b),!1)};DerNode.DerStructure.prototype.decode=function(a,b){var c=b;this.size_=this.decodeHeader(a,c);for(var c=c+this.header_.length,d=0;d<this.size_;){var e=DerNode.parse(a,c),f=e.getSize(),c=c+f,d=d+f;this.addChild(e,!1)}};
+DerNode.DerByteString=function(a,b){DerNode.call(this,b);null!=a&&(this.payloadAppend(a),this.encodeHeader(a.length))};DerNode.DerByteString.prototype=new DerNode;DerNode.DerByteString.prototype.name="DerByteString";DerNode.DerByteString.prototype.toVal=function(){return this.getPayload()};DerNode.DerBoolean=function(a){DerNode.call(this,DerNodeType.Boolean);void 0!=a&&(a=a?255:0,this.payload_.ensureLength(this.payloadPosition_+1),this.payload_.array[this.payloadPosition_++]=a,this.encodeHeader(1))};
+DerNode.DerBoolean.prototype=new DerNode;DerNode.DerBoolean.prototype.name="DerBoolean";DerNode.DerBoolean.prototype.toVal=function(){return 0!=this.payload_.array[0]};
+DerNode.DerInteger=function(a){DerNode.call(this,DerNodeType.Integer);if(void 0!=a){if(Buffer.isBuffer(a)){if(0<a.length&&128<=a[0])throw new DerEncodingException(Error("DerInteger: Negative integers are not currently supported"));0==a.length?this.payloadAppend(new Buffer([0])):this.payloadAppend(a)}else{a=Math.round(a);if(0>a)throw new DerEncodingException(Error("DerInteger: Negative integers are not currently supported"));for(var b=new DynamicBuffer(10),c=0;!(++c,b.ensureLengthFromBack(c),b.array[b.array.length-
+c]=a&255,a>>=8,0>=a););128<=b.array[b.array.length-c]&&(++c,b.ensureLengthFromBack(c),b.array[b.array.length-c]=0);this.payloadAppend(b.slice(b.array.length-c))}this.encodeHeader(this.payloadPosition_)}};DerNode.DerInteger.prototype=new DerNode;DerNode.DerInteger.prototype.name="DerInteger";
+DerNode.DerInteger.prototype.toVal=function(){if(0<this.payloadPosition_&&128<=this.payload_.array[0])throw new DerDecodingException(Error("DerInteger: Negative integers are not currently supported"));for(var a=0,b=0;b<this.payloadPosition_;++b)a<<=8,a+=this.payload_.array[b];return a};DerNode.DerBitString=function(a,b){DerNode.call(this,DerNodeType.BitString);void 0!=a&&(this.payload_.ensureLength(this.payloadPosition_+1),this.payload_.array[this.payloadPosition_++]=b&255,this.payloadAppend(a),this.encodeHeader(this.payloadPosition_))};
+DerNode.DerBitString.prototype=new DerNode;DerNode.DerBitString.prototype.name="DerBitString";DerNode.DerOctetString=function(a){DerNode.DerByteString.call(this,a,DerNodeType.OctetString)};DerNode.DerOctetString.prototype=new DerNode.DerByteString;DerNode.DerOctetString.prototype.name="DerOctetString";DerNode.DerNull=function(){DerNode.call(this,DerNodeType.Null);this.encodeHeader(0)};DerNode.DerNull.prototype=new DerNode;DerNode.DerNull.prototype.name="DerNull";
+DerNode.DerOid=function(a){DerNode.call(this,DerNodeType.ObjectIdentifier);if(void 0!=a)if("string"===typeof a){a=a.split(".");for(var b=[],c=0;c<a.length;++c)b.push(parseInt(a[c]));this.prepareEncoding(b)}else this.prepareEncoding(a.getIntegerList())};DerNode.DerOid.prototype=new DerNode;DerNode.DerOid.prototype.name="DerOid";
+DerNode.DerOid.prototype.prepareEncoding=function(a){var b;if(0==a.length)throw new DerEncodingException(Error("No integer in OID"));if(0<=a[0]&&2>=a[0])b=40*a[0];else throw new DerEncodingException(Error("First integer in OID is out of range"));if(2<=a.length)if(0<=a[1]&&39>=a[1])b+=a[1];else throw new DerEncodingException(Error("Second integer in OID is out of range"));var c=new DynamicBuffer(10),d=0,d=c.copy(DerNode.DerOid.encode128(b),d);if(2<a.length)for(b=2;b<a.length;++b)d=c.copy(DerNode.DerOid.encode128(a[b]),
+d);this.encodeHeader(d);this.payloadAppend(c.slice(0,d))};DerNode.DerOid.encode128=function(a){var b=new DynamicBuffer(10),c=0;if(128>a)++c,b.array[b.array.length-c]=a&127;else for(++c,b.array[b.array.length-c]=a&127,a>>=7;0!=a;)++c,b.ensureLengthFromBack(c),b.array[b.array.length-c]=a&127|128,a>>=7;return b.slice(b.array.length-c)};
+DerNode.DerOid.prototype.decode128=function(a,b){for(var c=0,d=a;0!=(this.payload_.array[a]&128);)c=128*c+(this.payload_.array[a]&255)-128,a+=1;c=128*c+(this.payload_.array[a]&255);b[0]=a-d+1;return c};DerNode.DerOid.prototype.toVal=function(){for(var a=0,b=[];a<this.payloadPosition_;){var c=[0],d=this.decode128(a,c),a=a+c[0];b.push(d)}a=b[0];a=Math.floor(a/40)+"."+a%40;for(c=1;c<b.length;++c)a+="."+b[c];return a};DerNode.DerSequence=function(){DerNode.DerStructure.call(this,DerNodeType.Sequence)};
+DerNode.DerSequence.prototype=new DerNode.DerStructure;DerNode.DerSequence.prototype.name="DerSequence";DerNode.DerPrintableString=function(a){DerNode.DerByteString.call(this,a,DerNodeType.PrintableString)};DerNode.DerPrintableString.prototype=new DerNode.DerByteString;DerNode.DerPrintableString.prototype.name="DerPrintableString";
+DerNode.DerGeneralizedTime=function(a){DerNode.call(this,DerNodeType.GeneralizedTime);void 0!=a&&(a=DerNode.DerGeneralizedTime.toDerTimeString(a),this.payloadAppend((new Blob(a)).buf()),this.encodeHeader(this.payloadPosition_))};DerNode.DerGeneralizedTime.prototype=new DerNode;DerNode.DerGeneralizedTime.prototype.name="DerGeneralizedTime";
+DerNode.DerGeneralizedTime.toDerTimeString=function(a){a=new Date(Math.round(a));return a.getUTCFullYear()+DerNode.DerGeneralizedTime.to2DigitString(a.getUTCMonth()+1)+DerNode.DerGeneralizedTime.to2DigitString(a.getUTCDate())+DerNode.DerGeneralizedTime.to2DigitString(a.getUTCHours())+DerNode.DerGeneralizedTime.to2DigitString(a.getUTCMinutes())+DerNode.DerGeneralizedTime.to2DigitString(a.getUTCSeconds())+"Z"};
+DerNode.DerGeneralizedTime.to2DigitString=function(a){a=a.toString();return 1===a.length?"0"+a:a};DerNode.DerGeneralizedTime.prototype.toVal=function(){var a=this.payload_.slice(0,this.payloadPosition_).toString();return Date.UTC(parseInt(a.substr(0,4)),parseInt(a.substr(4,2)-1),parseInt(a.substr(6,2)),parseInt(a.substr(8,2)),parseInt(a.substr(10,2)),parseInt(a.substr(12,2)))};var fs=require("fs"),BoostInfoTree=function(a,b){this.subtrees=[];this.value=a;this.parent=b;this.lastChild=null};
+BoostInfoTree.prototype.addSubtree=function(a,b){var c=this.find(a);null!==c?c.push(b):this.subtrees.push({key:a,value:[b]});b.parent=this;this.lastChild=b};BoostInfoTree.prototype.createSubtree=function(a,b){var c=new BoostInfoTree(b,this);this.addSubtree(a,c);return c};
+BoostInfoTree.prototype.get=function(a){a=a.replace(/^\/+/,"");if(0===a.length)return[this];var b=a.split("/");a=this.find(b[0]);if(null===a)return[];if(1==b.length)return a.slice(0);for(var b=b.slice(1).join("/"),c=[],d=0;d<a.length;++d)var e=a[d].get(b),c=c.concat(e);return c};BoostInfoTree.prototype.getFirstValue=function(a){a=this.get(a);return 1<=a.length?a[0].value:null};BoostInfoTree.prototype.getValue=function(){return this.value};BoostInfoTree.prototype.getParent=function(){return this.parent};
+BoostInfoTree.prototype.getLastChild=function(){return this.lastChild};
+BoostInfoTree.prototype.prettyPrint=function(a){a=a||1;var b=Array(a+1).join(" "),c="";null!=this.parent&&(this.value&&0<this.value.length&&(c+='"'+this.value+'"'),c+="\n");if(0<this.subtrees.length){this.parent&&(c+=b+"{\n");for(var d=Array(a+2+1).join(" "),e=0;e<this.subtrees.length;++e)for(var f=0;f<this.subtrees[e].value.length;++f)c+=d+this.subtrees[e].key+" "+this.subtrees[e].value[f].prettyPrint(a+2);this.parent&&(c+=b+"}\n")}return c};BoostInfoTree.prototype.toString=function(){return this.prettyPrint()};
+BoostInfoTree.prototype.find=function(a){for(var b=0;b<this.subtrees.length;++b)if(this.subtrees[b].key==a)return this.subtrees[b].value;return null};var BoostInfoParser=function(){this.root=new BoostInfoTree};exports.BoostInfoParser=BoostInfoParser;exports.BoostInfoTree=BoostInfoTree;BoostInfoParser.prototype.read=function(a,b){var c;"string"==typeof b?c=a:(b=a,c=fs.readFileSync(a).toString());var d=this.root,e=this;c.split(/\r?\n/).forEach(function(a){d=e.parseLine(a.trim(),d)})};
+BoostInfoParser.prototype.write=function(a){fs.writeFileSync(a,""+this.root)};BoostInfoParser.prototype.getRoot=function(){return this.root};
+BoostInfoParser.shlex_split=function(a){var b=[];if(""==a)return b;for(var c=0;;){for(;0<=" \t\n\r".indexOf(a[c]);)if(c+=1,c>=a.length)return b;for(var d=c,e=!1,f="";;){if("\\"==a[d]){if(f+=a.substring(c,d),d=c=d+1,d>=a.length)break}else if(e)'"'==a[d]&&(f+=a.substring(c,d),c=d+1,e=!1);else if('"'==a[d])f+=a.substring(c,d),c=d+1,e=!0;else if(0<=" \t\n\r".indexOf(a[d]))break;d+=1;if(d>=a.length)break}f+=a.substring(c,d);b.push(f);if(d>=a.length)return b;c=d}};
+BoostInfoParser.prototype.parseLine=function(a,b){var c=a.indexOf(";");0<=c&&(a=a.substring(0,c).trim());if(0==a.length)return b;for(var c=BoostInfoParser.shlex_split(a),d=!1,e=!1,f=0;f<c.length;++f)d=d||"{"==c[f],e=e||"}"==c[f];if(!d&&!e){var d=c[0],g;1<c.length&&(g=c[1]);b.createSubtree(d,g);return b}c=a.indexOf("{");if(0<c)return g=a.substring(0,c),c=a.substring(c),g=this.parseLine(g,b),this.parseLine(c,g);if("{"==a[0])return b=b.getLastChild();if("}"==a[0])return b=b.getParent();throw runtime_error("BoostInfoParser: input line is malformed");
+};
+var Name=require("../name.js").Name,InterestFilter=require("../interest-filter.js").InterestFilter,ForwardingFlags=require("../forwarding-flags.js").ForwardingFlags,WireFormat=require("../encoding/wire-format.js").WireFormat,LOG=require("../log.js").Log.LOG,MemoryContentCache=function(a,b){b=b||1E3;this.face=a;this.cleanupIntervalMilliseconds=b;this.nextCleanupTime=(new Date).getTime()+b;this.onDataNotFoundForPrefix={};this.interestFilterIdList=[];this.registeredPrefixIdList=[];this.noStaleTimeCache=[];
+this.staleTimeCache=[];this.emptyComponent=new Name.Component;this.pendingInterestTable=[];var c=this;this.storePendingInterestCallback=function(a,b,f,g,h){c.storePendingInterest(b,f)}};exports.MemoryContentCache=MemoryContentCache;
+MemoryContentCache.prototype.registerPrefix=function(a,b,c,d,e,f){var g=c,h=d,k=e;c="object"===typeof g&&1===g.length&&"function"===typeof g[0]?g[0]:null;d="function"===typeof g?g:"function"===typeof h?h:null;e=g instanceof ForwardingFlags?g:h instanceof ForwardingFlags?h:k instanceof ForwardingFlags?k:new ForwardingFlags;f=g instanceof WireFormat?g:h instanceof WireFormat?h:k instanceof WireFormat?k:f instanceof WireFormat?f:WireFormat.getDefaultWireFormat();d&&(this.onDataNotFoundForPrefix[a.toUri()]=
+d);a=this.face.registerPrefix(a,this.onInterest.bind(this),b,c,e,f);this.registeredPrefixIdList.push(a)};MemoryContentCache.prototype.setInterestFilter=function(a,b){if(b){var c;c="object"===typeof a&&a instanceof InterestFilter?a.getPrefix():a;this.onDataNotFoundForPrefix[c.toUri()]=b}c=this.face.setInterestFilter(a,this.onInterest.bind(this));this.interestFilterIdList.push(c)};
+MemoryContentCache.prototype.unregisterAll=function(){for(var a=0;a<this.interestFilterIdList.length;++a)this.face.unsetInterestFilter(this.interestFilterIdList[a]);this.interestFilterIdList=[];for(a=0;a<this.registeredPrefixIdList.length;++a)this.face.removeRegisteredPrefix(this.registeredPrefixIdList[a]);this.registeredPrefixIdList=[];this.onDataNotFoundForPrefix={}};
+MemoryContentCache.prototype.add=function(a){this.doCleanup();if(null!=a.getMetaInfo().getFreshnessPeriod()&&0<=a.getMetaInfo().getFreshnessPeriod()){for(var b=new MemoryContentCache.StaleTimeContent(a),c=this.staleTimeCache.length-1;0<=c&&!(this.staleTimeCache[c].staleTimeMilliseconds<=b.staleTimeMilliseconds);)--c;this.staleTimeCache.splice(c+1,0,b)}else this.noStaleTimeCache.push(new MemoryContentCache.Content(a));b=(new Date).getTime();for(c=this.pendingInterestTable.length-1;0<=c;--c)if(this.pendingInterestTable[c].isTimedOut(b))this.pendingInterestTable.splice(c,
+1);else if(this.pendingInterestTable[c].getInterest().matchesName(a.getName())){try{this.pendingInterestTable[c].getFace().send(a.wireEncode().buf())}catch(d){0<LOG&&console.log(""+d);break}this.pendingInterestTable.splice(c,1)}};MemoryContentCache.prototype.storePendingInterest=function(a,b){this.pendingInterestTable.push(new MemoryContentCache.PendingInterest(a,b))};MemoryContentCache.prototype.getStorePendingInterest=function(){return this.storePendingInterestCallback};
+MemoryContentCache.prototype.onInterest=function(a,b,c,d,e){this.doCleanup();for(var f=0,g=null,h=this.staleTimeCache.length+this.noStaleTimeCache.length,k=0;k<h;++k){var l;l=k<this.staleTimeCache.length?this.staleTimeCache[k]:this.noStaleTimeCache[k-this.staleTimeCache.length];if(b.matchesName(l.getName())){if(0>b.getChildSelector()){c.send(l.getDataEncoding());return}var m;m=l.getName().size()>b.getName().size()?l.getName().get(b.getName().size()):this.emptyComponent;var q=!1;null===g?q=!0:0==b.getChildSelector()?
+0>m.compare(f)&&(q=!0):0<m.compare(f)&&(q=!0);q&&(f=m,g=l.getDataEncoding())}}null!==g?c.send(g):(f=this.onDataNotFoundForPrefix[a.toUri()])&&f(a,b,c,d,e)};MemoryContentCache.prototype.doCleanup=function(){var a=(new Date).getTime();if(a>=this.nextCleanupTime){for(;0<this.staleTimeCache.length&&this.staleTimeCache[0].isStale(a);)this.staleTimeCache.shift();this.nextCleanupTime=a+this.cleanupIntervalMilliseconds}};
+MemoryContentCache.Content=function(a){a&&(this.name=new Name(a.getName()),this.dataEncoding=a.wireEncode().buf())};MemoryContentCache.Content.prototype.getName=function(){return this.name};MemoryContentCache.Content.prototype.getDataEncoding=function(){return this.dataEncoding};MemoryContentCache.StaleTimeContent=function(a){MemoryContentCache.Content.call(this,a);this.staleTimeMilliseconds=(new Date).getTime()+a.getMetaInfo().getFreshnessPeriod()};MemoryContentCache.StaleTimeContent.prototype=new MemoryContentCache.Content;
+MemoryContentCache.StaleTimeContent.prototype.name="StaleTimeContent";MemoryContentCache.StaleTimeContent.prototype.isStale=function(a){return this.staleTimeMilliseconds<=a};MemoryContentCache.PendingInterest=function(a,b){this.interest=a;this.face=b;0<=this.interest.getInterestLifetimeMilliseconds()?this.timeoutMilliseconds=(new Date).getTime()+this.interest.getInterestLifetimeMilliseconds():this.timeoutMilliseconds=-1};MemoryContentCache.PendingInterest.prototype.getInterest=function(){return this.interest};
+MemoryContentCache.PendingInterest.prototype.getFace=function(){return this.face};MemoryContentCache.PendingInterest.prototype.isTimedOut=function(a){return 0<=this.timeoutTimeMilliseconds&&a>=this.timeoutTimeMilliseconds};var Name=require("../name.js").Name,NdnRegexMatcher=function(){};exports.NdnRegexMatcher=NdnRegexMatcher;NdnRegexMatcher.match=function(a,b){var c=b.toUri();a=NdnRegexMatcher.sanitizeSets(a);a=a.replace(/<>/g,"(?:<.+?>)");a=a.replace(/>/g,"");a=a.replace(/<(?!!)/g,"/");return c.match(new RegExp(a))};
+NdnRegexMatcher.sanitizeSets=function(a){for(var b=a,c=/\[(\^?)(.*?)\]/g,d;null!==(d=c.exec(a));){var e=c.lastIndex-1-d[2].length,f=e+d[2].length;0!==e-f&&(d=d[2].replace(/></g,">|<"),b=b.substr(0,e)+d+b.substr(f))}0<=b.indexOf("[^")?(b=b.replace(/\[\^/g,"(?:(?!"),b=b.replace(/\]/g,")(?:/.*)*)")):(b=b.replace(/\[/g,"("),b=b.replace(/\]/g,")"));return b};
+var Interest=require("../interest.js").Interest,Blob=require("./blob.js").Blob,KeyChain=require("../security/key-chain.js").KeyChain,NdnCommon=require("./ndn-common.js").NdnCommon,SegmentFetcher=function(a,b,c,d,e){this.face=a;this.validatorKeyChain=b;this.verifySegment=c;this.onComplete=d;this.onError=e;this.contentParts=[]};exports.SegmentFetcher=SegmentFetcher;SegmentFetcher.ErrorCode={INTEREST_TIMEOUT:1,DATA_HAS_NO_SEGMENT:2,SEGMENT_VERIFICATION_FAILED:3};SegmentFetcher.DontVerifySegment=function(a){return!0};
+SegmentFetcher.fetch=function(a,b,c,d,e){null==c||c instanceof KeyChain?(new SegmentFetcher(a,c,SegmentFetcher.DontVerifySegment,d,e)).fetchFirstSegment(b):(new SegmentFetcher(a,null,c,d,e)).fetchFirstSegment(b)};SegmentFetcher.prototype.fetchFirstSegment=function(a){a=new Interest(a);a.setChildSelector(1);a.setMustBeFresh(!0);var b=this;this.face.expressInterest(a,function(a,d){b.onData(a,d)},function(a){b.onTimeout(a)})};
+SegmentFetcher.prototype.fetchNextSegment=function(a,b,c){a=new Interest(a);a.setChildSelector(0);a.setMustBeFresh(!1);a.setName(b.getPrefix(-1).appendSegment(c));var d=this;this.face.expressInterest(a,function(a,b){d.onData(a,b)},function(a){d.onTimeout(a)})};
+SegmentFetcher.prototype.onData=function(a,b){if(null!=this.validatorKeyChain)try{var c=this;this.validatorKeyChain.verifyData(b,function(b){c.onVerified(b,a)},this.onValidationFailed.bind(this))}catch(d){console.log("Error in KeyChain.verifyData: "+d)}else if(this.verifySegment(b))this.onVerified(b,a);else try{this.onError(SegmentFetcher.ErrorCode.SEGMENT_VERIFICATION_FAILED,"Segment verification failed")}catch(e){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(e))}};
+SegmentFetcher.prototype.onVerified=function(a,b){if(SegmentFetcher.endsWithSegmentNumber(a.getName())){var c=0;try{c=a.getName().get(-1).toSegment()}catch(d){try{this.onError(SegmentFetcher.ErrorCode.DATA_HAS_NO_SEGMENT,"Error decoding the name segment number "+a.getName().get(-1).toEscapedString()+": "+d)}catch(e){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(e))}return}var f=this.contentParts.length;if(c!=f)this.fetchNextSegment(b,a.getName(),f);else{this.contentParts.push(a.getContent().buf());
+if(0<a.getMetaInfo().getFinalBlockId().getValue().size()){var g=0;try{g=a.getMetaInfo().getFinalBlockId().toSegment()}catch(h){try{this.onError(SegmentFetcher.ErrorCode.DATA_HAS_NO_SEGMENT,"Error decoding the FinalBlockId segment number "+a.getMetaInfo().getFinalBlockId().toEscapedString()+": "+h)}catch(k){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(k))}return}if(c==g){c=Buffer.concat(this.contentParts);try{this.onComplete(new Blob(c,!1))}catch(l){console.log("Error in onComplete: "+
+NdnCommon.getErrorWithStackTrace(l))}return}}this.fetchNextSegment(b,a.getName(),f+1)}}else try{this.onError(SegmentFetcher.ErrorCode.DATA_HAS_NO_SEGMENT,"Got an unexpected packet without a segment number: "+a.getName().toUri())}catch(m){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(m))}};
+SegmentFetcher.prototype.onValidationFailed=function(a,b){try{this.onError(SegmentFetcher.ErrorCode.SEGMENT_VERIFICATION_FAILED,"Segment verification failed for "+a.getName().toUri()+" . Reason: "+b)}catch(c){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(c))}};SegmentFetcher.prototype.onTimeout=function(a){try{this.onError(SegmentFetcher.ErrorCode.INTEREST_TIMEOUT,"Time out for interest "+a.getName().toUri())}catch(b){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(b))}};
+SegmentFetcher.endsWithSegmentNumber=function(a){return 1<=a.size()&&a.get(-1).isSegment()};var Transport=function(){};exports.Transport=Transport;Transport.ConnectionInfo=function(){};Transport.prototype.isLocal=function(a,b,c){c("Transport.isLocal is not implemented")};
+var MicroForwarderTransport=function(){Transport.call(this);this.onReceivedObject=this.connectionInfo=this.elementReader=null;var a=this;window.addEventListener("message",function(b){if(b.source==window&&b.data.type&&"FromMicroForwarderStub"==b.data.type)if(b=b.data.object,b.type&&"Buffer"==b.type){if(null!=a.elementReader)a.elementReader.onReceivedData(new Buffer(b.data))}else if(a.onReceivedObject)a.onReceivedObject(b)},!1)};MicroForwarderTransport.prototype=new Transport;
+MicroForwarderTransport.prototype.name="MicroForwarderTransport";MicroForwarderTransport.ConnectionInfo=function(){Transport.ConnectionInfo.call(this)};MicroForwarderTransport.ConnectionInfo.prototype=new Transport.ConnectionInfo;MicroForwarderTransport.ConnectionInfo.prototype.name="MicroForwarderTransport.ConnectionInfo";MicroForwarderTransport.ConnectionInfo.prototype.equals=function(a){return null==a?!1:!0};MicroForwarderTransport.ConnectionInfo.prototype.toString=function(){return"{}"};
+MicroForwarderTransport.prototype.setOnReceivedObject=function(a){this.onReceivedObject=a};MicroForwarderTransport.prototype.isLocal=function(a,b,c){b(!0)};MicroForwarderTransport.prototype.connect=function(a,b,c,d){this.elementReader=new ElementReader(b);this.connectionInfo=a;c()};MicroForwarderTransport.prototype.sendObject=function(a){window.postMessage({type:"FromMicroForwarderTransport",object:a},"*")};
+MicroForwarderTransport.prototype.send=function(a){null==this.connectionInfo?console.log("MicroForwarderTransport connection is not established."):this.sendObject(a.toJSON())};var RuntimePortTransport=function(a){Transport.call(this);this.connectionInfo=this.elementReader=null;this.onReceivedObject=a;this.port=null};RuntimePortTransport.prototype=new Transport;RuntimePortTransport.prototype.name="RuntimePortTransport";
+RuntimePortTransport.ConnectionInfo=function(a){Transport.ConnectionInfo.call(this);this.port=a};RuntimePortTransport.ConnectionInfo.prototype=new Transport.ConnectionInfo;RuntimePortTransport.ConnectionInfo.prototype.name="RuntimePortTransport.ConnectionInfo";RuntimePortTransport.ConnectionInfo.prototype.equals=function(a){return null==a||void 0==a.port?!1:this.port==a.port};RuntimePortTransport.ConnectionInfo.prototype.toString=function(){return"{}"};
+RuntimePortTransport.prototype.setOnReceivedObject=function(a){this.onReceivedObject=a};RuntimePortTransport.prototype.isLocal=function(a,b,c){b(!0)};
+RuntimePortTransport.prototype.connect=function(a,b,c,d){this.elementReader=new ElementReader(b);this.connectionInfo=a;this.port=this.connectionInfo.port;var e=this;this.port.onMessage.addListener(function(a){if("Buffer"==a.type)e.elementReader.onReceivedData(Buffer.isBuffer(a.data)?a.data:new Buffer(a.data));else if(null!=e.onReceivedObject)e.onReceivedObject(a)});this.port.onDisconnect.addListener(function(){e.port=null;null!=d&&d()});c()};
+RuntimePortTransport.prototype.sendObject=function(a){null==this.port?console.log("RuntimePortTransport connection is not established."):this.port.postMessage(a)};RuntimePortTransport.prototype.send=function(a){this.sendObject(a.toJSON())};
+var ElementReader=require("../encoding/element-reader.js").ElementReader,LOG=require("../log.js").Log.LOG,Transport=require("./transport.js").Transport,Face,WebSocketTransport=function WebSocketTransport(){Transport.call(this);if(!WebSocket)throw Error("WebSocket support is not available on this platform.");this.elementReader=this.connectionInfo=this.ws=null;this.defaultGetConnectionInfo=Face.makeShuffledHostGetConnectionInfo("A.ws.ndn.ucla.edu B.ws.ndn.ucla.edu C.ws.ndn.ucla.edu D.ws.ndn.ucla.edu E.ws.ndn.ucla.edu F.ws.ndn.ucla.edu G.ws.ndn.ucla.edu H.ws.ndn.ucla.edu I.ws.ndn.ucla.edu J.ws.ndn.ucla.edu K.ws.ndn.ucla.edu L.ws.ndn.ucla.edu M.ws.ndn.ucla.edu N.ws.ndn.ucla.edu".split(" "),
+9696,function(b,c){return new WebSocketTransport.ConnectionInfo(b,c)})};WebSocketTransport.prototype=new Transport;WebSocketTransport.prototype.name="WebSocketTransport";WebSocketTransport.importFace=function(a){Face=a};exports.WebSocketTransport=WebSocketTransport;WebSocketTransport.ConnectionInfo=function(a,b){Transport.ConnectionInfo.call(this);this.host=a;this.port=void 0!==b?b:9696};WebSocketTransport.ConnectionInfo.prototype=new Transport.ConnectionInfo;
+WebSocketTransport.ConnectionInfo.prototype.name="WebSocketTransport.ConnectionInfo";WebSocketTransport.ConnectionInfo.prototype.equals=function(a){return null==a||void 0==a.host||void 0==a.port?!1:this.host==a.host&&this.port==a.port};WebSocketTransport.ConnectionInfo.prototype.toString=function(){return this.hostIsUri()?"{ uri: "+this.host+" }":"{ host: "+this.host+", port: "+this.port+" }"};
+WebSocketTransport.ConnectionInfo.prototype.hostIsUri=function(){return"ws:"==this.host.substr(0,3)||"wss:"==this.host.substr(0,4)};WebSocketTransport.prototype.isLocal=function(a,b,c){b(!1)};
+WebSocketTransport.prototype.connect=function(a,b,c,d){this.close();var e=a.hostIsUri()?a.host:"ws://"+a.host+":"+a.port;this.ws=new WebSocket(e);0<LOG&&console.log("ws connection created.");this.connectionInfo=a;this.ws.binaryType="arraybuffer";this.elementReader=new ElementReader(b);var f=this;this.ws.onmessage=function(a){a=a.data;if(null==a||void 0==a||""==a)console.log("INVALID ANSWER");else if(a instanceof ArrayBuffer){a=new Buffer(new Uint8Array(a));3<LOG&&console.log("BINARY RESPONSE IS "+
+a.toString("hex"));try{f.elementReader.onReceivedData(a)}catch(b){console.log("NDN.ws.onmessage exception: "+b)}}};this.ws.onopen=function(a){3<LOG&&console.log(a);3<LOG&&console.log("ws.onopen: WebSocket connection opened.");3<LOG&&console.log("ws.onopen: ReadyState: "+this.readyState);c()};this.ws.onerror=function(a){console.log("ws.onerror: ReadyState: "+this.readyState);console.log(a);console.log("ws.onerror: WebSocket error: "+a.data)};this.ws.onclose=function(a){console.log("ws.onclose: WebSocket connection closed.");
+f.ws=null;null!=d&&d()}};WebSocketTransport.prototype.connectByFace=function(a,b){this.connect(a.connectionInfo,a,b,function(){a.closeByTransport()})};WebSocketTransport.prototype.send=function(a){if(null!=this.ws){var b=new Uint8Array(a.length);b.set(a);this.ws.send(b.buffer);3<LOG&&console.log("ws.send() returned.")}else console.log("WebSocket connection is not established.")};WebSocketTransport.prototype.close=function(){null!=this.ws&&delete this.ws};exports.TcpTransport=require("./transport/web-socket-transport").WebSocketTransport;
+Blob=require("./util/blob.js").Blob;DataUtils=require("./encoding/data-utils.js").DataUtils;LOG=require("./log.js").Log.LOG;DecodingException=require("./encoding/decoding-exception.js").DecodingException;
+Name=function Name(b){if("string"==typeof b)3<LOG&&console.log("Content Name String "+b),this.components=Name.createNameArray(b);else if("object"===typeof b)if(this.components=[],b instanceof Name)this.append(b);else for(var c=0;c<b.length;++c)this.append(b[c]);else null==b?this.components=[]:1<LOG&&console.log("NO CONTENT NAME GIVEN");this.changeCount=0};exports.Name=Name;
+Name.Component=function(a){"object"===typeof a&&a instanceof Name.Component?(this.value_=a.value_,this.type_=a.type_):(this.value_=a?"object"===typeof a&&"undefined"!==typeof ArrayBuffer&&a instanceof ArrayBuffer?new Blob(new Buffer(new Uint8Array(a)),!1):"object"===typeof a&&a instanceof Blob?a:new Blob(a):new Blob([]),this.type_=Name.Component.ComponentType.GENERIC)};Name.Component.ComponentType={IMPLICIT_SHA256_DIGEST:1,GENERIC:8};Name.Component.prototype.getValue=function(){return this.value_};
+Name.Component.prototype.getValueAsBuffer=function(){return this.value_.buf()};Object.defineProperty(Name.Component.prototype,"value",{get:function(){return this.getValueAsBuffer()}});Name.Component.prototype.toEscapedString=function(){return this.type_===Name.Component.ComponentType.IMPLICIT_SHA256_DIGEST?"sha256digest="+this.value_.toHex():Name.toEscapedString(this.value_.buf())};Name.Component.prototype.isSegment=function(){return 1<=this.value_.size()&&0==this.value_.buf()[0]&&this.isGeneric()};
+Name.Component.prototype.isSegmentOffset=function(){return 1<=this.value_.size()&&251==this.value_.buf()[0]&&this.isGeneric()};Name.Component.prototype.isVersion=function(){return 1<=this.value_.size()&&253==this.value_.buf()[0]&&this.isGeneric()};Name.Component.prototype.isTimestamp=function(){return 1<=this.value_.size()&&252==this.value_.buf()[0]&&this.isGeneric()};Name.Component.prototype.isSequenceNumber=function(){return 1<=this.value_.size()&&254==this.value_.buf()[0]&&this.isGeneric()};
+Name.Component.prototype.isGeneric=function(){return this.type_===Name.Component.ComponentType.GENERIC};Name.Component.prototype.isImplicitSha256Digest=function(){return this.type_===Name.Component.ComponentType.IMPLICIT_SHA256_DIGEST};Name.Component.prototype.toNumber=function(){return DataUtils.bigEndianToUnsignedInt(this.value_.buf())};
+Name.Component.prototype.toNumberWithMarker=function(a){if(0==this.value_.size()||this.value_.buf()[0]!=a)throw Error("Name component does not begin with the expected marker");return DataUtils.bigEndianToUnsignedInt(this.value_.buf().slice(1))};Name.Component.prototype.toSegment=function(){return this.toNumberWithMarker(0)};Name.Component.prototype.toSegmentOffset=function(){return this.toNumberWithMarker(251)};Name.Component.prototype.toVersion=function(){return this.toNumberWithMarker(253)};
+Name.Component.prototype.toTimestamp=function(){return this.toNumberWithMarker(252)};Name.Component.prototype.toSequenceNumber=function(){return this.toNumberWithMarker(254)};Name.Component.fromNumber=function(a){var b=new TlvEncoder(8);b.writeNonNegativeInteger(a);return new Name.Component(new Blob(b.getOutput(),!1))};
+Name.Component.fromNumberWithMarker=function(a,b){var c=new TlvEncoder(9);c.writeNonNegativeInteger(a);c.writeNonNegativeInteger(b);return new Name.Component(new Blob(c.getOutput(),!1))};Name.Component.fromSegment=function(a){return Name.Component.fromNumberWithMarker(a,0)};Name.Component.fromSegmentOffset=function(a){return Name.Component.fromNumberWithMarker(a,251)};Name.Component.fromVersion=function(a){return Name.Component.fromNumberWithMarker(a,253)};
+Name.Component.fromTimestamp=function(a){return Name.Component.fromNumberWithMarker(a,252)};Name.Component.fromSequenceNumber=function(a){return Name.Component.fromNumberWithMarker(a,254)};
+Name.Component.fromImplicitSha256Digest=function(a){digestBlob="object"===typeof a&&a instanceof Blob?a:new Blob(a,!0);if(32!==digestBlob.size())throw new DecodingException("Name.Component.fromImplicitSha256Digest: The digest length must be 32 bytes");a=new Name.Component(digestBlob);a.type_=Name.Component.ComponentType.IMPLICIT_SHA256_DIGEST;return a};
+Name.Component.prototype.getSuccessor=function(){for(var a=new Buffer(this.value_.size()+1),b=!0,c=this.value_.size()-1;0<=c;--c)b?(a[c]=this.value_.buf()[c]+1&255,b=0===a[c]):a[c]=this.value_.buf()[c];b?a[a.length-1]=0:a=a.slice(0,this.value_.size());return new Name.Component(new Blob(a,!1))};Name.Component.prototype.equals=function(a){return"object"===typeof a&&a instanceof Name.Component&&this.value_.equals(a.value_)&&this.type_===a.type_};
+Name.Component.prototype.compare=function(a){return this.type_<a.type_?-1:this.type_>a.type_?1:Name.Component.compareBuffers(this.value_.buf(),a.value_.buf())};Name.Component.compareBuffers=function(a,b){if(a.length<b.length)return-1;if(a.length>b.length)return 1;for(var c=0;c<a.length;++c){if(a[c]<b[c])return-1;if(a[c]>b[c])return 1}return 0};Name.prototype.getName=function(){return this.toUri()};
+Name.createNameArray=function(a){a=a.trim();if(0>=a.length)return[];var b=a.indexOf(":");if(0<=b){var c=a.indexOf("/");if(0>c||b<c)a=a.substr(b+1,a.length-b-1).trim()}if("/"==a[0])if(2<=a.length&&"/"==a[1]){b=a.indexOf("/",2);if(0>b)return[];a=a.substr(b+1,a.length-b-1).trim()}else a=a.substr(1,a.length-1).trim();a=a.split("/");for(b=0;b<a.length;++b)"sha256digest="==a[b].substr(0,13)?(c=a[b].substr(13).trim(),c=Name.Component.fromImplicitSha256Digest(new Blob(new Buffer(c,"hex")),!1)):c=new Name.Component(Name.fromEscapedString(a[b])),
+c.getValue().isNull()?(a.splice(b,1),--b):a[b]=c;return a};Name.prototype.set=function(a){this.components=Name.createNameArray(a);++this.changeCount};
+Name.prototype.append=function(a){if("object"==typeof a&&a instanceof Name){a=a==this?this.components.slice(0,this.components.length):a.components;for(var b=0;b<a.length;++b)this.components.push(new Name.Component(a[b]))}else"object"===typeof a&&a instanceof Name.Component?this.components.push(a):this.components.push(new Name.Component(a));++this.changeCount;return this};Name.prototype.add=function(a){return this.append(a)};Name.prototype.clear=function(){this.components=[];++this.changeCount};
+Name.prototype.toUri=function(a){if(0==this.size())return a?"ndn:/":"/";a=a?"ndn:":"";for(var b=0;b<this.size();++b)a+="/"+this.components[b].toEscapedString();return a};Name.prototype.to_uri=function(){return this.toUri()};Name.prototype.toString=function(){return this.toUri()};Name.prototype.appendSegment=function(a){return this.append(Name.Component.fromSegment(a))};Name.prototype.appendSegmentOffset=function(a){return this.append(Name.Component.fromSegmentOffset(a))};
+Name.prototype.appendVersion=function(a){return this.append(Name.Component.fromVersion(a))};Name.prototype.appendTimestamp=function(a){return this.append(Name.Component.fromTimestamp(a))};Name.prototype.appendSequenceNumber=function(a){return this.append(Name.Component.fromSequenceNumber(a))};Name.prototype.appendImplicitSha256Digest=function(a){return this.append(Name.Component.fromImplicitSha256Digest(a))};Name.prototype.addSegment=function(a){return this.appendSegment(a)};
+Name.prototype.getSubName=function(a,b){0>a&&(a=this.components.length- -a);void 0==b&&(b=this.components.length-a);for(var c=new Name,d=a+b,e=a;e<d&&e<this.components.length;++e)c.components.push(this.components[e]);return c};Name.prototype.getPrefix=function(a){return 0>a?this.getSubName(0,this.components.length+a):this.getSubName(0,a)};Name.prototype.cut=function(a){return new Name(this.components.slice(0,this.components.length-a))};Name.prototype.size=function(){return this.components.length};
+Name.prototype.get=function(a){if(0<=a){if(a>=this.components.length)throw Error("Name.get: Index is out of bounds");return this.components[a]}if(a<-this.components.length)throw Error("Name.get: Index is out of bounds");return this.components[this.components.length- -a]};Name.prototype.getComponentCount=function(){return this.components.length};Name.prototype.getComponent=function(a){return new Buffer(this.components[a].getValue().buf())};
+Name.prototype.indexOfFileName=function(){for(var a=this.size()-1;0<=a;--a){var b=this.components[a].getValue().buf();if(!(0>=b.length||0==b[0]||192==b[0]||193==b[0]||245<=b[0]&&255>=b[0]))return a}return-1};Name.prototype.wireEncode=function(a){a=a||WireFormat.getDefaultWireFormat();return a.encodeName(this)};Name.prototype.wireDecode=function(a,b){b=b||WireFormat.getDefaultWireFormat();"object"===typeof a&&a instanceof Blob?b.decodeName(this,a.buf(),!1):b.decodeName(this,a,!0)};
+Name.prototype.compare=function(a,b,c,d,e){a instanceof Name&&(c=a,a=0,b=this.size());void 0==d&&(d=0);void 0==e&&(e=c.size());0>a&&(a=this.size()- -a);0>d&&(d=c.size()- -d);b=Math.min(b,this.size()-a);e=Math.min(e,c.size()-d);for(var f=Math.min(b,e),g=0;g<f;++g){var h=this.components[a+g].compare(c.components[d+g]);if(0!=h)return h}return b<e?-1:b>e?1:0};
+Name.prototype.equals=function(a){if(this.components.length!=a.components.length)return!1;for(var b=this.components.length-1;0<=b;--b)if(!this.components[b].equals(a.components[b]))return!1;return!0};Name.prototype.equalsName=function(a){return this.equals(a)};Name.prototype.getContentDigestValue=function(){for(var a=this.size()-1;0<=a;--a){var b=Name.getComponentContentDigestValue(this.components[a]);if(null!=b)return b}return null};
+Name.getComponentContentDigestValue=function(a){"object"==typeof a&&a instanceof Name.Component&&(a=a.getValue().buf());return a.length==Name.ContentDigestPrefix.length+32+Name.ContentDigestSuffix.length&&DataUtils.arraysEqual(a.slice(0,Name.ContentDigestPrefix.length),Name.ContentDigestPrefix)&&DataUtils.arraysEqual(a.slice(a.length-Name.ContentDigestSuffix.length,a.length),Name.ContentDigestSuffix)?a.slice(Name.ContentDigestPrefix.length,Name.ContentDigestPrefix.length+32):null};
+Name.ContentDigestPrefix=new Buffer([193,46,77,46,71,193,1,170,2,133]);Name.ContentDigestSuffix=new Buffer([0]);
+Name.toEscapedString=function(a){"object"==typeof a&&a instanceof Name.Component?a=a.getValue().buf():"object"===typeof a&&a instanceof Blob&&(a=a.buf());for(var b="",c=!1,d=0;d<a.length;++d)if(46!=a[d]){c=!0;break}if(c)for(d=0;d<a.length;++d)c=a[d],b=48<=c&&57>=c||65<=c&&90>=c||97<=c&&122>=c||43==c||45==c||46==c||95==c?b+String.fromCharCode(c):b+("%"+(16>c?"0":"")+c.toString(16).toUpperCase());else for(b="...",d=0;d<a.length;++d)b+=".";return b};
+Name.fromEscapedString=function(a){a=unescape(a.trim());return null==a.match(/[^.]/)?2>=a.length?new Blob:new Blob(DataUtils.toNumbersFromString(a.substr(3,a.length-3)),!1):new Blob(DataUtils.toNumbersFromString(a),!1)};Name.fromEscapedStringAsBuffer=function(a){return Name.fromEscapedString(a).buf()};Name.prototype.getSuccessor=function(){if(0==this.size()){var a=new Name;a.append(new Blob(new Buffer([0]),!1));return a}return this.getPrefix(-1).append(this.get(-1).getSuccessor())};
+Name.prototype.match=function(a){var b=this.components;a=a.components;if(b.length>a.length)return!1;for(var c=b.length-1;0<=c;--c)if(!b[c].equals(a[c]))return!1;return!0};Name.prototype.isPrefixOf=function(a){return this.match(a)};Name.prototype.getChangeCount=function(){return this.changeCount};
+var TlvEncoder=require("./encoding/tlv/tlv-encoder.js").TlvEncoder,WireFormat=require("./encoding/wire-format.js").WireFormat,Blob=require("./util/blob.js").Blob,ChangeCounter=require("./util/change-counter.js").ChangeCounter,Name=require("./name.js").Name,KeyLocatorType={KEYNAME:1,KEY_LOCATOR_DIGEST:2};exports.KeyLocatorType=KeyLocatorType;
+var KeyLocator=function KeyLocator(b,c){"object"===typeof b&&b instanceof KeyLocator?(this.type_=b.type_,this.keyName_=new ChangeCounter(new Name(b.getKeyName())),this.keyData_=b.keyData_):(this.type_=c,this.keyName_=new ChangeCounter(new Name),this.keyData_=new Blob,c==KeyLocatorType.KEYNAME?this.keyName_.set("object"===typeof b&&b instanceof Name?new Name(b):new Name):c==KeyLocatorType.KEY_LOCATOR_DIGEST&&(this.keyData_=new Blob(b)));this.changeCount_=0};exports.KeyLocator=KeyLocator;
+KeyLocator.prototype.getType=function(){return this.type_};KeyLocator.prototype.getKeyName=function(){return this.keyName_.get()};KeyLocator.prototype.getKeyData=function(){return this.keyData_};KeyLocator.prototype.getKeyDataAsBuffer=function(){return this.getKeyData().buf()};KeyLocator.prototype.setType=function(a){this.type_=a;++this.changeCount_};KeyLocator.prototype.setKeyName=function(a){this.keyName_.set("object"===typeof a&&a instanceof Name?new Name(a):new Name);++this.changeCount_};
+KeyLocator.prototype.setKeyData=function(a){this.keyData_="object"===typeof a&&a instanceof Blob?a:new Blob(a);++this.changeCount_};KeyLocator.prototype.clear=function(){this.type_=null;this.keyName_.set(new Name);this.keyData_=new Blob;++this.changeCount_};
+KeyLocator.prototype.equals=function(a){if(this.type_!=a.type_)return!1;if(this.type_==KeyLocatorType.KEYNAME){if(!this.getKeyName().equals(a.getKeyName()))return!1}else if(this.type_==KeyLocatorType.KEY_LOCATOR_DIGEST&&!this.getKeyData().equals(a.getKeyData()))return!1;return!0};KeyLocator.canGetFromSignature=function(a){return a instanceof Sha256WithRsaSignature||a instanceof Sha256WithEcdsaSignature||a instanceof HmacWithSha256Signature};
+KeyLocator.getFromSignature=function(a){if(a instanceof Sha256WithRsaSignature||a instanceof Sha256WithEcdsaSignature||a instanceof HmacWithSha256Signature)return a.getKeyLocator();throw Error("KeyLocator.getFromSignature: Signature type does not have a KeyLocator");};KeyLocator.prototype.getChangeCount=function(){this.keyName_.checkChanged()&&++this.changeCount_;return this.changeCount_};Object.defineProperty(KeyLocator.prototype,"type",{get:function(){return this.getType()},set:function(a){this.setType(a)}});
+Object.defineProperty(KeyLocator.prototype,"keyData",{get:function(){return this.getKeyDataAsBuffer()},set:function(a){this.setKeyData(a)}});var Sha256WithRsaSignature=require("./sha256-with-rsa-signature.js").Sha256WithRsaSignature,Sha256WithEcdsaSignature=require("./sha256-with-ecdsa-signature.js").Sha256WithEcdsaSignature,HmacWithSha256Signature=require("./hmac-with-sha256-signature.js").HmacWithSha256Signature,Name=require("./name.js").Name,ContentType={BLOB:0,LINK:1,KEY:2,NACK:3,OTHER_CODE:32767};
+exports.ContentType=ContentType;
+var MetaInfo=function MetaInfo(b,c,d,e,f,g){if(c)throw Error("MetaInfo constructor: timestamp support has been removed.");if(e)throw Error("MetaInfo constructor: locator support has been removed.");if("object"===typeof b&&b instanceof MetaInfo)this.publisher_=b.publisher_,this.type_=b.type_,this.otherTypeCode_=b.otherTypeCode_,this.freshnessPeriod_=b.freshnessPeriod_,this.finalBlockId_=b.finalBlockId_;else{if(b)throw Error("MetaInfo constructor: publisher support has been removed.");this.type=null==
+d||0>d?ContentType.BLOB:d;this.otherTypeCode_=-1;this.freshnessSeconds=f;this.finalBlockID=g}this.changeCount_=0};exports.MetaInfo=MetaInfo;MetaInfo.prototype.getType=function(){return this.type_};MetaInfo.prototype.getOtherTypeCode=function(){return this.otherTypeCode_};MetaInfo.prototype.getFreshnessPeriod=function(){return this.freshnessPeriod_};MetaInfo.prototype.getFinalBlockId=function(){return this.finalBlockId_};MetaInfo.prototype.getFinalBlockID=function(){return this.getFinalBlockId()};
+MetaInfo.prototype.getFinalBlockIDAsBuffer=function(){return this.finalBlockId_.getValue().buf()};MetaInfo.prototype.setType=function(a){this.type_=null==a||0>a?ContentType.BLOB:a;++this.changeCount_};MetaInfo.prototype.setOtherTypeCode=function(a){if(0>a)throw Error("MetaInfo other type code must be non-negative");this.otherTypeCode_=a;++this.changeCount_};MetaInfo.prototype.setFreshnessPeriod=function(a){this.freshnessPeriod_=null==a||0>a?null:a;++this.changeCount_};
+MetaInfo.prototype.setFinalBlockId=function(a){this.finalBlockId_="object"===typeof a&&a instanceof Name.Component?a:new Name.Component(a);++this.changeCount_};MetaInfo.prototype.setFinalBlockID=function(a){this.setFinalBlockId(a)};MetaInfo.prototype.getChangeCount=function(){return this.changeCount_};Object.defineProperty(MetaInfo.prototype,"type",{get:function(){return this.getType()},set:function(a){this.setType(a)}});
+Object.defineProperty(MetaInfo.prototype,"freshnessSeconds",{get:function(){return null==this.freshnessPeriod_||0>this.freshnessPeriod_?null:this.freshnessPeriod_/1E3},set:function(a){this.freshnessPeriod_=null==a||0>a?null:1E3*a;++this.changeCount_}});Object.defineProperty(MetaInfo.prototype,"finalBlockID",{get:function(){return this.getFinalBlockIDAsBuffer()},set:function(a){this.setFinalBlockId(a)}});Blob=require("./util/blob.js").Blob;ChangeCounter=require("./util/change-counter.js").ChangeCounter;
+KeyLocator=require("./key-locator.js").KeyLocator;Sha256WithEcdsaSignature=function Sha256WithEcdsaSignature(b){"object"===typeof b&&b instanceof Sha256WithEcdsaSignature?(this.keyLocator_=new ChangeCounter(new KeyLocator(b.getKeyLocator())),this.signature_=b.signature_):(this.keyLocator_=new ChangeCounter(new KeyLocator),this.signature_=new Blob);this.changeCount_=0};exports.Sha256WithEcdsaSignature=Sha256WithEcdsaSignature;Sha256WithEcdsaSignature.prototype.clone=function(){return new Sha256WithEcdsaSignature(this)};
+Sha256WithEcdsaSignature.prototype.getKeyLocator=function(){return this.keyLocator_.get()};Sha256WithEcdsaSignature.prototype.getSignature=function(){return this.signature_};Sha256WithEcdsaSignature.prototype.setKeyLocator=function(a){this.keyLocator_.set("object"===typeof a&&a instanceof KeyLocator?new KeyLocator(a):new KeyLocator);++this.changeCount_};Sha256WithEcdsaSignature.prototype.setSignature=function(a){this.signature_="object"===typeof a&&a instanceof Blob?a:new Blob(a);++this.changeCount_};
+Sha256WithEcdsaSignature.prototype.getChangeCount=function(){this.keyLocator_.checkChanged()&&++this.changeCount_;return this.changeCount_};Blob=require("./util/blob.js").Blob;ChangeCounter=require("./util/change-counter.js").ChangeCounter;KeyLocator=require("./key-locator.js").KeyLocator;
+Sha256WithRsaSignature=function Sha256WithRsaSignature(b){"object"===typeof b&&b instanceof Sha256WithRsaSignature?(this.keyLocator_=new ChangeCounter(new KeyLocator(b.getKeyLocator())),this.signature_=b.signature_):(this.keyLocator_=new ChangeCounter(new KeyLocator),this.signature_=new Blob);this.changeCount_=0};exports.Sha256WithRsaSignature=Sha256WithRsaSignature;Sha256WithRsaSignature.prototype.clone=function(){return new Sha256WithRsaSignature(this)};
+Sha256WithRsaSignature.prototype.getKeyLocator=function(){return this.keyLocator_.get()};Sha256WithRsaSignature.prototype.getSignature=function(){return this.signature_};Sha256WithRsaSignature.prototype.getSignatureAsBuffer=function(){return this.signature_.buf()};Sha256WithRsaSignature.prototype.setKeyLocator=function(a){this.keyLocator_.set("object"===typeof a&&a instanceof KeyLocator?new KeyLocator(a):new KeyLocator);++this.changeCount_};
+Sha256WithRsaSignature.prototype.setSignature=function(a){this.signature_="object"===typeof a&&a instanceof Blob?a:new Blob(a);++this.changeCount_};Sha256WithRsaSignature.prototype.getChangeCount=function(){this.keyLocator_.checkChanged()&&++this.changeCount_;return this.changeCount_};Object.defineProperty(Sha256WithRsaSignature.prototype,"keyLocator",{get:function(){return this.getKeyLocator()},set:function(a){this.setKeyLocator(a)}});
+Object.defineProperty(Sha256WithRsaSignature.prototype,"signature",{get:function(){return this.getSignatureAsBuffer()},set:function(a){this.setSignature(a)}});
+var Blob=require("./util/blob.js").Blob,GenericSignature=function GenericSignature(b){"object"===typeof b&&b instanceof GenericSignature?(this.signature_=b.signature_,this.signatureInfoEncoding_=b.signatureInfoEncoding_,this.typeCode_=b.typeCode_):(this.signature_=new Blob,this.signatureInfoEncoding_=new Blob,this.typeCode_=null);this.changeCount_=0};exports.GenericSignature=GenericSignature;GenericSignature.prototype.clone=function(){return new GenericSignature(this)};
+GenericSignature.prototype.getSignature=function(){return this.signature_};GenericSignature.prototype.getSignatureAsBuffer=function(){return this.signature_.buf()};GenericSignature.prototype.getSignatureInfoEncoding=function(){return this.signatureInfoEncoding_};GenericSignature.prototype.getTypeCode=function(){return this.typeCode_};GenericSignature.prototype.setSignature=function(a){this.signature_="object"===typeof a&&a instanceof Blob?a:new Blob(a);++this.changeCount_};
+GenericSignature.prototype.setSignatureInfoEncoding=function(a,b){this.signatureInfoEncoding_="object"===typeof a&&a instanceof Blob?a:new Blob(a);this.typeCode_=b;++this.changeCount_};GenericSignature.prototype.getChangeCount=function(){return this.changeCount_};Object.defineProperty(GenericSignature.prototype,"signature",{get:function(){return this.getSignatureAsBuffer()},set:function(a){this.setSignature(a)}});Blob=require("./util/blob.js").Blob;ChangeCounter=require("./util/change-counter.js").ChangeCounter;
+KeyLocator=require("./key-locator.js").KeyLocator;HmacWithSha256Signature=function HmacWithSha256Signature(b){"object"===typeof b&&b instanceof HmacWithSha256Signature?(this.keyLocator_=new ChangeCounter(new KeyLocator(b.getKeyLocator())),this.signature_=b.signature_):(this.keyLocator_=new ChangeCounter(new KeyLocator),this.signature_=new Blob);this.changeCount_=0};exports.HmacWithSha256Signature=HmacWithSha256Signature;HmacWithSha256Signature.prototype.clone=function(){return new HmacWithSha256Signature(this)};
+HmacWithSha256Signature.prototype.getKeyLocator=function(){return this.keyLocator_.get()};HmacWithSha256Signature.prototype.getSignature=function(){return this.signature_};HmacWithSha256Signature.prototype.getSignatureAsBuffer=function(){return this.signature_.buf()};HmacWithSha256Signature.prototype.setKeyLocator=function(a){this.keyLocator_.set("object"===typeof a&&a instanceof KeyLocator?new KeyLocator(a):new KeyLocator);++this.changeCount_};
+HmacWithSha256Signature.prototype.setSignature=function(a){this.signature_="object"===typeof a&&a instanceof Blob?a:new Blob(a);++this.changeCount_};HmacWithSha256Signature.prototype.getChangeCount=function(){this.keyLocator_.checkChanged()&&++this.changeCount_;return this.changeCount_};Object.defineProperty(HmacWithSha256Signature.prototype,"keyLocator",{get:function(){return this.getKeyLocator()},set:function(a){this.setKeyLocator(a)}});
+Object.defineProperty(HmacWithSha256Signature.prototype,"signature",{get:function(){return this.getSignatureAsBuffer()},set:function(a){this.setSignature(a)}});var Blob=require("./util/blob.js").Blob,DigestSha256Signature=function DigestSha256Signature(b){this.signature_="object"===typeof b&&b instanceof DigestSha256Signature?b.signature_:new Blob;this.changeCount_=0};exports.DigestSha256Signature=DigestSha256Signature;DigestSha256Signature.prototype.clone=function(){return new DigestSha256Signature(this)};
+DigestSha256Signature.prototype.getSignature=function(){return this.signature_};DigestSha256Signature.prototype.setSignature=function(a){this.signature_="object"===typeof a&&a instanceof Blob?a:new Blob(a);++this.changeCount_};DigestSha256Signature.prototype.getChangeCount=function(){return this.changeCount_};Object.defineProperty(DigestSha256Signature.prototype,"signature",{get:function(){return this.getSignature()},set:function(a){this.setSignature(a)}});
+var Blob=require("./util/blob.js").Blob,SignedBlob=require("./util/signed-blob.js").SignedBlob,ChangeCounter=require("./util/change-counter.js").ChangeCounter,Name=require("./name.js").Name,Sha256WithRsaSignature=require("./sha256-with-rsa-signature.js").Sha256WithRsaSignature,MetaInfo=require("./meta-info.js").MetaInfo,IncomingFaceId=require("./lp/incoming-face-id.js").IncomingFaceId,WireFormat=require("./encoding/wire-format.js").WireFormat,Crypto=require("./crypto.js"),Data=function Data(b,c,d){b instanceof
+Data?(this.name_=new ChangeCounter(new Name(b.getName())),this.metaInfo_=new ChangeCounter(new MetaInfo(b.getMetaInfo())),this.signature_=new ChangeCounter(b.getSignature().clone()),this.content_=b.content_,this.defaultWireEncoding_=b.getDefaultWireEncoding(),this.defaultFullName_=b.defaultFullName_,this.defaultWireEncodingFormat_=b.defaultWireEncodingFormat_):(this.name_="string"===typeof b?new ChangeCounter(new Name(b)):new ChangeCounter("object"===typeof b&&b instanceof Name?new Name(b):new Name),
+"object"===typeof c&&c instanceof MetaInfo?(b=c,c=d):b=null,this.metaInfo_=new ChangeCounter("object"===typeof b&&b instanceof MetaInfo?new MetaInfo(b):new MetaInfo),this.content_="object"===typeof c&&c instanceof Blob?c:new Blob(c,!0),this.signature_=new ChangeCounter(new Sha256WithRsaSignature),this.defaultWireEncoding_=new SignedBlob,this.defaultFullName_=new Name,this.defaultWireEncodingFormat_=null);this.changeCount_=this.getDefaultWireEncodingChangeCount_=0;this.lpPacket_=null};
+exports.Data=Data;Data.prototype.getName=function(){return this.name_.get()};Data.prototype.getMetaInfo=function(){return this.metaInfo_.get()};Data.prototype.getSignature=function(){return this.signature_.get()};Data.prototype.getContent=function(){return this.content_};Data.prototype.getContentAsBuffer=function(){return this.content_.buf()};
+Data.prototype.getDefaultWireEncoding=function(){this.getDefaultWireEncodingChangeCount_!=this.getChangeCount()&&(this.defaultWireEncoding_=new SignedBlob,this.defaultWireEncodingFormat_=null,this.getDefaultWireEncodingChangeCount_=this.getChangeCount());return this.defaultWireEncoding_};Data.prototype.getDefaultWireEncodingFormat=function(){return this.defaultWireEncodingFormat_};
+Data.prototype.getIncomingFaceId=function(){var a=null===this.lpPacket_?null:IncomingFaceId.getFirstHeader(this.lpPacket_);return null===a?null:a.getFaceId()};
+Data.prototype.getFullName=function(a){a=a||WireFormat.getDefaultWireFormat();if(!this.getDefaultWireEncoding().isNull()&&0<this.defaultFullName_.size()&&this.getDefaultWireEncodingFormat()==a)return this.defaultFullName_;var b=new Name(this.getName()),c=Crypto.createHash("sha256");c.update(this.wireEncode(a).buf());b.appendImplicitSha256Digest(new Blob(c.digest(),!1));a==WireFormat.getDefaultWireFormat()&&(this.defaultFullName_=b);return b};
+Data.prototype.setName=function(a){this.name_.set("object"===typeof a&&a instanceof Name?new Name(a):new Name);++this.changeCount_;return this};Data.prototype.setMetaInfo=function(a){this.metaInfo_.set("object"===typeof a&&a instanceof MetaInfo?new MetaInfo(a):new MetaInfo);++this.changeCount_;return this};Data.prototype.setSignature=function(a){this.signature_.set(null==a?new Sha256WithRsaSignature:a.clone());++this.changeCount_;return this};
+Data.prototype.setContent=function(a){this.content_="object"===typeof a&&a instanceof Blob?a:new Blob(a,!0);++this.changeCount_;return this};
+Data.prototype.wireEncode=function(a){a=a||WireFormat.getDefaultWireFormat();if(!this.getDefaultWireEncoding().isNull()&&this.getDefaultWireEncodingFormat()==a)return this.getDefaultWireEncoding();var b=a.encodeData(this),b=new SignedBlob(b.encoding,b.signedPortionBeginOffset,b.signedPortionEndOffset);a==WireFormat.getDefaultWireFormat()&&this.setDefaultWireEncoding(b,WireFormat.getDefaultWireFormat());return b};
+Data.prototype.wireDecode=function(a,b){b=b||WireFormat.getDefaultWireFormat();var c;c="object"===typeof a&&a instanceof Blob?b.decodeData(this,a.buf(),!1):b.decodeData(this,a,!0);b==WireFormat.getDefaultWireFormat()?this.setDefaultWireEncoding(new SignedBlob(new Blob(a,!0),c.signedPortionBeginOffset,c.signedPortionEndOffset),WireFormat.getDefaultWireFormat()):this.setDefaultWireEncoding(new SignedBlob,null)};Data.prototype.setLpPacket=function(a){this.lpPacket_=a;return this};
+Data.prototype.getChangeCount=function(){var a=this.name_.checkChanged(),a=this.metaInfo_.checkChanged()||a;(a=this.signature_.checkChanged()||a)&&++this.changeCount_;return this.changeCount_};Data.prototype.setDefaultWireEncoding=function(a,b){this.defaultWireEncoding_=a;this.defaultWireEncodingFormat_=b;this.getDefaultWireEncodingChangeCount_=this.getChangeCount()};Object.defineProperty(Data.prototype,"name",{get:function(){return this.getName()},set:function(a){this.setName(a)}});
+Object.defineProperty(Data.prototype,"metaInfo",{get:function(){return this.getMetaInfo()},set:function(a){this.setMetaInfo(a)}});Object.defineProperty(Data.prototype,"signature",{get:function(){return this.getSignature()},set:function(a){this.setSignature(a)}});Object.defineProperty(Data.prototype,"content",{get:function(){return this.getContentAsBuffer()},set:function(a){this.setContent(a)}});function SecurityException(a){if(a)return a.__proto__=SecurityException.prototype,a}
+SecurityException.prototype=Error();SecurityException.prototype.name="SecurityException";exports.SecurityException=SecurityException;function UnrecognizedKeyFormatException(a){SecurityException.call(this,a)}UnrecognizedKeyFormatException.prototype=new SecurityException;UnrecognizedKeyFormatException.prototype.name="UnrecognizedKeyFormatException";exports.UnrecognizedKeyFormatException=UnrecognizedKeyFormatException;function UnrecognizedDigestAlgorithmException(a){SecurityException.call(this,a)}
+UnrecognizedDigestAlgorithmException.prototype=new SecurityException;UnrecognizedDigestAlgorithmException.prototype.name="UnrecognizedDigestAlgorithmException";exports.UnrecognizedDigestAlgorithmException=UnrecognizedDigestAlgorithmException;var KeyType=function(){};exports.KeyType=KeyType;KeyType.RSA=0;KeyType.ECDSA=1;KeyType.AES=128;var KeyClass=function(){};exports.KeyClass=KeyClass;KeyClass.PUBLIC=1;KeyClass.PRIVATE=2;KeyClass.SYMMETRIC=3;var DigestAlgorithm=function(){};
+exports.DigestAlgorithm=DigestAlgorithm;DigestAlgorithm.SHA256=1;var KeyType=require("./security-types.js").KeyType,KeyParams=function(a){this.keyType=a};exports.KeyParams=KeyParams;KeyParams.prototype.getKeyType=function(){return this.keyType};var RsaKeyParams=function RsaKeyParams(b){KeyParams.call(this,RsaKeyParams.getType());null==b&&(b=RsaKeyParams.getDefaultSize());this.size=b};RsaKeyParams.prototype=new KeyParams;RsaKeyParams.prototype.name="RsaKeyParams";exports.RsaKeyParams=RsaKeyParams;
+RsaKeyParams.prototype.getKeySize=function(){return this.size};RsaKeyParams.getDefaultSize=function(){return 2048};RsaKeyParams.getType=function(){return KeyType.RSA};var EcdsaKeyParams=function EcdsaKeyParams(b){KeyParams.call(this,EcdsaKeyParams.getType());null==b&&(b=EcdsaKeyParams.getDefaultSize());this.size=b};EcdsaKeyParams.prototype=new KeyParams;EcdsaKeyParams.prototype.name="EcdsaKeyParams";exports.EcdsaKeyParams=EcdsaKeyParams;EcdsaKeyParams.prototype.getKeySize=function(){return this.size};
+EcdsaKeyParams.getDefaultSize=function(){return 256};EcdsaKeyParams.getType=function(){return KeyType.ECDSA};var AesKeyParams=function AesKeyParams(b){KeyParams.call(this,AesKeyParams.getType());null==b&&(b=AesKeyParams.getDefaultSize());this.size=b};AesKeyParams.prototype=new KeyParams;AesKeyParams.prototype.name="AesKeyParams";exports.AesKeyParams=AesKeyParams;AesKeyParams.prototype.getKeySize=function(){return this.size};AesKeyParams.getDefaultSize=function(){return 64};AesKeyParams.getType=function(){return KeyType.AES};
+var Crypto=require("../../crypto.js"),Blob=require("../../util/blob.js").Blob,DerDecodingException=require("../../encoding/der/der-decoding-exception.js").DerDecodingException,DerNode=require("../../encoding/der/der-node.js").DerNode,SecurityException=require("../security-exception.js").SecurityException,UnrecognizedKeyFormatException=require("../security-exception.js").UnrecognizedKeyFormatException,KeyType=require("../security-types.js").KeyType,DigestAlgorithm=require("../security-types.js").DigestAlgorithm,
+PublicKey=function PublicKey(b){if(b){this.keyDer=b;var c=null;try{var d=DerNode.parse(b.buf(),0).getChildren(),c=DerNode.getSequence(d,0).getChildren()[0].toVal()}catch(e){throw new UnrecognizedKeyFormatException(Error("PublicKey.decodeKeyType: Error decoding the public key: "+e.message));}c==PublicKey.RSA_ENCRYPTION_OID?this.keyType=KeyType.RSA:c==PublicKey.EC_ENCRYPTION_OID&&(this.keyType=KeyType.ECDSA)}else this.keyDer=new Blob,this.keyType=null};exports.PublicKey=PublicKey;
+PublicKey.prototype.toDer=function(){return DerNode.parse(this.keyDer.buf())};PublicKey.prototype.getKeyType=function(){return this.keyType};PublicKey.prototype.getDigest=function(a){void 0==a&&(a=DigestAlgorithm.SHA256);if(a==DigestAlgorithm.SHA256)return a=Crypto.createHash("sha256"),a.update(this.keyDer.buf()),new Blob(a.digest(),!1);throw new SecurityException(Error("Wrong format!"));};PublicKey.prototype.getKeyDer=function(){return this.keyDer};PublicKey.RSA_ENCRYPTION_OID="1.2.840.113549.1.1.1";
+PublicKey.EC_ENCRYPTION_OID="1.2.840.10045.2.1";var DerNode=require("../../encoding/der/der-node.js").DerNode,OID=require("../../encoding/oid.js").OID,CertificateExtension=function(a,b,c){this.extensionId="string"===typeof a?new OID(a):a;this.isCritical=b;this.extensionValue=c};exports.CertificateExtension=CertificateExtension;
+CertificateExtension.prototype.toDer=function(){var a=new DerNode.DerSequence,b=new DerNode.DerOid(this.extensionId),c=new DerNode.DerBoolean(this.isCritical),d=new DerNode.DerOctetString(this.extensionValue.buf());a.addChild(b);a.addChild(c);a.addChild(d);a.getSize();return a};CertificateExtension.prototype.toDerBlob=function(){return this.toDer().encode()};CertificateExtension.prototype.getOid=function(){return this.extensionId};CertificateExtension.prototype.getIsCritical=function(){return this.isCritical};
+CertificateExtension.prototype.getValue=function(){return this.extensionValue};var Blob=require("../../util/blob.js").Blob,OID=require("../../encoding/oid.js").OID,DerNode=require("../../encoding/der/der-node.js").DerNode,CertificateSubjectDescription=function(a,b){this.oid="string"===typeof a?new OID(a):a;this.value=b};exports.CertificateSubjectDescription=CertificateSubjectDescription;
+CertificateSubjectDescription.prototype.toDer=function(){var a=new DerNode.DerSequence,b=new DerNode.DerOid(this.oid),c=new DerNode.DerPrintableString((new Blob(this.value)).buf());a.addChild(b);a.addChild(c);return a};CertificateSubjectDescription.prototype.getOidString=function(){return this.oid.toString()};CertificateSubjectDescription.prototype.getValue=function(){return this.value};
+var Data=require("../../data.js").Data,ContentType=require("../../meta-info.js").ContentType,WireFormat=require("../../encoding/wire-format.js").WireFormat,DerNode=require("../../encoding/der/der-node.js").DerNode,KeyType=require("../../security/security-types.js").KeyType,PublicKey=require("./public-key.js").PublicKey,CertificateSubjectDescription=require("./certificate-subject-description.js").CertificateSubjectDescription,CertificateExtension=require("./certificate-extension.js").CertificateExtension,
+Certificate=function(a){void 0!=a?Data.call(this,a):Data.call(this);this.subjectDescriptionList=[];this.extensionList=[];this.notBefore=Number.MAX_VALUE;this.notAfter=-Number.MAX_VALUE;this.key=new PublicKey;void 0!=a&&this.decode()};Certificate.prototype=new Data;Certificate.prototype.name="Certificate";exports.Certificate=Certificate;Certificate.prototype.encode=function(){var a=this.toDer();this.setContent(a.encode());this.getMetaInfo().setType(ContentType.KEY)};
+Certificate.prototype.addSubjectDescription=function(a){this.subjectDescriptionList.push(a)};Certificate.prototype.getSubjectDescriptionList=function(){return this.subjectDescriptionList};Certificate.prototype.addExtension=function(a){this.extensionList.push(a)};Certificate.prototype.getExtensionList=function(){return this.extensionList};Certificate.prototype.setNotBefore=function(a){this.notBefore=a};Certificate.prototype.getNotBefore=function(){return this.notBefore};
+Certificate.prototype.setNotAfter=function(a){this.notAfter=a};Certificate.prototype.getNotAfter=function(){return this.notAfter};Certificate.prototype.setPublicKeyInfo=function(a){this.key=a};Certificate.prototype.getPublicKeyInfo=function(){return this.key};Certificate.prototype.isTooEarly=function(){return(new Date).getTime()<this.notBefore};Certificate.prototype.isTooLate=function(){return(new Date).getTime()>this.notAfter};
+Certificate.prototype.toDer=function(){var a=new DerNode.DerSequence,b=new DerNode.DerSequence,c=new DerNode.DerGeneralizedTime(this.notBefore),d=new DerNode.DerGeneralizedTime(this.notAfter);b.addChild(c);b.addChild(d);a.addChild(b);c=new DerNode.DerSequence;for(b=0;b<this.subjectDescriptionList.length;++b)c.addChild(this.subjectDescriptionList[b].toDer());a.addChild(c);a.addChild(this.key.toDer());if(0<this.extensionList.length){c=new DerNode.DerSequence;for(b=0;b<this.extensionList.length;++b)c.addChild(this.extensionList[b].toDer());
+a.addChild(c)}return a};
+Certificate.prototype.decode=function(){var a=DerNode.parse(this.getContent().buf()).getChildren(),b=DerNode.getSequence(a,0).getChildren();this.notBefore=b[0].toVal();this.notAfter=b[1].toVal();for(var c=DerNode.getSequence(a,1).getChildren(),b=0;b<c.length;++b){var d=DerNode.getSequence(c,b).getChildren(),e=d[0].toVal(),d=d[1].toVal().buf().toString("binary");this.addSubjectDescription(new CertificateSubjectDescription(e,d))}b=a[2].encode();this.key=new PublicKey(b);if(3<a.length)for(a=DerNode.getSequence(a,
+3).getChildren(),b=0;b<a.length;++b)d=DerNode.getSequence(a,b).getChildren(),e=d[0].toVal(),c=d[1].toVal(),d=d[2].toVal(),this.addExtension(new CertificateExtension(e,c,d))};Certificate.prototype.wireDecode=function(a,b){b=b||WireFormat.getDefaultWireFormat();Data.prototype.wireDecode.call(this,a,b);this.decode()};
+Certificate.prototype.toString=function(){var a;a="Certificate name:\n"+(" "+this.getName().toUri()+"\n");a+="Validity:\n";var b=Certificate.toIsoString(Math.round(this.notBefore)),c=Certificate.toIsoString(Math.round(this.notAfter));a=a+(" NotBefore: "+b+"\n")+(" NotAfter: "+c+"\n");for(b=0;b<this.subjectDescriptionList.length;++b)c=this.subjectDescriptionList[b],a+="Subject Description:\n",a+=" "+c.getOidString()+": "+c.getValue()+"\n";a+="Public key bits:\n";c=this.key.getKeyDer().buf().toString("base64");
+for(b=0;b<c.length;b+=64)a+=c.substring(b,Math.min(b+64,c.length))+"\n";if(0<this.extensionList.length)for(a+="Extensions:\n",b=0;b<this.extensionList.length;++b)c=this.extensionList[b],a+=" OID: "+c.getOid()+"\n",a+=" Is critical: "+(c.getIsCritical()?"Y":"N")+"\n",a+=" Value: "+c.getValue().toHex()+"\n";return a};
+Certificate.toIsoString=function(a){a=new Date(Math.round(a));return a.getUTCFullYear()+Certificate.to2DigitString(a.getUTCMonth()+1)+Certificate.to2DigitString(a.getUTCDate())+"T"+Certificate.to2DigitString(a.getUTCHours())+Certificate.to2DigitString(a.getUTCMinutes())+Certificate.to2DigitString(a.getUTCSeconds())};Certificate.to2DigitString=function(a){a=a.toString();return 1===a.length?"0"+a:a};
+var Data=require("../../data.js").Data,Name=require("../../name.js").Name,SecurityException=require("../../security//security-exception.js").SecurityException,Certificate=require("./certificate.js").Certificate,WireFormat=require("../../encoding/wire-format.js").WireFormat,IdentityCertificate=function IdentityCertificate(b){void 0!=b?Certificate.call(this,b):Certificate.call(this);this.publicKeyName=new Name;if(b instanceof IdentityCertificate)this.publicKeyName=new Name(b.publicKeyName);else if(b instanceof
+Data){if(!IdentityCertificate.isCorrectName(b.getName()))throw new SecurityException(Error("Wrong Identity Certificate Name!"));this.setPublicKeyName()}};IdentityCertificate.prototype=new Certificate;IdentityCertificate.prototype.name="IdentityCertificate";exports.IdentityCertificate=IdentityCertificate;
+IdentityCertificate.prototype.setName=function(a){if(!IdentityCertificate.isCorrectName(a))throw new SecurityException(Error("Wrong Identity Certificate Name!"));Certificate.prototype.setName.call(this,a);this.setPublicKeyName();return this};IdentityCertificate.prototype.wireDecode=function(a,b){b=b||WireFormat.getDefaultWireFormat();Certificate.prototype.wireDecode.call(this,a,b);this.setPublicKeyName()};IdentityCertificate.prototype.getPublicKeyName=function(){return this.publicKeyName};
+IdentityCertificate.isIdentityCertificate=function(a){return IdentityCertificate.isCorrectName(a.getName())};
+IdentityCertificate.certificateNameToPublicKeyName=function(a){for(var b=!1,c=a.size()-1;0<c+1;--c)if("ID-CERT"==a.get(c).toEscapedString()){b=!0;break}if(!b)throw Error("Incorrect identity certificate name "+a.toUri());for(var b=a.getSubName(0,c),c=!1,d=0;d<b.size();d++)if("KEY"==b.get(d).toEscapedString()){c=!0;break}if(!c)throw Error("Incorrect identity certificate name "+a.toUri());return b.getSubName(0,d).append(b.getSubName(d+1,b.size()-d-1))};
+IdentityCertificate.isCorrectName=function(a){for(var b=a.size()-1;0<=b&&"ID-CERT"!=a.get(b).toEscapedString();b--);if(0>b)return!1;for(b=0;b<a.size()&&"KEY"!=a.get(b).toEscapedString();b++);return b>=a.size()?!1:!0};IdentityCertificate.prototype.setPublicKeyName=function(){this.publicKeyName=IdentityCertificate.certificateNameToPublicKeyName(this.getName())};
+var Name=require("../../name.js").Name,SecurityException=require("../security-exception.js").SecurityException,SyncPromise=require("../../util/sync-promise.js").SyncPromise,IdentityStorage=function(){};exports.IdentityStorage=IdentityStorage;IdentityStorage.prototype.doesIdentityExistPromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.doesIdentityExistPromise is not implemented"))};
+IdentityStorage.prototype.doesIdentityExist=function(a){return SyncPromise.getValue(this.doesIdentityExistPromise(a,!0))};IdentityStorage.prototype.addIdentityPromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.addIdentityPromise is not implemented"))};IdentityStorage.prototype.addIdentity=function(a){return SyncPromise.getValue(this.addIdentityPromise(a,!0))};IdentityStorage.prototype.revokeIdentity=function(){return SyncPromise.reject(Error("IdentityStorage.revokeIdentity is not implemented"))};
+IdentityStorage.prototype.getNewKeyNamePromise=function(a,b,c){for(var d=Math.floor((new Date).getTime()/1E3);d<=IdentityStorage.lastTimestamp;)d+=1;IdentityStorage.lastTimestamp=d;d=""+d;b=b?"ksk-"+d:"dsk-"+d;var e=(new Name(a)).append(b);return this.doesKeyExistPromise(e,c).then(function(a){if(a)throw new SecurityException(Error("Key name already exists"));return SyncPromise.resolve(e)})};
+IdentityStorage.prototype.getNewKeyName=function(a,b){return SyncPromise.getValue(this.getNewKeyNamePromise(a,b,!0))};IdentityStorage.prototype.doesKeyExistPromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.doesKeyExistPromise is not implemented"))};IdentityStorage.prototype.doesKeyExist=function(a){return SyncPromise.getValue(this.doesKeyExistPromise(a,!0))};IdentityStorage.prototype.addKeyPromise=function(a,b,c,d){return SyncPromise.reject(Error("IdentityStorage.addKeyPromise is not implemented"))};
+IdentityStorage.prototype.addKey=function(a,b,c){return SyncPromise.getValue(this.addKeyPromise(a,b,c,!0))};IdentityStorage.prototype.getKeyPromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.getKeyPromise is not implemented"))};IdentityStorage.prototype.getKey=function(a){return SyncPromise.getValue(this.getKeyPromise(a,!0))};IdentityStorage.prototype.activateKey=function(a){throw Error("IdentityStorage.activateKey is not implemented");};
+IdentityStorage.prototype.deactivateKey=function(a){throw Error("IdentityStorage.deactivateKey is not implemented");};IdentityStorage.prototype.doesCertificateExistPromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.doesCertificateExistPromise is not implemented"))};IdentityStorage.prototype.doesCertificateExist=function(a){return SyncPromise.getValue(this.doesCertificateExistPromise(a,!0))};IdentityStorage.prototype.addCertificatePromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.addCertificatePromise is not implemented"))};
+IdentityStorage.prototype.addCertificate=function(a){return SyncPromise.getValue(this.addCertificatePromise(a,!0))};IdentityStorage.prototype.getCertificatePromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.getCertificatePromise is not implemented"))};IdentityStorage.prototype.getCertificate=function(a){return SyncPromise.getValue(this.getValuePromise(a,!0))};IdentityStorage.prototype.getTpmLocatorPromise=function(a){return SyncPromise.reject(Error("IdentityStorage.getTpmLocatorPromise is not implemented"))};
+IdentityStorage.prototype.getTpmLocator=function(){return SyncPromise.getValue(this.getTpmLocatorPromise(!0))};IdentityStorage.prototype.getDefaultIdentityPromise=function(a){return SyncPromise.reject(Error("IdentityStorage.getDefaultIdentityPromise is not implemented"))};IdentityStorage.prototype.getDefaultIdentity=function(){return SyncPromise.getValue(this.getDefaultIdentityPromise(!0))};IdentityStorage.prototype.getDefaultKeyNameForIdentityPromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.getDefaultKeyNameForIdentityPromise is not implemented"))};
+IdentityStorage.prototype.getDefaultKeyNameForIdentity=function(a){return SyncPromise.getValue(this.getDefaultKeyNameForIdentityPromise(a,!0))};IdentityStorage.prototype.getDefaultCertificateNameForIdentityPromise=function(a,b){var c=this;return this.getDefaultKeyNameForIdentityPromise(a).then(function(a){return c.getDefaultCertificateNameForKeyPromise(a)})};
+IdentityStorage.prototype.getDefaultCertificateNameForIdentity=function(a){return SyncPromise.getValue(this.getDefaultCertificateNameForIdentityPromise(a,!0))};IdentityStorage.prototype.getDefaultCertificateNameForKeyPromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.getDefaultCertificateNameForKeyPromise is not implemented"))};IdentityStorage.prototype.getDefaultCertificateNameForKey=function(a){return SyncPromise.getValue(this.getDefaultCertificateNameForKeyPromise(a,!0))};
+IdentityStorage.prototype.getAllIdentitiesPromise=function(a,b,c){return SyncPromise.reject(Error("IdentityStorage.getAllIdentitiesPromise is not implemented"))};IdentityStorage.prototype.getAllKeyNamesOfIdentityPromise=function(a,b,c,d){return SyncPromise.reject(Error("IdentityStorage.getAllKeyNamesOfIdentityPromise is not implemented"))};IdentityStorage.prototype.getAllCertificateNamesOfKeyPromise=function(a,b,c,d){return SyncPromise.reject(Error("IdentityStorage.getAllCertificateNamesOfKeyPromise is not implemented"))};
+IdentityStorage.prototype.getAllKeyNamesOfIdentity=function(a,b,c){return SyncPromise.getValue(this.getAllKeyNamesOfIdentityPromise(a,b,c,!0))};IdentityStorage.prototype.setDefaultIdentityPromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.setDefaultIdentityPromise is not implemented"))};IdentityStorage.prototype.setDefaultIdentity=function(a){return SyncPromise.getValue(this.setDefaultIdentityPromise(a,!0))};
+IdentityStorage.prototype.setDefaultKeyNameForIdentityPromise=function(a,b,c){return SyncPromise.reject(Error("IdentityStorage.setDefaultKeyNameForIdentityPromise is not implemented"))};IdentityStorage.prototype.setDefaultKeyNameForIdentity=function(a,b){return SyncPromise.getValue(this.setDefaultKeyNameForIdentityPromise(a,b,!0))};IdentityStorage.prototype.setDefaultCertificateNameForKeyPromise=function(a,b,c){return SyncPromise.reject(Error("IdentityStorage.setDefaultCertificateNameForKeyPromise is not implemented"))};
+IdentityStorage.prototype.setDefaultCertificateNameForKey=function(a,b){return SyncPromise.getValue(this.setDefaultCertificateNameForKeyPromise(a,b,!0))};IdentityStorage.prototype.getDefaultCertificatePromise=function(a){var b=this;return this.getDefaultIdentityPromise(a).then(function(c){return b.getDefaultCertificateNameForIdentityPromise(c,a)},function(a){return SyncPromise.resolve(null)}).then(function(c){return null==c?SyncPromise.resolve(null):b.getCertificatePromise(c,a)})};
+IdentityStorage.prototype.getDefaultCertificate=function(){return SyncPromise.getValue(this.getDefaultCertificatePromise(!0))};IdentityStorage.prototype.deleteCertificateInfoPromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.deleteCertificateInfoPromise is not implemented"))};IdentityStorage.prototype.deleteCertificateInfo=function(a){return SyncPromise.getValue(this.deleteCertificateInfoPromise(a,!0))};IdentityStorage.prototype.deletePublicKeyInfoPromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.deletePublicKeyInfoPromise is not implemented"))};
+IdentityStorage.prototype.deletePublicKeyInfo=function(a){return SyncPromise.getValue(this.deletePublicKeyInfoPromise(a,!0))};IdentityStorage.prototype.deleteIdentityInfoPromise=function(a,b){return SyncPromise.reject(Error("IdentityStorage.deleteIdentityInfoPromise is not implemented"))};IdentityStorage.prototype.deleteIdentityInfo=function(a){return SyncPromise.getValue(this.deleteIdentityInfoPromise(a,!0))};IdentityStorage.lastTimestamp=Math.floor((new Date).getTime()/1E3);
+var IndexedDbIdentityStorage=function(){IdentityStorage.call(this);this.database=new Dexie("ndnsec-public-info");this.database.version(1).stores({globals:"key",identity:"identityNameUri",publicKey:"keyNameUri",certificate:"certificateNameUri"});this.database.open()};IndexedDbIdentityStorage.prototype=new IdentityStorage;IndexedDbIdentityStorage.prototype.name="IndexedDbIdentityStorage";
+IndexedDbIdentityStorage.prototype.doesIdentityExistPromise=function(a,b){return b?Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.doesIdentityExistPromise is only supported for async"))):this.database.identity.where("identityNameUri").equals(a.toUri()).count().then(function(a){return Promise.resolve(0<a)})};
+IndexedDbIdentityStorage.prototype.addIdentityPromise=function(a,b){if(b)return Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.addIdentityPromise is only supported for async")));var c=this;return this.doesIdentityExistPromise(a).then(function(b){return b?Promise.resolve():c.database.identity.put({identityNameUri:a.toUri(),defaultKeyUri:null})})};
+IndexedDbIdentityStorage.prototype.doesKeyExistPromise=function(a,b){return b?Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.doesKeyExistPromise is only supported for async"))):this.database.publicKey.where("keyNameUri").equals(a.toUri()).count().then(function(a){return Promise.resolve(0<a)})};
+IndexedDbIdentityStorage.prototype.addKeyPromise=function(a,b,c,d){if(d)return Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.addKeyPromise is only supported for async")));if(0===a.size())return Promise.resolve();var e=this;return this.doesKeyExistPromise(a).then(function(d){if(d)return Promise.resolve();d=a.getPrefix(-1);return e.addIdentityPromise(d).then(function(){return e.database.publicKey.put({keyNameUri:a.toUri(),keyType:b,keyDer:(new Blob(c,!0)).buf(),defaultCertificate:null})})})};
+IndexedDbIdentityStorage.prototype.getKeyPromise=function(a,b){return b?Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.getKeyPromise is only supported for async"))):0===a.size()?Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage::getKeyPromise: Empty keyName"))):this.database.publicKey.get(a.toUri()).then(function(a){return a?Promise.resolve(new Blob(a.keyDer)):Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage::getKeyPromise: The key does not exist")))})};
+IndexedDbIdentityStorage.prototype.doesCertificateExistPromise=function(a,b){return b?Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.doesCertificateExistPromise is only supported for async"))):this.database.certificate.where("certificateNameUri").equals(a.toUri()).count().then(function(a){return Promise.resolve(0<a)})};
+IndexedDbIdentityStorage.prototype.addCertificatePromise=function(a,b){if(b)return Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.addCertificatePromise is only supported for async")));var c=a.getName(),d=a.getPublicKeyName(),e=this;return this.addKeyPromise(d,a.getPublicKeyInfo().getKeyType(),a.getPublicKeyInfo().getKeyDer(),b).then(function(){return e.doesCertificateExistPromise(c)}).then(function(b){return b?Promise.resolve():e.database.certificate.put({certificateNameUri:c.toUri(),
+encoding:a.wireEncode().buf()})})};IndexedDbIdentityStorage.prototype.getCertificatePromise=function(a,b){return b?Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.getCertificatePromise is only supported for async"))):this.database.certificate.get(a.toUri()).then(function(a){if(a){var b=new IdentityCertificate;try{b.wireDecode(a.encoding)}catch(e){return Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage::getCertificatePromise: The certificate cannot be decoded")))}return Promise.resolve(b)}return Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage::getCertificatePromise: The certificate does not exist")))})};
+IndexedDbIdentityStorage.prototype.getDefaultIdentityPromise=function(a){return this.database.globals.get("defaultIdentityUri").then(function(a){if(a)return Promise.resolve(new Name(a.value));throw new SecurityException(Error("IndexedDbIdentityStorage.getDefaultIdentity: The default identity is not defined"));})};
+IndexedDbIdentityStorage.prototype.getDefaultKeyNameForIdentityPromise=function(a,b){return b?Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.getDefaultKeyNameForIdentityPromise is only supported for async"))):this.database.identity.get(a.toUri()).then(function(a){if(a){if(null!=a.defaultKeyUri)return Promise.resolve(new Name(a.defaultKeyUri));throw new SecurityException(Error("No default key set."));}throw new SecurityException(Error("Identity not found."));})};
+IndexedDbIdentityStorage.prototype.getDefaultCertificateNameForKeyPromise=function(a,b){return b?Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.getDefaultCertificateNameForKeyPromise is only supported for async"))):this.database.publicKey.get(a.toUri()).then(function(a){if(a){if(null!=a.defaultCertificateUri)return Promise.resolve(new Name(a.defaultCertificateUri));throw new SecurityException(Error("No default certificate set."));}throw new SecurityException(Error("Key not found."));
+})};
+IndexedDbIdentityStorage.prototype.getAllIdentitiesPromise=function(a,b,c){if(c)return Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.getAllIdentitiesPromise is only supported for async")));var d=null,e=this;return this.getDefaultIdentityPromise().then(function(a){d=a;return SyncPromise.resolve()},function(a){return SyncPromise.resolve()}).then(function(){return e.database.identity.each(function(c){c=new Name(c.identityNameUri);var e=null!==d&&c.equals(d);b&&e?a.push(c):b||e||
+a.push(c)})})};
+IndexedDbIdentityStorage.prototype.getAllKeyNamesOfIdentityPromise=function(a,b,c,d){if(d)return Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.getAllKeyNamesOfIdentityPromise is only supported for async")));var e=null,f=this;return this.getDefaultKeyNameForIdentityPromise(a).then(function(a){e=a;return SyncPromise.resolve()},function(a){return SyncPromise.resolve()}).then(function(){return f.database.publicKey.each(function(d){d=new Name(d.keyNameUri);if(d.getPrefix(-1).equals(a)){var f=null!==
+e&&d.equals(e);c&&f?b.push(d):c||f||b.push(d)}})})};
+IndexedDbIdentityStorage.prototype.getAllCertificateNamesOfKeyPromise=function(a,b,c,d){if(d)return Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.getAllCertificateNamesOfKeyPromise is only supported for async")));var e=null,f=this;return this.getDefaultCertificateNameForKeyPromise(a).then(function(a){e=a;return SyncPromise.resolve()},function(a){return SyncPromise.resolve()}).then(function(){return f.database.certificate.each(function(d){d=new Name(d.certificateNameUri);if(IdentityCertificate.certificateNameToPublicKeyName(d).equals(a)){var f=
+null!==e&&d.equals(e);c&&f?b.push(d):c||f||b.push(d)}})})};IndexedDbIdentityStorage.prototype.setDefaultIdentityPromise=function(a,b){if(b)return Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.setDefaultIdentityPromise is only supported for async")));var c=this;return this.doesIdentityExistPromise(a).then(function(b){return b?c.database.globals.put({key:"defaultIdentityUri",value:a.toUri()}):c.database.globals["delete"]("defaultIdentityUri")})};
+IndexedDbIdentityStorage.prototype.setDefaultKeyNameForIdentityPromise=function(a,b,c){c="boolean"===typeof b?b:c;b=b instanceof Name?b:null;if(c)return Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.setDefaultKeyNameForIdentityPromise is only supported for async")));c=a.getPrefix(-1);return null!=b&&0<b.size()&&!b.equals(c)?Promise.reject(new SecurityException(Error("The specified identity name does not match the key name"))):this.database.identity.update(c.toUri(),{defaultKeyUri:a.toUri()})};
+IndexedDbIdentityStorage.prototype.setDefaultCertificateNameForKeyPromise=function(a,b,c){return c?Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.setDefaultCertificateNameForKeyPromise is only supported for async"))):this.database.publicKey.update(a.toUri(),{defaultCertificateUri:b.toUri()})};
+IndexedDbIdentityStorage.prototype.deleteCertificateInfoPromise=function(a,b){return b?Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.deleteCertificateInfoPromise is only supported for async"))):0===a.size()?Promise.resolve():this.database.certificate["delete"](a.toUri())};
+IndexedDbIdentityStorage.prototype.deletePublicKeyInfoPromise=function(a,b){if(b)return Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.deletePublicKeyInfoPromise is only supported for async")));if(0===a.size())return Promise.resolve();var c=this;return this.database.publicKey["delete"](a.toUri()).then(function(){return c.database.certificate.each(function(b){IdentityCertificate.certificateNameToPublicKeyName(new Name(b.certificateNameUri)).equals(a)&&c.database.certificate["delete"](b.certificateNameUri)})})};
+IndexedDbIdentityStorage.prototype.deleteIdentityInfoPromise=function(a,b){if(b)return Promise.reject(new SecurityException(Error("IndexedDbIdentityStorage.deleteIdentityInfoPromise is only supported for async")));var c=this;return this.database.identity["delete"](a.toUri()).then(function(){return c.database.publicKey.each(function(b){(new Name(b.keyNameUri)).getPrefix(-1).equals(a)&&c.database.publicKey["delete"](b.keyNameUri)})}).then(function(){return c.database.certificate.each(function(b){IdentityCertificate.certificateNameToPublicKeyName(new Name(b.certificateNameUri)).getPrefix(-1).equals(a)&&
+c.database.certificate["delete"](b.certificateNameUri)})})};
+var Name=require("../../name.js").Name,Blob=require("../../util/blob.js").Blob,SecurityException=require("../security-exception.js").SecurityException,IdentityCertificate=require("../certificate/identity-certificate.js").IdentityCertificate,SyncPromise=require("../../util/sync-promise.js").SyncPromise,IdentityStorage=require("./identity-storage.js").IdentityStorage,MemoryIdentityStorage=function(){IdentityStorage.call(this);this.identityStore={};this.defaultIdentity="";this.keyStore={};this.certificateStore=
+{}};MemoryIdentityStorage.prototype=new IdentityStorage;MemoryIdentityStorage.prototype.name="MemoryIdentityStorage";exports.MemoryIdentityStorage=MemoryIdentityStorage;MemoryIdentityStorage.prototype.doesIdentityExistPromise=function(a){return SyncPromise.resolve(void 0!==this.identityStore[a.toUri()])};MemoryIdentityStorage.prototype.addIdentityPromise=function(a){a=a.toUri();void 0===this.identityStore[a]&&(this.identityStore[a]={defaultKey:null});return SyncPromise.resolve()};
+MemoryIdentityStorage.prototype.doesKeyExistPromise=function(a){return SyncPromise.resolve(void 0!==this.keyStore[a.toUri()])};MemoryIdentityStorage.prototype.addKeyPromise=function(a,b,c){if(0===a.size()||this.doesKeyExist(a))return SyncPromise.resolve();var d=a.getSubName(0,a.size()-1);this.addIdentity(d);this.keyStore[a.toUri()]={keyType:b,keyDer:new Blob(c),defaultCertificate:null};return SyncPromise.resolve()};
+MemoryIdentityStorage.prototype.getKeyPromise=function(a){if(0===a.size())return SyncPromise.reject(new SecurityException(Error("MemoryIdentityStorage::getKeyPromise: Empty keyName")));a=a.toUri();a=this.keyStore[a];return void 0===a?SyncPromise.reject(new SecurityException(Error("MemoryIdentityStorage::getKeyPromise: The key does not exist"))):SyncPromise.resolve(a.keyDer)};MemoryIdentityStorage.prototype.doesCertificateExistPromise=function(a){return SyncPromise.resolve(void 0!==this.certificateStore[a.toUri()])};
+MemoryIdentityStorage.prototype.addCertificatePromise=function(a){var b=a.getName(),c=a.getPublicKeyName();this.addKey(c,a.getPublicKeyInfo().getKeyType(),a.getPublicKeyInfo().getKeyDer());if(this.doesCertificateExist(b))return SyncPromise.resolve();this.certificateStore[b.toUri()]=a.wireEncode();return SyncPromise.resolve()};
+MemoryIdentityStorage.prototype.getCertificatePromise=function(a){a=a.toUri();if(void 0===this.certificateStore[a])return SyncPromise.reject(new SecurityException(Error("MemoryIdentityStorage::getCertificatePromise: The certificate does not exist")));var b=new IdentityCertificate;try{b.wireDecode(this.certificateStore[a])}catch(c){return SyncPromise.reject(new SecurityException(Error("MemoryIdentityStorage::getCertificatePromise: The certificate cannot be decoded")))}return SyncPromise.resolve(b)};
+IdentityStorage.prototype.getTpmLocatorPromise=function(a){return SyncPromise.resolve("tpm-memory:")};MemoryIdentityStorage.prototype.getDefaultIdentityPromise=function(){return 0===this.defaultIdentity.length?SyncPromise.reject(new SecurityException(Error("MemoryIdentityStorage.getDefaultIdentity: The default identity is not defined"))):SyncPromise.resolve(new Name(this.defaultIdentity))};
+MemoryIdentityStorage.prototype.getDefaultKeyNameForIdentityPromise=function(a){a=a.toUri();return void 0!==this.identityStore[a]?null!=this.identityStore[a].defaultKey?SyncPromise.resolve(this.identityStore[a].defaultKey):SyncPromise.reject(new SecurityException(Error("No default key set."))):SyncPromise.reject(new SecurityException(Error("Identity not found.")))};
+MemoryIdentityStorage.prototype.getDefaultCertificateNameForKeyPromise=function(a){a=a.toUri();return void 0!==this.keyStore[a]?null!=this.keyStore[a].defaultCertificate?SyncPromise.resolve(this.keyStore[a].defaultCertificate):SyncPromise.reject(new SecurityException(Error("No default certificate set."))):SyncPromise.reject(new SecurityException(Error("Key not found.")))};
+MemoryIdentityStorage.prototype.setDefaultIdentityPromise=function(a){a=a.toUri();this.defaultIdentity=void 0!==this.identityStore[a]?a:"";return SyncPromise.resolve()};
+MemoryIdentityStorage.prototype.setDefaultKeyNameForIdentityPromise=function(a,b){b=b instanceof Name?b:null;var c=a.getPrefix(-1);if(null!=b&&0<b.size()&&!b.equals(c))return SyncPromise.reject(new SecurityException(Error("The specified identity name does not match the key name")));c=c.toUri();void 0!==this.identityStore[c]&&(this.identityStore[c].defaultKey=new Name(a));return SyncPromise.resolve()};
+MemoryIdentityStorage.prototype.setDefaultCertificateNameForKeyPromise=function(a,b){var c=a.toUri();void 0!==this.keyStore[c]&&(this.keyStore[c].defaultCertificate=new Name(b));return SyncPromise.resolve()};MemoryIdentityStorage.prototype.deleteCertificateInfoPromise=function(a){return SyncPromise.reject(Error("MemoryIdentityStorage.deleteCertificateInfoPromise is not implemented"))};MemoryIdentityStorage.prototype.deletePublicKeyInfoPromise=function(a){return SyncPromise.reject(Error("MemoryIdentityStorage.deletePublicKeyInfoPromise is not implemented"))};
+MemoryIdentityStorage.prototype.deleteIdentityInfoPromise=function(a){return SyncPromise.reject(Error("MemoryIdentityStorage.deleteIdentityInfoPromise is not implemented"))};var SyncPromise=require("../../util/sync-promise.js").SyncPromise,DerNode=require("../../encoding/der/der-node.js").DerNode,PrivateKeyStorage=function(){};exports.PrivateKeyStorage=PrivateKeyStorage;PrivateKeyStorage.prototype.generateKeyPairPromise=function(a,b,c){return SyncPromise.reject(Error("PrivateKeyStorage.generateKeyPairPromise is not implemented"))};
+PrivateKeyStorage.prototype.generateKeyPair=function(a,b){SyncPromise.getValue(this.generateKeyPairPromise(a,b,!0))};PrivateKeyStorage.prototype.deleteKeyPairPromise=function(a,b){return SyncPromise.reject(Error("PrivateKeyStorage.deleteKeyPairPromise is not implemented"))};PrivateKeyStorage.prototype.deleteKeyPair=function(a){SyncPromise.getValue(this.deleteKeyPairPromise(a,!0))};PrivateKeyStorage.prototype.getPublicKeyPromise=function(a,b){return SyncPromise.reject(Error("PrivateKeyStorage.getPublicKeyPromise is not implemented"))};
+PrivateKeyStorage.prototype.getPublicKey=function(a){return SyncPromise.getValue(this.getPublicKeyPromise(a,!0))};PrivateKeyStorage.prototype.signPromise=function(a,b,c,d){return SyncPromise.reject(Error("PrivateKeyStorage.sign is not implemented"))};PrivateKeyStorage.prototype.sign=function(a,b,c){return SyncPromise.getValue(this.signPromise(a,b,c,!0))};PrivateKeyStorage.prototype.decrypt=function(a,b,c){throw Error("PrivateKeyStorage.decrypt is not implemented");};
+PrivateKeyStorage.prototype.encrypt=function(a,b,c){throw Error("PrivateKeyStorage.encrypt is not implemented");};PrivateKeyStorage.prototype.generateKey=function(a,b){throw Error("PrivateKeyStorage.generateKey is not implemented");};PrivateKeyStorage.prototype.doesKeyExistPromise=function(a,b,c){return SyncPromise.reject(Error("PrivateKeyStorage.doesKeyExist is not implemented"))};PrivateKeyStorage.prototype.doesKeyExist=function(a,b){return SyncPromise.getValue(this.doesKeyExistPromise(a,b,!0))};
+PrivateKeyStorage.encodePkcs8PrivateKey=function(a,b,c){var d=new DerNode.DerSequence;d.addChild(new DerNode.DerOid(b));d.addChild(c);b=new DerNode.DerSequence;b.addChild(new DerNode.DerInteger(0));b.addChild(d);b.addChild(new DerNode.DerOctetString(a));return b.encode()};
+PrivateKeyStorage.encodePkcs1PrivateKeyFromRSAKey=function(a){var b=new DerNode.DerSequence;b.addChild(new DerNode.DerInteger(0));b.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(a.n)));b.addChild(new DerNode.DerInteger(a.e));b.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(a.d)));b.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(a.p)));b.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(a.q)));b.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(a.dmp1)));
+b.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(a.dmq1)));b.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(a.coeff)));return b.encode()};
+PrivateKeyStorage.encodePublicKeyFromRSAKey=function(a){var b=new DerNode.DerSequence;b.addChild(new DerNode.DerInteger(PrivateKeyStorage.bigIntegerToBuffer(a.n)));b.addChild(new DerNode.DerInteger(a.e));a=new DerNode.DerSequence;a.addChild(new DerNode.DerOid(new OID(PrivateKeyStorage.RSA_ENCRYPTION_OID)));a.addChild(new DerNode.DerNull);var c=new DerNode.DerSequence;c.addChild(a);c.addChild(new DerNode.DerBitString(b.encode().buf(),0));return c.encode()};
+PrivateKeyStorage.bigIntegerToBuffer=function(a){a=a.toString(16);if("-"==a.substr(0,1))throw Error("PrivateKeyStorage.bigIntegerToBuffer: Negative integers are not currently supported");1==a.length%2?a="0"+a:a.match(/^[0-7]/)||(a="00"+a);return new Buffer(a,"hex")};PrivateKeyStorage.RSA_ENCRYPTION_OID="1.2.840.113549.1.1.1";PrivateKeyStorage.EC_ENCRYPTION_OID="1.2.840.10045.2.1";
+var Crypto=require("../../crypto.js"),Blob=require("../../util/blob.js").Blob,SecurityException=require("../security-exception.js").SecurityException,PublicKey=require("../certificate/public-key.js").PublicKey,KeyClass=require("../security-types.js").KeyClass,KeyType=require("../security-types").KeyType,DigestAlgorithm=require("../security-types.js").DigestAlgorithm,DataUtils=require("../../encoding/data-utils.js").DataUtils,PrivateKeyStorage=require("./private-key-storage.js").PrivateKeyStorage,
+DerNode=require("../../encoding/der/der-node.js").DerNode,OID=require("../../encoding/oid.js").OID,SyncPromise=require("../../util/sync-promise.js").SyncPromise,UseSubtleCrypto=require("../../use-subtle-crypto-node.js").UseSubtleCrypto,rsaKeygen=null;try{rsaKeygen=require("rsa-keygen")}catch(e$$44){}var MemoryPrivateKeyStorage=function(){PrivateKeyStorage.call(this);this.publicKeyStore={};this.privateKeyStore={}};MemoryPrivateKeyStorage.prototype=new PrivateKeyStorage;
+MemoryPrivateKeyStorage.prototype.name="MemoryPrivateKeyStorage";exports.MemoryPrivateKeyStorage=MemoryPrivateKeyStorage;MemoryPrivateKeyStorage.prototype.setPublicKeyForKeyName=function(a,b,c){this.publicKeyStore[a.toUri()]=new PublicKey(new Blob(c,!0))};
+MemoryPrivateKeyStorage.prototype.setPrivateKeyForKeyName=function(a,b,c){c=c.toString("base64");var d;if(b===KeyType.RSA){d="-----BEGIN RSA PRIVATE KEY-----\n";for(var e=0;e<c.length;e+=64)d+=c.substr(e,64)+"\n";d+="-----END RSA PRIVATE KEY-----"}else if(b===KeyType.ECDSA){d="-----BEGIN EC PRIVATE KEY-----\n";for(e=0;e<c.length;e+=64)d+=c.substr(e,64)+"\n";d+="-----END EC PRIVATE KEY-----"}else throw new SecurityException(Error("MemoryPrivateKeyStorage: KeyType is not supported"));this.privateKeyStore[a.toUri()]=
+{keyType:b,privateKey:d}};MemoryPrivateKeyStorage.prototype.setKeyPairForKeyName=function(a,b,c,d){this.setPublicKeyForKeyName(a,b,c);this.setPrivateKeyForKeyName(a,b,d)};
+MemoryPrivateKeyStorage.prototype.generateKeyPairPromise=function(a,b,c){if(this.doesKeyExist(a,KeyClass.PUBLIC))return SyncPromise.reject(new SecurityException(Error("Public key already exists")));if(this.doesKeyExist(a,KeyClass.PRIVATE))return SyncPromise.reject(new SecurityException(Error("Private key already exists")));var d=this;if(UseSubtleCrypto()&&!c){if(b.getKeyType()===KeyType.RSA){var e=null,f=null;return crypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:b.getKeySize(),
+publicExponent:new Uint8Array([1,0,1]),hash:{name:"SHA-256"}},!0,["sign","verify"]).then(function(a){e=a.privateKey;return crypto.subtle.exportKey("spki",a.publicKey)}).then(function(a){f=(new Blob(new Uint8Array(a),!1)).buf();return crypto.subtle.exportKey("pkcs8",e)}).then(function(c){c=DerNode.parse((new Blob(new Uint8Array(c),!1)).buf()).getChildren()[2].toVal();d.setKeyPairForKeyName(a,b.getKeyType(),f,c.buf());d.privateKeyStore[a.toUri()].subtleKey=e;return Promise.resolve()})}return SyncPromise.reject(new SecurityException(Error("Only RSA key generation currently supported")))}return SyncPromise.resolve().then(function(){if("undefined"!==
+typeof RSAKey)if(b.getKeyType()===KeyType.RSA){var c=new RSAKey;c.generate(b.getKeySize(),"010001");d.setKeyPairForKeyName(a,b.getKeyType(),PrivateKeyStorage.encodePublicKeyFromRSAKey(c).buf(),PrivateKeyStorage.encodePkcs1PrivateKeyFromRSAKey(c).buf())}else return SyncPromise.reject(new SecurityException(Error("Only RSA key generation currently supported")));else{var e;if(b.getKeyType()===KeyType.RSA){if(!rsaKeygen)return SyncPromise.reject(new SecurityException(Error("Need to install rsa-keygen: sudo npm install rsa-keygen")));
+e=rsaKeygen.generate(b.getKeySize());c=e.public_key.toString().replace("-----BEGIN PUBLIC KEY-----","").replace("-----END PUBLIC KEY-----","");c=new Buffer(c,"base64");e=e.private_key.toString()}else return SyncPromise.reject(new SecurityException(Error("Only RSA key generation currently supported")));d.setPublicKeyForKeyName(a,b.getKeyType(),c);d.privateKeyStore[a.toUri()]={keyType:b.getKeyType(),privateKey:e}}return SyncPromise.resolve()})};
+MemoryPrivateKeyStorage.prototype.deleteKeyPairPromise=function(a){a=a.toUri();delete this.publicKeyStore[a];delete this.privateKeyStore[a];return SyncPromise.resolve()};MemoryPrivateKeyStorage.prototype.getPublicKeyPromise=function(a){var b=a.toUri(),b=this.publicKeyStore[b];return void 0===b?SyncPromise.reject(new SecurityException(Error("MemoryPrivateKeyStorage: Cannot find public key "+a.toUri()))):SyncPromise.resolve(b)};
+MemoryPrivateKeyStorage.prototype.signPromise=function(a,b,c,d){d="boolean"===typeof c?c:d;c="boolean"!==typeof c&&c?c:DigestAlgorithm.SHA256;if(c!=DigestAlgorithm.SHA256)return SyncPromise.reject(new SecurityException(Error("MemoryPrivateKeyStorage.sign: Unsupported digest algorithm")));b=b.toUri();var e=this.privateKeyStore[b];if(void 0===e)return SyncPromise.reject(new SecurityException(Error("MemoryPrivateKeyStorage: Cannot find private key "+b)));if(UseSubtleCrypto()&&!d){var f={name:"RSASSA-PKCS1-v1_5",
+hash:{name:"SHA-256"}};e.subtleKey?d=crypto.subtle.sign(f,e.subtleKey,a):(d=DataUtils.privateKeyPemToDer(e.privateKey),d=PrivateKeyStorage.encodePkcs8PrivateKey(d,new OID(PrivateKeyStorage.RSA_ENCRYPTION_OID),new DerNode.DerNull).buf(),d=crypto.subtle.importKey("pkcs8",d.buffer,f,!0,["sign"]).then(function(b){e.subtleKey=b;return crypto.subtle.sign(f,b,a)}));return d.then(function(a){a=new Blob(new Uint8Array(a),!0);return Promise.resolve(a)})}if(e.keyType===KeyType.RSA)d=Crypto.createSign("RSA-SHA256");
+else if(e.keyType===KeyType.ECDSA)d=Crypto.createSign("sha256");else return SyncPromise.reject(new SecurityException(Error("MemoryPrivateKeyStorage.sign: Unrecognized private key type")));d.update(a);d=new Buffer(DataUtils.toNumbersIfString(d.sign(e.privateKey)));d=new Blob(d,!1);return SyncPromise.resolve(d)};
+MemoryPrivateKeyStorage.prototype.doesKeyExistPromise=function(a,b){var c=a.toUri(),d=!1;b==KeyClass.PUBLIC?d=void 0!==this.publicKeyStore[c]:b==KeyClass.PRIVATE&&(d=void 0!==this.privateKeyStore[c]);return SyncPromise.resolve(d)};var Crypto=require("../../crypto.js"),IndexedDbPrivateKeyStorage=function(){PrivateKeyStorage.call(this);this.database=new Dexie("ndnsec-tpm");this.database.version(1).stores({publicKey:"nameHash",privateKey:"nameHash"});this.database.open()};
+IndexedDbPrivateKeyStorage.prototype=new PrivateKeyStorage;IndexedDbPrivateKeyStorage.prototype.name="IndexedDbPrivateKeyStorage";
+IndexedDbPrivateKeyStorage.prototype.generateKeyPairPromise=function(a,b,c){if(c)return Promise.reject(new SecurityException(Error("IndexedDbPrivateKeyStorage.generateKeyPairPromise is only supported for async")));var d=this;return d.doesKeyExistPromise(a,KeyClass.PUBLIC).then(function(b){if(b)throw Error("Public key already exists");return d.doesKeyExistPromise(a,KeyClass.PRIVATE)}).then(function(c){if(c)throw Error("Private key already exists");if(b.getKeyType()===KeyType.RSA){var f=null,g=null;
+return crypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:b.getKeySize(),publicExponent:new Uint8Array([1,0,1]),hash:{name:"SHA-256"}},!0,["sign","verify"]).then(function(a){f=a.privateKey;return crypto.subtle.exportKey("spki",a.publicKey)}).then(function(a){g=new Uint8Array(a);return crypto.subtle.exportKey("pkcs8",f)}).then(function(b){return d.database.transaction("rw",d.database.privateKey,d.database.publicKey,function(){d.database.publicKey.put({nameHash:IndexedDbPrivateKeyStorage.transformName(a),
+encoding:g});d.database.privateKey.put({nameHash:IndexedDbPrivateKeyStorage.transformName(a),encoding:new Uint8Array(b)})})})}throw Error("Only RSA key generation currently supported");})};IndexedDbPrivateKeyStorage.prototype.deleteKeyPairPromise=function(a,b){if(b)return Promise.reject(new SecurityException(Error("IndexedDbPrivateKeyStorage.deleteKeyPairPromise is only supported for async")));var c=this;return this.database.publicKey["delete"](IndexedDbPrivateKeyStorage.transformName(a)).then(function(){return c.database.privateKey["delete"](IndexedDbPrivateKeyStorage.transformName(a))})};
+IndexedDbPrivateKeyStorage.prototype.getPublicKeyPromise=function(a,b){return b?Promise.reject(new SecurityException(Error("IndexedDbPrivateKeyStorage.getPublicKeyPromise is only supported for async"))):this.database.publicKey.get(IndexedDbPrivateKeyStorage.transformName(a)).then(function(a){return Promise.resolve(new PublicKey(new Blob(a.encoding)))})};
+IndexedDbPrivateKeyStorage.prototype.signPromise=function(a,b,c,d){d="boolean"===typeof c?c:d;c="boolean"!==typeof c&&c?c:DigestAlgorithm.SHA256;if(d)return Promise.reject(new SecurityException(Error("IndexedDbPrivateKeyStorage.signPromise is only supported for async")));if(c!=DigestAlgorithm.SHA256)return Promise.reject(new SecurityException(Error("IndexedDbPrivateKeyStorage.sign: Unsupported digest algorithm")));var e={name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};return this.database.privateKey.get(IndexedDbPrivateKeyStorage.transformName(b)).then(function(a){return crypto.subtle.importKey("pkcs8",
+(new Blob(a.encoding)).buf(),e,!0,["sign"])}).then(function(b){return crypto.subtle.sign(e,b,a)}).then(function(a){return Promise.resolve(new Blob(new Uint8Array(a),!0))})};
+IndexedDbPrivateKeyStorage.prototype.doesKeyExistPromise=function(a,b,c){if(c)return Promise.reject(new SecurityException(Error("IndexedDbPrivateKeyStorage.doesKeyExistPromise is only supported for async")));c=null;if(b==KeyClass.PUBLIC)c=this.database.publicKey;else if(b==KeyClass.PRIVATE)c=this.database.privateKey;else return Promise.resolve(!1);return c.where("nameHash").equals(IndexedDbPrivateKeyStorage.transformName(a)).count().then(function(a){return Promise.resolve(0<a)})};
+IndexedDbPrivateKeyStorage.transformName=function(a){var b=Crypto.createHash("sha256");b.update(new Buffer(a.toUri()));return b.digest("base64").replace(/\//g,"%")};
+var Crypto=require("../../crypto.js"),Name=require("../../name.js").Name,Data=require("../../data.js").Data,Blob=require("../../util/blob.js").Blob,ConfigFile=require("../../util/config-file.js").ConfigFile,DigestSha256Signature=require("../../digest-sha256-signature.js").DigestSha256Signature,Sha256WithRsaSignature=require("../../sha256-with-rsa-signature.js").Sha256WithRsaSignature,Sha256WithEcdsaSignature=require("../../sha256-with-ecdsa-signature.js").Sha256WithEcdsaSignature,KeyLocatorType=require("../../key-locator.js").KeyLocatorType,
+WireFormat=require("../../encoding/wire-format.js").WireFormat,SecurityException=require("../security-exception.js").SecurityException,DigestAlgorithm=require("../security-types.js").DigestAlgorithm,KeyType=require("../security-types.js").KeyType,RsaKeyParams=require("../key-params.js").RsaKeyParams,IdentityCertificate=require("../certificate/identity-certificate.js").IdentityCertificate,PublicKey=require("../certificate/public-key.js").PublicKey,CertificateSubjectDescription=require("../certificate/certificate-subject-description.js").CertificateSubjectDescription,
+SyncPromise=require("../../util/sync-promise.js").SyncPromise,BasicIdentityStorage=require("./basic-identity-storage.js").BasicIdentityStorage,FilePrivateKeyStorage=require("./file-private-key-storage.js").FilePrivateKeyStorage,IdentityManager=function IdentityManager(b,c){if(c){if(!b)throw Error("IdentityManager: A custom privateKeyStorage is supplied with a null identityStorage");this.identityStorage=b;this.privateKeyStorage=c}else{if(!ConfigFile)throw new SecurityException(Error("IdentityManager: If not in Node.js then you must supply identityStorage and privateKeyStorage."));
+var d=new ConfigFile,e=[null],f=this;this.identityStorage=b?b:IdentityManager.getDefaultIdentityStorage_(d,function(){return f.checkTpmPromise_(e[0])});this.privateKeyStorage=IdentityManager.getDefaultPrivateKeyStorage_(d,e)}};exports.IdentityManager=IdentityManager;
+IdentityManager.prototype.createIdentityAndCertificatePromise=function(a,b,c){var d=this,e=!0,f=null;return this.identityStorage.addIdentityPromise(a,c).then(function(){return d.identityStorage.getDefaultKeyNameForIdentityPromise(a,c).then(function(a){f=a;return d.identityStorage.getKeyPromise(f,c).then(function(a){(new PublicKey(a)).getKeyType()==b.getKeyType()&&(e=!1);return SyncPromise.resolve()})},function(a){if(!(a instanceof SecurityException))throw a;return SyncPromise.resolve()})}).then(function(){return e?
+d.generateKeyPairPromise(a,!0,b,c).then(function(a){f=a;return d.identityStorage.setDefaultKeyNameForIdentityPromise(f,c)}):SyncPromise.resolve()}).then(function(){return d.identityStorage.getDefaultCertificateNameForKeyPromise(f,c).then(function(a){return SyncPromise.resolve(a)},function(a){if(!(a instanceof SecurityException))throw a;var b;return d.selfSignPromise(f,c).then(function(a){b=a.getName();return d.addCertificateAsIdentityDefaultPromise(a,c)}).then(function(){return SyncPromise.resolve(b)})})})};
+IdentityManager.prototype.createIdentityAndCertificate=function(a,b,c,d){return SyncPromise.complete(c,d,this.createIdentityAndCertificatePromise(a,b,!c))};IdentityManager.prototype.createIdentity=function(a,b){return IdentityCertificate.certificateNameToPublicKeyName(this.createIdentityAndCertificate(a,b))};
+IdentityManager.prototype.deleteIdentity=function(a,b,c){var d=this,e=!0,f=this.identityStorage.getDefaultIdentityPromise(!b).then(function(b){b.equals(a)&&(e=!1);return SyncPromise.resolve()},function(a){return SyncPromise.resolve()}).then(function(){if(!e)return SyncPromise.resolve();var b=[];return d.identityStorage.getAllKeyNamesOfIdentityPromise(a,b,!0).then(function(){return d.identityStorage.getAllKeyNamesOfIdentityPromise(a,b,!1)}).then(function(){return d.identityStorage.deleteIdentityInfoPromise(a)}).then(function(){function a(c){return c>=
+b.length?SyncPromise.resolve():d.privateKeyStorage.deleteKeyPairPromise(b[c]).then(function(){return a(c+1)})}return a(0)})});return SyncPromise.complete(b,c,f)};IdentityManager.prototype.setDefaultIdentityPromise=function(a,b){return this.identityStorage.setDefaultIdentityPromise(a,b)};IdentityManager.prototype.setDefaultIdentity=function(a,b,c){return SyncPromise.complete(b,c,this.identityStorage.setDefaultIdentityPromise(a,!b))};IdentityManager.prototype.getDefaultIdentityPromise=function(a){return this.identityStorage.getDefaultIdentityPromise(a)};
+IdentityManager.prototype.getDefaultIdentity=function(a,b){return SyncPromise.complete(a,b,this.identityStorage.getDefaultIdentityPromise(!a))};IdentityManager.prototype.getDefaultCertificatePromise=function(a){return this.identityStorage.getDefaultCertificatePromise(a)};IdentityManager.prototype.generateRSAKeyPair=function(a,b,c){return SyncPromise.getValue(this.generateKeyPairPromise(a,b,new RsaKeyParams(c),!0))};
+IdentityManager.prototype.setDefaultKeyForIdentity=function(a,b,c,d){d="function"===typeof b?c:d;c="function"===typeof b?b:c;b="function"!==typeof b&&b?b:new Name;return SyncPromise.complete(c,d,this.identityStorage.setDefaultKeyNameForIdentityPromise(a,b,!c))};IdentityManager.prototype.getDefaultKeyNameForIdentity=function(a,b,c){return SyncPromise.complete(b,c,this.identityStorage.getDefaultKeyNameForIdentityPromise(a,!b))};
+IdentityManager.prototype.generateRSAKeyPairAsDefaultPromise=function(a,b,c,d){var e,f=this;return this.generateKeyPairPromise(a,b,new RsaKeyParams(c)).then(function(a){e=a;return f.identityStorage.setDefaultKeyNameForIdentityPromise(e)}).then(function(){return SyncPromise.resolve(e)})};IdentityManager.prototype.generateRSAKeyPairAsDefault=function(a,b,c){return SyncPromise.getValue(this.generateRSAKeyPairAsDefaultPromise(a,b,c,!0))};
+IdentityManager.prototype.getPublicKey=function(a,b,c){return SyncPromise.complete(b,c,this.identityStorage.getKeyPromise(a,!b).then(function(a){return SyncPromise.resolve(new PublicKey(a))}))};
+IdentityManager.prototype.prepareUnsignedIdentityCertificate=function(a,b,c,d,e,f,g,h,k){b instanceof PublicKey||(k=h,h=g,g=f,f=e,e=d,d=c,c=b,b=null);var l=g,m=h;g=l instanceof Name?l:null;"function"===typeof l?(h=l,k=m):"function"===typeof m?h=m:k=h=null;a=null==b?this.prepareUnsignedIdentityCertificatePromise(a,c,d,e,f,g,!h):this.prepareUnsignedIdentityCertificatePromise(a,b,c,d,e,f,g,!h);return SyncPromise.complete(h,k,a)};
+IdentityManager.prototype.prepareUnsignedIdentityCertificatePromise=function(a,b,c,d,e,f,g,h){b instanceof PublicKey||(h=g,g=f,f=e,e=d,d=c,c=b,b=null);var k=g;g=k instanceof Name?k:null;return(null==b?this.identityStorage.getKeyPromise(a,"boolean"===typeof k?k:"boolean"===typeof h?h:!1).then(function(a){b=new PublicKey(a);return SyncPromise.resolve()}):SyncPromise.resolve()).then(function(){return SyncPromise.resolve(IdentityManager.prepareUnsignedIdentityCertificateHelper_(a,b,c,d,e,f,g))})};
+IdentityManager.prepareUnsignedIdentityCertificateHelper_=function(a,b,c,d,e,f,g){if(1>a.size())return null;var h=a.get(-1).toEscapedString();if(4>h.length)return null;keyIdPrefix=h.substr(0,4);if("ksk-"!=keyIdPrefix&&"dsk-"!=keyIdPrefix)return null;var h=new IdentityCertificate,k=new Name;if(null==g)c.match(a)?k.append(c).append("KEY").append(a.getSubName(c.size())).append("ID-CERT").appendVersion((new Date).getTime()):k.append(a.getPrefix(-1)).append("KEY").append(a.get(-1)).append("ID-CERT").appendVersion((new Date).getTime());
+else if(g.match(a)&&!g.equals(a))k.append(g).append("KEY").append(a.getSubName(g.size())).append("ID-CERT").appendVersion((new Date).getTime());else return null;h.setName(k);h.setNotBefore(d);h.setNotAfter(e);h.setPublicKeyInfo(b);if(null==f||0===f.length)h.addSubjectDescription(new CertificateSubjectDescription("2.5.4.41",a.getPrefix(-1).toUri()));else for(a=0;a<f.length;++a)h.addSubjectDescription(f[a]);try{h.encode()}catch(l){throw SecurityException(Error("DerEncodingException: "+l));}return h};
+IdentityManager.prototype.addCertificate=function(a,b,c){return SyncPromise.complete(b,c,this.identityStorage.addCertificatePromise(a,!b))};IdentityManager.prototype.setDefaultCertificateForKeyPromise=function(a,b){var c=this,d=a.getPublicKeyName();return this.identityStorage.doesKeyExistPromise(d,b).then(function(e){if(!e)throw new SecurityException(Error("No corresponding Key record for certificate!"));return c.identityStorage.setDefaultCertificateNameForKeyPromise(d,a.getName(),b)})};
+IdentityManager.prototype.setDefaultCertificateForKey=function(a,b,c){return SyncPromise.complete(b,c,this.setDefaultCertificateForKeyPromise(a,!b))};IdentityManager.prototype.addCertificateAsIdentityDefaultPromise=function(a,b){var c=this;return this.identityStorage.addCertificatePromise(a,b).then(function(){var d=a.getPublicKeyName();return c.identityStorage.setDefaultKeyNameForIdentityPromise(d,b)}).then(function(){return c.setDefaultCertificateForKeyPromise(a,b)})};
+IdentityManager.prototype.addCertificateAsDefault=function(a,b,c){var d=!b,e=this;return SyncPromise.complete(b,c,this.identityStorage.addCertificatePromise(a,d).then(function(){return e.setDefaultCertificateForKeyPromise(a,d)}))};IdentityManager.prototype.getCertificate=function(a,b,c){return SyncPromise.complete(b,c,this.identityStorage.getCertificatePromise(a,!1,!b))};
+IdentityManager.prototype.getDefaultCertificateNameForIdentityPromise=function(a,b){return this.identityStorage.getDefaultCertificateNameForIdentityPromise(a,b)};IdentityManager.prototype.getDefaultCertificateNameForIdentity=function(a,b,c){return SyncPromise.complete(b,c,this.identityStorage.getDefaultCertificateNameForIdentityPromise(a,!b))};
+IdentityManager.prototype.getDefaultCertificateName=function(a,b){var c=!a,d=this;return SyncPromise.complete(a,b,this.identityStorage.getDefaultIdentityPromise(c).then(function(a){return d.identityStorage.getDefaultCertificateNameForIdentityPromise(a,c)}))};IdentityManager.prototype.getAllIdentities=function(a,b,c,d){return SyncPromise.complete(c,d,this.identityStorage.getAllIdentitiesPromise(a,b,!c))};
+IdentityManager.prototype.getAllKeyNamesOfIdentity=function(a,b,c,d,e){return SyncPromise.complete(d,e,this.identityStorage.getAllKeyNamesOfIdentityPromise(a,b,c,!d))};IdentityManager.prototype.getAllCertificateNamesOfKey=function(a,b,c,d,e){return SyncPromise.complete(d,e,this.identityStorage.getAllCertificateNamesOfKeyPromise(a,b,c,!d))};
+IdentityManager.prototype.signByCertificatePromise=function(a,b,c,d){d="boolean"===typeof c?c:d;c="boolean"!==typeof c&&c?c:WireFormat.getDefaultWireFormat();var e=IdentityManager.certificateNameToPublicKeyName(b),f=this;if(a instanceof Data){var g=[0];return this.makeSignatureByCertificatePromise(b,g,d).then(function(b){a.setSignature(b);b=a.wireEncode(c);return f.privateKeyStorage.signPromise(b.signedBuf(),e,g[0],d)}).then(function(b){a.getSignature().setSignature(b);a.wireEncode(c);return SyncPromise.resolve(a)})}g=
+[0];return this.makeSignatureByCertificatePromise(b,g,d).then(function(b){return f.privateKeyStorage.signPromise(a,e,g[0],d)}).then(function(a){signature.setSignature(a);return SyncPromise.resolve(signature)})};IdentityManager.prototype.signByCertificate=function(a,b,c,d,e){e="function"===typeof c?d:e;d="function"===typeof c?c:d;c="function"!==typeof c&&c?c:WireFormat.getDefaultWireFormat();return SyncPromise.complete(d,e,this.signByCertificatePromise(a,b,c,!d))};
+IdentityManager.prototype.signInterestByCertificatePromise=function(a,b,c,d){d="boolean"===typeof c?c:d;c="boolean"!==typeof c&&c?c:WireFormat.getDefaultWireFormat();var e=this,f,g=[0];return this.makeSignatureByCertificatePromise(b,g,d).then(function(h){f=h;a.getName().append(c.encodeSignatureInfo(f));a.getName().append(new Name.Component);h=a.wireEncode(c);var k=IdentityManager.certificateNameToPublicKeyName(b);return e.privateKeyStorage.signPromise(h.signedBuf(),k,g[0],d)}).then(function(b){f.setSignature(b);
+a.setName(a.getName().getPrefix(-1).append(c.encodeSignatureValue(f)));return SyncPromise.resolve(a)})};IdentityManager.prototype.signInterestByCertificate=function(a,b,c,d,e){e="function"===typeof c?d:e;d="function"===typeof c?c:d;c="function"!==typeof c&&c?c:WireFormat.getDefaultWireFormat();return SyncPromise.complete(d,e,this.signInterestByCertificatePromise(a,b,c,!d))};
+IdentityManager.prototype.signWithSha256=function(a,b){b=b||WireFormat.getDefaultWireFormat();a.setSignature(new DigestSha256Signature);var c=a.wireEncode(b),d=Crypto.createHash("sha256");d.update(c.signedBuf());a.getSignature().setSignature(new Blob(d.digest(),!1));a.wireEncode(b)};
+IdentityManager.prototype.signInterestWithSha256=function(a,b){b=b||WireFormat.getDefaultWireFormat();var c=new DigestSha256Signature;a.getName().append(b.encodeSignatureInfo(c));a.getName().append(new Name.Component);var d=a.wireEncode(b),e=Crypto.createHash("sha256");e.update(d.signedBuf());c.setSignature(new Blob(e.digest(),!1));a.setName(a.getName().getPrefix(-1).append(b.encodeSignatureValue(c)))};
+IdentityManager.prototype.selfSignPromise=function(a,b){var c=new IdentityCertificate,d=this;return this.identityStorage.getKeyPromise(a,b).then(function(e){e=new PublicKey(e);var f=(new Date).getTime(),g=f+63072E6;c.setNotBefore(f);c.setNotAfter(g);f=a.getPrefix(-1).append("KEY").append(a.get(-1)).append("ID-CERT").appendVersion(c.getNotBefore());c.setName(f);c.setPublicKeyInfo(e);c.addSubjectDescription(new CertificateSubjectDescription("2.5.4.41",a.toUri()));c.encode();return d.signByCertificatePromise(c,
+c.getName(),b)})};IdentityManager.prototype.selfSign=function(a,b,c){return SyncPromise.complete(b,c,this.selfSignPromise(a,!b))};IdentityManager.certificateNameToPublicKeyName=function(a){for(var b=a.size()-1;0<=b&&"ID-CERT"!=a.get(b).toEscapedString();)--b;a=a.getSubName(0,b);for(b=0;b<a.size()&&"KEY"!=a.get(b).toEscapedString();)++b;return a.getSubName(0,b).append(a.getSubName(b+1,a.size()-b-1))};
+IdentityManager.prototype.makeSignatureByCertificatePromise=function(a,b,c){var d=IdentityManager.certificateNameToPublicKeyName(a);return this.privateKeyStorage.getPublicKeyPromise(d,c).then(function(c){c=c.getKeyType();var d=null;if(c==KeyType.RSA)d=new Sha256WithRsaSignature,b[0]=DigestAlgorithm.SHA256,d.getKeyLocator().setType(KeyLocatorType.KEYNAME),d.getKeyLocator().setKeyName(a.getPrefix(-1));else if(c==KeyType.ECDSA)d=new Sha256WithEcdsaSignature,b[0]=DigestAlgorithm.SHA256,d.getKeyLocator().setType(KeyLocatorType.KEYNAME),
+d.getKeyLocator().setKeyName(a.getPrefix(-1));else throw new SecurityException(Error("Key type is not recognized"));return SyncPromise.resolve(d)})};
+IdentityManager.prototype.generateKeyPairPromise=function(a,b,c,d){var e,f=this;return this.identityStorage.getNewKeyNamePromise(a,b,d).then(function(a){e=a;return f.privateKeyStorage.generateKeyPairPromise(e,c,d)}).then(function(){return f.privateKeyStorage.getPublicKeyPromise(e,d)}).then(function(a){return f.identityStorage.addKeyPromise(e,c.getKeyType(),a.getKeyDer())}).then(function(){return SyncPromise.resolve(e)})};
+IdentityManager.getDefaultIdentityStorage_=function(a,b){var c=a.get("pib","");if(""!==c&&"pib-sqlite3"!==c)throw new SecurityException(Error("Invalid config file pib value: "+c));return new BasicIdentityStorage(b)};
+IdentityManager.getDefaultPrivateKeyStorage_=function(a,b){var c=a.get("tpm","");if(""===c){if("darwin"===process.platform)throw b[0]="tpm-osxkeychain:",new SecurityException(Error("IdentityManager: OS X key chain storage is not yet implemented. You must supply a privateKeyStorage."));b[0]="tpm-file:";return new FilePrivateKeyStorage}if("tpm-osxkeychain"===c)throw b[0]="tpm-osxkeychain:",new SecurityException(Error("IdentityManager: tpm-osxkeychain is not yet implemented."));if("tpm-file"===c)return b[0]=
+"tpm-file:",new FilePrivateKeyStorage;throw new SecurityException(Error("Invalid config file tpm value: "+c));};IdentityManager.prototype.checkTpmPromise_=function(a){return this.identityStorage.getTpmLocatorPromise().then(function(b){return""!==b&&b!==a?Promise.reject(new SecurityException(Error("The TPM locator supplied does not match the TPM locator in the PIB: "+b+" != "+a))):Promise.resolve()},function(a){return Promise.resolve()})};
+var ValidationRequest=function(a,b,c,d,e){this.interest=a;this.onVerified=b;this.onValidationFailed=c;this.retry=d;this.stepCount=e};exports.ValidationRequest=ValidationRequest;
+var Crypto=require("../../crypto.js"),Blob=require("../../util/blob.js").Blob,DataUtils=require("../../encoding/data-utils.js").DataUtils,SecurityException=require("../security-exception.js").SecurityException,DigestSha256Signature=require("../../digest-sha256-signature.js").DigestSha256Signature,Sha256WithRsaSignature=require("../../sha256-with-rsa-signature.js").Sha256WithRsaSignature,Sha256WithEcdsaSignature=require("../../sha256-with-ecdsa-signature.js").Sha256WithEcdsaSignature,UseSubtleCrypto=
+require("../../use-subtle-crypto-node.js").UseSubtleCrypto,PolicyManager=function(){};exports.PolicyManager=PolicyManager;PolicyManager.prototype.skipVerifyAndTrust=function(a){throw Error("PolicyManager.skipVerifyAndTrust is not implemented");};PolicyManager.prototype.requireVerify=function(a){throw Error("PolicyManager.requireVerify is not implemented");};PolicyManager.prototype.checkVerificationPolicy=function(a,b,c,d,e){throw Error("PolicyManager.checkVerificationPolicy is not implemented");};
+PolicyManager.prototype.checkSigningPolicy=function(a,b){throw Error("PolicyManager.checkSigningPolicy is not implemented");};PolicyManager.prototype.inferSigningIdentity=function(a){throw Error("PolicyManager.inferSigningIdentity is not implemented");};PolicyManager.verifyUsesString_=null;PolicyManager.setVerifyUsesString_=function(){var a=Crypto.createHash("sha256").digest();PolicyManager.verifyUsesString_="string"===typeof a};
+PolicyManager.verifySignature=function(a,b,c,d){if(a instanceof Sha256WithRsaSignature)c.isNull()?d(!1):PolicyManager.verifySha256WithRsaSignature(a.getSignature(),b,c,d);else if(a instanceof Sha256WithEcdsaSignature)c.isNull()?d(!1):PolicyManager.verifySha256WithEcdsaSignature(a.getSignature(),b,c,d);else if(a instanceof DigestSha256Signature)PolicyManager.verifyDigestSha256Signature(a.getSignature(),b,d);else throw new SecurityException(Error("PolicyManager.verify: Signature type is unknown"));
+};
+PolicyManager.verifySha256WithRsaSignature=function(a,b,c,d){if(UseSubtleCrypto()){var e={name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}};crypto.subtle.importKey("spki",c.buf().buffer,e,!0,["verify"]).then(function(c){return crypto.subtle.verify(e,c,a.buf(),b.signedBuf())}).then(function(a){d(a)})}else{null===PolicyManager.verifyUsesString_&&PolicyManager.setVerifyUsesString_();var f=c.buf().toString("base64");c="-----BEGIN PUBLIC KEY-----\n";for(var g=0;g<f.length;g+=64)c+=f.substr(g,64)+"\n";c+=
+"-----END PUBLIC KEY-----";f=Crypto.createVerify("RSA-SHA256");f.update(b.signedBuf());g=PolicyManager.verifyUsesString_?DataUtils.toString(a.buf()):a.buf();d(f.verify(c,g))}};
+PolicyManager.verifySha256WithEcdsaSignature=function(a,b,c,d){if(UseSubtleCrypto())d(!1);else{null===PolicyManager.verifyUsesString_&&PolicyManager.setVerifyUsesString_();var e=c.buf().toString("base64");c="-----BEGIN PUBLIC KEY-----\n";for(var f=0;f<e.length;f+=64)c+=e.substr(f,64)+"\n";c+="-----END PUBLIC KEY-----";e=Crypto.createVerify("sha256");e.update(b.signedBuf());a=PolicyManager.verifyUsesString_?DataUtils.toString(a.buf()):a.buf();d(e.verify(c,a))}};
+PolicyManager.verifyDigestSha256Signature=function(a,b,c){var d=Crypto.createHash("sha256");d.update(b.signedBuf());b=new Blob(d.digest(),!1);c(b.equals(a))};var IdentityCertificate=require("../certificate/identity-certificate.js").IdentityCertificate,CertificateCache=function(){this.cache={}};exports.CertificateCache=CertificateCache;CertificateCache.prototype.insertCertificate=function(a){var b=a.getName().getPrefix(-1);this.cache[b.toUri()]=a.wireEncode()};
+CertificateCache.prototype.deleteCertificate=function(a){delete this.cache[a.toUri()]};CertificateCache.prototype.getCertificate=function(a){a=this.cache[a.toUri()];if(void 0===a)return null;var b=new IdentityCertificate;b.wireDecode(a);return b};CertificateCache.prototype.reset=function(){this.cache={}};
+var fs=require("fs"),path=require("path"),Name=require("../../name.js").Name,Data=require("../../data.js").Data,Interest=require("../../interest.js").Interest,KeyLocator=require("../../key-locator.js").KeyLocator,KeyLocatorType=require("../../key-locator.js").KeyLocatorType,Blob=require("../../util/blob.js").Blob,IdentityCertificate=require("../certificate/identity-certificate.js").IdentityCertificate,BoostInfoParser=require("../../util/boost-info-parser.js").BoostInfoParser,NdnRegexMatcher=require("../../util/ndn-regex-matcher.js").NdnRegexMatcher,
+CertificateCache=require("./certificate-cache.js").CertificateCache,ValidationRequest=require("./validation-request.js").ValidationRequest,SecurityException=require("../security-exception.js").SecurityException,WireFormat=require("../../encoding/wire-format.js").WireFormat,PolicyManager=require("./policy-manager.js").PolicyManager,NdnCommon=require("../../util/ndn-common.js").NdnCommon,ConfigPolicyManager=function(a,b,c,d,e,f){PolicyManager.call(this);void 0==b&&(b=null);void 0==c&&(c=5);void 0==
+d&&(d=3E3);void 0==e&&(e=36E5);void 0==f&&(f=1E3);this.certificateCache=null==b?new CertificateCache:b;this.maxDepth=c;this.keyGraceInterval=d;this.keyTimestampTtl=e;this.maxTrackedKeys=f;this.reset();null!=a&&""!=a&&this.load(a)};ConfigPolicyManager.prototype=new PolicyManager;ConfigPolicyManager.prototype.name="ConfigPolicyManager";exports.ConfigPolicyManager=ConfigPolicyManager;
+ConfigPolicyManager.prototype.reset=function(){this.certificateCache.reset();this.fixedCertificateCache={};this.keyTimestamps={};this.requiresVerification=!0;this.config=new BoostInfoParser;this.refreshManager=new ConfigPolicyManager.TrustAnchorRefreshManager};ConfigPolicyManager.prototype.load=function(a,b){this.reset();this.config.read(a,b);this.loadTrustAnchorCertificates()};ConfigPolicyManager.prototype.requireVerify=function(a){return this.requiresVerification};
+ConfigPolicyManager.prototype.checkSigningPolicy=function(a,b){return!0};ConfigPolicyManager.prototype.skipVerifyAndTrust=function(a){return!this.requiresVerification};
+ConfigPolicyManager.prototype.checkVerificationPolicy=function(a,b,c,d,e){if(b>this.maxDepth){try{d(a,"The verification stepCount "+b+" exceeded the maxDepth "+this.maxDepth)}catch(f){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(f))}return null}e=ConfigPolicyManager.extractSignature(a,e);if(null==e){try{d(a,"Cannot extract the signature from "+a.getName().toUri())}catch(g){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(g))}return null}if(!KeyLocator.canGetFromSignature(e)){try{d(a,
+"The signature type does not support a KeyLocator")}catch(h){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(h))}return null}var k=null;try{k=KeyLocator.getFromSignature(e)}catch(l){try{d(a,"Error in KeyLocator.getFromSignature: "+l)}catch(m){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(m))}return null}k=k.getKeyName();if(0==k.size()){try{d(a,"The signature KeyLocator doesn't have a key name")}catch(q){console.log("Error in onValidationFailed: "+
+NdnCommon.getErrorWithStackTrace(q))}return null}var p=a.getName(),s="data";a instanceof Interest&&(p=p.getPrefix(-4),s="interest");var w=this.findMatchingRule(p,s);if(null==w){try{d(a,"No matching rule found for "+p.toUri())}catch(M){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(M))}return null}s=["unknown"];if(!this.checkSignatureMatch(k,p,w,s)){try{d(a,s[0])}catch(O){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(O))}return null}this.refreshManager.refreshAnchors();
+p=this.refreshManager.getCertificate(k);null==p&&(p=this.certificateCache.getCertificate(k));var K=this;if(null==p)return e=new Interest(k),new ValidationRequest(e,function(e){var f;try{f=new IdentityCertificate(e)}catch(g){try{d(a,"Cannot decode certificate "+e.getName().toUri())}catch(h){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(h))}return null}K.certificateCache.insertCertificate(f);K.checkVerificationPolicy(a,b+1,c,d)},d,2,b+1);if(a instanceof Interest){var x=
+p.getPublicKeyName(),A=a.getName().get(-4).toNumber();if(!this.interestTimestampIsFresh(x,A,s)){try{d(a,s[0])}catch(I){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(I))}return null}}this.verify(e,a.wireEncode(),function(b,e){if(b){try{c(a)}catch(f){console.log("Error in onVerified: "+NdnCommon.getErrorWithStackTrace(f))}a instanceof Interest&&K.updateTimestampForKey(x,A)}else try{d(a,e)}catch(g){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(g))}})};
+ConfigPolicyManager.prototype.loadTrustAnchorCertificates=function(){for(var a=this.config.getRoot().get("validator/trust-anchor"),b=0;b<a.length;++b){var c=a[b],d=c.get("type")[0].getValue(),e=!1,f;if("file"==d)f=c.get("file-name")[0].getValue(),e=!0;else if("base64"==d)f=c.get("base64-string")[0].getValue(),e=!1;else if("dir"==d){d=c.get("dir")[0].getValue();e=0;c=c.get("refresh");1<=c.length&&(c=c[0].getValue().match(/(\d+)([hms])/),null==c?e=0:(e=parseInt(c[1]),"s"!=c[2]&&(e*=60,"m"!=c[2]&&(e*=
+60))));this.refreshManager.addDirectory(d,1E3*e);continue}else if("any"==d){this.requiresVerification=!1;break}this.lookupCertificate(f,e)}};
+ConfigPolicyManager.prototype.checkSignatureMatch=function(a,b,c,d){c=c.get("checker")[0];var e=c.get("type")[0].getValue();if("fixed-signer"==e){b=c.get("signer")[0];c=b.get("type")[0].getValue();if("file"==c){if(c=this.lookupCertificate(b.get("file-name")[0].getValue(),!0),null==c)return d[0]="Can't find fixed-signer certificate file: "+b.get("file-name")[0].getValue(),!1}else if("base64"==c){if(c=this.lookupCertificate(b.get("base64-string")[0].getValue(),!1),null==c)return d[0]="Can't find fixed-signer certificate base64: "+
+b.get("base64-string")[0].getValue(),!1}else return d[0]="Unrecognized fixed-signer signerType: "+c,!1;if(c.getName().equals(a))return!0;d[0]='fixed-signer cert name "'+c.getName().toUri()+'" does not equal signatureName "'+a.toUri()+'"';return!1}if("hierarchical"==e){c=NdnRegexMatcher.match("^([^<KEY>]*)<KEY>(<>*)<ksk-.+><ID-CERT>",a);if(null!=c){a=(new Name(c[1])).append(new Name(c[2]));if(ConfigPolicyManager.matchesRelation(b,a,"is-prefix-of"))return!0;d[0]='The hierarchical objectName "'+b.toUri()+
+'" is not a prefix of "'+a.toUri()+'"';return!1}d[0]='The hierarchical identityRegex "^([^<KEY>]*)<KEY>(<>*)<ksk-.+><ID-CERT>" does not match signatureName "'+a.toUri()+'"';return!1}if("customized"==e){var f=c.get("key-locator")[0];c=f.getFirstValue("relation");if(null!=c){b=new Name(f.get("name")[0].getValue());if(ConfigPolicyManager.matchesRelation(a,b,c))return!0;d[0]='The custom signatureName "'+a.toUri()+'" does not match matchName "'+b.toUri()+'" using relation '+c;return!1}var g=f.getFirstValue("regex");
+if(null!=g){if(null!=NdnRegexMatcher.match(g,a))return!0;d[0]='The custom signatureName "'+a.toUri()+'" does not regex match simpleKeyRegex "'+g+'"';return!1}c=f.get("hyper-relation");if(1<=c.length){c=c[0];var g=c.getFirstValue("k-regex"),h=c.getFirstValue("k-expand"),f=c.getFirstValue("p-regex"),k=c.getFirstValue("p-expand");c=c.getFirstValue("h-relation");if(null!=g&&null!=h&&null!=f&&null!=k&&null!=c){e=NdnRegexMatcher.match(g,a);if(null==e||void 0===e[1])return d[0]='The custom hyper-relation signatureName "'+
+a.toUri()+'" does not match the keyRegex "'+g+'"',!1;a=ConfigPolicyManager.expand(e,h);e=NdnRegexMatcher.match(f,b);if(null==e||void 0===e[1])return d[0]='The custom hyper-relation objectName "'+b.toUri()+'" does not match the nameRegex "'+f+'"',!1;b=ConfigPolicyManager.expand(e,k);if(ConfigPolicyManager.matchesRelation(new Name(b),new Name(a),c))return!0;d[0]='The custom hyper-relation nameMatch "'+b+'" does not match the keyMatchPrefix "'+a+'" using relation '+c;return!1}}}d[0]="Unrecognized checkerType: "+
+e;return!1};ConfigPolicyManager.expand=function(a,b){return b.replace(/\\(\d)/g,function(b,d){return a[parseInt(d)]})};
+ConfigPolicyManager.prototype.lookupCertificate=function(a,b){var c;c=this.fixedCertificateCache[a];if(void 0===c){if(b)c=ConfigPolicyManager.TrustAnchorRefreshManager.loadIdentityCertificateFromFile(a);else{var d=new Buffer(a,"base64");c=new IdentityCertificate;c.wireDecode(d)}d=c.getName().getPrefix(-1).toUri();this.fixedCertificateCache[a]=d;this.certificateCache.insertCertificate(c)}else c=this.certificateCache.getCertificate(new Name(c));return c};
+ConfigPolicyManager.prototype.findMatchingRule=function(a,b){for(var c=this.config.getRoot().get("validator/rule"),d=0;d<c.length;++d){var e=c[d];if(e.get("for")[0].getValue()==b){var f=!0,g=e.get("filter");if(0==g.length)return e;for(var h=0;h<g.length;++h){var k=g[h],f=k.getFirstValue("regex");null===f?(f=k.get("relation")[0].getValue(),k=k.get("name")[0].getValue(),k=new Name(k),f=ConfigPolicyManager.matchesRelation(a,k,f)):f=null!==NdnRegexMatcher.match(f,a);if(!f)break}if(f)return e}}return null};
+ConfigPolicyManager.matchesRelation=function(a,b,c){var d=!1;"is-strict-prefix-of"==c?b.size()==a.size()?d=!1:b.match(a)&&(d=!0):"is-prefix-of"==c?b.match(a)&&(d=!0):"equal"==c&&b.equals(a)&&(d=!0);return d};ConfigPolicyManager.extractSignature=function(a,b){if(a instanceof Data)return a.getSignature();if(a instanceof Interest){b=b||WireFormat.getDefaultWireFormat();try{var c=b.decodeSignatureInfoAndValue(a.getName().get(-2).getValue().buf(),a.getName().get(-1).getValue().buf(),!1)}catch(d){return null}return c}return null};
+ConfigPolicyManager.prototype.interestTimestampIsFresh=function(a,b,c){a=this.keyTimestamps[a.toUri()];if(void 0==a){a=(new Date).getTime();var d=a+this.keyGraceInterval;if(b>a-this.keyGraceInterval&&b<d)return!0;c[0]="The command interest timestamp is not within the first use grace period of "+this.keyGraceInterval+" milliseconds.";return!1}return b<=a?(c[0]="The command interest timestamp is not newer than the previous timestamp",!1):!0};
+ConfigPolicyManager.prototype.updateTimestampForKey=function(a,b){this.keyTimestamps[a.toUri()]=b;var c=0,d=[],e=(new Date).getTime(),f=e,g=null,h;for(h in this.keyTimestamps){++c;var k=this.keyTimestamps[h];e-k>this.keyTimestampTtl?d.push(h):k<f&&(f=k,g=h)}if(c>=this.maxTrackedKeys){for(e=0;e<d.length;++e)delete this.keyTimestamps[d[e]],--c;c>this.maxTrackedKeys&&delete this.keyTimestamps[g]}};
+ConfigPolicyManager.prototype.verify=function(a,b,c){var d=KeyLocator.getFromSignature(a);if(d.getType()==KeyLocatorType.KEYNAME){var e=d.getKeyName(),d=this.refreshManager.getCertificate(e);null==d&&(d=this.certificateCache.getCertificate(e));null==d?c(!1,"Cannot find a certificate with name "+e.toUri()):(e=d.getPublicKeyInfo().getKeyDer(),e.isNull()?c(!1,"There is no public key in the certificate with name "+d.getName().toUri()):PolicyManager.verifySignature(a,b,e,function(a){a?c(!0):c(!1,"The signature did not verify with the given public key")}))}else c(!1,
+"The KeyLocator does not have a key name")};ConfigPolicyManager.TrustAnchorRefreshManager=function(){this.certificateCache=new CertificateCache;this.refreshDirectories={}};ConfigPolicyManager.TrustAnchorRefreshManager.loadIdentityCertificateFromFile=function(a){a=fs.readFileSync(a).toString();a=new Buffer(a,"base64");var b=new IdentityCertificate;b.wireDecode(new Blob(a,!1));return b};ConfigPolicyManager.TrustAnchorRefreshManager.prototype.getCertificate=function(a){return this.certificateCache.getCertificate(a)};
+ConfigPolicyManager.TrustAnchorRefreshManager.prototype.addDirectory=function(a,b){var c;try{c=fs.readdirSync(a)}catch(d){throw new SecurityException(Error("Cannot list files in directory "+a));}for(var e=[],f=0;f<c.length;++f){var g;try{var h=path.join(a,c[f]);g=ConfigPolicyManager.TrustAnchorRefreshManager.loadIdentityCertificateFromFile(h)}catch(k){continue}var l=g.getName().getPrefix(-1).toUri();this.certificateCache.insertCertificate(g);e.push(l)}this.refreshDirectories[a]={certificates:e,nextRefresh:(new Date).getTime()+
+b,refreshPeriod:b}};ConfigPolicyManager.TrustAnchorRefreshManager.prototype.refreshAnchors=function(){var a=(new Date).getTime(),b;for(b in this.refreshDirectories){var c=this.refreshDirectories[b];if(c.nextRefresh<=a){var d=c.certificates.slice(0),e;for(e in d)this.certificateCache.deleteCertificate(new Name(e));this.addDirectory(b,c.refreshPeriod)}}};
+var Name=require("../../name.js").Name,PolicyManager=require("./policy-manager.js").PolicyManager,NdnCommon=require("../../util/ndn-common.js").NdnCommon,NoVerifyPolicyManager=function(){PolicyManager.call(this)};NoVerifyPolicyManager.prototype=new PolicyManager;NoVerifyPolicyManager.prototype.name="NoVerifyPolicyManager";exports.NoVerifyPolicyManager=NoVerifyPolicyManager;NoVerifyPolicyManager.prototype.skipVerifyAndTrust=function(a){return!0};NoVerifyPolicyManager.prototype.requireVerify=function(a){return!1};
+NoVerifyPolicyManager.prototype.checkVerificationPolicy=function(a,b,c,d,e){try{c(a)}catch(f){console.log("Error in onVerified: "+NdnCommon.getErrorWithStackTrace(f))}return null};NoVerifyPolicyManager.prototype.checkSigningPolicy=function(a,b){return!0};NoVerifyPolicyManager.prototype.inferSigningIdentity=function(a){return new Name};
+var Name=require("../../name.js").Name,Interest=require("../../interest.js").Interest,Data=require("../../data.js").Data,Blob=require("../../util/blob.js").Blob,IdentityCertificate=require("../certificate/identity-certificate.js").IdentityCertificate,KeyLocator=require("../../key-locator.js").KeyLocator,KeyLocatorType=require("../../key-locator.js").KeyLocatorType,SecurityException=require("../security-exception.js").SecurityException,WireFormat=require("../../encoding/wire-format.js").WireFormat,
+SyncPromise=require("../../util/sync-promise.js").SyncPromise,PolicyManager=require("./policy-manager.js").PolicyManager,NdnCommon=require("../../util/ndn-common.js").NdnCommon,SelfVerifyPolicyManager=function(a){PolicyManager.call(this);this.identityStorage=a};SelfVerifyPolicyManager.prototype=new PolicyManager;SelfVerifyPolicyManager.prototype.name="SelfVerifyPolicyManager";exports.SelfVerifyPolicyManager=SelfVerifyPolicyManager;SelfVerifyPolicyManager.prototype.skipVerifyAndTrust=function(a){return!1};
+SelfVerifyPolicyManager.prototype.requireVerify=function(a){return!0};
+SelfVerifyPolicyManager.prototype.checkVerificationPolicy=function(a,b,c,d,e){e=e||WireFormat.getDefaultWireFormat();if(a instanceof Data)this.verify(a.getSignature(),a.wireEncode(),function(b,e){if(b)try{c(a)}catch(f){console.log("Error in onVerified: "+NdnCommon.getErrorWithStackTrace(f))}else try{d(a,e)}catch(g){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(g))}});else if(a instanceof Interest){if(2>a.getName().size()){try{d(a,"The signed interest has less than 2 components: "+
+a.getName().toUri())}catch(f){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(f))}return}var g;try{g=e.decodeSignatureInfoAndValue(a.getName().get(-2).getValue().buf(),a.getName().get(-1).getValue().buf(),!1)}catch(h){try{d(a,"Error decoding the signed interest signature: "+h)}catch(k){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(k))}return}this.verify(g,a.wireEncode(),function(b,e){if(b)try{c(a)}catch(f){console.log("Error in onVerified: "+
+NdnCommon.getErrorWithStackTrace(f))}else try{d(a,e)}catch(g){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(g))}})}else throw new SecurityException(Error("checkVerificationPolicy: unrecognized type for dataOrInterest"));return null};SelfVerifyPolicyManager.prototype.checkSigningPolicy=function(a,b){return!0};SelfVerifyPolicyManager.prototype.inferSigningIdentity=function(a){return new Name};
+SelfVerifyPolicyManager.prototype.verify=function(a,b,c){if(KeyLocator.canGetFromSignature(a))this.getPublicKeyDer(KeyLocator.getFromSignature(a),function(d,f){if(d.isNull())c(!1,f);else try{PolicyManager.verifySignature(a,b,d,function(a){a?c(!0):c(!1,"The signature did not verify with the given public key")})}catch(g){c(!1,"Error in verifySignature: "+g)}});else try{PolicyManager.verifySignature(a,b,null,function(a){a?c(!0):c(!1,"The signature did not verify with the given public key")})}catch(d){c(!1,
+"Error in verifySignature: "+d)}};
+SelfVerifyPolicyManager.prototype.getPublicKeyDer=function(a,b){if(a.getType()==KeyLocatorType.KEYNAME&&null!=this.identityStorage){var c;try{c=IdentityCertificate.certificateNameToPublicKeyName(a.getKeyName())}catch(d){b(new Blob,"Cannot get a public key name from the certificate named: "+a.getKeyName().toUri());return}SyncPromise.complete(b,function(a){b(new Blob,"The identityStorage doesn't have the key named "+c.toUri())},this.identityStorage.getKeyPromise(c))}else b(new Blob,"The signature KeyLocator doesn't have a key name")};
+Crypto=require("../crypto.js");Name=require("../name.js").Name;Interest=require("../interest.js").Interest;Data=require("../data.js").Data;Blob=require("../util/blob.js").Blob;WireFormat=require("../encoding/wire-format.js").WireFormat;SecurityException=require("./security-exception.js").SecurityException;RsaKeyParams=require("./key-params.js").RsaKeyParams;IdentityCertificate=require("./certificate/identity-certificate.js").IdentityCertificate;SyncPromise=require("../util/sync-promise.js").SyncPromise;
+NdnCommon=require("../util/ndn-common.js").NdnCommon;IdentityManager=require("./identity/identity-manager.js").IdentityManager;NoVerifyPolicyManager=require("./policy/no-verify-policy-manager.js").NoVerifyPolicyManager;KeyChain=function(a,b){a||(a=new IdentityManager);b||(b=new NoVerifyPolicyManager);this.identityManager=a;this.policyManager=b;this.face=null};exports.KeyChain=KeyChain;
+KeyChain.prototype.createIdentityAndCertificate=function(a,b,c,d){d="function"===typeof b?c:d;c="function"===typeof b?b:c;b="function"!==typeof b&&b?b:KeyChain.DEFAULT_KEY_PARAMS;return this.identityManager.createIdentityAndCertificate(a,b,c,d)};KeyChain.prototype.createIdentity=function(a,b){return IdentityCertificate.certificateNameToPublicKeyName(this.createIdentityAndCertificate(a,b))};KeyChain.prototype.deleteIdentity=function(a,b,c){this.identityManager.deleteIdentity(a,b,c)};
+KeyChain.prototype.getDefaultIdentity=function(a,b){return this.identityManager.getDefaultIdentity(a,b)};KeyChain.prototype.getDefaultCertificateName=function(a,b){return this.identityManager.getDefaultCertificateName(a,b)};KeyChain.prototype.generateRSAKeyPair=function(a,b,c){(c="boolean"===typeof b?b:c)||(c=2048);return this.identityManager.generateRSAKeyPair(a,"boolean"===typeof b?b:!1,c)};
+KeyChain.prototype.setDefaultKeyForIdentity=function(a,b,c,d){return this.identityManager.setDefaultKeyForIdentity(a,b,c,d)};KeyChain.prototype.generateRSAKeyPairAsDefault=function(a,b,c){return this.identityManager.generateRSAKeyPairAsDefault(a,b,c)};KeyChain.prototype.createSigningRequest=function(a){return this.identityManager.getPublicKey(a).getKeyDer()};KeyChain.prototype.installIdentityCertificate=function(a,b,c){this.identityManager.addCertificate(a,b,c)};
+KeyChain.prototype.setDefaultCertificateForKey=function(a,b,c){this.identityManager.setDefaultCertificateForKey(a,b,c)};KeyChain.prototype.getCertificate=function(a,b,c){return this.identityManager.getCertificate(a,b,c)};KeyChain.prototype.getIdentityCertificate=function(a,b,c){return this.identityManager.getCertificate(a,b,c)};KeyChain.prototype.revokeKey=function(a){};KeyChain.prototype.revokeCertificate=function(a){};KeyChain.prototype.getIdentityManager=function(){return this.identityManager};
+KeyChain.prototype.getPolicyManager=function(){return this.policyManager};KeyChain.prototype.sign=function(a,b,c,d,e){var f=b,g=c,h=d;b=f instanceof Name?f:null;c=f instanceof WireFormat?f:g instanceof WireFormat?g:null;"function"===typeof f?(d=f,e=g):"function"===typeof g?(d=g,e=h):"function"===typeof h?d=h:e=d=null;return SyncPromise.complete(d,e,this.signPromise(a,b,c,!d))};
+KeyChain.prototype.signPromise=function(a,b,c,d){var e=b,f=c,g=d;b=e instanceof Name?e:null;c=e instanceof WireFormat?e:f instanceof WireFormat?f:null;d="boolean"===typeof e?e:"boolean"===typeof f?f:"boolean"===typeof g?g:!1;var h=this;return SyncPromise.resolve().then(function(){return null!=b?SyncPromise.resolve():h.identityManager.getDefaultCertificatePromise(d).then(function(a){return null!=a?(b=a.getName(),SyncPromise.resolve()):h.prepareDefaultCertificateNamePromise_(d).then(function(a){b=a;
+return SyncPromise.resolve()})})}).then(function(){return a instanceof Interest?h.identityManager.signInterestByCertificatePromise(a,b,c,d):a instanceof Data?h.identityManager.signByCertificatePromise(a,b,c,d):h.identityManager.signByCertificatePromise(a,b,d)})};
+KeyChain.prototype.signByIdentity=function(a,b,c,d,e){e="function"===typeof c?d:e;d="function"===typeof c?c:d;c="function"!==typeof c&&c?c:WireFormat.getDefaultWireFormat();var f=!d,g=this;null==b&&(b=new Name);if(a instanceof Data){var h=SyncPromise.resolve().then(function(){if(0==b.size()){var c=g.policyManager.inferSigningIdentity(a.getName());return 0==c.size()?g.identityManager.getDefaultCertificateNamePromise(f):g.identityManager.getDefaultCertificateNameForIdentityPromise(c,f)}return g.identityManager.getDefaultCertificateNameForIdentityPromise(b,
+f)}).then(function(b){if(0==b.size())throw new SecurityException(Error("No qualified certificate name found!"));if(!g.policyManager.checkSigningPolicy(a.getName(),b))throw new SecurityException(Error("Signing Cert name does not comply with signing policy"));return g.identityManager.signByCertificatePromise(a,b,c,f)});return SyncPromise.complete(d,e,h)}return SyncPromise.complete(d,e,this.identityManager.getDefaultCertificateNameForIdentityPromise(b,f).then(function(b){if(0==b.size())throw new SecurityException(Error("No qualified certificate name found!"));
+return g.identityManager.signByCertificatePromise(a,b,c,f)}))};KeyChain.prototype.signWithSha256=function(a,b){a instanceof Interest?this.identityManager.signInterestWithSha256(a,b):this.identityManager.signWithSha256(a,b)};
+KeyChain.prototype.verifyData=function(a,b,c,d){null==d&&(d=0);if(this.policyManager.requireVerify(a)){var e=this.policyManager.checkVerificationPolicy(a,d,b,c);if(null!=e){var f=this;this.face.expressInterest(e.interest,function(a,b){f.onCertificateData(a,b,e)},function(b){f.onCertificateInterestTimeout(b,e.retry,c,a,e)})}}else if(this.policyManager.skipVerifyAndTrust(a))try{b(a)}catch(g){console.log("Error in onVerified: "+NdnCommon.getErrorWithStackTrace(g))}else try{c(a,"The packet has no verify rule but skipVerifyAndTrust is false")}catch(h){console.log("Error in onValidationFailed: "+
+NdnCommon.getErrorWithStackTrace(h))}};
+KeyChain.prototype.verifyInterest=function(a,b,c,d,e){null==d&&(d=0);e=e||WireFormat.getDefaultWireFormat();if(this.policyManager.requireVerify(a)){var f=this.policyManager.checkVerificationPolicy(a,d,b,c,e);if(null!=f){var g=this;this.face.expressInterest(f.interest,function(a,b){g.onCertificateData(a,b,f)},function(b){g.onCertificateInterestTimeout(b,f.retry,c,a,f)})}}else if(this.policyManager.skipVerifyAndTrust(a))try{b(a)}catch(h){console.log("Error in onVerified: "+NdnCommon.getErrorWithStackTrace(h))}else try{c(a,
+"The packet has no verify rule but skipVerifyAndTrust is false")}catch(k){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(k))}};KeyChain.prototype.setFace=function(a){this.face=a};
+KeyChain.signWithHmacWithSha256=function(a,b,c){c=c||WireFormat.getDefaultWireFormat();if(a instanceof Data)c=a.wireEncode(c),b=Crypto.createHmac("sha256",b.buf()),b.update(c.signedBuf()),a.getSignature().setSignature(new Blob(b.digest(),!1));else throw new SecurityException(Error("signWithHmacWithSha256: Unrecognized target type"));};
+KeyChain.verifyDataWithHmacWithSha256=function(a,b,c){c=c||WireFormat.getDefaultWireFormat();c=a.wireEncode(c);b=Crypto.createHmac("sha256",b.buf());b.update(c.signedBuf());return(new Blob(b.digest(),!1)).equals(a.getSignature().getSignature())};KeyChain.DEFAULT_KEY_PARAMS=new RsaKeyParams;KeyChain.prototype.onCertificateData=function(a,b,c){this.verifyData(b,c.onVerified,c.onValidationFailed,c.stepCount)};
+KeyChain.prototype.onCertificateInterestTimeout=function(a,b,c,d,e){if(0<b){var f=this;this.face.expressInterest(a,function(a,b){f.onCertificateData(a,b,e)},function(a){f.onCertificateInterestTimeout(a,b-1,c,d,e)})}else try{c(d,"The retry count is zero after timeout for fetching "+a.getName().toUri())}catch(g){console.log("Error in onValidationFailed: "+NdnCommon.getErrorWithStackTrace(g))}};
+KeyChain.prototype.prepareDefaultCertificateNamePromise_=function(a){var b,c=this;return this.identityManager.getDefaultCertificatePromise(a).then(function(d){b=d;return null!=b?SyncPromise.resolve():c.setDefaultCertificatePromise_(a).then(function(){return c.identityManager.getDefaultCertificatePromise(a)}).then(function(a){b=a;return SyncPromise.resolve()})}).then(function(){return SyncPromise.resolve(b.getName())})};
+KeyChain.prototype.setDefaultCertificatePromise_=function(a){var b=this;return this.identityManager.getDefaultCertificatePromise(a).then(function(c){if(null!=c)return SyncPromise.resolve();var d;return b.identityManager.getDefaultIdentityPromise(a).then(function(a){d=a;return SyncPromise.resolve()},function(a){randomComponent=Crypto.randomBytes(4);d=(new Name).append("tmp-identity").append(new Blob(randomComponent,!1));return SyncPromise.resolve()}).then(function(){return b.identityManager.createIdentityAndCertificatePromise(d,
+KeyChain.DEFAULT_KEY_PARAMS,a)}).then(function(){return b.identityManager.setDefaultIdentityPromise(d,a)})})};var Name=require("./name.js").Name,DataUtils=require("./encoding/data-utils.js").DataUtils,Blob=require("./util/blob.js").Blob,Exclude=function Exclude(b){this.values=[];if("object"===typeof b&&b instanceof Exclude)this.values=b.values.slice(0);else if(b)for(var c=this.changeCount=0;c<b.length;++c)b[c]==Exclude.ANY?this.appendAny():this.appendComponent(b[c]);this.changeCount=0};
+exports.Exclude=Exclude;Exclude.ANY="*";Exclude.prototype.size=function(){return this.values.length};Exclude.prototype.get=function(a){return this.values[a]};Exclude.prototype.appendAny=function(){this.values.push(Exclude.ANY);++this.changeCount;return this};Exclude.prototype.appendComponent=function(a){this.values.push(new Name.Component(a));++this.changeCount;return this};Exclude.prototype.clear=function(){++this.changeCount;this.values=[]};
+Exclude.prototype.toUri=function(){if(null==this.values||0==this.values.length)return"";for(var a="",b=0;b<this.values.length;++b)0<b&&(a+=","),a=this.values[b]==Exclude.ANY?a+"*":a+this.values[b].toEscapedString();return a};
+Exclude.prototype.matches=function(a){"object"==typeof a&&a instanceof Name.Component||(a=new Name.Component(a));for(var b=0;b<this.values.length;++b)if(this.values[b]==Exclude.ANY){var c=null;0<b&&(c=this.values[b-1]);var d,e=null;for(d=b+1;d<this.values.length;++d)if(this.values[d]!=Exclude.ANY){e=this.values[d];break}if(null!=e){if(null!=c){if(0<a.compare(c)&&0>a.compare(e))return!0}else if(0>a.compare(e))return!0;b=d-1}else if(null!=c){if(0<a.compare(c))return!0}else return!0}else if(a.equals(this.values[b]))return!0;
+return!1};Exclude.compareComponents=function(a,b){"object"==typeof a&&a instanceof Name.Component&&(a=a.getValue().buf());"object"==typeof b&&b instanceof Name.Component&&(b=b.getValue().buf());return Name.Component.compareBuffers(a,b)};Exclude.prototype.getChangeCount=function(){return this.changeCount};
+var Crypto=require("./crypto.js"),Blob=require("./util/blob.js").Blob,SignedBlob=require("./util/signed-blob.js").SignedBlob,ChangeCounter=require("./util/change-counter.js").ChangeCounter,Name=require("./name.js").Name,Exclude=require("./exclude.js").Exclude,Link=require("./link.js").Link,KeyLocator=require("./key-locator.js").KeyLocator,IncomingFaceId=require("./lp/incoming-face-id.js").IncomingFaceId,WireFormat=require("./encoding/wire-format.js").WireFormat,Interest=function Interest(b,c,d,e,
+f,g,h,k,l,m){if(e)throw Error("Interest constructor: PublisherPublicKeyDigest support has been removed.");if(h)throw Error("Interest constructor: answerOriginKind support has been removed. Use setMustBeFresh().");if(k)throw Error("Interest constructor: scope support has been removed.");"object"===typeof b&&b instanceof Interest?(this.name_=new ChangeCounter(new Name(b.getName())),this.maxSuffixComponents_=b.maxSuffixComponents_,this.minSuffixComponents_=b.minSuffixComponents_,this.keyLocator_=new ChangeCounter(new KeyLocator(b.getKeyLocator())),
+this.exclude_=new ChangeCounter(new Exclude(b.getExclude())),this.childSelector_=b.childSelector_,this.mustBeFresh_=b.mustBeFresh_,this.interestLifetimeMilliseconds_=b.interestLifetimeMilliseconds_,this.nonce_=b.nonce_,this.linkWireEncoding_=b.linkWireEncoding_,this.linkWireEncodingFormat_=b.linkWireEncodingFormat_,this.link_=new ChangeCounter(null),null!=b.link_.get()&&this.link_.set(new Link(b.link_.get())),this.selectedDelegationIndex_=b.selectedDelegationIndex_,this.defaultWireEncoding_=b.getDefaultWireEncoding(),
+this.defaultWireEncodingFormat_=b.defaultWireEncodingFormat_):(this.name_=new ChangeCounter("object"===typeof b&&b instanceof Name?new Name(b):new Name),this.maxSuffixComponents_=d,this.minSuffixComponents_=c,this.keyLocator_=new ChangeCounter(new KeyLocator),this.exclude_=new ChangeCounter("object"===typeof f&&f instanceof Exclude?new Exclude(f):new Exclude),this.childSelector_=g,this.mustBeFresh_=!0,this.interestLifetimeMilliseconds_=l,this.nonce_="object"===typeof m&&m instanceof Blob?m:new Blob(m,
+!0),this.linkWireEncoding_=new Blob,this.linkWireEncodingFormat_=null,this.link_=new ChangeCounter(null),this.selectedDelegationIndex_=null,this.defaultWireEncoding_=new SignedBlob,this.defaultWireEncodingFormat_=null);this.changeCount_=this.getDefaultWireEncodingChangeCount_=this.getNonceChangeCount_=0;this.lpPacket_=null};exports.Interest=Interest;Interest.RECURSIVE_POSTFIX="*";Interest.CHILD_SELECTOR_LEFT=0;Interest.CHILD_SELECTOR_RIGHT=1;
+Interest.prototype.matchesName=function(a){return!this.getName().match(a)||null!=this.minSuffixComponents_&&!(a.size()+1-this.getName().size()>=this.minSuffixComponents_)||null!=this.maxSuffixComponents_&&!(a.size()+1-this.getName().size()<=this.maxSuffixComponents_)||null!=this.getExclude()&&a.size()>this.getName().size()&&this.getExclude().matches(a.get(this.getName().size()))?!1:!0};Interest.prototype.matches_name=function(a){return this.matchesName(a)};
+Interest.prototype.matchesData=function(a,b){b=b||WireFormat.getDefaultWireFormat();var c=this.getName().size(),d=a.getName(),e=d.size()+1,f=null!=this.getMinSuffixComponents()?this.getMinSuffixComponents():0;if(!(c+f<=e&&(null==this.getMaxSuffixComponents()||c+this.getMaxSuffixComponents()>=e)))return!1;if(c===e)if(this.getName().get(-1).isImplicitSha256Digest()){if(!this.getName().equals(a.getFullName(b)))return!1}else return!1;else if(!this.getName().isPrefixOf(d))return!1;if(0<this.getExclude().size()&&
+e>c)if(c==e-1){if(this.getExclude().matches(a.getFullName(b).get(c)))return!1}else if(this.getExclude().matches(d.get(c)))return!1;c=this.getKeyLocator();return!c.getType()||(d=a.getSignature(),KeyLocator.canGetFromSignature(d)&&c.equals(KeyLocator.getFromSignature(d)))?!0:!1};Interest.prototype.clone=function(){return new Interest(this)};Interest.prototype.getName=function(){return this.name_.get()};Interest.prototype.getMinSuffixComponents=function(){return this.minSuffixComponents_};
+Interest.prototype.getMaxSuffixComponents=function(){return this.maxSuffixComponents_};Interest.prototype.getKeyLocator=function(){return this.keyLocator_.get()};Interest.prototype.getExclude=function(){return this.exclude_.get()};Interest.prototype.getChildSelector=function(){return this.childSelector_};Interest.prototype.getMustBeFresh=function(){return this.mustBeFresh_};
+Interest.prototype.getNonce=function(){this.getNonceChangeCount_!=this.getChangeCount()&&(this.nonce_=new Blob,this.getNonceChangeCount_=this.getChangeCount());return this.nonce_};Interest.prototype.getNonceAsBuffer=function(){return this.getNonce().buf()};Interest.prototype.hasLink=function(){return null!=this.link_.get()||!this.linkWireEncoding_.isNull()};
+Interest.prototype.getLink=function(){if(null!=this.link_.get())return this.link_.get();if(this.linkWireEncoding_.isNull())return null;var a=new Link;a.wireDecode(this.linkWireEncoding_,this.linkWireEncodingFormat_);this.link_.set(a);this.linkWireEncoding_=new Blob;this.linkWireEncodingFormat_=null;return a};
+Interest.prototype.getLinkWireEncoding=function(a){a=a||WireFormat.getDefaultWireFormat();if(!this.linkWireEncoding_.isNull()&&this.linkWireEncodingFormat_==a)return this.linkWireEncoding_;var b=this.getLink();return null!=b?b.wireEncode(a):new Blob};Interest.prototype.getSelectedDelegationIndex=function(){return this.selectedDelegationIndex_};Interest.prototype.getInterestLifetimeMilliseconds=function(){return this.interestLifetimeMilliseconds_};
+Interest.prototype.getDefaultWireEncoding=function(){this.getDefaultWireEncodingChangeCount_!=this.getChangeCount()&&(this.defaultWireEncoding_=new SignedBlob,this.defaultWireEncodingFormat_=null,this.getDefaultWireEncodingChangeCount_=this.getChangeCount());return this.defaultWireEncoding_};Interest.prototype.getDefaultWireEncodingFormat=function(){return this.defaultWireEncodingFormat_};
+Interest.prototype.getIncomingFaceId=function(){var a=null===this.lpPacket_?null:IncomingFaceId.getFirstHeader(this.lpPacket_);return null===a?null:a.getFaceId()};Interest.prototype.setName=function(a){this.name_.set("object"===typeof a&&a instanceof Name?new Name(a):new Name);++this.changeCount_;return this};Interest.prototype.setMinSuffixComponents=function(a){this.minSuffixComponents_=a;++this.changeCount_;return this};
+Interest.prototype.setMaxSuffixComponents=function(a){this.maxSuffixComponents_=a;++this.changeCount_;return this};Interest.prototype.setKeyLocator=function(a){this.keyLocator_.set("object"===typeof a&&a instanceof KeyLocator?new KeyLocator(a):new KeyLocator);++this.changeCount_;return this};Interest.prototype.setExclude=function(a){this.exclude_.set("object"===typeof a&&a instanceof Exclude?new Exclude(a):new Exclude);++this.changeCount_;return this};
+Interest.prototype.setLinkWireEncoding=function(a,b){b=b||WireFormat.getDefaultWireFormat();this.linkWireEncoding_=a;this.linkWireEncodingFormat_=b;this.link_.set(null);++this.changeCount_;return this};Interest.prototype.unsetLink=function(){return this.setLinkWireEncoding(new Blob,null)};Interest.prototype.setSelectedDelegationIndex=function(a){this.selectedDelegationIndex_=a;++this.changeCount_;return this};
+Interest.prototype.setChildSelector=function(a){this.childSelector_=a;++this.changeCount_;return this};Interest.prototype.setMustBeFresh=function(a){this.mustBeFresh_=a?!0:!1;++this.changeCount_;return this};Interest.prototype.setInterestLifetimeMilliseconds=function(a){this.interestLifetimeMilliseconds_=a;++this.changeCount_;return this};
+Interest.prototype.setNonce=function(a){this.nonce_="object"===typeof a&&a instanceof Blob?a:new Blob(a,!0);++this.changeCount_;this.getNonceChangeCount_=this.getChangeCount();return this};
+Interest.prototype.toUri=function(){var a="";null!=this.minSuffixComponents_&&(a+="&ndn.MinSuffixComponents="+this.minSuffixComponents_);null!=this.maxSuffixComponents_&&(a+="&ndn.MaxSuffixComponents="+this.maxSuffixComponents_);null!=this.childSelector_&&(a+="&ndn.ChildSelector="+this.childSelector_);a+="&ndn.MustBeFresh="+(this.mustBeFresh_?1:0);null!=this.interestLifetimeMilliseconds_&&(a+="&ndn.InterestLifetime="+this.interestLifetimeMilliseconds_);0<this.getNonce().size()&&(a+="&ndn.Nonce="+
+Name.toEscapedString(this.getNonce().buf()));null!=this.getExclude()&&0<this.getExclude().size()&&(a+="&ndn.Exclude="+this.getExclude().toUri());var b=this.getName().toUri();""!=a&&(b+="?"+a.substr(1));return b};
+Interest.prototype.wireEncode=function(a){a=a||WireFormat.getDefaultWireFormat();if(!this.getDefaultWireEncoding().isNull()&&this.getDefaultWireEncodingFormat()==a)return this.getDefaultWireEncoding();var b=a.encodeInterest(this),b=new SignedBlob(b.encoding,b.signedPortionBeginOffset,b.signedPortionEndOffset);a==WireFormat.getDefaultWireFormat()&&this.setDefaultWireEncoding(b,WireFormat.getDefaultWireFormat());return b};
+Interest.prototype.wireDecode=function(a,b){b=b||WireFormat.getDefaultWireFormat();var c;c="object"===typeof a&&a instanceof Blob?b.decodeInterest(this,a.buf(),!1):b.decodeInterest(this,a,!0);b==WireFormat.getDefaultWireFormat()?this.setDefaultWireEncoding(new SignedBlob(new Blob(a,!0),c.signedPortionBeginOffset,c.signedPortionEndOffset),WireFormat.getDefaultWireFormat()):this.setDefaultWireEncoding(new SignedBlob,null)};
+Interest.prototype.refreshNonce=function(){var a=this.getNonce();if(0!==a.size()){for(var b;b=new Blob(Crypto.randomBytes(a.size()),!1),b.equals(a););this.nonce_=b;++this.changeCount_;this.getNonceChangeCount_=this.getChangeCount()}};Interest.prototype.setLpPacket=function(a){this.lpPacket_=a;return this};Interest.prototype.getChangeCount=function(){var a=this.name_.checkChanged(),a=this.keyLocator_.checkChanged()||a;(a=this.exclude_.checkChanged()||a)&&++this.changeCount_;return this.changeCount_};
+Interest.prototype.setDefaultWireEncoding=function(a,b){this.defaultWireEncoding_=a;this.defaultWireEncodingFormat_=b;this.getDefaultWireEncodingChangeCount_=this.getChangeCount()};Object.defineProperty(Interest.prototype,"name",{get:function(){return this.getName()},set:function(a){this.setName(a)}});Object.defineProperty(Interest.prototype,"minSuffixComponents",{get:function(){return this.getMinSuffixComponents()},set:function(a){this.setMinSuffixComponents(a)}});
+Object.defineProperty(Interest.prototype,"maxSuffixComponents",{get:function(){return this.getMaxSuffixComponents()},set:function(a){this.setMaxSuffixComponents(a)}});Object.defineProperty(Interest.prototype,"keyLocator",{get:function(){return this.getKeyLocator()},set:function(a){this.setKeyLocator(a)}});Object.defineProperty(Interest.prototype,"exclude",{get:function(){return this.getExclude()},set:function(a){this.setExclude(a)}});
+Object.defineProperty(Interest.prototype,"childSelector",{get:function(){return this.getChildSelector()},set:function(a){this.setChildSelector(a)}});Object.defineProperty(Interest.prototype,"interestLifetime",{get:function(){return this.getInterestLifetimeMilliseconds()},set:function(a){this.setInterestLifetimeMilliseconds(a)}});Object.defineProperty(Interest.prototype,"nonce",{get:function(){return this.getNonceAsBuffer()},set:function(a){this.setNonce(a)}});
+ForwardingFlags=function ForwardingFlags(b){"object"===typeof b&&b instanceof ForwardingFlags?(this.childInherit=b.childInherit,this.capture=b.capture):(this.childInherit=!0,this.capture=!1)};exports.ForwardingFlags=ForwardingFlags;ForwardingFlags.NfdForwardingFlags_CHILD_INHERIT=1;ForwardingFlags.NfdForwardingFlags_CAPTURE=2;
+ForwardingFlags.prototype.getNfdForwardingFlags=function(){var a=0;this.childInherit&&(a|=ForwardingFlags.NfdForwardingFlags_CHILD_INHERIT);this.capture&&(a|=ForwardingFlags.NfdForwardingFlags_CAPTURE);return a};ForwardingFlags.prototype.setNfdForwardingFlags=function(a){this.childInherit=0!=(a&ForwardingFlags.NfdForwardingFlags_CHILD_INHERIT);this.capture=0!=(a&ForwardingFlags.NfdForwardingFlags_CAPTURE)};ForwardingFlags.prototype.getChildInherit=function(){return this.childInherit};
+ForwardingFlags.prototype.getCapture=function(){return this.capture};ForwardingFlags.prototype.setChildInherit=function(a){this.childInherit=a};ForwardingFlags.prototype.setCapture=function(a){this.capture=a};
+var ForwardingFlags=require("./forwarding-flags.js").ForwardingFlags,Name=require("./name.js").Name,WireFormat=require("./encoding/wire-format.js").WireFormat,Blob=require("./util/blob.js").Blob,ControlParameters=function ControlParameters(b){"object"===typeof b&&b instanceof ControlParameters?(this.name=null==b.name?null:new Name(b.name),this.faceId=b.faceId,this.uri=b.uri,this.localControlFeature=b.localControlFeature,this.origin=b.origin,this.cost=b.cost,this.forwardingFlags=new ForwardingFlags(b.forwardingFlags),
+this.strategy=new Name(b.strategy),this.expirationPeriod=b.expirationPeriod):(this.faceId=this.name=null,this.uri="",this.cost=this.origin=this.localControlFeature=null,this.forwardingFlags=new ForwardingFlags,this.strategy=new Name,this.expirationPeriod=null)};exports.ControlParameters=ControlParameters;
+ControlParameters.prototype.clear=function(){this.faceId=this.name=null;this.uri="";this.cost=this.origin=this.localControlFeature=null;this.forwardingFlags=new ForwardingFlags;this.strategy=new Name;this.expirationPeriod=null};ControlParameters.prototype.wireEncode=function(a){a=a||WireFormat.getDefaultWireFormat();return a.encodeControlParameters(this)};
+ControlParameters.prototype.wireDecode=function(a,b){b=b||WireFormat.getDefaultWireFormat();"object"===typeof a&&a instanceof Blob?b.decodeControlParameters(this,a.buf(),!1):b.decodeControlParameters(this,a,!0)};ControlParameters.prototype.getName=function(){return this.name};ControlParameters.prototype.getFaceId=function(){return this.faceId};ControlParameters.prototype.getUri=function(){return this.uri};ControlParameters.prototype.getLocalControlFeature=function(){return this.localControlFeature};
+ControlParameters.prototype.getOrigin=function(){return this.origin};ControlParameters.prototype.getCost=function(){return this.cost};ControlParameters.prototype.getForwardingFlags=function(){return this.forwardingFlags};ControlParameters.prototype.getStrategy=function(){return this.strategy};ControlParameters.prototype.getExpirationPeriod=function(){return this.expirationPeriod};ControlParameters.prototype.setName=function(a){this.name="object"===typeof a&&a instanceof Name?new Name(a):null};
+ControlParameters.prototype.setFaceId=function(a){this.faceId=a};ControlParameters.prototype.setUri=function(a){this.uri=a||""};ControlParameters.prototype.setLocalControlFeature=function(a){this.localControlFeature=a};ControlParameters.prototype.setOrigin=function(a){this.origin=a};ControlParameters.prototype.setCost=function(a){this.cost=a};ControlParameters.prototype.setForwardingFlags=function(a){this.forwardingFlags="object"===typeof a&&a instanceof ForwardingFlags?new ForwardingFlags(a):new ForwardingFlags};
+ControlParameters.prototype.setStrategy=function(a){this.strategy="object"===typeof a&&a instanceof Name?new Name(a):new Name};ControlParameters.prototype.setExpirationPeriod=function(a){this.expirationPeriod=a};
+var ControlParameters=require("./control-parameters.js").ControlParameters,WireFormat=require("./encoding/wire-format.js").WireFormat,Blob=require("./util/blob.js").Blob,ControlResponse=function ControlResponse(b){"object"===typeof b&&b instanceof ControlResponse?(this.statusCode_=b.statusCode_,this.statusText_=b.statusText_,this.bodyAsControlParameters_=null==b.bodyAsControlParameters_?null:new ControlParameters(b.bodyAsControlParameters_)):(this.statusCode_=null,this.statusText_="",this.bodyAsControlParameters_=
+null)};exports.ControlResponse=ControlResponse;ControlResponse.prototype.clear=function(){this.statusCode_=null;this.statusText_="";this.bodyAsControlParameters_=null};ControlResponse.prototype.wireEncode=function(a){a=a||WireFormat.getDefaultWireFormat();return a.encodeControlResponse(this)};ControlResponse.prototype.wireDecode=function(a,b){b=b||WireFormat.getDefaultWireFormat();"object"===typeof a&&a instanceof Blob?b.decodeControlResponse(this,a.buf(),!1):b.decodeControlResponse(this,a,!0)};
+ControlResponse.prototype.getStatusCode=function(){return this.statusCode_};ControlResponse.prototype.getStatusText=function(){return this.statusText_};ControlResponse.prototype.getBodyAsControlParameters=function(){return this.bodyAsControlParameters_};ControlResponse.prototype.setStatusCode=function(a){this.statusCode_=a;return this};ControlResponse.prototype.setStatusText=function(a){this.statusText_=a||"";return this};
+ControlResponse.prototype.setBodyAsControlParameters=function(a){this.bodyAsControlParameters_="object"===typeof a&&a instanceof ControlParameters?new ControlParameters(a):null;return this};Name=require("./name.js").Name;NdnRegexMatcher=require("./util/ndn-regex-matcher.js").NdnRegexMatcher;
+InterestFilter=function InterestFilter(b,c){"object"===typeof b&&b instanceof InterestFilter?(this.prefix=new Name(b.prefix),this.regexFilter=b.regexFilter,this.regexFilterPattern=b.regexFilterPattern):(this.prefix=new Name(b),c?(this.regexFilter=c,this.regexFilterPattern=InterestFilter.makePattern(c)):this.regexFilterPattern=this.regexFilter=null)};exports.InterestFilter=InterestFilter;
+InterestFilter.prototype.doesMatch=function(a){return a.size()<this.prefix.size()?!1:this.hasRegexFilter()?this.prefix.match(a)?null!=NdnRegexMatcher.match(this.regexFilterPattern,a.getSubName(this.prefix.size())):!1:this.prefix.match(a)};InterestFilter.prototype.getPrefix=function(){return this.prefix};InterestFilter.prototype.hasRegexFilter=function(){return null!=this.regexFilter};InterestFilter.prototype.getRegexFilter=function(){return this.regexFilter};
+InterestFilter.makePattern=function(a){if(0==a.length)return"^$";"^"!=a[0]&&(a="^"+a);"$"!=a[a.length-1]&&(a+="$");return a};var Name=require("./name.js").Name,Blob=require("./util/blob.js").Blob,WireFormat=require("./encoding/wire-format.js").WireFormat,DelegationSet=function DelegationSet(b){this.delegations_="object"===typeof b&&b instanceof DelegationSet?b.delegations_.slice(0):[]};exports.DelegationSet=DelegationSet;DelegationSet.Delegation=function(a,b){this.preference_=a;this.name_=new Name(b)};
+DelegationSet.Delegation.prototype.getPreference=function(){return this.preference_};DelegationSet.Delegation.prototype.getName=function(){return this.name_};DelegationSet.Delegation.prototype.compare=function(a){return this.preference_<a.preference_?-1:this.preference_>a.preference_?1:this.name_.compare(a.name_)};
+DelegationSet.prototype.add=function(a,b){this.remove(b);for(var c=new DelegationSet.Delegation(a,b),d=0;d<this.delegations_.length&&!(0<=this.delegations_[d].compare(c));)++d;this.delegations_.splice(d,0,c)};DelegationSet.prototype.addUnsorted=function(a,b){this.delegations_.push(new DelegationSet.Delegation(a,b))};DelegationSet.prototype.remove=function(a){for(var b=!1,c=this.delegations_.length-1;0<=c;--c)this.delegations_[c].getName().equals(a)&&(b=!0,this.delegations_.splice(c,1));return b};
+DelegationSet.prototype.clear=function(){this.delegations_=[]};DelegationSet.prototype.size=function(){return this.delegations_.length};DelegationSet.prototype.get=function(a){return this.delegations_[a]};DelegationSet.prototype.find=function(a){for(var b=0;b<this.delegations_.length;++b)if(this.delegations_[b].getName().equals(a))return b;return-1};DelegationSet.prototype.wireEncode=function(a){a=a||WireFormat.getDefaultWireFormat();return a.encodeDelegationSet(this)};
+DelegationSet.prototype.wireDecode=function(a,b){b=b||WireFormat.getDefaultWireFormat();"object"===typeof a&&a instanceof Blob?b.decodeDelegationSet(this,a.buf(),!1):b.decodeDelegationSet(this,a,!0)};DelegationSet=require("./delegation-set.js").DelegationSet;ContentType=require("./meta-info.js").ContentType;WireFormat=require("./encoding/wire-format.js").WireFormat;Data=require("./data.js").Data;
+Link=function(a){this.delegations_=new DelegationSet;if(a instanceof Data){if(Data.call(this,a),!this.getContent().isNull())try{this.delegations_.wireDecode(this.getContent()),this.getMetaInfo().setType(ContentType.LINK)}catch(b){this.delegations_.clear()}}else void 0!=a?Data.call(this,a):Data.call(this),this.getMetaInfo().setType(ContentType.LINK)};Link.prototype=new Data;Link.prototype.name="Link";exports.Link=Link;
+Link.prototype.wireDecode=function(a,b){b=b||WireFormat.getDefaultWireFormat();Data.prototype.wireDecode.call(this,a,b);if(this.getMetaInfo().getType()!=ContentType.LINK)throw Error("Link.wireDecode: MetaInfo ContentType is not LINK.");this.delegations_.wireDecode(this.getContent())};Link.prototype.addDelegation=function(a,b,c){c=c||WireFormat.getDefaultWireFormat();this.delegations_.add(a,b);this.encodeContent(c);return this};
+Link.prototype.removeDelegation=function(a,b){b=b||WireFormat.getDefaultWireFormat();var c=this.delegations_.remove(a);c&&this.encodeContent(b);return c};Link.prototype.getDelegations=function(){return this.delegations_};Link.prototype.encodeContent=function(a){this.setContent(this.delegations_.wireEncode(a));this.getMetaInfo().setType(ContentType.LINK)};var NetworkNack=function NetworkNack(){this.reason_=NetworkNack.Reason.NONE;this.otherReasonCode_=-1};exports.NetworkNack=NetworkNack;
+NetworkNack.Reason={NONE:0,CONGESTION:50,DUPLICATE:100,NO_ROUTE:150,OTHER_CODE:32767};NetworkNack.prototype.getReason=function(){return this.reason_};NetworkNack.prototype.getOtherReasonCode=function(){return this.otherReasonCode_};NetworkNack.prototype.setReason=function(a){this.reason_=a};NetworkNack.prototype.setOtherReasonCode=function(a){if(0>a)throw Error("NetworkNack other reason code must be non-negative");this.otherReasonCode_=a};
+NetworkNack.getFirstHeader=function(a){for(var b=0;b<a.countHeaderFields();++b){var c=a.getHeaderField(b);if(c instanceof NetworkNack)return c}return null};
+var Crypto=require("../crypto.js"),Blob=require("../util/blob.js").Blob,Name=require("../name.js").Name,ForwardingFlags=require("../forwarding-flags").ForwardingFlags,Tlv=require("./tlv/tlv.js").Tlv,TlvEncoder=require("./tlv/tlv-encoder.js").TlvEncoder,TlvDecoder=require("./tlv/tlv-decoder.js").TlvDecoder,WireFormat=require("./wire-format.js").WireFormat,Exclude=require("../exclude.js").Exclude,ContentType=require("../meta-info.js").ContentType,KeyLocatorType=require("../key-locator.js").KeyLocatorType,
+Sha256WithRsaSignature=require("../sha256-with-rsa-signature.js").Sha256WithRsaSignature,Sha256WithEcdsaSignature=require("../sha256-with-ecdsa-signature.js").Sha256WithEcdsaSignature,GenericSignature=require("../generic-signature.js").GenericSignature,HmacWithSha256Signature=require("../hmac-with-sha256-signature.js").HmacWithSha256Signature,DigestSha256Signature=require("../digest-sha256-signature.js").DigestSha256Signature,ControlParameters=require("../control-parameters.js").ControlParameters,
+ForwardingFlags=require("../forwarding-flags.js").ForwardingFlags,NetworkNack=require("../network-nack.js").NetworkNack,IncomingFaceId=require("../lp/incoming-face-id.js").IncomingFaceId,DecodingException=require("./decoding-exception.js").DecodingException,Tlv0_2WireFormat=function(){WireFormat.call(this)};Tlv0_2WireFormat.prototype=new WireFormat;Tlv0_2WireFormat.prototype.name="Tlv0_2WireFormat";exports.Tlv0_2WireFormat=Tlv0_2WireFormat;Tlv0_2WireFormat.instance=null;
+Tlv0_2WireFormat.prototype.encodeName=function(a){var b=new TlvEncoder;Tlv0_2WireFormat.encodeName(a,b);return new Blob(b.getOutput(),!1)};Tlv0_2WireFormat.prototype.decodeName=function(a,b,c){null==c&&(c=!0);b=new TlvDecoder(b);Tlv0_2WireFormat.decodeName(a,b,c)};
+Tlv0_2WireFormat.prototype.encodeInterest=function(a){var b=new TlvEncoder(256),c=b.getLength();b.writeOptionalNonNegativeIntegerTlv(Tlv.SelectedDelegation,a.getSelectedDelegationIndex());var d=a.getLinkWireEncoding(this);d.isNull()||b.writeBuffer(d.buf());b.writeOptionalNonNegativeIntegerTlv(Tlv.InterestLifetime,a.getInterestLifetimeMilliseconds());if(a.getNonce().isNull()||0==a.getNonce().size())b.writeBlobTlv(Tlv.Nonce,Crypto.randomBytes(4));else if(4>a.getNonce().size()){d=Buffer(4);a.getNonce().buf().copy(d);
+for(var e=a.getNonce().size();4>e;++e)d[e]=Crypto.randomBytes(1)[0];b.writeBlobTlv(Tlv.Nonce,d)}else 4==a.getNonce().size()?b.writeBlobTlv(Tlv.Nonce,a.getNonce().buf()):b.writeBlobTlv(Tlv.Nonce,a.getNonce().buf().slice(0,4));Tlv0_2WireFormat.encodeSelectors(a,b);d=Tlv0_2WireFormat.encodeName(a.getName(),b);a=b.getLength()-d.signedPortionBeginOffset;d=b.getLength()-d.signedPortionEndOffset;b.writeTypeAndLength(Tlv.Interest,b.getLength()-c);c=b.getLength()-a;a=b.getLength()-d;return{encoding:new Blob(b.getOutput(),
+!1),signedPortionBeginOffset:c,signedPortionEndOffset:a}};
+Tlv0_2WireFormat.prototype.decodeInterest=function(a,b,c){null==c&&(c=!0);b=new TlvDecoder(b);var d=b.readNestedTlvsStart(Tlv.Interest),e=Tlv0_2WireFormat.decodeName(a.getName(),b,c);b.peekType(Tlv.Selectors,d)&&Tlv0_2WireFormat.decodeSelectors(a,b,c);var f=b.readBlobTlv(Tlv.Nonce);a.setInterestLifetimeMilliseconds(b.readOptionalNonNegativeIntegerTlv(Tlv.InterestLifetime,d));if(b.peekType(Tlv.Data,d)){var g=b.getOffset(),h=b.readNestedTlvsStart(Tlv.Data);b.seek(h);a.setLinkWireEncoding(new Blob(b.getSlice(g,
+h),c),this)}else a.unsetLink();a.setSelectedDelegationIndex(b.readOptionalNonNegativeIntegerTlv(Tlv.SelectedDelegation,d));if(null!=a.getSelectedDelegationIndex()&&0<=a.getSelectedDelegationIndex()&&!a.hasLink())throw Error("Interest has a selected delegation, but no link object");a.setNonce(new Blob(f,c));b.finishNestedTlvs(d);return e};
+Tlv0_2WireFormat.prototype.encodeData=function(a){var b=new TlvEncoder(1500),c=b.getLength();b.writeBlobTlv(Tlv.SignatureValue,a.getSignature().getSignature().buf());var d=b.getLength();Tlv0_2WireFormat.encodeSignatureInfo_(a.getSignature(),b);b.writeBlobTlv(Tlv.Content,a.getContent().buf());Tlv0_2WireFormat.encodeMetaInfo(a.getMetaInfo(),b);Tlv0_2WireFormat.encodeName(a.getName(),b);a=b.getLength();b.writeTypeAndLength(Tlv.Data,b.getLength()-c);c=b.getLength()-a;d=b.getLength()-d;return{encoding:new Blob(b.getOutput(),
+!1),signedPortionBeginOffset:c,signedPortionEndOffset:d}};
+Tlv0_2WireFormat.prototype.decodeData=function(a,b,c){null==c&&(c=!0);b=new TlvDecoder(b);var d=b.readNestedTlvsStart(Tlv.Data),e=b.getOffset();Tlv0_2WireFormat.decodeName(a.getName(),b,c);Tlv0_2WireFormat.decodeMetaInfo(a.getMetaInfo(),b,c);a.setContent(new Blob(b.readBlobTlv(Tlv.Content),c));Tlv0_2WireFormat.decodeSignatureInfo(a,b,c);var f=b.getOffset();a.getSignature().setSignature(new Blob(b.readBlobTlv(Tlv.SignatureValue),c));b.finishNestedTlvs(d);return{signedPortionBeginOffset:e,signedPortionEndOffset:f}};
+Tlv0_2WireFormat.prototype.encodeControlParameters=function(a){var b=new TlvEncoder(256);Tlv0_2WireFormat.encodeControlParameters(a,b);return new Blob(b.getOutput(),!1)};Tlv0_2WireFormat.prototype.decodeControlParameters=function(a,b,c){null==c&&(c=!0);b=new TlvDecoder(b);Tlv0_2WireFormat.decodeControlParameters(a,b,c)};
+Tlv0_2WireFormat.prototype.encodeControlResponse=function(a){var b=new TlvEncoder(256),c=b.getLength();null!=a.getBodyAsControlParameters()&&Tlv0_2WireFormat.encodeControlParameters(a.getBodyAsControlParameters(),b);b.writeBlobTlv(Tlv.NfdCommand_StatusText,(new Blob(a.getStatusText())).buf());b.writeNonNegativeIntegerTlv(Tlv.NfdCommand_StatusCode,a.getStatusCode());b.writeTypeAndLength(Tlv.NfdCommand_ControlResponse,b.getLength()-c);return new Blob(b.getOutput(),!1)};
+Tlv0_2WireFormat.prototype.decodeControlResponse=function(a,b,c){null==c&&(c=!0);b=new TlvDecoder(b);var d=b.readNestedTlvsStart(Tlv.NfdCommand_ControlResponse);a.setStatusCode(b.readNonNegativeIntegerTlv(Tlv.NfdCommand_StatusCode));var e=new Blob(b.readBlobTlv(Tlv.NfdCommand_StatusText),!1);a.setStatusText(e.toString());b.peekType(Tlv.ControlParameters_ControlParameters,d)?(a.setBodyAsControlParameters(new ControlParameters),Tlv0_2WireFormat.decodeControlParameters(a.getBodyAsControlParameters(),
+b,c)):a.setBodyAsControlParameters(null);b.finishNestedTlvs(d)};Tlv0_2WireFormat.prototype.encodeSignatureInfo=function(a){var b=new TlvEncoder(256);Tlv0_2WireFormat.encodeSignatureInfo_(a,b);return new Blob(b.getOutput(),!1)};Tlv0_2WireFormat.SignatureHolder=function(){};Tlv0_2WireFormat.SignatureHolder.prototype.setSignature=function(a){this.signature=a};Tlv0_2WireFormat.SignatureHolder.prototype.getSignature=function(){return this.signature};
+Tlv0_2WireFormat.prototype.decodeSignatureInfoAndValue=function(a,b,c){null==c&&(c=!0);var d=new Tlv0_2WireFormat.SignatureHolder;a=new TlvDecoder(a);Tlv0_2WireFormat.decodeSignatureInfo(d,a,c);a=new TlvDecoder(b);d.getSignature().setSignature(new Blob(a.readBlobTlv(Tlv.SignatureValue),c));return d.getSignature()};Tlv0_2WireFormat.prototype.encodeSignatureValue=function(a){var b=new TlvEncoder(256);b.writeBlobTlv(Tlv.SignatureValue,a.getSignature().buf());return new Blob(b.getOutput(),!1)};
+Tlv0_2WireFormat.prototype.decodeLpPacket=function(a,b,c){null==c&&(c=!0);a.clear();for(var d=new TlvDecoder(b),e=d.readNestedTlvsStart(Tlv.LpPacket_LpPacket);d.getOffset()<e;){var f=d.readVarNumber(),g=d.readVarNumber(),h=d.getOffset()+g;if(h>b.length)throw new DecodingException(Error("TLV length exceeds the buffer length"));if(f==Tlv.LpPacket_Fragment){a.setFragmentWireEncoding(new Blob(d.getSlice(d.getOffset(),h),c));d.seek(h);break}else if(f==Tlv.LpPacket_Nack)g=new NetworkNack,f=d.readOptionalNonNegativeIntegerTlv(Tlv.LpPacket_NackReason,
+h),0>f||f==NetworkNack.Reason.NONE?g.setReason(NetworkNack.Reason.NONE):f==NetworkNack.Reason.CONGESTION||f==NetworkNack.Reason.DUPLICATE||f==NetworkNack.Reason.NO_ROUTE?g.setReason(f):(g.setReason(NetworkNack.Reason.OTHER_CODE),g.setOtherReasonCode(f)),a.addHeaderField(g);else if(f==Tlv.LpPacket_IncomingFaceId)f=new IncomingFaceId,f.setFaceId(d.readNonNegativeInteger(g)),a.addHeaderField(f);else{if(!(f>=Tlv.LpPacket_IGNORE_MIN&&f<=Tlv.LpPacket_IGNORE_MAX&&1===(f&1)))throw new DecodingException(Error("Did not get the expected TLV type"));
+d.seek(h)}d.finishNestedTlvs(h)}d.finishNestedTlvs(e)};Tlv0_2WireFormat.prototype.encodeDelegationSet=function(a){for(var b=new TlvEncoder(256),c=a.size()-1;0<=c;--c){var d=b.getLength();Tlv0_2WireFormat.encodeName(a.get(c).getName(),b);b.writeNonNegativeIntegerTlv(Tlv.Link_Preference,a.get(c).getPreference());b.writeTypeAndLength(Tlv.Link_Delegation,b.getLength()-d)}return new Blob(b.getOutput(),!1)};
+Tlv0_2WireFormat.prototype.decodeDelegationSet=function(a,b,c){null==c&&(c=!0);var d=new TlvDecoder(b);b=b.length;for(a.clear();d.getOffset()<b;){d.readTypeAndLength(Tlv.Link_Delegation);var e=d.readNonNegativeIntegerTlv(Tlv.Link_Preference),f=new Name;Tlv0_2WireFormat.decodeName(f,d,c);a.addUnsorted(e,f)}};
+Tlv0_2WireFormat.prototype.encodeEncryptedContent=function(a){var b=new TlvEncoder(256),c=b.getLength();b.writeBlobTlv(Tlv.Encrypt_EncryptedPayload,a.getPayload().buf());b.writeOptionalBlobTlv(Tlv.Encrypt_InitialVector,a.getInitialVector().buf());b.writeNonNegativeIntegerTlv(Tlv.Encrypt_EncryptionAlgorithm,a.getAlgorithmType());Tlv0_2WireFormat.encodeKeyLocator(Tlv.KeyLocator,a.getKeyLocator(),b);b.writeTypeAndLength(Tlv.Encrypt_EncryptedContent,b.getLength()-c);return new Blob(b.getOutput(),!1)};
+Tlv0_2WireFormat.prototype.decodeEncryptedContent=function(a,b,c){null==c&&(c=!0);b=new TlvDecoder(b);var d=b.readNestedTlvsStart(Tlv.Encrypt_EncryptedContent);Tlv0_2WireFormat.decodeKeyLocator(Tlv.KeyLocator,a.getKeyLocator(),b,c);a.setAlgorithmType(b.readNonNegativeIntegerTlv(Tlv.Encrypt_EncryptionAlgorithm));a.setInitialVector(new Blob(b.readOptionalBlobTlv(Tlv.Encrypt_InitialVector,d),c));a.setPayload(new Blob(b.readBlobTlv(Tlv.Encrypt_EncryptedPayload),c));b.finishNestedTlvs(d)};
+Tlv0_2WireFormat.get=function(){null===Tlv0_2WireFormat.instance&&(Tlv0_2WireFormat.instance=new Tlv0_2WireFormat);return Tlv0_2WireFormat.instance};Tlv0_2WireFormat.encodeNameComponent=function(a,b){var c=a.isImplicitSha256Digest()?Tlv.ImplicitSha256DigestComponent:Tlv.NameComponent;b.writeBlobTlv(c,a.getValue().buf())};
+Tlv0_2WireFormat.decodeNameComponent=function(a,b){null==b&&(b=!0);var c=a.getOffset(),d=a.readVarNumber();a.seek(c);c=new Blob(a.readBlobTlv(d),b);return d===Tlv.ImplicitSha256DigestComponent?Name.Component.fromImplicitSha256Digest(c):new Name.Component(c)};
+Tlv0_2WireFormat.encodeName=function(a,b){for(var c=b.getLength(),d,e=a.size()-1;0<=e;--e)Tlv0_2WireFormat.encodeNameComponent(a.get(e),b),e==a.size()-1&&(d=b.getLength());e=b.getLength();b.writeTypeAndLength(Tlv.Name,b.getLength()-c);c=b.getLength()-e;d=0==a.size()?c:b.getLength()-d;return{signedPortionBeginOffset:c,signedPortionEndOffset:d}};
+Tlv0_2WireFormat.decodeName=function(a,b,c){a.clear();for(var d=b.readNestedTlvsStart(Tlv.Name),e=b.getOffset(),f=e;b.getOffset()<d;)f=b.getOffset(),a.append(Tlv0_2WireFormat.decodeNameComponent(b,c));b.finishNestedTlvs(d);return{signedPortionBeginOffset:e,signedPortionEndOffset:f}};
+Tlv0_2WireFormat.encodeSelectors=function(a,b){var c=b.getLength();a.getMustBeFresh()&&b.writeTypeAndLength(Tlv.MustBeFresh,0);b.writeOptionalNonNegativeIntegerTlv(Tlv.ChildSelector,a.getChildSelector());0<a.getExclude().size()&&Tlv0_2WireFormat.encodeExclude(a.getExclude(),b);null!=a.getKeyLocator().getType()&&Tlv0_2WireFormat.encodeKeyLocator(Tlv.PublisherPublicKeyLocator,a.getKeyLocator(),b);b.writeOptionalNonNegativeIntegerTlv(Tlv.MaxSuffixComponents,a.getMaxSuffixComponents());b.writeOptionalNonNegativeIntegerTlv(Tlv.MinSuffixComponents,
+a.getMinSuffixComponents());b.getLength()!=c&&b.writeTypeAndLength(Tlv.Selectors,b.getLength()-c)};
+Tlv0_2WireFormat.decodeSelectors=function(a,b,c){null==c&&(c=!0);var d=b.readNestedTlvsStart(Tlv.Selectors);a.setMinSuffixComponents(b.readOptionalNonNegativeIntegerTlv(Tlv.MinSuffixComponents,d));a.setMaxSuffixComponents(b.readOptionalNonNegativeIntegerTlv(Tlv.MaxSuffixComponents,d));b.peekType(Tlv.PublisherPublicKeyLocator,d)?Tlv0_2WireFormat.decodeKeyLocator(Tlv.PublisherPublicKeyLocator,a.getKeyLocator(),b,c):a.getKeyLocator().clear();b.peekType(Tlv.Exclude,d)?Tlv0_2WireFormat.decodeExclude(a.getExclude(),
+b,c):a.getExclude().clear();a.setChildSelector(b.readOptionalNonNegativeIntegerTlv(Tlv.ChildSelector,d));a.setMustBeFresh(b.readBooleanTlv(Tlv.MustBeFresh,d));b.finishNestedTlvs(d)};Tlv0_2WireFormat.encodeExclude=function(a,b){for(var c=b.getLength(),d=a.size()-1;0<=d;--d){var e=a.get(d);e==Exclude.ANY?b.writeTypeAndLength(Tlv.Any,0):Tlv0_2WireFormat.encodeNameComponent(e,b)}b.writeTypeAndLength(Tlv.Exclude,b.getLength()-c)};
+Tlv0_2WireFormat.decodeExclude=function(a,b,c){null==c&&(c=!0);var d=b.readNestedTlvsStart(Tlv.Exclude);for(a.clear();b.getOffset()<d;)b.peekType(Tlv.Any,d)?(b.readBooleanTlv(Tlv.Any,d),a.appendAny()):a.appendComponent(Tlv0_2WireFormat.decodeNameComponent(b,c));b.finishNestedTlvs(d)};
+Tlv0_2WireFormat.encodeKeyLocator=function(a,b,c){var d=c.getLength();if(null!=b.getType())if(b.getType()==KeyLocatorType.KEYNAME)Tlv0_2WireFormat.encodeName(b.getKeyName(),c);else if(b.getType()==KeyLocatorType.KEY_LOCATOR_DIGEST&&0<b.getKeyData().size())c.writeBlobTlv(Tlv.KeyLocatorDigest,b.getKeyData().buf());else throw Error("Unrecognized KeyLocatorType "+b.getType());c.writeTypeAndLength(a,c.getLength()-d)};
+Tlv0_2WireFormat.decodeKeyLocator=function(a,b,c,d){null==d&&(d=!0);a=c.readNestedTlvsStart(a);b.clear();if(c.getOffset()!=a){if(c.peekType(Tlv.Name,a))b.setType(KeyLocatorType.KEYNAME),Tlv0_2WireFormat.decodeName(b.getKeyName(),c,d);else if(c.peekType(Tlv.KeyLocatorDigest,a))b.setType(KeyLocatorType.KEY_LOCATOR_DIGEST),b.setKeyData(new Blob(c.readBlobTlv(Tlv.KeyLocatorDigest),d));else throw new DecodingException(Error("decodeKeyLocator: Unrecognized key locator type"));c.finishNestedTlvs(a)}};
+Tlv0_2WireFormat.encodeSignatureInfo_=function(a,b){if(a instanceof GenericSignature){var c=a.getSignatureInfoEncoding();try{var d=new TlvDecoder(c.buf()),e=d.readNestedTlvsStart(Tlv.SignatureInfo);d.readNonNegativeIntegerTlv(Tlv.SignatureType);d.finishNestedTlvs(e)}catch(f){throw Error("The GenericSignature encoding is not a valid NDN-TLV SignatureInfo: "+f.message);}b.writeBuffer(c.buf())}else{c=b.getLength();if(a instanceof Sha256WithRsaSignature)Tlv0_2WireFormat.encodeKeyLocator(Tlv.KeyLocator,
+a.getKeyLocator(),b),b.writeNonNegativeIntegerTlv(Tlv.SignatureType,Tlv.SignatureType_SignatureSha256WithRsa);else if(a instanceof Sha256WithEcdsaSignature)Tlv0_2WireFormat.encodeKeyLocator(Tlv.KeyLocator,a.getKeyLocator(),b),b.writeNonNegativeIntegerTlv(Tlv.SignatureType,Tlv.SignatureType_SignatureSha256WithEcdsa);else if(a instanceof HmacWithSha256Signature)Tlv0_2WireFormat.encodeKeyLocator(Tlv.KeyLocator,a.getKeyLocator(),b),b.writeNonNegativeIntegerTlv(Tlv.SignatureType,Tlv.SignatureType_SignatureHmacWithSha256);
+else if(a instanceof DigestSha256Signature)b.writeNonNegativeIntegerTlv(Tlv.SignatureType,Tlv.SignatureType_DigestSha256);else throw Error("encodeSignatureInfo: Unrecognized Signature object type");b.writeTypeAndLength(Tlv.SignatureInfo,b.getLength()-c)}};
+Tlv0_2WireFormat.decodeSignatureInfo=function(a,b,c){null==c&&(c=!0);var d=b.getOffset(),e=b.readNestedTlvsStart(Tlv.SignatureInfo),f=b.readNonNegativeIntegerTlv(Tlv.SignatureType);f==Tlv.SignatureType_SignatureSha256WithRsa?(a.setSignature(new Sha256WithRsaSignature),a=a.getSignature(),Tlv0_2WireFormat.decodeKeyLocator(Tlv.KeyLocator,a.getKeyLocator(),b,c)):f==Tlv.SignatureType_SignatureSha256WithEcdsa?(a.setSignature(new Sha256WithEcdsaSignature),a=a.getSignature(),Tlv0_2WireFormat.decodeKeyLocator(Tlv.KeyLocator,
+a.getKeyLocator(),b,c)):f==Tlv.SignatureType_SignatureHmacWithSha256?(a.setSignature(new HmacWithSha256Signature),a=a.getSignature(),Tlv0_2WireFormat.decodeKeyLocator(Tlv.KeyLocator,a.getKeyLocator(),b,c)):f==Tlv.SignatureType_DigestSha256?a.setSignature(new DigestSha256Signature):(a.setSignature(new GenericSignature),a=a.getSignature(),a.setSignatureInfoEncoding(new Blob(b.getSlice(d,e),c),f));b.finishNestedTlvs(e)};
+Tlv0_2WireFormat.encodeMetaInfo=function(a,b){var c=b.getLength(),d=a.getFinalBlockId().getValue().buf();null!=d&&0<d.length&&(d=b.getLength(),Tlv0_2WireFormat.encodeNameComponent(a.getFinalBlockId(),b),b.writeTypeAndLength(Tlv.FinalBlockId,b.getLength()-d));b.writeOptionalNonNegativeIntegerTlv(Tlv.FreshnessPeriod,a.getFreshnessPeriod());if(a.getType()!=ContentType.BLOB)if(a.getType()==ContentType.LINK||a.getType()==ContentType.KEY||a.getType()==ContentType.NACK)b.writeNonNegativeIntegerTlv(Tlv.ContentType,
+a.getType());else if(a.getType()==ContentType.OTHER_CODE)b.writeNonNegativeIntegerTlv(Tlv.ContentType,a.getOtherTypeCode());else throw Error("unrecognized TLV ContentType");b.writeTypeAndLength(Tlv.MetaInfo,b.getLength()-c)};
+Tlv0_2WireFormat.decodeMetaInfo=function(a,b,c){null==c&&(c=!0);var d=b.readNestedTlvsStart(Tlv.MetaInfo),e=b.readOptionalNonNegativeIntegerTlv(Tlv.ContentType,d);null==e||0>e||e===ContentType.BLOB?a.setType(ContentType.BLOB):e===ContentType.LINK||e===ContentType.KEY||e===ContentType.NACK?a.setType(e):(a.setType(ContentType.OTHER_CODE),a.setOtherTypeCode(e));a.setFreshnessPeriod(b.readOptionalNonNegativeIntegerTlv(Tlv.FreshnessPeriod,d));b.peekType(Tlv.FinalBlockId,d)?(e=b.readNestedTlvsStart(Tlv.FinalBlockId),
+a.setFinalBlockId(Tlv0_2WireFormat.decodeNameComponent(b,c)),b.finishNestedTlvs(e)):a.setFinalBlockId(null);b.finishNestedTlvs(d)};
+Tlv0_2WireFormat.encodeControlParameters=function(a,b){var c=b.getLength();b.writeOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_ExpirationPeriod,a.getExpirationPeriod());if(0<a.getStrategy().size()){var d=b.getLength();Tlv0_2WireFormat.encodeName(a.getStrategy(),b);b.writeTypeAndLength(Tlv.ControlParameters_Strategy,b.getLength()-d)}d=a.getForwardingFlags().getNfdForwardingFlags();d!=(new ForwardingFlags).getNfdForwardingFlags()&&b.writeNonNegativeIntegerTlv(Tlv.ControlParameters_Flags,d);b.writeOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_Cost,
+a.getCost());b.writeOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_Origin,a.getOrigin());b.writeOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_LocalControlFeature,a.getLocalControlFeature());0!=a.getUri().length&&b.writeBlobTlv(Tlv.ControlParameters_Uri,(new Blob(a.getUri())).buf());b.writeOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_FaceId,a.getFaceId());null!=a.getName()&&Tlv0_2WireFormat.encodeName(a.getName(),b);b.writeTypeAndLength(Tlv.ControlParameters_ControlParameters,b.getLength()-
+c)};
+Tlv0_2WireFormat.decodeControlParameters=function(a,b,c){null==c&&(c=!0);a.clear();var d=b.readNestedTlvsStart(Tlv.ControlParameters_ControlParameters);if(b.peekType(Tlv.Name,d)){var e=new Name;Tlv0_2WireFormat.decodeName(e,b,c);a.setName(e)}a.setFaceId(b.readOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_FaceId,d));b.peekType(Tlv.ControlParameters_Uri,d)&&(e=new Blob(b.readOptionalBlobTlv(Tlv.ControlParameters_Uri,d),!1),a.setUri(e.toString()));a.setLocalControlFeature(b.readOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_LocalControlFeature,d));
+a.setOrigin(b.readOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_Origin,d));a.setCost(b.readOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_Cost,d));b.peekType(Tlv.ControlParameters_Flags,d)&&(e=new ForwardingFlags,e.setNfdForwardingFlags(b.readNonNegativeIntegerTlv(Tlv.ControlParameters_Flags,d)),a.setForwardingFlags(e));b.peekType(Tlv.ControlParameters_Strategy,d)&&(e=b.readNestedTlvsStart(Tlv.ControlParameters_Strategy),Tlv0_2WireFormat.decodeName(a.getStrategy(),b,c),b.finishNestedTlvs(e));
+a.setExpirationPeriod(b.readOptionalNonNegativeIntegerTlv(Tlv.ControlParameters_ExpirationPeriod,d));b.finishNestedTlvs(d)};var Tlv0_2WireFormat=require("./tlv-0_2-wire-format.js").Tlv0_2WireFormat,Tlv0_1_1WireFormat=function(){Tlv0_2WireFormat.call(this)};Tlv0_1_1WireFormat.prototype=new Tlv0_2WireFormat;Tlv0_1_1WireFormat.prototype.name="Tlv0_1_1WireFormat";exports.Tlv0_1_1WireFormat=Tlv0_1_1WireFormat;Tlv0_1_1WireFormat.instance=null;
+Tlv0_1_1WireFormat.get=function(){null===Tlv0_1_1WireFormat.instance&&(Tlv0_1_1WireFormat.instance=new Tlv0_1_1WireFormat);return Tlv0_1_1WireFormat.instance};var WireFormat=require("./wire-format.js").WireFormat,Tlv0_1_1WireFormat=require("./tlv-0_1_1-wire-format.js").Tlv0_1_1WireFormat,Tlv0_1WireFormat=function(){Tlv0_1_1WireFormat.call(this)};Tlv0_1WireFormat.prototype=new Tlv0_1_1WireFormat;Tlv0_1WireFormat.prototype.name="Tlv0_1WireFormat";exports.Tlv0_1WireFormat=Tlv0_1WireFormat;
+Tlv0_1WireFormat.instance=null;Tlv0_1WireFormat.get=function(){null===Tlv0_1WireFormat.instance&&(Tlv0_1WireFormat.instance=new Tlv0_1WireFormat);return Tlv0_1WireFormat.instance};WireFormat=require("./wire-format.js").WireFormat;Tlv0_2WireFormat=require("./tlv-0_2-wire-format.js").Tlv0_2WireFormat;TlvWireFormat=function(){Tlv0_2WireFormat.call(this)};TlvWireFormat.prototype=new Tlv0_2WireFormat;TlvWireFormat.prototype.name="TlvWireFormat";exports.TlvWireFormat=TlvWireFormat;
+TlvWireFormat.instance=null;TlvWireFormat.get=function(){null===TlvWireFormat.instance&&(TlvWireFormat.instance=new TlvWireFormat);return TlvWireFormat.instance};WireFormat.setDefaultWireFormat(TlvWireFormat.get());
+var DataUtils=require("./data-utils.js").DataUtils,KeyLocatorType=require("../key-locator.js").KeyLocatorType,Interest=require("../interest.js").Interest,Data=require("../data.js").Data,Sha256WithRsaSignature=require("../sha256-with-rsa-signature.js").Sha256WithRsaSignature,Sha256WithEcdsaSignature=require("../sha256-with-ecdsa-signature.js").Sha256WithEcdsaSignature,HmacWithSha256Signature=require("../hmac-with-sha256-signature.js").HmacWithSha256Signature,DigestSha256Signature=require("../digest-sha256-signature.js").DigestSha256Signature,
+ContentType=require("../meta-info.js").ContentType,WireFormat=require("./wire-format.js").WireFormat,EncodingUtils=function(){};exports.EncodingUtils=EncodingUtils;EncodingUtils.encodeToHexInterest=function(a,b){b=b||WireFormat.getDefaultWireFormat();return DataUtils.toHex(a.wireEncode(b).buf())};EncodingUtils.encodeToHexData=function(a,b){b=b||WireFormat.getDefaultWireFormat();return DataUtils.toHex(a.wireEncode(b).buf())};
+EncodingUtils.decodeHexInterest=function(a,b){b=b||WireFormat.getDefaultWireFormat();var c=new Interest;c.wireDecode(DataUtils.toNumbers(a),b);return c};EncodingUtils.decodeHexData=function(a,b){b=b||WireFormat.getDefaultWireFormat();var c=new Data;c.wireDecode(DataUtils.toNumbers(a),b);return c};
+EncodingUtils.decodeSubjectPublicKeyInfo=function(a){a=DataUtils.toHex(a).toLowerCase();a=_x509_getPublicKeyHexArrayFromCertHex(a,_x509_getSubjectPublicKeyPosFromCertHex(a,0));var b=new RSAKey;b.setPublic(a[0],a[1]);return b};
+EncodingUtils.dataToHtml=function(a){function b(a){a=a.replace(/&/g,"&");a=a.replace(/</g,"<");c+=a;c+="<br/>"}if(-1==a)return"NO CONTENT FOUND";if(-2==a)return"CONTENT NAME IS EMPTY";var c="";b("name: "+a.getName().toUri());0<a.getContent().size()?(b("content (raw): "+a.getContent().buf().toString("binary")),b("content (hex): "+a.getContent().toHex())):b("content: <empty>");a.getMetaInfo().getType()!=ContentType.BLOB&&(a.getMetaInfo().getType()==ContentType.KEY?b("metaInfo.type: KEY"):a.getMetaInfo().getType()==
+ContentType.LINK?b("metaInfo.type: LINK"):a.getMetaInfo().getType()==ContentType.NACK?b("metaInfo.type: NACK"):a.getMetaInfo().getType()==ContentType.OTHER_CODE&&b("metaInfo.type: other code "+a.getMetaInfo().getOtherTypeCode()));b("metaInfo.freshnessPeriod (milliseconds): "+(0<=a.getMetaInfo().getFreshnessPeriod()?""+a.getMetaInfo().getFreshnessPeriod():"<none>"));b("metaInfo.finalBlockId: "+(0<a.getMetaInfo().getFinalBlockId().getValue().size()?a.getMetaInfo().getFinalBlockId().getValue().toHex():
+"<none>"));var d=null,e=a.getSignature();e instanceof Sha256WithRsaSignature?(e=a.getSignature(),b("Sha256WithRsa signature.signature: "+(0<e.getSignature().size()?e.getSignature().toHex():"<none>")),d=e.getKeyLocator()):e instanceof Sha256WithEcdsaSignature?(e=a.getSignature(),b("Sha256WithEcdsa signature.signature: "+(0<e.getSignature().size()?e.getSignature().toHex():"<none>")),d=e.getKeyLocator()):e instanceof HmacWithSha256Signature?(e=a.getSignature(),b("HmacWithSha256 signature.signature: "+
+(0<e.getSignature().size()?e.getSignature().toHex():"<none>")),d=e.getKeyLocator()):e instanceof DigestSha256Signature&&(e=a.getSignature(),b("DigestSha256 signature.signature: "+(0<e.getSignature().size()?e.getSignature().toHex():"<none>")));null!==d&&(null==d.getType()?b("signature.keyLocator: <none>"):d.getType()==KeyLocatorType.KEY_LOCATOR_DIGEST?b("signature.keyLocator: KeyLocatorDigest: "+d.getKeyData().toHex()):d.getType()==KeyLocatorType.KEYNAME?b("signature.keyLocator: KeyName: "+d.getKeyName().toUri()):
+b("signature.keyLocator: <unrecognized ndn_KeyLocatorType>"));return c};var encodeToHexInterest=function(a){return EncodingUtils.encodeToHexInterest(a)},decodeHexInterest=function(a){return EncodingUtils.decodeHexInterest(a)},decodeSubjectPublicKeyInfo=function(a){return EncodingUtils.decodeSubjectPublicKeyInfo(a)};function encodeToBinaryInterest(a){return a.wireEncode().buf()}
+var Crypto=require("../../crypto.js"),Blob=require("../../util/blob.js").Blob,DecryptKey=require("../decrypt-key.js").DecryptKey,EncryptKey=require("../encrypt-key.js").EncryptKey,EncryptAlgorithmType=require("./encrypt-params.js").EncryptAlgorithmType,UseSubtleCrypto=require("../../use-subtle-crypto-node.js").UseSubtleCrypto,SyncPromise=require("../../util/sync-promise.js").SyncPromise,AesAlgorithm=function(){};exports.AesAlgorithm=AesAlgorithm;
+AesAlgorithm.generateKey=function(a){a=Crypto.randomBytes(a.getKeySize()/8);return new DecryptKey(new Blob(a,!1))};AesAlgorithm.deriveEncryptKey=function(a){return new EncryptKey(a)};
+AesAlgorithm.decryptPromise=function(a,b,c,d){if(UseSubtleCrypto()&&!d&&c.getAlgorithmType()!=EncryptAlgorithmType.AesEcb)return c.getAlgorithmType()==EncryptAlgorithmType.AesCbc?crypto.subtle.importKey("raw",a.buf(),{name:"AES-CBC"},!1,["encrypt","decrypt"]).then(function(a){return crypto.subtle.decrypt({name:"AES-CBC",iv:c.getInitialVector().buf()},a,b.buf())}).then(function(a){return Promise.resolve(new Blob(new Uint8Array(a),!1))}):Promise.reject(Error("unsupported encryption mode"));if(c.getAlgorithmType()==
+EncryptAlgorithmType.AesEcb)try{var e=Crypto.createDecipheriv("aes-128-ecb",a.buf(),"");return SyncPromise.resolve(new Blob(Buffer.concat([e.update(b.buf()),e["final"]()]),!1))}catch(f){return SyncPromise.reject(f)}else if(c.getAlgorithmType()==EncryptAlgorithmType.AesCbc)try{return e=Crypto.createDecipheriv("aes-128-cbc",a.buf(),c.getInitialVector().buf()),SyncPromise.resolve(new Blob(Buffer.concat([e.update(b.buf()),e["final"]()]),!1))}catch(g){return SyncPromise.reject(g)}else return SyncPromise.reject(Error("unsupported encryption mode"))};
+AesAlgorithm.decrypt=function(a,b,c){return SyncPromise.getValue(this.decryptPromise(a,b,c,!0))};
+AesAlgorithm.encryptPromise=function(a,b,c,d){return c.getAlgorithmType()==EncryptAlgorithmType.AesCbc&&c.getInitialVector().size()!=AesAlgorithm.BLOCK_SIZE?SyncPromise.reject(Error("incorrect initial vector size")):UseSubtleCrypto()&&!d&&c.getAlgorithmType()!=EncryptAlgorithmType.AesEcb?c.getAlgorithmType()==EncryptAlgorithmType.AesCbc?crypto.subtle.importKey("raw",a.buf(),{name:"AES-CBC"},!1,["encrypt","decrypt"]).then(function(a){return crypto.subtle.encrypt({name:"AES-CBC",iv:c.getInitialVector().buf()},
+a,b.buf())}).then(function(a){return Promise.resolve(new Blob(new Uint8Array(a),!1))}):Promise.reject(Error("unsupported encryption mode")):c.getAlgorithmType()==EncryptAlgorithmType.AesEcb?(a=Crypto.createCipheriv("aes-128-ecb",a.buf(),""),SyncPromise.resolve(new Blob(Buffer.concat([a.update(b.buf()),a["final"]()]),!1))):c.getAlgorithmType()==EncryptAlgorithmType.AesCbc?(a=Crypto.createCipheriv("aes-128-cbc",a.buf(),c.getInitialVector().buf()),SyncPromise.resolve(new Blob(Buffer.concat([a.update(b.buf()),
+a["final"]()]),!1))):SyncPromise.reject(Error("unsupported encryption mode"))};AesAlgorithm.encrypt=function(a,b,c){return SyncPromise.getValue(this.encryptPromise(a,b,c,!0))};AesAlgorithm.BLOCK_SIZE=16;Crypto=require("../../crypto.js");Blob=require("../../util/blob.js").Blob;EncryptAlgorithmType=function(){};exports.EncryptAlgorithmType=EncryptAlgorithmType;EncryptAlgorithmType.AesEcb=0;EncryptAlgorithmType.AesCbc=1;EncryptAlgorithmType.RsaPkcs=2;EncryptAlgorithmType.RsaOaep=3;
+var EncryptParams=function(a,b){this.algorithmType_=a;if(null!=b&&0<b){var c=Crypto.randomBytes(b);this.initialVector_=new Blob(c,!1)}else this.initialVector_=new Blob};exports.EncryptParams=EncryptParams;EncryptParams.prototype.getAlgorithmType=function(){return this.algorithmType_};EncryptParams.prototype.getInitialVector=function(){return this.initialVector_};EncryptParams.prototype.setAlgorithmType=function(a){this.algorithmType_=a;return this};
+EncryptParams.prototype.setInitialVector=function(a){this.initialVector_="object"===typeof a&&a instanceof Blob?a:new Blob(a);return this};
+var Crypto=require("../../crypto.js"),Name=require("../../name.js").Name,KeyLocator=require("../../key-locator.js").KeyLocator,KeyLocatorType=require("../../key-locator.js").KeyLocatorType,TlvWireFormat=require("../../encoding/tlv-wire-format.js").TlvWireFormat,Blob=require("../../util/blob.js").Blob,AesAlgorithm=require("./aes-algorithm.js").AesAlgorithm,RsaAlgorithm=require("./rsa-algorithm.js").RsaAlgorithm,EncryptParams=require("./encrypt-params.js").EncryptParams,EncryptAlgorithmType=require("./encrypt-params.js").EncryptAlgorithmType,
+EncryptedContent=require("../encrypted-content.js").EncryptedContent,SyncPromise=require("../../util/sync-promise.js").SyncPromise,Encryptor=function(a){};exports.Encryptor=Encryptor;Encryptor.NAME_COMPONENT_FOR=new Name.Component("FOR");Encryptor.NAME_COMPONENT_READ=new Name.Component("READ");Encryptor.NAME_COMPONENT_SAMPLE=new Name.Component("SAMPLE");Encryptor.NAME_COMPONENT_ACCESS=new Name.Component("ACCESS");Encryptor.NAME_COMPONENT_E_KEY=new Name.Component("E-KEY");
+Encryptor.NAME_COMPONENT_D_KEY=new Name.Component("D-KEY");Encryptor.NAME_COMPONENT_C_KEY=new Name.Component("C-KEY");
+Encryptor.encryptDataPromise=function(a,b,c,d,e,f){a.getName().append(Encryptor.NAME_COMPONENT_FOR).append(c);var g=e.getAlgorithmType();return g==EncryptAlgorithmType.AesCbc||g==EncryptAlgorithmType.AesEcb?Encryptor.encryptSymmetricPromise_(b,d,c,e,f).then(function(b){a.setContent(b.wireEncode(TlvWireFormat.get()));return SyncPromise.resolve()}):g==EncryptAlgorithmType.RsaPkcs||g==EncryptAlgorithmType.RsaOaep?Encryptor.encryptAsymmetricPromise_(b,d,c,e,f).then(function(b){a.setContent(b.wireEncode(TlvWireFormat.get()));
+return SyncPromise.resolve()},function(g){if(0>g.message.indexOf("data too large for key size"))throw g;g=Crypto.randomBytes(16);var k=new Blob(g,!1);g=new Name(c);g.append("nonce");var l=new EncryptParams(EncryptAlgorithmType.AesCbc,AesAlgorithm.BLOCK_SIZE),m;return Encryptor.encryptSymmetricPromise_(b,k,g,l,f).then(function(a){m=a;return Encryptor.encryptAsymmetricPromise_(k,d,c,e,f)}).then(function(b){var c=m.wireEncode();b=b.wireEncode();var d=new Buffer(c.size()+b.size());b.buf().copy(d,0);c.buf().copy(d,
+b.size());a.setContent(new Blob(d,!1));return SyncPromise.resolve()})}):SyncPromise.reject(Error("Unsupported encryption method"))};Encryptor.encryptData=function(a,b,c,d,e){return SyncPromise.getValue(Encryptor.encryptDataPromise(a,b,c,d,e,!0))};
+Encryptor.encryptSymmetricPromise_=function(a,b,c,d,e){var f=d.getAlgorithmType(),g=d.getInitialVector(),h=new KeyLocator;h.setType(KeyLocatorType.KEYNAME);h.setKeyName(c);return f==EncryptAlgorithmType.AesCbc||f==EncryptAlgorithmType.AesEcb?f==EncryptAlgorithmType.AesCbc&&g.size()!=AesAlgorithm.BLOCK_SIZE?SyncPromise.reject(Error("incorrect initial vector size")):AesAlgorithm.encryptPromise(b,a,d,e).then(function(a){var b=new EncryptedContent;b.setAlgorithmType(f);b.setKeyLocator(h);b.setPayload(a);
+b.setInitialVector(g);return SyncPromise.resolve(b)}):SyncPromise.reject(Error("Unsupported encryption method"))};
+Encryptor.encryptAsymmetricPromise_=function(a,b,c,d,e){var f=d.getAlgorithmType(),g=new KeyLocator;g.setType(KeyLocatorType.KEYNAME);g.setKeyName(c);return f==EncryptAlgorithmType.RsaPkcs||f==EncryptAlgorithmType.RsaOaep?RsaAlgorithm.encryptPromise(b,a,d,e).then(function(a){var b=new EncryptedContent;b.setAlgorithmType(f);b.setKeyLocator(g);b.setPayload(a);return SyncPromise.resolve(b)}):SyncPromise.reject(Error("Unsupported encryption method"))};
+var constants=require("constants"),Crypto=require("../../crypto.js"),Blob=require("../../util/blob.js").Blob,DecryptKey=require("../decrypt-key.js").DecryptKey,EncryptKey=require("../encrypt-key.js").EncryptKey,EncryptAlgorithmType=require("./encrypt-params.js").EncryptAlgorithmType,DerNode=require("../../encoding/der/der-node.js").DerNode,OID=require("../../encoding/oid.js").OID,PrivateKeyStorage=require("../../security/identity/private-key-storage.js").PrivateKeyStorage,UseSubtleCrypto=require("../../use-subtle-crypto-node.js").UseSubtleCrypto,
+SyncPromise=require("../../util/sync-promise.js").SyncPromise,rsaKeygen=null;try{rsaKeygen=require("rsa-keygen")}catch(e$$48){}RsaAlgorithm=function(){};exports.RsaAlgorithm=RsaAlgorithm;
+RsaAlgorithm.generateKeyPromise=function(a,b){if(UseSubtleCrypto()&&!b)return crypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:a.getKeySize(),publicExponent:new Uint8Array([1,0,1]),hash:{name:"SHA-256"}},!0,["sign","verify"]).then(function(a){return crypto.subtle.exportKey("pkcs8",a.privateKey)}).then(function(a){return Promise.resolve(new DecryptKey(new Blob(new Uint8Array(a),!1)))});if(!rsaKeygen)return SyncPromise.reject(Error("Need to install rsa-keygen: sudo npm install rsa-keygen"));
+try{var c=rsaKeygen.generate(a.getKeySize()).private_key.toString().replace("-----BEGIN RSA PRIVATE KEY-----","").replace("-----END RSA PRIVATE KEY-----",""),d=new Buffer(c,"base64"),e=PrivateKeyStorage.encodePkcs8PrivateKey(d,new OID(PrivateKeyStorage.RSA_ENCRYPTION_OID),new DerNode.DerNull).buf();return SyncPromise.resolve(new DecryptKey(e))}catch(f){return SyncPromise.reject(f)}};RsaAlgorithm.generateKey=function(a){return SyncPromise.getValue(this.generateKeyPromise(a,!0))};
+RsaAlgorithm.deriveEncryptKey=function(a){a=RsaAlgorithm.getRsaPrivateKeyDer(a);var b=DerNode.parse(a.buf(),0).getChildren();a=b[1];var b=b[2],c=new DerNode.DerSequence;c.addChild(a);c.addChild(b);a=c.encode();b=new DerNode.DerSequence;b.addChild(new DerNode.DerOid(new OID(PrivateKeyStorage.RSA_ENCRYPTION_OID)));b.addChild(new DerNode.DerNull);c=new DerNode.DerSequence;c.addChild(b);c.addChild(new DerNode.DerBitString(a.buf(),0));return new EncryptKey(c.encode())};
+RsaAlgorithm.decryptPromise=function(a,b,c,d){if(UseSubtleCrypto()&&!d&&c.getAlgorithmType()!=EncryptAlgorithmType.RsaPkcs)return c.getAlgorithmType()==EncryptAlgorithmType.RsaOaep?crypto.subtle.importKey("pkcs8",a.buf(),{name:"RSA-OAEP",hash:{name:"SHA-1"}},!1,["decrypt"]).then(function(a){return crypto.subtle.decrypt({name:"RSA-OAEP"},a,b.buf())}).then(function(a){return Promise.resolve(new Blob(new Uint8Array(a),!1))}):Promise.reject(Error("unsupported padding scheme"));d=RsaAlgorithm.getRsaPrivateKeyDer(a).buf().toString("base64");
+a="-----BEGIN RSA PRIVATE KEY-----\n";for(var e=0;e<d.length;e+=64)a+=d.substr(e,64)+"\n";a+="-----END RSA PRIVATE KEY-----";if(c.getAlgorithmType()==EncryptAlgorithmType.RsaPkcs)c=constants.RSA_PKCS1_PADDING;else if(c.getAlgorithmType()==EncryptAlgorithmType.RsaOaep)c=constants.RSA_PKCS1_OAEP_PADDING;else return SyncPromise.reject(Error("unsupported padding scheme"));try{return SyncPromise.resolve(new Blob(Crypto.privateDecrypt({key:a,padding:c},b.buf()),!1))}catch(f){return SyncPromise.reject(f)}};
+RsaAlgorithm.decrypt=function(a,b,c){return SyncPromise.getValue(this.decryptPromise(a,b,c,!0))};
+RsaAlgorithm.encryptPromise=function(a,b,c,d){if(UseSubtleCrypto()&&!d&&c.getAlgorithmType()!=EncryptAlgorithmType.RsaPkcs)return c.getAlgorithmType()==EncryptAlgorithmType.RsaOaep?crypto.subtle.importKey("spki",a.buf(),{name:"RSA-OAEP",hash:{name:"SHA-1"}},!1,["encrypt"]).then(function(a){return crypto.subtle.encrypt({name:"RSA-OAEP"},a,b.buf())}).then(function(a){return Promise.resolve(new Blob(new Uint8Array(a),!1))}):Promise.reject(Error("unsupported padding scheme"));d=a.buf().toString("base64");
+a="-----BEGIN PUBLIC KEY-----\n";for(var e=0;e<d.length;e+=64)a+=d.substr(e,64)+"\n";a+="-----END PUBLIC KEY-----";if(c.getAlgorithmType()==EncryptAlgorithmType.RsaPkcs)c=constants.RSA_PKCS1_PADDING;else if(c.getAlgorithmType()==EncryptAlgorithmType.RsaOaep)c=constants.RSA_PKCS1_OAEP_PADDING;else return SyncPromise.reject(Error("unsupported padding scheme"));try{return SyncPromise.resolve(new Blob(Crypto.publicEncrypt({key:a,padding:c},b.buf()),!1))}catch(f){return SyncPromise.reject(f)}};
+RsaAlgorithm.encrypt=function(a,b,c){return SyncPromise.getValue(this.encryptPromise(a,b,c,!0))};RsaAlgorithm.getRsaPrivateKeyDer=function(a){a=DerNode.parse(a.buf(),0).getChildren();if(DerNode.getSequence(a,1).getChildren()[0].toVal()!=PrivateKeyStorage.RSA_ENCRYPTION_OID)throw Error("The PKCS #8 private key is not RSA_ENCRYPTION");return a[2].getPayload()};var SyncPromise=require("../util/sync-promise.js").SyncPromise,ConsumerDb=function(){};exports.ConsumerDb=ConsumerDb;
+ConsumerDb.Error=function(a){if(a)return a.__proto__=ConsumerDb.Error.prototype,a};ConsumerDb.Error.prototype=Error();ConsumerDb.Error.prototype.name="ConsumerDbError";ConsumerDb.prototype.getKeyPromise=function(a,b){return SyncPromise.reject(Error("ConsumerDb.getKeyPromise is not implemented"))};ConsumerDb.prototype.addKeyPromise=function(a,b,c){return SyncPromise.reject(Error("ConsumerDb.addKeyPromise is not implemented"))};ConsumerDb.prototype.deleteKeyPromise=function(a,b){return SyncPromise.reject(Error("ConsumerDb.addKeyPromise is not implemented"))};
+var Blob=require("../util/blob.js").Blob,Name=require("../name.js").Name,Interest=require("../interest.js").Interest,NetworkNack=require("../network-nack.js").NetworkNack,Link=require("../link.js").Link,EncryptedContent=require("./encrypted-content.js").EncryptedContent,EncryptError=require("./encrypt-error.js").EncryptError,EncryptParams=require("./algo/encrypt-params.js").EncryptParams,EncryptAlgorithmType=require("./algo/encrypt-params.js").EncryptAlgorithmType,RsaAlgorithm=require("./algo/rsa-algorithm.js").RsaAlgorithm,
+AesAlgorithm=require("./algo/aes-algorithm.js").AesAlgorithm,Encryptor=require("./algo/encryptor.js").Encryptor,SyncPromise=require("../util/sync-promise.js").SyncPromise,NdnCommon=require("../util/ndn-common.js").NdnCommon,Consumer=function Consumer(b,c,d,e,f,g,h){this.database_=f;this.keyChain_=c;this.face_=b;this.groupName_=new Name(d);this.consumerName_=new Name(e);this.cKeyLink_=void 0==g?Consumer.NO_LINK:new Link(g);this.dKeyLink_=void 0==h?Consumer.NO_LINK:new Link(h);this.cKeyMap_={};this.dKeyMap_=
+{}};exports.Consumer=Consumer;Consumer.prototype.consume=function(a,b,c,d){void 0==d&&(d=Consumer.NO_LINK);a=new Interest(a);var e=this;this.sendInterest_(a,1,new Link(d),function(a){e.decryptContent_(a,function(c){try{b(a,c)}catch(d){console.log("Error in onConsumeComplete: "+NdnCommon.getErrorWithStackTrace(d))}},c)},c)};Consumer.prototype.setGroup=function(a){this.groupName_=new Name(a)};
+Consumer.prototype.addDecryptionKeyPromise=function(a,b,c){return this.consumerName_.match(a)?this.database_.addKeyPromise(a,b,c):SyncPromise.reject(Error("addDecryptionKey: The consumer name must be a prefix of the key name"))};Consumer.prototype.addDecryptionKey=function(a,b,c,d){return SyncPromise.complete(c,d,this.addDecryptionKeyPromise(a,b,!c))};Consumer.Error=function(a,b){this.errorCode=a;this.message=b};
+Consumer.Error.callOnError=function(a,b,c){c||(c="");if(b instanceof Consumer.Error)try{a(b.errorCode,b.message)}catch(d){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(d))}else try{a(EncryptError.ErrorCode.General,c+b)}catch(e){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(e))}};
+Consumer.decryptPromise_=function(a,b){return SyncPromise.resolve().then(function(){if("object"==typeof a&&a instanceof Blob){var c=a;a=new EncryptedContent;a.wireDecode(c)}c=a.getPayload();if(a.getAlgorithmType()==EncryptAlgorithmType.AesCbc){var d=new EncryptParams(EncryptAlgorithmType.AesCbc);d.setInitialVector(a.getInitialVector());return AesAlgorithm.decryptPromise(b,c,d)}return a.getAlgorithmType()==EncryptAlgorithmType.RsaOaep?(d=new EncryptParams(EncryptAlgorithmType.RsaOaep),RsaAlgorithm.decryptPromise(b,
+c,d)):SyncPromise.reject(new Consumer.Error(EncryptError.ErrorCode.UnsupportedEncryptionScheme,""+a.getAlgorithmType()))})};Consumer.decrypt_=function(a,b,c,d){Consumer.decryptPromise_(a,b).then(function(a){c(a)},function(a){Consumer.Error.callOnError(d,a)})};
+Consumer.prototype.decryptContent_=function(a,b,c){var d=new EncryptedContent;try{d.wireDecode(a.getContent())}catch(e){Consumer.Error.callOnError(c,e,"Error decoding EncryptedContent: ");return}var f=d.getKeyLocator().getKeyName();if(a=this.cKeyMap_[f.toUri()])this.decrypt_(d,a,b,c);else{a=new Name(f);a.append(Encryptor.NAME_COMPONENT_FOR).append(this.groupName_);a=new Interest(a);var g=this;this.sendInterest_(a,1,this.cKeyLink_,function(a){g.decryptCKey_(a,function(a){g.cKeyMap_[f.toUri()]=a;Consumer.decrypt_(d,
+a,b,c)},c)},c)}};
+Consumer.prototype.decryptCKey_=function(a,b,c){a=a.getContent();var d=new EncryptedContent;try{d.wireDecode(a)}catch(e){Consumer.Error.callOnError(c,e,"Error decoding EncryptedContent: ");return}a=d.getKeyLocator().getKeyName();var f=a.getPrefix(-3);f.append(Encryptor.NAME_COMPONENT_D_KEY).append(a.getSubName(-2));if(a=this.dKeyMap_[f.toUri()])this.decrypt_(d,a,b,c);else{a=new Name(f);a.append(Encryptor.NAME_COMPONENT_FOR).append(this.consumerName_);a=new Interest(a);var g=this;this.sendInterest_(a,
+1,this.dKeyLink_,function(a){g.decryptDKeyPromise_(a).then(function(a){g.dKeyMap_[f.toUri()]=a;Consumer.decrypt_(d,a,b,c)},function(a){Consumer.Error.callOnError(c,a,"decryptDKey error: ")})},c)}};
+Consumer.prototype.decryptDKeyPromise_=function(a){var b,c,d,e=this;return SyncPromise.resolve().then(function(){b=a.getContent();c=new EncryptedContent;c.wireDecode(b);var d=c.getKeyLocator().getKeyName();return e.getDecryptionKeyPromise_(d)}).then(function(a){if(0==a.size())return SyncPromise.reject(new Consumer.Error(EncryptError.ErrorCode.NoDecryptKey,"The desired consumer decryption key in not in the database"));var e=b.buf().slice(c.wireEncode().size());d=new Blob(e,!1);return 0==d.size()?SyncPromise.reject(new Consumer.Error(EncryptError.ErrorCode.InvalidEncryptedFormat,
+"The data packet does not satisfy the D-KEY packet format")):Consumer.decryptPromise_(c,a)}).then(function(a){return Consumer.decryptPromise_(d,a)})};
+Consumer.prototype.sendInterest_=function(a,b,c,d,e){function f(a,b){try{e(EncryptError.ErrorCode.DataRetrievalFailure,a.getName().toUri())}catch(c){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(c))}}var g=this,h=function(a,b){try{g.keyChain_.verifyData(b,d,function(a,b){try{e(EncryptError.ErrorCode.Validation,"verifyData failed. Reason: "+b)}catch(c){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(c))}})}catch(c){Consumer.Error.callOnError(e,c,"verifyData error: ")}},
+k=function(a){0<b?g.sendInterest_(a,b-1,c,d,e):f(a,new NetworkNack)};0!==c.getDelegations().size()&&(a=new Interest(a),a.setLinkWireEncoding(c.wireEncode()));try{this.face_.expressInterest(a,h,k,f)}catch(l){Consumer.Error.callOnError(e,l,"expressInterest error: ")}};Consumer.prototype.getDecryptionKeyPromise_=function(a){return this.database_.getKeyPromise(a)};Consumer.NO_LINK=new Link;Blob=require("../util/blob.js").Blob;
+DecryptKey=function DecryptKey(b){this.keyBits_="object"===typeof b&&b instanceof DecryptKey?b.keyBits_:"object"===typeof b&&b instanceof Blob?b:new Blob(b)};exports.DecryptKey=DecryptKey;DecryptKey.prototype.getKeyBits=function(){return this.keyBits_};EncryptError=function(){};exports.EncryptError=EncryptError;EncryptError.ErrorCode={Timeout:1,Validation:2,UnsupportedEncryptionScheme:32,InvalidEncryptedFormat:33,NoDecryptKey:34,EncryptionFailure:35,DataRetrievalFailure:36,General:100};Blob=require("../util/blob.js").Blob;
+EncryptKey=function EncryptKey(b){this.keyBits_="object"===typeof b&&b instanceof EncryptKey?b.keyBits_:"object"===typeof b&&b instanceof Blob?b:new Blob(b)};exports.EncryptKey=EncryptKey;EncryptKey.prototype.getKeyBits=function(){return this.keyBits_};KeyLocator=require("../key-locator.js").KeyLocator;WireFormat=require("../encoding/wire-format.js").WireFormat;Blob=require("../util/blob.js").Blob;
+EncryptedContent=function EncryptedContent(b){"object"===typeof b&&b instanceof EncryptedContent?(this.algorithmType_=b.algorithmType_,this.keyLocator_=new KeyLocator(b.keyLocator_),this.initialVector_=b.initialVector_,this.payload_=b.payload_):(this.algorithmType_=null,this.keyLocator_=new KeyLocator,this.initialVector_=new Blob,this.payload_=new Blob)};exports.EncryptedContent=EncryptedContent;EncryptedContent.prototype.getAlgorithmType=function(){return this.algorithmType_};
+EncryptedContent.prototype.getKeyLocator=function(){return this.keyLocator_};EncryptedContent.prototype.getInitialVector=function(){return this.initialVector_};EncryptedContent.prototype.getPayload=function(){return this.payload_};EncryptedContent.prototype.setAlgorithmType=function(a){this.algorithmType_=a;return this};EncryptedContent.prototype.setKeyLocator=function(a){this.keyLocator_="object"===typeof a&&a instanceof KeyLocator?new KeyLocator(a):new KeyLocator;return this};
+EncryptedContent.prototype.setInitialVector=function(a){this.initialVector_="object"===typeof a&&a instanceof Blob?a:new Blob(a);return this};EncryptedContent.prototype.setPayload=function(a){this.payload_="object"===typeof a&&a instanceof Blob?a:new Blob(a);return this};EncryptedContent.prototype.wireEncode=function(a){a=a||WireFormat.getDefaultWireFormat();return a.encodeEncryptedContent(this)};
+EncryptedContent.prototype.wireDecode=function(a,b){b=b||WireFormat.getDefaultWireFormat();"object"===typeof a&&a instanceof Blob?b.decodeEncryptedContent(this,a.buf(),!1):b.decodeEncryptedContent(this,a,!0)};var SyncPromise=require("../util/sync-promise.js").SyncPromise,GroupManagerDb=function(){};exports.GroupManagerDb=GroupManagerDb;GroupManagerDb.Error=function(a){if(a)return a.__proto__=GroupManagerDb.Error.prototype,a};GroupManagerDb.Error.prototype=Error();
+GroupManagerDb.Error.prototype.name="GroupManagerDbError";GroupManagerDb.prototype.hasSchedulePromise=function(a,b){return SyncPromise.reject(Error("GroupManagerDb.hasSchedulePromise is not implemented"))};GroupManagerDb.prototype.listAllScheduleNamesPromise=function(a){return SyncPromise.reject(Error("GroupManagerDb.listAllScheduleNamesPromise is not implemented"))};GroupManagerDb.prototype.getSchedulePromise=function(a,b){return SyncPromise.reject(Error("GroupManagerDb.getSchedulePromise is not implemented"))};
+GroupManagerDb.prototype.getScheduleMembersPromise=function(a,b){return SyncPromise.reject(Error("GroupManagerDb.getScheduleMembersPromise is not implemented"))};GroupManagerDb.prototype.addSchedulePromise=function(a,b,c){return SyncPromise.reject(Error("GroupManagerDb.addSchedulePromise is not implemented"))};GroupManagerDb.prototype.deleteSchedulePromise=function(a,b){return SyncPromise.reject(Error("GroupManagerDb.deleteSchedulePromise is not implemented"))};
+GroupManagerDb.prototype.renameSchedulePromise=function(a,b,c){return SyncPromise.reject(Error("GroupManagerDb.renameSchedulePromise is not implemented"))};GroupManagerDb.prototype.updateSchedulePromise=function(a,b,c){return SyncPromise.reject(Error("GroupManagerDb.updateSchedulePromise is not implemented"))};GroupManagerDb.prototype.hasMemberPromise=function(a,b){return SyncPromise.reject(Error("GroupManagerDb.hasMemberPromise is not implemented"))};
+GroupManagerDb.prototype.listAllMembersPromise=function(a){return SyncPromise.reject(Error("GroupManagerDb.listAllMembersPromise is not implemented"))};GroupManagerDb.prototype.getMemberSchedulePromise=function(a,b){return SyncPromise.reject(Error("GroupManagerDb.getMemberSchedulePromise is not implemented"))};GroupManagerDb.prototype.addMemberPromise=function(a,b,c,d){return SyncPromise.reject(Error("GroupManagerDb.addMemberPromise is not implemented"))};
+GroupManagerDb.prototype.updateMemberSchedulePromise=function(a,b,c){return SyncPromise.reject(Error("GroupManagerDb.updateMemberSchedulePromise is not implemented"))};GroupManagerDb.prototype.deleteMemberPromise=function(a,b){return SyncPromise.reject(Error("GroupManagerDb.deleteMemberPromise is not implemented"))};
+var Name=require("../name.js").Name,Data=require("../data.js").Data,SyncPromise=require("../util/sync-promise.js").SyncPromise,IdentityCertificate=require("../security/certificate/identity-certificate.js").IdentityCertificate,SecurityException=require("../security/security-exception.js").SecurityException,RsaKeyParams=require("../security/key-params.js").RsaKeyParams,EncryptParams=require("./algo/encrypt-params.js").EncryptParams,EncryptAlgorithmType=require("./algo/encrypt-params.js").EncryptAlgorithmType,
+Encryptor=require("./algo/encryptor.js").Encryptor,RsaAlgorithm=require("./algo/rsa-algorithm.js").RsaAlgorithm,Interval=require("./interval.js").Interval,Schedule=require("./schedule.js").Schedule,GroupManager=function(a,b,c,d,e,f){this.namespace_=(new Name(a)).append(Encryptor.NAME_COMPONENT_READ).append(b);this.database_=c;this.keySize_=d;this.freshnessHours_=e;this.keyChain_=f};exports.GroupManager=GroupManager;
+GroupManager.prototype.getGroupKeyPromise=function(a,b){var c=[],d=[],e=this,f,g,h,k;return this.calculateIntervalPromise_(a,c,b).then(function(a){if(!1==a.isValid())return SyncPromise.resolve(d);h=Schedule.toIsoString(a.getStartTime());k=Schedule.toIsoString(a.getEndTime());return e.generateKeyPairPromise_(b).then(function(a){f=a.privateKeyBlob;g=a.publicKeyBlob;return e.createEKeyDataPromise_(h,k,g,b)}).then(function(a){function g(a){return a>=c.length?SyncPromise.resolve():e.createDKeyDataPromise_(h,
+k,c[a].keyName,f,c[a].publicKey,b).then(function(b){d.push(b);return g(a+1)})}d.push(a);return g(0)}).then(function(){return SyncPromise.resolve(d)})})};GroupManager.prototype.addSchedulePromise=function(a,b,c){return this.database_.addSchedulePromise(a,b,c)};GroupManager.prototype.deleteSchedulePromise=function(a,b){return this.database_.deleteSchedulePromise(a,b)};GroupManager.prototype.updateSchedulePromise=function(a,b,c){return this.database_.updateSchedulePromise(b,schedule,c)};
+GroupManager.prototype.addMemberPromise=function(a,b,c){b=new IdentityCertificate(b);return this.database_.addMemberPromise(a,b.getPublicKeyName(),b.getPublicKeyInfo().getKeyDer(),c)};GroupManager.prototype.removeMemberPromise=function(a,b){return this.database_.deleteMemberPromise(a,b)};GroupManager.prototype.updateMemberSchedulePromise=function(a,b,c){return this.database_.updateMemberSchedulePromise(a,b,c)};
+GroupManager.prototype.calculateIntervalPromise_=function(a,b,c){var d=new Interval,e=new Interval;b.splice(0,b.length);var f=this;return this.database_.listAllScheduleNamesPromise(c).then(function(g){function h(k){if(k>=g.length)return SyncPromise.resolve();var l=g[k];return f.database_.getSchedulePromise(l,c).then(function(g){g=g.getCoveringInterval(a);var q=g.interval;if(g.isPositive)return d.isValid()||(d=q),d.intersectWith(q),f.database_.getScheduleMembersPromise(l,c).then(function(a){for(var c=
+0;c<a.length;++c)GroupManager.memberKeysAdd_(b,a[c]);return h(k+1)});e.isValid()||(e=q);e.intersectWith(q);return h(k+1)})}return h(0)}).then(function(){if(!d.isValid())return SyncPromise.resolve(new Interval(!1));var a;a=e.isValid()?d.intersectWith(e):d;return SyncPromise.resolve(a)})};GroupManager.memberKeysAdd_=function(a,b){for(var c=0;c<a.length;){var d=a[c].keyName.compare(b.keyName);if(0==d)return;if(0<d)break;c+=1}a.splice(c,0,b)};
+GroupManager.prototype.generateKeyPairPromise_=function(a){a=new RsaKeyParams(this.keySize_);return RsaAlgorithm.generateKeyPromise(a).then(function(a){a=a.getKeyBits();var c=RsaAlgorithm.deriveEncryptKey(a).getKeyBits();return SyncPromise.resolve({privateKeyBlob:a,publicKeyBlob:c})})};
+GroupManager.prototype.createEKeyDataPromise_=function(a,b,c,d){d=new Name(this.namespace_);d.append(Encryptor.NAME_COMPONENT_E_KEY).append(a).append(b);a=new Data(d);a.getMetaInfo().setFreshnessPeriod(this.freshnessHours_*GroupManager.MILLISECONDS_IN_HOUR);a.setContent(c);return this.keyChain_.signPromise(a)};
+GroupManager.prototype.createDKeyDataPromise_=function(a,b,c,d,e,f){var g=new Name(this.namespace_);g.append(Encryptor.NAME_COMPONENT_D_KEY);g.append(a).append(b);var h=new Data(g);h.getMetaInfo().setFreshnessPeriod(this.freshnessHours_*GroupManager.MILLISECONDS_IN_HOUR);a=new EncryptParams(EncryptAlgorithmType.RsaOaep);var k=this;return Encryptor.encryptDataPromise(h,d,c,e,a,f)["catch"](function(a){return SyncPromise.reject(SecurityException(Error("createDKeyData: Error in encryptData: "+a)))}).then(function(){return k.keyChain_.signPromise(h)})};
+GroupManager.MILLISECONDS_IN_HOUR=36E5;Interval=function Interval(b,c){if("object"===typeof b&&b instanceof Interval)this.startTime_=b.startTime_,this.endTime_=b.endTime_,this.isValid_=b.isValid_;else if("number"===typeof b){if(!(b<c))throw Error("Interval start time must be less than the end time");this.startTime_=b;this.endTime_=c;this.isValid_=!0}else this.startTime_=-Number.MAX_VALUE,this.endTime_=-Number.MAX_VALUE,this.isValid_=b?!0:!1};exports.Interval=Interval;
+Interval.prototype.set=function(a){this.startTime_=a.startTime_;this.endTime_=a.endTime_;this.isValid_=a.isValid_};Interval.prototype.covers=function(a){if(!this.isValid_)throw Error("Interval.covers: This Interval is invalid");return this.isEmpty()?!1:this.startTime_<=a&&a<this.endTime_};
+Interval.prototype.intersectWith=function(a){if(!this.isValid_)throw Error("Interval.intersectWith: This Interval is invalid");if(!a.isValid_)throw Error("Interval.intersectWith: The other Interval is invalid");if(this.isEmpty()||a.isEmpty()||this.startTime_>=a.endTime_||this.endTime_<=a.startTime_)return this.startTime_=this.endTime_,this;this.startTime_<=a.startTime_&&(this.startTime_=a.startTime_);this.endTime_>a.endTime_&&(this.endTime_=a.endTime_);return this};
+Interval.prototype.unionWith=function(a){if(!this.isValid_)throw Error("Interval.intersectWith: This Interval is invalid");if(!a.isValid_)throw Error("Interval.intersectWith: The other Interval is invalid");if(this.isEmpty())return this.startTime_=a.startTime_,this.endTime_=a.endTime_,this;if(a.isEmpty())return this;if(this.startTime_>=a.endTime_||this.endTime_<=a.startTime_)throw Error("Interval.unionWith: The two intervals do not have an intersection");this.startTime_>a.startTime_&&(this.startTime_=
+a.startTime_);this.endTime_<a.endTime_&&(this.endTime_=a.endTime_);return this};Interval.prototype.getStartTime=function(){if(!this.isValid_)throw Error("Interval.getStartTime: This Interval is invalid");return this.startTime_};Interval.prototype.getEndTime=function(){if(!this.isValid_)throw Error("Interval.getEndTime: This Interval is invalid");return this.endTime_};Interval.prototype.isValid=function(){return this.isValid_};
+Interval.prototype.isEmpty=function(){if(!this.isValid_)throw Error("Interval.isEmpty: This Interval is invalid");return this.startTime_==this.endTime_};var SyncPromise=require("../util/sync-promise.js").SyncPromise,ProducerDb=function(){};exports.ProducerDb=ProducerDb;ProducerDb.Error=function(a){if(a)return a.__proto__=ProducerDb.Error.prototype,a};ProducerDb.Error.prototype=Error();ProducerDb.Error.prototype.name="ProducerDbError";ProducerDb.prototype.hasContentKeyPromise=function(a,b){return SyncPromise.reject(Error("ProducerDb.hasContentKeyPromise is not implemented"))};
+ProducerDb.prototype.getContentKeyPromise=function(a,b){return SyncPromise.reject(Error("ProducerDb.getContentKeyPromise is not implemented"))};ProducerDb.prototype.addContentKeyPromise=function(a,b,c){return SyncPromise.reject(Error("ProducerDb.addContentKeyPromise is not implemented"))};ProducerDb.prototype.deleteContentKeyPromise=function(a,b){return SyncPromise.reject(Error("ProducerDb.deleteContentKeyPromise is not implemented"))};
+ProducerDb.getFixedTimeSlot=function(a){return Math.floor(Math.round(a)/36E5)};
+var Name=require("../name.js").Name,Interest=require("../interest.js").Interest,Data=require("../data.js").Data,Link=require("../link.js").Link,NetworkNack=require("../network-nack.js").NetworkNack,Exclude=require("../exclude.js").Exclude,Encryptor=require("./algo/encryptor.js").Encryptor,EncryptParams=require("./algo/encrypt-params.js").EncryptParams,EncryptAlgorithmType=require("./algo/encrypt-params.js").EncryptAlgorithmType,AesKeyParams=require("../security/key-params.js").AesKeyParams,AesAlgorithm=
+require("./algo/aes-algorithm.js").AesAlgorithm,Schedule=require("./schedule.js").Schedule,EncryptError=require("./encrypt-error.js").EncryptError,NdnCommon=require("../util/ndn-common.js").NdnCommon,SyncPromise=require("../util/sync-promise.js").SyncPromise,Producer=function Producer(b,c,d,e,f,g,h){this.face_=d;this.keyChain_=e;this.database_=f;this.maxRepeatAttempts_=void 0==g?3:g;this.keyRetrievalLink_=void 0==h?Producer.NO_LINK:new Link(h);this.eKeyInfo_={};this.keyRequests_={};d=new Name(b);
+e=new Name(c);for(d.append(Encryptor.NAME_COMPONENT_READ);0<e.size();)f=new Name(d),f.append(e),f.append(Encryptor.NAME_COMPONENT_E_KEY),this.eKeyInfo_[f.toUri()]={keyName:f,keyInfo:new Producer.KeyInfo_},e=e.getPrefix(-1);d.append(c);this.namespace_=new Name(b);this.namespace_.append(Encryptor.NAME_COMPONENT_SAMPLE);this.namespace_.append(c)};exports.Producer=Producer;
+Producer.prototype.createContentKey=function(a,b,c,d){d||(d=Producer.defaultOnError);var e=Producer.getRoundedTimeSlot_(a),f=new Name(this.namespace_);f.append(Encryptor.NAME_COMPONENT_C_KEY);f.append(Schedule.toIsoString(e));var g,h=this;this.database_.hasContentKeyPromise(a).then(function(e){e?null!=c&&c(f):(e=new AesKeyParams(128),g=AesAlgorithm.generateKey(e).getKeyBits(),h.database_.addContentKeyPromise(a,g).then(function(){var e=Math.round(a);h.keyRequests_[e]=new Producer.KeyRequest_(h.getEKeyInfoSize_());
+var e=h.keyRequests_[e],g=new Exclude;Producer.excludeAfter(g,new Name.Component(Schedule.toIsoString(a)));for(var k in h.eKeyInfo_){var p=h.eKeyInfo_[k],s=p.keyInfo;a<s.beginTimeSlot||a>=s.endTimeSlot?(e.repeatAttempts[k]=0,h.sendKeyInterest_((new Interest(p.keyName)).setExclude(g).setChildSelector(1),a,b,d)):(p=new Name(p.keyName),p.append(Schedule.toIsoString(s.beginTimeSlot)),p.append(Schedule.toIsoString(s.endTimeSlot)),h.encryptContentKeyPromise_(s.keyBits,p,a,b,d))}null!=c&&c(f)}))})};
+Producer.prototype.produce=function(a,b,c,d,e){e||(e=Producer.defaultOnError);var f=this;this.createContentKey(b,null,function(g){f.database_.getContentKeyPromise(b).then(function(d){var e=new Name(f.namespace_);e.append(Schedule.toIsoString(b));a.setName(e);e=new EncryptParams(EncryptAlgorithmType.AesCbc,16);return Encryptor.encryptData(a,c,g,d,e)}).then(function(){return f.keyChain_.signPromise(a)}).then(function(){try{d()}catch(a){console.log("Error in onComplete: "+NdnCommon.getErrorWithStackTrace(a))}},
+function(a){try{e(EncryptError.ErrorCode.General,""+a)}catch(b){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(b))}})},e)};Producer.defaultOnError=function(a,b){};Producer.KeyInfo_=function(){this.endTimeSlot=this.beginTimeSlot=0;this.keyBits=null};Producer.KeyRequest_=function(a){this.interestCount=a;this.repeatAttempts={};this.encryptedKeys=[]};Producer.getRoundedTimeSlot_=function(a){return Math.round(36E5*Math.floor(Math.round(a)/36E5))};
+Producer.prototype.sendKeyInterest_=function(a,b,c,d){var e=this;0!==this.keyRetrievalLink_.getDelegations().size()&&(a=new Interest(a),a.setLinkWireEncoding(this.keyRetrievalLink_.wireEncode()));this.face_.expressInterest(a,function(a,g){e.handleCoveringKey_(a,g,b,c,d)},function(a){e.handleTimeout_(a,b,c,d)},function(a,g){e.handleNetworkNack_(a,g,b,c,d)})};
+Producer.prototype.handleTimeout_=function(a,b,c,d){var e=Math.round(b),e=this.keyRequests_[e],f=a.getName().toUri();e.repeatAttempts[f]<this.maxRepeatAttempts_?(++e.repeatAttempts[f],this.sendKeyInterest_(a,b,c,d)):this.handleNetworkNack_(a,new NetworkNack,b,c,d)};Producer.prototype.handleNetworkNack_=function(a,b,c,d,e){a=Math.round(c);this.updateKeyRequest_(this.keyRequests_[a],a,d)};
+Producer.prototype.updateKeyRequest_=function(a,b,c){--a.interestCount;if(0==a.interestCount&&null!=c){try{c(a.encryptedKeys)}catch(d){console.log("Error in onEncryptedKeys: "+NdnCommon.getErrorWithStackTrace(d))}delete this.keyRequests_[b]}};
+Producer.prototype.handleCoveringKey_=function(a,b,c,d,e){var f=Math.round(c),f=this.keyRequests_[f],g=a.getName(),h=g.toUri(),k=b.getName(),l=Schedule.fromIsoString(k.get(Producer.START_TIME_STAMP_INDEX).getValue().toString()),m=Schedule.fromIsoString(k.get(Producer.END_TIME_STAMP_INDEX).getValue().toString());if(c>=m)a=new Exclude(a.getExclude()),Producer.excludeBefore(a,k.get(Producer.START_TIME_STAMP_INDEX)),f.repeatAttempts[h]=0,this.sendKeyInterest_((new Interest(g)).setExclude(a).setChildSelector(1),
+c,d,e);else{var q=b.getContent(),p=this;this.encryptContentKeyPromise_(q,k,c,d,e).then(function(a){a&&(a=p.eKeyInfo_[h].keyInfo,a.beginTimeSlot=l,a.endTimeSlot=m,a.keyBits=q)})}};
+Producer.prototype.encryptContentKeyPromise_=function(a,b,c,d,e){var f=Math.round(c),g=this.keyRequests_[f],h=new Name(this.namespace_);h.append(Encryptor.NAME_COMPONENT_C_KEY);h.append(Schedule.toIsoString(Producer.getRoundedTimeSlot_(c)));var k,l=this;return this.database_.getContentKeyPromise(c).then(function(c){k=new Data;k.setName(h);var d=new EncryptParams(EncryptAlgorithmType.RsaOaep);return Encryptor.encryptDataPromise(k,c,b,a,d)}).then(function(){return SyncPromise.resolve(!0)},function(a){try{e(EncryptError.ErrorCode.EncryptionFailure,
+"encryptData failed: "+a)}catch(b){console.log("Error in onError: "+NdnCommon.getErrorWithStackTrace(b))}return SyncPromise.resolve(!1)}).then(function(a){return a?l.keyChain_.signPromise(k).then(function(){g.encryptedKeys.push(k);l.updateKeyRequest_(g,f,d);return SyncPromise.resolve(!0)}):SyncPromise.resolve(!1)})};Producer.prototype.getEKeyInfoSize_=function(){var a=0;for(key in this.eKeyInfo_)this.eKeyInfo_.hasOwnProperty(key)&&++a;return a};
+Producer.ExcludeEntry=function(a,b){this.component_=a;this.anyFollowsComponent_=b};Producer.getExcludeEntries=function(a){for(var b=[],c=0;c<a.size();++c)a.get(c)==Exclude.ANY?0==b.length?b.push(new Producer.ExcludeEntry(new Name.Component,!0)):b[b.length-1].anyFollowsComponent_=!0:b.push(new Producer.ExcludeEntry(a.get(c),!1));return b};
+Producer.setExcludeEntries=function(a,b){a.clear();for(var c=0;c<b.length;++c){var d=b[c];0==c&&0==d.component_.getValue().size()&&d.anyFollowsComponent_?a.appendAny():(a.appendComponent(d.component_),d.anyFollowsComponent_&&a.appendAny())}};Producer.findEntryBeforeOrAt=function(a,b){for(var c=a.length-1;0<=c&&!(0>=a[c].component_.compare(b));)--c;return c};
+Producer.excludeAfter=function(a,b){var c=Producer.getExcludeEntries(a),d;d=Producer.findEntryBeforeOrAt(c,b);if(0>d)c.splice(0,0,new Producer.ExcludeEntry(b,!0)),d=0;else{var e=c[d];e.anyFollowsComponent_||(e.component_.equals(b)?e.anyFollowsComponent_=!0:(c.splice(d+1,0,new Producer.ExcludeEntry(b,!0)),d+=1))}d+=1;c.splice(d,c.length-d);Producer.setExcludeEntries(a,c)};Producer.excludeBefore=function(a,b){Producer.excludeRange(a,new Name.Component,b)};
+Producer.excludeRange=function(a,b,c){if(0<=b.compare(c)){if(0==b.compare(c))throw Error("excludeRange: from == to. To exclude a single component, sue excludeOne.");throw Error("excludeRange: from must be less than to. Invalid range: ["+b.toEscapedString()+", "+c.toEscapedString()+"]");}var d=Producer.getExcludeEntries(a),e=Producer.findEntryBeforeOrAt(d,b);if(0>e)d.splice(0,0,new Producer.ExcludeEntry(b,!0)),b=0;else{var f=d[e];f.anyFollowsComponent_?b=e:f.component_.equals(b)?(f.anyFollowsComponent_=
+!0,b=e):(d.splice(e+1,0,new Producer.ExcludeEntry(b,!0)),b=e+1)}e=Producer.findEntryBeforeOrAt(d,c);f=d[e];e==b?d.splice(b+1,0,new Producer.ExcludeEntry(c,!1)):(f.anyFollowsComponent_?c=e+1:f.component_.equals(c)?c=e:(d.splice(e+1,0,new Producer.ExcludeEntry(c,!1)),c=e+1),b+=1,d.splice(b,c-b));Producer.setExcludeEntries(a,d)};Producer.START_TIME_STAMP_INDEX=-2;Producer.END_TIME_STAMP_INDEX=-1;Producer.NO_LINK=new Link;
+var Interval=require("./interval.js").Interval,RepetitiveInterval=function RepetitiveInterval(b,c,d,e,f,g){if("object"===typeof b&&b instanceof RepetitiveInterval)repetitiveInterval=b,this.startDate_=repetitiveInterval.startDate_,this.endDate_=repetitiveInterval.endDate_,this.intervalStartHour_=repetitiveInterval.intervalStartHour_,this.intervalEndHour_=repetitiveInterval.intervalEndHour_,this.nRepeats_=repetitiveInterval.nRepeats_,this.repeatUnit_=repetitiveInterval.repeatUnit_;else if("number"===
+typeof b){void 0==f&&(f=0);void 0==g&&(g=RepetitiveInterval.RepeatUnit.NONE);this.startDate_=RepetitiveInterval.toDateOnlyMilliseconds_(b);this.endDate_=RepetitiveInterval.toDateOnlyMilliseconds_(c);this.intervalStartHour_=Math.round(d);this.intervalEndHour_=Math.round(e);this.nRepeats_=Math.round(f);this.repeatUnit_=g;if(!(this.intervalStartHour_<this.intervalEndHour_))throw Error("ReptitiveInterval: startHour must be less than endHour");if(!(this.startDate_<=this.endDate_))throw Error("ReptitiveInterval: startDate must be earlier than or same as endDate");
+if(!(0<=this.intervalStartHour_))throw Error("ReptitiveInterval: intervalStartHour must be non-negative");if(!(1<=this.intervalEndHour_&&24>=this.intervalEndHour_))throw Error("ReptitiveInterval: intervalEndHour must be from 1 to 24");if(this.repeatUnit_==RepetitiveInterval.RepeatUnit.NONE&&this.startDate_!=this.endDate_)throw Error("ReptitiveInterval: With RepeatUnit.NONE, startDate must equal endDate");}else this.startDate_=-Number.MAX_VALUE,this.endDate_=-Number.MAX_VALUE,this.intervalStartHour_=
+0,this.intervalEndHour_=24,this.nRepeats_=0,this.repeatUnit_=RepetitiveInterval.RepeatUnit.NONE};exports.RepetitiveInterval=RepetitiveInterval;RepetitiveInterval.RepeatUnit={NONE:0,DAY:1,MONTH:2,YEAR:3};
+RepetitiveInterval.prototype.getInterval=function(a){var b,c;this.hasIntervalOnDate_(a)?(b=RepetitiveInterval.toDateOnlyMilliseconds_(a)+this.intervalStartHour_*RepetitiveInterval.MILLISECONDS_IN_HOUR,c=RepetitiveInterval.toDateOnlyMilliseconds_(a)+this.intervalEndHour_*RepetitiveInterval.MILLISECONDS_IN_HOUR,a<b?(c=b,b=RepetitiveInterval.toDateOnlyMilliseconds_(a),a=!1):a>c?(b=c,c=RepetitiveInterval.toDateOnlyMilliseconds_(a)+RepetitiveInterval.MILLISECONDS_IN_DAY,a=!1):a=!0):(b=RepetitiveInterval.toDateOnlyMilliseconds_(a),
+c=RepetitiveInterval.toDateOnlyMilliseconds_(a)+24*RepetitiveInterval.MILLISECONDS_IN_HOUR,a=!1);return{isPositive:a,interval:new Interval(b,c)}};
+RepetitiveInterval.prototype.compare=function(a){return this.startDate_<a.startDate_?-1:this.startDate_>a.startDate_?1:this.endDate_<a.endDate_?-1:this.endDate_>a.endDate_?1:this.intervalStartHour_<a.intervalStartHour_?-1:this.intervalStartHour_>a.intervalStartHour_?1:this.intervalEndHour_<a.intervalEndHour_?-1:this.intervalEndHour_>a.intervalEndHour_?1:this.nRepeats_<a.nRepeats_?-1:this.nRepeats_>a.nRepeats_?1:this.repeatUnit_<a.repeatUnit_?-1:this.repeatUnit_>a.repeatUnit_?1:0};
+RepetitiveInterval.prototype.getStartDate=function(){return this.startDate_};RepetitiveInterval.prototype.getEndDate=function(){return this.endDate_};RepetitiveInterval.prototype.getIntervalStartHour=function(){return this.intervalStartHour_};RepetitiveInterval.prototype.getIntervalEndHour=function(){return this.intervalEndHour_};RepetitiveInterval.prototype.getNRepeats=function(){return this.nRepeats_};RepetitiveInterval.prototype.getRepeatUnit=function(){return this.repeatUnit_};
+RepetitiveInterval.prototype.hasIntervalOnDate_=function(a){a=RepetitiveInterval.toDateOnlyMilliseconds_(a);if(a<this.startDate_||a>this.endDate_)return!1;if(this.repeatUnit_==RepetitiveInterval.RepeatUnit.NONE)return!0;if(this.repeatUnit_==RepetitiveInterval.RepeatUnit.DAY){if(0==(a-this.startDate_)/RepetitiveInterval.MILLISECONDS_IN_DAY%this.nRepeats_)return!0}else{a=new Date(a);var b=new Date(this.startDate_);if(this.repeatUnit_==RepetitiveInterval.RepeatUnit.MONTH&&a.getUTCDate()==b.getUTCDate()){if(0==
+(12*(a.getUTCFullYear()-b.getUTCFullYear())+a.getUTCMonth()-b.getUTCMonth())%this.nRepeats_)return!0}else if(this.repeatUnit_==RepetitiveInterval.RepeatUnit.YEAR&&a.getUTCDate()==b.getUTCDate()&&a.getUTCMonth()==b.getUTCMonth()&&0==(a.getUTCFullYear()-b.getUTCFullYear())%this.nRepeats_)return!0}return!1};RepetitiveInterval.toDateOnlyMilliseconds_=function(a){a=Math.round(a);return a-=a%RepetitiveInterval.MILLISECONDS_IN_DAY};RepetitiveInterval.MILLISECONDS_IN_HOUR=36E5;
+RepetitiveInterval.MILLISECONDS_IN_DAY=864E5;Interval=require("./interval.js").Interval;RepetitiveInterval=require("./repetitive-interval.js").RepetitiveInterval;Tlv=require("../encoding/tlv/tlv.js").Tlv;TlvEncoder=require("../encoding/tlv/tlv-encoder.js").TlvEncoder;TlvDecoder=require("../encoding/tlv/tlv-decoder.js").TlvDecoder;Blob=require("../util/blob.js").Blob;
+Schedule=function Schedule(b){"object"===typeof b&&b instanceof Schedule?(this.whiteIntervalList_=b.whiteIntervalList_.slice(0),this.blackIntervalList_=b.blackIntervalList_.slice(0)):(this.whiteIntervalList_=[],this.blackIntervalList_=[])};exports.Schedule=Schedule;Schedule.prototype.addWhiteInterval=function(a){Schedule.sortedSetAdd_(this.whiteIntervalList_,a);return this};Schedule.prototype.addBlackInterval=function(a){Schedule.sortedSetAdd_(this.blackIntervalList_,a);return this};
+Schedule.prototype.getCoveringInterval=function(a){var b=new Interval(!0),c=new Interval(!0),d=new Interval,e=new Interval;Schedule.calculateIntervalResult_(this.blackIntervalList_,a,b,d);if(!b.isEmpty())return{isPositive:!1,interval:b};Schedule.calculateIntervalResult_(this.whiteIntervalList_,a,c,e);return c.isEmpty()&&!e.isValid()?(a=RepetitiveInterval.toDateOnlyMilliseconds_(a),{isPositive:!1,interval:new Interval(a,a+RepetitiveInterval.MILLISECONDS_IN_DAY)}):c.isEmpty()?{isPositive:!1,interval:e}:
+d.isValid()?{isPositive:!0,interval:c.intersectWith(d)}:{isPositive:!0,interval:c}};
+Schedule.prototype.wireEncode=function(){for(var a=new TlvEncoder(256),b=a.getLength(),c=a.getLength(),d=this.blackIntervalList_.length-1;0<=d;d--)Schedule.encodeRepetitiveInterval_(this.blackIntervalList_[d],a);a.writeTypeAndLength(Tlv.Encrypt_BlackIntervalList,a.getLength()-c);c=a.getLength();for(d=this.whiteIntervalList_.length-1;0<=d;d--)Schedule.encodeRepetitiveInterval_(this.whiteIntervalList_[d],a);a.writeTypeAndLength(Tlv.Encrypt_WhiteIntervalList,a.getLength()-c);a.writeTypeAndLength(Tlv.Encrypt_Schedule,
+a.getLength()-b);return new Blob(a.getOutput(),!1)};
+Schedule.prototype.wireDecode=function(a){a="object"===typeof a&&a instanceof Blob?a.buf():a;a=new TlvDecoder(a);var b=a.readNestedTlvsStart(Tlv.Encrypt_Schedule);this.whiteIntervalList_=[];for(var c=a.readNestedTlvsStart(Tlv.Encrypt_WhiteIntervalList);a.getOffset()<c;)Schedule.sortedSetAdd_(this.whiteIntervalList_,Schedule.decodeRepetitiveInterval_(a));a.finishNestedTlvs(c);this.blackIntervalList_=[];for(c=a.readNestedTlvsStart(Tlv.Encrypt_BlackIntervalList);a.getOffset()<c;)Schedule.sortedSetAdd_(this.blackIntervalList_,
+Schedule.decodeRepetitiveInterval_(a));a.finishNestedTlvs(c);a.finishNestedTlvs(b)};Schedule.sortedSetAdd_=function(a,b){for(var c=0;c<a.length;){var d=a[c].compare(b);if(0==d)return;if(!(0>d))break;++c}a.splice(c,0,b)};
+Schedule.encodeRepetitiveInterval_=function(a,b){var c=b.getLength();b.writeNonNegativeIntegerTlv(Tlv.Encrypt_RepeatUnit,a.getRepeatUnit());b.writeNonNegativeIntegerTlv(Tlv.Encrypt_NRepeats,a.getNRepeats());b.writeNonNegativeIntegerTlv(Tlv.Encrypt_IntervalEndHour,a.getIntervalEndHour());b.writeNonNegativeIntegerTlv(Tlv.Encrypt_IntervalStartHour,a.getIntervalStartHour());b.writeBlobTlv(Tlv.Encrypt_EndDate,(new Blob(Schedule.toIsoString(a.getEndDate()))).buf());b.writeBlobTlv(Tlv.Encrypt_StartDate,
+(new Blob(Schedule.toIsoString(a.getStartDate()))).buf());b.writeTypeAndLength(Tlv.Encrypt_RepetitiveInterval,b.getLength()-c)};
+Schedule.decodeRepetitiveInterval_=function(a){var b=a.readNestedTlvsStart(Tlv.Encrypt_RepetitiveInterval),c=Schedule.fromIsoString((new Blob(a.readBlobTlv(Tlv.Encrypt_StartDate),!0)).toString()),d=Schedule.fromIsoString((new Blob(a.readBlobTlv(Tlv.Encrypt_EndDate),!0)).toString()),e=a.readNonNegativeIntegerTlv(Tlv.Encrypt_IntervalStartHour),f=a.readNonNegativeIntegerTlv(Tlv.Encrypt_IntervalEndHour),g=a.readNonNegativeIntegerTlv(Tlv.Encrypt_NRepeats),h=a.readNonNegativeIntegerTlv(Tlv.Encrypt_RepeatUnit);
+a.finishNestedTlvs(b);return new RepetitiveInterval(c,d,e,f,g,h)};Schedule.calculateIntervalResult_=function(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e].getInterval(b),g=f.interval;!0==f.isPositive?c.unionWith(g):d.isValid()?d.intersectWith(g):d.set(g)}};
+Schedule.toIsoString=function(a){a=new Date(Math.round(a));return a.getUTCFullYear()+Schedule.to2DigitString(a.getUTCMonth()+1)+Schedule.to2DigitString(a.getUTCDate())+"T"+Schedule.to2DigitString(a.getUTCHours())+Schedule.to2DigitString(a.getUTCMinutes())+Schedule.to2DigitString(a.getUTCSeconds())};Schedule.to2DigitString=function(a){a=a.toString();return 1===a.length?"0"+a:a};
+Schedule.fromIsoString=function(a){if(15!=a.length||"T"!=a.substr(8,1))throw Error("fromIsoString: Format is not the expected yyyymmddThhmmss");return Date.UTC(parseInt(a.substr(0,4)),parseInt(a.substr(4,2)-1),parseInt(a.substr(6,2)),parseInt(a.substr(9,2)),parseInt(a.substr(11,2)),parseInt(a.substr(13,2)))};var IndexedDbConsumerDb=function(a){ConsumerDb.call(this);this.database=new Dexie(a);this.database.version(1).stores({decryptionKeys:"keyName"});this.database.open()};
+IndexedDbConsumerDb.prototype=new ConsumerDb;IndexedDbConsumerDb.prototype.name="IndexedDbConsumerDb";
+IndexedDbConsumerDb.prototype.getKeyPromise=function(a,b){return b?Promise.reject(new ConsumerDb.Error(Error("IndexedDbConsumerDb.getKeyPromise is only supported for async"))):this.database.decryptionKeys.get(a.toUri()).then(function(a){return a?Promise.resolve(new Blob(a.key)):Promise.resolve(new Blob)})["catch"](function(a){return Promise.reject(new ConsumerDb.Error(Error("IndexedDbConsumerDb.getKeyPromise: Error: "+a)))})};
+IndexedDbConsumerDb.prototype.addKeyPromise=function(a,b,c){return c?Promise.reject(new ConsumerDb.Error(Error("IndexedDbConsumerDb.addKeyPromise is only supported for async"))):this.database.decryptionKeys.add({keyName:a.toUri(),key:b.buf()})["catch"](function(a){return Promise.reject(new ConsumerDb.Error(Error("IndexedDbConsumerDb.addKeyPromise: Error: "+a)))})};
+IndexedDbConsumerDb.prototype.deleteKeyPromise=function(a,b){return b?Promise.reject(new ConsumerDb.Error(Error("IndexedDbConsumerDb.deleteKeyPromise is only supported for async"))):this.database.decryptionKeys["delete"](a.toUri())["catch"](function(a){return Promise.reject(new ConsumerDb.Error(Error("IndexedDbConsumerDb.deleteKeyPromise: Error: "+a)))})};
+var IndexedDbGroupManagerDb=function(a){GroupManagerDb.call(this);this.database=new Dexie(a);this.database.version(1).stores({schedules:"++scheduleId, &scheduleName",members:"memberNameUri, scheduleId"});this.database.open()};IndexedDbGroupManagerDb.prototype=new GroupManagerDb;IndexedDbGroupManagerDb.prototype.name="IndexedDbGroupManagerDb";
+IndexedDbGroupManagerDb.prototype.hasSchedulePromise=function(a,b){return b?Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.hasSchedulePromise is only supported for async"))):this.getScheduleIdPromise_(a).then(function(a){return Promise.resolve(-1!=a)})};
+IndexedDbGroupManagerDb.prototype.listAllScheduleNamesPromise=function(a){if(a)return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.listAllScheduleNamesPromise is only supported for async")));var b=[];return this.database.schedules.each(function(a){b.push(a.scheduleName)}).then(function(){return Promise.resolve(b)})["catch"](function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.listAllScheduleNamesPromise: Error: "+a)))})};
+IndexedDbGroupManagerDb.prototype.getSchedulePromise=function(a,b){if(b)return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.getSchedulePromise is only supported for async")));var c=this;return this.getScheduleIdPromise_(a).then(function(a){return-1!=a?c.database.schedules.get(a).then(function(a){var b=new Schedule;b.wireDecode(new Blob(a.schedule,!1));return Promise.resolve(b)})["catch"](function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.getSchedulePromise: Error: "+
+a)))}):Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.getSchedulePromise: Cannot get the result from the database")))})};
+IndexedDbGroupManagerDb.prototype.getScheduleMembersPromise=function(a,b){if(b)return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.getScheduleMembersPromise is only supported for async")));var c=[],d=this;return this.getScheduleIdPromise_(a).then(function(a){if(-1==a)return Promise.resolve(c);var b=null;return d.database.members.where("scheduleId").equals(a).each(function(a){try{var d=new Name;d.wireDecode(new Blob(a.keyName,!1),TlvWireFormat.get());c.push({keyName:d,publicKey:new Blob(a.publicKey,
+!1)})}catch(e){b=new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.getScheduleMembersPromise: Error decoding name: "+e))}}).then(function(){return b?Promise.reject(b):Promise.resolve(c)},function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.getScheduleMembersPromise: Error: "+a)))})})};
+IndexedDbGroupManagerDb.prototype.addSchedulePromise=function(a,b,c){return c?Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.addSchedulePromise is only supported for async"))):0==a.length?Promise.reject(new GroupManagerDb.Error("IndexedDbGroupManagerDb.addSchedulePromise: The schedule name cannot be empty")):this.database.schedules.add({scheduleName:a,schedule:b.wireEncode().buf()})["catch"](function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.addContentKeyPromise: Error: "+
+a)))})};
+IndexedDbGroupManagerDb.prototype.deleteSchedulePromise=function(a,b){if(b)return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.deleteSchedulePromise is only supported for async")));var c,d=this;return this.getScheduleIdPromise_(a).then(function(a){c=a;return d.database.members.where("scheduleId").equals(c).toArray()}).then(function(a){a=a.map(function(a){return d.database.members["delete"](a.memberNameUri)});return Promise.all(a)}).then(function(){return d.database.schedules["delete"](c)})["catch"](function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.deleteSchedulePromise: Error: "+a)))})};
+IndexedDbGroupManagerDb.prototype.renameSchedulePromise=function(a,b,c){if(c)return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.renameSchedulePromise is only supported for async")));if(0==b.length)return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.renameSchedule: The schedule newName cannot be empty")));var d=this;return this.getScheduleIdPromise_(a).then(function(a){return-1==a?Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.renameSchedule: The schedule oldName does not exist"))):
+d.database.schedules.update(a,{scheduleName:b})["catch"](function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.renameSchedulePromise: Error: "+a)))})})};
+IndexedDbGroupManagerDb.prototype.updateSchedulePromise=function(a,b,c){if(c)return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.updateSchedulePromise is only supported for async")));var d=this;return this.getScheduleIdPromise_(a).then(function(c){return-1==c?d.addSchedulePromise(a,b):d.database.schedules.update(c,{schedule:b.wireEncode().buf()})["catch"](function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.updateSchedulePromise: Error: "+
+a)))})})};IndexedDbGroupManagerDb.prototype.hasMemberPromise=function(a,b){return b?Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.hasMemberPromise is only supported for async"))):this.database.members.get(a.toUri()).then(function(a){return Promise.resolve(void 0!=a)})["catch"](function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.hasMemberPromise: Error: "+a)))})};
+IndexedDbGroupManagerDb.prototype.listAllMembersPromise=function(a){if(a)return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.listAllMembersPromise is only supported for async")));var b=[],c=null;return this.database.members.each(function(a){try{var e=new Name;e.wireDecode(new Blob(a.memberName,!1),TlvWireFormat.get());b.push(e)}catch(f){c=new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.listAllMembersPromise: Error decoding name: "+f))}}).then(function(){return c?
+Promise.reject(c):Promise.resolve(b)},function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.listAllMembersPromise: Error: "+a)))})};
+IndexedDbGroupManagerDb.prototype.getMemberSchedulePromise=function(a,b){if(b)return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.getMemberSchedulePromise is only supported for async")));var c=this;return this.database.members.get(a.toUri()).then(function(a){if(!a)throw Error("The member identity name does not exist in the database");return c.database.schedules.get(a.scheduleId)}).then(function(a){if(!a)throw Error("The schedule ID for the member identity name does not exist in the database");
+return Promise.resolve(a.scheduleName)})["catch"](function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.getScheduleIdPromise_: Error: "+a)))})};
+IndexedDbGroupManagerDb.prototype.addMemberPromise=function(a,b,c,d){if(d)return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.addMemberPromise is only supported for async")));var e=this;return this.getScheduleIdPromise_(a).then(function(a){if(-1==a)return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.addMemberPromise: The schedule does not exist")));var d=b.getPrefix(-1);return e.database.members.add({memberNameUri:d.toUri(),memberName:d.wireEncode(TlvWireFormat.get()).buf(),
+scheduleId:a,keyName:b.wireEncode(TlvWireFormat.get()).buf(),publicKey:c.buf()})["catch"](function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.addMemberPromise: Error: "+a)))})})};
+IndexedDbGroupManagerDb.prototype.updateMemberSchedulePromise=function(a,b,c){if(c)return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.updateMemberSchedulePromise is only supported for async")));var d=this;return this.getScheduleIdPromise_(b).then(function(b){return-1==b?Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.updateMemberSchedulePromise: The schedule does not exist"))):d.database.members.update(a.toUri(),{scheduleId:b})["catch"](function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.updateMemberSchedulePromise: Error: "+
+a)))})})};IndexedDbGroupManagerDb.prototype.deleteMemberPromise=function(a,b){return b?Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.deleteMemberPromise is only supported for async"))):this.database.members["delete"](a.toUri())["catch"](function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.deleteMemberPromise: Error: "+a)))})};
+IndexedDbGroupManagerDb.prototype.getScheduleIdPromise_=function(a){var b=-1;return this.database.schedules.where("scheduleName").equals(a).each(function(a){b=a.scheduleId}).then(function(){return Promise.resolve(b)})["catch"](function(a){return Promise.reject(new GroupManagerDb.Error(Error("IndexedDbGroupManagerDb.getScheduleIdPromise_: Error: "+a)))})};var IndexedDbProducerDb=function(a){ProducerDb.call(this);this.database=new Dexie(a);this.database.version(1).stores({contentKeys:"timeSlot"});this.database.open()};
+IndexedDbProducerDb.prototype=new ProducerDb;IndexedDbProducerDb.prototype.name="IndexedDbProducerDb";
+IndexedDbProducerDb.prototype.hasContentKeyPromise=function(a,b){if(b)return Promise.reject(new ProducerDb.Error(Error("IndexedDbProducerDb.hasContentKeyPromise is only supported for async")));var c=ProducerDb.getFixedTimeSlot(a);return this.database.contentKeys.get(c).then(function(a){return Promise.resolve(void 0!=a)})["catch"](function(a){return Promise.reject(new ProducerDb.Error(Error("IndexedDbProducerDb.hasContentKeyPromise: Error: "+a)))})};
+IndexedDbProducerDb.prototype.getContentKeyPromise=function(a,b){if(b)return Promise.reject(new ProducerDb.Error(Error("IndexedDbProducerDb.getContentKeyPromise is only supported for async")));var c=ProducerDb.getFixedTimeSlot(a);return this.database.contentKeys.get(c).then(function(a){return a?Promise.resolve(new Blob(a.key)):Promise.reject(new ProducerDb.Error(Error("IndexedDbProducerDb.getContentKeyPromise: Cannot get the key from the database")))},function(a){return Promise.reject(new ProducerDb.Error(Error("IndexedDbProducerDb.getContentKeyPromise: Error: "+
+a)))})};IndexedDbProducerDb.prototype.addContentKeyPromise=function(a,b,c){if(c)return Promise.reject(new ProducerDb.Error(Error("IndexedDbProducerDb.addContentKeyPromise is only supported for async")));a=ProducerDb.getFixedTimeSlot(a);return this.database.contentKeys.add({timeSlot:a,key:b.buf()})["catch"](function(a){return Promise.reject(new ProducerDb.Error(Error("IndexedDbProducerDb.addContentKeyPromise: Error: "+a)))})};
+IndexedDbProducerDb.prototype.deleteContentKeyPromise=function(a,b){if(b)return Promise.reject(new ProducerDb.Error(Error("IndexedDbProducerDb.deleteContentKeyPromise is only supported for async")));var c=ProducerDb.getFixedTimeSlot(a);return this.database.contentKeys["delete"](c)["catch"](function(a){return Promise.reject(new ProducerDb.Error(Error("IndexedDbProducerDb.deleteContentKeyPromise: Error: "+a)))})};
+var DigestTree=require("./digest-tree.js").DigestTree,Interest=require("../interest.js").Interest,Data=require("../data.js").Data,Name=require("../name.js").Name,Blob=require("../util/blob.js").Blob,MemoryContentCache=require("../util/memory-content-cache.js").MemoryContentCache,SyncStateProto=require("./sync-state.js").SyncStateProto,NdnCommon=require("../util/ndn-common.js").NdnCommon,ChronoSync2013=function ChronoSync2013(b,c,d,e,f,g,h,k,l,m){this.onReceivedSyncState=b;this.onInitialized=c;this.applicationDataPrefixUri=
+d.toUri();this.applicationBroadcastPrefix=e;this.session=f;this.face=g;this.keyChain=h;this.certificateName=k;this.sync_lifetime=l;this.usrseq=-1;this.digest_tree=new DigestTree;this.contentCache=new MemoryContentCache(g);this.digest_log=[];this.digest_log.push(new ChronoSync2013.DigestLogEntry("00",[]));this.contentCache.registerPrefix(this.applicationBroadcastPrefix,m,this.onInterest.bind(this));this.enabled=!0;b=new Interest(this.applicationBroadcastPrefix);b.getName().append("00");b.setInterestLifetimeMilliseconds(1E3);
+var q;try{q=dcodeIO.ProtoBuf.newBuilder()["import"](SyncStateProto).build("Sync")}catch(p){q=require("protobufjs").newBuilder()["import"](SyncStateProto).build("Sync")}this.SyncStateMsg=q.SyncStateMsg;this.SyncState=q.SyncState;this.face.expressInterest(b,this.onData.bind(this),this.initialTimeOut.bind(this))};exports.ChronoSync2013=ChronoSync2013;ChronoSync2013.prototype.getProducerSequenceNo=function(a,b){var c=this.digest_tree.find(a,b);return 0>c?-1:this.digest_tree.get(c).getSequenceNo()};
+ChronoSync2013.prototype.publishNextSequenceNo=function(){this.usrseq++;var a=[new this.SyncState({name:this.applicationDataPrefixUri,type:"UPDATE",seqno:{seq:this.usrseq,session:this.session}})],b=new this.SyncStateMsg({ss:a});this.broadcastSyncState(this.digest_tree.getRoot(),b);this.update(a)||console.log("Warning: ChronoSync: update did not create a new digest log entry");a=new Interest(this.applicationBroadcastPrefix);a.getName().append(this.digest_tree.getRoot());a.setInterestLifetimeMilliseconds(this.sync_lifetime);
+this.face.expressInterest(a,this.onData.bind(this),this.syncTimeout.bind(this))};ChronoSync2013.prototype.getSequenceNo=function(){return this.usrseq};ChronoSync2013.DigestLogEntry=function(a,b){this.digest=a;this.data=b};ChronoSync2013.DigestLogEntry.prototype.getDigest=function(){return this.digest};ChronoSync2013.DigestLogEntry.prototype.getData=function(){return this.data};ChronoSync2013.prototype.shutdown=function(){this.enabled=!1;this.contentCache.unregisterAll()};
+ChronoSync2013.SyncState=function(a,b,c){this.dataPrefixUri_=a;this.sessionNo_=b;this.sequenceNo_=c};ChronoSync2013.SyncState.prototype.getDataPrefix=function(){return this.dataPrefixUri_};ChronoSync2013.SyncState.prototype.getSessionNo=function(){return this.sessionNo_};ChronoSync2013.SyncState.prototype.getSequenceNo=function(){return this.sequenceNo_};
+ChronoSync2013.prototype.broadcastSyncState=function(a,b){var c=new Uint8Array(b.toArrayBuffer()),d=new Data(this.applicationBroadcastPrefix);d.getName().append(a);d.setContent(new Blob(c,!1));var e=this;this.keyChain.sign(d,this.certificateName,function(){e.contentCache.add(d)})};
+ChronoSync2013.prototype.update=function(a){for(var b=0;b<a.length;b++)0==a[b].type&&this.digest_tree.update(a[b].name,a[b].seqno.session,a[b].seqno.seq)&&this.applicationDataPrefixUri==a[b].name&&(this.usrseq=a[b].seqno.seq);return-1==this.logfind(this.digest_tree.getRoot())?(a=new ChronoSync2013.DigestLogEntry(this.digest_tree.getRoot(),a),this.digest_log.push(a),!0):!1};
+ChronoSync2013.prototype.logfind=function(a){for(var b=0;b<this.digest_log.length;b++)if(a==this.digest_log[b].digest)return b;return-1};
+ChronoSync2013.prototype.onInterest=function(a,b,c,d,e){this.enabled&&(a=b.getName().get(this.applicationBroadcastPrefix.size()).toEscapedString(),b.getName().size()==this.applicationBroadcastPrefix.size()+2&&(a=b.getName().get(this.applicationBroadcastPrefix.size()+1).toEscapedString()),b.getName().size()==this.applicationBroadcastPrefix.size()+2||"00"==a?this.processRecoveryInst(b,a,c):(this.contentCache.storePendingInterest(b,c),a!=this.digest_tree.getRoot()&&(b=this.logfind(a),-1==b?(b=new Interest(new Name("/local/timeout")),
+b.setInterestLifetimeMilliseconds(2E3),this.face.expressInterest(b,this.dummyOnData,this.judgeRecovery.bind(this,b,a,c))):this.processSyncInst(b,a,c))))};
+ChronoSync2013.prototype.onData=function(a,b){if(this.enabled){var c=new Uint8Array(b.getContent().size());c.set(b.getContent().buf());var c=this.SyncStateMsg.decode(c.buffer).ss,d=!1;"00"==this.digest_tree.getRoot()?(d=!0,this.initialOndata(c)):(this.update(c),d=a.getName().size()==this.applicationBroadcastPrefix.size()+2?!0:!1);for(var e=[],f=0;f<c.length;f++)0==c[f].type&&e.push(new ChronoSync2013.SyncState(c[f].name,c[f].seqno.session,c[f].seqno.seq));try{this.onReceivedSyncState(e,d)}catch(g){console.log("Error in onReceivedSyncState: "+
+NdnCommon.getErrorWithStackTrace(g))}c=new Name(this.applicationBroadcastPrefix);c.append(this.digest_tree.getRoot());a=new Interest(c);a.setInterestLifetimeMilliseconds(this.sync_lifetime);this.face.expressInterest(a,this.onData.bind(this),this.syncTimeout.bind(this))}};
+ChronoSync2013.prototype.initialTimeOut=function(a){if(this.enabled){console.log("no other people");this.usrseq++;try{this.onInitialized()}catch(b){console.log("Error in onInitialized: "+NdnCommon.getErrorWithStackTrace(b))}a=[new this.SyncState({name:this.applicationDataPrefixUri,type:"UPDATE",seqno:{seq:this.usrseq,session:this.session}})];this.update(a);a=new Name(this.applicationBroadcastPrefix);a.append(this.digest_tree.getRoot());a=new Interest(a);a.setInterestLifetimeMilliseconds(this.sync_lifetime);
+this.face.expressInterest(a,this.onData.bind(this),this.syncTimeout.bind(this))}};
+ChronoSync2013.prototype.processRecoveryInst=function(a,b,c){if(-1!=this.logfind(b)){b=[];for(var d=0;d<this.digest_tree.digestnode.length;d++)b[d]=new this.SyncState({name:this.digest_tree.digestnode[d].getDataPrefix(),type:"UPDATE",seqno:{seq:this.digest_tree.digestnode[d].getSequenceNo(),session:this.digest_tree.digestnode[d].getSessionNo()}});if(0!=b.length){b=new this.SyncStateMsg({ss:b});b=new Uint8Array(b.toArrayBuffer());var e=new Data(a.getName());e.setContent(new Blob(b,!1));"00"==a.getName().get(-1).toEscapedString()&&
+e.getMetaInfo().setFreshnessPeriod(1E3);this.keyChain.sign(e,this.certificateName,function(){try{c.putData(e)}catch(a){console.log(a.toString())}})}}};
+ChronoSync2013.prototype.processSyncInst=function(a,b,c){for(var d=[],e=[],f=[],g=[],h=a+1;h<this.digest_log.length;h++)for(var k=this.digest_log[h].getData(),l=0;l<k.length;l++)0==k[l].type&&-1!=this.digest_tree.find(k[l].name,k[l].seqno.session)&&(a=e.indexOf(k[l].name),-1==a?(e.push(k[l].name),f.push(k[l].seqno.seq),g.push(k[l].seqno.session)):(f[a]=k[l].seqno.seq,g[a]=k[l].seqno.session));for(l=0;l<e.length;l++)d[l]=new this.SyncState({name:e[l],type:"UPDATE",seqno:{seq:f[l],session:g[l]}});if(0!=
+d.length){d=new this.SyncStateMsg({ss:d});d=new Uint8Array(d.toArrayBuffer());a=new Name(this.prefix);a.append(this.chatroom).append(b);var m=new Data(a);m.setContent(new Blob(d,!1));this.keyChain.sign(m,this.certificateName,function(){try{c.putData(m)}catch(a){console.log(a.toString())}})}};
+ChronoSync2013.prototype.sendRecovery=function(a){var b=new Name(this.applicationBroadcastPrefix);b.append("recovery").append(a);a=new Interest(b);a.setInterestLifetimeMilliseconds(this.sync_lifetime);this.face.expressInterest(a,this.onData.bind(this),this.syncTimeout.bind(this))};ChronoSync2013.prototype.judgeRecovery=function(a,b,c){a=this.logfind(b);-1!=a?b!=this.digest_tree.root&&this.processSyncInst(a,b,c):this.sendRecovery(b)};
+ChronoSync2013.prototype.syncTimeout=function(a){if(this.enabled&&a.getName().get(4).toEscapedString()==this.digest_tree.root){var b=new Name(a.getName()),b=new Interest(b);a.setInterestLifetimeMilliseconds(this.sync_lifetime);this.face.expressInterest(b,this.onData.bind(this),this.syncTimeout.bind(this))}};
+ChronoSync2013.prototype.initialOndata=function(a){this.update(a);for(var b=this.digest_tree.getRoot(),c=0;c<a.length;c++)if(a[c].name==this.applicationDataPrefixUri&&a[c].seqno.session==this.session){var d=[new this.SyncState({name:this.applicationDataPrefixUri,type:"UPDATE",seqno:{seq:a[c].seqno.seq+1,session:this.session}})];if(this.update(d)){d=new ChronoSync2013.DigestLogEntry(this.digest_tree.getRoot(),d);this.digest_log.push(d);try{this.onInitialized()}catch(e){console.log("Error in onInitialized: "+
+NdnCommon.getErrorWithStackTrace(e))}}}d=0<=this.usrseq?new this.SyncState({name:this.applicationDataPrefixUri,type:"UPDATE",seqno:{seq:this.usrseq,session:this.session}}):new this.SyncState({name:this.applicationDataPrefixUri,type:"UPDATE",seqno:{seq:0,session:this.session}});a=new this.SyncStateMsg({ss:d});this.broadcastSyncState(b,a);if(-1==this.digest_tree.find(this.applicationDataPrefixUri,this.session)&&(this.usrseq++,a=[new this.SyncState({name:this.applicationDataPrefixUri,type:"UPDATE",seqno:{seq:this.usrseq,
+session:this.session}})],this.update(a)))try{this.onInitialized()}catch(f){console.log("Error in onInitialized: "+NdnCommon.getErrorWithStackTrace(f))}};ChronoSync2013.prototype.dummyOnData=function(a,b){console.log("*** dummyOnData called. ***")};Crypto=require("../crypto.js");DigestTree=function(){this.root="00";this.digestnode=[]};exports.DigestTree=DigestTree;DigestTree.Node=function(a,b,c){this.dataPrefix=a;this.seqno_session=b;this.seqno_seq=c;this.recomputeDigest()};
+DigestTree.Node.prototype.getDataPrefix=function(){return this.dataPrefix};DigestTree.Node.prototype.getSessionNo=function(){return this.seqno_session};DigestTree.Node.prototype.getSequenceNo=function(){return this.seqno_seq};DigestTree.Node.prototype.getDigest=function(){return this.digest};DigestTree.Node.prototype.setSequenceNo=function(a){this.seqno_seq=a;this.recomputeDigest()};
+DigestTree.Node.prototype.Int32ToBuffer=function(a){for(var b=new Buffer(4),c=0;4>c;c++)b[c]=a%256,a=Math.floor(a/256);return b};DigestTree.Node.prototype.recomputeDigest=function(){var a=Crypto.createHash("sha256");a.update(this.Int32ToBuffer(this.seqno_session));a.update(this.Int32ToBuffer(this.seqno_seq));var a=a.digest(),b=Crypto.createHash("sha256");b.update(this.dataPrefix);var b=b.digest(),c=Crypto.createHash("sha256");c.update(b);c.update(a);this.digest=c.digest("hex")};
+DigestTree.Node.Compare=function(a,b){return a.dataPrefix!=b.dataPrefix?a.dataPrefix<b.dataPrefix:a.seqno_session<b.seqno_session};DigestTree.prototype.update=function(a,b,c){var d=this.find(a,b);if(0<=d)if(this.digestnode[d].getSequenceNo()<c)this.digestnode[d].setSequenceNo(c);else return!1;else a=new DigestTree.Node(a,b,c),this.digestnode.push(a),this.digestnode.sort(this.sortNodes);this.recomputeRoot();return!0};
+DigestTree.prototype.sortNodes=function(){for(var a,b=this.digestnode.length;0<b;b--)for(var c=0;c<b-1;c++)this.digestnode[c].getDataPrefix()>this.digestnode[c+1].getDataPrefix()&&(a=this.digestnode[c],this.digestnode[c]=this.digestnode[c+1],this.digestnode[c+1]=a)};
+DigestTree.prototype.sortNodes=function(a,b){return a.getDataPrefix()==b.getDataPrefix()&&a.getSessionNo()==b.getSessionNo()?0:a.getDataPrefix()>b.getDataPrefix()||a.getDataPrefix()==b.getDataPrefix()&&a.getSessionNo()>b.getSessionNo()?1:-1};DigestTree.prototype.find=function(a,b){for(var c=0;c<this.digestnode.length;++c)if(this.digestnode[c].getDataPrefix()==a&&this.digestnode[c].getSessionNo()==b)return c;return-1};DigestTree.prototype.size=function(){return this.digestnode.size()};
+DigestTree.prototype.get=function(a){return this.digestnode[a]};DigestTree.prototype.getRoot=function(){return this.root};DigestTree.prototype.recomputeRoot=function(){for(var a=Crypto.createHash("sha256"),b=0;b<this.digestnode.length;b++)a.update(new Buffer(this.digestnode[b].digest,"hex"));this.root=a.digest("hex")};
+SyncStateProto={"package":"Sync",messages:[{name:"SyncState",fields:[{rule:"required",type:"string",name:"name",id:1,options:{}},{rule:"required",type:"ActionType",name:"type",id:2,options:{}},{rule:"optional",type:"SeqNo",name:"seqno",id:3,options:{}}],enums:[{name:"ActionType",values:[{name:"UPDATE",id:0},{name:"DELETE",id:1},{name:"OTHER",id:2}],options:{}}],messages:[{name:"SeqNo",fields:[{rule:"required",type:"uint32",name:"seq",id:1,options:{}},{rule:"required",type:"uint32",name:"session",
+id:2,options:{}}],enums:[],messages:[],options:{}}],options:{}},{name:"SyncStateMsg",fields:[{rule:"repeated",type:"SyncState",name:"ss",id:1,options:{}}],enums:[],messages:[],options:{}}],enums:[],imports:[],options:{}};exports.SyncStateProto=SyncStateProto;
+var Crypto=require("../crypto.js"),WireFormat=require("../encoding/wire-format.js").WireFormat,TlvEncoder=require("../encoding/tlv/tlv-encoder.js").TlvEncoder,Blob=require("./blob.js").Blob,CommandInterestGenerator=function(){this.lastTimestamp=Math.round((new Date).getTime())};exports.CommandInterestGenerator=CommandInterestGenerator;
+CommandInterestGenerator.prototype.generate=function(a,b,c,d,e){e="function"===typeof d?d:e;d="function"!==typeof d&&d?d:WireFormat.getDefaultWireFormat();for(var f=Math.round((new Date).getTime());f<=this.lastTimestamp;)f+=1;var g=new TlvEncoder(8);g.writeNonNegativeInteger(f);a.getName().append(new Blob(g.getOutput(),!1));a.getName().append(new Blob(Crypto.randomBytes(8),!1));this.lastTimestamp=f;b.sign(a,c,d,function(){(null==a.getInterestLifetimeMilliseconds()||0>a.getInterestLifetimeMilliseconds())&&
+a.setInterestLifetimeMilliseconds(1E3);e&&e()})};var LOG=require("../log.js").Log.LOG,InterestFilterTable=function(){this.table_=[]};exports.InterestFilterTable=InterestFilterTable;InterestFilterTable.Entry=function(a,b,c,d){this.interestFilterId_=a;this.filter_=b;this.onInterest_=c;this.face_=d};InterestFilterTable.Entry.prototype.getInterestFilterId=function(){return this.interestFilterId_};InterestFilterTable.Entry.prototype.getFilter=function(){return this.filter_};
+InterestFilterTable.Entry.prototype.getOnInterest=function(){return this.onInterest_};InterestFilterTable.Entry.prototype.getFace=function(){return this.face_};InterestFilterTable.prototype.setInterestFilter=function(a,b,c,d){this.table_.push(new InterestFilterTable.Entry(a,b,c,d))};InterestFilterTable.prototype.getMatchedFilters=function(a,b){for(var c=0;c<this.table_.length;++c){var d=this.table_[c];d.getFilter().doesMatch(a.getName())&&b.push(d)}};
+InterestFilterTable.prototype.unsetInterestFilter=function(a){for(var b=0,c=this.table_.length-1;0<=c;--c)this.table_[c].getInterestFilterId()==a&&(++b,this.table_.splice(c,1));0===b&&0<LOG&&console.log("unsetInterestFilter: Didn't find interestFilterId "+a)};var NdnCommon=require("../util/ndn-common.js").NdnCommon,LOG=require("../log.js").Log.LOG,PendingInterestTable=function(){this.table_=[];this.removeRequests_=[]};exports.PendingInterestTable=PendingInterestTable;
+PendingInterestTable.Entry=function(a,b,c,d,e){this.pendingInterestId_=a;this.interest_=b;this.onData_=c;this.onTimeout_=d;this.onNetworkNack_=e;this.timerId_=-1};PendingInterestTable.Entry.prototype.getPendingInterestId=function(){return this.pendingInterestId_};PendingInterestTable.Entry.prototype.getInterest=function(){return this.interest_};PendingInterestTable.Entry.prototype.getOnData=function(){return this.onData_};PendingInterestTable.Entry.prototype.getOnNetworkNack=function(){return this.onNetworkNack_};
+PendingInterestTable.Entry.prototype.callTimeout=function(){if(this.onTimeout_)try{this.onTimeout_(this.interest_)}catch(a){console.log("Error in onTimeout: "+NdnCommon.getErrorWithStackTrace(a))}};PendingInterestTable.Entry.prototype.setTimeout=function(a,b){-1===this.timerId_&&(this.timerId_=setTimeout(a,b))};PendingInterestTable.Entry.prototype.clearTimeout=function(){-1!==this.timerId_&&(clearTimeout(this.timerId_),this.timerId_=-1)};
+PendingInterestTable.prototype.add=function(a,b,c,d,e){var f=this.removeRequests_.indexOf(a);if(0<=f)return this.removeRequests_.splice(f,1),null;var g=new PendingInterestTable.Entry(a,b,c,d,e);this.table_.push(g);a=b.getInterestLifetimeMilliseconds()||4E3;var h=this;g.setTimeout(function(){1<LOG&&console.log("Interest time out: "+b.getName().toUri());var a=h.table_.indexOf(g);0<=a&&h.table_.splice(a,1);g.callTimeout()},a);return g};
+PendingInterestTable.prototype.extractEntriesForExpressedInterest=function(a,b){for(var c=this.table_.length-1;0<=c;--c){var d=this.table_[c];d.getInterest().matchesData(a)&&(d.clearTimeout(),b.push(d),this.table_.splice(c,1))}};PendingInterestTable.prototype.extractEntriesForNackInterest=function(a,b){for(var c=a.wireEncode(),d=this.table_.length-1;0<=d;--d){var e=this.table_[d];null!=e.getOnNetworkNack()&&e.getInterest().wireEncode().equals(c)&&(e.clearTimeout(),b.push(e),this.table_.splice(d,1))}};
+PendingInterestTable.prototype.removePendingInterest=function(a){if(null!=a){for(var b=0,c=this.table_.length-1;0<=c;--c){var d=this.table_[c];d.getPendingInterestId()==a&&(d.clearTimeout(),this.table_.splice(c,1),++b)}0===b&&0<LOG&&console.log("removePendingInterest: Didn't find pendingInterestId "+a);0===b&&0>this.removeRequests_.indexOf(a)&&this.removeRequests_.push(a)}};
+var LOG=require("../log.js").Log.LOG,RegisteredPrefixTable=function(a){this.interestFilterTable_=a;this.table_=[];this.removeRequests_=[]};exports.RegisteredPrefixTable=RegisteredPrefixTable;RegisteredPrefixTable.prototype.add=function(a,b,c){var d=this.removeRequests_.indexOf(a);if(0<=d)return this.removeRequests_.splice(d,1),!1;this.table_.push(new RegisteredPrefixTable._Entry(a,b,c));return!0};
+RegisteredPrefixTable.prototype.removeRegisteredPrefix=function(a){for(var b=0,c=this.table_.length-1;0<=c;--c){var d=this.table_[c];d.getRegisteredPrefixId()==a&&(++b,0<d.getRelatedInterestFilterId()&&this.interestFilterTable_.unsetInterestFilter(d.getRelatedInterestFilterId()),this.table_.splice(c,1))}0===b&&0<LOG&&console.log("removeRegisteredPrefix: Didn't find registeredPrefixId "+a);0===b&&0>this.removeRequests_.indexOf(a)&&this.removeRequests_.push(a)};
+RegisteredPrefixTable._Entry=function(a,b,c){this.registeredPrefixId_=a;this.prefix_=b;this.relatedInterestFilterId_=c};RegisteredPrefixTable._Entry.prototype.getRegisteredPrefixId=function(){return this.registeredPrefixId};RegisteredPrefixTable._Entry.prototype.getPrefix=function(){return this.prefix};RegisteredPrefixTable._Entry.prototype.getRelatedInterestFilterId=function(){return this.relatedInterestFilterId};IncomingFaceId=function(){this.faceId_=null};exports.IncomingFaceId=IncomingFaceId;
+IncomingFaceId.prototype.getFaceId=function(){return this.faceId_};IncomingFaceId.prototype.setFaceId=function(a){this.faceId_=a};IncomingFaceId.getFirstHeader=function(a){for(var b=0;b<a.countHeaderFields();++b){var c=a.getHeaderField(b);if(c instanceof IncomingFaceId)return c}return null};var Blob=require("../util/blob.js").Blob,LpPacket=function(){this.headerFields_=[];this.fragmentWireEncoding_=new Blob};exports.LpPacket=LpPacket;LpPacket.prototype.getFragmentWireEncoding=function(){return this.fragmentWireEncoding_};
+LpPacket.prototype.countHeaderFields=function(){return this.headerFields_.length};LpPacket.prototype.getHeaderField=function(a){return this.headerFields_[a]};LpPacket.prototype.clear=function(){this.headerFields_=[];this.fragmentWireEncoding_=new Blob};LpPacket.prototype.setFragmentWireEncoding=function(a){this.fragmentWireEncoding_="object"===typeof a&&a instanceof Blob?a:new Blob(a)};LpPacket.prototype.addHeaderField=function(a){this.headerFields_.push(a)};
+var DataUtils=require("./encoding/data-utils.js").DataUtils,Name=require("./name.js").Name,Interest=require("./interest.js").Interest,Data=require("./data.js").Data,ControlParameters=require("./control-parameters.js").ControlParameters,ControlResponse=require("./control-response.js").ControlResponse,InterestFilter=require("./interest-filter.js").InterestFilter,WireFormat=require("./encoding/wire-format.js").WireFormat,TlvWireFormat=require("./encoding/tlv-wire-format.js").TlvWireFormat,Tlv=require("./encoding/tlv/tlv.js").Tlv,
+TlvDecoder=require("./encoding/tlv/tlv-decoder.js").TlvDecoder,ForwardingFlags=require("./forwarding-flags.js").ForwardingFlags,Transport=require("./transport/transport.js").Transport,TcpTransport=require("./transport/tcp-transport.js").TcpTransport,UnixTransport=require("./transport/unix-transport.js").UnixTransport,CommandInterestGenerator=require("./util/command-interest-generator.js").CommandInterestGenerator,Blob=require("./util/blob.js").Blob,NdnCommon=require("./util/ndn-common.js").NdnCommon,
+NetworkNack=require("./network-nack.js").NetworkNack,LpPacket=require("./lp/lp-packet.js").LpPacket,InterestFilterTable=require("./impl/interest-filter-table.js").InterestFilterTable,PendingInterestTable=require("./impl/pending-interest-table.js").PendingInterestTable,RegisteredPrefixTable=require("./impl/registered-prefix-table.js").RegisteredPrefixTable,fs=require("fs"),LOG=require("./log.js").Log.LOG;
+Face=function Face(b,c){if(!Face.supported)throw Error("The necessary JavaScript support is not available on this platform.");var d;if("object"==typeof b&&b instanceof Transport){if(this.getConnectionInfo=null,this.transport=b,this.connectionInfo=c||null,d={},null==this.connectionInfo&&this.transport&&this.transport.__proto__&&"UnixTransport"==this.transport.__proto__.name){var e=Face.getUnixSocketFilePathForLocalhost();null!=e?this.connectionInfo=new UnixTransport.ConnectionInfo(e):console.log("Face constructor: Cannot determine the default Unix socket file path for UnixTransport");
+0<LOG&&console.log("Using "+this.connectionInfo.toString())}}else d=b||{},this.transport=(d.getTransport||function(){return new TcpTransport})(),this.getConnectionInfo=d.getConnectionInfo||this.transport.defaultGetConnectionInfo,this.connectionInfo=d.connectionInfo||null,null==this.connectionInfo&&(e=void 0!==d.host?d.host:null,this.transport&&this.transport.__proto__&&"UnixTransport"==this.transport.__proto__.name?null!=e?this.connectionInfo=new UnixTransport.ConnectionInfo(e):null==this.getConnectionInfo&&
+(e=Face.getUnixSocketFilePathForLocalhost(),null!=e?this.connectionInfo=new UnixTransport.ConnectionInfo(e):console.log("Face constructor: Cannot determine the default Unix socket file path for UnixTransport")):null!=e&&(this.connectionInfo="undefined"!=typeof WebSocketTransport?new WebSocketTransport.ConnectionInfo(e,d.port||9696):new TcpTransport.ConnectionInfo(e,d.port||6363)));null==this.connectionInfo?this.host=this.host=null:(this.host=this.connectionInfo.host,this.host=this.connectionInfo.port);
+this.readyStatus=Face.UNOPEN;this.onopen=d.onopen||function(){3<LOG&&console.log("Face connection established.")};this.onclose=d.onclose||function(){3<LOG&&console.log("Face connection closed.")};this.onConnectedCallbacks=[];this.commandKeyChain=null;this.commandCertificateName=new Name;this.commandInterestGenerator=new CommandInterestGenerator;this.timeoutPrefix=new Name("/local/timeout");this.pendingInterestTable_=new PendingInterestTable;this.interestFilterTable_=new InterestFilterTable;this.registeredPrefixTable_=
+new RegisteredPrefixTable(this.interestFilterTable_);this.lastEntryId=0};exports.Face=Face;Face.UNOPEN=0;Face.OPEN_REQUESTED=1;Face.OPENED=2;Face.CLOSED=3;TcpTransport.importFace(Face);Face.getUnixSocketFilePathForLocalhost=function(){var a="/var/run/nfd.sock";if(fs.existsSync(a))return a;a="/tmp/.ndnd.sock";return fs.existsSync(a)?a:""};Face.getSupported=function(){try{(new Buffer(1)).slice(0,1)}catch(a){return console.log("NDN not available: Buffer not supported. "+a),!1}return!0};
+Face.supported=Face.getSupported();Face.prototype.createRoute=function(a,b){this.connectionInfo=a instanceof Transport.ConnectionInfo?a:new TcpTransport.ConnectionInfo(a,b);this.host=this.connectionInfo.host;this.host=this.connectionInfo.port};Face.prototype.close=function(){this.readyStatus==Face.OPENED&&(this.readyStatus=Face.CLOSED,this.transport.close())};Face.prototype.getNextEntryId=function(){return++this.lastEntryId};
+Face.makeShuffledHostGetConnectionInfo=function(a,b,c){a=a.slice(0,a.length);DataUtils.shuffle(a);return function(){return 0==a.length?null:c(a.splice(0,1)[0],b)}};
+Face.prototype.expressInterest=function(a,b,c,d,e,f){var g;"object"===typeof a&&a instanceof Interest?g=new Interest(a):b&&"object"===typeof b&&b instanceof Interest?(g=new Interest(b),g.setName(a),b=c,c=d,d=e,e=f):(g=new Interest(a),g.setInterestLifetimeMilliseconds(4E3));var h=b,k,l,m;k="function"===typeof c?c:function(){};l="function"===typeof d?d:null;m=c instanceof WireFormat?c:d instanceof WireFormat?d:e instanceof WireFormat?e:WireFormat.getDefaultWireFormat();var q=this.getNextEntryId();g.setNonce(Face.nonceTemplate_);
+g.refreshNonce();if(null==this.connectionInfo)if(null==this.getConnectionInfo)console.log("ERROR: connectionInfo is NOT SET");else{var p=this;this.connectAndExecute(function(){p.reconnectAndExpressInterest(q,g,h,k,l,m)})}else this.reconnectAndExpressInterest(q,g,h,k,l,m);return q};
+Face.prototype.reconnectAndExpressInterest=function(a,b,c,d,e,f){var g=this;if(this.connectionInfo.equals(this.transport.connectionInfo)&&this.readyStatus!==Face.UNOPEN)if(this.readyStatus===Face.OPEN_REQUESTED)this.onConnectedCallbacks.push(function(){g.expressInterestHelper(a,b,c,d,e,f)});else if(this.readyStatus===Face.OPENED)this.expressInterestHelper(a,b,c,d,e,f);else throw Error("reconnectAndExpressInterest: unexpected connection is not opened");else this.readyStatus=Face.OPEN_REQUESTED,this.onConnectedCallbacks.push(function(){g.expressInterestHelper(a,
+b,c,d,e,f)}),this.transport.connect(this.connectionInfo,this,function(){for(g.readyStatus=Face.OPENED;0<g.onConnectedCallbacks.length;)try{g.onConnectedCallbacks.shift()()}catch(a){console.log("Face.reconnectAndExpressInterest: ignoring exception from onConnectedCallbacks: "+a)}if(g.onopen)g.onopen()},function(){g.closeByTransport()})};
+Face.prototype.expressInterestHelper=function(a,b,c,d,e,f){if(null!=this.pendingInterestTable_.add(a,b,c,d,e)&&!this.timeoutPrefix.match(b.getName())){a=b.wireEncode(f);if(a.size()>Face.getMaxNdnPacketSize())throw Error("The encoded interest size exceeds the maximum limit getMaxNdnPacketSize()");this.transport.send(a.buf())}};Face.prototype.removePendingInterest=function(a){this.pendingInterestTable_.removePendingInterest(a)};
+Face.prototype.setCommandSigningInfo=function(a,b){this.commandKeyChain=a;this.commandCertificateName=new Name(b)};Face.prototype.setCommandCertificateName=function(a){this.commandCertificateName=new Name(a)};Face.prototype.makeCommandInterest=function(a,b,c){c="function"===typeof b?b:c;b="function"!==typeof b&&b?b:WireFormat.getDefaultWireFormat();this.nodeMakeCommandInterest(a,this.commandKeyChain,this.commandCertificateName,b,c)};
+Face.prototype.nodeMakeCommandInterest=function(a,b,c,d,e){this.commandInterestGenerator.generate(a,b,c,d,e)};
+Face.prototype.registerPrefix=function(a,b,c,d,e,f){var g=d,h=e,k=f;d="function"===typeof g?g:null;e=g instanceof ForwardingFlags?g:h instanceof ForwardingFlags?h:new ForwardingFlags;f=g instanceof WireFormat?g:h instanceof WireFormat?h:k instanceof WireFormat?k:WireFormat.getDefaultWireFormat();c||(c=function(){});var l=this.getNextEntryId(),m=this,g=function(){m.nfdRegisterPrefix(l,a,b,e,c,d,m.commandKeyChain,m.commandCertificateName,f)};null==this.connectionInfo?null==this.getConnectionInfo?console.log("ERROR: connectionInfo is NOT SET"):
+this.connectAndExecute(g):g();return l};Face.getMaxNdnPacketSize=function(){return NdnCommon.MAX_NDN_PACKET_SIZE};Face.RegisterResponse=function(a,b,c,d,e,f){this.prefix=a;this.onRegisterFailed=b;this.onRegisterSuccess=c;this.registeredPrefixId=d;this.parent=e;this.onInterest=f};
+Face.RegisterResponse.prototype.onData=function(a,b){var c=new ControlResponse;try{c.wireDecode(b.getContent(),TlvWireFormat.get())}catch(d){0<LOG&&console.log("Register prefix failed: Error decoding the NFD response: "+d);if(this.onRegisterFailed)try{this.onRegisterFailed(this.prefix)}catch(e){console.log("Error in onRegisterFailed: "+NdnCommon.getErrorWithStackTrace(e))}return}if(200!=c.getStatusCode()){if(0<LOG&&console.log("Register prefix failed: Expected NFD status code 200, got: "+c.getStatusCode()),
+this.onRegisterFailed)try{this.onRegisterFailed(this.prefix)}catch(f){console.log("Error in onRegisterFailed: "+NdnCommon.getErrorWithStackTrace(f))}}else{if(0!=this.registeredPrefixId&&(c=0,null!=this.onInterest&&(c=this.parent.setInterestFilter(new InterestFilter(this.prefix),this.onInterest)),!this.parent.registeredPrefixTable_.add(this.registeredPrefixId,this.prefix,c))){0<c&&this.parent.unsetInterestFilter(c);return}2<LOG&&console.log("Register prefix succeeded with the NFD forwarder for prefix "+
+this.prefix.toUri());if(null!=this.onRegisterSuccess)try{this.onRegisterSuccess(this.prefix,this.registeredPrefixId)}catch(g){console.log("Error in onRegisterSuccess: "+NdnCommon.getErrorWithStackTrace(g))}}};Face.RegisterResponse.prototype.onTimeout=function(a){2<LOG&&console.log("Timeout for NFD register prefix command.");if(this.onRegisterFailed)try{this.onRegisterFailed(this.prefix)}catch(b){console.log("Error in onRegisterFailed: "+NdnCommon.getErrorWithStackTrace(b))}};
+Face.prototype.nfdRegisterPrefix=function(a,b,c,d,e,f,g,h,k){if(null==g)throw Error("registerPrefix: The command KeyChain has not been set. You must call setCommandSigningInfo.");if(0==h.size())throw Error("registerPrefix: The command certificate name has not been set. You must call setCommandSigningInfo.");var l=new ControlParameters;l.setName(b);l.setForwardingFlags(d);var m=this;this.isLocal(function(d){var p=new Interest;d?(p.setName(new Name("/localhost/nfd/rib/register")),p.setInterestLifetimeMilliseconds(2E3)):
+(p.setName(new Name("/localhop/nfd/rib/register")),p.setInterestLifetimeMilliseconds(4E3));p.getName().append(l.wireEncode(TlvWireFormat.get()));m.nodeMakeCommandInterest(p,g,h,TlvWireFormat.get(),function(){var d=new Face.RegisterResponse(b,e,f,a,m,c);m.reconnectAndExpressInterest(null,p,d.onData.bind(d),d.onTimeout.bind(d),null,k)})},function(a){0<LOG&&console.log("Error in Transport.isLocal: "+a);if(e)try{e(b)}catch(c){console.log("Error in onRegisterFailed: "+NdnCommon.getErrorWithStackTrace(c))}})};
+Face.prototype.removeRegisteredPrefix=function(a){this.registeredPrefixTable_.removeRegisteredPrefix(a)};Face.prototype.setInterestFilter=function(a,b){var c=this.getNextEntryId();this.interestFilterTable_.setInterestFilter(c,new InterestFilter(a),b,this);return c};Face.prototype.unsetInterestFilter=function(a){this.interestFilterTable_.unsetInterestFilter(a)};
+Face.prototype.putData=function(a,b){b=b||WireFormat.getDefaultWireFormat();var c=a.wireEncode(b);if(c.size()>Face.getMaxNdnPacketSize())throw Error("The encoded Data packet size exceeds the maximum limit getMaxNdnPacketSize()");this.transport.send(c.buf())};Face.prototype.send=function(a){if(a.length>Face.getMaxNdnPacketSize())throw Error("The encoded packet size exceeds the maximum limit getMaxNdnPacketSize()");this.transport.send(a)};
+Face.prototype.isLocal=function(a,b){null==this.connectionInfo?a(!1):this.transport.isLocal(this.connectionInfo,a,b)};
+Face.prototype.onReceivedElement=function(a){3<LOG&&console.log("Complete element received. Length "+a.length+". Start decoding.");var b=null;a[0]==Tlv.LpPacket_LpPacket&&(b=new LpPacket,TlvWireFormat.get().decodeLpPacket(b,a,!1),a=b.getFragmentWireEncoding().buf());var c=null,d=null;if(a[0]==Tlv.Interest||a[0]==Tlv.Data){var e=new TlvDecoder(a);e.peekType(Tlv.Interest,a.length)?(c=new Interest,c.wireDecode(a,TlvWireFormat.get()),null!=b&&c.setLpPacket(b)):e.peekType(Tlv.Data,a.length)&&(d=new Data,
+d.wireDecode(a,TlvWireFormat.get()),null!=b&&d.setLpPacket(b))}if(null!==b&&(b.setFragmentWireEncoding(new Blob),b=NetworkNack.getFirstHeader(b),null!=b)){if(null==c)return;d=[];this.pendingInterestTable_.extractEntriesForNackInterest(c,d);for(a=0;a<d.length;++a){c=d[a];try{c.getOnNetworkNack()(c.getInterest(),b)}catch(f){console.log("Error in onNetworkNack: "+NdnCommon.getErrorWithStackTrace(f))}}return}if(null!==c)for(3<LOG&&console.log("Interest packet received."),d=[],this.interestFilterTable_.getMatchedFilters(c,
+d),a=0;a<d.length;++a){b=d[a];3<LOG&&console.log("Found interest filter for "+c.getName().toUri());try{b.getOnInterest()(b.getFilter().getPrefix(),c,this,b.getInterestFilterId(),b.getFilter())}catch(g){console.log("Error in onInterest: "+NdnCommon.getErrorWithStackTrace(g))}}else if(null!==d)for(3<LOG&&console.log("Data packet received."),b=[],this.pendingInterestTable_.extractEntriesForExpressedInterest(d,b),a=0;a<b.length;++a){c=b[a];try{c.getOnData()(c.getInterest(),d)}catch(h){console.log("Error in onData: "+
+NdnCommon.getErrorWithStackTrace(h))}}};
+Face.prototype.connectAndExecute=function(a){var b=this.getConnectionInfo();if(null==b)console.log("ERROR: No more connectionInfo from getConnectionInfo"),this.host=this.host=this.connectionInfo=null;else if(b.equals(this.connectionInfo))console.log("ERROR: The host returned by getConnectionInfo is not alive: "+this.connectionInfo.toString());else{this.connectionInfo=b;0<LOG&&console.log("connectAndExecute: trying host from getConnectionInfo: "+this.connectionInfo.toString());this.host=this.connectionInfo.host;
+this.host=this.connectionInfo.port;b=new Interest(new Name("/"));b.setInterestLifetimeMilliseconds(4E3);var c=this,d=setTimeout(function(){0<LOG&&console.log("connectAndExecute: timeout waiting for host "+c.host);c.connectAndExecute(a)},3E3);this.reconnectAndExpressInterest(null,b,function(b,f){clearTimeout(d);0<LOG&&console.log("connectAndExecute: connected to host "+c.host);a()},function(a){},null,WireFormat.getDefaultWireFormat())}};
+Face.prototype.closeByTransport=function(){this.readyStatus=Face.CLOSED;this.onclose()};Face.nonceTemplate_=new Blob(new Buffer(4),!1);
+(function(a,b,c){function d(a,b){return"object"!=typeof b&&(b=b()),Object.keys(b).forEach(function(c){a[c]=b[c]}),a}function e(a){return{from:function(b){return a.prototype=Object.create(b.prototype),a.prototype.constructor=a,{extend:function(c){d(a.prototype,"object"!=typeof c?c(b.prototype):c)}}}}}function f(a,b){return b(a)}function g(a,b){function u(a){this._cfg={version:a,storesSource:null,dbschema:{},tables:{},contentUpgrade:null};this.stores({})}function m(a,b,d,e){var g,h,da,k,ra;if(0===a)Object.keys(F).forEach(function(a){W(b,
+a,F[a].primKey,F[a].indexes)}),g=v._createTransaction(H,R,F),g.idbtrans=b,g.idbtrans.onerror=G(d,["populating database"]),g.on("error").subscribe(d),r.newPSD(function(){r.PSD.trans=g;try{v.on("populate").fire(g)}catch(a){e.onerror=b.onerror=function(a){a.preventDefault()};try{b.abort()}catch(c){}b.db.close();d(a)}});else{if(h=[],da=S.filter(function(b){return b._cfg.version===a})[0],!da)throw new y("Dexie specification of currently installed DB version is missing");F=v._dbSchema=da._cfg.dbschema;
+k=!1;da=S.filter(function(b){return b._cfg.version>a});da.forEach(function(a){var e=F,g=a._cfg.dbschema;sa(e,b);sa(g,b);F=v._dbSchema=g;e=ja(e,g);e.add.forEach(function(a){h.push(function(b,c){W(b,a[0],a[1].primKey,a[1].indexes);c()})});e.change.forEach(function(a){if(a.recreate)throw new y("Not yet support for changing primary key");h.push(function(b,c){var d=b.objectStore(a.name);a.add.forEach(function(a){A(d,a)});a.change.forEach(function(a){d.deleteIndex(a.name);A(d,a)});a.del.forEach(function(a){d.deleteIndex(a)});
+c()})});a._cfg.contentUpgrade&&h.push(function(b,c){var e,h;k=!0;e=v._createTransaction(H,[].slice.call(b.db.objectStoreNames,0),g);e.idbtrans=b;h=0;e._promise=f(e._promise,function(a){return function(b,d,e){function f(a){return function(){a.apply(this,arguments);0==--h&&c()}}return++h,a.call(this,b,function(a,b){arguments[0]=f(a);arguments[1]=f(b);d.apply(this,arguments)},e)}});b.onerror=G(d,["running upgrader function for version",a._cfg.version]);e.on("error").subscribe(d);a._cfg.contentUpgrade(e);
+0===h&&c()});k&&(0<=navigator.userAgent.indexOf("Trident")||0<=navigator.userAgent.indexOf("MSIE"))||h.push(function(a,b){for(var d,e=0;e<a.db.objectStoreNames.length;++e)d=a.db.objectStoreNames[e],null!==g[d]&&g[d]!==c||a.db.deleteObjectStore(d);b()})});ra=function(){try{h.length?h.shift()(b,ra):s(F,b)}catch(a){e.onerror=b.onerror=function(a){a.preventDefault()};try{b.abort()}catch(c){}b.db.close();d(a)}};ra()}}function ja(a,b){var c={del:[],add:[],change:[]},d,e,f,g,h,k,n,u;for(d in a)b[d]||c.del.push(d);
+for(d in b)if(e=a[d],f=b[d],e)if(g={name:d,def:b[d],recreate:!1,del:[],add:[],change:[]},e.primKey.src!==f.primKey.src)g.recreate=!0,c.change.push(g);else{h=e.indexes.reduce(function(a,b){return a[b.name]=b,a},{});f=f.indexes.reduce(function(a,b){return a[b.name]=b,a},{});for(k in h)f[k]||g.del.push(k);for(k in f)n=h[k],u=f[k],n?n.src!==u.src&&g.change.push(u):g.add.push(u);(g.recreate||0<g.del.length||0<g.add.length||0<g.change.length)&&c.change.push(g)}else c.add.push([d,f]);return c}function W(a,
+b,c,d){var e=a.db.createObjectStore(b,c.keyPath?{keyPath:c.keyPath,autoIncrement:c.auto}:{autoIncrement:c.auto});return d.forEach(function(a){A(e,a)}),e}function s(a,b){Object.keys(a).forEach(function(c){b.db.objectStoreNames.contains(c)||W(b,c,a[c].primKey,a[c].indexes)})}function A(a,b){a.createIndex(b.name,b.keyPath,{unique:b.unique,multiEntry:b.multi})}function Ha(a,b){throw new y("Table "+b[0]+" not part of transaction. Original Scope Function Source: "+g.Promise.PSD.trans.scopeFunc.toString());
+}function T(a,b,c,d){this.name=a;this.schema=c;this.hook=N[a]?N[a].hook:K(null,{creating:[q,h],reading:[l,k],updating:[p,h],deleting:[M,h]});this._tpf=b;this._collClass=d||X}function ta(a,b,c,d){T.call(this,a,b,c,d||ua)}function va(a,b,c,d){function e(a,b,c,d){return f._promise(a,c,d)}var f=this,g,h;this.db=v;this.mode=a;this.storeNames=b;this.idbtrans=null;this.on=K(this,["complete","error"],"abort");this._reculock=0;this._blockedFuncs=[];this._psd=null;this.active=!0;this._dbschema=c;d&&(this.parent=
+d);this._tpf=e;this.tables=Object.create(wa);for(d=b.length-1;-1!==d;--d)g=b[d],h=v._tableFactory(a,c[g],e),this.tables[g]=h,this[g]||(this[g]=h)}function Y(a,b,c){this._ctx={table:a,index:":id"===b?null:b,collClass:a._collClass,or:c}}function X(a,b){var c=null,d=null,e;if(b)try{c=b()}catch(f){d=f}e=a._ctx;this._ctx={table:e.table,index:e.index,isPrimKey:!e.index||e.table.schema.primKey.keyPath&&e.index===e.table.schema.primKey.name,range:c,op:"openCursor",dir:"next",unique:"",algorithm:null,filter:null,
+isMatch:null,offset:0,limit:Infinity,error:d,or:e.or}}function ua(){X.apply(this,arguments)}function Ia(a,b){return a._cfg.version-b._cfg.version}function U(a,b,c,d,e,f){c.forEach(function(c){var g=v._tableFactory(d,e[c],b);a.forEach(function(a){a[c]||(f?Object.defineProperty(a,c,{configurable:!0,enumerable:!0,get:function(){var a=r.PSD&&r.PSD.trans;return a&&a.db===v?a.tables[c]:g}}):a[c]=g)})})}function ea(a){a.forEach(function(a){for(var b in a)a[b]instanceof T&&delete a[b]})}function ka(a,b,c,
+d,e,f){var g=r.PSD;f=f||k;a.onerror||(a.onerror=G(e));a.onsuccess=b?I(function(){var g=a.result,h;g?(h=function(){g["continue"]()},b(g,function(a){h=a},d,e)&&c(f(g.value),g,function(a){h=a}),h()):d()},e,g):I(function(){var b=a.result,e;b?(e=function(){b["continue"]()},c(f(b.value),b,function(a){e=a}),e()):d()},e,g)}function fa(a){var b=[];return a.split(",").forEach(function(a){a=a.trim();var c=a.replace("&","").replace("++","").replace("*",""),d=0!==c.indexOf("[")?c:a.substring(a.indexOf("[")+1,
+a.indexOf("]")).split("+");b.push(new Z(c,d||null,-1!==a.indexOf("&"),-1!==a.indexOf("*"),-1!==a.indexOf("++"),Array.isArray(d),-1!==d.indexOf(".")))}),b}function la(a,b){return a<b?-1:a>b?1:0}function Da(a,b){return a<b?1:a>b?-1:0}function Ea(a){return function(b,c){for(var d=0,e;;){if(e=a(b[d],c[d]),0!==e)return e;if(++d,d===b.length||d===c.length)return a(b.length,c.length)}}}function ma(a,b){return a?b?function(){return a.apply(this,arguments)&&b.apply(this,arguments)}:a:b}function Ja(){if(v.verno=
+D.version/10,v._dbSchema=F={},R=[].slice.call(D.objectStoreNames,0),0!==R.length){var a=D.transaction(xa(R),"readonly");R.forEach(function(b){for(var c,d=a.objectStore(b),e=d.keyPath,f=e&&"string"==typeof e&&-1!==e.indexOf("."),g=new Z(e,e||"",!1,!1,!!d.autoIncrement,e&&"string"!=typeof e,f),h=[],k=0;k<d.indexNames.length;++k)c=d.index(d.indexNames[k]),f=(e=c.keyPath)&&"string"==typeof e&&-1!==e.indexOf("."),c=new Z(c.name,e,!!c.unique,!!c.multiEntry,!1,e&&"string"!=typeof e,f),h.push(c);F[b]=new ya(b,
+g,h,{})});U([N],v._transPromiseFactory,Object.keys(F),H,F)}}function sa(a,b){for(var c,d,e,f,g=b.db.objectStoreNames,h=0;h<g.length;++h)for(c=g[h],d=b.objectStore(c),e=0;e<d.indexNames.length;++e){var k=d.indexNames[e],n=d.index(k).keyPath,n="string"==typeof n?n:"["+[].slice.call(n).join("+")+"]";a[c]&&(f=a[c].idxByName[n],f&&(f.name=k))}}var ca=b&&b.addons||g.addons,na=g.dependencies,ga=na.indexedDB,L=na.IDBKeyRange,oa=na.TypeError,y=na.Error,F=this._dbSchema={},S=[],R=[],N={},wa={},D=null,$=!0,
+V=null,ha=!1,H="readwrite",v=this,Q=[],aa=!1,ia=!!Fa();this.version=function(a){if(D)throw new y("Cannot add version when database is open");this.verno=Math.max(this.verno,a);var b=S.filter(function(b){return b._cfg.version===a})[0];return b?b:(b=new u(a),S.push(b),S.sort(Ia),b)};d(u.prototype,{stores:function(a){var b,c;return this._cfg.storesSource=this._cfg.storesSource?d(this._cfg.storesSource,a):a,b={},S.forEach(function(a){d(b,a._cfg.storesSource)}),c=this._cfg.dbschema={},this._parseStoresSpec(b,
+c),F=v._dbSchema=c,ea([N,v,wa]),U([wa],Ha,Object.keys(c),H,c),U([N,v,this._cfg.tables],v._transPromiseFactory,Object.keys(c),H,c,!0),R=Object.keys(c),this},upgrade:function(a){var b=this;return B(function(){a(v._createTransaction(H,Object.keys(b._cfg.dbschema),b._cfg.dbschema))}),this._cfg.contentUpgrade=a,this},_parseStoresSpec:function(a,b){Object.keys(a).forEach(function(c){if(null!==a[c]){var d={},e=fa(a[c]),f=e.shift();if(f.multi)throw new y("Primary key cannot be multi-valued");f.keyPath&&f.auto&&
+J(d,f.keyPath,0);e.forEach(function(a){if(a.auto)throw new y("Only primary key can be marked as autoIncrement (++)");if(!a.keyPath)throw new y("Index must have a name and cannot be an empty string");J(d,a.keyPath,a.compound?a.keyPath.map(function(){return""}):"")});b[c]=new ya(c,f,e,d)}})}});this._allTables=N;this._tableFactory=function(a,b,c){return"readonly"===a?new T(b.name,c,b,X):new ta(b.name,c,b)};this._createTransaction=function(a,b,c,d){return new va(a,b,c,d)};this._transPromiseFactory=function(a,
+b,c){var d,e;return!$||r.PSD&&r.PSD.letThrough?(e=v._createTransaction(a,b,F),e._promise(a,function(a,b){e.error(function(a){v.on("error").fire(a)});c(function(b){e.complete(function(){a(b)})},b,e)})):d=new r(function(e,f){Q.push({resume:function(){var g=v._transPromiseFactory(a,b,c);d.onuncatched=g.onuncatched;g.then(e,f)}})})};this._whenReady=function(a){return!$||r.PSD&&r.PSD.letThrough?new r(a):new r(function(b,c){B(function(){new r(function(){a(b,c)})});Q.push({resume:function(){a(b,c)}})})};
+this.verno=0;this.open=function(){return new r(function(b,c){function d(a){try{e.transaction.abort()}catch(b){}ha=!1;V=a;$=!1;c(V);Q.forEach(function(a){a.resume()});Q=[]}if(D||ha)throw new y("Database already opened or being opened");var e;try{if(V=null,ha=!0,0===S.length&&(aa=!0),!ga)throw new y("indexedDB API not found. If using IE10+, make sure to run your code on a server URL (not locally). If using Safari, make sure to include indexedDB polyfill.");e=aa?ga.open(a):ga.open(a,Math.round(10*v.verno));
+e.onerror=G(d,["opening database",a]);e.onblocked=function(a){v.on("blocked").fire(a)};e.onupgradeneeded=I(function(b){var c,f;aa&&!v._allowEmptyDB?(e.onerror=function(a){a.preventDefault()},e.transaction.abort(),e.result.close(),c=ga.deleteDatabase(a),c.onsuccess=c.onerror=function(){d(new y("Database '"+a+"' doesnt exist"))}):(e.transaction.onerror=G(d),f=b.oldVersion>Math.pow(2,62)?0:b.oldVersion,m(f/10,e.transaction,d,e))},d);e.onsuccess=I(function(){ha=!1;D=e.result;aa?Ja():0<D.objectStoreNames.length&&
+sa(F,D.transaction(xa(D.objectStoreNames),"readonly"));D.onversionchange=v.on("versionchange").fire;ia||za(function(b){if(-1===b.indexOf(a))return b.push(a)});r.newPSD(function(){function a(){$=!1;Q.forEach(function(a){a.resume()});Q=[];b()}r.PSD.letThrough=!0;try{var c=v.on.ready.fire();c&&"function"==typeof c.then?c.then(a,function(a){D.close();D=null;d(a)}):x(a)}catch(e){d(e)}})},d)}catch(f){d(f)}})};this.close=function(){D&&(D.close(),D=null,$=!0,V=null)};this["delete"]=function(){var b=arguments;
+return new r(function(c,d){function e(){v.close();var b=ga.deleteDatabase(a);b.onsuccess=function(){ia||za(function(b){var c=b.indexOf(a);if(0<=c)return b.splice(c,1)});c()};b.onerror=G(d,["deleting",a]);b.onblocked=function(){v.on("blocked").fire()}}if(0<b.length)throw new y("Arguments not allowed in db.delete()");ha?Q.push({resume:e}):e()})};this.backendDB=function(){return D};this.isOpen=function(){return null!==D};this.hasFailed=function(){return null!==V};this.dynamicallyOpened=function(){return aa};
+this.name=a;Object.defineProperty(this,"tables",{get:function(){return Object.keys(N).map(function(a){return N[a]})}});this.on=K(this,"error","populate","blocked",{ready:[O,h],versionchange:[w,h]});this.on.ready.subscribe=f(this.on.ready.subscribe,function(a){return function(b,c){function d(){return c||v.on.ready.unsubscribe(d),b.apply(this,arguments)}a.call(this,d);v.isOpen()&&($?Q.push({resume:d}):d())}});B(function(){v.on("populate").fire(v._createTransaction(H,R,F));v.on("error").fire(new y)});
+this.transaction=function(a,b,c){function d(b,g){var n=null,u,l,E;try{if(h)throw h;n=v._createTransaction(a,k,F,e);u=k.map(function(a){return n.tables[a]});u.push(n);E=0;r.newPSD(function(){r.PSD.trans=n;n.scopeFunc=c;e&&(n.idbtrans=e.idbtrans,n._promise=f(n._promise,function(a){return function(b,c,d){function e(a){return function(b){var c;return r._rootExec(function(){c=a(b);r._tickFinalize(function(){0==--E&&n.active&&(n.active=!1,n.on.complete.fire())})}),c}}return++E,a.call(this,b,function(a,
+b,d){return c(e(a),e(b),d)},d)}}));n.complete(function(){b(l)});n.error(function(a){n.idbtrans&&(n.idbtrans.onerror=Ga);try{n.abort()}catch(b){}e&&(e.active=!1,e.on.error.fire(a));var c=g(a);e||c||v.on.error.fire(a)});r._rootExec(function(){l=c.apply(n,u)})});(!n.idbtrans||e&&0===E)&&n._nop()}catch(m){n&&n.idbtrans&&(n.idbtrans.onerror=Ga),n&&n.abort(),e&&e.on.error.fire(m),x(function(){g(m)||v.on("error").fire(m)})}}var e,g;b=[].slice.call(arguments,1,arguments.length-1);c=arguments[arguments.length-
+1];(e=r.PSD&&r.PSD.trans)&&e.db===v&&-1===a.indexOf("!")||(e=null);g=-1!==a.indexOf("?");a=a.replace("!","").replace("?","");var h=null,k=(Array.isArray(b[0])?b.reduce(function(a,b){return a.concat(b)}):b).map(function(a){return"string"==typeof a?a:(a instanceof T||(h=h||new oa("Invalid type. Arguments following mode must be instances of Table or String")),a.name)});return"r"==a||"readonly"==a?a="readonly":"rw"==a||a==H?a=H:h=new y("Invalid transaction mode: "+a),e&&(h||(e&&"readonly"===e.mode&&a===
+H&&(g?e=null:h=h||new y("Cannot enter a sub-transaction with READWRITE mode when parent transaction is READONLY")),e&&k.forEach(function(a){e.tables.hasOwnProperty(a)||(g?e=null:h=h||new y("Table "+a+" not included in parent transaction. Parent Transaction function: "+e.scopeFunc.toString()))}))),e?e._promise(a,d,"lock"):v._whenReady(d)};this.table=function(a){if(!aa&&!N.hasOwnProperty(a))throw new y("Table does not exist");return N[a]};d(T.prototype,function(){function a(){throw new y("Current Transaction is READONLY");
+}return{_trans:function(a,b,c){return this._tpf(a,[this.name],b,c)},_idbstore:function(a,b,c){var d=this;return this._tpf(a,[this.name],function(a,c,e){b(a,c,e.idbtrans.objectStore(d.name),e)},c)},get:function(a,b){var c=this;return B(function(){b(c.schema.instanceTemplate)}),this._idbstore("readonly",function(b,d,e){var f=e.get(a);f.onerror=G(d,["getting",a,"from",c.name]);f.onsuccess=function(){b(c.hook.reading.fire(f.result))}}).then(b)},where:function(a){return new Y(this,a)},count:function(a){return this.toCollection().count(a)},
+offset:function(a){return this.toCollection().offset(a)},limit:function(a){return this.toCollection().limit(a)},reverse:function(){return this.toCollection().reverse()},filter:function(a){return this.toCollection().and(a)},each:function(a){var b=this;return B(function(){a(b.schema.instanceTemplate)}),this._idbstore("readonly",function(c,d,e){e=e.openCursor();e.onerror=G(d,["calling","Table.each()","on",b.name]);ka(e,null,a,c,d,b.hook.reading.fire)})},toArray:function(a){var b=this;return B(function(){a([b.schema.instanceTemplate])}),
+this._idbstore("readonly",function(a,c,d){var e=[];d=d.openCursor();d.onerror=G(c,["calling","Table.toArray()","on",b.name]);ka(d,null,function(a){e.push(a)},function(){a(e)},c,b.hook.reading.fire)}).then(a)},orderBy:function(a){return new this._collClass(new Y(this,a))},toCollection:function(){return new this._collClass(new Y(this))},mapToClass:function(a,b){var c,d;return this.schema.mappedClass=a,c=Object.create(a.prototype),this.schema.primKey.keyPath&&(J(c,this.schema.primKey.keyPath,this.schema.primKey.auto?
+0:""),Ba(a.prototype,this.schema.primKey.keyPath)),b&&Aa(c,b),this.schema.instanceTemplate=c,d=Object.setPrototypeOf?function(b){return b?(Object.setPrototypeOf(b,a.prototype),b):b}:function(b){var c,d;if(!b)return b;c=Object.create(a.prototype);for(d in b)b.hasOwnProperty(d)&&(c[d]=b[d]);return c},this.schema.readHook&&this.hook.reading.unsubscribe(this.schema.readHook),this.schema.readHook=d,this.hook("reading",d),a},defineClass:function(a){return this.mapToClass(g.defineClass(a),a)},add:a,put:a,
+"delete":a,clear:a,update:a}});e(ta).from(T).extend(function(){return{add:function(a,b){var d=this,e=this.hook.creating.fire;return this._idbstore(H,function(f,g,k,n){var u={},l,E,m;e!==h&&(l=b||(k.keyPath?P(a,k.keyPath):c),E=e.call(u,l,a,n),l===c&&E!==c&&(k.keyPath?J(a,k.keyPath,E):b=E));m=b?k.add(a,b):k.add(a);m.onerror=G(function(a){if(u.onerror)u.onerror(a);return g(a)},["adding",a,"into",d.name]);m.onsuccess=function(b){var c=k.keyPath;if(c&&J(a,c,b.target.result),u.onsuccess)u.onsuccess(b.target.result);
+f(m.result)}})},put:function(a,b){var d=this,e=this.hook.updating.fire;return this.hook.creating.fire!==h||e!==h?this._trans(H,function(e,f,g){var h=b||d.schema.primKey.keyPath&&P(a,d.schema.primKey.keyPath);h===c?g.tables[d.name].add(a).then(e,f):(g._lock(),a=ba(a),g.tables[d.name].where(":id").equals(h).modify(function(){this.value=a}).then(function(c){return 0===c?g.tables[d.name].add(a,b):h})["finally"](function(){g._unlock()}).then(e,f))}):this._idbstore(H,function(c,e,f){var g=b?f.put(a,b):
+f.put(a);g.onerror=G(e,["putting",a,"into",d.name]);g.onsuccess=function(b){var d=f.keyPath;d&&J(a,d,b.target.result);c(g.result)}})},"delete":function(a){return this.hook.deleting.subscribers.length?this.where(":id").equals(a)["delete"]():this._idbstore(H,function(b,c,d){var e=d["delete"](a);e.onerror=G(c,["deleting",a,"from",d.name]);e.onsuccess=function(){b(e.result)}})},clear:function(){return this.hook.deleting.subscribers.length?this.toCollection()["delete"]():this._idbstore(H,function(a,b,
+c){var d=c.clear();d.onerror=G(b,["clearing",c.name]);d.onsuccess=function(){a(d.result)}})},update:function(a,b){if("object"!=typeof b||Array.isArray(b))throw new y("db.update(keyOrObject, modifications). modifications must be an object.");if("object"!=typeof a||Array.isArray(a))return this.where(":id").equals(a).modify(b);Object.keys(b).forEach(function(c){J(a,c,b[c])});var d=P(a,this.schema.primKey.keyPath);return d===c&&r.reject(new y("Object does not contain its primary key")),this.where(":id").equals(d).modify(b)}}});
+d(va.prototype,{_lock:function(){return++this._reculock,1===this._reculock&&r.PSD&&(r.PSD.lockOwnerFor=this),this},_unlock:function(){if(0==--this._reculock)for(r.PSD&&(r.PSD.lockOwnerFor=null);0<this._blockedFuncs.length&&!this._locked();){var a=this._blockedFuncs.shift();try{a()}catch(b){}}return this},_locked:function(){return this._reculock&&(!r.PSD||r.PSD.lockOwnerFor!==this)},_nop:function(a){this.tables[this.storeNames[0]].get(0).then(a)},_promise:function(a,b,c){var d=this;return r.newPSD(function(){var e;
+return d._locked()?e=new r(function(e,f){d._blockedFuncs.push(function(){d._promise(a,b,c).then(e,f)})}):(e=d.active?new r(function(e,f){if(!d.idbtrans&&a){if(!D)throw V?new y("Database not open. Following error in populate, ready or upgrade function made Dexie.open() fail: "+V):new y("Database not open");var h=d.idbtrans=D.transaction(xa(d.storeNames),d.mode);h.onerror=function(a){d.on("error").fire(a&&a.target.error);a.preventDefault();d.abort()};h.onabort=function(a){d.active=!1;d.on("abort").fire(a)};
+h.oncomplete=function(a){d.active=!1;d.on("complete").fire(a)}}c&&d._lock();try{b(e,f,d)}catch(k){g.ignoreTransaction(function(){d.on("error").fire(k)}),d.abort(),f(k)}}):r.reject(Ka(new y("Transaction is inactive. Original Scope Function Source: "+d.scopeFunc.toString()))),d.active&&c&&e["finally"](function(){d._unlock()})),e.onuncatched=function(a){g.ignoreTransaction(function(){d.on("error").fire(a)});d.abort()},e})},complete:function(a){return this.on("complete",a)},error:function(a){return this.on("error",
+a)},abort:function(){if(this.idbtrans&&this.active)try{this.active=!1,this.idbtrans.abort(),this.on.error.fire(new y("Transaction Aborted"))}catch(a){}},table:function(a){if(!this.tables.hasOwnProperty(a))throw new y("Table "+a+" not in transaction");return this.tables[a]}});d(Y.prototype,function(){function a(b,c){try{throw c;}catch(d){b._ctx.error=d}return b}function b(a){return Array.prototype.slice.call(1===a.length&&Array.isArray(a[0])?a[0]:a)}function c(a){return"next"===a?function(a){return a.toUpperCase()}:
+function(a){return a.toLowerCase()}}function d(a){return"next"===a?function(a){return a.toLowerCase()}:function(a){return a.toUpperCase()}}function e(a,b,c,d,f,g){for(var h,k=Math.min(a.length,d.length),n=-1,u=0;u<k;++u){if(h=b[u],h!==d[u])return 0>f(a[u],c[u])?a.substr(0,u)+c[u]+c.substr(u+1):0>f(a[u],d[u])?a.substr(0,u)+d[u]+c.substr(u+1):0<=n?a.substr(0,n)+b[n]+c.substr(n+1):null;0>f(a[u],h)&&(n=u)}return k<d.length&&"next"===g?a+c.substr(a.length):k<a.length&&"prev"===g?a.substr(0,c.length):0>
+n?null:a.substr(0,n)+d[n]+c.substr(n+1)}function f(a,b,g){function h(a){k=c(a);n=d(a);u="next"===a?la:Da;l=k(g);E=n(g);m=a}var k,n,u,l,E,m;h("next");a._ondirectionchange=function(a){h(a)};a._addAlgorithm(function(a,c,d){var f=a.key,g,h;return"string"!=typeof f?!1:(g=n(f),b(g,E)?(c(function(){a["continue"]()}),!0):(h=e(f,g,l,E,u,m),h?c(function(){a["continue"](h)}):c(d),!1))})}return{between:function(a,b,c,d){return(c=!1!==c,d=!0===d,a>b||a===b&&!(!c&&!d||c&&d))?(new this._ctx.collClass(this,function(){return L.only(a)})).limit(0):
+new this._ctx.collClass(this,function(){return L.bound(a,b,!c,!d)})},equals:function(a){return new this._ctx.collClass(this,function(){return L.only(a)})},above:function(a){return new this._ctx.collClass(this,function(){return L.lowerBound(a,!0)})},aboveOrEqual:function(a){return new this._ctx.collClass(this,function(){return L.lowerBound(a)})},below:function(a){return new this._ctx.collClass(this,function(){return L.upperBound(a,!0)})},belowOrEqual:function(a){return new this._ctx.collClass(this,
+function(){return L.upperBound(a)})},startsWith:function(b){return"string"!=typeof b?a(new this._ctx.collClass(this),new oa("String expected")):this.between(b,b+String.fromCharCode(65535),!0,!0)},startsWithIgnoreCase:function(b){if("string"!=typeof b)return a(new this._ctx.collClass(this),new oa("String expected"));if(""===b)return this.startsWith(b);var c=new this._ctx.collClass(this,function(){return L.bound(b.toUpperCase(),b.toLowerCase()+String.fromCharCode(65535))});return f(c,function(a,b){return 0===
+a.indexOf(b)},b),c._ondirectionchange=function(){a(c,new y("reverse() not supported with WhereClause.startsWithIgnoreCase()"))},c},equalsIgnoreCase:function(b){if("string"!=typeof b)return a(new this._ctx.collClass(this),new oa("String expected"));var c=new this._ctx.collClass(this,function(){return L.bound(b.toUpperCase(),b.toLowerCase())});return f(c,function(a,b){return a===b},b),c},anyOf:function(){var a=this._ctx,c=a.table.schema,d=(a=a.index?c.idxByName[a.index]:c.primKey)&&a.compound,e=b(arguments),
+f=d?Ea(la):la,g,h;return(e.sort(f),0===e.length)?(new this._ctx.collClass(this,function(){return L.only("")})).limit(0):(g=new this._ctx.collClass(this,function(){return L.bound(e[0],e[e.length-1])}),g._ondirectionchange=function(a){f="next"===a?la:Da;d&&(f=Ea(f));e.sort(f)},h=0,g._addAlgorithm(function(a,b,c){for(var d=a.key;0<f(d,e[h]);)if(++h,h===e.length)return b(c),!1;return 0===f(d,e[h])?(b(function(){a["continue"]()}),!0):(b(function(){a["continue"](e[h])}),!1)}),g)}}});d(X.prototype,function(){function a(b,
+c){b.filter=ma(b.filter,c)}function b(a,c){a.isMatch=ma(a.isMatch,c)}function c(a,b){if(a.isPrimKey)return b;var d=a.table.schema.idxByName[a.index];if(!d)throw new y("KeyPath "+a.index+" on object store "+b.name+" is not indexed");return a.isPrimKey?b:b.index(d.name)}function d(a,b){return c(a,b)[a.op](a.range||null,a.dir+a.unique)}function e(a,b,c,f,g){a.or?function(){function e(){2==++u&&c()}function h(a,c,d){if(!k||k(c,d,e,f)){var g=c.primaryKey.toString();n.hasOwnProperty(g)||(n[g]=!0,b(a,c,
+d))}}var k=a.filter,n={},u=0;a.or._iterate(h,e,f,g);ka(d(a,g),a.algorithm,h,e,f,a.table.hook.reading.fire)}():ka(d(a,g),ma(a.algorithm,a.filter),b,c,f,a.table.hook.reading.fire)}function f(a){return a.table.schema.instanceTemplate}return{_read:function(a,b){var c=this._ctx;return c.error?c.table._trans(null,function(a,b){b(c.error)}):c.table._idbstore("readonly",a).then(b)},_write:function(a){var b=this._ctx;return b.error?b.table._trans(null,function(a,c){c(b.error)}):b.table._idbstore(H,a,"locked")},
+_addAlgorithm:function(a){var b=this._ctx;b.algorithm=ma(b.algorithm,a)},_iterate:function(a,b,c,d){return e(this._ctx,a,b,c,d)},each:function(a){var b=this._ctx;return B(function(){a(f(b))}),this._read(function(c,d,f){e(b,a,c,d,f)})},count:function(a){var b,d,f;return B(function(){a(0)}),b=this,d=this._ctx,d.filter||d.algorithm||d.or?(f=0,this._read(function(a,b,c){e(d,function(){return++f,!1},function(){a(f)},b,c)},a)):this._read(function(a,e,f){f=c(d,f);f=d.range?f.count(d.range):f.count();f.onerror=
+G(e,["calling","count()","on",b.name]);f.onsuccess=function(b){a(Math.min(b.target.result,Math.max(0,d.limit-d.offset)))}},a)},sortBy:function(a,b){function c(a,b){return b?c(a[h[b]],b-1):a[k]}function d(a,b){var e=c(a,n),f=c(b,n);return e<f?-g:e>f?g:0}var e=this._ctx,g;B(function(){b([f(e)])});var h=a.split(".").reverse(),k=h[0],n=h.length-1;return g="next"===this._ctx.dir?1:-1,this.toArray(function(a){return a.sort(d)}).then(b)},toArray:function(a){var b=this._ctx;return B(function(){a([f(b)])}),
+this._read(function(a,c,d){var f=[];e(b,function(a){f.push(a)},function(){a(f)},c,d)},a)},offset:function(b){var c=this._ctx;return 0>=b?this:(c.offset+=b,c.or||c.algorithm||c.filter?a(c,function(){return 0>--b}):a(c,function(a,c){return 0===b?!0:1===b?(--b,!1):(c(function(){a.advance(b);b=0}),!1)}),this)},limit:function(b){return this._ctx.limit=Math.min(this._ctx.limit,b),a(this._ctx,function(a,c,d){return 0>=--b&&c(d),0<=b}),this},until:function(b,c){var d=this._ctx;return B(function(){b(f(d))}),
+a(this._ctx,function(a,d,e){return b(a.value)?(d(e),c):!0}),this},first:function(a){var b=this;return B(function(){a(f(b._ctx))}),this.limit(1).toArray(function(a){return a[0]}).then(a)},last:function(a){return this.reverse().first(a)},and:function(c){var d=this;return B(function(){c(f(d._ctx))}),a(this._ctx,function(a){return c(a.value)}),b(this._ctx,c),this},or:function(a){return new Y(this._ctx.table,a,this)},reverse:function(){return this._ctx.dir="prev"===this._ctx.dir?"next":"prev",this._ondirectionchange&&
+this._ondirectionchange(this._ctx.dir),this},desc:function(){return this.reverse()},eachKey:function(a){var b=this,c=this._ctx;return B(function(){a(f(b._ctx)[b._ctx.index])}),c.isPrimKey||(c.op="openKeyCursor"),this.each(function(b,c){a(c.key,c)})},eachUniqueKey:function(a){return this._ctx.unique="unique",this.eachKey(a)},keys:function(a){var b,c,d;return B(function(){a([f(c)[b._ctx.index]])}),b=this,c=this._ctx,c.isPrimKey||(c.op="openKeyCursor"),d=[],this.each(function(a,b){d.push(b.key)}).then(function(){return d}).then(a)},
+uniqueKeys:function(a){return this._ctx.unique="unique",this.keys(a)},firstKey:function(a){return this.limit(1).keys(function(a){return a[0]}).then(a)},lastKey:function(a){return this.reverse().firstKey(a)},distinct:function(){var b={};return a(this._ctx,function(a){a=a.primaryKey.toString();var c=b.hasOwnProperty(a);return b[a]=!0,!c}),this}}});e(ua).from(X).extend({modify:function(a){var b=this,c=this._ctx,e=c.table.hook,f=e.updating.fire,g=e.deleting.fire;return B(function(){"function"==typeof a&&
+a.call({value:c.table.schema.instanceTemplate},c.table.schema.instanceTemplate)}),this._write(function(e,k,n,u){function l(a){return a&&(r.push(a),fa.push(W)),k(new pa("Error modifying one or more objects",r,p,fa))}function E(){ja&&p+r.length===q&&(0<r.length?l():e(p))}var m,U,Ca,ea;"function"==typeof a?m=f===h&&g===h?a:function(b){var c=ba(b),d,e;if(!1===a.call(this,b))return!1;this.hasOwnProperty("value")?(d=La(c,this.value),e=f.call(this,d,this.primKey,c,u),e&&(b=this.value,Object.keys(e).forEach(function(a){J(b,
+a,e[a])}))):g.call(this,this.primKey,b,u)}:f===h?(U=Object.keys(a),Ca=U.length,m=function(b){for(var c,d,e=!1,f=0;f<Ca;++f)c=U[f],d=a[c],P(b,c)!==d&&(J(b,c,d),e=!0);return e}):(ea=a,a=qa(ea),m=function(b){var c=!1,e=f.call(this,a,this.primKey,ba(b),u);return e&&d(a,e),Object.keys(a).forEach(function(d){var e=a[d];P(b,d)!==e&&(J(b,d,e),c=!0)}),e&&(a=qa(ea)),c});var q=0,p=0,ja=!1,r=[],fa=[],W=null;b._iterate(function(a,b){var d,e,f;if(W=b.primaryKey,d={primKey:b.primaryKey,value:a},!1!==m.call(d,a))f=
+(e=!d.hasOwnProperty("value"))?b["delete"]():b.update(d.value),++q,f.onerror=G(function(a){if(r.push(a),fa.push(d.primKey),d.onerror)d.onerror(a);return E(),!0},e?["deleting",a,"from",c.table.name]:["modifying",a,"on",c.table.name]),f.onsuccess=function(){if(d.onsuccess)d.onsuccess(d.value);++p;E()};else if(d.onsuccess)d.onsuccess(d.value)},function(){ja=!0;E()},l,n)})},"delete":function(){return this.modify(function(){delete this.value})}});d(this,{Collection:X,Table:T,Transaction:va,Version:u,WhereClause:Y,
+WriteableCollection:ua,WriteableTable:ta});(function(){v.on("versionchange",function(){v.close();v.on("error").fire(new y("Database version changed by other database connection."))})})();ca.forEach(function(a){a(v)})}function h(){}function k(a){return a}function l(a,b){return a===k?b:function(c){return b(a(c))}}function m(a,b){return function(){a.apply(this,arguments);b.apply(this,arguments)}}function q(a,b){return a===h?b:function(){var d=a.apply(this,arguments),e,f,g;return d!==c&&(arguments[0]=
+d),e=this.onsuccess,f=this.onerror,delete this.onsuccess,delete this.onerror,g=b.apply(this,arguments),e&&(this.onsuccess=this.onsuccess?m(e,this.onsuccess):e),f&&(this.onerror=this.onerror?m(f,this.onerror):f),g!==c?g:d}}function p(a,b){return a===h?b:function(){var e=a.apply(this,arguments),f,g,h;return e!==c&&d(arguments[0],e),f=this.onsuccess,g=this.onerror,delete this.onsuccess,delete this.onerror,h=b.apply(this,arguments),f&&(this.onsuccess=this.onsuccess?m(f,this.onsuccess):f),g&&(this.onerror=
+this.onerror?m(g,this.onerror):g),e===c?h===c?c:h:h===c?e:d(e,h)}}function s(a,b){return a===h?b:function(){return!1===a.apply(this,arguments)?!1:b.apply(this,arguments)}}function w(a,b){return a===h?b:function(){return!1===b.apply(this,arguments)?!1:a.apply(this,arguments)}}function M(a,b){return a===h?b:function(){a.apply(this,arguments);b.apply(this,arguments)}}function O(a,b){return a===h?b:function(){var c=a.apply(this,arguments),d,e;return c&&"function"==typeof c.then?(d=this,e=arguments,c.then(function(){return b.apply(d,
+e)})):b.apply(this,arguments)}}function K(b){function c(a,b,f){if(Array.isArray(a))return e(a);if("object"==typeof a)return d(a);b||(b=s);f||(f=h);var n={subscribers:[],fire:f,subscribe:function(a){n.subscribers.push(a);n.fire=b(n.fire,a)},unsubscribe:function(a){n.subscribers=n.subscribers.filter(function(b){return b!==a});n.fire=n.subscribers.reduce(b,f)}};return g[a]=k[a]=n,n}function d(b){Object.keys(b).forEach(function(d){var e=b[d],f;if(Array.isArray(e))c(d,b[d][0],b[d][1]);else if("asap"===
+e)f=c(d,null,function(){var b=arguments;f.subscribers.forEach(function(c){x(function(){c.apply(a,b)})})}),f.subscribe=function(a){-1===f.subscribers.indexOf(a)&&f.subscribers.push(a)},f.unsubscribe=function(a){a=f.subscribers.indexOf(a);-1!==a&&f.subscribers.splice(a,1)};else throw Error("Invalid event config");})}function e(a){function b(){if(d)return!1;d=!0}var d=!1;a.forEach(function(a){c(a).subscribe(b)})}var f=arguments,g={},k=function(a,c){if(c){var d=[].slice.call(arguments,1),e=g[a];return e.subscribe.apply(e,
+d),b}if("string"==typeof a)return g[a]},l,m;k.addEventType=c;l=1;for(m=f.length;l<m;++l)c(f[l]);return k}function x(b){a.setImmediate?setImmediate(b):setTimeout(b,0)}function A(a){a=setTimeout(a,1E3);clearTimeout(a)}function I(a,b,c){return function(){var d=r.PSD;r.PSD=c;try{a.apply(this,arguments)}catch(e){b(e)}finally{r.PSD=d}}}function P(a,b){var d,e,f,g;if(a.hasOwnProperty(b))return a[b];if(!b)return a;if("string"!=typeof b){d=[];e=0;for(f=b.length;e<f;++e)g=P(a,b[e]),d.push(g);return d}return(d=
+b.indexOf("."),-1!==d)?(e=a[b.substr(0,d)],e===c?c:P(e,b.substr(d+1))):c}function J(a,b,d){var e,f,g,h;if(a&&b!==c)if("string"!=typeof b&&"length"in b){if(!("string"!=typeof d&&"length"in d))throw Error("Assertion failed");e=0;for(f=b.length;e<f;++e)J(a,b[e],d[e])}else e=b.indexOf("."),-1!==e?(f=b.substr(0,e),g=b.substr(e+1),""===g?d===c?delete a[f]:a[f]=d:(h=a[f],h||(h=a[f]={}),J(h,g,d))):d===c?delete a[b]:a[b]=d}function Ba(a,b){J(a,b,c)}function qa(a){var b={},c;for(c in a)a.hasOwnProperty(c)&&
+(b[c]=a[c]);return b}function ba(a){var b,c,d;if(!a||"object"!=typeof a)return a;if(Array.isArray(a))for(b=[],c=0,d=a.length;c<d;++c)b.push(ba(a[c]));else if(a instanceof Date)b=new Date,b.setTime(a.getTime());else for(c in b=a.constructor?Object.create(a.constructor.prototype):{},a)a.hasOwnProperty(c)&&(b[c]=ba(a[c]));return b}function La(a,b){var d={},e;for(e in a)a.hasOwnProperty(e)&&(b.hasOwnProperty(e)?a[e]!==b[e]&&JSON.stringify(a[e])!=JSON.stringify(b[e])&&(d[e]=b[e]):d[e]=c);for(e in b)b.hasOwnProperty(e)&&
+!a.hasOwnProperty(e)&&(d[e]=b[e]);return d}function ia(a){if("function"==typeof a)return new a;if(Array.isArray(a))return[ia(a[0])];if(a&&"object"==typeof a){var b={};return Aa(b,a),b}return a}function Aa(a,b){Object.keys(b).forEach(function(c){var d=ia(b[c]);a[c]=d})}function G(a,b){return function(c){var d=c&&c.target.error||Error(),e;return b&&(e=" occurred when "+b.map(function(a){switch(typeof a){case "function":return a();case "string":return a;default:return JSON.stringify(a)}}).join(" "),
+d.name?d.toString=function(){return d.name+e+(d.message?". "+d.message:"")}:d+=e),a(d),c&&(c.stopPropagation&&c.stopPropagation(),c.preventDefault&&c.preventDefault()),!1}}function Ka(a){try{throw a;}catch(b){return b}}function Ga(a){a.preventDefault()}function za(a){var b,c=g.dependencies.localStorage;if(!c)return a([]);try{b=JSON.parse(c.getItem("Dexie.DatabaseNames")||"[]")}catch(d){b=[]}a(b)&&c.setItem("Dexie.DatabaseNames",JSON.stringify(b))}function Z(a,b,c,d,e,f,g){this.name=a;this.keyPath=
+b;this.unique=c;this.multi=d;this.auto=e;this.compound=f;this.dotted=g;a="string"==typeof b?b:b&&"["+[].join.call(b,"+")+"]";this.src=(c?"&":"")+(d?"*":"")+(e?"++":"")+a}function ya(a,b,c,d){this.name=a;this.primKey=b||new Z;this.indexes=c||[new Z];this.instanceTemplate=d;this.mappedClass=null;this.idxByName=c.reduce(function(a,b){return a[b.name]=b,a},{})}function pa(a,b,c,d){this.name="ModifyError";this.failures=b;this.failedKeys=d;this.successCount=c;this.message=b.join("\n")}function xa(a){return 1===
+a.length?a[0]:a}function Fa(){var a=g.dependencies.indexedDB,b=a&&(a.getDatabaseNames||a.webkitGetDatabaseNames);return b&&b.bind(a)}var r=function(){function b(a){x.push([a,p.call(arguments,1)])}function c(){var b=x,d,e,f;x=[];d=0;for(e=b.length;d<e;++d)f=b[d],f[0].apply(a,f[1])}function d(a){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof a)throw new TypeError("not a function");this._value=this._state=null;this._deferreds=[];this._catched=
+!1;var b=this,c=!0;this._PSD=d.PSD;try{q(this,a,function(a){c?s(g,b,a):g(b,a)},function(a){return c?(s(k,b,a),!1):k(b,a)})}finally{c=!1}}function e(a,g){var h,k,l,m,q,p;if(null===a._state)a._deferreds.push(g);else{if(h=a._state?g.onFulfilled:g.onRejected,null===h)return(a._state?g.resolve:g.reject)(a._value);l=w;w=!1;s=b;try{m=d.PSD,d.PSD=a._PSD,k=h(a._value),a._state||k&&"function"==typeof k.then&&!1===k._state||f(a),g.resolve(k)}catch(B){if(q=g.reject(B),!q&&a.onuncatched)try{a.onuncatched(B)}catch(G){}}finally{if(d.PSD=
+m,l){do{for(;0<x.length;)c();if(p=A.pop(),p)try{p()}catch(I){}}while(0<A.length||0<x.length);s=r;w=!0}}}}function f(a){a._catched=!0;a._parent&&f(a._parent)}function g(a,b){var c=d.PSD;d.PSD=a._PSD;try{if(b===a)throw new TypeError("A promise cannot be resolved with itself.");!b||"object"!=typeof b&&"function"!=typeof b||"function"!=typeof b.then?(a._state=!0,a._value=b,l.call(a)):q(a,function(a,c){b.then(a,c)},function(b){g(a,b)},function(b){k(a,b)})}catch(e){k(e)}finally{d.PSD=c}}function k(a,b){var c=
+d.PSD;if(d.PSD=a._PSD,a._state=!1,a._value=b,l.call(a),!a._catched)try{if(a.onuncatched)a.onuncatched(a._value);d.on.error.fire(a._value)}catch(e){}return d.PSD=c,a._catched}function l(){for(var a=0,b=this._deferreds.length;a<b;a++)e(this,this._deferreds[a]);this._deferreds=[]}function m(a,b,c,d){this.onFulfilled="function"==typeof a?a:null;this.onRejected="function"==typeof b?b:null;this.resolve=c;this.reject=d}function q(a,b,c,d){var e=!1;try{b(function(a){e||(e=!0,c(a))},function(b){return e?a._catched:
+(e=!0,d(b))})}catch(f){return e?void 0:d(f)}}var p=[].slice,r="undefined"==typeof setImmediate?function(b){var c=arguments;setTimeout(function(){b.apply(a,p.call(c,1))},0)}:setImmediate,s=r,w=!0,x=[],A=[];return d.on=K(null,"error"),d.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&Array.isArray(arguments[0])?arguments[0]:arguments);return new d(function(b,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h){h.call(g,
+function(a){d(f,a)},c);return}}a[f]=g;0==--e&&b(a)}catch(k){c(k)}}var e,f;if(0===a.length)return b([]);e=a.length;for(f=0;f<a.length;f++)d(f,a[f])})},d.prototype.then=function(a,b){var c=this,f=new d(function(d,f){null===c._state?e(c,new m(a,b,d,f)):s(e,c,new m(a,b,d,f))});return f._PSD=this._PSD,f.onuncatched=this.onuncatched,f._parent=this,f},d.prototype._then=function(a,b){e(this,new m(a,b,h,h))},d.prototype["catch"]=function(a){if(1===arguments.length)return this.then(null,a);var b=arguments[0],
+c=arguments[1];return"function"==typeof b?this.then(null,function(a){return a instanceof b?c(a):d.reject(a)}):this.then(null,function(a){return a&&a.name===b?c(a):d.reject(a)})},d.prototype["finally"]=function(a){return this.then(function(b){return a(),b},function(b){return a(),d.reject(b)})},d.prototype.onuncatched=null,d.resolve=function(a){var b=new d(function(){});return b._state=!0,b._value=a,b},d.reject=function(a){var b=new d(function(){});return b._state=!1,b._value=a,b},d.race=function(a){return new d(function(b,
+c){a.map(function(a){a.then(b,c)})})},d.PSD=null,d.newPSD=function(a){var b=d.PSD;d.PSD=b?Object.create(b):{};try{return a()}finally{d.PSD=b}},d._rootExec=function(a){var d=w,e;w=!1;s=b;try{a()}finally{if(d){do{for(;0<x.length;)c();if(e=A.pop(),e)try{e()}catch(f){}}while(0<A.length||0<x.length);s=r;w=!0}}},d._tickFinalize=function(a){if(w)throw Error("Not in a virtual tick");A.push(a)},d}(),B=function(){},ca;e(pa).from(Error);g["delete"]=function(a){var b=new g(a);a=b["delete"]();return a.onblocked=
+function(a){b.on("blocked",a);return this},a};g.getDatabaseNames=function(a){return(new r(function(a,b){var c=Fa(),d;c?(d=c(),d.onsuccess=function(b){a([].slice.call(b.target.result,0))},d.onerror=G(b)):za(function(b){return a(b),!1})})).then(a)};g.defineClass=function(a){function b(a){a&&d(this,a)}return Aa(b.prototype,a),b};g.ignoreTransaction=function(a){return r.newPSD(function(){return r.PSD.trans=null,a()})};g.spawn=function(){return a.console&&console.warn("Dexie.spawn() is deprecated. Use Dexie.ignoreTransaction() instead."),
+g.ignoreTransaction.apply(this,arguments)};g.vip=function(a){return r.newPSD(function(){return r.PSD.letThrough=!0,a()})};Object.defineProperty(g,"currentTransaction",{get:function(){return r.PSD&&r.PSD.trans||null}});g.Promise=r;g.derive=e;g.extend=d;g.override=f;g.events=K;g.getByKeyPath=P;g.setByKeyPath=J;g.delByKeyPath=Ba;g.shallowClone=qa;g.deepClone=ba;g.addons=[];g.fakeAutoComplete=B;g.asap=x;g.ModifyError=pa;g.MultiModifyError=pa;g.IndexSpec=Z;g.TableSchema=ya;ca=a.idbModules&&a.idbModules.shimIndexedDB?
+a.idbModules:{};g.dependencies={indexedDB:ca.shimIndexedDB||a.indexedDB||a.mozIndexedDB||a.webkitIndexedDB||a.msIndexedDB,IDBKeyRange:ca.IDBKeyRange||a.IDBKeyRange||a.webkitIDBKeyRange,IDBTransaction:ca.IDBTransaction||a.IDBTransaction||a.webkitIDBTransaction,Error:a.Error||String,SyntaxError:a.SyntaxError||String,TypeError:a.TypeError||String,DOMError:a.DOMError||String,localStorage:null!=("undefined"!=typeof chrome&&null!==chrome?chrome.storage:void 0)?null:a.localStorage};g.version=1.1;b("Dexie",
+g);A(function(){B=A})}).apply(null,"function"==typeof define&&define.amd?[self||window,function(a,b){define(a,function(){return b})}]:"undefined"!=typeof global&&"undefined"!=typeof module&&module.exports?[global,function(a,b){module.exports=b}]:[self||window,function(a,b){(self||window)[a]=b}]);