// Copyright (c) 2017 The Regents of the University of California.
// All rights reserved.
// Permission is hereby granted, without written agreement and without
// license or royalty fees, to use, copy, modify, and distribute this
// software and its documentation for any purpose, provided that the above
// copyright notice and the following two paragraphs appear in all copies
// of this software.
// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
// ENHANCEMENTS, OR MODIFICATIONS.
/**
* IoT Auth JavaScript module for client and server entities.
*
* @module @accessors-modules/iot-auth
* @author Hokeun Kin
* @version $$Id$$
*/
// Stop extra messages from jslint. Note that there should be no
// space between the / and the * and global.
/*globals exports, Java, require, util */
/*jshint globalstrict: true */
'use strict';
// modules
var crypto = require('crypto');
var net = require('net');
var fs = require('fs');
var util = require('util');
var dgram = require('dgram');
//var sleep =require('sleep'); // for testing
// local modules
var common = require('./common');
var msgType = common.msgType;
var iotAuthService = require('./iotAuthService');
var iotSecureClient = require('./iotSecureClient');
var iotSecureServer = require('./iotSecureServer');
exports.msgType = msgType;
exports.parseTimePeriod = function(str) {
str = str.replace(/sec/gi, '1000');
str = str.replace(/min/gi, '1000*60');
str = str.replace(/hour/gi, '1000*60*60');
str = str.replace(/day/gi, '1000*60*60*24');
str = str.replace(/week/gi, '1000*60*60*24*7');
return eval(str);
};
exports.loadJSONConfig = function(inputFileName) {
console.log('loading from config file: ' + inputFileName);
var fileLines = fs.readFileSync(inputFileName, 'utf8').split('\n');
var fileString = "";
for (var i = 0; i < fileLines.length; i++) {
var line = fileLines[i].trim();
if (line.startsWith('//') || line.length == 0) {
continue;
}
fileString += line;
}
return JSON.parse(fileString);
};
exports.loadEntityConfig = function(inputFileName) {
var entityConfig = exports.loadJSONConfig(inputFileName);
if (entityConfig.entityInfo.usePermanentDistKey) {
entityConfig.entityInfo.permanentDistKey = {
absValidity: new Date(new Date().getTime() +
exports.parseTimePeriod(entityConfig.entityInfo.permanentDistKey.validity)),
cipherKeyVal: fs.readFileSync(entityConfig.entityInfo.permanentDistKey.cipherKey),
macKeyVal: fs.readFileSync(entityConfig.entityInfo.permanentDistKey.macKey)
};
}
else {
entityConfig.entityInfo.privateKey = fs.readFileSync(entityConfig.entityInfo.privateKey);
entityConfig.authInfo.publicKey = fs.readFileSync(entityConfig.authInfo.publicKey);
}
return entityConfig;
};
/*
options = {
authHost,
authPort,
entityName,
numKeysPerRequest,
purpose,
distProtocol,
distributionKey = {val, absValidity},
distributionCryptoSpec = {cipher, mac},
publicKeyCryptoSpec = {sign},
authPublicKey,
entityPrivateKey,
connectionTimeout
}
*/
exports.getSessionKeyReqOptions = function(entityConfig, distributionKey, purpose, numKeys) {
return {
authHost: entityConfig.authInfo.host,
authPort: entityConfig.authInfo.port,
entityName: entityConfig.entityInfo.name,
numKeysPerRequest: numKeys,
purpose: purpose,
distProtocol: entityConfig.entityInfo.distProtocol,
distributionKey: distributionKey,
distributionCryptoSpec: entityConfig.cryptoInfo.distributionCryptoSpec,
publicKeyCryptoSpec: entityConfig.cryptoInfo.publicKeyCryptoSpec,
authPublicKey: entityConfig.authInfo.publicKey,
entityPrivateKey: entityConfig.entityInfo.privateKey,
connectionTimeout: entityConfig.entityInfo.connectionTimeout
};
}
/*
eventHandlers = {
onError
}
*/
exports.sendSessionKeyReq = function(options, sessionKeyRespHandler, eventHandlers, callbackParams) {
console.log('Distribution protocol: ' + options.distProtocol);
if (options.distProtocol === 'TCP') {
iotAuthService.sendSessionKeyReqViaTCP(options, sessionKeyRespHandler, eventHandlers, callbackParams);
}
else if (options.distProtocol === 'UDP') {
iotAuthService.sendSessionKeyReqViaUDP(options, sessionKeyRespHandler, eventHandlers, callbackParams);
}
else {
console.error('Unknown distribution protocol!');
}
};
/*
options = {
serverHost,
serverPort,
sessionKey = {val, absValidity},
sessionCryptoSpec,
sessionProtocol,
handshakeTimeout
}
*/
/*
eventHandlers = {
onClose,
onError,
onData,
onConnection
}
*/
exports.initializeSecureCommunication = function(options, eventHandlers) {
console.log('Session protocol: ' + options.sessionProtocol);
if (options.sessionProtocol === 'TCP') {
iotSecureClient.initializeSecureCommunicationOnTCP(options, eventHandlers);
}
else if (options.sessionProtocol === 'UDP') {
iotSecureClient.initializeSecureCommunicationOnUDP(options, eventHandlers);
}
else {
console.error('Unknown Session protocol!');
}
};
/*
options = {
serverPort,
sessionCryptoSpec,
sessionProtocol,
handshakeTimeout
}
*/
/*
eventHandlers = {
onServerError, // for server
onServerListening,
onClientRequest, // for client's communication initialization request
onClose, // for individual sockets
onError,
onData,
onConnection
}
*/
exports.initializeSecureServer = function(options, eventHandlers) {
console.log('Session protocol: ' + options.sessionProtocol);
if (options.sessionProtocol === 'TCP') {
iotSecureServer.initializeSecureServerOnTCP(options, eventHandlers);
}
else if (options.sessionProtocol === 'UDP') {
iotSecureServer.initializeSecureServerOnUDP(options, eventHandlers);
}
else {
console.error('Unknown Session protocol!');
}
};
/*
SecureMqtt Format
{
keyId: /UIntBE/, // SESSION_KEY_ID_SIZE Bytes - in plain text
seqNum: /UIntBE/, SEQ_NUM_SIZE Bytes - encrypted
data: /Buffer/, // data - encrypted
}
*/
exports.encryptSerializeSecureqMqtt = function(obj, sessionKey, sessionCryptoSpec) {
if (obj.seqNum == undefined || obj.data == undefined) {
console.log('Error: SecureMqtt seqNum or data is missing.');
return;
}
var seqNumBuf = new Buffer(common.SEQ_NUM_SIZE);
seqNumBuf.writeUIntBE(obj.seqNum, 0, common.SEQ_NUM_SIZE);
var buf = Buffer.concat([seqNumBuf, obj.data]);
var encBuf = common.symmetricEncryptAuthenticate(buf, sessionKey, sessionCryptoSpec);
var keyIdBuf = new Buffer(common.SESSION_KEY_ID_SIZE);
keyIdBuf.writeUIntBE(sessionKey.id, 0, common.SESSION_KEY_ID_SIZE);
var buf = common.serializeIoTSP({
msgType: msgType.SECURE_PUB,
payload: Buffer.concat([keyIdBuf, encBuf])
});
return buf;
}
exports.parseDecryptSecureMqtt = function(buf, sessionKeyList, sessionCryptoSpec) {
var keyId = buf.readUIntBE(0, common.SESSION_KEY_ID_SIZE);
// find id
for (var i = 0; i < sessionKeyList.length; i++) {
if (sessionKeyList[i].id == keyId) {
var decBuf = common.symmetricDecryptAuthenticate(buf.slice(common.SESSION_KEY_ID_SIZE),
sessionKeyList[i], sessionCryptoSpec);
var seqNum = decBuf.readUIntBE(0, common.SEQ_NUM_SIZE);
var data = decBuf.slice(common.SEQ_NUM_SIZE);
return {seqNum: seqNum, data:data};
}
}
console.log('cannot find the session key id: ' + keyId);
}
/*
options = {
newAuthHost,
newAuthPort,
currentAuthPublicKey,
entityName,
entityPrivateKey,
publicKeyCryptoSpec,
connectionTimeout,
usePermanentDistKey,
distributionCryptoSpec,
permanentDistKey
}
*/
exports.getMigrationReqOptions = function(entityConfig, currentMigrationInfo, trustedAuthPublicKeyList) {
return {
newAuthHost: currentMigrationInfo.host,
newAuthPort: currentMigrationInfo.port,
trustedAuthPublicKeyList: trustedAuthPublicKeyList,
entityName: entityConfig.entityInfo.name,
entityPrivateKey: entityConfig.entityInfo.privateKey,
publicKeyCryptoSpec: entityConfig.cryptoInfo.publicKeyCryptoSpec,
connectionTimeout: entityConfig.entityInfo.connectionTimeout,
usePermanentDistKey: entityConfig.entityInfo.usePermanentDistKey,
distributionCryptoSpec: entityConfig.cryptoInfo.distributionCryptoSpec,
permanentDistKey: entityConfig.entityInfo.permanentDistKey
};
}
/*
eventHandlers = {
onError
}
*/
exports.migrateToTrustedAuth = function(options, migrationRespHandler, eventHandlers) {
//console.log('In iotAuth.js migrateToTrustedAuth: ' + util.inspect(options));
console.log('In iotAuth.js migrateToTrustedAuth: ');
iotAuthService.sendMigrationReqViaTCP(options, migrationRespHandler, eventHandlers);
}