Calculate the key id of public key using openpgp.js - javascript

Using OpenPGP.js v1.3.0 I can successfully create a public/private key pair and encrypt/decrypt ok.
I am struggling to obtain the key id using this function (found in openpgp.js and public_key.js):
/**
* Calculates the key id of the key
* #return {String} A 8 byte key id
*/
PublicKey.prototype.getKeyId = function () {
if (this.keyid) {
return this.keyid;
}
this.keyid = new type_keyid();
if (this.version == 4) {
this.keyid.read(util.hex2bin(this.getFingerprint()).substr(12, 8));
} else if (this.version == 3) {
this.keyid.read(this.mpi[0].write().substr(-8));
}
return this.keyid;
};
PublicKey() also in openpgp.js:
/**
* #constructor
*/
function PublicKey() {
this.tag = enums.packet.publicKey;
this.version = 4;
/** Key creation date.
* #type {Date} */
this.created = new Date();
/** A list of multiprecision integers
* #type {module:type/mpi} */
this.mpi = [];
/** Public key algorithm
* #type {module:enums.publicKey} */
this.algorithm = 'rsa_sign';
// time in days (V3 only)
this.expirationTimeV3 = 0;
/**
* Fingerprint in lowercase hex
* #type {String}
*/
this.fingerprint = null;
/**
* Keyid
* #type {module:type/keyid}
*/
this.keyid = null;
}
and trying to get the key id like this:
var publickey = openpgp.key.readArmored(myPublicKey);
//var keyID = openpgp.packet.PublicKey(publickey).getKeyId()[0].toHex();
var keyID = openpgp.PublicKey(publickey).getKeyId()[0].toHex();
console.log(keyID);
which gives me the error:
TypeError: openpgp.PublicKey is not a function.
Thanks.

I asked the question here:
http://www.mail-archive.com/list#openpgpjs.org/msg00932.html
and received a very helpful reply.
var openpgp = window.openpgp;
var testPublicKey = sessionStorage.getItem('testPublicKey');
var foundKeys = openpgp.key.readArmored(testPublicKey).keys;
if (!foundKeys || foundKeys.length !== 1) {
throw new Error("Key not read, or more than one key found");
}
var pubKey = foundKeys[0]; foundKeys = null;
var getFingerprint = function (key) {
// openpgp.key <- Class
// key <- Instance received by params
return key.primaryKey.fingerprint;
};
var getKeyId = function (key) {
return key.primaryKey.getKeyId().toHex();
}
console.log(getFingerprint(pubKey));
console.log(getKeyId(pubKey));

Related

Javascript sanity check of netsuite suitescript

Currently, I have a Netsuite SuiteScript where I export saved searches to csv's. It runs but it's not reusable and I'd like to make it easier by just adding key-value pairs. I have to a lot of copy pasting and it's easy to forget to update to the latest iteration of the run. It's a scheduled search which means it runs every 15 minutes and makes it hard to debug and test.
Right now, my code looks like this, but with more searches, and it's a pain is not reusable.
/**
* #NApiVersion 2.x
* #NScriptType ScheduledScript
* #NModuleScope SameAccount
*/
define(['N/task'],
/**
* #param {record} record
* #param {search} search
*/
function(task) {
var FILE_ID = 2992639;
var SEARCH_ID = 2993;
function execute(scriptContext) {
//first search
var searchTask1 = task.create({
taskType: task.TaskType.SEARCH
});
searchTask1.savedSearchId = SEARCH_ID;
searchTask1.fileId = FILE_ID;
var searchTaskId1 = searchTask1.submit();
//next search
FILE_ID = 2992640;
SEARCH_ID = 3326;
var searchTask2 = task.create({
taskType: task.TaskType.SEARCH
});
searchTask2.savedSearchId = SEARCH_ID;
searchTask2.fileId = FILE_ID;
var searchTaskId2 = searchTask2.submit();
//next search
FILE_ID = 2992634;
SEARCH_ID = 3327;
var searchTask3 = task.create({
taskType: task.TaskType.SEARCH
});
searchTask3.savedSearchId = SEARCH_ID;
searchTask3.fileId = FILE_ID;
var searchTaskId3 = searchTask3.submit();
//this pattern repeats 19 times total.
}
return {
execute: execute
};
});
I've tried to code below
/**
* #NApiVersion 2.x
* #NScriptType ScheduledScript
* #NModuleScope SameAccount
*/
define(['N/task'],
/**
* #param {record} record
* #param {search} search
*/
function(task) {
const searchList = {
2993:2992639,
3326:2992640,
3327:2992634
};
function execute(scriptContext) {
for (const [key, value] of Object.entries(searchList)) {
var searchTask = task.create({
taskType: task.TaskType.SEARCH
});
searchTask.savedSearchId = $key;
searchTask.fileId = $value;
var searchTaskId = searchTask.submit();
}
}
return {
execute: execute
};
});
but get the following error, and I'm not sure what is wrong with my syntax. Netsuite makes it hard to tell what I'm doing wrong, so I'm hoping someone can help here. Thanks!
{"type":"error.SuiteScriptError","name":"SSS_MISSING_REQD_ARGUMENT","message":"task.submit: Missing a required argument: SearchTask.savedSearchId","stack":["createError(N/error)","execute(/SuiteScripts/dashboardreports.js:224)","createError(N/error)"],"cause":{"name":"SSS_MISSING_REQD_ARGUMENT","message":"task.submit: Missing a required argument: SearchTask.savedSearchId"},"id":"","notifyOff":false,"userFacing":true}
I would use a custom record to store the search and file IDs, so you can add/update without modifying code. Then I would do something like the code below. This code first queries your custom record to get all of the search and file ids, then for each one, starts a new task.
/**
* #NApiVersion 2.0
* #NScriptType ScheduledScript
* #NModuleScope Public
*/
define(['N/log', 'N/search', 'N/task'], function(log, search, task) {
function execute(context) {
var searchInfos = getSearchInfo();
searchInfos.forEach(function(searchInfo) {
var searchTask = task.create({
taskType: task.TaskType.SEARCH
});
searchTask.savedSearchId = searchInfo.searchId;
searchTask.fileId = searchInfo.fileId;
var searchTaskId = searchTask.submit();
log.debug({ title: 'searchTaskId', details: searchTaskId });
});
}
function getSearchInfo() {
var results = search.create({
type: 'customrecord_search_to_csv_info',
filters: [
['isinactive', 'is', 'F']
],
columns: [
'custcolumn_search_id',
'custcolumn_file_id'
]
}).run().getRange({ start: 0, end: 1000 });
return (results || []).map(function(result) {
return {
searchId: result.getValue({ name: 'custcolumn_search_id '}),
fileId: result.getValue({ name: 'custcolumn_file_id' })
}
});
}
return {
execute: execute
};
});

