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.ts126
1 files changed, 61 insertions, 65 deletions
diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts
index 1424949d0..9eb782302 100644
--- a/server/helpers/peertube-crypto.ts
+++ b/server/helpers/peertube-crypto.ts
@@ -1,13 +1,13 @@
1import { Request } from 'express' 1import { Request } from 'express'
2import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants' 2import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants'
3import { ActorModel } from '../models/activitypub/actor'
4import { createPrivateKey, getPublicKey, promisify1, promisify2, sha256 } from './core-utils' 3import { createPrivateKey, getPublicKey, promisify1, promisify2, sha256 } from './core-utils'
5import { jsig, jsonld } from './custom-jsonld-signature' 4import { jsonld } from './custom-jsonld-signature'
6import { logger } from './logger' 5import { logger } from './logger'
7import { cloneDeep } from 'lodash' 6import { cloneDeep } from 'lodash'
8import { createVerify } from 'crypto' 7import { createSign, createVerify } from 'crypto'
9import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils' 8import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils'
10import * as bcrypt from 'bcrypt' 9import * as bcrypt from 'bcrypt'
10import { MActor } from '../typings/models'
11 11
12const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare) 12const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
13const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt) 13const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
@@ -46,7 +46,7 @@ function isHTTPSignatureDigestValid (rawBody: Buffer, req: Request): boolean {
46 return true 46 return true
47} 47}
48 48
49function isHTTPSignatureVerified (httpSignatureParsed: any, actor: ActorModel): boolean { 49function isHTTPSignatureVerified (httpSignatureParsed: any, actor: MActor): boolean {
50 return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true 50 return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true
51} 51}
52 52
@@ -56,70 +56,21 @@ function parseHTTPSignature (req: Request, clockSkew?: number) {
56 56
57// JSONLD 57// JSONLD
58 58
59async function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: any): Promise<boolean> { 59function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> {
60 if (signedDocument.signature.type === 'RsaSignature2017') { 60 if (signedDocument.signature.type === 'RsaSignature2017') {
61 // Mastodon algorithm 61 return isJsonLDRSA2017Verified(fromActor, signedDocument)
62 const res = await isJsonLDRSA2017Verified(fromActor, signedDocument)
63 // Success? If no, try with our library
64 if (res === true) return true
65 } 62 }
66 63
67 const publicKeyObject = { 64 logger.warn('Unknown JSON LD signature %s.', signedDocument.signature.type, signedDocument)
68 '@context': jsig.SECURITY_CONTEXT_URL,
69 id: fromActor.url,
70 type: 'CryptographicKey',
71 owner: fromActor.url,
72 publicKeyPem: fromActor.publicKey
73 }
74
75 const publicKeyOwnerObject = {
76 '@context': jsig.SECURITY_CONTEXT_URL,
77 id: fromActor.url,
78 publicKey: [ publicKeyObject ]
79 }
80 65
81 const options = { 66 return Promise.resolve(false)
82 publicKey: publicKeyObject,
83 publicKeyOwner: publicKeyOwnerObject
84 }
85
86 return jsig.promises
87 .verify(signedDocument, options)
88 .then((result: { verified: boolean }) => result.verified)
89 .catch(err => {
90 logger.error('Cannot check signature.', { err })
91 return false
92 })
93} 67}
94 68
95// Backward compatibility with "other" implementations 69// Backward compatibility with "other" implementations
96async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: any) { 70async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any) {
97 function hash (obj: any): Promise<any> {
98 return jsonld.promises
99 .normalize(obj, {
100 algorithm: 'URDNA2015',
101 format: 'application/n-quads'
102 })
103 .then(res => sha256(res))
104 }
105
106 const signatureCopy = cloneDeep(signedDocument.signature)
107 Object.assign(signatureCopy, {
108 '@context': [
109 'https://w3id.org/security/v1',
110 { RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' }
111 ]
112 })
113 delete signatureCopy.type
114 delete signatureCopy.id
115 delete signatureCopy.signatureValue
116
117 const docWithoutSignature = cloneDeep(signedDocument)
118 delete docWithoutSignature.signature
119
120 const [ documentHash, optionsHash ] = await Promise.all([ 71 const [ documentHash, optionsHash ] = await Promise.all([
121 hash(docWithoutSignature), 72 createDocWithoutSignatureHash(signedDocument),
122 hash(signatureCopy) 73 createSignatureHash(signedDocument.signature)
123 ]) 74 ])
124 75
125 const toVerify = optionsHash + documentHash 76 const toVerify = optionsHash + documentHash
@@ -130,14 +81,27 @@ async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: a
130 return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64') 81 return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64')
131} 82}
132 83
133function signJsonLDObject (byActor: ActorModel, data: any) { 84async function signJsonLDObject (byActor: MActor, data: any) {
134 const options = { 85 const signature = {
135 privateKeyPem: byActor.privateKey, 86 type: 'RsaSignature2017',
136 creator: byActor.url, 87 creator: byActor.url,
137 algorithm: 'RsaSignature2017' 88 created: new Date().toISOString()
138 } 89 }
139 90
140 return jsig.promises.sign(data, options) 91 const [ documentHash, optionsHash ] = await Promise.all([
92 createDocWithoutSignatureHash(data),
93 createSignatureHash(signature)
94 ])
95
96 const toSign = optionsHash + documentHash
97
98 const sign = createSign('RSA-SHA256')
99 sign.update(toSign, 'utf8')
100
101 const signatureValue = sign.sign(byActor.privateKey, 'base64')
102 Object.assign(signature, { signatureValue })
103
104 return Object.assign(data, { signature })
141} 105}
142 106
143// --------------------------------------------------------------------------- 107// ---------------------------------------------------------------------------
@@ -154,3 +118,35 @@ export {
154} 118}
155 119
156// --------------------------------------------------------------------------- 120// ---------------------------------------------------------------------------
121
122function hash (obj: any): Promise<any> {
123 return jsonld.promises
124 .normalize(obj, {
125 algorithm: 'URDNA2015',
126 format: 'application/n-quads'
127 })
128 .then(res => sha256(res))
129}
130
131function createSignatureHash (signature: any) {
132 const signatureCopy = cloneDeep(signature)
133 Object.assign(signatureCopy, {
134 '@context': [
135 'https://w3id.org/security/v1',
136 { RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' }
137 ]
138 })
139
140 delete signatureCopy.type
141 delete signatureCopy.id
142 delete signatureCopy.signatureValue
143
144 return hash(signatureCopy)
145}
146
147function createDocWithoutSignatureHash (doc: any) {
148 const docWithoutSignature = cloneDeep(doc)
149 delete docWithoutSignature.signature
150
151 return hash(docWithoutSignature)
152}