Source: org/terraswarm/accessor/accessors/web/hosts/node/node_modules/@accessors-modules/iot-auth/iot-auth.js

// 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);
}