Remove Specific Item From Array With Class Objects Inside

My Problem
I am trying to remove a specific item from my array, however, my array contains other objects which I cannot get a handle to.
I am defining a "Station" like this:
/* CLASS Station
* #param id int unique id this station
* #param name str name of the station
* #param location obj the position of the station
* in the workflow diagram
*/
var Station = function(id, name, posX=null, posY=null) {
this.id = ko.observable(id || self.getUniqueId());
this.name = ko.observable(name);
this.posX = ko.observable(posX);
this.posY = ko.observable(posY);
};
So I added a station to my array by using this function ...
.
.
self.addStation(new Station(76, "Receiving", 0, 10));
Now, I want to know how to remove from array by passing the name, as in:
self.removeStation("Receiving");
I can't figure it out. I have researched all the links on here, with no luck.
Entire Source Code
// CLASS Workflow
var Workflow = function(id, status){
this.status = status || false;
this.id = id;
}
/* CLASS Station
* #param id int unique id this station
* #param name str name of the station
* #param location obj the position of the station
* in the workflow diagram
*/
var Station = function(id, name, posX=null, posY=null) {
this.id = ko.observable(id || self.getUniqueId());
this.name = ko.observable(name);
this.posX = ko.observable(posX);
this.posY = ko.observable(posY);
};
// CLASS ViewModel
var ViewModel = function(workFlowId) {
var self = this; // Scope Trick
/*******************************
* Observables
*-----------------------------*/
self.station = ko.observableArray();
/*******************************
* Initialize The Builder
*-----------------------------*/
self.workflowId = ko.observable();
/*******************************
* Arrays
*-----------------------------*/
self.workflow = ko.observableArray();
/*******************************
* Actions
*-----------------------------*/
/* Method: initWorkflow
*
* Desc: When the user gets to the builder
* page, we have to configure a Workflow.
* If they are loading a saved one, the ID
* will be passed. If they are starting a new
* one, we will give it a unique ID.
*
* #param int workFlowId The id of the workflow
* #return int workFlowId The id is returned
*/
self.initWorkflow = function(workFlowId, status=false) {
var id;
if(!workFlowId){
/* Call Function to generate unique ID */
id = self.getUniqueId();
} else {
id = workFlowId;
}
/* Set ID */
this.workflowId = id;
this.workflow = new Workflow(id, status);
};
/*-------------------------------------------------------
* Method: addStation
*
* Desc: Adds a station to current workflow
* #param station object A station object
*--------------------------------------------------------*/
self.addStation = function(station){
self.station.push(station);
}
/* Remove Station - */
self.removeStation = function (Name) {
for( var i = 0; i < self.station().length; i++){
console.dir("In Remove Function: " + self.station()[i]);
}
}
/*-------------------------------------------------------
* Method: getUniqueId
*
* Desc: Generates a random unique Id
* #returns id int A unique random ID
*--------------------------------------------------------*/
self.getUniqueId = function(){
var id = new Date().getTime();
console.group("In Funtion: self.getUniqueId");
console.log("Returned unique id of: " + id);
console.groupEnd("In Funtion: self.getUniqueId");
return id;
}
/* Start it up */
self.initWorkflow(workFlowId);
//------------------------
// UNIT TESTING
//------------------------
//........ STATION RELATED ..........................
// 1. Add
self.addStation(new Station(76, "Receiving", 0, 10));
// 2. Remove
self.removeStation("Receiving");
} // end ViewModel
// Instantiate the ViewModel
window.view_model = new ViewModel();
// Away we go...
ko.applyBindings(window.view_model);
I can't seem to get a hold of the name in the array:
// DON'T WORK
self.station()[i].Station.name
Thanks for looking.
John
u could use function to find its index look like:
function arrayFirstIndexOf(array, predicate, predicateOwner) {
for (var i = 0, j = array.length; i < j; i++) {
if (predicate.call(predicateOwner, array[i])) {
return i;
}
}
return -1;
}
then in your /* Remove Station - */u edit code look like this:
/* Remove Station - */
self.removeStation = function (Name) {
var index = arrayFirstIndexOf(self.station(), function(item){
return item.name === Name;
});
index > -1 && self.station.splice(index, 1);
}
Hope this help !
you can use ko.utils.arrayRemoveItem to remove an item from the array you need to find it first
/* Remove Station */
self.removeStation = function (search) {
// find the station
var foundStation = ko.utils.arrayFirst(this.station, function(item) {
return ko.utils.stringStartsWith(item.name().toLowerCase(), search);
});
// remove the station
ko.utils.arrayRemoveItem(this.station, foundStation);
}
Thank you for your responses. But I found an acceptable solution to my problem by using this following code:
/* Remove Station */
self.removeStation = function (Name) {
var c = -1;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
var value = item.name();
if(value==Name){
console.log("found it! at index " + c);
self.station().splice(c,1);
}
});
// Force the UI to update
self.station.valueHasMutated();
}
I realize this is not the cleanest or most efficient solution, but it seems to work. I would love to know how to optimize this, but that's above my pay grade, lol.
sry for long days answer, i think the problem you got is about variable you declared and variable you use not the same type, and you not use any convert of them when u assigned to your object look like:
var Station = function(id, name, posX=null, posY=null) {
this.id = ko.observable(id || self.getUniqueId()); //observalbe declared
this.name = ko.observable(name);//observalbe declared
this.posX = ko.observable(posX);//observalbe declared
this.posY = ko.observable(posY);//observalbe declared
};
but then you use assign look like
var newObj = {
id: "82",
name: "Updated Name",
posX: 92,
posY: 88
}
self.modifyStation("name","Shipping",newObj);
when u assign it:
objNewItem.id = oldId; // Put old ID back in
self.station.push(objNewItem); //not convert its properties to observable will make some errors on binding html.
i make a working jsfildle with your example, please read it, and reply when u not understand which part. hope this help.
function arrayFirstIndexOf(array, predicate, predicateOwner) {
for (var i = 0, j = array.length; i < j; i++) {
if (predicate.call(predicateOwner, array[i])) {
return i;
}
}
return -1;
}
// CLASS Workflow
var Workflow = function(id, status) {
this.status = status || false;
this.id = id;
}
/* CLASS Field
* #param id int unique id this station
* #param fieldType str type of input
* #param fieldName obj name of the input
* #param options array options array
*/
var Field = function(fieldId, fieldType, fieldName, options) {
this.fieldId = ko.observable(fieldId);
this.fieldType = ko.observable(fieldType);
this.fieldName = ko.observable(fieldName);
this.options = ko.observableArray(options);
};
/* CLASS Station
* #param id int unique id this station
* #param name str name of the station
* #param location obj the position of the station
* in the workflow diagram
*/
var Station = function(id, name, posX = null, posY = null, fields) {
this.id = ko.observable(id || self.getUniqueId());
this.name = ko.observable(name);
this.posX = ko.observable(posX);
this.posY = ko.observable(posY);
this.fields = ko.observableArray(fields || []);
};
// CLASS ViewModel
var ViewModel = function(workFlowId) {
var self = this; // Scope Trick
/*******************************
* Observables
*-----------------------------*/
self.fieldId = ko.observable();
self.fieldName = ko.observable();
self.fieldType = ko.observable();
/*******************************
* Initialize The Builder
*-----------------------------*/
self.workflowId = ko.observable();
/*******************************
* Arrays
*-----------------------------*/
self.workflow = ko.observableArray();
self.station = ko.observableArray();
self.fields = ko.observableArray();
/*******************************
* Computed Observables
*-----------------------------*/
/*******************************
* Actions
*-----------------------------*/
/* Method: initWorkflow
*
* Desc: When the user gets to the builder
* page, we have to configure a Workflow.
* If they are loading a saved one, the ID
* will be passed. If they are starting a new
* one, we will give it a unique ID.
*
* #param int workFlowId The id of the workflow
* #return int workFlowId The id is returned
*/
self.initWorkflow = function(workFlowId, status = false) {
var id;
if (!workFlowId) {
/* Call Function to generate unique ID */
id = self.getUniqueId();
} else {
id = workFlowId;
}
/* Set ID */
this.workflowId = id;
this.workflow = new Workflow(id, status);
};
/*-------------------------------------------------------
* Method: addStation
*
* Desc: Adds a station to current workflow
* #param station object A station object
*--------------------------------------------------------*/
self.addStation = function(station) {
self.station.push(station);
}
/* Remove Station */
self.removeStation = function(Name) {
var index = arrayFirstIndexOf(self.station(), function(item){
return item.name() === Name;
});
index > -1 && self.station.splice(index, 1);
}
/* Update A Station ** NEEDS FIXING **
*
* #param findBy string Property to find ( "id" or "name")
* #param cmpVal string Value to compare against
* #param objNewItem object The new object replacing old
* */
self.modifyStation = function(findBy, cmpVal, objNewItem) {
var sourceIndex;
var oldId;
var found = false;
/* Find Index Of Old Station */
var c = -1;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
switch (findBy) {
case "id":
var value = ko.unwrap(item.id);
if (value == cmpVal) {
sourceIndex = c;
oldId = value;
found = true;
}
break;
case "name":
var value = ko.unwrap(item.name);
if (value == cmpVal) {
sourceIndex = c;
oldId = ko.unwrap(item.id);
found = true;
}
break;
}
});
/* Remove Old */
if (found === true) {
self.station().splice(sourceIndex, 1);
/* Insert New Station
* [For Now] not allowing updating of ID. Only
* can update the other properties (yes, I realize that
* only leaves "name", but more will be added )
*/
objNewItem.id(oldId); // Put old ID back in
self.station.push(objNewItem);
} else {
alert(cmpVal + " was not found in array!");
}
}
self.addField = function(stationId, newField) {
var c = -1;
found = false;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
var value = ko.unwrap(item.id);
console.log(value, c);
if (value == stationId) {
//console.log("found it! at index " + c);
self.station()[c].fields.push(newField);
}
});
self.station.valueHasMutated();
};
self.modifyField = function(stationId, oldFieldId, newObjField) {
// TO DO
};
self.removeField = function(field) {
self.fields.remove(field);
};
/* Perform Test On Button Click */
self.doTest = function() {
self.removeStation("Shipping");
}
self.doTest2 = function() {
var newObj = {
id: ko.observable("82"),
name: ko.observable("Updated Name"),
posX: ko.observable(92),
posY: ko.observable(88),
fields: ko.observableArray([])
}
self.modifyStation("name", "Shipping", newObj);
self.station.valueHasMutated();
}
// Add Fields
self.doTest3 = function() {
var objNewField = {
fieldId: 456,
fieldName: "Last Name",
fieldType: "Text"
}
self.addField(86, objNewField);
}
/*-------------------------------------------------------
* Method: getUniqueId
*
* Desc: Generates a random unique Id
* #returns id int A unique random ID
*--------------------------------------------------------*/
self.getUniqueId = function() {
var id = new Date().getTime();
console.group("In Funtion: self.getUniqueId");
console.log("Returned unique id of: " + id);
console.groupEnd("In Funtion: self.getUniqueId");
return id;
}
/*-------------------------------------------------------
* Method: debugAll
*
* Desc: Console Logs Our View Model
*--------------------------------------------------------*/
this.debugAll = function() {
console.group("Debug:");
// Workflow
console.group("Current Workflow Id")
console.log("self.workflowId = " + self.workflowId);
console.groupEnd("Current Workflow Id")
// Stations
console.group("Stations In This Workflow")
console.table(self.station());
console.groupEnd("Stations In This Workflow")
// Fields
console.groupEnd("Debug:");
}
/* Start it up */
self.initWorkflow(workFlowId);
//------------------------
// UNIT TESTING
//------------------------
//........ STATION RELATED ..........................
// 1. Add
self.addStationShipping = function() {
self.addStation(new Station(86, "Shipping", 0, 10, [{
fieldId: 45,
fieldName: "First Name",
fieldType: "Text"
}]));
}
self.addStation(new Station(76, "Receiving", 0, 10, null));
self.addStationShipping();
/* Dump ViewModel */
self.debugAll();
//----------------------------------------------------------------
} // end ViewModel
// Instantiate the ViewModel
window.view_model = new ViewModel(1213131212);
// Away we go...
ko.applyBindings(window.view_model);
// Page Utility Functions
function wait(ms) {
var start = new Date().getTime();
var end = start;
while (end < start + ms) {
end = new Date().getTime();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div>
<ul data-bind="foreach: station">
<li data-bind="text: 'id: ' + id()"></li>
<li data-bind="text: 'name: ' +name()"></li>
<li data-bind="text: 'posX: ' +posX()"></li>
<li data-bind="text: 'posY: ' +posY()"></li>
<ul data-bind="foreach: fields">
<li data-bind="text: fieldId"></li>
<li data-bind="text: fieldName"></li>
<li data-bind="text: fieldType"></li>
</ul>
</ul>
</div>
<button data-bind="click: doTest">Remove "Shipping" From Station Array</button>
<button data-bind="click: doTest2">Update "Shipping" In Station Array</button>
<hr>
<h3>Test: Add Field To Shipping Station</h3>
<button data-bind="click: function() { addStationShipping()}">Add Shpping</button>
<button data-bind="click: doTest3()">Add Field</button>

Use jQuery Search in Bootstrap Popover but method won't trigger

Hi Stackoverflow Community,
I've got the following problem and I can't handle it myself:
I've got the following search form:
<li class="list-group-item active" data-ignore-list-search="ignore"><?php echo $translate->_('Animals'); ?><span class="icon-search pull-right" rel="SearchBox" data-placement="bottom" data-container="body"></span>
<div id="popover_content_wrapper" class="hide">
<input placeholder="<?php echo $translate->_('Search'); ?> " id="FilterForm" class="filterform filterinput" type="text">
</div>
</li>
And I'm using following jQuery code to filter ul items:
(function(jQuery){
var ListSearch = function() {};
ListSearch.prototype = {
/**
* run
* start incremental search.
* #param {} input
* #param {} target
* #returns {}
*/
run: function(input, target){
var _this = this;
var tagName = target.get(0).tagName;
input.on('keyup', function(e){
text = _this.getInputText(input);
switch(tagName){
case 'TABLE':
_this.listSearchTable(target, text);
break;
case 'UL':
_this.listSearchListUl(target, text);
break;
case 'OL':
_this.listSearchListOl(target, text);
break;
case 'DL':
_this.listSearchListDl(target, text);
break;
default:
throw new Error('Illegal tag name ' + targetObject.targetTagName);
}
});
},
getInputText: function(input){
return input.val();
},
/**
* tag table
*/
listSearchTable: function(target, text){
this.listSearchCommon('tr', target, text);
},
/**
* tag ul
*/
listSearchListUl: function(target, text){
this.listSearchCommon('li', target, text);
},
/**
* tag ol
*/
listSearchListOl: function(target, text){
return this.listSearchListUl(target, text);
},
/**
* tag dl
*/
listSearchListDl: function(target, text){
this.listSearchCommon('dd dt', target, text);
},
/**
* commondSearchList
*/
listSearchCommon: function(tagName ,target, text){
var _this = this;
var searchWords = this.searchWordToWords(text);
var wordLength = searchWords.length;
target.find(tagName).each(function(){
var thisJQuery = jQuery(this);
if (thisJQuery.data('ignore-list-search') === 'ignore') return true;
var body = _this.getBody(thisJQuery);
var displayFlag = true;
var wordCount = 0;
for(wordCount = 0; wordCount < wordLength; wordCount++){
var word = searchWords[wordCount];
var pattern = new RegExp(word, 'i');
if ( !body.match(pattern) ) {
displayFlag = false;
break;
}
}
jQuery(this).css('display', _this.getDisplayProperty(tagName, displayFlag));
return true;
})
},
/**
* getDisplayProperty
* #param {} tagName
* #param {} flag
* #returns {}
*/
getDisplayProperty: function(tagName, flag){
switch(tagName){
case 'tr':
return flag?'table-row':'none';
case 'li':
return flag?'block':'none';
case 'dd dt':
return flag?'list-item':'none';
default:
throw new Error('Illegal tag name ' + targetObject.targetTagName);
}
},
/**
* getBody
* #returns {}
*/
getBody: function(target){
var body = target.text();
return jQuery.trim(body);
},
/**
* searchWordToWords
* a search text split to search words.
* #param {} body
* #param {} searchWord
* #returns {}
*/
searchWordToWords: function(text){
text = jQuery.trim(text);
var pattern = new RegExp(/[  \-\/]/);
var words = text.split(pattern);
// delete empty element
var newWords = new Array();
var wordsLength = words.length;
var wordsCount = 0;
for (wordsCount = 0; wordsCount < wordsLength; wordsCount++){
var word = words[wordsCount];
if (word != ""){
newWords.push(words[wordsCount]);
}
}
return newWords;
}
}
/**
* Main stream
*/
jQuery.fn.listSearch = function(input, options){
var options = jQuery.extend(jQuery.fn.listSearch.defaults, options);
// set using objects.
var target = jQuery(this);
switch (jQuery.type(input)){
case 'string':
input = jQuery(input);
break;
case 'object':
input = input;
break;
default:
throw 'Argiment value "' + input + '" is invalid.';
}
// Event start
listSearch = new ListSearch();
listSearch.run(input, target);
return target;
};
/**
* default settings.
*/
jQuery.fn.listSearch.defaults = {
};
$('#AnimalList').listSearch('#FilterForm')
;
})(jQuery);
This works fine .. but if I put the #FilterForm input into the popover it doesn't filter out my uls anymore.
My Popover jQuery code:
$('[rel=SearchBox]').popover({
html : true,
content: function() {
return $('#popover_content_wrapper').html();
},
trigger: 'click'
});
my UL hasn't any special tags or anything.
Thanks in advance and sorry for my weird English [Ed - fixed]: I am from Germany.
Greetings,
Daniel
#user2908086, Its because you are using the same "filterform" text twice in the same input field but in different Types! Here is what you use: id="FilterForm" + class="filterform"! Locate the "filterform" class text in your CSS and rename the "filterform" and make sure its not the same as your ID. Then add the same class name back in your input and see if that will work for you! Good luck!

Connecting to an RFID/NFC (ACR122) reader from a JavaScript script

I'm using a script that reads the data off the e-Passport. However I need some help.
The script I have is from another program called SmartCardShell 3. This is the code:
/**
* ---------
* |.##> <##.| Open Smart Card Development Platform (www.openscdp.org)
* |# #|
* |# #| Copyright (c) 1999-2006 CardContact Software & System Consulting
* |'##> <##'| Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
* ---------
*
* This file is part of OpenSCDP.
*
* OpenSCDP is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* OpenSCDP 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenSCDP; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* #fileoverview Dump the content of a passport with Basic Access Control to file
*
* Before running this script, please make sure that the variable mrz2 is set
* to the second line of the machine readable zone on your passport.
*/
// MRZ of silver data set
//
// P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<
// L898902C<3UTO6908061F9406236ZE184226B<<<<<14
// '-DocNo--' '-DoB-' '-DoE-'
//var mrz2 = "L898902C<3UTO6908061F9406236ZE184226B<<<<<14";
// Harvinder Khaira MRZ:
// P<GBRKHAIRA<<HARVINDER<SINGH<<<<<<<<<<<<<<<<
// 4604503228GBR8711164M1711291<<<<<<<<<<<<<<06
var mrz2 = "4604503228GBR8711164M1711291<<<<<<<<<<<<<<06";
// MRZ of Tsukuba data set
//
// WG30004036UTO6007078M0511014<<<<<<<<<<<<<<06
// '-DocNo--' '-DoB-' '-DoE-'
// var mrz2 = "WG30004036UTO6007078M0511014<<<<<<<<<<<<<<06";
//============================================tools.js=========================
// Import some tools
/*
* ---------
* |.##> <##.| Open Smart Card Development Platform (www.openscdp.org)
* |# #|
* |# #| Copyright (c) 1999-2006 CardContact Software & System Consulting
* |'##> <##'| Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
* ---------
*
* This file is part of OpenSCDP.
*
* OpenSCDP is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* OpenSCDP 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenSCDP; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Tools for accessing a ICAO conform Machine Readable Travel Document
*/
/*
* Calculate a single Basic Access Control (BAC) key from the second
* line of the Machine Readable Zone (MRZ).
*
* The function extracts the Document Number, Date of Birth and Date of Expiration
* from the second line of the machine readable zone
*
* E.g. MRZ of Silver Data Set
* P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<
* L898902C<3UTO6908061F9406236ZE184226B<<<<<14
* '-DocNo--' '-DoB-' '-DoE-'
*
* This extract is then hashed, concatenated with a key number and
* hashed again.
* crypto Crypto object used for hashing
* mrz2 String containing second line of MRZ
* keyno Number of key to calculate (1 for Kenc and 2 for Kmac)
*
* Returns Key object
*/
function calculateBACKey(crypto, mrz2, keyno) {
// Convert to byte string
var strbin = new ByteString(mrz2, ASCII);
// Extract Document Number, Date of Birth and Date of Expiration
var hash_input = strbin.bytes(0, 10);
hash_input = hash_input.concat(strbin.bytes(13, 7));
hash_input = hash_input.concat(strbin.bytes(21, 7));
printlnln("Hash Input : " + hash_input.toString(ASCII));
// Hash input
var mrz_hash = crypto.digest(Crypto.SHA_1, hash_input);
printlnln("MRZ Hash : " + mrz_hash);
// Extract first 16 byte and append 00000001 or 00000002
var bb = new ByteBuffer(mrz_hash.bytes(0, 16));
bb.append(new ByteString("000000", HEX));
bb.append(keyno);
// Hash again to calculate key value
var keyval = crypto.digest(Crypto.SHA_1, bb.toByteString());
keyval = keyval.bytes(0, 16);
printlnln("Value of Key : " + keyval);
var key = new Key();
key.setComponent(Key.DES, keyval);
return key;
}
// The SecureChannel object is required as a credential for the CardFile objects
// SecureChannel objects must at least implement a wrap() method. The unwrap()
// method is optional, but called when defined
function SecureChannel(crypto, kenc, kmac, ssc) {
this.crypto = crypto;
this.kenc = kenc;
this.kmac = kmac;
this.ssc = ssc;
this.trace = false;
}
SecureChannel.prototype.enableTrace = function () {
this.trace = true;
}
//
// Increment send sequence counter
//
SecureChannel.prototype.incssc = function () {
var c = this.ssc.bytes(4, 4).toUnsigned() + 1;
bb = new ByteBuffer(this.ssc.bytes(0, 4));
bb.append((c >> 24) & 0xFF);
bb.append((c >> 16) & 0xFF);
bb.append((c >> 8) & 0xFF);
bb.append((c ) & 0xFF);
this.ssc = bb.toByteString();
}
//
// Wrap command-APDU with secure messaging
//
SecureChannel.prototype.wrap = function(apduToWrap) {
if (this.trace) {
printlnln("Command-APDU to wrap :");
printlnln(apduToWrap);
}
var b = new ByteBuffer();
var macb = new ByteBuffer();
// Transform CLA byte and add header
var cla = apduToWrap.byteAt(0);
cla |= 0x0C;
b.append(cla);
b.append(apduToWrap.bytes(1, 3));
this.incssc();
macb.append(this.ssc);
macb.append(b.toByteString().pad(Crypto.ISO9797_METHOD_2));
var do87 = null;
var le = apduToWrap.bytes(apduToWrap.length - 1, 1);
if (apduToWrap.length > 5) {
var lc = apduToWrap.byteAt(4);
var plain = apduToWrap.bytes(5, lc);
plain = plain.pad(Crypto.ISO9797_METHOD_2);
if (this.trace) {
printlnln("Input to cipher:");
printlnln(plain);
}
var cipher = this.crypto.encrypt(this.kenc, Crypto.DES_CBC, plain, new ByteString("0000000000000000", HEX));
do87 = new ByteString("01", HEX);
do87 = do87.concat(cipher);
do87 = new TLV(0x87, do87, TLV.EMV);
do87 = do87.getTLV();
macb.append(do87);
if (apduToWrap.length == 5 + lc) {
le = new ByteString("", HEX);
}
} else if (apduToWrap.length == 4) {
le = new ByteString("", HEX);
}
var do97;
if (le.length > 0) {
do97 = new ByteString("9701", HEX);
do97 = do97.concat(le);
macb.append(do97);
} else {
do97 = new ByteString("", HEX);
}
if (this.trace) {
printlnln("Input to MAC calculation :");
}
var macinput = macb.toByteString().pad(Crypto.ISO9797_METHOD_2);
if (this.trace) {
printlnln(macinput);
}
var mac = this.crypto.sign(this.kmac, Crypto.DES_MAC_EMV, macinput);
if (this.trace) {
printlnln("Calculated MAC :");
printlnln(mac);
}
var macdo = new ByteString("8E08", HEX);
macdo = macdo.concat(mac);
if (do87 != null) {
b.append(do87.length + do97.length + macdo.length);
b.append(do87);
} else {
b.append(do97.length + macdo.length);
}
b.append(do97);
b.append(macdo);
if (le.length > 0) {
b.append(0);
}
if (this.trace) {
printlnln("Wrapped Command-APDU :");
printlnln(b.toByteString());
}
return(b.toByteString());
}
//
// Unwrap response-APDU with secure messaging
//
SecureChannel.prototype.unwrap = function(apduToUnwrap) {
if (this.trace) {
printlnln("Response-APDU to unwrap :");
printlnln(apduToUnwrap);
}
if (apduToUnwrap.length == 2) {
return(apduToUnwrap);
}
var b = new ByteBuffer();
var macb = new ByteBuffer();
this.incssc();
macb.append(this.ssc);
var tl = new TLVList(apduToUnwrap.left(apduToUnwrap.length - 2), TLV.EMV);
var mac = null;
for (i = 0; i < tl.length; i++) {
var t = tl.index(i);
if (t.getTag() == 0x8E) {
mac = t.getValue();
} else {
macb.append(t.getTLV());
}
}
if (mac == null) {
throw new GPError("SecureChannelCredential", GPError.OBJECT_NOT_FOUND, 0, "MAC data object missing");
}
if (this.trace) {
printlnln(macb.toByteString());
}
if (!this.crypto.verify(this.kmac, Crypto.DES_MAC_EMV, macb.toByteString().pad(Crypto.ISO9797_METHOD_2), mac)) {
throw new GPError("SecureChannelCredential", GPError.CRYPTO_FAILED, 0, "MAC verification failed");
}
var t = tl.find(0x87);
if (t != null) {
var cryptogram = t.getValue();
var padding = cryptogram.byteAt(0);
cryptogram = cryptogram.right(cryptogram.length - 1);
if (padding != 0x01) {
throw new GPError("SecureChannelCredential", GPError.INVALID_MECH, padding, "Unsupported padding mode " + padding + " in cryptogram");
}
var plain = this.crypto.decrypt(this.kenc, Crypto.DES_CBC, cryptogram, new ByteString("0000000000000000", HEX));
for (i = plain.length - 1; (i > 0) && (plain.byteAt(i) != 0x80); i--);
b.append(plain.left(i));
}
var t = tl.find(0x81);
if (t != null) {
b.append(t.getValue());
}
var t = tl.find(0x99);
if (t == null) {
b.append(apduToUnwrap.right(2));
} else {
b.append(t.getValue());
}
if (this.trace) {
printlnln("Unwrapped Response-APDU :");
printlnln(b.toByteString());
}
return(b.toByteString());
}
/*
* Open secure channel using basic access control keys
*
* card Card object for access to passport
* crypto Crypto object to be used for cryptographic operations
* kenc Kenc key
* kmac Kmac key
*
* Returns Open secure channel object
*/
function openSecureChannel(card, crypto, kenc, kmac) {
// Perform mutual authentication procedure
printlnln("Performing mutual authentication");
var rndicc = card.sendApdu(0x00, 0x84, 0x00, 0x00, 0x08, [0x9000]);
//printlnln("======RNDicc : " + rndicc);
var rndifd = crypto.generateRandom(8);
//println("======RNDifd : " + rndifd);
var kifd = crypto.generateRandom(16);
var plain = rndifd.concat(rndicc).concat(kifd);
println("Plain Block : " + plain);
var cryptogram = crypto.encrypt(kenc, Crypto.DES_CBC, plain, new ByteString("0000000000000000", HEX));
println("Cryptogram : " + cryptogram);
var mac = crypto.sign(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2));
println("MAC : " + mac);
var autresp = card.sendApdu(0x00, 0x82, 0x00, 0x00, cryptogram.concat(mac), 0);
if (card.SW != 0x9000) {
println("Mutual authenticate failed with " + card.SW.toString(16) + " \"" + card.SWMSG + "\". MRZ correct ?");
throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card did not accept MAC");
}
println("Response : " + autresp);
cryptogram = autresp.bytes(0, 32);
mac = autresp.bytes(32, 8);
if (!crypto.verify(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2), mac)) {
throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card MAC did not verify correctly");
}
plain = crypto.decrypt(kenc, Crypto.DES_CBC, cryptogram, new ByteString("0000000000000000", HEX));
println("Plain Block : " + plain);
//var iccifd = rndicc.concat(rndifd);
//println ("==RNDicc + RNDifd : " + iccifd);
if (!plain.bytes(0, 8).equals(rndicc)) {
throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card response does not contain matching RND.ICC");
}
if (!plain.bytes(8, 8).equals(rndifd)) {
throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card response does not contain matching RND.IFD");
}
var kicc = plain.bytes(16, 16);
keyinp = kicc.xor(kifd);
var hashin = keyinp.concat(new ByteString("00000001", HEX));
var kencval = crypto.digest(Crypto.SHA_1, hashin);
kencval = kencval.bytes(0, 16);
println("Kenc : " + kencval);
var kenc = new Key();
kenc.setComponent(Key.DES, kencval);
var hashin = keyinp.concat(new ByteString("00000002", HEX));
var kmacval = crypto.digest(Crypto.SHA_1, hashin);
kmacval = kmacval.bytes(0, 16);
println("Kmac : " + kmacval);
var kmac = new Key();
kmac.setComponent(Key.DES, kmacval);
var ssc = rndicc.bytes(4, 4).concat(rndifd.bytes(4, 4));
println("SSC : " + ssc);
// Disable to use script-secure messaging secure messaging
var sc = new IsoSecureChannel(crypto);
sc.setEncKey(kenc);
sc.setMacKey(kmac);
sc.setSendSequenceCounter(ssc);
return sc;
//
// Enable to use script-secure messaging secure messaging
// return new SecureChannel(crypto, kenc, kmac, ssc);
//
}
/*
* Write a byte string object to file
*
* The filename is mapped to the location of the script
*
* name Name of file
* content ByteString content for file
*
*/
function writeFileOnDisk(name, content) {
// Map filename
var filename = GPSystem.mapFilename(name, GPSystem.USR);
println("Writing " + filename);
var file = new java.io.FileOutputStream(filename);
file.write(content);
file.close();
}
/*
* Read a byte string object from file
*
* The filename is mapped to the location of the script
*
* name Name of file
*
*/
function readFileFromDisk(name) {
// Map filename
var filename = GPSystem.mapFilename(name, GPSystem.USR);
println("Reading " + filename);
var file = new java.io.FileInputStream(filename);
var content = new ByteBuffer();
var buffer = new ByteString(" ", ASCII);
var len;
while ((len = file.read(buffer)) >= 0) {
content.append(buffer.bytes(0, len));
}
file.close();
return(content.toByteString());
}
/*
* Extract the length of the file from the TLV encoding at the beginning of the
* file
*
* header First bytes read from file
*
* Return Total length of TLV object
*/
function lengthFromHeader(header) {
var value;
value = header.byteAt(1);
if (value > 0x82) {
throw new GPError("lengthfromheader()", GPError.INVALID_DATA, value, "");
}
switch(value) {
case 0x81:
value = header.byteAt(2) + 1;
break;
case 0x82:
value = (header.byteAt(2) << 8) + header.byteAt(3) + 2;
break;
}
return value + 2;
}
//===========================================end of tools.js===================
/*
* Read file from passport and save to disk
*
*/
function handleFile(secureChannel, lds, name, fid) {
printlnln("Reading " + name + " (" + fid + ")...");
// Select file
var ef = new CardFile(lds, ":" + fid);
if (secureChannel) {
// Set secure channel as credential for read access
ef.setCredential(CardFile.READ, Card.ALL, secureChannel);
}
// Read first 4 bytes of file
var res = ef.readBinary(0, 4);
// Determine file length from TLV header
var len = lengthFromHeader(res);
// Read complete file
var res = ef.readBinary(0, len);
printlnln("Content");
printlnln(res);
writeFileOnDisk(name + ".bin", res);
return res;
}
/*
* Save picture from DG2
*
*/
function savePicture(dg2) {
// Save picture to .jpeg file
var tlv = new ASN1(dg2);
var bin = tlv.get(0).get(1).get(1).value;
var offset = bin.find(new ByteString("FFD8", HEX));
if (offset >= 0) {
writeFileOnDisk("face.jpg", bin.bytes(offset));
}
}
// Create card and crypto object
var card = new Card( /* somehow connect to ACR122 reader */);
card.reset(Card.RESET_COLD);
var crypto = new Crypto();
// Select LDS application
var lds = new CardFile(card, "#A0000002471001");
var secureChannel = null;
// Try reading EF_COM to figure out if BAC is needed
card.sendApdu(0x00, 0xB0, 0x9E, 0x00, 0x01);
if (card.SW != 0x9000) {
// Calculate kenc and kmac for mutual authentication from the MRZ data
printlnln("Trying BAC with MRZ2=" + mrz2);
var kenc = calculateBACKey(crypto, mrz2, 1);
var kmac = calculateBACKey(crypto, mrz2, 2);
// Dummy to load crypto libraries (Saves some time later)
crypto.encrypt(kenc, Crypto.DES_CBC, new ByteString("0000000000000000", HEX), new ByteString("0000000000000000", HEX));
secureChannel = openSecureChannel(card, crypto, kenc, kmac);
/* Only works with script based secure messaging. See tools.js for details
secureChannel.enableTrace();
*/
// Enable SELECT commands to be send in secure messaging
// lds.setCredential(CardFile.SELECT, CardFile.ALL, secureChannel);
/*
card.setCredential(secureChannel);
var resp = card.sendSecMsgApdu(Card.ALL, 0x00, 0xA4, 0x02, 0x0C, new ByteString("011E", HEX));
println(resp);
*/
}
handleFile(secureChannel, lds, "EF_COM", "1E");
handleFile(secureChannel, lds, "EF_DG1", "01");
var dg2 = handleFile(secureChannel, lds, "EF_DG2", "02");
savePicture(dg2);
handleFile(secureChannel, lds, "EF_SOD", "1D");
I have incorporated 2 scripts (dumpmrtd.js and tools.js) together.
The issue is that towards the bottom where:
// Create card and crypto object
var card = new Card( /* somehow connect to ACR122 reader */);
card.reset(Card.RESET_COLD);
In order to create a new Card object, it requires a String reader. This is my problem!
I do not know how to obtain this "reader".
Anyone have any ideas?
Thanks in advance
**PS. sorry for the terrible indentation/code quotation. This is the first time im actually posting on this website

