Server: use crypto instead of ursa for pod signature
This commit is contained in:
parent
b981a525c3
commit
bdfbd4f162
7 changed files with 94 additions and 41 deletions
|
@ -69,7 +69,6 @@
|
||||||
"safe-buffer": "^5.0.1",
|
"safe-buffer": "^5.0.1",
|
||||||
"scripty": "^1.5.0",
|
"scripty": "^1.5.0",
|
||||||
"sequelize": "^3.27.0",
|
"sequelize": "^3.27.0",
|
||||||
"ursa": "^0.9.1",
|
|
||||||
"winston": "^2.1.1",
|
"winston": "^2.1.1",
|
||||||
"ws": "^1.1.1"
|
"ws": "^1.1.1"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const crypto = require('crypto')
|
||||||
const bcrypt = require('bcrypt')
|
const bcrypt = require('bcrypt')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const openssl = require('openssl-wrapper')
|
const openssl = require('openssl-wrapper')
|
||||||
const ursa = require('ursa')
|
|
||||||
|
|
||||||
const constants = require('../initializers/constants')
|
const constants = require('../initializers/constants')
|
||||||
const logger = require('./logger')
|
const logger = require('./logger')
|
||||||
|
@ -16,12 +16,51 @@ const peertubeCrypto = {
|
||||||
sign
|
sign
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkSignature (publicKey, rawData, hexSignature) {
|
function checkSignature (publicKey, data, hexSignature) {
|
||||||
const crt = ursa.createPublicKey(publicKey)
|
const verify = crypto.createVerify(constants.SIGNATURE_ALGORITHM)
|
||||||
const isValid = crt.hashAndVerify('sha256', new Buffer(rawData).toString('hex'), hexSignature, 'hex')
|
|
||||||
|
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
|
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) {
|
function comparePassword (plainPassword, hashPassword, callback) {
|
||||||
bcrypt.compare(plainPassword, hashPassword, function (err, isPasswordMatch) {
|
bcrypt.compare(plainPassword, hashPassword, function (err, isPasswordMatch) {
|
||||||
if (err) return callback(err)
|
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
|
module.exports = peertubeCrypto
|
||||||
|
|
|
@ -28,31 +28,37 @@ function makeSecureRequest (params, callback) {
|
||||||
url: constants.REMOTE_SCHEME.HTTP + '://' + params.toPod.host + params.path
|
url: constants.REMOTE_SCHEME.HTTP + '://' + params.toPod.host + params.path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add data with POST requst ?
|
if (params.method !== 'POST') {
|
||||||
if (params.method === 'POST') {
|
return callback(new Error('Cannot make a secure request with a non POST method.'))
|
||||||
|
}
|
||||||
|
|
||||||
requestParams.json = {}
|
requestParams.json = {}
|
||||||
|
|
||||||
// Add signature if it is specified in the params
|
// Add signature if it is specified in the params
|
||||||
if (params.sign === true) {
|
if (params.sign === true) {
|
||||||
const host = constants.CONFIG.WEBSERVER.HOST
|
const host = constants.CONFIG.WEBSERVER.HOST
|
||||||
|
|
||||||
|
let dataToSign
|
||||||
|
if (params.data) {
|
||||||
|
dataToSign = dataToSign = params.data
|
||||||
|
} else {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
requestParams.json.signature = {
|
requestParams.json.signature = {
|
||||||
host,
|
host, // Which host we pretend to be
|
||||||
signature: peertubeCrypto.sign(host)
|
signature: peertubeCrypto.sign(dataToSign)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are data informations
|
// If there are data informations
|
||||||
if (params.data) {
|
if (params.data) {
|
||||||
requestParams.json.data = params.data
|
requestParams.json.data = params.data
|
||||||
request.post(requestParams, callback)
|
|
||||||
} else {
|
|
||||||
// No data
|
|
||||||
request.post(requestParams, callback)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
request.get(requestParams, callback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request.post(requestParams, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -118,16 +118,21 @@ const REQUEST_ENDPOINTS = {
|
||||||
VIDEOS: 'videos'
|
VIDEOS: 'videos'
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const REMOTE_SCHEME = {
|
const REMOTE_SCHEME = {
|
||||||
HTTP: 'https',
|
HTTP: 'https',
|
||||||
WS: 'wss'
|
WS: 'wss'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const SIGNATURE_ALGORITHM = 'RSA-SHA256'
|
||||||
|
const SIGNATURE_ENCODING = 'hex'
|
||||||
|
|
||||||
// Password encryption
|
// Password encryption
|
||||||
const BCRYPT_SALT_SIZE = 10
|
const BCRYPT_SALT_SIZE = 10
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Express static paths (router)
|
// Express static paths (router)
|
||||||
const STATIC_PATHS = {
|
const STATIC_PATHS = {
|
||||||
PREVIEWS: '/static/previews/',
|
PREVIEWS: '/static/previews/',
|
||||||
|
@ -143,6 +148,8 @@ let STATIC_MAX_AGE = '30d'
|
||||||
const THUMBNAILS_SIZE = '200x110'
|
const THUMBNAILS_SIZE = '200x110'
|
||||||
const PREVIEWS_SIZE = '640x480'
|
const PREVIEWS_SIZE = '640x480'
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const USER_ROLES = {
|
const USER_ROLES = {
|
||||||
ADMIN: 'admin',
|
ADMIN: 'admin',
|
||||||
USER: 'user'
|
USER: 'user'
|
||||||
|
@ -180,6 +187,8 @@ module.exports = {
|
||||||
REQUESTS_LIMIT,
|
REQUESTS_LIMIT,
|
||||||
RETRY_REQUESTS,
|
RETRY_REQUESTS,
|
||||||
SEARCHABLE_COLUMNS,
|
SEARCHABLE_COLUMNS,
|
||||||
|
SIGNATURE_ALGORITHM,
|
||||||
|
SIGNATURE_ENCODING,
|
||||||
SORTABLE_COLUMNS,
|
SORTABLE_COLUMNS,
|
||||||
STATIC_MAX_AGE,
|
STATIC_MAX_AGE,
|
||||||
STATIC_PATHS,
|
STATIC_PATHS,
|
||||||
|
|
|
@ -23,7 +23,14 @@ function checkSignature (req, res, next) {
|
||||||
|
|
||||||
logger.debug('Checking signature from %s.', host)
|
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) {
|
if (signatureOk === true) {
|
||||||
res.locals.secure = {
|
res.locals.secure = {
|
||||||
|
|
|
@ -11,7 +11,7 @@ function signature (req, res, next) {
|
||||||
req.checkBody('signature.host', 'Should have a signature host').isURL()
|
req.checkBody('signature.host', 'Should have a signature host').isURL()
|
||||||
req.checkBody('signature.signature', 'Should have a signature').notEmpty()
|
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)
|
checkErrors(req, res, next)
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,7 @@ function makeRequest (toPod, requestEndpoint, requestsToMake, callback) {
|
||||||
'Error sending secure request to %s pod.',
|
'Error sending secure request to %s pod.',
|
||||||
toPod.host,
|
toPod.host,
|
||||||
{
|
{
|
||||||
error: err || new Error('Status code not 20x : ' + res.statusCode)
|
error: err ? err.message : 'Status code not 20x : ' + res.statusCode
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue