From 38392977b15a314071d282d2de920e7dededebdd Mon Sep 17 00:00:00 2001 From: Youngsoo Choi Date: Thu, 13 May 2021 06:27:25 -0700 Subject: [PATCH] [Service] Upgrade device home as v1.0.4 - Generate session secret key in random - Move checking pincode retrial number to server side - Don't use Math.random API - Don't re-use pin code - Prevent connection with same session id from different devices - Prevent execution of non MDE apps - Remove x-powered-by header Change-Id: I6ef7deb26c9f50655894b76ef63b92186a4426e0 Signed-off-by: Youngsoo Choi --- device_home/client/client.html | 19 ++++++++++--- device_home/pincode/js/jsencrypt.js | 6 +++++ device_home/pincode/js/pincode.js | 52 +++++++++++++++++++----------------- device_home/service/app_proxy.js | 36 +++++++++++++++---------- device_home/service/jsencrypt.js | 7 +++++ device_home/service/security.js | 53 +++---------------------------------- device_home/service/service.js | 51 +++++++++++++++++++++++------------ 7 files changed, 116 insertions(+), 108 deletions(-) diff --git a/device_home/client/client.html b/device_home/client/client.html index d487d71..db9726b 100755 --- a/device_home/client/client.html +++ b/device_home/client/client.html @@ -1,5 +1,6 @@ + @@ -13,6 +14,7 @@ +
@@ -36,6 +38,14 @@
- + + \ No newline at end of file diff --git a/device_home/pincode/js/jsencrypt.js b/device_home/pincode/js/jsencrypt.js index de5a14f..8659f54 100755 --- a/device_home/pincode/js/jsencrypt.js +++ b/device_home/pincode/js/jsencrypt.js @@ -5,6 +5,12 @@ }(this, (function (exports) { 'use strict'; var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"; + +Math.random = function() { + const byteArray = new Uint32Array(1); + return (window.crypto.getRandomValues(byteArray))[0]; +} + function int2char(n) { return BI_RM.charAt(n); } diff --git a/device_home/pincode/js/pincode.js b/device_home/pincode/js/pincode.js index b5bcbed..2520121 100755 --- a/device_home/pincode/js/pincode.js +++ b/device_home/pincode/js/pincode.js @@ -5,11 +5,10 @@ const serverURL = window.location.protocol + '//' + window.location.hostname; var publicKey; var input = ''; var dots = document.querySelectorAll('.dot'), numbers = document.querySelectorAll('.number'); -var tryCount = 0; function displayPincode() { var xhr = new XMLHttpRequest(); - xhr.onload = function () { + xhr.onload = function() { if (xhr.status === 200 || xhr.status === 201) { publicKey = xhr.responseText; } else { @@ -23,8 +22,8 @@ function displayPincode() { function init() { dots = Array.prototype.slice.call(dots); numbers = Array.prototype.slice.call(numbers); - numbers.forEach(function (number, index) { - number.addEventListener('click', function () { + numbers.forEach(function(number, index) { + number.addEventListener('click', function() { number.className += ' grow'; if (number.innerHTML === '0') { input += 0; @@ -36,17 +35,17 @@ function init() { console.log(`${TAG} input: ${input}`); sendPinCode(input); - setTimeout(function () { - dots.forEach(function (dot, index) { + setTimeout(function() { + dots.forEach(function(dot, index) { dot.className = 'dot'; }); input = ''; }, 900); - setTimeout(function () { + setTimeout(function() { document.body.className = ''; }, 1000); } - setTimeout(function () { + setTimeout(function() { number.className = 'number'; }, 1000); }); @@ -57,10 +56,14 @@ async function sendPinCode(data) { var encrypt = new JSEncrypt(); encrypt.setPublicKey(publicKey); var xhr = new XMLHttpRequest(); - xhr.onload = function () { + xhr.onload = function() { if (xhr.status === 200 || xhr.status === 201) { console.log(`result : ${xhr.responseText}`); - chkPinCode(xhr.responseText === 'true' ? true : false); + if (xhr.responseText === 'retry') { + retryPinCode(); + } else { + chkPinCode(xhr.responseText === 'true' ? true : false); + } } else { console.error(xhr.responseText); } @@ -72,30 +75,31 @@ async function sendPinCode(data) { xhr.send(JSON.stringify({ pincode: data.toString("utf8") })); } +function retryPinCode() { + dots.forEach(function(dot, index) { + dot.className += ' wrong'; + }); + document.body.className += ' wrong'; + displayPincode(); + setTimeout(function() { + alert('Failed to input 5 times. A new Pincode has been generated, so check the TV notification.'); + }, 1000); +} + function chkPinCode(returnVal) { if (returnVal) { - dots.forEach(function (dot, index) { + dots.forEach(function(dot, index) { dot.className += ' correct'; }); document.body.className += ' correct'; - tryCount = 0; - setTimeout(function () { + setTimeout(function() { loginForm.submit(); - //pinCode(); }, 1000); } else { - dots.forEach(function (dot, index) { + dots.forEach(function(dot, index) { dot.className += ' wrong'; }); document.body.className += ' wrong'; - tryCount++; - if (tryCount === 5) { - tryCount = 0; - displayPincode(); - setTimeout(function () { - alert('Failed to input 5 times. A new Pincode has been generated, so check the TV notification.'); - }, 1000); - } } } @@ -103,7 +107,7 @@ function pinCode() { window.location.href = serverURL + ':' + serverPort + '/client/client.html'; } -window.onload = function () { +window.onload = function() { displayPincode(); init(); }; diff --git a/device_home/service/app_proxy.js b/device_home/service/app_proxy.js index 3acfa30..455530f 100755 --- a/device_home/service/app_proxy.js +++ b/device_home/service/app_proxy.js @@ -37,9 +37,17 @@ function runApp(appId, port, callback) { tizen.application.getAppsContext(onRunningAppsContext); } -module.exports = function(app, port) { +function verifyD2DApp(appID) { + var metaDataArray = tizen.application.getAppMetaData(appID), + metaDataArray = metaDataArray.filter(function (metaData) { + return metaData.key === "d2dservice" && metaData.value === "enable"; + }); + return metaDataArray.length; +} + +module.exports = function (app, port) { var appProxy = express.Router(); - + appProxy.use('/app', express.json()); appProxy.post('/', (req, res) => { var action = req.body.action; @@ -51,20 +59,20 @@ module.exports = function(app, port) { return router.path === path; })[0]; - if (!appRouter) { - appRouters.push({ - path: path, - name: name, - router: new AppRouter(app, path) + if (verifyD2DApp(appId)) { + if (!appRouter) { + appRouters.push({ + path: path, + name: name, + router: new AppRouter(app, path) + }); + } + console.log('[GlobalWebServer] appProxy.post ', path, action); + // run app + runApp(appId, port, function () { + res.send({ port: port }); }); } - - console.log('[GlobalWebServer] appProxy.post ', path, action); - - // run app - runApp(appId, port, function() { - res.send({port:port}); - }); }); appProxy.get('/', (req, res) => { diff --git a/device_home/service/jsencrypt.js b/device_home/service/jsencrypt.js index de5a14f..f2b0104 100644 --- a/device_home/service/jsencrypt.js +++ b/device_home/service/jsencrypt.js @@ -4,7 +4,14 @@ (factory((global.JSEncrypt = {}))); }(this, (function (exports) { 'use strict'; +const crypto = require('crypto'); var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"; + +Math.random = function() { + const byteArray = new Uint32Array(1); + return (crypto.getRandomValues(byteArray))[0]; +} + function int2char(n) { return BI_RM.charAt(n); } diff --git a/device_home/service/security.js b/device_home/service/security.js index bf4d447..078860b 100755 --- a/device_home/service/security.js +++ b/device_home/service/security.js @@ -36,58 +36,13 @@ module.exports.Security = class Security { }); } - awaitKeyPair(pincode) { - return this.getKeyPairPromise(pincode).then(r => { - this.privateKey = r.privateKey; - this.publicKey = r.publicKey; + awaitKeyPair(req) { + return this.getKeyPairPromise(req).then(r => { + req.session.privateKey = r.privateKey; + req.session.publicKey = r.publicKey; console.log(`${TAG}[awaitKeyPair] RSA keys are generated`); }).catch(err => { console.log(`${TAG} Error : ${err}`); }); } - - // TODO: Use following APIs instead of JSEncryptLib - decryptWithPrivateKey(encryptedString, pincode) { - const key = crypto.createPrivateKey({ - key: this.privateKey, - format: "pem", - passphrase: pincode - }); - const encrypted = crypto.privateDecrypt(key, Buffer.from(encryptedString, "base64")); - const decryptedString = encrypted.toString("utf8"); - return decryptedString; - } - - encryptWithPrivateKey(plain, pincode) { - const keyp = crypto.createPrivateKey({ - key: this.privateKey, - format: "pem", - passphrase: pincode - }); - const encrypted = crypto.privateEncrypt(keyp, Buffer.from(plain)); - const encryptedString = encrypted.toString("base64"); - return encryptedString; - } - - decryptWithPublicKey(encryptedString) { - const decrypted = crypto.publicDecrypt(this.publicKey, Buffer.from(encryptedString, "base64")); - const decryptedString = decrypted.toString("utf8"); - return decryptedString; - } - - encryptWithPublicKey(plain) { - const encrypted = crypto.publicEncrypt(this.publicKey, Buffer.from(plain)); - const encryptedString = encrypted.toString("base64"); - return encryptedString; - } - - getPrivateKey() { - console.log(`${TAG} getPrivateKey`); - return this.privateKey; - } - - getPublicKey() { - console.log(`${TAG} getPublicKey`); - return this.publicKey; - } } diff --git a/device_home/service/service.js b/device_home/service/service.js index 6e5e2f2..aa41ef6 100755 --- a/device_home/service/service.js +++ b/device_home/service/service.js @@ -36,6 +36,8 @@ var g = { port: 9000, baseDir: __dirname, }; +var tryCount = 0; +var sip; // Watch together doesn't use pincode just for demo. var MODE_WT = false; @@ -219,27 +221,37 @@ function comparePincode(req, res, encrypted) { console.log(`${TAG} encrypted : ${encrypted}`); // Decrypt pincode using private key const decrypt = new JSEncryptLib.JSEncrypt(); - decrypt.setPrivateKey(security.getPrivateKey()); + decrypt.setPrivateKey(req.session.privateKey); const decrypted = decrypt.decrypt(encrypted); console.log(`${TAG} decrypted : ${decrypted}`); - const pincode_passed = decrypted === pincode ? true : false; + const pincode_passed = decrypted === req.session.pincode ? true : false; console.log(`${TAG} pincode result : ${pincode_passed}`); if (pincode_passed) { - req.session.pincode = pincode; - console.log(`${TAG} session : ${JSON.stringify(req.session)}`); + // The pincode is disposable. + req.session.pincode = undefined; + if (!req.session.ip || req.session.ip === undefined) { + req.session.ip = sip; + } + console.log(`${TAG} pincode passed`); + } + tryCount++; + if (tryCount === 5 && !pincode_passed) { + tryCount = 0; + res.send('retry'); + } else { + res.send(pincode_passed); } - res.send(pincode_passed); } -async function displayPincode() { +async function displayPincode(req) { // Generate pincode const byteData = crypto.randomBytes(256); - pincode = parseInt(byteData.toString('hex').substr(0, 8), 16).toString().substr(0, 4); + req.session.pincode = parseInt(byteData.toString('hex').substr(0, 8), 16).toString().substr(0, 4); // Generate RSA keys - await security.awaitKeyPair(); + await security.awaitKeyPair(req); // Show pincode popup - webapis.postPlainNotification("Input Pincode: ", pincode, 10); + webapis.postPlainNotification("Input Pincode: ", req.session.pincode, 10); } var HTTPserverStart = function() { @@ -248,6 +260,7 @@ var HTTPserverStart = function() { app.engine('html', require('ejs').renderFile); app.set('view engine', 'ejs'); app.set('views', `${g.baseDir}/../`); + app.disable('x-powered-by'); app.use('/pincode', express.static(`${g.baseDir}/../pincode`)); // For post method @@ -259,7 +272,7 @@ var HTTPserverStart = function() { app.use(session({ resave: false, saveUninitialized: true, - secret: 'mde framework', + secret: String(crypto.randomBytes(20)), cookie: { httpOnly: true, secure: false, @@ -268,11 +281,14 @@ var HTTPserverStart = function() { var sessionChecker = function(req, res, next) { console.log(`${TAG} url : ${req.url}`); - console.log(`${TAG} session : ${JSON.stringify(req.session)}`); + console.log(`${TAG} session id : ${req.session.id}`); const rawIp = req.socket.remoteAddress; const ip = rawIp.slice(rawIp.lastIndexOf(":") + 1); + console.log(`${TAG} ip : ${ip}`); + sip = ip; // The pincode page and local connections are allowed without session. - if (req.session.pincode === undefined && + if (req.session.ip !== ip && + req.session.ip === undefined && !req.url.includes('id=') && !req.url.includes('/pincode/') && !non_ip_list.includes(ip)) { @@ -368,6 +384,8 @@ var HTTPserverStart = function() { if (req.query.roomId !== undefined) { // FIXME: Remove app logic here res.render("client/invited.html"); + } else if (req.query.pageUrl !== undefined) { + webapis.mde.launchBrowserFromUrl(req.query.pageUrl); } else { // Device home res.render("client/client.html"); @@ -396,10 +414,9 @@ var HTTPserverStart = function() { }); app.get('/pincode/publicKey', async (req, res) => { - await displayPincode(); - const key = security.getPublicKey(); - console.log(`${TAG} Send public key`); - res.send(key); + tryCount = 0; + await displayPincode(req); + res.send(req.session.publicKey); }); app.post('/pincode/pinCodeToServer', express.json(), (req, res) => { @@ -410,7 +427,7 @@ var HTTPserverStart = function() { }); app.post('/d2d', (req, res) => { - if (req.session.pincode !== undefined) { + if (req.session.ip !== undefined) { console.log(`${TAG} client.html`); res.render("client/client.html"); } else { -- 2.7.4