"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",
import * as AsyncLRU from 'async-lru'
import * as jsonld from 'jsonld'
-import * as jsig from 'jsonld-signatures'
import { logger } from './logger'
const CACHE = {
lru.get(url, cb)
}
-jsig.use('jsonld', jsonld)
-
-export { jsig, jsonld }
+export { jsonld }
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'
// 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
- }
-
- const publicKeyOwnerObject = {
- '@context': jsig.SECURITY_CONTEXT_URL,
- id: fromActor.url,
- publicKey: [ publicKeyObject ]
- }
+ logger.warn('Unknown JSON LD signature %s.', signedDocument.signature.type, signedDocument)
- 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
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 })
}
// ---------------------------------------------------------------------------
}
// ---------------------------------------------------------------------------
+
+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)
+}
const verified = await isJsonLDSignatureVerified(actor, req.body)
if (verified !== true) {
+ logger.warn('Signature not verified.', req.body)
+
res.sendStatus(403)
return false
}
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')
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"