Remove old JSON LD signature implementation

Only PeerTube was compatible with it, and the library has moved on
RsaSignature2018 and removed RsaSignature2017 support. We had to create
a dirty fork of the RsaSignature2017 branch, which is not ideal.

Now we use the Mastodon implementation, that most other AP
implementations that support JSONLD signatures use.
This commit is contained in:
Chocobozzz 2019-08-29 16:15:41 +02:00
parent f0a47bc92a
commit ad513607a3
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
6 changed files with 61 additions and 205 deletions

View File

@ -128,7 +128,6 @@
"iso-639-3": "^1.0.1",
"js-yaml": "^3.5.4",
"jsonld": "~1.1.0",
"jsonld-signatures": "https://github.com/Chocobozzz/jsonld-signatures#rsa2017",
"lodash": "^4.17.10",
"lru-cache": "^5.1.1",
"magnet-uri": "^5.1.4",

View File

@ -1,6 +1,5 @@
import * as AsyncLRU from 'async-lru'
import * as jsonld from 'jsonld'
import * as jsig from 'jsonld-signatures'
import { logger } from './logger'
const CACHE = {
@ -79,6 +78,4 @@ jsonld.documentLoader = (url, cb) => {
lru.get(url, cb)
}
jsig.use('jsonld', jsonld)
export { jsig, jsonld }
export { jsonld }

View File

@ -1,11 +1,10 @@
import { Request } from 'express'
import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants'
import { ActorModel } from '../models/activitypub/actor'
import { createPrivateKey, getPublicKey, promisify1, promisify2, sha256 } from './core-utils'
import { jsig, jsonld } from './custom-jsonld-signature'
import { jsonld } from './custom-jsonld-signature'
import { logger } from './logger'
import { cloneDeep } from 'lodash'
import { createVerify } from 'crypto'
import { createSign, createVerify } from 'crypto'
import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils'
import * as bcrypt from 'bcrypt'
import { MActor } from '../typings/models'
@ -57,70 +56,21 @@ function parseHTTPSignature (req: Request, clockSkew?: number) {
// JSONLD
async function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> {
function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> {
if (signedDocument.signature.type === 'RsaSignature2017') {
// Mastodon algorithm
const res = await isJsonLDRSA2017Verified(fromActor, signedDocument)
// Success? If no, try with our library
if (res === true) return true
return isJsonLDRSA2017Verified(fromActor, signedDocument)
}
const publicKeyObject = {
'@context': jsig.SECURITY_CONTEXT_URL,
id: fromActor.url,
type: 'CryptographicKey',
owner: fromActor.url,
publicKeyPem: fromActor.publicKey
}
logger.warn('Unknown JSON LD signature %s.', signedDocument.signature.type, signedDocument)
const publicKeyOwnerObject = {
'@context': jsig.SECURITY_CONTEXT_URL,
id: fromActor.url,
publicKey: [ publicKeyObject ]
}
const options = {
publicKey: publicKeyObject,
publicKeyOwner: publicKeyOwnerObject
}
return jsig.promises
.verify(signedDocument, options)
.then((result: { verified: boolean }) => result.verified)
.catch(err => {
logger.error('Cannot check signature.', { err })
return false
})
return Promise.resolve(false)
}
// Backward compatibility with "other" implementations
async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any) {
function hash (obj: any): Promise<any> {
return jsonld.promises
.normalize(obj, {
algorithm: 'URDNA2015',
format: 'application/n-quads'
})
.then(res => sha256(res))
}
const signatureCopy = cloneDeep(signedDocument.signature)
Object.assign(signatureCopy, {
'@context': [
'https://w3id.org/security/v1',
{ RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' }
]
})
delete signatureCopy.type
delete signatureCopy.id
delete signatureCopy.signatureValue
const docWithoutSignature = cloneDeep(signedDocument)
delete docWithoutSignature.signature
const [ documentHash, optionsHash ] = await Promise.all([
hash(docWithoutSignature),
hash(signatureCopy)
createDocWithoutSignatureHash(signedDocument),
createSignatureHash(signedDocument.signature)
])
const toVerify = optionsHash + documentHash
@ -131,14 +81,27 @@ async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any)
return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64')
}
function signJsonLDObject (byActor: MActor, data: any) {
const options = {
privateKeyPem: byActor.privateKey,
async function signJsonLDObject (byActor: MActor, data: any) {
const signature = {
type: 'RsaSignature2017',
creator: byActor.url,
algorithm: 'RsaSignature2017'
created: new Date().toISOString()
}
return jsig.promises.sign(data, options)
const [ documentHash, optionsHash ] = await Promise.all([
createDocWithoutSignatureHash(data),
createSignatureHash(signature)
])
const toSign = optionsHash + documentHash
const sign = createSign('RSA-SHA256')
sign.update(toSign, 'utf8')
const signatureValue = sign.sign(byActor.privateKey, 'base64')
Object.assign(signature, { signatureValue })
return Object.assign(data, { signature })
}
// ---------------------------------------------------------------------------
@ -155,3 +118,35 @@ export {
}
// ---------------------------------------------------------------------------
function hash (obj: any): Promise<any> {
return jsonld.promises
.normalize(obj, {
algorithm: 'URDNA2015',
format: 'application/n-quads'
})
.then(res => sha256(res))
}
function createSignatureHash (signature: any) {
const signatureCopy = cloneDeep(signature)
Object.assign(signatureCopy, {
'@context': [
'https://w3id.org/security/v1',
{ RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' }
]
})
delete signatureCopy.type
delete signatureCopy.id
delete signatureCopy.signatureValue
return hash(signatureCopy)
}
function createDocWithoutSignatureHash (doc: any) {
const docWithoutSignature = cloneDeep(doc)
delete docWithoutSignature.signature
return hash(docWithoutSignature)
}

View File

@ -101,6 +101,8 @@ async function checkJsonLDSignature (req: Request, res: Response) {
const verified = await isJsonLDSignatureVerified(actor, req.body)
if (verified !== true) {
logger.warn('Signature not verified.', req.body)
res.sendStatus(403)
return false
}

View File

@ -53,19 +53,6 @@ describe('Test activity pub helpers', function () {
expect(result).to.be.false
})
it('Should fail with an invalid PeerTube URL', async function () {
const keys = require('./json/peertube/keys.json')
const body = require('./json/peertube/announce-without-context.json')
const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey }
const signedBody = await buildSignedActivity(actorSignature as any, body)
const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9003/accounts/peertube' }
const result = await isJsonLDSignatureVerified(fromActor as any, signedBody)
expect(result).to.be.false
})
it('Should succeed with a valid PeerTube signature', async function () {
const keys = require('./json/peertube/keys.json')
const body = require('./json/peertube/announce-without-context.json')

124
yarn.lock
View File

@ -828,24 +828,6 @@ bindings@~1.3.0:
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.1.tgz#21fc7c6d67c18516ec5aaa2815b145ff77b26ea5"
integrity sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew==
bitcore-lib@^0.13.7:
version "0.13.19"
resolved "https://registry.yarnpkg.com/bitcore-lib/-/bitcore-lib-0.13.19.tgz#48af1e9bda10067c1ab16263472b5add2000f3dc"
integrity sha1-SK8em9oQBnwasWJjRyta3SAA89w=
dependencies:
bn.js "=2.0.4"
bs58 "=2.0.0"
buffer-compare "=1.0.0"
elliptic "=3.0.3"
inherits "=2.0.1"
lodash "=3.10.1"
"bitcore-message@github:CoMakery/bitcore-message#dist":
version "1.0.2"
resolved "https://codeload.github.com/CoMakery/bitcore-message/tar.gz/8799cc327029c3d34fc725f05b2cf981363f6ebf"
dependencies:
bitcore-lib "^0.13.7"
bitfield@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/bitfield/-/bitfield-2.0.0.tgz#fbe6767592fe5b4c87ecf1d04126294cc1bfa837"
@ -968,16 +950,6 @@ bluebird@^3.0.5, bluebird@^3.5.0:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
bn.js@=2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-2.0.4.tgz#220a7cd677f7f1bfa93627ff4193776fe7819480"
integrity sha1-Igp81nf38b+pNif/QZN3b+eBlIA=
bn.js@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-2.2.0.tgz#12162bc2ae71fc40a5626c33438f3a875cd37625"
integrity sha1-EhYrwq5x/EClYmwzQ486h1zTdiU=
bn.js@^4.4.0:
version "4.11.8"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
@ -1043,11 +1015,6 @@ braces@^3.0.1:
dependencies:
fill-range "^7.0.1"
brorand@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
browser-stdout@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
@ -1058,11 +1025,6 @@ browserify-package-json@^1.0.0:
resolved "https://registry.yarnpkg.com/browserify-package-json/-/browserify-package-json-1.0.1.tgz#98dde8aa5c561fd6d3fe49bbaa102b74b396fdea"
integrity sha1-mN3oqlxWH9bT/km7qhArdLOW/eo=
bs58@=2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-2.0.0.tgz#72b713bed223a0ac518bbda0e3ce3f4817f39eb5"
integrity sha1-crcTvtIjoKxRi72g484/SBfznrU=
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
@ -1076,16 +1038,6 @@ buffer-alloc@^1.1.0, buffer-alloc@^1.2.0:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-compare@=1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-compare/-/buffer-compare-1.0.0.tgz#acaa7a966e98eee9fae14b31c39a5f158fb3c4a2"
integrity sha1-rKp6lm6Y7un64Usxw5pfFY+zxKI=
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
buffer-equals@^1.0.3, buffer-equals@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/buffer-equals/-/buffer-equals-1.0.4.tgz#0353b54fd07fd9564170671ae6f66b9cf10d27f5"
@ -2096,13 +2048,6 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
ecdsa-sig-formatter@1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@ -2113,16 +2058,6 @@ elegant-spinner@^1.0.1:
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=
elliptic@=3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-3.0.3.tgz#865c9b420bfbe55006b9f969f97a0d2c44966595"
integrity sha1-hlybQgv75VAGuflp+XoNLESWZZU=
dependencies:
bn.js "^2.0.0"
brorand "^1.0.1"
hash.js "^1.0.0"
inherits "^2.0.1"
emoji-regex@^7.0.1:
version "7.0.3"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
@ -3250,14 +3185,6 @@ has@^1.0.1, has@^1.0.3:
dependencies:
function-bind "^1.1.1"
hash.js@^1.0.0:
version "1.1.7"
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
dependencies:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
hashish@~0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/hashish/-/hashish-0.0.4.tgz#6d60bc6ffaf711b6afd60e426d077988014e6554"
@ -3481,11 +3408,6 @@ inherits@2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
inherits@=2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
ini@^1.3.4, ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
@ -4028,25 +3950,6 @@ jsonify@~0.0.0:
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
"jsonld-signatures@https://github.com/Chocobozzz/jsonld-signatures#rsa2017":
version "1.2.2-2"
resolved "https://github.com/Chocobozzz/jsonld-signatures#77660963e722eb4541d2d255f9d9d4216329665f"
dependencies:
bitcore-message "github:CoMakery/bitcore-message#dist"
jsonld "^0.5.12"
jws "^3.1.4"
node-forge "^0.7.1"
jsonld@^0.5.12:
version "0.5.21"
resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-0.5.21.tgz#4d5b78d717eb92bcd1ac9d88e34efad95370c0bf"
integrity sha512-1dQhaw1Eb3p7Cz5ECE2DNPwLvTmK+f6D45hACBdonJaFKP1bN9zlKLZWbPZQeZtduAc/LNv10J4ML0IiTBVahw==
dependencies:
rdf-canonize "^0.2.1"
request "^2.83.0"
semver "^5.5.0"
xmldom "0.1.19"
jsonld@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-1.1.0.tgz#afcb168c44557a7bddead4d4513c3cbcae3bc5b9"
@ -4082,23 +3985,6 @@ junk@^3.1.0:
resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1"
integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==
jwa@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
dependencies:
buffer-equal-constant-time "1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"
jws@^3.1.4:
version "3.2.2"
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
dependencies:
jwa "^1.4.1"
safe-buffer "^5.0.1"
k-bucket@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/k-bucket/-/k-bucket-4.0.1.tgz#3fc2e5693f0b7bff90d7b6b476edd6087955d542"
@ -4335,11 +4221,6 @@ lodash@4.17.4:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=
lodash@=3.10.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=
lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.3.0, lodash@~4.17.10:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
@ -4638,11 +4519,6 @@ mimic-response@^1.0.0:
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
minimalistic-assert@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
minimatch@3.0.4, minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"