Add ability to limit user registrations
[oweals/peertube.git] / server / helpers / peertube-crypto.ts
1 import * as crypto from 'crypto'
2 import * as Promise from 'bluebird'
3 import { join } from 'path'
4
5 import {
6   SIGNATURE_ALGORITHM,
7   SIGNATURE_ENCODING,
8   PRIVATE_CERT_NAME,
9   CONFIG,
10   BCRYPT_SALT_SIZE,
11   PUBLIC_CERT_NAME
12 } from '../initializers'
13 import {
14   readFilePromise,
15   bcryptComparePromise,
16   bcryptGenSaltPromise,
17   bcryptHashPromise,
18   accessPromise,
19   opensslExecPromise
20 } from './core-utils'
21 import { logger } from './logger'
22
23 function checkSignature (publicKey: string, data: string, hexSignature: string) {
24   const verify = crypto.createVerify(SIGNATURE_ALGORITHM)
25
26   let dataString
27   if (typeof data === 'string') {
28     dataString = data
29   } else {
30     try {
31       dataString = JSON.stringify(data)
32     } catch (err) {
33       logger.error('Cannot check signature.', err)
34       return false
35     }
36   }
37
38   verify.update(dataString, 'utf8')
39
40   const isValid = verify.verify(publicKey, hexSignature, SIGNATURE_ENCODING)
41   return isValid
42 }
43
44 function sign (data: string|Object) {
45   const sign = crypto.createSign(SIGNATURE_ALGORITHM)
46
47   let dataString: string
48   if (typeof data === 'string') {
49     dataString = data
50   } else {
51     try {
52       dataString = JSON.stringify(data)
53     } catch (err) {
54       logger.error('Cannot sign data.', err)
55       return Promise.resolve('')
56     }
57   }
58
59   sign.update(dataString, 'utf8')
60
61   return getMyPrivateCert().then(myKey => {
62     return sign.sign(myKey, SIGNATURE_ENCODING)
63   })
64 }
65
66 function comparePassword (plainPassword: string, hashPassword: string) {
67   return bcryptComparePromise(plainPassword, hashPassword)
68 }
69
70 function createCertsIfNotExist () {
71   return certsExist().then(exist => {
72     if (exist === true) {
73       return undefined
74     }
75
76     return createCerts()
77   })
78 }
79
80 function cryptPassword (password: string) {
81   return bcryptGenSaltPromise(BCRYPT_SALT_SIZE).then(salt => bcryptHashPromise(password, salt))
82 }
83
84 function getMyPrivateCert () {
85   const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
86   return readFilePromise(certPath, 'utf8')
87 }
88
89 function getMyPublicCert () {
90   const certPath = join(CONFIG.STORAGE.CERT_DIR, PUBLIC_CERT_NAME)
91   return readFilePromise(certPath, 'utf8')
92 }
93
94 // ---------------------------------------------------------------------------
95
96 export {
97   checkSignature,
98   comparePassword,
99   createCertsIfNotExist,
100   cryptPassword,
101   getMyPrivateCert,
102   getMyPublicCert,
103   sign
104 }
105
106 // ---------------------------------------------------------------------------
107
108 function certsExist () {
109   const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
110
111   // If there is an error the certificates do not exist
112   return accessPromise(certPath)
113     .then(() => true)
114     .catch(() => false)
115 }
116
117 function createCerts () {
118   return certsExist().then(exist => {
119     if (exist === true) {
120       const errorMessage = 'Certs already exist.'
121       logger.warning(errorMessage)
122       throw new Error(errorMessage)
123     }
124
125     logger.info('Generating a RSA key...')
126
127     const privateCertPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
128     const genRsaOptions = {
129       'out': privateCertPath,
130       '2048': false
131     }
132     return opensslExecPromise('genrsa', genRsaOptions)
133       .then(() => {
134         logger.info('RSA key generated.')
135         logger.info('Managing public key...')
136
137         const publicCertPath = join(CONFIG.STORAGE.CERT_DIR, 'peertube.pub')
138         const rsaOptions = {
139           'in': privateCertPath,
140           'pubout': true,
141           'out': publicCertPath
142         }
143         return opensslExecPromise('rsa', rsaOptions)
144           .then(() => logger.info('Public key managed.'))
145           .catch(err => {
146             logger.error('Cannot create public key on this pod.')
147             throw err
148           })
149       })
150       .catch(err => {
151         logger.error('Cannot create private key on this pod.')
152         throw err
153       })
154   })
155 }