Merge branch 'master' into sigverify
Conflicts:
js/tools/build/ndn-js.js
diff --git a/js/Key.js b/js/Key.js
index d60585e..ecce73d 100644
--- a/js/Key.js
+++ b/js/Key.js
@@ -20,23 +20,25 @@
* KeyLocator
*/
var KeyLocatorType = {
- NAME:1,
- KEY:2,
- CERTIFICATE:3
+ KEY:1,
+ CERTIFICATE:2,
+ KEYNAME:3
};
var KeyLocator = function KeyLocator(_input,_type){
- this.type=_type;
+ this.type = _type;
- if (_type==KeyLocatorType.NAME){
+ if (_type == KeyLocatorType.KEYNAME){
+ if (LOG>3) console.log('KeyLocator: SET KEYNAME');
this.keyName = _input;
}
- else if(_type==KeyLocatorType.KEY){
- if(LOG>4)console.log('SET KEY');
+ else if (_type == KeyLocatorType.KEY){
+ if (LOG>3) console.log('KeyLocator: SET KEY');
this.publicKey = _input;
}
- else if(_type==KeyLocatorType.CERTIFICATE){
+ else if (_type == KeyLocatorType.CERTIFICATE){
+ if (LOG>3) console.log('KeyLocator: SET CERTIFICATE');
this.certificate = _input;
}
@@ -44,95 +46,94 @@
KeyLocator.prototype.from_ccnb = function(decoder) {
- decoder.readStartElement(this.getElementLabel());
+ decoder.readStartElement(this.getElementLabel());
- if (decoder.peekStartElement(CCNProtocolDTags.Key)) {
- try {
- encodedKey = decoder.readBinaryElement(CCNProtocolDTags.Key);
- // This is a DER-encoded SubjectPublicKeyInfo.
-
- //TODO FIX THIS, This should create a Key Object instead of keeping bytes
+ if (decoder.peekStartElement(CCNProtocolDTags.Key)) {
+ try {
+ encodedKey = decoder.readBinaryElement(CCNProtocolDTags.Key);
+ // This is a DER-encoded SubjectPublicKeyInfo.
+
+ //TODO FIX THIS, This should create a Key Object instead of keeping bytes
- this.publicKey = encodedKey;//CryptoUtil.getPublicKey(encodedKey);
- this.type = 2;
-
+ this.publicKey = encodedKey;//CryptoUtil.getPublicKey(encodedKey);
+ this.type = KeyLocatorType.KEY;
+
- if(LOG>4) console.log('PUBLIC KEY FOUND: '+ this.publicKey);
- //this.publicKey = encodedKey;
-
-
- } catch (e) {
- throw new Error("Cannot parse key: ", e);
- }
+ if(LOG>4) console.log('PUBLIC KEY FOUND: '+ this.publicKey);
+ //this.publicKey = encodedKey;
+
+
+ } catch (e) {
+ throw new Error("Cannot parse key: ", e);
+ }
- if (null == this.publicKey) {
- throw new Error("Cannot parse key: ");
- }
-
- } else if ( decoder.peekStartElement(CCNProtocolDTags.Certificate)) {
- try {
- encodedCert = decoder.readBinaryElement(CCNProtocolDTags.Certificate);
-
- /*
- * Certificates not yet working
- */
-
- //CertificateFactory factory = CertificateFactory.getInstance("X.509");
- //this.certificate = (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(encodedCert));
-
-
- this.certificate = encodedCert;
- this.type = 3;
-
- 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 = 1;
-
-
- this.keyName = new KeyName();
- this.keyName.from_ccnb(decoder);
+ if (null == this.publicKey) {
+ throw new Error("Cannot parse key: ");
}
- decoder.readEndElement();
+
+ } else if ( decoder.peekStartElement(CCNProtocolDTags.Certificate)) {
+ try {
+ encodedCert = decoder.readBinaryElement(CCNProtocolDTags.Certificate);
+
+ /*
+ * Certificates not yet working
+ */
+
+ //CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ //this.certificate = (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(encodedCert));
+
+
+ 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_ccnb(decoder);
}
+ decoder.readEndElement();
+};
- KeyLocator.prototype.to_ccnb = 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.");
- }
+KeyLocator.prototype.to_ccnb = 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(CCNProtocolDTags.Key, this.publicKey);
- //TODO FIX THIS TOO
- encoder.writeStartElement(this.getElementLabel());
+ } else if (this.type == KeyLocatorType.CERTIFICATE) {
- if (this.type == KeyLocatorType.KEY) {
- if(LOG>5)console.log('About to encode a public key' +this.publicKey);
- encoder.writeElement(CCNProtocolDTags.Key, this.publicKey);
-
- } else if (this.type == KeyLocatorType.CERTIFICATE) {
-
- try {
- encoder.writeElement(CCNProtocolDTags.Certificate, this.certificate);
- } catch ( e) {
- throw new Error("CertificateEncodingException attempting to write key locator: " + e);
- }
-
- } else if (this.type == KeyLocatorType.NAME) {
-
- this.keyName.to_ccnb(encoder);
+ try {
+ encoder.writeElement(CCNProtocolDTags.Certificate, this.certificate);
+ } catch ( e) {
+ throw new Error("CertificateEncodingException attempting to write key locator: " + e);
}
- encoder.writeEndElement();
+ } else if (this.type == KeyLocatorType.KEYNAME) {
+
+ this.keyName.to_ccnb(encoder);
+ }
+ encoder.writeEndElement();
+
};
KeyLocator.prototype.getElementLabel = function() {
@@ -147,10 +148,8 @@
* KeyName is only used by KeyLocator.
*/
var KeyName = function KeyName() {
-
-
- this.contentName = this.contentName;//contentName
- this.publisherID =this.publisherID;//publisherID
+ this.contentName = this.contentName; //contentName
+ this.publisherID = this.publisherID; //publisherID
};
@@ -193,3 +192,21 @@
// null signedInfo ok
return (null != this.contentName);
};
+
+KeyName.prototype.matches_name = function(/*Name*/ name) {
+ var i_name = this.contentName.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;
+}
+
diff --git a/js/NDN.js b/js/NDN.js
index 2b36d5b..1c35221 100644
--- a/js/NDN.js
+++ b/js/NDN.js
@@ -44,6 +44,29 @@
this.port=port;
}
+
+NDN.KeyStore = new Array();
+
+var KeyStoreEntry = function KeyStoreEntry(name, key, rsa) {
+ this.keyName = name; // KeyName
+ this.keyHex = key; // Raw key hex string
+ this.rsaKey = rsa; // RSA key
+};
+
+NDN.getKeyByName = function(/* KeyName */ name) {
+ var result = null;
+
+ for (var i = 0; i < NDN.KeyStore.length; i++) {
+ if (NDN.KeyStore[i].keyName.matches_name(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();
diff --git a/js/WebSocketTransport.js b/js/WebSocketTransport.js
index dcec482..b9900a8 100644
--- a/js/WebSocketTransport.js
+++ b/js/WebSocketTransport.js
@@ -108,8 +108,8 @@
var co = new ContentObject();
co.from_ccnb(decoder);
if (LOG > 3) console.log(co);
- nameStr = co.name.getName();
- if (LOG > 3) console.log(nameStr);
+ var nameStr = co.name.getName();
+ console.log(nameStr);
if (self.ccndid == null && nameStr.match(NDN.ccndIdFetcher) != null) {
// We are in starting phase, record publisherPublicKeyDigest in self.ccndid
@@ -135,19 +135,132 @@
var pitEntry = NDN.getEntryForExpressedInterest(co.name);
if (pitEntry != null) {
//console.log(pitEntry);
-
- // Cancel interest timer
- clearTimeout(pitEntry.closure.timerID);
- //console.log("Clear interest timer");
- //console.log(pitEntry.closure.timerID);
-
// Remove PIT entry from NDN.PITTable
var index = NDN.PITTable.indexOf(pitEntry);
if (index >= 0)
- NDN.PITTable.splice(index, 1);
+ NDN.PITTable.splice(index, 1);
- // Raise callback
- pitEntry.closure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
+ var currentClosure = pitEntry.closure;
+
+ // Cancel interest timer
+ clearTimeout(currentClosure.timerID);
+ //console.log("Clear interest timer");
+ //console.log(currentClosure.timerID);
+
+ // Key verification
+ var verified = false;
+
+ // Recursive key fetching & verification closure
+ var KeyFetchClosure = function KeyFetchClosure(content, closure, key, signature) {
+ 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.signature = signature; // hex signature string to be verified
+
+ Closure.call(this);
+ };
+
+ KeyFetchClosure.prototype.upcall = function(kind, upcallInfo) {
+ if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
+ console.log("In KeyFetchClosure.upcall: interest time out.");
+ } else if (kind == Closure.UPCALL_CONTENT) {
+ console.log("In KeyFetchClosure.upcall: signature verification passed");
+ var keyHex = DataUtils.toHex(upcallInfo.contentObject.content).toLowerCase();
+ //console.log("Key: " + keyHex);
+
+ var kp = keyHex.slice(56, 314);
+ var exp = keyHex.slice(318, 324);
+
+ var rsakey = new RSAKey();
+ rsakey.setPublic(kp, exp);
+ var verified = rsakey.verifyByteArray(this.contentObject.rawSignatureData, this.signature);
+ var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+
+ //console.log("raise encapsulated closure");
+ this.closure.upcall(flag, new UpcallInfo(ndn, null, 0, this.contentObject));
+ }
+ };
+
+ if (co.signedInfo && co.signedInfo.locator && co.signature) {
+ if (LOG > 3) console.log("Key verification...");
+ var sigHex = DataUtils.toHex(co.signature.signature).toLowerCase();
+
+ var keylocator = co.signedInfo.locator;
+ if (keylocator.type == KeyLocatorType.KEYNAME) {
+ console.log("KeyLocator contains KEYNAME");
+ var keyname = keylocator.keyName.contentName.getName();
+ console.log(keyname);
+
+ if (nameStr.match(keyname)) {
+ console.log("Content is key itself");
+
+ var keyHex = DataUtils.toHex(co.content).toLowerCase();
+ console.log("Key content: " + keyHex);
+
+ var kp = keyHex.slice(56, 314);
+ var exp = keyHex.slice(318, 324);
+
+ var rsakey = new RSAKey();
+ rsakey.setPublic(kp, exp);
+ var verified = rsakey.verifyByteArray(co.rawSignatureData, sigHex);
+ var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+
+ currentClosure.upcall(flag, new UpcallInfo(ndn, null, 0, co));
+
+ // Store key in cache
+ var keyEntry = new KeyStoreEntry(keylocator.keyName, keyHex, rsakey);
+ NDN.KeyStore.push(keyEntry);
+ } else {
+ console.log("Fetch key according to keylocator");
+
+ // Check local key store
+ var keyEntry = NDN.getKeyByName(keylocator.keyName);
+ if (keyEntry) {
+ // Key found, verify now
+ console.log("Local key cache hit");
+ var rsakey = keyEntry.rsaKey;
+ verified = rsakey.verifyByteArray(co.rawSignatureData, sigHex);
+
+ var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+
+ // Raise callback
+ currentClosure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
+ } else {
+ // Not found, fetch now
+ var nextClosure = new KeyFetchClosure(co, currentClosure, keyname, sigHex);
+ var interest = new Interest(keylocator.keyName.contentName.getPrefix(4));
+ interest.interestLifetime = 4.0;
+ self.expressInterest(ndn, interest, nextClosure);
+ }
+ }
+ } else if (keylocator.type == KeyLocatorType.KEY) {
+ console.log("Keylocator contains KEY");
+ var publickeyHex = DataUtils.toHex(keylocator.publicKey).toLowerCase();
+ console.log(publickeyHex);
+
+ var kp = publickeyHex.slice(56, 314);
+ var exp = publickeyHex.slice(318, 324);
+
+ var rsakey = new RSAKey();
+ rsakey.setPublic(kp, exp);
+ verified = rsakey.verifyByteArray(co.rawSignatureData, sigHex);
+
+ var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+
+ // Raise callback
+ currentClosure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
+
+ // Store key in cache
+ var keyEntry = new KeyStoreEntry(keylocator.keyName, publickeyHex, rsakey);
+ NDN.KeyStore.push(keyEntry);
+ } else {
+ var cert = keylocator.certificate;
+ console.log("KeyLocator contains CERT");
+ console.log(cert);
+
+ // TODO: verify certificate
+ }
+ }
}
}
} else {
diff --git a/js/testing/.DS_Store b/js/testing/.DS_Store
index e1477e1..a3d2768 100644
--- a/js/testing/.DS_Store
+++ b/js/testing/.DS_Store
Binary files differ
diff --git a/js/testing/test-get-async.html b/js/testing/test-get-async.html
index 015eeb1..e019537 100644
--- a/js/testing/test-get-async.html
+++ b/js/testing/test-get-async.html
@@ -16,6 +16,10 @@
var ndn = new NDN({port:9696});
ndn.transport.connectWebSocket(ndn);
+ ndn.onopen = function() {
+ document.getElementById("testBtn").disabled = false;
+ };
+
var AsyncGetClosure = function AsyncGetClosure() {
// Inherit from Closure.
Closure.call(this);
@@ -26,6 +30,7 @@
if (kind == Closure.UPCALL_FINAL) {
// Do nothing.
} else if (kind == Closure.UPCALL_CONTENT) {
+ console.log("Closure.upcall: content signature verification pass.");
var content = upcallInfo.contentObject;
//console.log(content.name);
nameStr = escape(content.name.getName());
@@ -35,6 +40,8 @@
//console.log("In callback, content: ");
//console.log(content);
document.getElementById('content').innerHTML += contentObjectToHtml(content);
+ } else if (kind == Closure.UPCALL_CONTENT_BAD) {
+ console.log("Closure.upcall: content signature verification fail.");
} else if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
console.log("Closure.upcall called with interest time out.");
}
@@ -56,7 +63,7 @@
<input id="interest" type="text" name="INTEREST" size="50" value="/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY" />
</form>
- <button onclick="run()">Fetch Content</button>
+ <button id="testBtn" onclick="run()" disabled="disabled">Fetch Content</button>
<p id="content">Content: <br/></p>
diff --git a/js/testing/test-publish-async.html b/js/testing/test-publish-async.html
index cb8a7c1..f69756a 100644
--- a/js/testing/test-publish-async.html
+++ b/js/testing/test-publish-async.html
@@ -17,6 +17,10 @@
var ndn = new NDN();
ndn.transport.connectWebSocket(ndn);
+ ndn.onopen = function() {
+ document.getElementById("testBtn").disabled = false;
+ };
+
var AsyncPutClosure = function AsyncPutClosure() {
// Inherit from Closure.
Closure.call(this);
@@ -29,11 +33,11 @@
console.log('AsyncPutClosure.upcall() called.');
var content = document.getElementById('content').value;
var interest = upcallInfo.interest;
- var nameStr = escape(interest.name.getName());
+ var nameStr = interest.name.getName();
var si = new SignedInfo();
- var co = new ContentObject(new Name(nameStr), si, content, new Signature());
+ var co = new ContentObject(new Name(nameStr), si, content, new Signature());
co.sign();
upcallInfo.contentObject = co;
@@ -70,7 +74,7 @@
</div>
</form>
<div>
- <button onclick="run()">Publish Content</button>
+ <button id="testBtn" onclick="run()" disabled="disabled">Publish Content</button>
</div>
<p id="result"></p>
diff --git a/js/testing/test-throughput-http.html b/js/testing/test-throughput-http.html
new file mode 100644
index 0000000..3720be7
--- /dev/null
+++ b/js/testing/test-throughput-http.html
@@ -0,0 +1,45 @@
+<?xml version = "1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"DTD/xhtml1-strict.dtd">
+<html xmlns = "http://www.w3.org/1999/xhtml">
+<meta charset="UTF-8">
+
+<head>
+ <title>HTTP Get via XHR</title>
+
+ <script type="text/javascript" src="../tools/build/ndn-js.js"></script>
+
+ <script type="text/javascript">
+ function run() {
+ var xhr = new XMLHttpRequest();
+ var url = document.getElementById('interest').value;
+ xhr.open("GET", url, true);
+ //xhr.responseType = "arraybuffer";
+ var T0 = new Date();
+ xhr.onload = function(e) {
+ //var arraybuffer = xhr.response; // not responseText
+ /* ... */
+ //document.getElementById('content').innerHTML += xhr.responseText;
+ var T1 = new Date();
+ document.getElementById('content').innerHTML += "<p>Time elapsed: " + (T1 - T0) + " ms</p>";
+ document.getElementById('content').innerHTML += "<p>Buffer size: " + xhr.responseText.length + "</p>";
+ };
+ xhr.send();
+ }
+
+ </script>
+
+</head>
+<body >
+
+ <form>
+ Please Enter an Interest:<br />
+ <input id="interest" type="text" name="INTEREST" size="50" value="marsgale_curiosity_1452.jpg" />
+ </form>
+
+ <button onclick="run()">Fetch Content</button>
+
+ <p id="content">Result: <br/></p>
+
+</body>
+</html>
diff --git a/js/testing/test-throughput-ws.html b/js/testing/test-throughput-ws.html
new file mode 100644
index 0000000..6544798
--- /dev/null
+++ b/js/testing/test-throughput-ws.html
@@ -0,0 +1,215 @@
+<?xml version = "1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"DTD/xhtml1-strict.dtd">
+<html xmlns = "http://www.w3.org/1999/xhtml">
+<meta charset="UTF-8">
+
+<head>
+ <title>NDN Get File via WebSocket</title>
+
+ <script type="text/javascript" src="../tools/build/ndn-js.js"></script>
+
+ <script type="text/javascript">
+ hostip = "131.179.196.232";
+ //hostip = "localhost";
+ var ndncon = new NDN({port:9696,host:hostip});
+ ndncon.transport.connectWebSocket(ndncon);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /*
+ * Closure for calling expressInterest to fetch big file.
+ */
+ var ContentClosure = function ContentClosure(ndn, T0) {
+ // Inherit from Closure.
+ Closure.call(this);
+
+ this.T0 = T0;
+ this.ndn = ndn;
+ this.totalBlocks = 0;
+
+ this.firstReceivedSegmentNumber = null;
+ this.firstReceivedContentObject = null;
+ }
+
+ ContentClosure.prototype.upcall = function(kind, upcallInfo) {
+ if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
+ var T1 = new Date();
+ document.getElementById('content').innerHTML += "<p>Interest time out.</p>";
+ document.getElementById('content').innerHTML += "<p>Time elapsed: " + (T1 - this.T0) + " ms including 4s timeout</p>";
+ document.getElementById('content').innerHTML += "<p>Total number of blocks: " + this.totalBlocks + "</p>";
+ return Closure.RESULT_OK;
+ }
+
+ if (!(kind == Closure.UPCALL_CONTENT ||
+ kind == Closure.UPCALL_CONTENT_UNVERIFIED))
+ // The upcall is not for us.
+ return Closure.RESULT_ERR;
+
+ var contentObject = upcallInfo.contentObject;
+ if (contentObject.content == null) {
+ console.log("NdnProtocol.ContentClosure: contentObject.content is null\n");
+ return Closure.RESULT_ERR;
+ }
+
+ // Use the segmentNumber to load multiple segments.
+ //var segmentNumber = ArrayToNum(contentObject.name.components[contentObject.name.components.length - 1]);
+ var segmentNumber = DataUtils.bigEndianToUnsignedInt
+ (contentObject.name.components[contentObject.name.components.length - 1]);
+ //console.log(segmentNumber);
+
+ if (this.firstReceivedSegmentNumber == null) {
+ // This is the first call.
+ this.firstReceivedSegmentNumber = segmentNumber;
+ if (segmentNumber != 0) {
+ // Special case: Save this content object for later and request segment zero.
+ this.firstReceivedContentObject = contentObject;
+ var componentsForZero = contentObject.name.components.slice
+ (0, contentObject.name.components.length - 1);
+ componentsForZero.push([0]);
+ this.ndn.expressInterest(new Name(componentsForZero), this);
+ return Closure.RESULT_OK;
+ }
+ }
+
+ // Process received data here...
+ // Count content length
+ //nameStr = escape(contentObject.name.getName());
+ //document.getElementById('content').innerHTML += "<p>Name string: " + nameStr + "</p>";
+ //document.getElementById('content').innerHTML += "<p>Content buffer length: " + contentObject.content.length + "</p>";
+ this.totalBlocks++;
+
+ // Check for the special case if the saved content is for the next segment that we need.
+ if (this.firstReceivedContentObject != null &&
+ this.firstReceivedSegmentNumber == segmentNumber + 1) {
+ // Substitute the saved contentObject send its content and keep going.
+ contentObject = this.firstReceivedContentObject;
+ segmentNumber = segmentNumber + 1;
+ // Clear firstReceivedContentObject to save memory.
+ this.firstReceivedContentObject = null;
+
+ // Process received data here...
+ // Count content length
+ //nameStr = escape(contentObject.name.getName());
+ //document.getElementById('content').innerHTML += "<p>Name string: " + nameStr + "</p>";
+ //document.getElementById('content').innerHTML += "<p>Content buffer length: " + contentObject.content.length + "</p>";
+ this.totalBlocks++;
+ }
+
+ var finalSegmentNumber = null;
+ if (contentObject.signedInfo != null && contentObject.signedInfo.finalBlockID != null)
+ //finalSegmentNumber = ArrayToNum(contentObject.signedInfo.finalBlockID);
+ finalSegmentNumber = DataUtils.bigEndianToUnsignedInt(contentObject.signedInfo.finalBlockID);
+
+ if (finalSegmentNumber == null || segmentNumber != finalSegmentNumber) {
+ // Make a name for the next segment and get it.
+ //var segmentNumberPlus1 = NumToArray(segmentNumber + 1);
+ // Make a name for the next segment and get it.
+ var segmentNumberPlus1 = DataUtils.nonNegativeIntToBigEndian(segmentNumber + 1);
+ // Put a 0 byte in front.
+ var nextSegmentNumber = new Uint8Array(segmentNumberPlus1.length + 1);
+ nextSegmentNumber.set(segmentNumberPlus1, 1);
+
+ var components = contentObject.name.components.slice
+ (0, contentObject.name.components.length - 1);
+ components.push(nextSegmentNumber);
+ //components.push(segmentNumberPlus1);
+ this.ndn.expressInterest(new Name(components), this);
+ }
+ else {
+ // Final block received.
+ // Record stop time
+ var T1 = new Date();
+ document.getElementById('content').innerHTML += "<p>Final block received.</p>";
+ document.getElementById('content').innerHTML += "<p>Time elapsed: " + (T1 - this.T0) + " ms</p>";
+ document.getElementById('content').innerHTML += "<p>Total number of blocks: " + this.totalBlocks + "</p>";
+ }
+
+ return Closure.RESULT_OK;
+ };
+
+ /*
+ * Convert the big endian Uint8Array to an unsigned int.
+ * Don't check for overflow.
+ */
+ function ArrayToNum(bytes) {
+ var result = 0;
+ for (var i = 0; i < bytes.length; ++i) {
+ result = result * 10;
+ result += (bytes[i] - 48);
+ }
+ return result;
+ };
+
+ /*
+ * Convert the int value to a new big endian Uint8Array and return.
+ * If value is 0 or negative, return Uint8Array(0).
+ */
+ function NumToArray(value) {
+ value = Math.round(value);
+ if (value <= 0)
+ return new Uint8Array(0);
+
+ numString = value.toString();
+ var size = numString.length;
+ var result = new Uint8Array(size);
+ for (i = 0; i < size; i++) {
+ result[i] = numString.charCodeAt(i);
+ }
+ return result;
+ };
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /*
+ var AsyncGetClosure = function AsyncGetClosure(T0) {
+ this.T0 = T0; // Start time
+ // Inherit from Closure.
+ Closure.call(this);
+ };
+
+ AsyncGetClosure.prototype.upcall = function(kind, upcallInfo) {
+ //console.log("Closure.upcall() executed.");
+ if (kind == Closure.UPCALL_FINAL) {
+ // Do nothing.
+ } else if (kind == Closure.UPCALL_CONTENT) {
+ var T1 = new Date();
+
+ var content = upcallInfo.contentObject;
+ nameStr = escape(content.name.getName());
+ document.getElementById('content').innerHTML += "<p>Time elapsed: " + (T1 - this.T0) + " ms</p>";
+ document.getElementById('content').innerHTML += "<p>Name string: " + nameStr + "</p>";
+ document.getElementById('content').innerHTML += "<p>Content buffer length: " + content.content.length + "</p>";
+ //console.log("In callback, nameStr: " + nameStr);
+ //console.log("In callback, content: ");
+ //console.log(content.content.length);
+ //document.getElementById('content').innerHTML += contentObjectToHtml(content);
+ } else if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
+ console.log("Closure.upcall called with interest time out.");
+ }
+ return Closure.RESULT_OK;
+ };
+ */
+
+ function run() {
+ document.getElementById('content').innerHTML += "<p>Started...</p>";
+ //ndn.expressInterest(new Name(document.getElementById('interest').value), new AsyncGetClosure( new Date() ));
+ var name = new Name(document.getElementById('interest').value);
+ ndncon.expressInterest( name,
+ new ContentClosure( ndncon, new Date() ));
+ }
+
+ </script>
+
+</head>
+<body >
+
+ <form>
+ Please Enter an Interest:<br />
+ <input id="interest" type="text" name="INTEREST" size="50" value="/wentao.shang/choir_jail.png" />
+ </form>
+
+ <button onclick="run()">Fetch Content</button>
+
+ <p id="content">Result: <br/></p>
+
+</body>
+</html>
diff --git a/js/testing/test-throughput-xpcom.html b/js/testing/test-throughput-xpcom.html
new file mode 100644
index 0000000..a8b3f46
--- /dev/null
+++ b/js/testing/test-throughput-xpcom.html
@@ -0,0 +1,27 @@
+<?xml version = "1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"DTD/xhtml1-strict.dtd">
+<html xmlns = "http://www.w3.org/1999/xhtml">
+<meta charset="UTF-8">
+
+<head>
+ <title>NDN Get via XPCOM</title>
+</head>
+<body >
+
+ <h1>NDN Protocol Examples</h1>
+ <h2>Segmented files</h2>
+ Here is a small text file:
+ <br/>
+ <a target="_blank" class="moz-txt-link-freetext"
+ href="ndn:/wentao.shang/mars.jpg">ndn:/wentao.shang/mars.jpg</a>
+ <br/>
+
+ Here is a large image file:
+ <br/>
+ <a target="_blank" class="moz-txt-link-freetext"
+ href="ndn:/wentao.shang/choir_jail.png">ndn:/wentao.shang/choir_jail.png</a>
+ <br/>
+
+</body>
+</html>
diff --git a/js/tools/build/.gitignore b/js/tools/build/.gitignore
new file mode 100644
index 0000000..6faf4d3
--- /dev/null
+++ b/js/tools/build/.gitignore
@@ -0,0 +1,2 @@
+ndn-js-uncomp.js
+ndn-js.js
diff --git a/js/tools/build/ndn-js-uncomp.js b/js/tools/build/ndn-js-uncomp.js
index b5064ef..2f6f4f8 100644
--- a/js/tools/build/ndn-js-uncomp.js
+++ b/js/tools/build/ndn-js-uncomp.js
@@ -110,6 +110,29 @@
this.port=port;
}
+
+NDN.KeyStore = new Array();
+
+var KeyStoreEntry = function KeyStoreEntry(name, key, rsa) {
+ this.keyName = name; // KeyName
+ this.keyHex = key; // Raw key hex string
+ this.rsaKey = rsa; // RSA key
+};
+
+NDN.getKeyByName = function(/* KeyName */ name) {
+ var result = null;
+
+ for (var i = 0; i < NDN.KeyStore.length; i++) {
+ if (NDN.KeyStore[i].keyName.matches_name(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();
@@ -364,8 +387,8 @@
var co = new ContentObject();
co.from_ccnb(decoder);
if (LOG > 3) console.log(co);
- nameStr = co.name.getName();
- if (LOG > 3) console.log(nameStr);
+ var nameStr = co.name.getName();
+ console.log(nameStr);
if (self.ccndid == null && nameStr.match(NDN.ccndIdFetcher) != null) {
// We are in starting phase, record publisherPublicKeyDigest in self.ccndid
@@ -391,19 +414,132 @@
var pitEntry = NDN.getEntryForExpressedInterest(co.name);
if (pitEntry != null) {
//console.log(pitEntry);
-
- // Cancel interest timer
- clearTimeout(pitEntry.closure.timerID);
- //console.log("Clear interest timer");
- //console.log(pitEntry.closure.timerID);
-
// Remove PIT entry from NDN.PITTable
var index = NDN.PITTable.indexOf(pitEntry);
if (index >= 0)
- NDN.PITTable.splice(index, 1);
+ NDN.PITTable.splice(index, 1);
- // Raise callback
- pitEntry.closure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
+ var currentClosure = pitEntry.closure;
+
+ // Cancel interest timer
+ clearTimeout(currentClosure.timerID);
+ //console.log("Clear interest timer");
+ //console.log(currentClosure.timerID);
+
+ // Key verification
+ var verified = false;
+
+ // Recursive key fetching & verification closure
+ var KeyFetchClosure = function KeyFetchClosure(content, closure, key, signature) {
+ 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.signature = signature; // hex signature string to be verified
+
+ Closure.call(this);
+ };
+
+ KeyFetchClosure.prototype.upcall = function(kind, upcallInfo) {
+ if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
+ console.log("In KeyFetchClosure.upcall: interest time out.");
+ } else if (kind == Closure.UPCALL_CONTENT) {
+ console.log("In KeyFetchClosure.upcall: signature verification passed");
+ var keyHex = DataUtils.toHex(upcallInfo.contentObject.content).toLowerCase();
+ //console.log("Key: " + keyHex);
+
+ var kp = keyHex.slice(56, 314);
+ var exp = keyHex.slice(318, 324);
+
+ var rsakey = new RSAKey();
+ rsakey.setPublic(kp, exp);
+ var verified = rsakey.verifyByteArray(this.contentObject.rawSignatureData, this.signature);
+ var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+
+ //console.log("raise encapsulated closure");
+ this.closure.upcall(flag, new UpcallInfo(ndn, null, 0, this.contentObject));
+ }
+ };
+
+ if (co.signedInfo && co.signedInfo.locator && co.signature) {
+ if (LOG > 3) console.log("Key verification...");
+ var sigHex = DataUtils.toHex(co.signature.signature).toLowerCase();
+
+ var keylocator = co.signedInfo.locator;
+ if (keylocator.type == KeyLocatorType.KEYNAME) {
+ console.log("KeyLocator contains KEYNAME");
+ var keyname = keylocator.keyName.contentName.getName();
+ console.log(keyname);
+
+ if (nameStr.match(keyname)) {
+ console.log("Content is key itself");
+
+ var keyHex = DataUtils.toHex(co.content).toLowerCase();
+ console.log("Key content: " + keyHex);
+
+ var kp = keyHex.slice(56, 314);
+ var exp = keyHex.slice(318, 324);
+
+ var rsakey = new RSAKey();
+ rsakey.setPublic(kp, exp);
+ var verified = rsakey.verifyByteArray(co.rawSignatureData, sigHex);
+ var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+
+ currentClosure.upcall(flag, new UpcallInfo(ndn, null, 0, co));
+
+ // Store key in cache
+ var keyEntry = new KeyStoreEntry(keylocator.keyName, keyHex, rsakey);
+ NDN.KeyStore.push(keyEntry);
+ } else {
+ console.log("Fetch key according to keylocator");
+
+ // Check local key store
+ var keyEntry = NDN.getKeyByName(keylocator.keyName);
+ if (keyEntry) {
+ // Key found, verify now
+ console.log("Local key cache hit");
+ var rsakey = keyEntry.rsaKey;
+ verified = rsakey.verifyByteArray(co.rawSignatureData, sigHex);
+
+ var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+
+ // Raise callback
+ currentClosure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
+ } else {
+ // Not found, fetch now
+ var nextClosure = new KeyFetchClosure(co, currentClosure, keyname, sigHex);
+ var interest = new Interest(keylocator.keyName.contentName.getPrefix(4));
+ interest.interestLifetime = 4.0;
+ self.expressInterest(ndn, interest, nextClosure);
+ }
+ }
+ } else if (keylocator.type == KeyLocatorType.KEY) {
+ console.log("Keylocator contains KEY");
+ var publickeyHex = DataUtils.toHex(keylocator.publicKey).toLowerCase();
+ console.log(publickeyHex);
+
+ var kp = publickeyHex.slice(56, 314);
+ var exp = publickeyHex.slice(318, 324);
+
+ var rsakey = new RSAKey();
+ rsakey.setPublic(kp, exp);
+ verified = rsakey.verifyByteArray(co.rawSignatureData, sigHex);
+
+ var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
+
+ // Raise callback
+ currentClosure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
+
+ // Store key in cache
+ var keyEntry = new KeyStoreEntry(keylocator.keyName, publickeyHex, rsakey);
+ NDN.KeyStore.push(keyEntry);
+ } else {
+ var cert = keylocator.certificate;
+ console.log("KeyLocator contains CERT");
+ console.log(cert);
+
+ // TODO: verify certificate
+ }
+ }
}
}
} else {
@@ -1919,23 +2055,25 @@
* KeyLocator
*/
var KeyLocatorType = {
- NAME:1,
- KEY:2,
- CERTIFICATE:3
+ KEY:1,
+ CERTIFICATE:2,
+ KEYNAME:3
};
var KeyLocator = function KeyLocator(_input,_type){
- this.type=_type;
+ this.type = _type;
- if (_type==KeyLocatorType.NAME){
+ if (_type == KeyLocatorType.KEYNAME){
+ if (LOG>3) console.log('KeyLocator: SET KEYNAME');
this.keyName = _input;
}
- else if(_type==KeyLocatorType.KEY){
- if(LOG>4)console.log('SET KEY');
+ else if (_type == KeyLocatorType.KEY){
+ if (LOG>3) console.log('KeyLocator: SET KEY');
this.publicKey = _input;
}
- else if(_type==KeyLocatorType.CERTIFICATE){
+ else if (_type == KeyLocatorType.CERTIFICATE){
+ if (LOG>3) console.log('KeyLocator: SET CERTIFICATE');
this.certificate = _input;
}
@@ -1943,95 +2081,94 @@
KeyLocator.prototype.from_ccnb = function(decoder) {
- decoder.readStartElement(this.getElementLabel());
+ decoder.readStartElement(this.getElementLabel());
- if (decoder.peekStartElement(CCNProtocolDTags.Key)) {
- try {
- encodedKey = decoder.readBinaryElement(CCNProtocolDTags.Key);
- // This is a DER-encoded SubjectPublicKeyInfo.
-
- //TODO FIX THIS, This should create a Key Object instead of keeping bytes
+ if (decoder.peekStartElement(CCNProtocolDTags.Key)) {
+ try {
+ encodedKey = decoder.readBinaryElement(CCNProtocolDTags.Key);
+ // This is a DER-encoded SubjectPublicKeyInfo.
+
+ //TODO FIX THIS, This should create a Key Object instead of keeping bytes
- this.publicKey = encodedKey;//CryptoUtil.getPublicKey(encodedKey);
- this.type = 2;
-
+ this.publicKey = encodedKey;//CryptoUtil.getPublicKey(encodedKey);
+ this.type = KeyLocatorType.KEY;
+
- if(LOG>4) console.log('PUBLIC KEY FOUND: '+ this.publicKey);
- //this.publicKey = encodedKey;
-
-
- } catch (e) {
- throw new Error("Cannot parse key: ", e);
- }
+ if(LOG>4) console.log('PUBLIC KEY FOUND: '+ this.publicKey);
+ //this.publicKey = encodedKey;
+
+
+ } catch (e) {
+ throw new Error("Cannot parse key: ", e);
+ }
- if (null == this.publicKey) {
- throw new Error("Cannot parse key: ");
- }
-
- } else if ( decoder.peekStartElement(CCNProtocolDTags.Certificate)) {
- try {
- encodedCert = decoder.readBinaryElement(CCNProtocolDTags.Certificate);
-
- /*
- * Certificates not yet working
- */
-
- //CertificateFactory factory = CertificateFactory.getInstance("X.509");
- //this.certificate = (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(encodedCert));
-
-
- this.certificate = encodedCert;
- this.type = 3;
-
- 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 = 1;
-
-
- this.keyName = new KeyName();
- this.keyName.from_ccnb(decoder);
+ if (null == this.publicKey) {
+ throw new Error("Cannot parse key: ");
}
- decoder.readEndElement();
+
+ } else if ( decoder.peekStartElement(CCNProtocolDTags.Certificate)) {
+ try {
+ encodedCert = decoder.readBinaryElement(CCNProtocolDTags.Certificate);
+
+ /*
+ * Certificates not yet working
+ */
+
+ //CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ //this.certificate = (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(encodedCert));
+
+
+ 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_ccnb(decoder);
}
+ decoder.readEndElement();
+};
- KeyLocator.prototype.to_ccnb = 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.");
- }
+KeyLocator.prototype.to_ccnb = 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(CCNProtocolDTags.Key, this.publicKey);
- //TODO FIX THIS TOO
- encoder.writeStartElement(this.getElementLabel());
+ } else if (this.type == KeyLocatorType.CERTIFICATE) {
- if (this.type == KeyLocatorType.KEY) {
- if(LOG>5)console.log('About to encode a public key' +this.publicKey);
- encoder.writeElement(CCNProtocolDTags.Key, this.publicKey);
-
- } else if (this.type == KeyLocatorType.CERTIFICATE) {
-
- try {
- encoder.writeElement(CCNProtocolDTags.Certificate, this.certificate);
- } catch ( e) {
- throw new Error("CertificateEncodingException attempting to write key locator: " + e);
- }
-
- } else if (this.type == KeyLocatorType.NAME) {
-
- this.keyName.to_ccnb(encoder);
+ try {
+ encoder.writeElement(CCNProtocolDTags.Certificate, this.certificate);
+ } catch ( e) {
+ throw new Error("CertificateEncodingException attempting to write key locator: " + e);
}
- encoder.writeEndElement();
+ } else if (this.type == KeyLocatorType.KEYNAME) {
+
+ this.keyName.to_ccnb(encoder);
+ }
+ encoder.writeEndElement();
+
};
KeyLocator.prototype.getElementLabel = function() {
@@ -2046,10 +2183,8 @@
* KeyName is only used by KeyLocator.
*/
var KeyName = function KeyName() {
-
-
- this.contentName = this.contentName;//contentName
- this.publisherID =this.publisherID;//publisherID
+ this.contentName = this.contentName; //contentName
+ this.publisherID = this.publisherID; //publisherID
};
@@ -2092,6 +2227,24 @@
// null signedInfo ok
return (null != this.contentName);
};
+
+KeyName.prototype.matches_name = function(/*Name*/ name) {
+ var i_name = this.contentName.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.
diff --git a/js/tools/build/ndn-js.js b/js/tools/build/ndn-js.js
index 578347e..0eb7b55 100644
--- a/js/tools/build/ndn-js.js
+++ b/js/tools/build/ndn-js.js
@@ -1,8 +1,8 @@
var Closure=function(){this.ndn_data=null;this.ndn_data_dirty=!1;this.timerID=-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 LOG=0,NDN=function NDN(b){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:"localhost";this.port=b.port||9696;this.readyStatus=NDN.UNOPEN;this.onopen=b.onopen||function(){3<LOG&&console.log("NDN connection established.")};this.onclose=b.onclose||function(){3<LOG&&console.log("NDN connection closed.")}};NDN.UNOPEN=0;NDN.OPENED=1;NDN.CLOSED=2;
-NDN.ccndIdFetcher="/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY";NDN.prototype.createRoute=function(a,b){this.host=a;this.port=b};NDN.PITTable=[];var PITEntry=function(a,b){this.interest=a;this.closure=b};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.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.ccndIdFetcher="/%C1.M.S.localhost/%C1.M.SRV/ccnd/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.keyHex=b;this.rsaKey=c};NDN.getKeyByName=function(a){for(var b=null,c=0;c<NDN.KeyStore.length;c++)if(NDN.KeyStore[c].keyName.matches_name(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};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.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){a=new Interest(a);null!=c?(a.minSuffixComponents=c.minSuffixComponents,a.maxSuffixComponents=c.maxSuffixComponents,a.publisherPublicKeyDigest=c.publisherPublicKeyDigest,a.exclude=c.exclude,a.childSelector=c.childSelector,a.answerOriginKind=c.answerOriginKind,a.scope=c.scope,a.interestLifetime=c.interestLifetime):a.interestLifetime=4E3;null==this.host||null==this.port?null==this.getHostAndPort?console.log("ERROR: host OR port NOT SET"):this.connectAndExpressInterest(a,
b):this.transport.expressInterest(this,a,b)};NDN.prototype.registerPrefix=function(a,b,c){return this.transport.registerPrefix(this,a,b,c)};
NDN.prototype.connectAndExpressInterest=function(a,b){var c=this.getHostAndPort();if(null==c)console.log("ERROR: No more hosts from getHostAndPort"),this.host=null;else if(c.host==this.host&&c.port==this.port)console.log("ERROR: The host returned by getHostAndPort is not alive: "+this.host+":"+this.port);else{this.host=c.host;this.port=c.port;console.log("Trying host from getHostAndPort: "+this.host);c=new Interest(new Name(NDN.ccndIdFetcher));c.interestLifetime=4E3;var d=this,e=setTimeout(function(){console.log("Timeout waiting for host "+
@@ -11,11 +11,15 @@
var WebSocketTransport=function(){this.ccndid=this.ws=null;this.maxBufferSize=1E4;this.buffer=new Uint8Array(this.maxBufferSize);this.bufferOffset=0;this.structureDecoder=new BinaryXMLStructureDecoder;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.connectWebSocket=function(a){null!=this.ws&&delete this.ws;this.ws=new WebSocket("ws://"+a.host+":"+a.port);0<LOG&&console.log("ws connection created.");this.ws.binaryType="arraybuffer";var b=this;this.ws.onmessage=function(c){c=c.data;if(null==c||void 0==c||""==c)console.log("INVALID ANSWER");else if(c instanceof ArrayBuffer){var d=new Uint8Array(c);3<LOG&&console.log("BINARY RESPONSE IS "+DataUtils.toHex(d));try{if(d.length+b.bufferOffset>=b.buffer.byteLength){3<LOG&&
console.log("NDN.ws.onmessage: buffer overflow. Accumulate received length: "+b.bufferOffset+". Current packet length: "+d.length+".");delete b.structureDecoder;delete b.buffer;b.structureDecoder=new BinaryXMLStructureDecoder;b.buffer=new Uint8Array(b.maxBufferSize);b.bufferOffset=0;return}b.buffer.set(d,b.bufferOffset);b.bufferOffset+=d.length;if(!b.structureDecoder.findElementEnd(b.buffer.subarray(0,b.bufferOffset))){3<LOG&&console.log("Incomplete packet received. Length "+d.length+". Wait for more input.");
-return}3<LOG&&console.log("Complete packet received. Length "+d.length+". Start decoding.")}catch(e){console.log("NDN.ws.onmessage exception: "+e);return}c=new BinaryXMLDecoder(b.buffer);if(c.peekStartElement(CCNProtocolDTags.Interest)){3<LOG&&console.log("Interest packet received.");d=new Interest;d.from_ccnb(c);3<LOG&&console.log(d);var f=escape(d.name.getName());3<LOG&&console.log(f);f=getEntryForRegisteredPrefix(f);null!=f&&(d=new UpcallInfo(a,d,0,null),f.closure.upcall(Closure.UPCALL_INTEREST,
-d)==Closure.RESULT_INTEREST_CONSUMED&&null!=d.contentObject&&(f=encodeToBinaryContentObject(d.contentObject),d=new Uint8Array(f.length),d.set(f),b.ws.send(d.buffer)))}else if(c.peekStartElement(CCNProtocolDTags.ContentObject))if(3<LOG&&console.log("ContentObject packet received."),d=new ContentObject,d.from_ccnb(c),3<LOG&&console.log(d),f=d.name.getName(),3<LOG&&console.log(f),null==b.ccndid&&null!=f.match(NDN.ccndIdFetcher))!d.signedInfo||!d.signedInfo.publisher||!d.signedInfo.publisher.publisherPublicKeyDigest?
-(console.log("Cannot contact router, close NDN now."),a.readyStatus=NDN.CLOSED,a.onclose()):(b.ccndid=d.signedInfo.publisher.publisherPublicKeyDigest,3<LOG&&console.log(b.ccndid),a.readyStatus=NDN.OPENED,a.onopen());else{if(f=NDN.getEntryForExpressedInterest(d.name),null!=f){clearTimeout(f.closure.timerID);var g=NDN.PITTable.indexOf(f);0<=g&&NDN.PITTable.splice(g,1);f.closure.upcall(Closure.UPCALL_CONTENT,new UpcallInfo(a,null,0,d))}}else console.log("Incoming packet is not Interest or ContentObject. Discard now.");
-delete c;delete b.structureDecoder;delete b.buffer;b.structureDecoder=new BinaryXMLStructureDecoder;b.buffer=new Uint8Array(b.maxBufferSize);b.bufferOffset=0}};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);a=new Interest(new Name(NDN.ccndIdFetcher));a.interestLifetime=4E3;var a=encodeToBinaryInterest(a),d=new Uint8Array(a.length);d.set(a);b.ws.send(d.buffer)};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.");b.ws=null;a.readyStatus=NDN.CLOSED;a.onclose()}};
+return}3<LOG&&console.log("Complete packet received. Length "+d.length+". Start decoding.")}catch(e){console.log("NDN.ws.onmessage exception: "+e);return}c=new BinaryXMLDecoder(b.buffer);if(c.peekStartElement(CCNProtocolDTags.Interest)){3<LOG&&console.log("Interest packet received.");d=new Interest;d.from_ccnb(c);3<LOG&&console.log(d);var f=escape(d.name.getName());3<LOG&&console.log(f);var g=getEntryForRegisteredPrefix(f);null!=g&&(d=new UpcallInfo(a,d,0,null),g.closure.upcall(Closure.UPCALL_INTEREST,
+d)==Closure.RESULT_INTEREST_CONSUMED&&null!=d.contentObject&&(g=encodeToBinaryContentObject(d.contentObject),d=new Uint8Array(g.length),d.set(g),b.ws.send(d.buffer)))}else if(c.peekStartElement(CCNProtocolDTags.ContentObject))if(3<LOG&&console.log("ContentObject packet received."),d=new ContentObject,d.from_ccnb(c),3<LOG&&console.log(d),f=d.name.getName(),console.log(f),null==b.ccndid&&null!=f.match(NDN.ccndIdFetcher))!d.signedInfo||!d.signedInfo.publisher||!d.signedInfo.publisher.publisherPublicKeyDigest?
+(console.log("Cannot contact router, close NDN now."),a.readyStatus=NDN.CLOSED,a.onclose()):(b.ccndid=d.signedInfo.publisher.publisherPublicKeyDigest,3<LOG&&console.log(b.ccndid),a.readyStatus=NDN.OPENED,a.onopen());else{if(g=NDN.getEntryForExpressedInterest(d.name),null!=g){var h=NDN.PITTable.indexOf(g);0<=h&&NDN.PITTable.splice(h,1);h=g.closure;clearTimeout(h.timerID);var i=!1,j=function(a,b,c,d){this.contentObject=a;this.closure=b;this.keyName=c;this.signature=d;Closure.call(this)};j.prototype.upcall=
+function(b,c){if(b==Closure.UPCALL_INTEREST_TIMED_OUT)console.log("In KeyFetchClosure.upcall: interest time out.");else if(b==Closure.UPCALL_CONTENT){console.log("In KeyFetchClosure.upcall: signature verification passed");var d=DataUtils.toHex(c.contentObject.content).toLowerCase(),e=d.slice(56,314),d=d.slice(318,324),f=new RSAKey;f.setPublic(e,d);e=!0==f.verifyByteArray(this.contentObject.rawSignatureData,this.signature)?Closure.UPCALL_CONTENT:Closure.UPCALL_CONTENT_BAD;this.closure.upcall(e,new UpcallInfo(a,
+null,0,this.contentObject))}};if(d.signedInfo&&d.signedInfo.locator&&d.signature)if(3<LOG&&console.log("Key verification..."),i=DataUtils.toHex(d.signature.signature).toLowerCase(),g=d.signedInfo.locator,g.type==KeyLocatorType.KEYNAME){console.log("KeyLocator contains KEYNAME");var l=g.keyName.contentName.getName();console.log(l);if(f.match(l)){console.log("Content is key itself");j=DataUtils.toHex(d.content).toLowerCase();console.log("Key content: "+j);var l=j.slice(56,314),m=j.slice(318,324),f=
+new RSAKey;f.setPublic(l,m);i=f.verifyByteArray(d.rawSignatureData,i);i=!0==i?Closure.UPCALL_CONTENT:Closure.UPCALL_CONTENT_BAD;h.upcall(i,new UpcallInfo(a,null,0,d));f=new KeyStoreEntry(g.keyName,j,f);NDN.KeyStore.push(f)}else console.log("Fetch key according to keylocator"),(f=NDN.getKeyByName(g.keyName))?(console.log("Local key cache hit"),f=f.rsaKey,i=f.verifyByteArray(d.rawSignatureData,i),i=!0==i?Closure.UPCALL_CONTENT:Closure.UPCALL_CONTENT_BAD,h.upcall(Closure.UPCALL_CONTENT,new UpcallInfo(a,
+null,0,d))):(h=new j(d,h,l,i),d=new Interest(g.keyName.contentName.getPrefix(4)),d.interestLifetime=4,b.expressInterest(a,d,h))}else g.type==KeyLocatorType.KEY?(console.log("Keylocator contains KEY"),j=DataUtils.toHex(g.publicKey).toLowerCase(),console.log(j),l=j.slice(56,314),m=j.slice(318,324),f=new RSAKey,f.setPublic(l,m),i=f.verifyByteArray(d.rawSignatureData,i),i=!0==i?Closure.UPCALL_CONTENT:Closure.UPCALL_CONTENT_BAD,h.upcall(Closure.UPCALL_CONTENT,new UpcallInfo(a,null,0,d)),f=new KeyStoreEntry(g.keyName,
+j,f),NDN.KeyStore.push(f)):(d=g.certificate,console.log("KeyLocator contains CERT"),console.log(d))}}else console.log("Incoming packet is not Interest or ContentObject. Discard now.");delete c;delete b.structureDecoder;delete b.buffer;b.structureDecoder=new BinaryXMLStructureDecoder;b.buffer=new Uint8Array(b.maxBufferSize);b.bufferOffset=0}};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);
+a=new Interest(new Name(NDN.ccndIdFetcher));a.interestLifetime=4E3;var a=encodeToBinaryInterest(a),d=new Uint8Array(a.length);d.set(a);b.ws.send(d.buffer)};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.");b.ws=null;a.readyStatus=NDN.CLOSED;a.onclose()}};
WebSocketTransport.prototype.expressInterest=function(a,b,c){if(null!=this.ws){var d=encodeToBinaryInterest(b),e=new Uint8Array(d.length);e.set(d);var f=new PITEntry(b,c);NDN.PITTable.push(f);this.ws.send(e.buffer);3<LOG&&console.log("ws.send() returned.");c.timerID=setTimeout(function(){3<LOG&&console.log("Interest time out.");var d=NDN.PITTable.indexOf(f);0<=d&&NDN.PITTable.splice(d,1);c.upcall(Closure.UPCALL_INTEREST_TIMED_OUT,new UpcallInfo(a,b,0,null))},b.interestLifetime)}else console.log("WebSocket connection is not established.")};
var CSTable=[],CSEntry=function(a,b){this.name=a;this.closure=b};function getEntryForRegisteredPrefix(a){for(var b=0;b<CSTable.length;b++)if(null!=CSTable[b].name.match(a))return CSTable[b];return null}
WebSocketTransport.prototype.registerPrefix=function(a,b,c){if(null!=this.ws){if(null==this.ccndid)return console.log("ccnd node ID unkonwn. Cannot register prefix."),-1;var a=new ForwardingEntry("selfreg",b,null,null,3,2147483647),a=encodeForwardingEntry(a),d=new SignedInfo;d.setFields();a=new ContentObject(new Name,d,a,new Signature);a.sign();a=encodeToBinaryContentObject(a);a=new Name(["ccnx",this.ccndid,"selfreg",a]);a=new Interest(a);a.scope=1;d=encodeToBinaryInterest(a);a=new Uint8Array(d.length);
@@ -57,10 +61,10 @@
SignedInfo.prototype.to_ccnb=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_ccnb(a));null!=this.timestamp&&a.writeDateTime(CCNProtocolDTags.Timestamp,this.timestamp);null!=this.type&&0!=this.type&&a.writeElement(CCNProtocolDTags.type,this.type);null!=this.freshnessSeconds&&a.writeElement(CCNProtocolDTags.FreshnessSeconds,
this.freshnessSeconds);null!=this.finalBlockID&&a.writeElement(CCNProtocolDTags.FinalBlockID,this.finalBlockID);null!=this.locator&&this.locator.to_ccnb(a);a.writeEndElement()};SignedInfo.prototype.valueToType=function(){return null};SignedInfo.prototype.getElementLabel=function(){return CCNProtocolDTags.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 i=g?"getUTC":"get",k=e[i+"Date"](),m=e[i+"Day"](),l=e[i+"Month"](),n=e[i+"FullYear"](),j=e[i+"Hours"](),p=e[i+"Minutes"](),r=e[i+"Seconds"](),i=e[i+"Milliseconds"](),q=g?0:e.getTimezoneOffset(),s={d:k,dd:d(k),ddd:h.i18n.dayNames[m],dddd:h.i18n.dayNames[m+7],m:l+1,mm:d(l+1),mmm:h.i18n.monthNames[l],mmmm:h.i18n.monthNames[l+12],yy:String(n).slice(2),yyyy:n,h:j%12||12,hh:d(j%12||12),H:j,HH:d(j),M:p,MM:d(p),s:r,
-ss:d(r),l:d(i,3),L:d(99<i?Math.round(i/10):i),t:12>j?"a":"p",tt:12>j?"am":"pm",T:12>j?"A":"P",TT:12>j?"AM":"PM",Z:g?"UTC":(String(e).match(b)||[""]).pop().replace(c,""),o:(0<q?"-":"+")+d(100*Math.floor(Math.abs(q)/60)+Math.abs(q)%60,4),S:["th","st","nd","rd"][3<k%10?0:(10!=k%100-k%10)*k%10]};return f.replace(a,function(a){return a in s?s[a]:a.slice(1,a.length-1)})}}();
+f=String(h.masks[f]||f||h.masks["default"]);"UTC:"==f.slice(0,4)&&(f=f.slice(4),g=!0);var i=g?"getUTC":"get",j=e[i+"Date"](),l=e[i+"Day"](),m=e[i+"Month"](),n=e[i+"FullYear"](),k=e[i+"Hours"](),p=e[i+"Minutes"](),r=e[i+"Seconds"](),i=e[i+"Milliseconds"](),q=g?0:e.getTimezoneOffset(),s={d:j,dd:d(j),ddd:h.i18n.dayNames[l],dddd:h.i18n.dayNames[l+7],m:m+1,mm:d(m+1),mmm:h.i18n.monthNames[m],mmmm:h.i18n.monthNames[m+12],yy:String(n).slice(2),yyyy:n,h:k%12||12,hh:d(k%12||12),H:k,HH:d(k),M:p,MM:d(p),s:r,
+ss:d(r),l:d(i,3),L:d(99<i?Math.round(i/10):i),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<q?"-":"+")+d(100*Math.floor(Math.abs(q)/60)+Math.abs(q)%60,4),S:["th","st","nd","rd"][3<j%10?0:(10!=j%100-j%10)*j%10]};return f.replace(a,function(a){return a in s?s[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,i,k,m){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=i;this.interestLifetime=k;this.nonce=m};Interest.RECURSIVE_POSTFIX="*";Interest.CHILD_SELECTOR_LEFT=0;Interest.CHILD_SELECTOR_RIGHT=1;Interest.ANSWER_CONTENT_STORE=1;Interest.ANSWER_GENERATED=2;
+Date.prototype.format=function(a,b){return dateFormat(this,a,b)};var Interest=function(a,b,c,d,e,f,g,h,i,j,l){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=i;this.interestLifetime=j;this.nonce=l};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_ccnb=function(a){a.readStartElement(CCNProtocolDTags.Interest);this.name=new Name;this.name.from_ccnb(a);a.peekStartElement(CCNProtocolDTags.MinSuffixComponents)&&(this.minSuffixComponents=a.readIntegerElement(CCNProtocolDTags.MinSuffixComponents));a.peekStartElement(CCNProtocolDTags.MaxSuffixComponents)&&(this.maxSuffixComponents=a.readIntegerElement(CCNProtocolDTags.MaxSuffixComponents));a.peekStartElement(CCNProtocolDTags.PublisherPublicKeyDigest)&&(this.publisherPublicKeyDigest=
new PublisherPublicKeyDigest,this.publisherPublicKeyDigest.from_ccnb(a));a.peekStartElement(CCNProtocolDTags.Exclude)&&(this.exclude=new Exclude,this.exclude.from_ccnb(a));a.peekStartElement(CCNProtocolDTags.ChildSelector)&&(this.childSelector=a.readIntegerElement(CCNProtocolDTags.ChildSelector));a.peekStartElement(CCNProtocolDTags.AnswerOriginKind)&&(this.answerOriginKind=a.readIntegerElement(CCNProtocolDTags.AnswerOriginKind));a.peekStartElement(CCNProtocolDTags.Scope)&&(this.scope=a.readIntegerElement(CCNProtocolDTags.Scope));
@@ -70,13 +74,14 @@
Interest.prototype.matches_name=function(a){var b=this.name.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 Exclude=function(a){this.OPTIMUM_FILTER_SIZE=100;this.values=a};Exclude.prototype.from_ccnb=function(a){a.readStartElement(this.getElementLabel());a.readEndElement()};
Exclude.prototype.to_ccnb=function(a){if(!validate())throw new ContentEncodingException("Cannot encode "+this.getClass().getName()+": field values missing.");empty()||(a.writeStartElement(getElementLabel()),a.writeEndElement())};Exclude.prototype.getElementLabel=function(){return CCNProtocolDTags.Exclude};var ExcludeAny=function(){};ExcludeAny.prototype.from_ccnb=function(a){a.readStartElement(this.getElementLabel());a.readEndElement()};
ExcludeAny.prototype.to_ccnb=function(a){a.writeStartElement(this.getElementLabel());a.writeEndElement()};ExcludeAny.prototype.getElementLabel=function(){return CCNProtocolDTags.Any};var ExcludeComponent=function(a){this.body=a};ExcludeComponent.prototype.from_ccnb=function(a){this.body=a.readBinaryElement(this.getElementLabel())};ExcludeComponent.prototype.to_ccnb=function(a){a.writeElement(this.getElementLabel(),this.body)};ExcludeComponent.prototype.getElementLabel=function(){return CCNProtocolDTags.Component};
-var Key=function(){},KeyLocatorType={NAME:1,KEY:2,CERTIFICATE:3},KeyLocator=function(a,b){this.type=b;b==KeyLocatorType.NAME?this.keyName=a:b==KeyLocatorType.KEY?(4<LOG&&console.log("SET KEY"),this.publicKey=a):b==KeyLocatorType.CERTIFICATE&&(this.certificate=a)};
-KeyLocator.prototype.from_ccnb=function(a){a.readStartElement(this.getElementLabel());if(a.peekStartElement(CCNProtocolDTags.Key)){try{this.publicKey=encodedKey=a.readBinaryElement(CCNProtocolDTags.Key),this.type=2,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(CCNProtocolDTags.Certificate)){try{this.certificate=encodedCert=a.readBinaryElement(CCNProtocolDTags.Certificate),
-this.type=3,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=1,this.keyName=new KeyName,this.keyName.from_ccnb(a);a.readEndElement()};
+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_ccnb=function(a){a.readStartElement(this.getElementLabel());if(a.peekStartElement(CCNProtocolDTags.Key)){try{this.publicKey=encodedKey=a.readBinaryElement(CCNProtocolDTags.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(CCNProtocolDTags.Certificate)){try{this.certificate=encodedCert=a.readBinaryElement(CCNProtocolDTags.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_ccnb(a);a.readEndElement()};
KeyLocator.prototype.to_ccnb=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(CCNProtocolDTags.Key,this.publicKey);else if(this.type==KeyLocatorType.CERTIFICATE)try{a.writeElement(CCNProtocolDTags.Certificate,this.certificate)}catch(b){throw Error("CertificateEncodingException attempting to write key locator: "+
-b);}else this.type==KeyLocatorType.NAME&&this.keyName.to_ccnb(a);a.writeEndElement()};KeyLocator.prototype.getElementLabel=function(){return CCNProtocolDTags.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};
+b);}else this.type==KeyLocatorType.KEYNAME&&this.keyName.to_ccnb(a);a.writeEndElement()};KeyLocator.prototype.getElementLabel=function(){return CCNProtocolDTags.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_ccnb=function(a){a.readStartElement(this.getElementLabel());this.contentName=new Name;this.contentName.from_ccnb(a);4<LOG&&console.log("KEY NAME FOUND: ");PublisherID.peek(a)&&(this.publisherID=new PublisherID,this.publisherID.from_ccnb(a));a.readEndElement()};
KeyName.prototype.to_ccnb=function(a){if(!this.validate())throw Error("Cannot encode : field values missing.");a.writeStartElement(this.getElementLabel());this.contentName.to_ccnb(a);null!=this.publisherID&&this.publisherID.to_ccnb(a);a.writeEndElement()};KeyName.prototype.getElementLabel=function(){return CCNProtocolDTags.KeyName};KeyName.prototype.validate=function(){return null!=this.contentName};
+KeyName.prototype.matches_name=function(a){var b=this.contentName.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 PublisherType=function(a){this.KEY=CCNProtocolDTags.PublisherPublicKeyDigest;this.CERTIFICATE=CCNProtocolDTags.PublisherCertificateDigest;this.ISSUER_KEY=CCNProtocolDTags.PublisherIssuerKeyDigest;this.ISSUER_CERTIFICATE=CCNProtocolDTags.PublisherIssuerCertificateDigest;this.Tag=a},isTypeTagVal=function(a){return a==CCNProtocolDTags.PublisherPublicKeyDigest||a==CCNProtocolDTags.PublisherCertificateDigest||a==CCNProtocolDTags.PublisherIssuerKeyDigest||a==CCNProtocolDTags.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_ccnb=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+"."));};
@@ -164,8 +169,8 @@
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,i,k,m,l,n,j,p;e=c[0];f=c[1];g=c[2];h=c[3];i=c[4];k=c[5];m=c[6];l=c[7];for(n=0;64>n;n++)d[n]=16>n?a[n+b]:safe_add(safe_add(safe_add(sha256_Gamma1256(d[n-2]),d[n-7]),sha256_Gamma0256(d[n-15])),d[n-16]),j=safe_add(safe_add(safe_add(safe_add(l,sha256_Sigma1256(i)),sha256_Ch(i,k,m)),sha256_K[n]),d[n]),p=safe_add(sha256_Sigma0256(e),sha256_Maj(e,f,g)),l=m,m=k,k=i,i=safe_add(h,j),h=g,g=f,f=e,e=safe_add(j,p);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(i,c[4]);c[5]=safe_add(k,c[5]);c[6]=safe_add(m,c[6]);c[7]=safe_add(l,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};
+function processBlock_sha256(a,b,c,d){var e,f,g,h,i,j,l,m,n,k,p;e=c[0];f=c[1];g=c[2];h=c[3];i=c[4];j=c[5];l=c[6];m=c[7];for(n=0;64>n;n++)d[n]=16>n?a[n+b]:safe_add(safe_add(safe_add(sha256_Gamma1256(d[n-2]),d[n-7]),sha256_Gamma0256(d[n-15])),d[n-16]),k=safe_add(safe_add(safe_add(safe_add(m,sha256_Sigma1256(i)),sha256_Ch(i,j,l)),sha256_K[n]),d[n]),p=safe_add(sha256_Sigma0256(e),sha256_Maj(e,f,g)),m=l,l=j,j=i,i=safe_add(h,k),h=g,g=f,f=e,e=safe_add(k,p);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(i,c[4]);c[5]=safe_add(j,c[5]);c[6]=safe_add(l,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="=";
@@ -180,8 +185,8 @@
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);for(var b=a.mod(this.p).modPow(this.dmp1,this.p),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),i=ASN1HEX.getPosOfNextSibling_AtObj(a,h),k=ASN1HEX.getPosOfNextSibling_AtObj(a,i),a=ASN1HEX.getPosOfNextSibling_AtObj(a,k);b.push(c,d,e,f,g,h,i,k,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]),i=ASN1HEX.getHexOfV_AtObj(a,b[6]),k=ASN1HEX.getHexOfV_AtObj(a,b[7]),a=ASN1HEX.getHexOfV_AtObj(a,b[8]),b=[];b.push(c,d,e,f,g,h,i,k,a);return b}
+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),i=ASN1HEX.getPosOfNextSibling_AtObj(a,h),j=ASN1HEX.getPosOfNextSibling_AtObj(a,i),a=ASN1HEX.getPosOfNextSibling_AtObj(a,j);b.push(c,d,e,f,g,h,i,j,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]),i=ASN1HEX.getHexOfV_AtObj(a,b[6]),j=ASN1HEX.getHexOfV_AtObj(a,b[7]),a=ASN1HEX.getHexOfV_AtObj(a,b[8]),b=[];b.push(c,d,e,f,g,h,i,j,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){for(var b=b/4,a=(0,_RSASIGN_HASHHEXFUNC[c])(a),c="00"+_RSASIGN_DIHEAD[c]+a,a="",b=b-4-c.length,d=0;d<b;d+=2)a+="ff";return sPaddedMessageHex="0001"+a+c}
@@ -211,8 +216,8 @@
function _x509_readCertHex(a){var a=a.toLowerCase(),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){var a=_x509_pemToHex(a),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){for(var g=b&32767,b=b>>15;0<=--f;){var h=this[a]&32767,i=this[a++]>>15,k=b*h+i*g,h=g*h+((k&32767)<<15)+c[d]+(e&1073741823),e=(h>>>30)+(k>>>15)+b*i+(e>>>30);c[d++]=h&1073741823}return e}
-function am3(a,b,c,d,e,f){for(var g=b&16383,b=b>>14;0<=--f;){var h=this[a]&16383,i=this[a++]>>14,k=b*h+i*g,h=g*h+((k&16383)<<14)+c[d]+e,e=(h>>28)+(k>>14)+b*i;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 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){for(var g=b&32767,b=b>>15;0<=--f;){var h=this[a]&32767,i=this[a++]>>15,j=b*h+i*g,h=g*h+((j&32767)<<15)+c[d]+(e&1073741823),e=(h>>>30)+(j>>>15)+b*i+(e>>>30);c[d++]=h&1073741823}return e}
+function am3(a,b,c,d,e,f){for(var g=b&16383,b=b>>14;0<=--f;){var h=this[a]&16383,i=this[a++]>>14,j=b*h+i*g,h=g*h+((j&16383)<<14)+c[d]+e,e=(h>>28)+(j>>14)+b*i;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 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-
@@ -224,8 +229,8 @@
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,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 i=e*(1<<this.F1)+(1<d?f[d-2]>>this.F2:0),k=this.FV/i,i=(1<<this.F1)/i,m=1<<this.F2,l=c.t,n=l-d,j=null==b?nbi():b;f.dlShiftTo(n,j);0<=c.compareTo(j)&&(c[c.t++]=1,c.subTo(j,c));BigInteger.ONE.dlShiftTo(d,
-j);for(j.subTo(f,f);f.t<d;)f[f.t++]=0;for(;0<=--n;){var p=c[--l]==e?this.DM:Math.floor(c[l]*k+(c[l-1]+m)*i);if((c[l]+=f.am(0,p,c,n,0,d))<p){f.dlShiftTo(n,j);for(c.subTo(j,c);c[l]<--p;)c.subTo(j,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 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,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 i=e*(1<<this.F1)+(1<d?f[d-2]>>this.F2:0),j=this.FV/i,i=(1<<this.F1)/i,l=1<<this.F2,m=c.t,n=m-d,k=null==b?nbi():b;f.dlShiftTo(n,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<=--n;){var p=c[--m]==e?this.DM:Math.floor(c[m]*j+(c[m-1]+l)*i);if((c[m]+=f.am(0,p,c,n,0,d))<p){f.dlShiftTo(n,k);for(c.subTo(k,c);c[m]<--p;)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 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}
@@ -249,8 +254,8 @@
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,i=d-1,k=(1<<d)-1;g[1]=f.convert(this);if(1<d){c=nbi();for(f.sqrTo(g[1],c);h<=k;)g[h]=nbi(),f.mulTo(c,g[h-2],g[h]),h+=2}for(var m=a.t-1,l,n=!0,j=nbi(),c=nbits(a[m])-1;0<=m;){c>=i?l=a[m]>>c-i&k:(l=(a[m]&(1<<c+1)-1)<<i-c,0<m&&(l|=a[m-1]>>this.DB+c-i));for(h=d;0==(l&1);)l>>=1,--h;if(0>(c-=h))c+=this.DB,--m;if(n)g[l].copyTo(e),
-n=!1;else{for(;1<h;)f.sqrTo(e,j),f.sqrTo(j,e),h-=2;0<h?f.sqrTo(e,j):(h=e,e=j,j=h);f.mulTo(j,g[l],e)}for(;0<=m&&0==(a[m]&1<<c);)f.sqrTo(e,j),h=e,e=j,j=h,0>--c&&(c=this.DB-1,--m)}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,i=d-1,j=(1<<d)-1;g[1]=f.convert(this);if(1<d){c=nbi();for(f.sqrTo(g[1],c);h<=j;)g[h]=nbi(),f.mulTo(c,g[h-2],g[h]),h+=2}for(var l=a.t-1,m,n=!0,k=nbi(),c=nbits(a[l])-1;0<=l;){c>=i?m=a[l]>>c-i&j:(m=(a[l]&(1<<c+1)-1)<<i-c,0<l&&(m|=a[l-1]>>this.DB+c-i));for(h=d;0==(m&1);)m>>=1,--h;if(0>(c-=h))c+=this.DB,--l;if(n)g[m].copyTo(e),
+n=!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<=l&&0==(a[l]&1<<c);)f.sqrTo(e,k),h=e,e=k,k=h,0>--c&&(c=this.DB-1,--l)}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,