diff options
Diffstat (limited to 'server/helpers')
-rw-r--r-- | server/helpers/activitypub.ts | 11 | ||||
-rw-r--r-- | server/helpers/custom-jsonld-signature.ts | 4 | ||||
-rw-r--r-- | server/helpers/ffmpeg-utils.ts | 1 | ||||
-rw-r--r-- | server/helpers/peertube-crypto.ts | 71 | ||||
-rw-r--r-- | server/helpers/requests.ts | 4 |
5 files changed, 79 insertions, 12 deletions
diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts index 278010e78..4bf6e387d 100644 --- a/server/helpers/activitypub.ts +++ b/server/helpers/activitypub.ts | |||
@@ -6,6 +6,7 @@ import { ACTIVITY_PUB } from '../initializers' | |||
6 | import { ActorModel } from '../models/activitypub/actor' | 6 | import { ActorModel } from '../models/activitypub/actor' |
7 | import { signJsonLDObject } from './peertube-crypto' | 7 | import { signJsonLDObject } from './peertube-crypto' |
8 | import { pageToStartAndCount } from './core-utils' | 8 | import { pageToStartAndCount } from './core-utils' |
9 | import { parse } from 'url' | ||
9 | 10 | ||
10 | function activityPubContextify <T> (data: T) { | 11 | function activityPubContextify <T> (data: T) { |
11 | return Object.assign(data, { | 12 | return Object.assign(data, { |
@@ -24,7 +25,7 @@ function activityPubContextify <T> (data: T) { | |||
24 | sensitive: 'as:sensitive', | 25 | sensitive: 'as:sensitive', |
25 | language: 'sc:inLanguage', | 26 | language: 'sc:inLanguage', |
26 | views: 'sc:Number', | 27 | views: 'sc:Number', |
27 | stats: 'sc:Number', | 28 | state: 'sc:Number', |
28 | size: 'sc:Number', | 29 | size: 'sc:Number', |
29 | fps: 'sc:Number', | 30 | fps: 'sc:Number', |
30 | commentsEnabled: 'sc:Boolean', | 31 | commentsEnabled: 'sc:Boolean', |
@@ -111,9 +112,17 @@ function getActorUrl (activityActor: string | ActivityPubActor) { | |||
111 | return activityActor.id | 112 | return activityActor.id |
112 | } | 113 | } |
113 | 114 | ||
115 | function checkUrlsSameHost (url1: string, url2: string) { | ||
116 | const idHost = parse(url1).host | ||
117 | const actorHost = parse(url2).host | ||
118 | |||
119 | return idHost && actorHost && idHost.toLowerCase() === actorHost.toLowerCase() | ||
120 | } | ||
121 | |||
114 | // --------------------------------------------------------------------------- | 122 | // --------------------------------------------------------------------------- |
115 | 123 | ||
116 | export { | 124 | export { |
125 | checkUrlsSameHost, | ||
117 | getActorUrl, | 126 | getActorUrl, |
118 | activityPubContextify, | 127 | activityPubContextify, |
119 | activityPubCollectionPagination, | 128 | activityPubCollectionPagination, |
diff --git a/server/helpers/custom-jsonld-signature.ts b/server/helpers/custom-jsonld-signature.ts index e4f28018e..27a187db1 100644 --- a/server/helpers/custom-jsonld-signature.ts +++ b/server/helpers/custom-jsonld-signature.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as AsyncLRU from 'async-lru' | 1 | import * as AsyncLRU from 'async-lru' |
2 | import * as jsonld from 'jsonld/' | 2 | import * as jsonld from 'jsonld' |
3 | import * as jsig from 'jsonld-signatures' | 3 | import * as jsig from 'jsonld-signatures' |
4 | 4 | ||
5 | const nodeDocumentLoader = jsonld.documentLoaders.node() | 5 | const nodeDocumentLoader = jsonld.documentLoaders.node() |
@@ -17,4 +17,4 @@ jsonld.documentLoader = (url, cb) => { | |||
17 | 17 | ||
18 | jsig.use('jsonld', jsonld) | 18 | jsig.use('jsonld', jsonld) |
19 | 19 | ||
20 | export { jsig } | 20 | export { jsig, jsonld } |
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index a108d46a0..8b9045038 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts | |||
@@ -310,6 +310,7 @@ async function presetH264 (command: ffmpeg.FfmpegCommand, resolution: VideoResol | |||
310 | .outputOption('-level 3.1') // 3.1 is the minimal ressource allocation for our highest supported resolution | 310 | .outputOption('-level 3.1') // 3.1 is the minimal ressource allocation for our highest supported resolution |
311 | .outputOption('-b_strategy 1') // NOTE: b-strategy 1 - heuristic algorythm, 16 is optimal B-frames for it | 311 | .outputOption('-b_strategy 1') // NOTE: b-strategy 1 - heuristic algorythm, 16 is optimal B-frames for it |
312 | .outputOption('-bf 16') // NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16 | 312 | .outputOption('-bf 16') // NOTE: Why 16: https://github.com/Chocobozzz/PeerTube/pull/774. b-strategy 2 -> B-frames<16 |
313 | .outputOption('-pix_fmt yuv420p') // allows import of source material with incompatible pixel formats (e.g. MJPEG video) | ||
313 | .outputOption('-map_metadata -1') // strip all metadata | 314 | .outputOption('-map_metadata -1') // strip all metadata |
314 | .outputOption('-movflags faststart') | 315 | .outputOption('-movflags faststart') |
315 | 316 | ||
diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts index 8ef7b1359..ab9ec077e 100644 --- a/server/helpers/peertube-crypto.ts +++ b/server/helpers/peertube-crypto.ts | |||
@@ -1,9 +1,12 @@ | |||
1 | import { Request } from 'express' | 1 | import { Request } from 'express' |
2 | import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers' | 2 | import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers' |
3 | import { ActorModel } from '../models/activitypub/actor' | 3 | import { ActorModel } from '../models/activitypub/actor' |
4 | import { bcryptComparePromise, bcryptGenSaltPromise, bcryptHashPromise, createPrivateKey, getPublicKey } from './core-utils' | 4 | import { bcryptComparePromise, bcryptGenSaltPromise, bcryptHashPromise, createPrivateKey, getPublicKey, sha256 } from './core-utils' |
5 | import { jsig } from './custom-jsonld-signature' | 5 | import { jsig, jsonld } from './custom-jsonld-signature' |
6 | import { logger } from './logger' | 6 | import { logger } from './logger' |
7 | import { cloneDeep } from 'lodash' | ||
8 | import { createVerify } from 'crypto' | ||
9 | import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils' | ||
7 | 10 | ||
8 | const httpSignature = require('http-signature') | 11 | const httpSignature = require('http-signature') |
9 | 12 | ||
@@ -30,21 +33,36 @@ async function cryptPassword (password: string) { | |||
30 | 33 | ||
31 | // HTTP Signature | 34 | // HTTP Signature |
32 | 35 | ||
33 | function isHTTPSignatureVerified (httpSignatureParsed: any, actor: ActorModel) { | 36 | function 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 | |||
44 | function isHTTPSignatureVerified (httpSignatureParsed: any, actor: ActorModel): boolean { | ||
34 | return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true | 45 | return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true |
35 | } | 46 | } |
36 | 47 | ||
37 | function parseHTTPSignature (req: Request) { | 48 | function parseHTTPSignature (req: Request, clockSkew?: number) { |
38 | return httpSignature.parse(req, { authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME }) | 49 | return httpSignature.parse(req, { authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME, clockSkew }) |
39 | } | 50 | } |
40 | 51 | ||
41 | // JSONLD | 52 | // JSONLD |
42 | 53 | ||
43 | function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: any) { | 54 | async 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 | |||
44 | const publicKeyObject = { | 62 | const publicKeyObject = { |
45 | '@context': jsig.SECURITY_CONTEXT_URL, | 63 | '@context': jsig.SECURITY_CONTEXT_URL, |
46 | id: fromActor.url, | 64 | id: fromActor.url, |
47 | type: 'CryptographicKey', | 65 | type: 'CryptographicKey', |
48 | owner: fromActor.url, | 66 | owner: fromActor.url, |
49 | publicKeyPem: fromActor.publicKey | 67 | publicKeyPem: fromActor.publicKey |
50 | } | 68 | } |
@@ -69,6 +87,44 @@ function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: any) | |||
69 | }) | 87 | }) |
70 | } | 88 | } |
71 | 89 | ||
90 | // Backward compatibility with "other" implementations | ||
91 | async 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') | ||
126 | } | ||
127 | |||
72 | function signJsonLDObject (byActor: ActorModel, data: any) { | 128 | function signJsonLDObject (byActor: ActorModel, data: any) { |
73 | const options = { | 129 | const options = { |
74 | privateKeyPem: byActor.privateKey, | 130 | privateKeyPem: byActor.privateKey, |
@@ -82,6 +138,7 @@ function signJsonLDObject (byActor: ActorModel, data: any) { | |||
82 | // --------------------------------------------------------------------------- | 138 | // --------------------------------------------------------------------------- |
83 | 139 | ||
84 | export { | 140 | export { |
141 | isHTTPSignatureDigestValid, | ||
85 | parseHTTPSignature, | 142 | parseHTTPSignature, |
86 | isHTTPSignatureVerified, | 143 | isHTTPSignatureVerified, |
87 | isJsonLDSignatureVerified, | 144 | isJsonLDSignatureVerified, |
diff --git a/server/helpers/requests.ts b/server/helpers/requests.ts index ee9e80404..51facc9e0 100644 --- a/server/helpers/requests.ts +++ b/server/helpers/requests.ts | |||
@@ -3,7 +3,7 @@ import { createWriteStream } from 'fs-extra' | |||
3 | import * as request from 'request' | 3 | import * as request from 'request' |
4 | import { ACTIVITY_PUB } from '../initializers' | 4 | import { ACTIVITY_PUB } from '../initializers' |
5 | 5 | ||
6 | function doRequest ( | 6 | function doRequest <T> ( |
7 | requestOptions: request.CoreOptions & request.UriOptions & { activityPub?: boolean } | 7 | requestOptions: request.CoreOptions & request.UriOptions & { activityPub?: boolean } |
8 | ): Bluebird<{ response: request.RequestResponse, body: any }> { | 8 | ): Bluebird<{ response: request.RequestResponse, body: any }> { |
9 | if (requestOptions.activityPub === true) { | 9 | if (requestOptions.activityPub === true) { |
@@ -11,7 +11,7 @@ function doRequest ( | |||
11 | requestOptions.headers['accept'] = ACTIVITY_PUB.ACCEPT_HEADER | 11 | requestOptions.headers['accept'] = ACTIVITY_PUB.ACCEPT_HEADER |
12 | } | 12 | } |
13 | 13 | ||
14 | return new Bluebird<{ response: request.RequestResponse, body: any }>((res, rej) => { | 14 | return new Bluebird<{ response: request.RequestResponse, body: T }>((res, rej) => { |
15 | request(requestOptions, (err, response, body) => err ? rej(err) : res({ response, body })) | 15 | request(requestOptions, (err, response, body) => err ? rej(err) : res({ response, body })) |
16 | }) | 16 | }) |
17 | } | 17 | } |