diff --git a/src/api/endpoint/api/user.js b/src/api/endpoint/api/user.js index 66d83ec78..908aa3161 100644 --- a/src/api/endpoint/api/user.js +++ b/src/api/endpoint/api/user.js @@ -17,13 +17,13 @@ export default function(route: Router, auth: IAuth) { route.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function(req: $RequestExtend, res: $Response, next: $NextFunctionVer) { let token = (req.body.name && req.body.password) - ? auth.aes_encrypt(new Buffer(req.body.name + ':' + req.body.password)).toString('base64') + ? auth.aesEncrypt(new Buffer(req.body.name + ':' + req.body.password)).toString('base64') : undefined; if (_.isNil(req.remote_user.name) === false) { res.status(201); return next({ ok: 'you are authenticated as \'' + req.remote_user.name + '\'', - token: token, + token, }); } else { auth.add_user(req.body.name, req.body.password, function(err, user) { diff --git a/src/api/middleware.js b/src/api/middleware.js index 01a98d7a3..10a636ee7 100644 --- a/src/api/middleware.js +++ b/src/api/middleware.js @@ -1,6 +1,5 @@ // @flow -import crypto from 'crypto'; import _ from 'lodash'; import { validate_name as utilValidateName, @@ -8,6 +7,7 @@ import { isObject, ErrorCode} from '../lib/utils'; import {HEADERS} from '../lib/constants'; +import {stringToMD5} from '../lib/crypto-utils'; import type {$ResponseExtend, $RequestExtend, $NextFunctionVer, IAuth} from '../../types'; import type {Config} from '@verdaccio/types'; @@ -97,18 +97,6 @@ export function anti_loop(config: Config) { }; } -/** - * Express doesn't do etags with requests <= 1024b - * we use md5 here, it works well on 1k+ bytes, but sucks with fewer data - * could improve performance using crc32 after benchmarks. - * @param {Object} data - * @return {String} - */ -function md5sum(data) { - return crypto.createHash('md5').update(data).digest('hex'); -} - - export function allow(auth: IAuth) { return function(action: string) { return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) { @@ -155,7 +143,7 @@ export function allow(auth: IAuth) { // don't send etags with errors if (!res.statusCode || (res.statusCode >= 200 && res.statusCode < 300)) { - res.header('ETag', '"' + md5sum(body) + '"'); + res.header('ETag', '"' + stringToMD5(body) + '"'); } } else { // send(null), send(204), etc. diff --git a/src/lib/auth.js b/src/lib/auth.js index 3b4d7f366..a9e06fcfc 100644 --- a/src/lib/auth.js +++ b/src/lib/auth.js @@ -1,13 +1,13 @@ // @flow import {loadPlugin} from '../lib/plugin-loader'; -import Crypto from 'crypto'; import jwt from 'jsonwebtoken'; import {ErrorCode} from './utils'; import type {Config, Logger, Callback} from '@verdaccio/types'; import type {$Response, NextFunction} from 'express'; import type {$RequestExtend} from '../../types'; +import {aesDecrypt, aesEncrypt} from './crypto-utils'; const LoggerApi = require('./logger'); /** @@ -254,7 +254,9 @@ class Auth { this.logger.warn('basic authentication is deprecated, please use JWT instead'); return credentials; } else if (scheme.toUpperCase() === 'BEARER') { - credentials = this.aes_decrypt(new Buffer(parts[1], 'base64')).toString('utf8'); + const token = new Buffer(parts[1], 'base64'); + + credentials = aesDecrypt(token, this.secret).toString('utf8'); return credentials; } else { return; @@ -331,25 +333,8 @@ class Auth { /** * Encrypt a string. */ - aes_encrypt(buf: Buffer): Buffer { - const c = Crypto.createCipher('aes192', this.secret); - const b1 = c.update(buf); - const b2 = c.final(); - return Buffer.concat([b1, b2]); - } - - /** - * Dencrypt a string. - */ - aes_decrypt(buf: Buffer ) { - try { - const c = Crypto.createDecipher('aes192', this.secret); - const b1 = c.update(buf); - const b2 = c.final(); - return Buffer.concat([b1, b2]); - } catch (_) { - return new Buffer(0); - } + aesEncrypt(buf: Buffer): Buffer { + return aesEncrypt(buf, this.secret); } } diff --git a/src/lib/config.js b/src/lib/config.js index 37d0556e4..4fcbe4388 100644 --- a/src/lib/config.js +++ b/src/lib/config.js @@ -1,7 +1,8 @@ +import {generateRandomHexString} from './crypto-utils'; + const assert = require('assert'); const _ = require('lodash'); const Error = require('http-errors'); -const Crypto = require('crypto'); const minimatch = require('minimatch'); const Utils = require('./utils'); @@ -164,7 +165,7 @@ class Config { // unique identifier of self server (or a cluster), used to avoid loops if (!self.server_id) { - self.server_id = Crypto.pseudoRandomBytes(6).toString('hex'); + self.server_id = generateRandomHexString(6); } } @@ -209,7 +210,7 @@ class Config { } // it generates a secret key // FUTURE: this might be an external secret key, perhaps whitin config file? - this.secret = Crypto.pseudoRandomBytes(32).toString('hex'); + this.secret = generateRandomHexString(32); return this.secret; } } diff --git a/src/lib/crypto-utils.js b/src/lib/crypto-utils.js new file mode 100644 index 000000000..1ec3e73b1 --- /dev/null +++ b/src/lib/crypto-utils.js @@ -0,0 +1,43 @@ +// @flow + +import {createDecipher, createCipher, createHash, pseudoRandomBytes} from 'crypto'; + +export const defaultAlgorithm = 'aes192'; + +export function aesEncrypt(buf: Buffer, secret: string): Buffer { + const c = createCipher(defaultAlgorithm, secret); + const b1 = c.update(buf); + const b2 = c.final(); + return Buffer.concat([b1, b2]); +} + + +export function aesDecrypt(buf: Buffer, secret: string) { + try { + const c = createDecipher(defaultAlgorithm, secret); + const b1 = c.update(buf); + const b2 = c.final(); + return Buffer.concat([b1, b2]); + } catch (_) { + return new Buffer(0); + } +} + +export function createTarballHash() { + return createHash('sha1'); +} + +/** + * Express doesn't do etags with requests <= 1024b + * we use md5 here, it works well on 1k+ bytes, but sucks with fewer data + * could improve performance using crc32 after benchmarks. + * @param {Object} data + * @return {String} + */ +export function stringToMD5(data: Buffer | string) { + return createHash('md5').update(data).digest('hex'); +} + +export function generateRandomHexString(length: number = 8) { + return pseudoRandomBytes(length).toString('hex'); +} diff --git a/src/lib/local-storage.js b/src/lib/local-storage.js index 9798f1292..f7f19c55c 100644 --- a/src/lib/local-storage.js +++ b/src/lib/local-storage.js @@ -2,7 +2,6 @@ /* eslint prefer-rest-params: 0 */ -import Crypto from 'crypto'; import assert from 'assert'; import fs from 'fs'; import Path from 'path'; @@ -13,27 +12,28 @@ import async from 'async'; import {ErrorCode, isObject, getLatestVersion, tagVersion, validate_name, semverSort, DIST_TAGS} from './utils'; import { generatePackageTemplate, normalizePackage, generateRevision, getLatestReadme, cleanUpReadme, - fileExist, noSuchFile, DEFAULT_REVISION, pkgFileName, +fileExist, noSuchFile, DEFAULT_REVISION, pkgFileName, } from './storage-utils'; +import {createTarballHash} from './crypto-utils'; import {loadPlugin} from '../lib/plugin-loader'; import LocalDatabase from '@verdaccio/local-storage'; import {UploadTarball, ReadTarball} from '@verdaccio/streams'; import type { - Package, - Config, - MergeTags, - Version, - DistFile, - Callback, - Logger, +Package, +Config, +MergeTags, +Version, +DistFile, +Callback, +Logger, } from '@verdaccio/types'; import type { - ILocalData, - IPackageStorage, +ILocalData, +IPackageStorage, } from '@verdaccio/local-storage'; import type { - IUploadTarball, - IReadTarball, +IUploadTarball, +IReadTarball, } from '@verdaccio/streams'; import type {IStorage, StringValue} from '../../types'; @@ -386,7 +386,7 @@ class LocalStorage implements IStorage { assert(validate_name(filename)); let length = 0; - const shaOneHash = Crypto.createHash('sha1'); + const shaOneHash = createTarballHash(); const uploadStream: IUploadTarball = new UploadTarball(); const _transform = uploadStream._transform; const storage = this._getLocalStorage(name); diff --git a/src/lib/storage-utils.js b/src/lib/storage-utils.js index 70d878999..ffe655b2a 100644 --- a/src/lib/storage-utils.js +++ b/src/lib/storage-utils.js @@ -1,9 +1,9 @@ // @flow import _ from 'lodash'; -import crypto from 'crypto'; import {ErrorCode, isObject, normalizeDistTags, DIST_TAGS} from './utils'; import Search from './search'; +import {generateRandomHexString} from '../lib/crypto-utils'; import type {Package, Version} from '@verdaccio/types'; import type {IStorage} from '../../types'; @@ -60,7 +60,7 @@ function normalizePackage(pkg: Package) { function generateRevision(rev: string): string { const _rev = rev.split('-'); - return ((+_rev[0] || 0) + 1) + '-' + crypto.pseudoRandomBytes(8).toString('hex'); + return ((+_rev[0] || 0) + 1) + '-' + generateRandomHexString(); } function getLatestReadme(pkg: Package): string { diff --git a/src/utils/string.js b/src/utils/string.js index 3dd698da7..a539e9ff2 100644 --- a/src/utils/string.js +++ b/src/utils/string.js @@ -1,13 +1,5 @@ // @flow -import crypto from 'crypto'; export function spliceURL(...args: Array): string { return Array.from(args).reduce((lastResult, current) => lastResult + current).replace(/([^:])(\/)+(.)/g, `$1/$3`); } - -/** - * Get MD5 from string - */ -export function stringToMD5(string: string): string { - return crypto.createHash('md5').update(string).digest('hex'); -} diff --git a/src/utils/user.js b/src/utils/user.js index d3ca87c0f..5609c72d8 100644 --- a/src/utils/user.js +++ b/src/utils/user.js @@ -1,5 +1,5 @@ // @flow -import {stringToMD5} from './string'; +import {stringToMD5} from '../lib/crypto-utils'; export const GRAVATAR_DEFAULT = 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm'; diff --git a/types/index.js b/types/index.js index 5e141354d..ef5ef7ac8 100644 --- a/types/index.js +++ b/types/index.js @@ -23,7 +23,7 @@ export interface IAuth { logger: Logger; secret: string; plugins: Array; - aes_encrypt(buf: Buffer): Buffer; + aesEncrypt(buf: Buffer): Buffer; apiJWTmiddleware(): $NextFunctionVer; webUIJWTmiddleware(): $NextFunctionVer; authenticate(user: string, password: string, cb: Callback): void;