47f0243e7e5ee5bb4daeb5640d7b848659a62738
[oweals/peertube.git] / server / helpers / peertube-crypto.ts
1 import * as crypto from 'crypto'
2 import { join } from 'path'
3
4 import {
5   SIGNATURE_ALGORITHM,
6   SIGNATURE_ENCODING,
7   PRIVATE_CERT_NAME,
8   CONFIG,
9   BCRYPT_SALT_SIZE,
10   PUBLIC_CERT_NAME
11 } from '../initializers'
12 import {
13   readFilePromise,
14   bcryptComparePromise,
15   bcryptGenSaltPromise,
16   bcryptHashPromise,
17   accessPromise,
18   opensslExecPromise
19 } from './core-utils'
20 import { logger } from './logger'
21
22 function checkSignature (publicKey: string, data: string, hexSignature: string) {
23   const verify = crypto.createVerify(SIGNATURE_ALGORITHM)
24
25   let dataString
26   if (typeof data === 'string') {
27     dataString = data
28   } else {
29     try {
30       dataString = JSON.stringify(data)
31     } catch (err) {
32       logger.error('Cannot check signature.', err)
33       return false
34     }
35   }
36
37   verify.update(dataString, 'utf8')
38
39   const isValid = verify.verify(publicKey, hexSignature, SIGNATURE_ENCODING)
40   return isValid
41 }
42
43 async function sign (data: string|Object) {
44   const sign = crypto.createSign(SIGNATURE_ALGORITHM)
45
46   let dataString: string
47   if (typeof data === 'string') {
48     dataString = data
49   } else {
50     try {
51       dataString = JSON.stringify(data)
52     } catch (err) {
53       logger.error('Cannot sign data.', err)
54       return ''
55     }
56   }
57
58   sign.update(dataString, 'utf8')
59
60   const myKey = await getMyPrivateCert()
61   return await sign.sign(myKey, SIGNATURE_ENCODING)
62 }
63
64 function comparePassword (plainPassword: string, hashPassword: string) {
65   return bcryptComparePromise(plainPassword, hashPassword)
66 }
67
68 async function createCertsIfNotExist () {
69   const exist = await certsExist()
70   if (exist === true) {
71     return undefined
72   }
73
74   return await createCerts()
75 }
76
77 async function cryptPassword (password: string) {
78   const salt = await bcryptGenSaltPromise(BCRYPT_SALT_SIZE)
79
80   return await bcryptHashPromise(password, salt)
81 }
82
83 function getMyPrivateCert () {
84   const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
85   return readFilePromise(certPath, 'utf8')
86 }
87
88 function getMyPublicCert () {
89   const certPath = join(CONFIG.STORAGE.CERT_DIR, PUBLIC_CERT_NAME)
90   return readFilePromise(certPath, 'utf8')
91 }
92
93 // ---------------------------------------------------------------------------
94
95 export {
96   checkSignature,
97   comparePassword,
98   createCertsIfNotExist,
99   cryptPassword,
100   getMyPrivateCert,
101   getMyPublicCert,
102   sign
103 }
104
105 // ---------------------------------------------------------------------------
106
107 async function certsExist () {
108   const certPath = join(CONFIG.STORAGE.CERT_DIR, PRIVATE_CERT_NAME)
109
110   // If there is an error the certificates do not exist
111   try {
112     await accessPromise(certPath)
113
114     return true
115   } catch {
116     return false
117   }
118 }
119
120 async function createCerts () {
121   const exist = await certsExist()
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
136   await opensslExecPromise('genrsa', genRsaOptions)
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
147   await opensslExecPromise('rsa', rsaOptions)
148 }