diff options
author | Chocobozzz <me@florianbigard.com> | 2022-10-10 11:12:23 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2022-10-10 11:12:23 +0200 |
commit | a3e5f804ad821f6979e8735b0569b1209986fedc (patch) | |
tree | 5b34a6bd6b3cb1c5e3eed32a72d02922100d53dc /server/helpers | |
parent | a0da6f90d16027b385a67da6a5691b163626a363 (diff) | |
download | PeerTube-a3e5f804ad821f6979e8735b0569b1209986fedc.tar.gz PeerTube-a3e5f804ad821f6979e8735b0569b1209986fedc.tar.zst PeerTube-a3e5f804ad821f6979e8735b0569b1209986fedc.zip |
Encrypt OTP secret
Diffstat (limited to 'server/helpers')
-rw-r--r-- | server/helpers/core-utils.ts | 14 | ||||
-rw-r--r-- | server/helpers/otp.ts | 10 | ||||
-rw-r--r-- | server/helpers/peertube-crypto.ts | 47 |
3 files changed, 63 insertions, 8 deletions
diff --git a/server/helpers/core-utils.ts b/server/helpers/core-utils.ts index c762f6a29..73bd994c1 100644 --- a/server/helpers/core-utils.ts +++ b/server/helpers/core-utils.ts | |||
@@ -6,7 +6,7 @@ | |||
6 | */ | 6 | */ |
7 | 7 | ||
8 | import { exec, ExecOptions } from 'child_process' | 8 | import { exec, ExecOptions } from 'child_process' |
9 | import { ED25519KeyPairOptions, generateKeyPair, randomBytes, RSAKeyPairOptions } from 'crypto' | 9 | import { ED25519KeyPairOptions, generateKeyPair, randomBytes, RSAKeyPairOptions, scrypt } from 'crypto' |
10 | import { truncate } from 'lodash' | 10 | import { truncate } from 'lodash' |
11 | import { pipeline } from 'stream' | 11 | import { pipeline } from 'stream' |
12 | import { URL } from 'url' | 12 | import { URL } from 'url' |
@@ -311,7 +311,17 @@ function promisify2<T, U, A> (func: (arg1: T, arg2: U, cb: (err: any, result: A) | |||
311 | } | 311 | } |
312 | } | 312 | } |
313 | 313 | ||
314 | // eslint-disable-next-line max-len | ||
315 | function promisify3<T, U, V, A> (func: (arg1: T, arg2: U, arg3: V, cb: (err: any, result: A) => void) => void): (arg1: T, arg2: U, arg3: V) => Promise<A> { | ||
316 | return function promisified (arg1: T, arg2: U, arg3: V): Promise<A> { | ||
317 | return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => { | ||
318 | func.apply(null, [ arg1, arg2, arg3, (err: any, res: A) => err ? reject(err) : resolve(res) ]) | ||
319 | }) | ||
320 | } | ||
321 | } | ||
322 | |||
314 | const randomBytesPromise = promisify1<number, Buffer>(randomBytes) | 323 | const randomBytesPromise = promisify1<number, Buffer>(randomBytes) |
324 | const scryptPromise = promisify3<string, string, number, Buffer>(scrypt) | ||
315 | const execPromise2 = promisify2<string, any, string>(exec) | 325 | const execPromise2 = promisify2<string, any, string>(exec) |
316 | const execPromise = promisify1<string, string>(exec) | 326 | const execPromise = promisify1<string, string>(exec) |
317 | const pipelinePromise = promisify(pipeline) | 327 | const pipelinePromise = promisify(pipeline) |
@@ -339,6 +349,8 @@ export { | |||
339 | promisify1, | 349 | promisify1, |
340 | promisify2, | 350 | promisify2, |
341 | 351 | ||
352 | scryptPromise, | ||
353 | |||
342 | randomBytesPromise, | 354 | randomBytesPromise, |
343 | 355 | ||
344 | generateRSAKeyPairPromise, | 356 | generateRSAKeyPairPromise, |
diff --git a/server/helpers/otp.ts b/server/helpers/otp.ts index a13edc5e2..a32cc9621 100644 --- a/server/helpers/otp.ts +++ b/server/helpers/otp.ts | |||
@@ -1,11 +1,15 @@ | |||
1 | import { Secret, TOTP } from 'otpauth' | 1 | import { Secret, TOTP } from 'otpauth' |
2 | import { CONFIG } from '@server/initializers/config' | ||
2 | import { WEBSERVER } from '@server/initializers/constants' | 3 | import { WEBSERVER } from '@server/initializers/constants' |
4 | import { decrypt } from './peertube-crypto' | ||
3 | 5 | ||
4 | function isOTPValid (options: { | 6 | async function isOTPValid (options: { |
5 | secret: string | 7 | encryptedSecret: string |
6 | token: string | 8 | token: string |
7 | }) { | 9 | }) { |
8 | const { token, secret } = options | 10 | const { token, encryptedSecret } = options |
11 | |||
12 | const secret = await decrypt(encryptedSecret, CONFIG.SECRETS.PEERTUBE) | ||
9 | 13 | ||
10 | const totp = new TOTP({ | 14 | const totp = new TOTP({ |
11 | ...baseOTPOptions(), | 15 | ...baseOTPOptions(), |
diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts index dcf47ce76..ae7d11800 100644 --- a/server/helpers/peertube-crypto.ts +++ b/server/helpers/peertube-crypto.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import { compare, genSalt, hash } from 'bcrypt' | 1 | import { compare, genSalt, hash } from 'bcrypt' |
2 | import { createSign, createVerify } from 'crypto' | 2 | import { createCipheriv, createDecipheriv, createSign, createVerify } from 'crypto' |
3 | import { Request } from 'express' | 3 | import { Request } from 'express' |
4 | import { cloneDeep } from 'lodash' | 4 | import { cloneDeep } from 'lodash' |
5 | import { sha256 } from '@shared/extra-utils' | 5 | import { sha256 } from '@shared/extra-utils' |
6 | import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants' | 6 | import { BCRYPT_SALT_SIZE, ENCRYPTION, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants' |
7 | import { MActor } from '../types/models' | 7 | import { MActor } from '../types/models' |
8 | import { generateRSAKeyPairPromise, promisify1, promisify2 } from './core-utils' | 8 | import { generateRSAKeyPairPromise, promisify1, promisify2, randomBytesPromise, scryptPromise } from './core-utils' |
9 | import { jsonld } from './custom-jsonld-signature' | 9 | import { jsonld } from './custom-jsonld-signature' |
10 | import { logger } from './logger' | 10 | import { logger } from './logger' |
11 | 11 | ||
@@ -21,7 +21,9 @@ function createPrivateAndPublicKeys () { | |||
21 | return generateRSAKeyPairPromise(PRIVATE_RSA_KEY_SIZE) | 21 | return generateRSAKeyPairPromise(PRIVATE_RSA_KEY_SIZE) |
22 | } | 22 | } |
23 | 23 | ||
24 | // --------------------------------------------------------------------------- | ||
24 | // User password checks | 25 | // User password checks |
26 | // --------------------------------------------------------------------------- | ||
25 | 27 | ||
26 | function comparePassword (plainPassword: string, hashPassword: string) { | 28 | function comparePassword (plainPassword: string, hashPassword: string) { |
27 | if (!plainPassword) return Promise.resolve(false) | 29 | if (!plainPassword) return Promise.resolve(false) |
@@ -35,7 +37,9 @@ async function cryptPassword (password: string) { | |||
35 | return bcryptHashPromise(password, salt) | 37 | return bcryptHashPromise(password, salt) |
36 | } | 38 | } |
37 | 39 | ||
40 | // --------------------------------------------------------------------------- | ||
38 | // HTTP Signature | 41 | // HTTP Signature |
42 | // --------------------------------------------------------------------------- | ||
39 | 43 | ||
40 | function isHTTPSignatureDigestValid (rawBody: Buffer, req: Request): boolean { | 44 | function isHTTPSignatureDigestValid (rawBody: Buffer, req: Request): boolean { |
41 | if (req.headers[HTTP_SIGNATURE.HEADER_NAME] && req.headers['digest']) { | 45 | if (req.headers[HTTP_SIGNATURE.HEADER_NAME] && req.headers['digest']) { |
@@ -64,7 +68,9 @@ function parseHTTPSignature (req: Request, clockSkew?: number) { | |||
64 | return parsed | 68 | return parsed |
65 | } | 69 | } |
66 | 70 | ||
71 | // --------------------------------------------------------------------------- | ||
67 | // JSONLD | 72 | // JSONLD |
73 | // --------------------------------------------------------------------------- | ||
68 | 74 | ||
69 | function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> { | 75 | function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> { |
70 | if (signedDocument.signature.type === 'RsaSignature2017') { | 76 | if (signedDocument.signature.type === 'RsaSignature2017') { |
@@ -114,6 +120,8 @@ async function signJsonLDObject <T> (byActor: MActor, data: T) { | |||
114 | return Object.assign(data, { signature }) | 120 | return Object.assign(data, { signature }) |
115 | } | 121 | } |
116 | 122 | ||
123 | // --------------------------------------------------------------------------- | ||
124 | |||
117 | function buildDigest (body: any) { | 125 | function buildDigest (body: any) { |
118 | const rawBody = typeof body === 'string' ? body : JSON.stringify(body) | 126 | const rawBody = typeof body === 'string' ? body : JSON.stringify(body) |
119 | 127 | ||
@@ -121,6 +129,34 @@ function buildDigest (body: any) { | |||
121 | } | 129 | } |
122 | 130 | ||
123 | // --------------------------------------------------------------------------- | 131 | // --------------------------------------------------------------------------- |
132 | // Encryption | ||
133 | // --------------------------------------------------------------------------- | ||
134 | |||
135 | async function encrypt (str: string, secret: string) { | ||
136 | const iv = await randomBytesPromise(ENCRYPTION.IV) | ||
137 | |||
138 | const key = await scryptPromise(secret, ENCRYPTION.SALT, 32) | ||
139 | const cipher = createCipheriv(ENCRYPTION.ALGORITHM, key, iv) | ||
140 | |||
141 | let encrypted = iv.toString(ENCRYPTION.ENCODING) + ':' | ||
142 | encrypted += cipher.update(str, 'utf8', ENCRYPTION.ENCODING) | ||
143 | encrypted += cipher.final(ENCRYPTION.ENCODING) | ||
144 | |||
145 | return encrypted | ||
146 | } | ||
147 | |||
148 | async function decrypt (encryptedArg: string, secret: string) { | ||
149 | const [ ivStr, encryptedStr ] = encryptedArg.split(':') | ||
150 | |||
151 | const iv = Buffer.from(ivStr, 'hex') | ||
152 | const key = await scryptPromise(secret, ENCRYPTION.SALT, 32) | ||
153 | |||
154 | const decipher = createDecipheriv(ENCRYPTION.ALGORITHM, key, iv) | ||
155 | |||
156 | return decipher.update(encryptedStr, ENCRYPTION.ENCODING, 'utf8') + decipher.final('utf8') | ||
157 | } | ||
158 | |||
159 | // --------------------------------------------------------------------------- | ||
124 | 160 | ||
125 | export { | 161 | export { |
126 | isHTTPSignatureDigestValid, | 162 | isHTTPSignatureDigestValid, |
@@ -131,7 +167,10 @@ export { | |||
131 | comparePassword, | 167 | comparePassword, |
132 | createPrivateAndPublicKeys, | 168 | createPrivateAndPublicKeys, |
133 | cryptPassword, | 169 | cryptPassword, |
134 | signJsonLDObject | 170 | signJsonLDObject, |
171 | |||
172 | encrypt, | ||
173 | decrypt | ||
135 | } | 174 | } |
136 | 175 | ||
137 | // --------------------------------------------------------------------------- | 176 | // --------------------------------------------------------------------------- |