Remove old JSON LD signature implementation
authorChocobozzz <me@florianbigard.com>
Thu, 29 Aug 2019 14:15:41 +0000 (16:15 +0200)
committerChocobozzz <me@florianbigard.com>
Thu, 29 Aug 2019 14:15:41 +0000 (16:15 +0200)
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.

package.json
server/helpers/custom-jsonld-signature.ts
server/helpers/peertube-crypto.ts
server/middlewares/activitypub.ts
server/tests/api/activitypub/helpers.ts
yarn.lock

index ce689a4b3a1a148243a39ad7550b226f17636520..0c1ec93d13e33212392715d4e0b3905616fc1221 100644 (file)
     "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",
index a3bceb047700fc1027b0704f459ee88a88e12624..cb07fa3b28bc7ce6c08f2cdf4ad23c14887aa8c9 100644 (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 }
index 085cd62c90df1cc5d05610f9b6f9f04f6bf5634d..9eb7823026cc3222bca162ed6841bef45152300d 100644 (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
-  }
-
-  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
@@ -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)
+}
index b1e5b52369c115dadc556264262c1db4260c2f47..bea213d270af840ba0edfd1beddfb4ea5ac75905 100644 (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
   }
index 365d0e1aefeba3422484148cc3d413b26cf325c2..0d1f154fe5bc503b231271bd5cea1bfa6888537d 100644 (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')
index f26763845b9c023c3a1032aa8dd1a952fb8a66c9..b5a3a3f477303d73ecf71ca04bc02790544dc6c6 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"