]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - server/helpers/peertube-crypto.ts
Translated using Weblate (German)
[github/Chocobozzz/PeerTube.git] / server / helpers / peertube-crypto.ts
... / ...
CommitLineData
1import { compare, genSalt, hash } from 'bcrypt'
2import { createSign, createVerify } from 'crypto'
3import { Request } from 'express'
4import { cloneDeep } from 'lodash'
5import { sha256 } from '@shared/extra-utils'
6import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants'
7import { MActor } from '../types/models'
8import { generateRSAKeyPairPromise, promisify1, promisify2 } from './core-utils'
9import { jsonld } from './custom-jsonld-signature'
10import { logger } from './logger'
11
12const bcryptComparePromise = promisify2<any, string, boolean>(compare)
13const bcryptGenSaltPromise = promisify1<number, string>(genSalt)
14const bcryptHashPromise = promisify2<any, string | number, string>(hash)
15
16const httpSignature = require('@peertube/http-signature')
17
18function createPrivateAndPublicKeys () {
19 logger.info('Generating a RSA key...')
20
21 return generateRSAKeyPairPromise(PRIVATE_RSA_KEY_SIZE)
22}
23
24// User password checks
25
26function comparePassword (plainPassword: string, hashPassword: string) {
27 return bcryptComparePromise(plainPassword, hashPassword)
28}
29
30async function cryptPassword (password: string) {
31 const salt = await bcryptGenSaltPromise(BCRYPT_SALT_SIZE)
32
33 return bcryptHashPromise(password, salt)
34}
35
36// HTTP Signature
37
38function isHTTPSignatureDigestValid (rawBody: Buffer, req: Request): boolean {
39 if (req.headers[HTTP_SIGNATURE.HEADER_NAME] && req.headers['digest']) {
40 return buildDigest(rawBody.toString()) === req.headers['digest']
41 }
42
43 return true
44}
45
46function isHTTPSignatureVerified (httpSignatureParsed: any, actor: MActor): boolean {
47 return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true
48}
49
50function parseHTTPSignature (req: Request, clockSkew?: number) {
51 const requiredHeaders = req.method === 'POST'
52 ? [ '(request-target)', 'host', 'digest' ]
53 : [ '(request-target)', 'host' ]
54
55 const parsed = httpSignature.parse(req, { clockSkew, headers: requiredHeaders })
56
57 const parsedHeaders = parsed.params.headers
58 if (!parsedHeaders.includes('date') && !parsedHeaders.includes('(created)')) {
59 throw new Error(`date or (created) must be included in signature`)
60 }
61
62 return parsed
63}
64
65// JSONLD
66
67function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> {
68 if (signedDocument.signature.type === 'RsaSignature2017') {
69 return isJsonLDRSA2017Verified(fromActor, signedDocument)
70 }
71
72 logger.warn('Unknown JSON LD signature %s.', signedDocument.signature.type, signedDocument)
73
74 return Promise.resolve(false)
75}
76
77// Backward compatibility with "other" implementations
78async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any) {
79 const [ documentHash, optionsHash ] = await Promise.all([
80 createDocWithoutSignatureHash(signedDocument),
81 createSignatureHash(signedDocument.signature)
82 ])
83
84 const toVerify = optionsHash + documentHash
85
86 const verify = createVerify('RSA-SHA256')
87 verify.update(toVerify, 'utf8')
88
89 return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64')
90}
91
92async function signJsonLDObject <T> (byActor: MActor, data: T) {
93 const signature = {
94 type: 'RsaSignature2017',
95 creator: byActor.url,
96 created: new Date().toISOString()
97 }
98
99 const [ documentHash, optionsHash ] = await Promise.all([
100 createDocWithoutSignatureHash(data),
101 createSignatureHash(signature)
102 ])
103
104 const toSign = optionsHash + documentHash
105
106 const sign = createSign('RSA-SHA256')
107 sign.update(toSign, 'utf8')
108
109 const signatureValue = sign.sign(byActor.privateKey, 'base64')
110 Object.assign(signature, { signatureValue })
111
112 return Object.assign(data, { signature })
113}
114
115function buildDigest (body: any) {
116 const rawBody = typeof body === 'string' ? body : JSON.stringify(body)
117
118 return 'SHA-256=' + sha256(rawBody, 'base64')
119}
120
121// ---------------------------------------------------------------------------
122
123export {
124 isHTTPSignatureDigestValid,
125 parseHTTPSignature,
126 isHTTPSignatureVerified,
127 buildDigest,
128 isJsonLDSignatureVerified,
129 comparePassword,
130 createPrivateAndPublicKeys,
131 cryptPassword,
132 signJsonLDObject
133}
134
135// ---------------------------------------------------------------------------
136
137function hashObject (obj: any): Promise<any> {
138 return jsonld.promises
139 .normalize(obj, {
140 algorithm: 'URDNA2015',
141 format: 'application/n-quads'
142 })
143 .then(res => sha256(res))
144}
145
146function createSignatureHash (signature: any) {
147 const signatureCopy = cloneDeep(signature)
148 Object.assign(signatureCopy, {
149 '@context': [
150 'https://w3id.org/security/v1',
151 { RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' }
152 ]
153 })
154
155 delete signatureCopy.type
156 delete signatureCopy.id
157 delete signatureCopy.signatureValue
158
159 return hashObject(signatureCopy)
160}
161
162function createDocWithoutSignatureHash (doc: any) {
163 const docWithoutSignature = cloneDeep(doc)
164 delete docWithoutSignature.signature
165
166 return hashObject(docWithoutSignature)
167}