Server: use crypto instead of ursa for pod signature
authorChocobozzz <florian.bigard@gmail.com>
Wed, 4 Jan 2017 21:23:07 +0000 (22:23 +0100)
committerChocobozzz <florian.bigard@gmail.com>
Wed, 4 Jan 2017 21:23:07 +0000 (22:23 +0100)
package.json
server/helpers/peertube-crypto.js
server/helpers/requests.js
server/initializers/constants.js
server/middlewares/secure.js
server/middlewares/validators/remote/signature.js
server/models/request.js

index 5eadcc363fd1bab62a0331bdb0103c9c1f08c17d..554ad16df3389eb078f374a3dc28edecbf922500 100644 (file)
@@ -69,7 +69,6 @@
     "safe-buffer": "^5.0.1",
     "scripty": "^1.5.0",
     "sequelize": "^3.27.0",
-    "ursa": "^0.9.1",
     "winston": "^2.1.1",
     "ws": "^1.1.1"
   },
index 610cb16cdbaec505dc0379bb5bfc679fdcaf86d4..0f1e02ad6babb6f6364cedcba89fbe6853ffcd4c 100644 (file)
@@ -1,9 +1,9 @@
 'use strict'
 
+const crypto = require('crypto')
 const bcrypt = require('bcrypt')
 const fs = require('fs')
 const openssl = require('openssl-wrapper')
-const ursa = require('ursa')
 
 const constants = require('../initializers/constants')
 const logger = require('./logger')
@@ -16,12 +16,51 @@ const peertubeCrypto = {
   sign
 }
 
