diff --git a/cli.js b/cli.js index bb601af..098134e 100644 --- a/cli.js +++ b/cli.js @@ -29,7 +29,36 @@ if (args.length <= 2) { } var filename = process.argv[2]; - -p.parseFile(filename, function() { +var printresults = function() { console.log('got', arguments); -}); +} + +if (args.length >=5) { + var command = process.argv[3]; + var keyArg = process.argv[4]; + var initializationVector = ""; + var outputFile = ""; + + if (command == 'sign'){ + outputFile = (args.length == 6) ? process.argv[5] : filename + '.signed'; + } + else if(command == 'encrypt') { + if(args.length < 6){ + console.log('missing correct # of args for encrypt command'); + process.exit(-1); + } + initializationVector = process.argv[5]; + outputFile = (args.length == 7) ? process.argv[6] : filename + '.encrypted'; + } + else { + console.log('unrecognized command ', command); + process.exit(-1); + } + + p.secureFile(filename, command, keyArg, initializationVector, outputFile, printresults); +} +else { + p.parseFile(filename, printresults); +} + + diff --git a/lib/HalModuleParser.js b/lib/HalModuleParser.js index be02e0c..458d6b6 100644 --- a/lib/HalModuleParser.js +++ b/lib/HalModuleParser.js @@ -20,6 +20,7 @@ var fs = require('fs'); var when = require('when'); +var crypto = require('crypto'); var pipeline = require('when/pipeline'); var utilities = require('./utilities.js'); var ModuleInfo = require('./ModuleInfo'); @@ -179,6 +180,69 @@ HalModuleParser.prototype = { }); }, + + /** + * Read file bytes to sign/encrypt from filename + * Read the key data (.pem or AES key bytes) to use from keyfile + * Encrypt/Sign the image file + * Write signed file to disk with outputfilename + * + * @param {String} filename, name of file to sign + * @param {String} command, encrypt or sign file + * @param {String} keyfile, name of key file to use for signing or encryption + * @param {String} keyfile, initialization vector hex string to use for encryption + * @param {String} outputfile, name of signed/encrypted image file to write to disk + * @param {Function} callback, function to call on success/failure + * @returns {Promise} + */ + secureFile: function(filename, command, keyfile, initializationvector, outputfile, callback) { + var fileInfo = { + filename: filename, + initializationvector: initializationvector + }; + + var that = this; + var allDone = pipeline([ + function() { + return that._loadFile(filename); + }, + function(fileBuffer) { + fileInfo.fileBuffer = fileBuffer; + return that.parseBuffer(fileInfo); + }, + function() { + return that._loadFile(keyfile); + }, + function(fileBuffer) { + fileInfo.keyBuffer = fileBuffer; + if (command == 'sign') { + that._signFile(fileInfo); + return that.parseBuffer(fileInfo); + } + else if (command == 'encrypt') { + return that._encryptFile(fileInfo); + } + }, + function() { + delete fileInfo.keyBuffer; + fs.writeFileSync(outputfile, fileInfo.fileBuffer); + return fileInfo; + } + ]); + + if (callback) { + when(allDone).then( + function(info) { + callback(info, null); + }, + function(err) { + callback(null, err); + }); + } + + return allDone; + }, + /* * goes and reads out the file if it exists and returns * a promise with the fileBuffer @@ -344,6 +408,7 @@ HalModuleParser.prototype = { 0x184, // Gen 2 0xC0, // Bluez 0x10C, // Core + 0x20, // Tron ]; let likelyOffset = 0; @@ -573,6 +638,50 @@ HalModuleParser.prototype = { }; }, + _signFile: function(fileInfo) { + // Exclude dummy signature, module suffix and crc bytes from signature + const signaturePaddingLength = 48; + const signatureLength = 64; + var bytesOffEndToExclude = signaturePaddingLength + signatureLength + fileInfo.suffixInfo.suffixSize + 4; + var bootloaderBytesToSign = fileInfo.fileBuffer.slice(fileInfo.prefixInfo.prefixOffset, -bytesOffEndToExclude); + + // Calculate Signature + const signature = crypto.sign(null, bootloaderBytesToSign, fileInfo.keyBuffer); + var signatureOffset = fileInfo.fileBuffer.length - bytesOffEndToExclude + signaturePaddingLength; + for(let i = 0; i < signature.length; i++) { + fileInfo.fileBuffer.writeUInt8(signature[i], signatureOffset + i); + } + + // Recalculate SHA256, exclude existing SHA, length and crc bytes from end of image + bytesOffEndToExclude = 32 + 2 + 4; + var hashBytes = fileInfo.fileBuffer.slice(0, -bytesOffEndToExclude); + var hash = Buffer.from(crypto.createHash('sha256').update(hashBytes).digest('hex'), 'hex'); + var hashOffset = fileInfo.fileBuffer.length - bytesOffEndToExclude; + for(let i = 0; i < hash.length; i++) { + fileInfo.fileBuffer.writeUInt8(hash[i], hashOffset + i); + } + + // Recalculate CRC32 + var crcOffset = fileInfo.fileBuffer.length - 4; + var dataRegion = fileInfo.fileBuffer.slice(0, crcOffset); + var crcResult = utilities.crc32(dataRegion); + for(let i = 0; i < crcResult.length; i++) { + fileInfo.fileBuffer.writeUInt8(crcResult[i], crcOffset + i); + } + + return fileInfo; + }, + + _encryptFile: function(fileInfo) { + const algorithm = 'aes-128-ctr' + const iv = Buffer.from(fileInfo.initializationvector, 'hex') + let cipher = crypto.createCipheriv(algorithm, fileInfo.keyBuffer, iv); + let encrypted = cipher.update(fileInfo.fileBuffer); + encrypted = Buffer.concat([encrypted, cipher.final()]); + + fileInfo.fileBuffer = encrypted + return fileInfo; + }, _: null }; diff --git a/lib/ModuleInfo.js b/lib/ModuleInfo.js index b7b066a..043d99d 100644 --- a/lib/ModuleInfo.js +++ b/lib/ModuleInfo.js @@ -79,7 +79,8 @@ const ModuleInfoPlatform = { BSOM: 23, XSOM: 24, B5SOM: 25, - ASSETTRACKER: 26 + ASSETTRACKER: 26, + TRON: 32, }; /**