@nivinjoseph/n-sec 4.0.6 → 5.0.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/.eslintignore +2 -0
- package/.eslintrc +335 -0
- package/dist/api-security/alg-type.js +2 -0
- package/dist/api-security/alg-type.js.map +1 -1
- package/dist/api-security/claim.d.ts +2 -3
- package/dist/api-security/claim.js +3 -2
- package/dist/api-security/claim.js.map +1 -1
- package/dist/api-security/claims-identity.js +2 -1
- package/dist/api-security/claims-identity.js.map +1 -1
- package/dist/api-security/expired-token-exception.d.ts +0 -1
- package/dist/api-security/expired-token-exception.js +2 -2
- package/dist/api-security/expired-token-exception.js.map +1 -1
- package/dist/api-security/invalid-token-exception.d.ts +0 -1
- package/dist/api-security/invalid-token-exception.js +3 -3
- package/dist/api-security/invalid-token-exception.js.map +1 -1
- package/dist/api-security/json-web-token.d.ts +4 -5
- package/dist/api-security/json-web-token.js +88 -81
- package/dist/api-security/json-web-token.js.map +1 -1
- package/dist/api-security/security-token.js +2 -2
- package/dist/api-security/security-token.js.map +1 -1
- package/dist/crypto/asymmetric-encryption.js +49 -0
- package/dist/crypto/asymmetric-encryption.js.map +1 -1
- package/dist/crypto/crypto-exception.js +1 -0
- package/dist/crypto/crypto-exception.js.map +1 -1
- package/dist/crypto/digital-signature.js +51 -0
- package/dist/crypto/digital-signature.js.map +1 -1
- package/dist/crypto/hash.d.ts +2 -3
- package/dist/crypto/hash.js +5 -5
- package/dist/crypto/hash.js.map +1 -1
- package/dist/crypto/hmac.d.ts +1 -2
- package/dist/crypto/hmac.js +4 -4
- package/dist/crypto/hmac.js.map +1 -1
- package/dist/crypto/symmetric-encryption.d.ts +1 -2
- package/dist/crypto/symmetric-encryption.js +15 -20
- package/dist/crypto/symmetric-encryption.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +12 -11
- package/src/api-security/claim.ts +5 -5
- package/src/api-security/claims-identity.ts +1 -1
- package/src/api-security/expired-token-exception.ts +1 -2
- package/src/api-security/invalid-token-exception.ts +2 -3
- package/src/api-security/json-web-token.ts +80 -79
- package/src/crypto/hash.ts +7 -9
- package/src/crypto/hmac.ts +4 -5
- package/src/crypto/symmetric-encryption.ts +17 -27
- package/test/hash.test.ts +42 -41
- package/test/hmac.test.ts +24 -23
- package/test/json-web-token.test.ts +66 -61
- package/test/other.test.ts +3 -3
- package/test/symmetric-encryption.test.ts +12 -10
- package/tsconfig.json +9 -11
- package/dist/crypto/uuid.js.map +0 -1
- package/tslint.json +0 -64
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nivinjoseph/n-sec",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"description": "Security library",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"ts-compile": "tsc -p .",
|
|
9
|
-
"ts-lint": "
|
|
9
|
+
"ts-lint": "eslint . --ext .ts",
|
|
10
10
|
"ts-build": "npm run ts-compile && npm run ts-lint",
|
|
11
11
|
"ts-build-dist": "npm run ts-build && tsc -p ./dist",
|
|
12
12
|
"clean-src": "find ./src -name '*.js' -delete -o -name '*.map' -delete",
|
|
@@ -32,17 +32,18 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://github.com/nivinjoseph/n-sec#readme",
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@types/mocha": "^
|
|
35
|
+
"@types/mocha": "^9.1.1",
|
|
36
36
|
"@types/node": "^14.14.43",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
37
|
+
"@typescript-eslint/eslint-plugin": "^5.25.0",
|
|
38
|
+
"@typescript-eslint/parser": "^5.25.0",
|
|
39
|
+
"eslint": "^8.15.0",
|
|
40
|
+
"mocha": "^10.0.0",
|
|
41
|
+
"ts-node": "^10.7.0",
|
|
42
|
+
"typescript": "^4.6.4"
|
|
42
43
|
},
|
|
43
44
|
"dependencies": {
|
|
44
|
-
"@nivinjoseph/n-defensive": "^1.0.
|
|
45
|
-
"@nivinjoseph/n-exception": "^1.0.
|
|
46
|
-
"@nivinjoseph/n-ext": "^1.1.
|
|
45
|
+
"@nivinjoseph/n-defensive": "^1.0.39",
|
|
46
|
+
"@nivinjoseph/n-exception": "^1.0.26",
|
|
47
|
+
"@nivinjoseph/n-ext": "^1.1.35"
|
|
47
48
|
}
|
|
48
49
|
}
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import { given } from "@nivinjoseph/n-defensive";
|
|
2
|
-
import "@nivinjoseph/n-ext";
|
|
3
2
|
|
|
4
3
|
|
|
5
4
|
// public
|
|
6
5
|
export class Claim
|
|
7
6
|
{
|
|
8
7
|
private readonly _type: string;
|
|
9
|
-
private readonly _value:
|
|
8
|
+
private readonly _value: unknown;
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
public get type(): string { return this._type; }
|
|
13
|
-
public get value():
|
|
12
|
+
public get value(): unknown { return this._value; }
|
|
14
13
|
|
|
15
14
|
|
|
16
|
-
public constructor(type: string, value:
|
|
15
|
+
public constructor(type: string, value: unknown)
|
|
17
16
|
{
|
|
18
|
-
given(type, "type").ensureHasValue().
|
|
17
|
+
given(type, "type").ensureHasValue().ensureIsString();
|
|
19
18
|
|
|
20
19
|
this._type = type.trim();
|
|
21
20
|
this._value = value;
|
|
@@ -24,6 +23,7 @@ export class Claim
|
|
|
24
23
|
|
|
25
24
|
public equals(claim: Claim): boolean
|
|
26
25
|
{
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
27
27
|
if (claim == null)
|
|
28
28
|
return false;
|
|
29
29
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Exception } from "@nivinjoseph/n-exception";
|
|
2
2
|
import { given } from "@nivinjoseph/n-defensive";
|
|
3
|
-
import "@nivinjoseph/n-ext";
|
|
4
3
|
|
|
5
4
|
|
|
6
5
|
// public
|
|
@@ -14,7 +13,7 @@ export class ExpiredTokenException extends Exception
|
|
|
14
13
|
|
|
15
14
|
public constructor(token: string)
|
|
16
15
|
{
|
|
17
|
-
given(token, "token").ensureHasValue().
|
|
16
|
+
given(token, "token").ensureHasValue().ensureIsString();
|
|
18
17
|
token = token.trim();
|
|
19
18
|
super(`Token '${token}' is expired.`);
|
|
20
19
|
this._token = token;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Exception } from "@nivinjoseph/n-exception";
|
|
2
2
|
import { given } from "@nivinjoseph/n-defensive";
|
|
3
|
-
import "@nivinjoseph/n-ext";
|
|
4
3
|
|
|
5
4
|
|
|
6
5
|
// public
|
|
@@ -16,8 +15,8 @@ export class InvalidTokenException extends Exception
|
|
|
16
15
|
|
|
17
16
|
public constructor(token: string, reason: string)
|
|
18
17
|
{
|
|
19
|
-
given(token, "token").ensureHasValue().
|
|
20
|
-
given(reason, "reason").ensureHasValue().
|
|
18
|
+
given(token, "token").ensureHasValue().ensureIsString();
|
|
19
|
+
given(reason, "reason").ensureHasValue().ensureIsString();
|
|
21
20
|
|
|
22
21
|
token = token.trim();
|
|
23
22
|
super(`Token '${token}' is invalid because ${reason}.`);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Claim } from "./claim";
|
|
2
2
|
import { InvalidOperationException } from "@nivinjoseph/n-exception";
|
|
3
3
|
import { given } from "@nivinjoseph/n-defensive";
|
|
4
|
-
import "@nivinjoseph/n-ext";
|
|
5
4
|
import { InvalidTokenException } from "./invalid-token-exception";
|
|
6
5
|
import { AlgType } from "./alg-type";
|
|
7
6
|
import { Hmac } from "./../crypto/hmac";
|
|
@@ -32,14 +31,13 @@ export class JsonWebToken
|
|
|
32
31
|
private constructor(issuer: string, algType: AlgType, key: string, isFullKey: boolean, expiry: number,
|
|
33
32
|
claims: Array<Claim>)
|
|
34
33
|
{
|
|
35
|
-
given(issuer, "issuer").ensureHasValue().
|
|
36
|
-
given(algType, "algType").ensureHasValue().
|
|
37
|
-
given(key, "key").ensureHasValue().
|
|
38
|
-
given(isFullKey, "isFullKey").ensureHasValue();
|
|
39
|
-
given(expiry, "expiry").ensureHasValue();
|
|
40
|
-
given(claims, "claims")
|
|
41
|
-
.
|
|
42
|
-
.ensure(t => t.length > 0);
|
|
34
|
+
given(issuer, "issuer").ensureHasValue().ensureIsString();
|
|
35
|
+
given(algType, "algType").ensureHasValue().ensureIsEnum(AlgType);
|
|
36
|
+
given(key, "key").ensureHasValue().ensureIsString();
|
|
37
|
+
given(isFullKey, "isFullKey").ensureHasValue().ensureIsBoolean();
|
|
38
|
+
given(expiry, "expiry").ensureHasValue().ensureIsNumber();
|
|
39
|
+
given(claims, "claims").ensureHasValue().ensureIsArray()
|
|
40
|
+
.ensure(t => t.isNotEmpty, "cannot be empty");
|
|
43
41
|
|
|
44
42
|
this._issuer = issuer.trim();
|
|
45
43
|
this._algType = algType;
|
|
@@ -49,84 +47,61 @@ export class JsonWebToken
|
|
|
49
47
|
this._claims = [...claims];
|
|
50
48
|
}
|
|
51
49
|
|
|
52
|
-
public async generateToken(): Promise<string>
|
|
53
|
-
{
|
|
54
|
-
if (!this._isfullKey)
|
|
55
|
-
throw new InvalidOperationException("generating token using an instance created from token");
|
|
56
|
-
|
|
57
|
-
let header: Header = {
|
|
58
|
-
iss: this._issuer,
|
|
59
|
-
alg: this._algType,
|
|
60
|
-
exp: this._expiry
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
let body: any = {};
|
|
64
|
-
this._claims.forEach(t => body[t.type] = t.value);
|
|
65
|
-
|
|
66
|
-
let headerAndBody = this.toHex(header) + "." + this.toHex(body);
|
|
67
|
-
|
|
68
|
-
// let signature = this._algType === AlgType.hmac
|
|
69
|
-
// ? await Hmac.create(this._key, headerAndBody)
|
|
70
|
-
// : await DigitalSignature.sign(this._key, headerAndBody);
|
|
71
|
-
|
|
72
|
-
let signature = await Hmac.create(this._key, headerAndBody);
|
|
73
|
-
|
|
74
|
-
let token = headerAndBody + "." + signature;
|
|
75
|
-
return token;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
50
|
public static fromClaims(issuer: string, algType: AlgType, key: string, expiry: number,
|
|
80
51
|
claims: Array<Claim>): JsonWebToken
|
|
81
52
|
{
|
|
82
53
|
return new JsonWebToken(issuer, algType, key, true, expiry, claims);
|
|
83
54
|
}
|
|
84
|
-
|
|
85
|
-
public static
|
|
55
|
+
|
|
56
|
+
public static fromToken(issuer: string, algType: AlgType, key: string, token: string): JsonWebToken
|
|
86
57
|
{
|
|
87
|
-
given(issuer, "issuer").ensureHasValue()
|
|
88
|
-
given(algType, "algType").ensureHasValue().
|
|
89
|
-
given(key, "key").ensureHasValue()
|
|
90
|
-
given(token, "token").ensureHasValue()
|
|
91
|
-
|
|
58
|
+
given(issuer, "issuer").ensureHasValue();
|
|
59
|
+
given(algType, "algType").ensureHasValue().ensureIsEnum(AlgType);
|
|
60
|
+
given(key, "key").ensureHasValue();
|
|
61
|
+
given(token, "token").ensureHasValue();
|
|
62
|
+
|
|
92
63
|
issuer = issuer.trim();
|
|
93
64
|
key = key.trim();
|
|
94
65
|
token = token.trim();
|
|
95
|
-
|
|
96
|
-
|
|
66
|
+
|
|
67
|
+
const tokenSplitted = token.split(".");
|
|
97
68
|
if (tokenSplitted.length !== 3)
|
|
98
69
|
throw new InvalidTokenException(token, "format is incorrect");
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
70
|
+
|
|
71
|
+
const headerString = tokenSplitted[0];
|
|
72
|
+
const bodyString = tokenSplitted[1];
|
|
73
|
+
const signature = tokenSplitted[2];
|
|
74
|
+
|
|
75
|
+
const header: Header = JsonWebToken._toObject(headerString) as Header;
|
|
76
|
+
const body: any = JsonWebToken._toObject(bodyString);
|
|
77
|
+
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
107
79
|
if (header.iss === undefined || header.iss === null)
|
|
108
80
|
throw new InvalidTokenException(token, "iss was not present");
|
|
109
|
-
|
|
81
|
+
|
|
110
82
|
if (header.iss !== issuer)
|
|
111
83
|
throw new InvalidTokenException(token,
|
|
112
|
-
`iss was expected to be '${issuer}' but instead was '${header.iss}'`);
|
|
113
|
-
|
|
84
|
+
`iss was expected to be '${issuer}' but instead was '${header.iss}'`);
|
|
85
|
+
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
114
87
|
if (header.alg === undefined || header.alg === null)
|
|
115
88
|
throw new InvalidTokenException(token, "alg was not present");
|
|
116
|
-
|
|
89
|
+
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
117
91
|
if (header.alg !== algType)
|
|
118
92
|
throw new InvalidTokenException(token,
|
|
119
|
-
`alg was expected to be '${algType}' but instead was '${header.alg}'`);
|
|
120
|
-
|
|
93
|
+
`alg was expected to be '${algType}' but instead was '${header.alg}'`);
|
|
94
|
+
|
|
95
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
121
96
|
if (header.exp === undefined || header.exp === null)
|
|
122
97
|
throw new InvalidTokenException(token, "exp was not present");
|
|
123
|
-
|
|
124
|
-
if (typeof
|
|
98
|
+
|
|
99
|
+
if (typeof header.exp !== "number")
|
|
125
100
|
throw new InvalidTokenException(token, `exp value '${header.exp}' is invalid`);
|
|
126
|
-
|
|
101
|
+
|
|
127
102
|
if (header.exp <= Date.now())
|
|
128
103
|
throw new ExpiredTokenException(token);
|
|
129
|
-
|
|
104
|
+
|
|
130
105
|
// if (algType === AlgType.hmac)
|
|
131
106
|
// {
|
|
132
107
|
// let computedSignature = await Hmac.create(key, headerString + "." + bodyString);
|
|
@@ -139,30 +114,56 @@ export class JsonWebToken
|
|
|
139
114
|
// if (!verification)
|
|
140
115
|
// throw new InvalidTokenException(token, "signature could not be verified");
|
|
141
116
|
// }
|
|
142
|
-
|
|
143
|
-
|
|
117
|
+
|
|
118
|
+
const computedSignature = Hmac.create(key, headerString + "." + bodyString);
|
|
144
119
|
if (computedSignature !== signature)
|
|
145
|
-
throw new InvalidTokenException(token, "signature could not be verified");
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
for (
|
|
149
|
-
claims.push(new Claim(item, body[item]));
|
|
150
|
-
|
|
120
|
+
throw new InvalidTokenException(token, "signature could not be verified");
|
|
121
|
+
|
|
122
|
+
const claims = new Array<Claim>();
|
|
123
|
+
for (const item in body)
|
|
124
|
+
claims.push(new Claim(item, body[item]));
|
|
125
|
+
|
|
151
126
|
return new JsonWebToken(issuer, algType, key, false, header.exp, claims);
|
|
152
127
|
}
|
|
128
|
+
|
|
129
|
+
private static _toObject(hex: string): object
|
|
130
|
+
{
|
|
131
|
+
const json = Buffer.from(hex.toLowerCase(), "hex").toString("utf8");
|
|
132
|
+
const obj = JSON.parse(json) as object;
|
|
133
|
+
return obj;
|
|
134
|
+
}
|
|
153
135
|
|
|
154
|
-
|
|
136
|
+
public generateToken(): string
|
|
155
137
|
{
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
138
|
+
if (!this._isfullKey)
|
|
139
|
+
throw new InvalidOperationException("generating token using an instance created from token");
|
|
140
|
+
|
|
141
|
+
const header: Header = {
|
|
142
|
+
iss: this._issuer,
|
|
143
|
+
alg: this._algType,
|
|
144
|
+
exp: this._expiry
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const body: any = {};
|
|
148
|
+
this._claims.forEach(t => body[t.type] = t.value);
|
|
149
|
+
|
|
150
|
+
const headerAndBody = this._toHex(header) + "." + this._toHex(body);
|
|
151
|
+
|
|
152
|
+
// let signature = this._algType === AlgType.hmac
|
|
153
|
+
// ? await Hmac.create(this._key, headerAndBody)
|
|
154
|
+
// : await DigitalSignature.sign(this._key, headerAndBody);
|
|
155
|
+
|
|
156
|
+
const signature = Hmac.create(this._key, headerAndBody);
|
|
157
|
+
|
|
158
|
+
const token = headerAndBody + "." + signature;
|
|
159
|
+
return token;
|
|
159
160
|
}
|
|
160
161
|
|
|
161
|
-
private
|
|
162
|
+
private _toHex(obj: object): string
|
|
162
163
|
{
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return
|
|
164
|
+
const json = JSON.stringify(obj);
|
|
165
|
+
const hex = Buffer.from(json, "utf8").toString("hex");
|
|
166
|
+
return hex.toUpperCase();
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
|
package/src/crypto/hash.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { given } from "@nivinjoseph/n-defensive";
|
|
2
|
-
import "@nivinjoseph/n-ext";
|
|
3
2
|
import * as Crypto from "crypto";
|
|
4
3
|
|
|
5
4
|
|
|
@@ -9,26 +8,25 @@ export class Hash
|
|
|
9
8
|
private constructor() { }
|
|
10
9
|
|
|
11
10
|
|
|
12
|
-
public static create(value: string):
|
|
11
|
+
public static create(value: string): string
|
|
13
12
|
{
|
|
14
|
-
given(value, "value").ensureHasValue().ensureIsString()
|
|
15
|
-
|
|
13
|
+
given(value, "value").ensureHasValue().ensureIsString();
|
|
16
14
|
value = value.trim();
|
|
17
15
|
|
|
18
16
|
const hash = Crypto.createHash("sha512");
|
|
19
17
|
hash.update(value, "utf8");
|
|
20
|
-
return
|
|
18
|
+
return hash.digest("hex").toUpperCase();
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
public static createUsingSalt(value: string, salt: string):
|
|
21
|
+
public static createUsingSalt(value: string, salt: string): string
|
|
24
22
|
{
|
|
25
|
-
given(value, "value").ensureHasValue().ensureIsString()
|
|
26
|
-
given(salt, "salt").ensureHasValue().ensureIsString()
|
|
23
|
+
given(value, "value").ensureHasValue().ensureIsString();
|
|
24
|
+
given(salt, "salt").ensureHasValue().ensureIsString();
|
|
27
25
|
|
|
28
26
|
value = value.trim();
|
|
29
27
|
salt = salt.trim();
|
|
30
28
|
|
|
31
|
-
const reverse = (val: string) =>
|
|
29
|
+
const reverse = (val: string): string =>
|
|
32
30
|
{
|
|
33
31
|
let rev = "";
|
|
34
32
|
for (let i = 0; i < val.length; i++)
|
package/src/crypto/hmac.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { given } from "@nivinjoseph/n-defensive";
|
|
2
|
-
import "@nivinjoseph/n-ext";
|
|
3
2
|
import * as Crypto from "crypto";
|
|
4
3
|
|
|
5
4
|
|
|
@@ -9,10 +8,10 @@ export class Hmac
|
|
|
9
8
|
private constructor() { }
|
|
10
9
|
|
|
11
10
|
|
|
12
|
-
public static create(key: string, value: string):
|
|
11
|
+
public static create(key: string, value: string): string
|
|
13
12
|
{
|
|
14
|
-
given(key, "key").ensureHasValue().ensureIsString()
|
|
15
|
-
given(value, "value").ensureHasValue().ensureIsString()
|
|
13
|
+
given(key, "key").ensureHasValue().ensureIsString();
|
|
14
|
+
given(value, "value").ensureHasValue().ensureIsString();
|
|
16
15
|
|
|
17
16
|
key = key.trim();
|
|
18
17
|
value = value.trim();
|
|
@@ -20,6 +19,6 @@ export class Hmac
|
|
|
20
19
|
const hmac = Crypto.createHmac("sha256", Buffer.from(key, "hex"));
|
|
21
20
|
|
|
22
21
|
hmac.update(value, "utf8");
|
|
23
|
-
return
|
|
22
|
+
return hmac.digest("hex").toUpperCase();
|
|
24
23
|
}
|
|
25
24
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as Crypto from "crypto";
|
|
2
2
|
import { CryptoException } from "./crypto-exception";
|
|
3
3
|
import { given } from "@nivinjoseph/n-defensive";
|
|
4
|
-
import "@nivinjoseph/n-ext";
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
// public
|
|
@@ -29,14 +28,14 @@ export class SymmetricEncryption
|
|
|
29
28
|
|
|
30
29
|
public static encrypt(key: string, value: string): Promise<string>
|
|
31
30
|
{
|
|
32
|
-
given(key, "key").ensureHasValue().ensureIsString().ensure(t => !t.isEmptyOrWhiteSpace());
|
|
33
|
-
given(value, "value").ensureHasValue().ensureIsString().ensure(t => !t.isEmptyOrWhiteSpace());
|
|
34
|
-
|
|
35
|
-
key = key.trim();
|
|
36
|
-
value = value.trim();
|
|
37
|
-
|
|
38
31
|
return new Promise<string>((resolve, reject) =>
|
|
39
32
|
{
|
|
33
|
+
given(key, "key").ensureHasValue().ensureIsString();
|
|
34
|
+
given(value, "value").ensureHasValue().ensureIsString();
|
|
35
|
+
|
|
36
|
+
key = key.trim();
|
|
37
|
+
value = value.trim();
|
|
38
|
+
|
|
40
39
|
Crypto.randomBytes(16, (err, buf) =>
|
|
41
40
|
{
|
|
42
41
|
if (err)
|
|
@@ -60,33 +59,24 @@ export class SymmetricEncryption
|
|
|
60
59
|
}
|
|
61
60
|
});
|
|
62
61
|
});
|
|
63
|
-
|
|
64
|
-
|
|
65
62
|
}
|
|
66
63
|
|
|
67
|
-
public static decrypt(key: string, value: string):
|
|
64
|
+
public static decrypt(key: string, value: string): string
|
|
68
65
|
{
|
|
69
|
-
given(key, "key").ensureHasValue().ensureIsString()
|
|
70
|
-
given(value, "value").ensureHasValue().ensureIsString()
|
|
66
|
+
given(key, "key").ensureHasValue().ensureIsString();
|
|
67
|
+
given(value, "value").ensureHasValue().ensureIsString();
|
|
71
68
|
|
|
72
69
|
key = key.trim();
|
|
73
70
|
value = value.trim();
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (splitted.length !== 2)
|
|
79
|
-
throw new CryptoException("Invalid value.");
|
|
72
|
+
const splitted = value.split(".");
|
|
73
|
+
if (splitted.length !== 2)
|
|
74
|
+
throw new CryptoException("Invalid value.");
|
|
80
75
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
catch (error)
|
|
88
|
-
{
|
|
89
|
-
return Promise.reject(error);
|
|
90
|
-
}
|
|
76
|
+
const iv = Buffer.from(splitted[1], "hex");
|
|
77
|
+
const deCipher = Crypto.createDecipheriv("AES-256-CBC", Buffer.from(key, "hex"), iv);
|
|
78
|
+
let decrypted = deCipher.update(splitted[0], "hex", "utf8");
|
|
79
|
+
decrypted += deCipher.final("utf8");
|
|
80
|
+
return decrypted;
|
|
91
81
|
}
|
|
92
82
|
}
|
package/test/hash.test.ts
CHANGED
|
@@ -2,34 +2,34 @@ import * as Assert from "assert";
|
|
|
2
2
|
import { Hash } from "./../src/index";
|
|
3
3
|
import "@nivinjoseph/n-ext";
|
|
4
4
|
// import { CryptoException } from "./../src/crypto-exception";
|
|
5
|
-
import "@nivinjoseph/n-ext";
|
|
6
5
|
|
|
7
6
|
suite("Hash", () =>
|
|
8
7
|
{
|
|
9
8
|
suite("create", () =>
|
|
10
9
|
{
|
|
11
|
-
test("must return a string value that is not null, empty, whitespace or same as input when called with a valid input",
|
|
10
|
+
test("must return a string value that is not null, empty, whitespace or same as input when called with a valid input", () =>
|
|
12
11
|
{
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const input = "hello world";
|
|
13
|
+
const hash = Hash.create(input);
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
15
15
|
Assert.ok(hash !== null && !hash.isEmptyOrWhiteSpace());
|
|
16
16
|
Assert.notStrictEqual(hash, input);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
test("multiple invocations with the same input must return the same output",
|
|
19
|
+
test("multiple invocations with the same input must return the same output", () =>
|
|
20
20
|
{
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const input = "hello world";
|
|
22
|
+
const hash1 = Hash.create(input);
|
|
23
|
+
const hash2 = Hash.create(input);
|
|
24
24
|
Assert.strictEqual(hash1, hash2);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
test("multiple invocations with the different inputs must return different outputs",
|
|
27
|
+
test("multiple invocations with the different inputs must return different outputs", () =>
|
|
28
28
|
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
const input1 = "hello world";
|
|
30
|
+
const hash1 = Hash.create(input1);
|
|
31
|
+
const input2 = "goodbye world";
|
|
32
|
+
const hash2 = Hash.create(input2);
|
|
33
33
|
Assert.notStrictEqual(hash1, hash2);
|
|
34
34
|
});
|
|
35
35
|
|
|
@@ -92,58 +92,59 @@ suite("Hash", () =>
|
|
|
92
92
|
|
|
93
93
|
suite("createUsingSalt", () =>
|
|
94
94
|
{
|
|
95
|
-
test("must return a string value that is not null, empty, whitespace or same as input or salt when called with a valid input and salt",
|
|
95
|
+
test("must return a string value that is not null, empty, whitespace or same as input or salt when called with a valid input and salt", () =>
|
|
96
96
|
{
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
const input = "hello world";
|
|
98
|
+
const salt = "salt";
|
|
99
|
+
const hash = Hash.createUsingSalt(input, salt);
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
100
101
|
Assert.ok(hash !== null && !hash.isEmptyOrWhiteSpace());
|
|
101
102
|
Assert.notStrictEqual(hash, input);
|
|
102
103
|
Assert.notStrictEqual(hash, salt);
|
|
103
104
|
});
|
|
104
105
|
|
|
105
|
-
test("multiple invocations with the same input and salt must return the same output",
|
|
106
|
+
test("multiple invocations with the same input and salt must return the same output", () =>
|
|
106
107
|
{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
const input = "hello world";
|
|
109
|
+
const salt = "salt";
|
|
110
|
+
const hash1 = Hash.createUsingSalt(input, salt);
|
|
111
|
+
const hash2 = Hash.createUsingSalt(input, salt);
|
|
111
112
|
Assert.strictEqual(hash1, hash2);
|
|
112
113
|
});
|
|
113
114
|
|
|
114
|
-
test("multiple invocations with different inputs and different salts must return different outputs",
|
|
115
|
+
test("multiple invocations with different inputs and different salts must return different outputs", () =>
|
|
115
116
|
{
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
const input1 = "hello world";
|
|
118
|
+
const salt1 = "salt-1";
|
|
119
|
+
const hash1 = Hash.createUsingSalt(input1, salt1);
|
|
119
120
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
const input2 = "goodbye world";
|
|
122
|
+
const salt2 = "salt-2";
|
|
123
|
+
const hash2 = Hash.createUsingSalt(input2, salt2);
|
|
123
124
|
|
|
124
125
|
Assert.notStrictEqual(hash1, hash2);
|
|
125
126
|
});
|
|
126
127
|
|
|
127
|
-
test("multiple invocations with different inputs and the same salt must return different outputs",
|
|
128
|
+
test("multiple invocations with different inputs and the same salt must return different outputs", () =>
|
|
128
129
|
{
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
const input1 = "hello world";
|
|
131
|
+
const salt1 = "salt-1";
|
|
132
|
+
const hash1 = Hash.createUsingSalt(input1, salt1);
|
|
132
133
|
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
const input2 = "goodbye world";
|
|
135
|
+
const hash2 = Hash.createUsingSalt(input2, salt1);
|
|
135
136
|
|
|
136
137
|
Assert.notStrictEqual(hash1, hash2);
|
|
137
138
|
});
|
|
138
139
|
|
|
139
|
-
test("multiple invocations with the same input and different salts must return different outputs",
|
|
140
|
+
test("multiple invocations with the same input and different salts must return different outputs", () =>
|
|
140
141
|
{
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
const input = "hello world";
|
|
143
|
+
const salt1 = "salt-1";
|
|
144
|
+
const hash1 = Hash.createUsingSalt(input, salt1);
|
|
144
145
|
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
const salt2 = "salt-2";
|
|
147
|
+
const hash2 = Hash.createUsingSalt(input, salt2);
|
|
147
148
|
|
|
148
149
|
Assert.notStrictEqual(hash1, hash2);
|
|
149
150
|
});
|