-function checkSignature (publicKey, rawData, hexSignature) {
-  const crt = ursa.createPublicKey(publicKey)
-  const isValid = crt.hashAndVerify('sha256', new Buffer(rawData).toString('hex'), hexSignature, 'hex')
+function checkSignature (publicKey, data, hexSignature) {
+  const verify = crypto.createVerify(constants.SIGNATURE_ALGORITHM)
+
+  let dataString
+  if (typeof data === 'string') {
+    dataString = data
+  } else {
+    try {
+      dataString = JSON.stringify(data)
+    } catch (err) {
+      logger.error('Cannot check signature.', { error: err })
+      return false
+    }
+  }
+
+  verify.update(dataString, 'utf8')
+
+  const isValid = verify.verify(publicKey, hexSignature, constants.SIGNATURE_ENCODING)
   return isValid
 }
 
+function sign (data) {
+  const sign = crypto.createSign(constants.SIGNATURE_ALGORITHM)
+
+  let dataString
+  if (typeof data === 'string') {
+    dataString = data
+  } else {
+    try {
+      dataString = JSON.stringify(data)
+    } catch (err) {
+      logger.error('Cannot sign data.', { error: err })
+      return ''
+    }
+  }
+
+  sign.update(dataString, 'utf8')
+
+  // TODO: make async
+  const myKey = fs.readFileSync(constants.CONFIG.STORAGE.CERT_DIR + 'peertube.key.pem')
+  const signature = sign.sign(myKey, constants.SIGNATURE_ENCODING)
+
+  return signature
+}
+
 function comparePassword (plainPassword, hashPassword, callback) {
   bcrypt.compare(plainPassword, hashPassword, function (err, isPasswordMatch) {
     if (err) return callback(err)
@@ -52,13 +91,6 @@ function cryptPassword (password, callback) {
   })
 }
 
-function sign (data) {
-  const myKey = ursa.createPrivateKey(fs.readFileSync(constants.CONFIG.STORAGE.CERT_DIR + 'peertube.key.pem'))
-  const signature = myKey.hashAndSign('sha256', data, 'utf8', 'hex')
-
-  return signature
-}
-
 // ---------------------------------------------------------------------------
 
 module.exports = peertubeCrypto
index b0cda09fe02452a520072f230288148ae5af3bbe..095b95e1c4026997f0c80278975b044ab4de1699 100644 (file)
@@ -28,31 +28,37 @@ function makeSecureRequest (params, callback) {
     url: constants.REMOTE_SCHEME.HTTP + '://' + params.toPod.host + params.path
   }
 
-  // Add data with POST requst ?
-  if (params.method === 'POST') {
-    requestParams.json = {}
-
-    // Add signature if it is specified in the params
-    if (params.sign === true) {
-      const host = constants.CONFIG.WEBSERVER.HOST
-
-      requestParams.json.signature = {
-        host,
-        signature: peertubeCrypto.sign(host)
-      }
-    }
+  if (params.method !== 'POST') {
+    return callback(new Error('Cannot make a secure request with a non POST method.'))
+  }
+
+  requestParams.json = {}
 
-    // If there are data informations
+  // Add signature if it is specified in the params
+  if (params.sign === true) {
+    const host = constants.CONFIG.WEBSERVER.HOST
+
+    let dataToSign
     if (params.data) {
-      requestParams.json.data = params.data
-      request.post(requestParams, callback)
+      dataToSign = dataToSign = params.data
     } else {
-      // No data
-      request.post(requestParams, callback)
+      // We do not have data to sign so we just take our host
+      // It is not ideal but the connection should be in HTTPS
+      dataToSign = host
     }
-  } else {
-    request.get(requestParams, callback)
+
+    requestParams.json.signature = {
+      host, // Which host we pretend to be
+      signature: peertubeCrypto.sign(dataToSign)
+    }
+  }
+
+  // If there are data informations
+  if (params.data) {
+    requestParams.json.data = params.data
   }
+
+  request.post(requestParams, callback)
 }
 
 // ---------------------------------------------------------------------------
index 6ba8a9da067ff4acd0c3de1d18b67c5a2f0a3394..a6adb75bf6714334e2b6fda470d295e2da3e93ac 100644 (file)
@@ -118,16 +118,21 @@ const REQUEST_ENDPOINTS = {
   VIDEOS: 'videos'
 }
 
-// ---------------------------------------------------------------------------
-
 const REMOTE_SCHEME = {
   HTTP: 'https',
   WS: 'wss'
 }
 
+// ---------------------------------------------------------------------------
+
+const SIGNATURE_ALGORITHM = 'RSA-SHA256'
+const SIGNATURE_ENCODING = 'hex'
+
 // Password encryption
 const BCRYPT_SALT_SIZE = 10
 
+// ---------------------------------------------------------------------------
+
 // Express static paths (router)
 const STATIC_PATHS = {
   PREVIEWS: '/static/previews/',
@@ -143,6 +148,8 @@ let STATIC_MAX_AGE = '30d'
 const THUMBNAILS_SIZE = '200x110'
 const PREVIEWS_SIZE = '640x480'
 
+// ---------------------------------------------------------------------------
+
 const USER_ROLES = {
   ADMIN: 'admin',
   USER: 'user'
@@ -180,6 +187,8 @@ module.exports = {
   REQUESTS_LIMIT,
   RETRY_REQUESTS,
   SEARCHABLE_COLUMNS,
+  SIGNATURE_ALGORITHM,
+  SIGNATURE_ENCODING,
   SORTABLE_COLUMNS,
   STATIC_MAX_AGE,
   STATIC_PATHS,
index 2aae715c47e927bb265edf892d452dbcf081d3b0..b6e6d818b91995561b98f06df70459a6315e3360 100644 (file)
@@ -23,7 +23,14 @@ function checkSignature (req, res, next) {
 
     logger.debug('Checking signature from %s.', host)
 
-    const signatureOk = peertubeCrypto.checkSignature(pod.publicKey, host, req.body.signature.signature)
+    let signatureShouldBe
+    if (req.body.data) {
+      signatureShouldBe = req.body.data
+    } else {
+      signatureShouldBe = host
+    }
+
+    const signatureOk = peertubeCrypto.checkSignature(pod.publicKey, signatureShouldBe, req.body.signature.signature)
 
     if (signatureOk === true) {
       res.locals.secure = {
index 5880a2c2c08963175e8afca9bdd9133393f18831..002232c05cb68473a7037a1d8068cbf7132e4de7 100644 (file)
@@ -11,7 +11,7 @@ function signature (req, res, next) {
   req.checkBody('signature.host', 'Should have a signature host').isURL()
   req.checkBody('signature.signature', 'Should have a signature').notEmpty()
 
-  logger.debug('Checking signature parameters', { parameters: { signatureHost: req.body.signature.host } })
+  logger.debug('Checking signature parameters', { parameters: { signature: req.body.signature } })
 
   checkErrors(req, res, next)
 }
index e18f8fe3df5978176d50e4abf3f8dfed8d97877c..bae227c058aaa902ecdba0acc682a5f743bbb64a 100644 (file)
@@ -122,7 +122,7 @@ function makeRequest (toPod, requestEndpoint, requestsToMake, callback) {
         'Error sending secure request to %s pod.',
         toPod.host,
         {
-          error: err || new Error('Status code not 20x : ' + res.statusCode)
+          error: err ? err.message : 'Status code not 20x : ' + res.statusCode
         }
       )