diff options
author | Chocobozzz <me@florianbigard.com> | 2018-10-23 11:38:48 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-11-14 16:32:27 +0100 |
commit | df66d81583e07ce049daeeef1edc6a87b57b3684 (patch) | |
tree | 2a12747cd442713807e2b7d93899bc621d303459 /server/helpers | |
parent | b83b8dd5aef03084133c5983de6f312e7d1654b8 (diff) | |
download | PeerTube-df66d81583e07ce049daeeef1edc6a87b57b3684.tar.gz PeerTube-df66d81583e07ce049daeeef1edc6a87b57b3684.tar.zst PeerTube-df66d81583e07ce049daeeef1edc6a87b57b3684.zip |
Add compatibility with other Linked Signature algorithms
Diffstat (limited to 'server/helpers')
-rw-r--r-- | server/helpers/custom-jsonld-signature.ts | 4 | ||||
-rw-r--r-- | server/helpers/peertube-crypto.ts | 71 |
2 files changed, 66 insertions, 9 deletions
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/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, |