8e8001cd69bbd3563351ad025e176852a28cac1e
[oweals/peertube.git] / server / helpers / peertube-crypto.ts
1 import * as crypto from 'crypto'
2 import * as fs from 'fs'
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.', { error: 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.', { error: err })
55       return ''
56     }
57   }
58
59   sign.update(dataString, 'utf8')
60
61   // TODO: make async
62   const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
63   const myKey = fs.readFileSync(certPath)
64   const signature = sign.sign(myKey.toString(), SIGNATURE_ENCODING)
65
66   return signature
67 }
68
69 function comparePassword (plainPassword: string, hashPassword: string) {
70   return bcryptComparePromise(plainPassword, hashPassword)
71 }
72
73 function createCertsIfNotExist () {
74   return certsExist().then(exist => {
75     if (exist === true) {
76       return undefined
77     }
78
79     return createCerts()
80   })
81 }
82
83 function cryptPassword (password: string) {
84   return bcryptGenSaltPromise(BCRYPT_SALT_SIZE).then(salt => bcryptHashPromise(password, salt))
85 }
86
87 function getMyPrivateCert () {
88   const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
89   return readFilePromise(certPath, 'utf8')
90 }
91
92 function getMyPublicCert () {
93   const certPath = join(CONFIG.STORAGE.CERT_DIR, PUBLIC_CERT_NAME)
94   return readFilePromise(certPath, 'utf8')
95 }
96
97 // ---------------------------------------------------------------------------
98
99 export {
100   checkSignature,
101   comparePassword,
102   createCertsIfNotExist,
103   cryptPassword,
104   getMyPrivateCert,
105   getMyPublicCert,
106   sign
107 }
108
109 // ---------------------------------------------------------------------------
110
111 function certsExist () {
112   const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
113
114   // If there is an error the certificates do not exist
115   return accessPromise(certPath)
116     .then(() => true)
117     .catch(() => false)
118 }
119
120 function createCerts () {
121   return certsExist().then(exist => {
122     if (exist === true) {
123       const errorMessage = 'Certs already exist.'
124       logger.warning(errorMessage)
125       throw new Error(errorMessage)
126     }
127
128     logger.info('Generating a RSA key...')
129
130     const privateCertPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
131     const genRsaOptions = {
132       'out': privateCertPath,
133       '2048': false
134     }
135     return opensslExecPromise('genrsa', genRsaOptions)
136       .then(() => {
137         logger.info('RSA key generated.')
138         logger.info('Managing public key...')
139
140         const publicCertPath = join(CONFIG.STORAGE.CERT_DIR, 'peertube.pub')
141         const rsaOptions = {
142           'in': privateCertPath,
143           'pubout': true,
144           'out': publicCertPath
145         }
146         return opensslExecPromise('rsa', rsaOptions)
147           .then(() => logger.info('Public key managed.'))
148           .catch(err => {
149             logger.error('Cannot create public key on this pod.')
150             throw err
151           })
152       })
153       .catch(err => {
154         logger.error('Cannot create private key on this pod.')
155         throw err
156       })
157   })
158 }