Server: generate magnet uri on the fly
[oweals/peertube.git] / server / helpers / peertube-crypto.js
1 'use strict'
2
3 const bcrypt = require('bcrypt')
4 const crypto = require('crypto')
5 const fs = require('fs')
6 const openssl = require('openssl-wrapper')
7 const ursa = require('ursa')
8
9 const constants = require('../initializers/constants')
10 const logger = require('./logger')
11
12 const algorithm = 'aes-256-ctr'
13
14 const peertubeCrypto = {
15   checkSignature,
16   comparePassword,
17   createCertsIfNotExist,
18   cryptPassword,
19   decrypt,
20   encrypt,
21   sign
22 }
23
24 function checkSignature (publicKey, rawData, hexSignature) {
25   const crt = ursa.createPublicKey(publicKey)
26   const isValid = crt.hashAndVerify('sha256', new Buffer(rawData).toString('hex'), hexSignature, 'hex')
27   return isValid
28 }
29
30 function comparePassword (plainPassword, hashPassword, callback) {
31   bcrypt.compare(plainPassword, hashPassword, function (err, isPasswordMatch) {
32     if (err) return callback(err)
33
34     return callback(null, isPasswordMatch)
35   })
36 }
37
38 function createCertsIfNotExist (callback) {
39   certsExist(function (exist) {
40     if (exist === true) {
41       return callback(null)
42     }
43
44     createCerts(function (err) {
45       return callback(err)
46     })
47   })
48 }
49
50 function cryptPassword (password, callback) {
51   bcrypt.genSalt(constants.BCRYPT_SALT_SIZE, function (err, salt) {
52     if (err) return callback(err)
53
54     bcrypt.hash(password, salt, function (err, hash) {
55       return callback(err, hash)
56     })
57   })
58 }
59
60 function decrypt (key, data, callback) {
61   fs.readFile(constants.CONFIG.STORAGE.CERT_DIR + 'peertube.key.pem', function (err, file) {
62     if (err) return callback(err)
63
64     const myPrivateKey = ursa.createPrivateKey(file)
65     const decryptedKey = myPrivateKey.decrypt(key, 'hex', 'utf8')
66     const decryptedData = symetricDecrypt(data, decryptedKey)
67
68     return callback(null, decryptedData)
69   })
70 }
71
72 function encrypt (publicKey, data, callback) {
73   const crt = ursa.createPublicKey(publicKey)
74
75   symetricEncrypt(data, function (err, dataEncrypted) {
76     if (err) return callback(err)
77
78     const key = crt.encrypt(dataEncrypted.password, 'utf8', 'hex')
79     const encrypted = {
80       data: dataEncrypted.crypted,
81       key: key
82     }
83
84     callback(null, encrypted)
85   })
86 }
87
88 function sign (data) {
89   const myKey = ursa.createPrivateKey(fs.readFileSync(constants.CONFIG.STORAGE.CERT_DIR + 'peertube.key.pem'))
90   const signature = myKey.hashAndSign('sha256', data, 'utf8', 'hex')
91
92   return signature
93 }
94
95 // ---------------------------------------------------------------------------
96
97 module.exports = peertubeCrypto
98
99 // ---------------------------------------------------------------------------
100
101 function certsExist (callback) {
102   fs.exists(constants.CONFIG.STORAGE.CERT_DIR + 'peertube.key.pem', function (exists) {
103     return callback(exists)
104   })
105 }
106
107 function createCerts (callback) {
108   certsExist(function (exist) {
109     if (exist === true) {
110       const string = 'Certs already exist.'
111       logger.warning(string)
112       return callback(new Error(string))
113     }
114
115     logger.info('Generating a RSA key...')
116
117     let options = {
118       'out': constants.CONFIG.STORAGE.CERT_DIR + 'peertube.key.pem',
119       '2048': false
120     }
121     openssl.exec('genrsa', options, function (err) {
122       if (err) {
123         logger.error('Cannot create private key on this pod.')
124         return callback(err)
125       }
126       logger.info('RSA key generated.')
127
128       options = {
129         'in': constants.CONFIG.STORAGE.CERT_DIR + 'peertube.key.pem',
130         'pubout': true,
131         'out': constants.CONFIG.STORAGE.CERT_DIR + 'peertube.pub'
132       }
133       logger.info('Manage public key...')
134       openssl.exec('rsa', options, function (err) {
135         if (err) {
136           logger.error('Cannot create public key on this pod.')
137           return callback(err)
138         }
139
140         logger.info('Public key managed.')
141         return callback(null)
142       })
143     })
144   })
145 }
146
147 function generatePassword (callback) {
148   crypto.randomBytes(32, function (err, buf) {
149     if (err) return callback(err)
150
151     callback(null, buf.toString('utf8'))
152   })
153 }
154
155 function symetricDecrypt (text, password) {
156   const decipher = crypto.createDecipher(algorithm, password)
157   let dec = decipher.update(text, 'hex', 'utf8')
158   dec += decipher.final('utf8')
159   return dec
160 }
161
162 function symetricEncrypt (text, callback) {
163   generatePassword(function (err, password) {
164     if (err) return callback(err)
165
166     const cipher = crypto.createCipher(algorithm, password)
167     let crypted = cipher.update(text, 'utf8', 'hex')
168     crypted += cipher.final('hex')
169     callback(null, { crypted: crypted, password: password })
170   })
171 }