some problem when using jsdoc

'use strict';
/**
* ImagePreloader is a tool for multi-images preloading
*
* #example
* new ImagePreloader(['http://image1.jpg', 'http://image2.jpg'], function (oImage, this.aImages, this.nProcessed, this.nLoaded) {
* //use jQuery
* $('<img>').attr('src', oImage.src);
* });
*
* #author nhnst Liuming
* #version 20110623.4
* #constructor
* #param {String[]} images A collection of images url for preloading
* #param {Function} callback(oImage, this.aImages, this.nProcessed, this.nLoaded) A function to call once the every image request finishes
*/
function ImagePreloader(images, callback) {
this.callback = callback;
this.nLoaded = 0;
this.nProcessed = 0;
this.aImages = [];
this.nImages = images.length;
for (var i = 0, len = images.length ; i < len ; i += 1) {
this.preload(images[i], i);
}
}
/**
* Initialization
* #private
*/
ImagePreloader.prototype.preload = function (image, index) {
var oImage = new Image;
oImage.nIndex = index;
oImage.bLoaded = false;
oImage.oImagePreloader = this;
oImage.src = image + '?flush=' + (+new Date());
this.aImages.push(oImage);
// handle ie cache
if (oImage.width > 0) {
this.onload.call(oImage);
}
else {
oImage.onload = this.onload;
oImage.onerror = this.onerror;
oImage.onabort = this.onabort;
}
};
/**
* A inner function to be called when every image request finishes
* #private
*/
ImagePreloader.prototype.onComplete = function (oImage) {
this.nProcessed++;
this.callback(oImage, this.aImages, this.nProcessed, this.nLoaded);
};
/**
* A inner function to be called when image loads successful
* #private
*/
ImagePreloader.prototype.onload = function () {
this.bLoaded = true;
this.oImagePreloader.nLoaded++;
this.oImagePreloader.onComplete(this);
};
/**
* A inner function to be called when an error occurs loading the image
* #private
*/
ImagePreloader.prototype.onerror = function () {
this.bError = true;
this.oImagePreloader.onComplete(this);
};
/**
* A inner function to be called when image loading is aborted
* #private
*/
ImagePreloader.prototype.onabort = function () {
this.bAbort = true;
this.oImagePreloader.onComplete(this);
};
when I generate doc file using jsdoc, some warnnings occur
>> WARNING: Trying to document ImagePreloader#onload without first documenting I
magePreloader.
>> WARNING: Trying to document ImagePreloader#onerror without first documenting
ImagePreloader.
>> WARNING: Trying to document ImagePreloader#onabort without first documenting
ImagePreloader.
3 warnings.
why?
I would suggest adding #name and #class to ImagePreloader:
'use strict';
/**
* #class ImagePreloader is a tool for multi-images preloading
* #name ImagePreloader
*
* #example
* new ImagePreloader(['http://image1.jpg', 'http://image2.jpg'], function (oImage, this.aImages, this.nProcessed, this.nLoaded) {
* //use jQuery
* $('<img>').attr('src', oImage.src);
* });
*
* #author nhnst Liuming
* #version 20110623.4
* #constructor
* #param {String[]} images A collection of images url for preloading
* #param {Function} callback(oImage, this.aImages, this.nProcessed, this.nLoaded) A function to call once the every image request finishes
*/
function ImagePreloader(images, callback) {
this.callback = callback;
this.nLoaded = 0;
this.nProcessed = 0;
this.aImages = [];
this.nImages = images.length;
for (var i = 0, len = images.length ; i < len ; i += 1) {
this.preload(images[i], i);
}
}

Categories