Fix weird bug where CPU jumps and stays to 100%
Seems related to lazy import of custom-jsonld-signature So we refactored jsonld function calls a little bit
This commit is contained in:
parent
f93bc6a8be
commit
b017d4d02f
5 changed files with 108 additions and 105 deletions
|
@ -1,6 +1,7 @@
|
||||||
import { ContextType } from '@peertube/peertube-models'
|
import { ContextType } from '@peertube/peertube-models'
|
||||||
import { ACTIVITY_PUB } from '@server/initializers/constants.js'
|
import { ACTIVITY_PUB } from '@server/initializers/constants.js'
|
||||||
import { buildDigest, signJsonLDObject } from './peertube-crypto.js'
|
import { buildDigest } from './peertube-crypto.js'
|
||||||
|
import type { signJsonLDObject } from './peertube-jsonld.js'
|
||||||
|
|
||||||
export type ContextFilter = <T> (arg: T) => Promise<T>
|
export type ContextFilter = <T> (arg: T) => Promise<T>
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
import httpSignature from '@peertube/http-signature'
|
import httpSignature from '@peertube/http-signature'
|
||||||
import { sha256 } from '@peertube/peertube-node-utils'
|
import { sha256 } from '@peertube/peertube-node-utils'
|
||||||
import { createCipheriv, createDecipheriv, createSign, createVerify } from 'crypto'
|
import { createCipheriv, createDecipheriv } from 'crypto'
|
||||||
import { Request } from 'express'
|
import { Request } from 'express'
|
||||||
import cloneDeep from 'lodash-es/cloneDeep.js'
|
|
||||||
import { BCRYPT_SALT_SIZE, ENCRYPTION, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants.js'
|
import { BCRYPT_SALT_SIZE, ENCRYPTION, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants.js'
|
||||||
import { MActor } from '../types/models/index.js'
|
import { MActor } from '../types/models/index.js'
|
||||||
import { generateRSAKeyPairPromise, randomBytesPromise, scryptPromise } from './core-utils.js'
|
import { generateRSAKeyPairPromise, randomBytesPromise, scryptPromise } from './core-utils.js'
|
||||||
import { logger } from './logger.js'
|
import { logger } from './logger.js'
|
||||||
import { assertIsInWorkerThread } from './threads.js'
|
|
||||||
|
|
||||||
function createPrivateAndPublicKeys () {
|
function createPrivateAndPublicKeys () {
|
||||||
logger.info('Generating a RSA key...')
|
logger.info('Generating a RSA key...')
|
||||||
|
@ -66,66 +64,6 @@ function parseHTTPSignature (req: Request, clockSkew?: number) {
|
||||||
return parsed
|
return parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// JSONLD
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> {
|
|
||||||
if (signedDocument.signature.type === 'RsaSignature2017') {
|
|
||||||
return isJsonLDRSA2017Verified(fromActor, signedDocument)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.warn('Unknown JSON LD signature %s.', signedDocument.signature.type, signedDocument)
|
|
||||||
|
|
||||||
return Promise.resolve(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backward compatibility with "other" implementations
|
|
||||||
async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any) {
|
|
||||||
const [ documentHash, optionsHash ] = await Promise.all([
|
|
||||||
createDocWithoutSignatureHash(signedDocument),
|
|
||||||
createSignatureHash(signedDocument.signature)
|
|
||||||
])
|
|
||||||
|
|
||||||
const toVerify = optionsHash + documentHash
|
|
||||||
|
|
||||||
const verify = createVerify('RSA-SHA256')
|
|
||||||
verify.update(toVerify, 'utf8')
|
|
||||||
|
|
||||||
return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function signJsonLDObject <T> (options: {
|
|
||||||
byActor: { url: string, privateKey: string }
|
|
||||||
data: T
|
|
||||||
disableWorkerThreadAssertion?: boolean
|
|
||||||
}) {
|
|
||||||
const { byActor, data, disableWorkerThreadAssertion = false } = options
|
|
||||||
|
|
||||||
if (!disableWorkerThreadAssertion) assertIsInWorkerThread()
|
|
||||||
|
|
||||||
const signature = {
|
|
||||||
type: 'RsaSignature2017',
|
|
||||||
creator: byActor.url,
|
|
||||||
created: new Date().toISOString()
|
|
||||||
}
|
|
||||||
|
|
||||||
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 buildDigest (body: any) {
|
function buildDigest (body: any) {
|
||||||
|
@ -169,49 +107,10 @@ export {
|
||||||
parseHTTPSignature,
|
parseHTTPSignature,
|
||||||
isHTTPSignatureVerified,
|
isHTTPSignatureVerified,
|
||||||
buildDigest,
|
buildDigest,
|
||||||
isJsonLDSignatureVerified,
|
|
||||||
comparePassword,
|
comparePassword,
|
||||||
createPrivateAndPublicKeys,
|
createPrivateAndPublicKeys,
|
||||||
cryptPassword,
|
cryptPassword,
|
||||||
signJsonLDObject,
|
|
||||||
|
|
||||||
encrypt,
|
encrypt,
|
||||||
decrypt
|
decrypt
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
async function hashObject (obj: any): Promise<any> {
|
|
||||||
const { jsonld } = await import('./custom-jsonld-signature.js')
|
|
||||||
|
|
||||||
const res = await (jsonld as any).promises.normalize(obj, {
|
|
||||||
safe: false,
|
|
||||||
algorithm: 'URDNA2015',
|
|
||||||
format: 'application/n-quads'
|
|
||||||
})
|
|
||||||
|
|
||||||
return 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 hashObject(signatureCopy)
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDocWithoutSignatureHash (doc: any) {
|
|
||||||
const docWithoutSignature = cloneDeep(doc)
|
|
||||||
delete docWithoutSignature.signature
|
|
||||||
|
|
||||||
return hashObject(docWithoutSignature)
|
|
||||||
}
|
|
||||||
|
|
100
server/core/helpers/peertube-jsonld.ts
Normal file
100
server/core/helpers/peertube-jsonld.ts
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import { sha256 } from '@peertube/peertube-node-utils'
|
||||||
|
import { createSign, createVerify } from 'crypto'
|
||||||
|
import cloneDeep from 'lodash-es/cloneDeep.js'
|
||||||
|
import { MActor } from '../types/models/index.js'
|
||||||
|
import { logger } from './logger.js'
|
||||||
|
import { assertIsInWorkerThread } from './threads.js'
|
||||||
|
import { jsonld } from './custom-jsonld-signature.js'
|
||||||
|
|
||||||
|
export function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> {
|
||||||
|
if (signedDocument.signature.type === 'RsaSignature2017') {
|
||||||
|
return isJsonLDRSA2017Verified(fromActor, signedDocument)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.warn('Unknown JSON LD signature %s.', signedDocument.signature.type, signedDocument)
|
||||||
|
|
||||||
|
return Promise.resolve(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backward compatibility with "other" implementations
|
||||||
|
export async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any) {
|
||||||
|
const [ documentHash, optionsHash ] = await Promise.all([
|
||||||
|
createDocWithoutSignatureHash(signedDocument),
|
||||||
|
createSignatureHash(signedDocument.signature)
|
||||||
|
])
|
||||||
|
|
||||||
|
const toVerify = optionsHash + documentHash
|
||||||
|
|
||||||
|
const verify = createVerify('RSA-SHA256')
|
||||||
|
verify.update(toVerify, 'utf8')
|
||||||
|
|
||||||
|
return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function signJsonLDObject <T> (options: {
|
||||||
|
byActor: { url: string, privateKey: string }
|
||||||
|
data: T
|
||||||
|
disableWorkerThreadAssertion?: boolean
|
||||||
|
}) {
|
||||||
|
const { byActor, data, disableWorkerThreadAssertion = false } = options
|
||||||
|
|
||||||
|
if (!disableWorkerThreadAssertion) assertIsInWorkerThread()
|
||||||
|
|
||||||
|
const signature = {
|
||||||
|
type: 'RsaSignature2017',
|
||||||
|
creator: byActor.url,
|
||||||
|
created: new Date().toISOString()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Private
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function hashObject (obj: any): Promise<any> {
|
||||||
|
const res = await (jsonld as any).promises.normalize(obj, {
|
||||||
|
safe: false,
|
||||||
|
algorithm: 'URDNA2015',
|
||||||
|
format: 'application/n-quads'
|
||||||
|
})
|
||||||
|
|
||||||
|
return 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 hashObject(signatureCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDocWithoutSignatureHash (doc: any) {
|
||||||
|
const docWithoutSignature = cloneDeep(doc)
|
||||||
|
delete docWithoutSignature.signature
|
||||||
|
|
||||||
|
return hashObject(docWithoutSignature)
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
import { signJsonLDObject } from '@server/helpers/peertube-crypto.js'
|
import { signJsonLDObject } from '@server/helpers/peertube-jsonld.js'
|
||||||
|
|
||||||
export default signJsonLDObject
|
export default signJsonLDObject
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { getAPId } from '@server/lib/activitypub/activity.js'
|
||||||
import { wrapWithSpanAndContext } from '@server/lib/opentelemetry/tracing.js'
|
import { wrapWithSpanAndContext } from '@server/lib/opentelemetry/tracing.js'
|
||||||
import { ActivityDelete, ActivityPubSignature, HttpStatusCode } from '@peertube/peertube-models'
|
import { ActivityDelete, ActivityPubSignature, HttpStatusCode } from '@peertube/peertube-models'
|
||||||
import { logger } from '../helpers/logger.js'
|
import { logger } from '../helpers/logger.js'
|
||||||
import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../helpers/peertube-crypto.js'
|
import { isHTTPSignatureVerified, parseHTTPSignature } from '../helpers/peertube-crypto.js'
|
||||||
import { ACCEPT_HEADERS, ACTIVITY_PUB, HTTP_SIGNATURE } from '../initializers/constants.js'
|
import { ACCEPT_HEADERS, ACTIVITY_PUB, HTTP_SIGNATURE } from '../initializers/constants.js'
|
||||||
import { getOrCreateAPActor, loadActorUrlOrGetFromWebfinger } from '../lib/activitypub/actors/index.js'
|
import { getOrCreateAPActor, loadActorUrlOrGetFromWebfinger } from '../lib/activitypub/actors/index.js'
|
||||||
|
|
||||||
|
@ -122,6 +122,9 @@ async function checkHttpSignature (req: Request, res: Response) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkJsonLDSignature (req: Request, res: Response) {
|
async function checkJsonLDSignature (req: Request, res: Response) {
|
||||||
|
// Lazy load the module as it's quite big with json.ld dependency
|
||||||
|
const { isJsonLDSignatureVerified } = await import('../helpers/peertube-jsonld.js')
|
||||||
|
|
||||||
return wrapWithSpanAndContext('peertube.activitypub.JSONLDSignature', async () => {
|
return wrapWithSpanAndContext('peertube.activitypub.JSONLDSignature', async () => {
|
||||||
const signatureObject: ActivityPubSignature = req.body.signature
|
const signatureObject: ActivityPubSignature = req.body.signature
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue