@verdaccio/signature 6.0.0-6-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +3 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/build/index.d.ts +6 -0
- package/build/index.js +65 -0
- package/build/index.js.map +1 -0
- package/build/jwt-token.d.ts +10 -0
- package/build/jwt-token.js +40 -0
- package/build/jwt-token.js.map +1 -0
- package/build/legacy-signature/index.d.ts +22 -0
- package/build/legacy-signature/index.js +61 -0
- package/build/legacy-signature/index.js.map +1 -0
- package/build/signature.d.ts +3 -0
- package/build/signature.js +65 -0
- package/build/signature.js.map +1 -0
- package/build/token.d.ts +7 -0
- package/build/token.js +24 -0
- package/build/token.js.map +1 -0
- package/build/types.d.ts +5 -0
- package/build/types.js +6 -0
- package/build/types.js.map +1 -0
- package/build/utils.d.ts +28 -0
- package/build/utils.js +52 -0
- package/build/utils.js.map +1 -0
- package/jest.config.js +3 -0
- package/package.json +53 -0
- package/src/index.ts +10 -0
- package/src/jwt-token.ts +42 -0
- package/src/legacy-signature/index.ts +50 -0
- package/src/signature.ts +66 -0
- package/src/token.ts +18 -0
- package/src/types.ts +6 -0
- package/src/utils.ts +40 -0
- package/test/jwt.spec.ts +13 -0
- package/test/legacy-token-deprecated.spec.ts +23 -0
- package/test/legacy-token.spec.ts +19 -0
- package/test/utilts.spec.ts +18 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +17 -0
package/.babelrc
ADDED
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Verdaccio contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { aesDecryptDeprecated, aesEncryptDeprecated, generateRandomSecretKeyDeprecated, } from './legacy-signature';
|
|
2
|
+
export { aesDecrypt, aesEncrypt } from './signature';
|
|
3
|
+
export { signPayload, verifyPayload } from './jwt-token';
|
|
4
|
+
export * as utils from './utils';
|
|
5
|
+
export * as types from './types';
|
|
6
|
+
export { parseBasicPayload } from './token';
|
package/build/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "aesDecrypt", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _signature.aesDecrypt;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "aesDecryptDeprecated", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _legacySignature.aesDecryptDeprecated;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "aesEncrypt", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _signature.aesEncrypt;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "aesEncryptDeprecated", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () {
|
|
27
|
+
return _legacySignature.aesEncryptDeprecated;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(exports, "generateRandomSecretKeyDeprecated", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get: function () {
|
|
33
|
+
return _legacySignature.generateRandomSecretKeyDeprecated;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(exports, "parseBasicPayload", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () {
|
|
39
|
+
return _token.parseBasicPayload;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
Object.defineProperty(exports, "signPayload", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () {
|
|
45
|
+
return _jwtToken.signPayload;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
exports.utils = exports.types = void 0;
|
|
49
|
+
Object.defineProperty(exports, "verifyPayload", {
|
|
50
|
+
enumerable: true,
|
|
51
|
+
get: function () {
|
|
52
|
+
return _jwtToken.verifyPayload;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
var _legacySignature = require("./legacy-signature");
|
|
56
|
+
var _signature = require("./signature");
|
|
57
|
+
var _jwtToken = require("./jwt-token");
|
|
58
|
+
var _utils = _interopRequireWildcard(require("./utils"));
|
|
59
|
+
exports.utils = _utils;
|
|
60
|
+
var _types = _interopRequireWildcard(require("./types"));
|
|
61
|
+
exports.types = _types;
|
|
62
|
+
var _token = require("./token");
|
|
63
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
64
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
65
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["export {\n aesDecryptDeprecated,\n aesEncryptDeprecated,\n generateRandomSecretKeyDeprecated,\n} from './legacy-signature';\nexport { aesDecrypt, aesEncrypt } from './signature';\nexport { signPayload, verifyPayload } from './jwt-token';\nexport * as utils from './utils';\nexport * as types from './types';\nexport { parseBasicPayload } from './token';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAKA;AACA;AAAyD;AAAA;AAAA;AAAA;AAGzD;AAA4C;AAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { JWTSignOptions, RemoteUser } from '@verdaccio/types';
|
|
2
|
+
/**
|
|
3
|
+
* Sign the payload and return JWT
|
|
4
|
+
* https://github.com/auth0/node-jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback
|
|
5
|
+
* @param payload
|
|
6
|
+
* @param secretOrPrivateKey
|
|
7
|
+
* @param options
|
|
8
|
+
*/
|
|
9
|
+
export declare function signPayload(payload: RemoteUser, secretOrPrivateKey: string, options?: JWTSignOptions): Promise<string>;
|
|
10
|
+
export declare function verifyPayload(token: string, secretOrPrivateKey: string): RemoteUser;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.signPayload = signPayload;
|
|
7
|
+
exports.verifyPayload = verifyPayload;
|
|
8
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
9
|
+
var _jsonwebtoken = _interopRequireDefault(require("jsonwebtoken"));
|
|
10
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
+
const debug = (0, _debug.default)('verdaccio:auth:token:jwt');
|
|
12
|
+
/**
|
|
13
|
+
* Sign the payload and return JWT
|
|
14
|
+
* https://github.com/auth0/node-jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback
|
|
15
|
+
* @param payload
|
|
16
|
+
* @param secretOrPrivateKey
|
|
17
|
+
* @param options
|
|
18
|
+
*/
|
|
19
|
+
async function signPayload(payload, secretOrPrivateKey, options = {}) {
|
|
20
|
+
return new Promise(function (resolve, reject) {
|
|
21
|
+
debug('sign jwt token');
|
|
22
|
+
return _jsonwebtoken.default.sign(payload, secretOrPrivateKey,
|
|
23
|
+
// FIXME: upgrade to the latest library and types
|
|
24
|
+
// @ts-ignore
|
|
25
|
+
{
|
|
26
|
+
// 1 === 1ms (one millisecond)
|
|
27
|
+
notBefore: '1',
|
|
28
|
+
// Make sure the time will not rollback :)
|
|
29
|
+
...options
|
|
30
|
+
}, (error, token) => {
|
|
31
|
+
debug('error on sign jwt token');
|
|
32
|
+
return error ? reject(error) : resolve(token);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function verifyPayload(token, secretOrPrivateKey) {
|
|
37
|
+
debug('verify jwt token');
|
|
38
|
+
return _jsonwebtoken.default.verify(token, secretOrPrivateKey);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=jwt-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-token.js","names":["debug","buildDebug","signPayload","payload","secretOrPrivateKey","options","Promise","resolve","reject","jwt","sign","notBefore","error","token","verifyPayload","verify"],"sources":["../src/jwt-token.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport jwt from 'jsonwebtoken';\n\nimport { JWTSignOptions, RemoteUser } from '@verdaccio/types';\n\nconst debug = buildDebug('verdaccio:auth:token:jwt');\n/**\n * Sign the payload and return JWT\n * https://github.com/auth0/node-jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback\n * @param payload\n * @param secretOrPrivateKey\n * @param options\n */\nexport async function signPayload(\n payload: RemoteUser,\n secretOrPrivateKey: string,\n options: JWTSignOptions = {}\n): Promise<string> {\n return new Promise(function (resolve, reject): Promise<string> {\n debug('sign jwt token');\n return jwt.sign(\n payload,\n secretOrPrivateKey,\n // FIXME: upgrade to the latest library and types\n // @ts-ignore\n {\n // 1 === 1ms (one millisecond)\n notBefore: '1', // Make sure the time will not rollback :)\n ...options,\n },\n (error, token: string) => {\n debug('error on sign jwt token');\n return error ? reject(error) : resolve(token);\n }\n );\n });\n}\n\nexport function verifyPayload(token: string, secretOrPrivateKey: string): RemoteUser {\n debug('verify jwt token');\n return jwt.verify(token, secretOrPrivateKey) as RemoteUser;\n}\n"],"mappings":";;;;;;;AAAA;AACA;AAA+B;AAI/B,MAAMA,KAAK,GAAG,IAAAC,cAAU,EAAC,0BAA0B,CAAC;AACpD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAeC,WAAW,CAC/BC,OAAmB,EACnBC,kBAA0B,EAC1BC,OAAuB,GAAG,CAAC,CAAC,EACX;EACjB,OAAO,IAAIC,OAAO,CAAC,UAAUC,OAAO,EAAEC,MAAM,EAAmB;IAC7DR,KAAK,CAAC,gBAAgB,CAAC;IACvB,OAAOS,qBAAG,CAACC,IAAI,CACbP,OAAO,EACPC,kBAAkB;IAClB;IACA;IACA;MACE;MACAO,SAAS,EAAE,GAAG;MAAE;MAChB,GAAGN;IACL,CAAC,EACD,CAACO,KAAK,EAAEC,KAAa,KAAK;MACxBb,KAAK,CAAC,yBAAyB,CAAC;MAChC,OAAOY,KAAK,GAAGJ,MAAM,CAACI,KAAK,CAAC,GAAGL,OAAO,CAACM,KAAK,CAAC;IAC/C,CAAC,CACF;EACH,CAAC,CAAC;AACJ;AAEO,SAASC,aAAa,CAACD,KAAa,EAAET,kBAA0B,EAAc;EACnFJ,KAAK,CAAC,kBAAkB,CAAC;EACzB,OAAOS,qBAAG,CAACM,MAAM,CAACF,KAAK,EAAET,kBAAkB,CAAC;AAC9C"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
export declare const defaultAlgorithm = "aes192";
|
|
3
|
+
export declare const defaultTarballHashAlgorithm = "sha1";
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param buf
|
|
7
|
+
* @param secret
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
export declare function aesEncryptDeprecated(buf: Buffer, secret: string): Buffer;
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param buf
|
|
14
|
+
* @param secret
|
|
15
|
+
* @returns
|
|
16
|
+
*/
|
|
17
|
+
export declare function aesDecryptDeprecated(buf: Buffer, secret: string): Buffer;
|
|
18
|
+
export declare const TOKEN_VALID_LENGTH_DEPRECATED = 64;
|
|
19
|
+
/**
|
|
20
|
+
* Generate a secret key of 64 characters.
|
|
21
|
+
*/
|
|
22
|
+
export declare function generateRandomSecretKeyDeprecated(): string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.TOKEN_VALID_LENGTH_DEPRECATED = void 0;
|
|
7
|
+
exports.aesDecryptDeprecated = aesDecryptDeprecated;
|
|
8
|
+
exports.aesEncryptDeprecated = aesEncryptDeprecated;
|
|
9
|
+
exports.defaultTarballHashAlgorithm = exports.defaultAlgorithm = void 0;
|
|
10
|
+
exports.generateRandomSecretKeyDeprecated = generateRandomSecretKeyDeprecated;
|
|
11
|
+
var _crypto = require("crypto");
|
|
12
|
+
var _utils = require("../utils");
|
|
13
|
+
const defaultAlgorithm = 'aes192';
|
|
14
|
+
exports.defaultAlgorithm = defaultAlgorithm;
|
|
15
|
+
const defaultTarballHashAlgorithm = 'sha1';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
*
|
|
19
|
+
* @param buf
|
|
20
|
+
* @param secret
|
|
21
|
+
* @returns
|
|
22
|
+
*/
|
|
23
|
+
exports.defaultTarballHashAlgorithm = defaultTarballHashAlgorithm;
|
|
24
|
+
function aesEncryptDeprecated(buf, secret) {
|
|
25
|
+
// deprecated (it will be removed in Verdaccio 6), it is a breaking change
|
|
26
|
+
// https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options
|
|
27
|
+
// https://www.grainger.xyz/changing-from-cipher-to-cipheriv/
|
|
28
|
+
const c = (0, _crypto.createCipher)(defaultAlgorithm, secret);
|
|
29
|
+
const b1 = c.update(buf);
|
|
30
|
+
const b2 = c.final();
|
|
31
|
+
return Buffer.concat([b1, b2]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
*
|
|
36
|
+
* @param buf
|
|
37
|
+
* @param secret
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
function aesDecryptDeprecated(buf, secret) {
|
|
41
|
+
try {
|
|
42
|
+
// https://nodejs.org/api/crypto.html#crypto_crypto_createdecipher_algorithm_password_options
|
|
43
|
+
// https://www.grainger.xyz/changing-from-cipher-to-cipheriv/
|
|
44
|
+
const c = (0, _crypto.createDecipher)(defaultAlgorithm, secret);
|
|
45
|
+
const b1 = c.update(buf);
|
|
46
|
+
const b2 = c.final();
|
|
47
|
+
return Buffer.concat([b1, b2]);
|
|
48
|
+
} catch (_) {
|
|
49
|
+
return Buffer.alloc(0);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const TOKEN_VALID_LENGTH_DEPRECATED = 64;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Generate a secret key of 64 characters.
|
|
56
|
+
*/
|
|
57
|
+
exports.TOKEN_VALID_LENGTH_DEPRECATED = TOKEN_VALID_LENGTH_DEPRECATED;
|
|
58
|
+
function generateRandomSecretKeyDeprecated() {
|
|
59
|
+
return (0, _utils.generateRandomHexString)(6);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["defaultAlgorithm","defaultTarballHashAlgorithm","aesEncryptDeprecated","buf","secret","c","createCipher","b1","update","b2","final","Buffer","concat","aesDecryptDeprecated","createDecipher","_","alloc","TOKEN_VALID_LENGTH_DEPRECATED","generateRandomSecretKeyDeprecated","generateRandomHexString"],"sources":["../../src/legacy-signature/index.ts"],"sourcesContent":["import { createCipher, createDecipher } from 'crypto';\n\nimport { generateRandomHexString } from '../utils';\n\nexport const defaultAlgorithm = 'aes192';\nexport const defaultTarballHashAlgorithm = 'sha1';\n\n/**\n *\n * @param buf\n * @param secret\n * @returns\n */\nexport function aesEncryptDeprecated(buf: Buffer, secret: string): Buffer {\n // deprecated (it will be removed in Verdaccio 6), it is a breaking change\n // https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options\n // https://www.grainger.xyz/changing-from-cipher-to-cipheriv/\n const c = createCipher(defaultAlgorithm, secret);\n const b1 = c.update(buf);\n const b2 = c.final();\n return Buffer.concat([b1, b2]);\n}\n\n/**\n *\n * @param buf\n * @param secret\n * @returns\n */\nexport function aesDecryptDeprecated(buf: Buffer, secret: string): Buffer {\n try {\n // https://nodejs.org/api/crypto.html#crypto_crypto_createdecipher_algorithm_password_options\n // https://www.grainger.xyz/changing-from-cipher-to-cipheriv/\n const c = createDecipher(defaultAlgorithm, secret);\n const b1 = c.update(buf);\n const b2 = c.final();\n return Buffer.concat([b1, b2]);\n } catch (_) {\n return Buffer.alloc(0);\n }\n}\n\nexport const TOKEN_VALID_LENGTH_DEPRECATED = 64;\n\n/**\n * Generate a secret key of 64 characters.\n */\nexport function generateRandomSecretKeyDeprecated(): string {\n return generateRandomHexString(6);\n}\n"],"mappings":";;;;;;;;;;AAAA;AAEA;AAEO,MAAMA,gBAAgB,GAAG,QAAQ;AAAC;AAClC,MAAMC,2BAA2B,GAAG,MAAM;;AAEjD;AACA;AACA;AACA;AACA;AACA;AALA;AAMO,SAASC,oBAAoB,CAACC,GAAW,EAAEC,MAAc,EAAU;EACxE;EACA;EACA;EACA,MAAMC,CAAC,GAAG,IAAAC,oBAAY,EAACN,gBAAgB,EAAEI,MAAM,CAAC;EAChD,MAAMG,EAAE,GAAGF,CAAC,CAACG,MAAM,CAACL,GAAG,CAAC;EACxB,MAAMM,EAAE,GAAGJ,CAAC,CAACK,KAAK,EAAE;EACpB,OAAOC,MAAM,CAACC,MAAM,CAAC,CAACL,EAAE,EAAEE,EAAE,CAAC,CAAC;AAChC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO,SAASI,oBAAoB,CAACV,GAAW,EAAEC,MAAc,EAAU;EACxE,IAAI;IACF;IACA;IACA,MAAMC,CAAC,GAAG,IAAAS,sBAAc,EAACd,gBAAgB,EAAEI,MAAM,CAAC;IAClD,MAAMG,EAAE,GAAGF,CAAC,CAACG,MAAM,CAACL,GAAG,CAAC;IACxB,MAAMM,EAAE,GAAGJ,CAAC,CAACK,KAAK,EAAE;IACpB,OAAOC,MAAM,CAACC,MAAM,CAAC,CAACL,EAAE,EAAEE,EAAE,CAAC,CAAC;EAChC,CAAC,CAAC,OAAOM,CAAC,EAAE;IACV,OAAOJ,MAAM,CAACK,KAAK,CAAC,CAAC,CAAC;EACxB;AACF;AAEO,MAAMC,6BAA6B,GAAG,EAAE;;AAE/C;AACA;AACA;AAFA;AAGO,SAASC,iCAAiC,GAAW;EAC1D,OAAO,IAAAC,8BAAuB,EAAC,CAAC,CAAC;AACnC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.aesDecrypt = aesDecrypt;
|
|
7
|
+
exports.aesEncrypt = aesEncrypt;
|
|
8
|
+
exports.defaultAlgorithm = void 0;
|
|
9
|
+
var _crypto = require("crypto");
|
|
10
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
11
|
+
var _config = require("@verdaccio/config");
|
|
12
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
+
const debug = (0, _debug.default)('verdaccio:auth:token:legacy');
|
|
14
|
+
const defaultAlgorithm = process.env.VERDACCIO_LEGACY_ALGORITHM || 'aes-256-ctr';
|
|
15
|
+
exports.defaultAlgorithm = defaultAlgorithm;
|
|
16
|
+
const inputEncoding = 'utf8';
|
|
17
|
+
const outputEncoding = 'hex';
|
|
18
|
+
// Must be 256 bits (32 characters)
|
|
19
|
+
// https://stackoverflow.com/questions/50963160/invalid-key-length-in-crypto-createcipheriv#50963356
|
|
20
|
+
const VERDACCIO_LEGACY_ENCRYPTION_KEY = process.env.VERDACCIO_LEGACY_ENCRYPTION_KEY;
|
|
21
|
+
function aesEncrypt(value, key) {
|
|
22
|
+
// https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options
|
|
23
|
+
// https://www.grainger.xyz/posts/changing-from-cipher-to-cipheriv
|
|
24
|
+
debug('encrypt %o', value);
|
|
25
|
+
debug('algorithm %o', defaultAlgorithm);
|
|
26
|
+
// IV must be a buffer of length 16
|
|
27
|
+
const iv = Buffer.from((0, _crypto.randomBytes)(16));
|
|
28
|
+
const secretKey = VERDACCIO_LEGACY_ENCRYPTION_KEY || key;
|
|
29
|
+
const isKeyValid = (secretKey === null || secretKey === void 0 ? void 0 : secretKey.length) === _config.TOKEN_VALID_LENGTH;
|
|
30
|
+
debug('length secret key %o', secretKey === null || secretKey === void 0 ? void 0 : secretKey.length);
|
|
31
|
+
debug('is valid secret %o', isKeyValid);
|
|
32
|
+
if (!value || !secretKey || !isKeyValid) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const cipher = (0, _crypto.createCipheriv)(defaultAlgorithm, secretKey, iv);
|
|
36
|
+
let encrypted = cipher.update(value, inputEncoding, outputEncoding);
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
encrypted += cipher.final(outputEncoding);
|
|
39
|
+
const token = `${iv.toString('hex')}:${encrypted.toString()}`;
|
|
40
|
+
debug('token generated successfully');
|
|
41
|
+
return Buffer.from(token).toString('base64');
|
|
42
|
+
}
|
|
43
|
+
function aesDecrypt(value, key) {
|
|
44
|
+
try {
|
|
45
|
+
const buff = Buffer.from(value, 'base64');
|
|
46
|
+
const textParts = buff.toString().split(':');
|
|
47
|
+
|
|
48
|
+
// extract the IV from the first half of the value
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
const IV = Buffer.from(textParts.shift(), outputEncoding);
|
|
51
|
+
// extract the encrypted text without the IV
|
|
52
|
+
const encryptedText = Buffer.from(textParts.join(':'), outputEncoding);
|
|
53
|
+
const secretKey = VERDACCIO_LEGACY_ENCRYPTION_KEY || key;
|
|
54
|
+
// decipher the string
|
|
55
|
+
const decipher = (0, _crypto.createDecipheriv)(defaultAlgorithm, secretKey, IV);
|
|
56
|
+
// FIXME: fix type here should allow Buffer
|
|
57
|
+
let decrypted = decipher.update(encryptedText, outputEncoding, inputEncoding);
|
|
58
|
+
decrypted += decipher.final(inputEncoding);
|
|
59
|
+
debug('token decrypted successfully');
|
|
60
|
+
return decrypted.toString();
|
|
61
|
+
} catch (_) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=signature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.js","names":["debug","buildDebug","defaultAlgorithm","process","env","VERDACCIO_LEGACY_ALGORITHM","inputEncoding","outputEncoding","VERDACCIO_LEGACY_ENCRYPTION_KEY","aesEncrypt","value","key","iv","Buffer","from","randomBytes","secretKey","isKeyValid","length","TOKEN_VALID_LENGTH","cipher","createCipheriv","encrypted","update","final","token","toString","aesDecrypt","buff","textParts","split","IV","shift","encryptedText","join","decipher","createDecipheriv","decrypted","_"],"sources":["../src/signature.ts"],"sourcesContent":["import {\n BinaryToTextEncoding,\n CharacterEncoding,\n createCipheriv,\n createDecipheriv,\n randomBytes,\n} from 'crypto';\nimport buildDebug from 'debug';\n\nimport { TOKEN_VALID_LENGTH } from '@verdaccio/config';\n\nconst debug = buildDebug('verdaccio:auth:token:legacy');\n\nexport const defaultAlgorithm = process.env.VERDACCIO_LEGACY_ALGORITHM || 'aes-256-ctr';\nconst inputEncoding: CharacterEncoding = 'utf8';\nconst outputEncoding: BinaryToTextEncoding = 'hex';\n// Must be 256 bits (32 characters)\n// https://stackoverflow.com/questions/50963160/invalid-key-length-in-crypto-createcipheriv#50963356\nconst VERDACCIO_LEGACY_ENCRYPTION_KEY = process.env.VERDACCIO_LEGACY_ENCRYPTION_KEY;\n\nexport function aesEncrypt(value: string, key: string): string | void {\n // https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options\n // https://www.grainger.xyz/posts/changing-from-cipher-to-cipheriv\n debug('encrypt %o', value);\n debug('algorithm %o', defaultAlgorithm);\n // IV must be a buffer of length 16\n const iv = Buffer.from(randomBytes(16));\n const secretKey = VERDACCIO_LEGACY_ENCRYPTION_KEY || key;\n const isKeyValid = secretKey?.length === TOKEN_VALID_LENGTH;\n debug('length secret key %o', secretKey?.length);\n debug('is valid secret %o', isKeyValid);\n if (!value || !secretKey || !isKeyValid) {\n return;\n }\n\n const cipher = createCipheriv(defaultAlgorithm, secretKey, iv);\n let encrypted = cipher.update(value, inputEncoding, outputEncoding);\n // @ts-ignore\n encrypted += cipher.final(outputEncoding);\n const token = `${iv.toString('hex')}:${encrypted.toString()}`;\n debug('token generated successfully');\n return Buffer.from(token).toString('base64');\n}\n\nexport function aesDecrypt(value: string, key: string): string | void {\n try {\n const buff = Buffer.from(value, 'base64');\n const textParts = buff.toString().split(':');\n\n // extract the IV from the first half of the value\n // @ts-ignore\n const IV = Buffer.from(textParts.shift(), outputEncoding);\n // extract the encrypted text without the IV\n const encryptedText = Buffer.from(textParts.join(':'), outputEncoding);\n const secretKey = VERDACCIO_LEGACY_ENCRYPTION_KEY || key;\n // decipher the string\n const decipher = createDecipheriv(defaultAlgorithm, secretKey, IV);\n // FIXME: fix type here should allow Buffer\n let decrypted = decipher.update(encryptedText as any, outputEncoding, inputEncoding);\n decrypted += decipher.final(inputEncoding);\n debug('token decrypted successfully');\n return decrypted.toString();\n } catch (_: any) {\n return;\n }\n}\n"],"mappings":";;;;;;;;AAAA;AAOA;AAEA;AAAuD;AAEvD,MAAMA,KAAK,GAAG,IAAAC,cAAU,EAAC,6BAA6B,CAAC;AAEhD,MAAMC,gBAAgB,GAAGC,OAAO,CAACC,GAAG,CAACC,0BAA0B,IAAI,aAAa;AAAC;AACxF,MAAMC,aAAgC,GAAG,MAAM;AAC/C,MAAMC,cAAoC,GAAG,KAAK;AAClD;AACA;AACA,MAAMC,+BAA+B,GAAGL,OAAO,CAACC,GAAG,CAACI,+BAA+B;AAE5E,SAASC,UAAU,CAACC,KAAa,EAAEC,GAAW,EAAiB;EACpE;EACA;EACAX,KAAK,CAAC,YAAY,EAAEU,KAAK,CAAC;EAC1BV,KAAK,CAAC,cAAc,EAAEE,gBAAgB,CAAC;EACvC;EACA,MAAMU,EAAE,GAAGC,MAAM,CAACC,IAAI,CAAC,IAAAC,mBAAW,EAAC,EAAE,CAAC,CAAC;EACvC,MAAMC,SAAS,GAAGR,+BAA+B,IAAIG,GAAG;EACxD,MAAMM,UAAU,GAAG,CAAAD,SAAS,aAATA,SAAS,uBAATA,SAAS,CAAEE,MAAM,MAAKC,0BAAkB;EAC3DnB,KAAK,CAAC,sBAAsB,EAAEgB,SAAS,aAATA,SAAS,uBAATA,SAAS,CAAEE,MAAM,CAAC;EAChDlB,KAAK,CAAC,oBAAoB,EAAEiB,UAAU,CAAC;EACvC,IAAI,CAACP,KAAK,IAAI,CAACM,SAAS,IAAI,CAACC,UAAU,EAAE;IACvC;EACF;EAEA,MAAMG,MAAM,GAAG,IAAAC,sBAAc,EAACnB,gBAAgB,EAAEc,SAAS,EAAEJ,EAAE,CAAC;EAC9D,IAAIU,SAAS,GAAGF,MAAM,CAACG,MAAM,CAACb,KAAK,EAAEJ,aAAa,EAAEC,cAAc,CAAC;EACnE;EACAe,SAAS,IAAIF,MAAM,CAACI,KAAK,CAACjB,cAAc,CAAC;EACzC,MAAMkB,KAAK,GAAI,GAAEb,EAAE,CAACc,QAAQ,CAAC,KAAK,CAAE,IAAGJ,SAAS,CAACI,QAAQ,EAAG,EAAC;EAC7D1B,KAAK,CAAC,8BAA8B,CAAC;EACrC,OAAOa,MAAM,CAACC,IAAI,CAACW,KAAK,CAAC,CAACC,QAAQ,CAAC,QAAQ,CAAC;AAC9C;AAEO,SAASC,UAAU,CAACjB,KAAa,EAAEC,GAAW,EAAiB;EACpE,IAAI;IACF,MAAMiB,IAAI,GAAGf,MAAM,CAACC,IAAI,CAACJ,KAAK,EAAE,QAAQ,CAAC;IACzC,MAAMmB,SAAS,GAAGD,IAAI,CAACF,QAAQ,EAAE,CAACI,KAAK,CAAC,GAAG,CAAC;;IAE5C;IACA;IACA,MAAMC,EAAE,GAAGlB,MAAM,CAACC,IAAI,CAACe,SAAS,CAACG,KAAK,EAAE,EAAEzB,cAAc,CAAC;IACzD;IACA,MAAM0B,aAAa,GAAGpB,MAAM,CAACC,IAAI,CAACe,SAAS,CAACK,IAAI,CAAC,GAAG,CAAC,EAAE3B,cAAc,CAAC;IACtE,MAAMS,SAAS,GAAGR,+BAA+B,IAAIG,GAAG;IACxD;IACA,MAAMwB,QAAQ,GAAG,IAAAC,wBAAgB,EAAClC,gBAAgB,EAAEc,SAAS,EAAEe,EAAE,CAAC;IAClE;IACA,IAAIM,SAAS,GAAGF,QAAQ,CAACZ,MAAM,CAACU,aAAa,EAAS1B,cAAc,EAAED,aAAa,CAAC;IACpF+B,SAAS,IAAIF,QAAQ,CAACX,KAAK,CAAClB,aAAa,CAAC;IAC1CN,KAAK,CAAC,8BAA8B,CAAC;IACrC,OAAOqC,SAAS,CAACX,QAAQ,EAAE;EAC7B,CAAC,CAAC,OAAOY,CAAM,EAAE;IACf;EACF;AACF"}
|
package/build/token.d.ts
ADDED
package/build/token.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.parseBasicPayload = parseBasicPayload;
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param credentials
|
|
10
|
+
* @returns
|
|
11
|
+
*/
|
|
12
|
+
function parseBasicPayload(credentials) {
|
|
13
|
+
const index = credentials.indexOf(':');
|
|
14
|
+
if (index < 0) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const user = credentials.slice(0, index);
|
|
18
|
+
const password = credentials.slice(index + 1);
|
|
19
|
+
return {
|
|
20
|
+
user,
|
|
21
|
+
password
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.js","names":["parseBasicPayload","credentials","index","indexOf","user","slice","password"],"sources":["../src/token.ts"],"sourcesContent":["import { BasicPayload } from './types';\n\n/**\n *\n * @param credentials\n * @returns\n */\nexport function parseBasicPayload(credentials: string): BasicPayload {\n const index = credentials.indexOf(':');\n if (index < 0) {\n return;\n }\n\n const user: string = credentials.slice(0, index);\n const password: string = credentials.slice(index + 1);\n\n return { user, password };\n}\n"],"mappings":";;;;;;AAEA;AACA;AACA;AACA;AACA;AACO,SAASA,iBAAiB,CAACC,WAAmB,EAAgB;EACnE,MAAMC,KAAK,GAAGD,WAAW,CAACE,OAAO,CAAC,GAAG,CAAC;EACtC,IAAID,KAAK,GAAG,CAAC,EAAE;IACb;EACF;EAEA,MAAME,IAAY,GAAGH,WAAW,CAACI,KAAK,CAAC,CAAC,EAAEH,KAAK,CAAC;EAChD,MAAMI,QAAgB,GAAGL,WAAW,CAACI,KAAK,CAACH,KAAK,GAAG,CAAC,CAAC;EAErD,OAAO;IAAEE,IAAI;IAAEE;EAAS,CAAC;AAC3B"}
|
package/build/types.d.ts
ADDED
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["export interface AESPayload {\n user: string;\n password: string;\n}\n\nexport type BasicPayload = AESPayload | void;\n"],"mappings":""}
|
package/build/utils.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { Hash } from 'crypto';
|
|
4
|
+
export declare const defaultTarballHashAlgorithm = "sha1";
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export declare function createTarballHash(algorithm?: string): Hash;
|
|
10
|
+
/**
|
|
11
|
+
* Express doesn't do ETAGS with requests <= 1024b
|
|
12
|
+
* we use md5 here, it works well on 1k+ bytes, but with fewer data
|
|
13
|
+
* could improve performance using crc32 after benchmarks.
|
|
14
|
+
* @param {Object} data
|
|
15
|
+
* @return {String}
|
|
16
|
+
*/
|
|
17
|
+
export declare function stringToMD5(data: Buffer | string): string;
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param length
|
|
21
|
+
* @returns
|
|
22
|
+
*/
|
|
23
|
+
export declare function generateRandomHexString(length?: number): string;
|
|
24
|
+
export declare const TOKEN_VALID_LENGTH = 32;
|
|
25
|
+
/**
|
|
26
|
+
* Generate a secret of 32 characters.
|
|
27
|
+
*/
|
|
28
|
+
export declare function generateRandomSecretKey(): string;
|
package/build/utils.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.TOKEN_VALID_LENGTH = void 0;
|
|
7
|
+
exports.createTarballHash = createTarballHash;
|
|
8
|
+
exports.defaultTarballHashAlgorithm = void 0;
|
|
9
|
+
exports.generateRandomHexString = generateRandomHexString;
|
|
10
|
+
exports.generateRandomSecretKey = generateRandomSecretKey;
|
|
11
|
+
exports.stringToMD5 = stringToMD5;
|
|
12
|
+
var _crypto = require("crypto");
|
|
13
|
+
const defaultTarballHashAlgorithm = 'sha1';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
*
|
|
17
|
+
* @returns
|
|
18
|
+
*/
|
|
19
|
+
exports.defaultTarballHashAlgorithm = defaultTarballHashAlgorithm;
|
|
20
|
+
function createTarballHash(algorithm = defaultTarballHashAlgorithm) {
|
|
21
|
+
return (0, _crypto.createHash)(algorithm);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Express doesn't do ETAGS with requests <= 1024b
|
|
26
|
+
* we use md5 here, it works well on 1k+ bytes, but with fewer data
|
|
27
|
+
* could improve performance using crc32 after benchmarks.
|
|
28
|
+
* @param {Object} data
|
|
29
|
+
* @return {String}
|
|
30
|
+
*/
|
|
31
|
+
function stringToMD5(data) {
|
|
32
|
+
return (0, _crypto.createHash)('md5').update(data).digest('hex');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @param length
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
function generateRandomHexString(length = 8) {
|
|
41
|
+
return (0, _crypto.pseudoRandomBytes)(length).toString('hex');
|
|
42
|
+
}
|
|
43
|
+
const TOKEN_VALID_LENGTH = 32;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Generate a secret of 32 characters.
|
|
47
|
+
*/
|
|
48
|
+
exports.TOKEN_VALID_LENGTH = TOKEN_VALID_LENGTH;
|
|
49
|
+
function generateRandomSecretKey() {
|
|
50
|
+
return (0, _crypto.randomBytes)(TOKEN_VALID_LENGTH).toString('base64').substring(0, TOKEN_VALID_LENGTH);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","names":["defaultTarballHashAlgorithm","createTarballHash","algorithm","createHash","stringToMD5","data","update","digest","generateRandomHexString","length","pseudoRandomBytes","toString","TOKEN_VALID_LENGTH","generateRandomSecretKey","randomBytes","substring"],"sources":["../src/utils.ts"],"sourcesContent":["import { Hash, createHash, pseudoRandomBytes, randomBytes } from 'crypto';\n\nexport const defaultTarballHashAlgorithm = 'sha1';\n\n/**\n *\n * @returns\n */\nexport function createTarballHash(algorithm = defaultTarballHashAlgorithm): Hash {\n return createHash(algorithm);\n}\n\n/**\n * Express doesn't do ETAGS with requests <= 1024b\n * we use md5 here, it works well on 1k+ bytes, but with fewer data\n * could improve performance using crc32 after benchmarks.\n * @param {Object} data\n * @return {String}\n */\nexport function stringToMD5(data: Buffer | string): string {\n return createHash('md5').update(data).digest('hex');\n}\n\n/**\n *\n * @param length\n * @returns\n */\nexport function generateRandomHexString(length = 8): string {\n return pseudoRandomBytes(length).toString('hex');\n}\n\nexport const TOKEN_VALID_LENGTH = 32;\n\n/**\n * Generate a secret of 32 characters.\n */\nexport function generateRandomSecretKey(): string {\n return randomBytes(TOKEN_VALID_LENGTH).toString('base64').substring(0, TOKEN_VALID_LENGTH);\n}\n"],"mappings":";;;;;;;;;;;AAAA;AAEO,MAAMA,2BAA2B,GAAG,MAAM;;AAEjD;AACA;AACA;AACA;AAHA;AAIO,SAASC,iBAAiB,CAACC,SAAS,GAAGF,2BAA2B,EAAQ;EAC/E,OAAO,IAAAG,kBAAU,EAACD,SAAS,CAAC;AAC9B;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASE,WAAW,CAACC,IAAqB,EAAU;EACzD,OAAO,IAAAF,kBAAU,EAAC,KAAK,CAAC,CAACG,MAAM,CAACD,IAAI,CAAC,CAACE,MAAM,CAAC,KAAK,CAAC;AACrD;;AAEA;AACA;AACA;AACA;AACA;AACO,SAASC,uBAAuB,CAACC,MAAM,GAAG,CAAC,EAAU;EAC1D,OAAO,IAAAC,yBAAiB,EAACD,MAAM,CAAC,CAACE,QAAQ,CAAC,KAAK,CAAC;AAClD;AAEO,MAAMC,kBAAkB,GAAG,EAAE;;AAEpC;AACA;AACA;AAFA;AAGO,SAASC,uBAAuB,GAAW;EAChD,OAAO,IAAAC,mBAAW,EAACF,kBAAkB,CAAC,CAACD,QAAQ,CAAC,QAAQ,CAAC,CAACI,SAAS,CAAC,CAAC,EAAEH,kBAAkB,CAAC;AAC5F"}
|
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@verdaccio/signature",
|
|
3
|
+
"version": "6.0.0-6-next.2",
|
|
4
|
+
"description": "verdaccio signature utils",
|
|
5
|
+
"main": "./build/index.js",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Juan Picado",
|
|
9
|
+
"email": "juanpicado19@gmail.com"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "https",
|
|
13
|
+
"url": "https://github.com/verdaccio/verdaccio"
|
|
14
|
+
},
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"homepage": "https://verdaccio.org",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"private",
|
|
19
|
+
"package",
|
|
20
|
+
"repository",
|
|
21
|
+
"registry",
|
|
22
|
+
"enterprise",
|
|
23
|
+
"modules",
|
|
24
|
+
"proxy",
|
|
25
|
+
"server",
|
|
26
|
+
"verdaccio"
|
|
27
|
+
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=12"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"jsonwebtoken": "9.0.0",
|
|
33
|
+
"debug": "4.3.4",
|
|
34
|
+
"lodash": "4.17.21"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@verdaccio/config": "6.0.0-6-next.63",
|
|
38
|
+
"@verdaccio/types": "11.0.0-6-next.22"
|
|
39
|
+
},
|
|
40
|
+
"funding": {
|
|
41
|
+
"type": "opencollective",
|
|
42
|
+
"url": "https://opencollective.com/verdaccio"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"clean": "rimraf ./build",
|
|
46
|
+
"test": "jest",
|
|
47
|
+
"type-check": "tsc --noEmit -p tsconfig.build.json",
|
|
48
|
+
"build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json",
|
|
49
|
+
"build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
|
|
50
|
+
"watch": "pnpm build:js -- --watch",
|
|
51
|
+
"build": "pnpm run build:js && pnpm run build:types"
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export {
|
|
2
|
+
aesDecryptDeprecated,
|
|
3
|
+
aesEncryptDeprecated,
|
|
4
|
+
generateRandomSecretKeyDeprecated,
|
|
5
|
+
} from './legacy-signature';
|
|
6
|
+
export { aesDecrypt, aesEncrypt } from './signature';
|
|
7
|
+
export { signPayload, verifyPayload } from './jwt-token';
|
|
8
|
+
export * as utils from './utils';
|
|
9
|
+
export * as types from './types';
|
|
10
|
+
export { parseBasicPayload } from './token';
|
package/src/jwt-token.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import buildDebug from 'debug';
|
|
2
|
+
import jwt from 'jsonwebtoken';
|
|
3
|
+
|
|
4
|
+
import { JWTSignOptions, RemoteUser } from '@verdaccio/types';
|
|
5
|
+
|
|
6
|
+
const debug = buildDebug('verdaccio:auth:token:jwt');
|
|
7
|
+
/**
|
|
8
|
+
* Sign the payload and return JWT
|
|
9
|
+
* https://github.com/auth0/node-jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback
|
|
10
|
+
* @param payload
|
|
11
|
+
* @param secretOrPrivateKey
|
|
12
|
+
* @param options
|
|
13
|
+
*/
|
|
14
|
+
export async function signPayload(
|
|
15
|
+
payload: RemoteUser,
|
|
16
|
+
secretOrPrivateKey: string,
|
|
17
|
+
options: JWTSignOptions = {}
|
|
18
|
+
): Promise<string> {
|
|
19
|
+
return new Promise(function (resolve, reject): Promise<string> {
|
|
20
|
+
debug('sign jwt token');
|
|
21
|
+
return jwt.sign(
|
|
22
|
+
payload,
|
|
23
|
+
secretOrPrivateKey,
|
|
24
|
+
// FIXME: upgrade to the latest library and types
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
{
|
|
27
|
+
// 1 === 1ms (one millisecond)
|
|
28
|
+
notBefore: '1', // Make sure the time will not rollback :)
|
|
29
|
+
...options,
|
|
30
|
+
},
|
|
31
|
+
(error, token: string) => {
|
|
32
|
+
debug('error on sign jwt token');
|
|
33
|
+
return error ? reject(error) : resolve(token);
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function verifyPayload(token: string, secretOrPrivateKey: string): RemoteUser {
|
|
40
|
+
debug('verify jwt token');
|
|
41
|
+
return jwt.verify(token, secretOrPrivateKey) as RemoteUser;
|
|
42
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createCipher, createDecipher } from 'crypto';
|
|
2
|
+
|
|
3
|
+
import { generateRandomHexString } from '../utils';
|
|
4
|
+
|
|
5
|
+
export const defaultAlgorithm = 'aes192';
|
|
6
|
+
export const defaultTarballHashAlgorithm = 'sha1';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @param buf
|
|
11
|
+
* @param secret
|
|
12
|
+
* @returns
|
|
13
|
+
*/
|
|
14
|
+
export function aesEncryptDeprecated(buf: Buffer, secret: string): Buffer {
|
|
15
|
+
// deprecated (it will be removed in Verdaccio 6), it is a breaking change
|
|
16
|
+
// https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options
|
|
17
|
+
// https://www.grainger.xyz/changing-from-cipher-to-cipheriv/
|
|
18
|
+
const c = createCipher(defaultAlgorithm, secret);
|
|
19
|
+
const b1 = c.update(buf);
|
|
20
|
+
const b2 = c.final();
|
|
21
|
+
return Buffer.concat([b1, b2]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
*
|
|
26
|
+
* @param buf
|
|
27
|
+
* @param secret
|
|
28
|
+
* @returns
|
|
29
|
+
*/
|
|
30
|
+
export function aesDecryptDeprecated(buf: Buffer, secret: string): Buffer {
|
|
31
|
+
try {
|
|
32
|
+
// https://nodejs.org/api/crypto.html#crypto_crypto_createdecipher_algorithm_password_options
|
|
33
|
+
// https://www.grainger.xyz/changing-from-cipher-to-cipheriv/
|
|
34
|
+
const c = createDecipher(defaultAlgorithm, secret);
|
|
35
|
+
const b1 = c.update(buf);
|
|
36
|
+
const b2 = c.final();
|
|
37
|
+
return Buffer.concat([b1, b2]);
|
|
38
|
+
} catch (_) {
|
|
39
|
+
return Buffer.alloc(0);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const TOKEN_VALID_LENGTH_DEPRECATED = 64;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Generate a secret key of 64 characters.
|
|
47
|
+
*/
|
|
48
|
+
export function generateRandomSecretKeyDeprecated(): string {
|
|
49
|
+
return generateRandomHexString(6);
|
|
50
|
+
}
|
package/src/signature.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BinaryToTextEncoding,
|
|
3
|
+
CharacterEncoding,
|
|
4
|
+
createCipheriv,
|
|
5
|
+
createDecipheriv,
|
|
6
|
+
randomBytes,
|
|
7
|
+
} from 'crypto';
|
|
8
|
+
import buildDebug from 'debug';
|
|
9
|
+
|
|
10
|
+
import { TOKEN_VALID_LENGTH } from '@verdaccio/config';
|
|
11
|
+
|
|
12
|
+
const debug = buildDebug('verdaccio:auth:token:legacy');
|
|
13
|
+
|
|
14
|
+
export const defaultAlgorithm = process.env.VERDACCIO_LEGACY_ALGORITHM || 'aes-256-ctr';
|
|
15
|
+
const inputEncoding: CharacterEncoding = 'utf8';
|
|
16
|
+
const outputEncoding: BinaryToTextEncoding = 'hex';
|
|
17
|
+
// Must be 256 bits (32 characters)
|
|
18
|
+
// https://stackoverflow.com/questions/50963160/invalid-key-length-in-crypto-createcipheriv#50963356
|
|
19
|
+
const VERDACCIO_LEGACY_ENCRYPTION_KEY = process.env.VERDACCIO_LEGACY_ENCRYPTION_KEY;
|
|
20
|
+
|
|
21
|
+
export function aesEncrypt(value: string, key: string): string | void {
|
|
22
|
+
// https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options
|
|
23
|
+
// https://www.grainger.xyz/posts/changing-from-cipher-to-cipheriv
|
|
24
|
+
debug('encrypt %o', value);
|
|
25
|
+
debug('algorithm %o', defaultAlgorithm);
|
|
26
|
+
// IV must be a buffer of length 16
|
|
27
|
+
const iv = Buffer.from(randomBytes(16));
|
|
28
|
+
const secretKey = VERDACCIO_LEGACY_ENCRYPTION_KEY || key;
|
|
29
|
+
const isKeyValid = secretKey?.length === TOKEN_VALID_LENGTH;
|
|
30
|
+
debug('length secret key %o', secretKey?.length);
|
|
31
|
+
debug('is valid secret %o', isKeyValid);
|
|
32
|
+
if (!value || !secretKey || !isKeyValid) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const cipher = createCipheriv(defaultAlgorithm, secretKey, iv);
|
|
37
|
+
let encrypted = cipher.update(value, inputEncoding, outputEncoding);
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
encrypted += cipher.final(outputEncoding);
|
|
40
|
+
const token = `${iv.toString('hex')}:${encrypted.toString()}`;
|
|
41
|
+
debug('token generated successfully');
|
|
42
|
+
return Buffer.from(token).toString('base64');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function aesDecrypt(value: string, key: string): string | void {
|
|
46
|
+
try {
|
|
47
|
+
const buff = Buffer.from(value, 'base64');
|
|
48
|
+
const textParts = buff.toString().split(':');
|
|
49
|
+
|
|
50
|
+
// extract the IV from the first half of the value
|
|
51
|
+
// @ts-ignore
|
|
52
|
+
const IV = Buffer.from(textParts.shift(), outputEncoding);
|
|
53
|
+
// extract the encrypted text without the IV
|
|
54
|
+
const encryptedText = Buffer.from(textParts.join(':'), outputEncoding);
|
|
55
|
+
const secretKey = VERDACCIO_LEGACY_ENCRYPTION_KEY || key;
|
|
56
|
+
// decipher the string
|
|
57
|
+
const decipher = createDecipheriv(defaultAlgorithm, secretKey, IV);
|
|
58
|
+
// FIXME: fix type here should allow Buffer
|
|
59
|
+
let decrypted = decipher.update(encryptedText as any, outputEncoding, inputEncoding);
|
|
60
|
+
decrypted += decipher.final(inputEncoding);
|
|
61
|
+
debug('token decrypted successfully');
|
|
62
|
+
return decrypted.toString();
|
|
63
|
+
} catch (_: any) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/token.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BasicPayload } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param credentials
|
|
6
|
+
* @returns
|
|
7
|
+
*/
|
|
8
|
+
export function parseBasicPayload(credentials: string): BasicPayload {
|
|
9
|
+
const index = credentials.indexOf(':');
|
|
10
|
+
if (index < 0) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const user: string = credentials.slice(0, index);
|
|
15
|
+
const password: string = credentials.slice(index + 1);
|
|
16
|
+
|
|
17
|
+
return { user, password };
|
|
18
|
+
}
|
package/src/types.ts
ADDED
package/src/utils.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Hash, createHash, pseudoRandomBytes, randomBytes } from 'crypto';
|
|
2
|
+
|
|
3
|
+
export const defaultTarballHashAlgorithm = 'sha1';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export function createTarballHash(algorithm = defaultTarballHashAlgorithm): Hash {
|
|
10
|
+
return createHash(algorithm);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Express doesn't do ETAGS with requests <= 1024b
|
|
15
|
+
* we use md5 here, it works well on 1k+ bytes, but with fewer data
|
|
16
|
+
* could improve performance using crc32 after benchmarks.
|
|
17
|
+
* @param {Object} data
|
|
18
|
+
* @return {String}
|
|
19
|
+
*/
|
|
20
|
+
export function stringToMD5(data: Buffer | string): string {
|
|
21
|
+
return createHash('md5').update(data).digest('hex');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
*
|
|
26
|
+
* @param length
|
|
27
|
+
* @returns
|
|
28
|
+
*/
|
|
29
|
+
export function generateRandomHexString(length = 8): string {
|
|
30
|
+
return pseudoRandomBytes(length).toString('hex');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const TOKEN_VALID_LENGTH = 32;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Generate a secret of 32 characters.
|
|
37
|
+
*/
|
|
38
|
+
export function generateRandomSecretKey(): string {
|
|
39
|
+
return randomBytes(TOKEN_VALID_LENGTH).toString('base64').substring(0, TOKEN_VALID_LENGTH);
|
|
40
|
+
}
|
package/test/jwt.spec.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createRemoteUser } from '@verdaccio/config';
|
|
2
|
+
|
|
3
|
+
import { signPayload, verifyPayload } from '../src';
|
|
4
|
+
|
|
5
|
+
describe('verifyJWTPayload', () => {
|
|
6
|
+
test('should verify the token and return a remote user', async () => {
|
|
7
|
+
const remoteUser = createRemoteUser('foo', []);
|
|
8
|
+
const token = await signPayload(remoteUser, '12345');
|
|
9
|
+
const verifiedToken = verifyPayload(token, '12345');
|
|
10
|
+
expect(verifiedToken.groups).toEqual(remoteUser.groups);
|
|
11
|
+
expect(verifiedToken.name).toEqual(remoteUser.name);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
aesDecryptDeprecated,
|
|
3
|
+
aesEncryptDeprecated,
|
|
4
|
+
generateRandomSecretKeyDeprecated,
|
|
5
|
+
} from '../src';
|
|
6
|
+
|
|
7
|
+
describe('test deprecated crypto utils', () => {
|
|
8
|
+
test('decrypt payload flow', () => {
|
|
9
|
+
const secret = generateRandomSecretKeyDeprecated();
|
|
10
|
+
const payload = 'juan:password';
|
|
11
|
+
const token = aesEncryptDeprecated(Buffer.from(payload), secret);
|
|
12
|
+
const data = aesDecryptDeprecated(token, secret);
|
|
13
|
+
|
|
14
|
+
expect(data.toString()).toEqual(payload.toString());
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('crypt fails if secret is incorrect', () => {
|
|
18
|
+
const payload = 'juan:password';
|
|
19
|
+
expect(aesEncryptDeprecated(Buffer.from(payload), 'fake_token').toString()).not.toEqual(
|
|
20
|
+
Buffer.from(payload)
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { aesDecrypt, aesEncrypt } from '../src';
|
|
2
|
+
|
|
3
|
+
describe('test crypto utils', () => {
|
|
4
|
+
test('decrypt payload flow', () => {
|
|
5
|
+
const secret = 'f5bb945cc57fea2f25961e1bd6fb3c89';
|
|
6
|
+
const payload = 'juan:password';
|
|
7
|
+
const token = aesEncrypt(payload, secret) as string;
|
|
8
|
+
const data = aesDecrypt(token, secret);
|
|
9
|
+
|
|
10
|
+
expect(payload).toEqual(data);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('crypt fails if secret is incorrect', () => {
|
|
14
|
+
const secret = 'f5bb945cc57fea2f25961e1bd6fb3c89_TO_LONG';
|
|
15
|
+
const payload = 'juan';
|
|
16
|
+
const token = aesEncrypt(payload, secret) as string;
|
|
17
|
+
expect(token).toBeUndefined();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TOKEN_VALID_LENGTH,
|
|
3
|
+
createTarballHash,
|
|
4
|
+
generateRandomSecretKey,
|
|
5
|
+
stringToMD5,
|
|
6
|
+
} from '../src/utils';
|
|
7
|
+
|
|
8
|
+
test('token generation length is valid', () => {
|
|
9
|
+
expect(generateRandomSecretKey()).toHaveLength(TOKEN_VALID_LENGTH);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('string to md5 has valid length', () => {
|
|
13
|
+
expect(stringToMD5(Buffer.from('foo'))).toHaveLength(32);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('create a hash of content', () => {
|
|
17
|
+
expect(typeof createTarballHash().update('1').digest('hex')).toEqual('string');
|
|
18
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.reference.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "./src",
|
|
5
|
+
"outDir": "./build",
|
|
6
|
+
"noImplicitAny": false
|
|
7
|
+
},
|
|
8
|
+
"include": ["src/**/*.ts", "types/*.d.ts"],
|
|
9
|
+
"references": [
|
|
10
|
+
{
|
|
11
|
+
"path": "../config"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"path": "../utils"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|