diff --git a/backend/.eslintrc.js b/backend/.eslintrc.js index 0007fd125..e853c8bf6 100644 --- a/backend/.eslintrc.js +++ b/backend/.eslintrc.js @@ -25,10 +25,12 @@ module.exports = { }, node: true, }, + // the parser cannot handle the split sodium import + 'import/ignore': ['sodium-native'], }, rules: { 'no-console': 'error', - camelcase: ['error', { allow: ['FederationClient_*'] }], + camelcase: ['error', { allow: ['FederationClient_*', 'crypto_*', 'randombytes_random'] }], 'no-debugger': 'error', 'prettier/prettier': [ 'error', @@ -58,7 +60,10 @@ module.exports = { 'import/no-dynamic-require': 'error', 'import/no-internal-modules': 'off', 'import/no-relative-packages': 'error', - 'import/no-relative-parent-imports': ['error', { ignore: ['@/*', 'random-bigint'] }], + 'import/no-relative-parent-imports': [ + 'error', + { ignore: ['@/*', 'random-bigint', 'sodium-native'] }, + ], 'import/no-self-import': 'error', 'import/no-unresolved': 'error', 'import/no-useless-path-segments': 'error', diff --git a/backend/@types/sodium-native/index.d.ts b/backend/@types/sodium-native/index.d.ts new file mode 100644 index 000000000..773d85ee5 --- /dev/null +++ b/backend/@types/sodium-native/index.d.ts @@ -0,0 +1,8 @@ +// eslint-disable-next-line import/no-unresolved +export * from '@/node_modules/@types/sodium-native' + +declare module 'sodium-native' { + export function crypto_hash_sha512_init(state: Buffer, key?: Buffer, outlen?: Buffer): void + export function crypto_hash_sha512_update(state: Buffer, input: Buffer): void + export function crypto_hash_sha512_final(state: Buffer, out: Buffer): void +} diff --git a/backend/package.json b/backend/package.json index 68ec89ec0..10d58f8ed 100644 --- a/backend/package.json +++ b/backend/package.json @@ -55,6 +55,7 @@ "@types/lodash.clonedeep": "^4.5.6", "@types/node": "^16.10.3", "@types/nodemailer": "^6.4.4", + "@types/sodium-native": "^2.3.5", "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^5.57.1", "@typescript-eslint/parser": "^5.57.1", diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 9934c93de..a61bbfd32 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -71,15 +71,13 @@ import { hasElopageBuys } from '@/util/hasElopageBuys' import { getTimeDurationObject, printTimeDuration } from '@/util/time' import random from 'random-bigint' +import { randombytes_random } from 'sodium-native' import { FULL_CREATION_AVAILABLE } from './const/const' import { getUserCreations } from './util/creations' import { findUserByIdentifier } from './util/findUserByIdentifier' import { validateAlias } from './util/validateAlias' -// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-commonjs -const sodium = require('sodium-native') - const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl'] const DEFAULT_LANGUAGE = 'de' const isLanguage = (language: string): boolean => { @@ -237,7 +235,7 @@ export class UserResolver { // TODO: this is unsecure, but the current implementation of the login server. This way it can be queried if the user with given EMail is existent. const user = new User(communityDbUser) - user.id = sodium.randombytes_random() % (2048 * 16) // TODO: for a better faking derive id from email so that it will be always the same id when the same email comes in? + user.id = randombytes_random() % (2048 * 16) // TODO: for a better faking derive id from email so that it will be always the same id when the same email comes in? user.gradidoID = uuidv4() user.firstName = firstName user.lastName = lastName diff --git a/backend/src/password/EncryptorUtils.ts b/backend/src/password/EncryptorUtils.ts index 827fc9547..64dcb2289 100644 --- a/backend/src/password/EncryptorUtils.ts +++ b/backend/src/password/EncryptorUtils.ts @@ -10,8 +10,19 @@ import { CONFIG } from '@/config' import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' -// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-commonjs -const sodium = require('sodium-native') +import { + crypto_shorthash_KEYBYTES, + crypto_box_SEEDBYTES, + crypto_hash_sha512_init, + crypto_hash_sha512_update, + crypto_hash_sha512_final, + crypto_hash_sha512_BYTES, + crypto_hash_sha512_STATEBYTES, + crypto_shorthash_BYTES, + crypto_pwhash_SALTBYTES, + crypto_pwhash, + crypto_shorthash, +} from 'sodium-native' // We will reuse this for changePassword export const isValidPassword = (password: string): boolean => { @@ -22,36 +33,36 @@ export const SecretKeyCryptographyCreateKey = (salt: string, password: string): logger.trace('SecretKeyCryptographyCreateKey...') const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex') const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex') - if (configLoginServerKey.length !== sodium.crypto_shorthash_KEYBYTES) { + if (configLoginServerKey.length !== crypto_shorthash_KEYBYTES) { throw new LogError( 'ServerKey has an invalid size', configLoginServerKey.length, - sodium.crypto_shorthash_KEYBYTES, + crypto_shorthash_KEYBYTES, ) } - const state = Buffer.alloc(sodium.crypto_hash_sha512_STATEBYTES) - sodium.crypto_hash_sha512_init(state) - sodium.crypto_hash_sha512_update(state, Buffer.from(salt)) - sodium.crypto_hash_sha512_update(state, configLoginAppSecret) - const hash = Buffer.alloc(sodium.crypto_hash_sha512_BYTES) - sodium.crypto_hash_sha512_final(state, hash) + const state = Buffer.alloc(crypto_hash_sha512_STATEBYTES) + crypto_hash_sha512_init(state) + crypto_hash_sha512_update(state, Buffer.from(salt)) + crypto_hash_sha512_update(state, configLoginAppSecret) + const hash = Buffer.alloc(crypto_hash_sha512_BYTES) + crypto_hash_sha512_final(state, hash) - const encryptionKey = Buffer.alloc(sodium.crypto_box_SEEDBYTES) + const encryptionKey = Buffer.alloc(crypto_box_SEEDBYTES) const opsLimit = 10 const memLimit = 33554432 const algo = 2 - sodium.crypto_pwhash( + crypto_pwhash( encryptionKey, Buffer.from(password), - hash.slice(0, sodium.crypto_pwhash_SALTBYTES), + hash.slice(0, crypto_pwhash_SALTBYTES), opsLimit, memLimit, algo, ) - const encryptionKeyHash = Buffer.alloc(sodium.crypto_shorthash_BYTES) - sodium.crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey) + const encryptionKeyHash = Buffer.alloc(crypto_shorthash_BYTES) + crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey) return [encryptionKeyHash, encryptionKey] } diff --git a/backend/yarn.lock b/backend/yarn.lock index 4d070934b..24537fc57 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1181,6 +1181,13 @@ "@types/mime" "^1" "@types/node" "*" +"@types/sodium-native@^2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@types/sodium-native/-/sodium-native-2.3.5.tgz#5d2681e7b6b67bcbdc63cfb133e303ec9e942e43" + integrity sha512-a3DAIpW8+36XAY8aIR36JBQQsfOabxHuJwx11DL/PTvnbwEWPAXW66b8QbMi0r2vUnkOfREsketxdvjBmQxqDQ== + dependencies: + "@types/node" "*" + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" @@ -3645,7 +3652,7 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== "gradido-database@file:../database": - version "1.20.0" + version "1.21.0" dependencies: "@types/uuid" "^8.3.4" cross-env "^7.0.3" diff --git a/database/.eslintrc.js b/database/.eslintrc.js index 587506ecc..896a7ed68 100644 --- a/database/.eslintrc.js +++ b/database/.eslintrc.js @@ -101,43 +101,43 @@ module.exports = { // ], // 'import/prefer-default-export': 'off', // n - // 'n/handle-callback-err': 'error', - // 'n/no-callback-literal': 'error', - // 'n/no-exports-assign': 'error', + 'n/handle-callback-err': 'error', + 'n/no-callback-literal': 'error', + 'n/no-exports-assign': 'error', // 'n/no-extraneous-import': 'error', - // 'n/no-extraneous-require': 'error', - // 'n/no-hide-core-modules': 'error', - // 'n/no-missing-import': 'off', // not compatible with typescript - // 'n/no-missing-require': 'error', - // 'n/no-new-require': 'error', - // 'n/no-path-concat': 'error', + 'n/no-extraneous-require': 'error', + 'n/no-hide-core-modules': 'error', + 'n/no-missing-import': 'off', // not compatible with typescript + 'n/no-missing-require': 'error', + 'n/no-new-require': 'error', + 'n/no-path-concat': 'error', // 'n/no-process-exit': 'error', - // 'n/no-unpublished-bin': 'error', - // 'n/no-unpublished-import': 'off', // TODO need to exclude seeds - // 'n/no-unpublished-require': 'error', - // 'n/no-unsupported-features': ['error', { ignores: ['modules'] }], - // 'n/no-unsupported-features/es-builtins': 'error', - // 'n/no-unsupported-features/es-syntax': 'error', - // 'n/no-unsupported-features/node-builtins': 'error', - // 'n/process-exit-as-throw': 'error', - // 'n/shebang': 'error', - // 'n/callback-return': 'error', - // 'n/exports-style': 'error', - // 'n/file-extension-in-import': 'off', - // 'n/global-require': 'error', - // 'n/no-mixed-requires': 'error', - // 'n/no-process-env': 'error', - // 'n/no-restricted-import': 'error', - // 'n/no-restricted-require': 'error', + 'n/no-unpublished-bin': 'error', + 'n/no-unpublished-import': 'off', // TODO need to exclude seeds + 'n/no-unpublished-require': 'error', + 'n/no-unsupported-features': ['error', { ignores: ['modules'] }], + 'n/no-unsupported-features/es-builtins': 'error', + 'n/no-unsupported-features/es-syntax': 'error', + 'n/no-unsupported-features/node-builtins': 'error', + 'n/process-exit-as-throw': 'error', + 'n/shebang': 'error', + 'n/callback-return': 'error', + 'n/exports-style': 'error', + 'n/file-extension-in-import': 'off', + 'n/global-require': 'error', + 'n/no-mixed-requires': 'error', + 'n/no-process-env': 'error', + 'n/no-restricted-import': 'error', + 'n/no-restricted-require': 'error', // 'n/no-sync': 'error', - // 'n/prefer-global/buffer': 'error', - // 'n/prefer-global/console': 'error', - // 'n/prefer-global/process': 'error', - // 'n/prefer-global/text-decoder': 'error', - // 'n/prefer-global/text-encoder': 'error', - // 'n/prefer-global/url': 'error', - // 'n/prefer-global/url-search-params': 'error', - // 'n/prefer-promises/dns': 'error', + 'n/prefer-global/buffer': 'error', + 'n/prefer-global/console': 'error', + 'n/prefer-global/process': 'error', + 'n/prefer-global/text-decoder': 'error', + 'n/prefer-global/text-encoder': 'error', + 'n/prefer-global/url': 'error', + 'n/prefer-global/url-search-params': 'error', + 'n/prefer-promises/dns': 'error', // 'n/prefer-promises/fs': 'error', // promise // 'promise/catch-or-return': 'error', diff --git a/database/package.json b/database/package.json index 65b76ca14..abc69d21d 100644 --- a/database/package.json +++ b/database/package.json @@ -48,5 +48,8 @@ "ts-mysql-migrate": "^1.0.2", "typeorm": "^0.2.38", "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14" } } diff --git a/database/src/config/index.ts b/database/src/config/index.ts index ba41f11d4..2da46f777 100644 --- a/database/src/config/index.ts +++ b/database/src/config/index.ts @@ -1,4 +1,4 @@ -// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) +/* eslint-disable n/no-process-env */ import dotenv from 'dotenv' dotenv.config() diff --git a/dht-node/src/dht_node/index.ts b/dht-node/src/dht_node/index.ts index 004fc816e..dddebbfc7 100644 --- a/dht-node/src/dht_node/index.ts +++ b/dht-node/src/dht_node/index.ts @@ -24,6 +24,8 @@ type CommunityApi = { url: string } +type KeyPair = { publicKey: Buffer; secretKey: Buffer } + export const startDHT = async (topic: string): Promise => { try { const TOPIC = DHT.hash(Buffer.from(topic)) @@ -32,11 +34,11 @@ export const startDHT = async (topic: string): Promise => { CONFIG.FEDERATION_DHT_SEED ? Buffer.alloc(KEY_SECRET_SEEDBYTES, CONFIG.FEDERATION_DHT_SEED) : null, - ) + ) as KeyPair const pubKeyString = keyPair.publicKey.toString('hex') logger.info(`keyPairDHT: publicKey=${pubKeyString}`) logger.debug(`keyPairDHT: secretKey=${keyPair.secretKey.toString('hex')}`) - await writeHomeCommunityEntry(pubKeyString) + await writeHomeCommunityEntry(keyPair) const ownApiVersions = await writeFederatedHomeCommunityEntries(pubKeyString) logger.info(`ApiList: ${JSON.stringify(ownApiVersions)}`) @@ -212,13 +214,13 @@ async function writeFederatedHomeCommunityEntries(pubKey: string): Promise { +async function writeHomeCommunityEntry(keyPair: KeyPair): Promise { try { // check for existing homeCommunity entry let homeCom = await DbCommunity.findOne({ foreign: false }) if (homeCom) { // simply update the existing entry, but it MUST keep the ID and UUID because of possible relations - homeCom.publicKey = Buffer.from(pubKey) + homeCom.publicKey = keyPair.publicKey homeCom.url = CONFIG.FEDERATION_COMMUNITY_URL + '/api/' homeCom.name = CONFIG.COMMUNITY_NAME homeCom.description = CONFIG.COMMUNITY_DESCRIPTION @@ -228,7 +230,7 @@ async function writeHomeCommunityEntry(pubKey: string): Promise { // insert a new homecommunity entry including a new ID and a new but ensured unique UUID homeCom = new DbCommunity() homeCom.foreign = false - homeCom.publicKey = Buffer.from(pubKey) + homeCom.publicKey = keyPair.publicKey homeCom.communityUuid = await newCommunityUuid() homeCom.url = CONFIG.FEDERATION_COMMUNITY_URL + '/api/' homeCom.name = CONFIG.COMMUNITY_NAME