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.ts121
1 files changed, 58 insertions, 63 deletions
diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts
index 085cd62c9..9eb782302 100644
--- a/server/helpers/peertube-crypto.ts
+++ b/server/helpers/peertube-crypto.ts
@@ -1,11 +1,10 @@
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'
11import { MActor } from '../typings/models' 10import { MActor } from '../typings/models'
@@ -57,70 +56,21 @@ function parseHTTPSignature (req: Request, clockSkew?: number) {
57 56
58// JSONLD 57// JSONLD
59 58
60async function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> { 59function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> {
61 if (signedDocument.signature.type === 'RsaSignature2017') { 60 if (signedDocument.signature.type === 'RsaSignature2017') {
62 // Mastodon algorithm 61 return isJsonLDRSA2017Verified(fromActor, signedDocument)
63 const res = await isJsonLDRSA2017Verified(fromActor, signedDocument)
64 // Success? If no, try with our library
65 if (res === true) return true
66 } 62 }
67 63
68 const publicKeyObject = { 64 logger.warn('Unknown JSON LD signature %s.', signedDocument.signature.type, signedDocument)
69 '@context': jsig.SECURITY_CONTEXT_URL,
70 id: fromActor.url,
71 type: 'CryptographicKey',
72 owner: fromActor.url,
73 publicKeyPem: fromActor.publicKey
74 }
75
76 const publicKeyOwnerObject = {
77 '@context': jsig.SECURITY_CONTEXT_URL,
78 id: fromActor.url,
79 publicKey: [ publicKeyObject ]
80 }
81 65
82 const options = { 66 return Promise.resolve(false)
83 publicKey: publicKeyObject,
84 publicKeyOwner: publicKeyOwnerObject
85 }
86
87 return jsig.promises
88 .verify(signedDocument, options)
89 .then((result: { verified: boolean }) => result.verified)
90 .catch(err => {
91 logger.error('Cannot check signature.', { err })
92 return false
93 })
94} 67}
95 68
96// Backward compatibility with "other" implementations 69// Backward compatibility with "other" implementations
97async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any) { 70async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any) {
98 function hash (obj: any): Promise<any> {
99 return jsonld.promises
100 .normalize(obj, {
101 algorithm: 'URDNA2015',
102 format: 'application/n-quads'
103 })
104 .then(res => sha256(res))
105 }
106
107 const signatureCopy = cloneDeep(signedDocument.signature)
108 Object.assign(signatureCopy, {
109 '@context': [
110 'https://w3id.org/security/v1',
111 { RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' }
112 ]
113 })
114 delete signatureCopy.type
115 delete signatureCopy.id
116 delete signatureCopy.signatureValue
117
118 const docWithoutSignature = cloneDeep(signedDocument)
119 delete docWithoutSignature.signature
120
121 const [ documentHash, optionsHash ] = await Promise.all([ 71 const [ documentHash, optionsHash ] = await Promise.all([
122 hash(docWithoutSignature), 72 createDocWithoutSignatureHash(signedDocument),
123 hash(signatureCopy) 73 createSignatureHash(signedDocument.signature)
124 ]) 74 ])
125 75
126 const toVerify = optionsHash + documentHash 76 const toVerify = optionsHash + documentHash
@@ -131,14 +81,27 @@ async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any)
131 return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64') 81 return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64')
132} 82}
133 83
134function signJsonLDObject (byActor: MActor, data: any) { 84async function signJsonLDObject (byActor: MActor, data: any) {
135 const options = { 85 const signature = {
136 privateKeyPem: byActor.privateKey, 86 type: 'RsaSignature2017',
137 creator: byActor.url, 87 creator: byActor.url,
138 algorithm: 'RsaSignature2017' 88 created: new Date().toISOString()
139 } 89 }
140 90
141 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 })
142} 105}
143 106
144// --------------------------------------------------------------------------- 107// ---------------------------------------------------------------------------
@@ -155,3 +118,35 @@ export {
155} 118}
156 119
157// --------------------------------------------------------------------------- 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}