aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/helpers/peertube-crypto.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/helpers/peertube-crypto.ts')
-rw-r--r--server/helpers/peertube-crypto.ts128
1 files changed, 103 insertions, 25 deletions
diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts
index 5c182961d..ab9ec077e 100644
--- a/server/helpers/peertube-crypto.ts
+++ b/server/helpers/peertube-crypto.ts
@@ -1,8 +1,14 @@
1import { BCRYPT_SALT_SIZE, PRIVATE_RSA_KEY_SIZE } from '../initializers' 1import { Request } from 'express'
2import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers'
2import { ActorModel } from '../models/activitypub/actor' 3import { ActorModel } from '../models/activitypub/actor'
3import { bcryptComparePromise, bcryptGenSaltPromise, bcryptHashPromise, createPrivateKey, getPublicKey } from './core-utils' 4import { bcryptComparePromise, bcryptGenSaltPromise, bcryptHashPromise, createPrivateKey, getPublicKey, sha256 } from './core-utils'
4import { jsig } from './custom-jsonld-signature' 5import { jsig, jsonld } from './custom-jsonld-signature'
5import { logger } from './logger' 6import { logger } from './logger'
7import { cloneDeep } from 'lodash'
8import { createVerify } from 'crypto'
9import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils'
10
11const httpSignature = require('http-signature')
6 12
7async function createPrivateAndPublicKeys () { 13async function createPrivateAndPublicKeys () {
8 logger.info('Generating a RSA key...') 14 logger.info('Generating a RSA key...')
@@ -13,18 +19,57 @@ async function createPrivateAndPublicKeys () {
13 return { privateKey: key, publicKey } 19 return { privateKey: key, publicKey }
14} 20}
15 21
16function isSignatureVerified (fromActor: ActorModel, signedDocument: object) { 22// User password checks
23
24function comparePassword (plainPassword: string, hashPassword: string) {
25 return bcryptComparePromise(plainPassword, hashPassword)
26}
27
28async function cryptPassword (password: string) {
29 const salt = await bcryptGenSaltPromise(BCRYPT_SALT_SIZE)
30
31 return bcryptHashPromise(password, salt)
32}
33
34// HTTP Signature
35
36function isHTTPSignatureDigestValid (rawBody: Buffer, req: Request): boolean {
37 if (req.headers[HTTP_SIGNATURE.HEADER_NAME] && req.headers['digest']) {
38 return buildDigest(rawBody.toString()) === req.headers['digest']
39 }
40
41 return true
42}
43
44function isHTTPSignatureVerified (httpSignatureParsed: any, actor: ActorModel): boolean {
45 return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true
46}
47
48function parseHTTPSignature (req: Request, clockSkew?: number) {
49 return httpSignature.parse(req, { authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME, clockSkew })
50}
51
52// JSONLD
53
54async function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: any): Promise<boolean> {
55 if (signedDocument.signature.type === 'RsaSignature2017') {
56 // Mastodon algorithm
57 const res = await isJsonLDRSA2017Verified(fromActor, signedDocument)
58 // Success? If no, try with our library
59 if (res === true) return true
60 }
61
17 const publicKeyObject = { 62 const publicKeyObject = {
18 '@context': jsig.SECURITY_CONTEXT_URL, 63 '@context': jsig.SECURITY_CONTEXT_URL,
19 '@id': fromActor.url, 64 id: fromActor.url,
20 '@type': 'CryptographicKey', 65 type: 'CryptographicKey',
21 owner: fromActor.url, 66 owner: fromActor.url,
22 publicKeyPem: fromActor.publicKey 67 publicKeyPem: fromActor.publicKey
23 } 68 }
24 69
25 const publicKeyOwnerObject = { 70 const publicKeyOwnerObject = {
26 '@context': jsig.SECURITY_CONTEXT_URL, 71 '@context': jsig.SECURITY_CONTEXT_URL,
27 '@id': fromActor.url, 72 id: fromActor.url,
28 publicKey: [ publicKeyObject ] 73 publicKey: [ publicKeyObject ]
29 } 74 }
30 75
@@ -33,14 +78,54 @@ function isSignatureVerified (fromActor: ActorModel, signedDocument: object) {
33 publicKeyOwner: publicKeyOwnerObject 78 publicKeyOwner: publicKeyOwnerObject
34 } 79 }
35 80
36 return jsig.promises.verify(signedDocument, options) 81 return jsig.promises
37 .catch(err => { 82 .verify(signedDocument, options)
38 logger.error('Cannot check signature.', { err }) 83 .then((result: { verified: boolean }) => result.verified)
39 return false 84 .catch(err => {
40 }) 85 logger.error('Cannot check signature.', { err })
86 return false
87 })
88}
89
90// Backward compatibility with "other" implementations
91async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: any) {
92 function hash (obj: any): Promise<any> {
93 return jsonld.promises
94 .normalize(obj, {
95 algorithm: 'URDNA2015',
96 format: 'application/n-quads'
97 })
98 .then(res => sha256(res))
99 }
100
101 const signatureCopy = cloneDeep(signedDocument.signature)
102 Object.assign(signatureCopy, {
103 '@context': [
104 'https://w3id.org/security/v1',
105 { RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' }
106 ]
107 })
108 delete signatureCopy.type
109 delete signatureCopy.id
110 delete signatureCopy.signatureValue
111
112 const docWithoutSignature = cloneDeep(signedDocument)
113 delete docWithoutSignature.signature
114
115 const [ documentHash, optionsHash ] = await Promise.all([
116 hash(docWithoutSignature),
117 hash(signatureCopy)
118 ])
119
120 const toVerify = optionsHash + documentHash
121
122 const verify = createVerify('RSA-SHA256')
123 verify.update(toVerify, 'utf8')
124
125 return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64')
41} 126}
42 127
43function signObject (byActor: ActorModel, data: any) { 128function signJsonLDObject (byActor: ActorModel, data: any) {
44 const options = { 129 const options = {
45 privateKeyPem: byActor.privateKey, 130 privateKeyPem: byActor.privateKey,
46 creator: byActor.url, 131 creator: byActor.url,
@@ -50,22 +135,15 @@ function signObject (byActor: ActorModel, data: any) {
50 return jsig.promises.sign(data, options) 135 return jsig.promises.sign(data, options)
51} 136}
52 137
53function comparePassword (plainPassword: string, hashPassword: string) {
54 return bcryptComparePromise(plainPassword, hashPassword)
55}
56
57async function cryptPassword (password: string) {
58 const salt = await bcryptGenSaltPromise(BCRYPT_SALT_SIZE)
59
60 return bcryptHashPromise(password, salt)
61}
62
63// --------------------------------------------------------------------------- 138// ---------------------------------------------------------------------------
64 139
65export { 140export {
66 isSignatureVerified, 141 isHTTPSignatureDigestValid,
142 parseHTTPSignature,
143 isHTTPSignatureVerified,
144 isJsonLDSignatureVerified,
67 comparePassword, 145 comparePassword,
68 createPrivateAndPublicKeys, 146 createPrivateAndPublicKeys,
69 cryptPassword, 147 cryptPassword,
70 signObject 148 signJsonLDObject
71} 149}