[Service] Upgrade device home as v1.0.4 38/258338/1 submit/tizen/20210513.133954
authorYoungsoo Choi <kenshin.choi@samsung.com>
Thu, 13 May 2021 13:27:25 +0000 (06:27 -0700)
committerYoungsoo Choi <kenshin.choi@samsung.com>
Thu, 13 May 2021 13:27:25 +0000 (06:27 -0700)
- 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 <kenshin.choi@samsung.com>
device_home/client/client.html
device_home/pincode/js/jsencrypt.js
device_home/pincode/js/pincode.js
device_home/service/app_proxy.js
device_home/service/jsencrypt.js
device_home/service/security.js
device_home/service/service.js

index d487d71..db9726b 100755 (executable)
@@ -1,5 +1,6 @@
 <!DOCTYPE html>
 <html lang="en">
+
 <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, user-scalable=no">
@@ -13,6 +14,7 @@
        <script src="client/js/app.js" type="module"></script>
        <script src="client/js/myApps.js" type="module"></script>
 </head>
+
 <body>
        <div class="ui-page ui-page-active" id="main">
                <header>
                        </div>
                </header>
                <div class="ui-content" id="page-main">
+                       <div class="ui-content-subheader">
+                               Promotion
+                       </div>
+                       <div>
+                               <iframe frameborder="0" framespacing="0" width="100%" height="380"
+                                       src="http://219.254.222.198/webclip/basic/webclip.html">
+                               </iframe>
+                       </div>
                        <div id="web-clips"></div>
 
                        <div class="ui-content-subheader">
@@ -52,7 +62,7 @@
                                <ul class="ui-listview ui-content-area">
                                        <li class="ui-li-divider ui-li-has-icon">
                                                <div class="ui-li-icon">
-                                                       <img src="client/images/tw_list_icon_wallpaper.svg"/>
+                                                       <img src="client/images/tw_list_icon_wallpaper.svg" />
                                                </div>
                                                <a href="#" data-rel="back">
                                                        <span class="ui-li-text">Media item 1</span>
@@ -60,7 +70,7 @@
                                        </li>
                                        <li class="ui-li-divider ui-li-has-icon">
                                                <div class="ui-li-icon">
-                                                       <img src="client/images/tw_list_icon_wallpaper.svg"/>
+                                                       <img src="client/images/tw_list_icon_wallpaper.svg" />
                                                </div>
                                                <a href="#" data-rel="back">
                                                        <span class="ui-li-text">Media item 2</span>
@@ -68,7 +78,7 @@
                                        </li>
                                        <li class="ui-li-divider ui-li-has-icon">
                                                <div class="ui-li-icon">
-                                                       <img src="client/images/tw_list_icon_wallpaper.svg"/>
+                                                       <img src="client/images/tw_list_icon_wallpaper.svg" />
                                                </div>
                                                <a href="#" data-rel="back">
                                                        <span class="ui-li-text">Media item 3</span>
                </div>
        </div>
 </body>
-</html>
+
+</html>
\ No newline at end of file
index de5a14f..8659f54 100755 (executable)
@@ -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);
 }
index b5bcbed..2520121 100755 (executable)
@@ -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();
 };
index 3acfa30..455530f 100755 (executable)
@@ -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) => {
index de5a14f..f2b0104 100644 (file)
@@ -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);
 }
index bf4d447..078860b 100755 (executable)
@@ -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;
-  }
 }
index 6e5e2f2..aa41ef6 100755 (executable)
@@ -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 {