@1auth/crypto 0.0.0-alpha.32 → 0.0.0-alpha.33
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/index.js +241 -286
- package/package.json +3 -3
package/index.js
CHANGED
|
@@ -5,27 +5,43 @@ import {
|
|
|
5
5
|
createCipheriv,
|
|
6
6
|
createDecipheriv,
|
|
7
7
|
generateKeyPair as generateKeyPairCallback,
|
|
8
|
-
sign,
|
|
9
|
-
verify
|
|
8
|
+
sign as signCallback,
|
|
9
|
+
verify as verifyCallback
|
|
10
10
|
} from 'node:crypto'
|
|
11
11
|
// https://github.com/napi-rs/node-rs/tree/main/packages/argon2
|
|
12
12
|
import { hash as secretHash, verify as secretVerify } from '@node-rs/argon2'
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
const generateKeyPair = promisify(generateKeyPairCallback)
|
|
15
|
+
const sign = promisify(signCallback)
|
|
16
|
+
const verify = promisify(verifyCallback)
|
|
17
|
+
|
|
18
|
+
const defaults = {
|
|
19
|
+
// symetricEncryptionKey: randomBytes(32).toString('base64') // 256 bits
|
|
20
|
+
symetricEncryptionKey: undefined,
|
|
21
|
+
symetricEncryptionMethod: 'chacha20-poly1305', // 2024-05: AES-256 GCM (aes-256-gcm) or ChaCha20-Poly1305 (chacha20-poly1305)
|
|
22
|
+
symetricEncryptionEncoding: 'base64', // https://nodejs.org/api/buffer.html#buffers-and-character-encodings
|
|
23
|
+
asymetricKey: 'P-384',
|
|
20
24
|
digestAlgorithm: 'sha3-384',
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
signatureEncoding: 'base64'
|
|
26
|
+
}
|
|
27
|
+
const symetricEncryptionEncodingLengths = {}
|
|
28
|
+
const options = {}
|
|
29
|
+
export default (opt = {}) => {
|
|
30
|
+
Object.assign(options, defaults, opt)
|
|
31
|
+
symetricEncryptionEncodingLengths.iv = randomBytes(12).toString(
|
|
32
|
+
options.symetricEncryptionEncoding
|
|
33
|
+
).length
|
|
34
|
+
symetricEncryptionEncodingLengths.ivAndAuthTag =
|
|
35
|
+
symetricEncryptionEncodingLengths.iv +
|
|
36
|
+
randomBytes(16).toString(options.symetricEncryptionEncoding).length
|
|
37
|
+
if (!options.symetricEncryptionKey) {
|
|
38
|
+
console.warn(
|
|
39
|
+
"@1auth/crypto symetricEncryptionKey is empty, use a stored secret made from randomBytes(32).toString('base64'). Encryption disabled."
|
|
40
|
+
)
|
|
41
|
+
}
|
|
26
42
|
}
|
|
27
43
|
|
|
28
|
-
const
|
|
44
|
+
export const getOptions = () => options
|
|
29
45
|
|
|
30
46
|
// *** entropy *** //
|
|
31
47
|
// export const characterPoolSize = (value) => {
|
|
@@ -43,188 +59,107 @@ const generateKeyPair = promisify(generateKeyPairCallback)
|
|
|
43
59
|
// return max - min
|
|
44
60
|
// }
|
|
45
61
|
|
|
46
|
-
// ***
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
hex: 16,
|
|
52
|
-
numeric: 10
|
|
62
|
+
// *** Helpers *** //
|
|
63
|
+
// Ref: https://therootcompany.com/blog/how-many-bits-of-entropy-per-character/
|
|
64
|
+
export const entropyToCharacterLength = (bits, characterPoolSize) => {
|
|
65
|
+
// bits*ln(2)/ln(characterPoolSize)
|
|
66
|
+
return Math.ceil((bits * Math.LN2) / Math.log(characterPoolSize))
|
|
53
67
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
/* export const characterLengthToEntropy = (
|
|
69
|
+
characterLength,
|
|
70
|
+
characterPoolSize,
|
|
71
|
+
) => {
|
|
72
|
+
// log_2(characterPoolSize^characterLength)
|
|
73
|
+
return Math.floor(Math.log2(characterPoolSize ** characterLength));
|
|
74
|
+
}; */
|
|
75
|
+
|
|
76
|
+
// *** Random generators *** //
|
|
77
|
+
// Ref: https://github.com/sindresorhus/crypto-random-string/blob/main/core.js
|
|
78
|
+
export const charactersAlphaNumeric = [
|
|
79
|
+
...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
|
80
|
+
]
|
|
81
|
+
export const charactersDistinguishable = [...'CDEHKMPRTUWXY012458']
|
|
82
|
+
export const charactersNumeric = [...'0123456789']
|
|
83
|
+
|
|
84
|
+
export const randomCharacters = (
|
|
85
|
+
length,
|
|
86
|
+
characters = charactersAlphaNumeric
|
|
87
|
+
) => {
|
|
88
|
+
// Generating entropy is faster than complex math operations, so we use the simplest way
|
|
89
|
+
const characterCount = characters.length
|
|
90
|
+
const maxValidSelector =
|
|
91
|
+
Math.floor(0x1_00_00 / characterCount) * characterCount - 1 // Using values above this will ruin distribution when using modular division
|
|
92
|
+
const entropyLength = 2 * Math.ceil(1.1 * length) // Generating a bit more than required so chances we need more than one pass will be really low
|
|
93
|
+
let string = ''
|
|
94
|
+
let stringLength = 0
|
|
95
|
+
|
|
96
|
+
while (stringLength < length) {
|
|
97
|
+
// In case we had many bad values, which may happen for character sets of size above 0x8000 but close to it
|
|
98
|
+
const entropy = new Uint8Array(randomBytes(entropyLength))
|
|
99
|
+
let entropyPosition = 0
|
|
100
|
+
|
|
101
|
+
while (entropyPosition < entropyLength && stringLength < length) {
|
|
102
|
+
const entropyValue =
|
|
103
|
+
entropy[entropyPosition] + (entropy[entropyPosition + 1] << 8) // eslint-disable-line no-bitwise
|
|
104
|
+
entropyPosition += 2
|
|
105
|
+
if (entropyValue > maxValidSelector) {
|
|
106
|
+
// Skip values which will ruin distribution when using modular division
|
|
107
|
+
continue
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
string += characters[entropyValue % characterCount]
|
|
111
|
+
stringLength++
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return string
|
|
64
116
|
}
|
|
65
117
|
|
|
66
|
-
export const
|
|
67
|
-
|
|
68
|
-
bits,
|
|
69
|
-
characterPoolSize.base64
|
|
70
|
-
)
|
|
71
|
-
return randomBytes(characterLength + 1)
|
|
72
|
-
.toString('base64')
|
|
73
|
-
.replace('=', '')
|
|
118
|
+
export const randomAlphaNumeric = (characterLength) => {
|
|
119
|
+
return randomCharacters(characterLength, charactersAlphaNumeric)
|
|
74
120
|
}
|
|
75
121
|
|
|
76
|
-
export const randomNumeric = (
|
|
77
|
-
|
|
78
|
-
bits,
|
|
79
|
-
characterPoolSize.numeric
|
|
80
|
-
)
|
|
81
|
-
const max = 10 ** characterLength
|
|
82
|
-
let value = Math.floor(Math.random() * max).toString()
|
|
83
|
-
if (value === max.toString()) {
|
|
84
|
-
return randomNumeric(bits)
|
|
85
|
-
}
|
|
86
|
-
while (value.length < characterLength) value = '0' + value
|
|
87
|
-
return value
|
|
122
|
+
export const randomNumeric = (characterLength) => {
|
|
123
|
+
return randomCharacters(characterLength, charactersNumeric)
|
|
88
124
|
}
|
|
89
125
|
|
|
90
|
-
export const
|
|
91
|
-
|
|
92
|
-
return randomBytes(characterLength).toString('hex')
|
|
126
|
+
export const randomSymetricEncryptionKey = () => {
|
|
127
|
+
return randomBytes(32).toString('base64') // 256 bits
|
|
93
128
|
}
|
|
94
129
|
|
|
95
130
|
// *** configs *** //
|
|
96
131
|
export const randomId = {
|
|
97
132
|
type: 'id',
|
|
98
|
-
|
|
99
|
-
charPool: characterPoolSize.alphaNumeric,
|
|
133
|
+
minLength: entropyToCharacterLength(64, charactersAlphaNumeric.length),
|
|
100
134
|
// TODO update to use https://github.com/jetpack-io/typeid
|
|
101
|
-
create:
|
|
102
|
-
(prefix ? prefix + '_' : '') + randomAlphaNumeric(randomId.
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export const subject = {
|
|
106
|
-
type: 'id',
|
|
107
|
-
entropy: 64,
|
|
108
|
-
charPool: characterPoolSize.alphaNumeric,
|
|
109
|
-
create: async (prefix) =>
|
|
110
|
-
(prefix ? prefix + '_' : '') + randomAlphaNumeric(subject.entropy)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export const session = {
|
|
114
|
-
type: 'id',
|
|
115
|
-
entropy: 128, // ASVS 3.2.2
|
|
116
|
-
charPool: characterPoolSize.alphaNumeric,
|
|
117
|
-
expire: 15 * 60,
|
|
118
|
-
create: async (prefix) =>
|
|
119
|
-
(prefix ? prefix + '_' : '') + randomAlphaNumeric(session.entropy)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export const passwordSecret = {
|
|
123
|
-
type: 'secret',
|
|
124
|
-
entropy: 64,
|
|
125
|
-
charPool: characterPoolSize.keyboard,
|
|
126
|
-
otp: false,
|
|
127
|
-
encode: async (value, encryptedKey, sub) =>
|
|
128
|
-
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
|
|
129
|
-
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
|
|
130
|
-
verify: async (value, hash) => verifySecretHash(hash, value)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// aka Password/Credential Recovery
|
|
134
|
-
export const passwordToken = {
|
|
135
|
-
type: 'token',
|
|
136
|
-
entropy: 20,
|
|
137
|
-
charPool: characterPoolSize.numeric,
|
|
138
|
-
otp: true,
|
|
139
|
-
expire: 30,
|
|
140
|
-
create: async () => randomNumeric(passwordToken.entropy),
|
|
141
|
-
encode: async (value, encryptedKey, sub) =>
|
|
142
|
-
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
|
|
143
|
-
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
|
|
144
|
-
verify: async (value, hash) => verifySecretHash(hash, value)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export const oneTimeSecret = {
|
|
148
|
-
type: 'secret',
|
|
149
|
-
entropy: 64, //
|
|
150
|
-
charPool: characterPoolSize.base64,
|
|
151
|
-
otp: false,
|
|
152
|
-
expire: null,
|
|
153
|
-
create: async () => randomBase64(oneTimeSecret.entropy),
|
|
154
|
-
encode: async (value, encryptedKey, sub) => encrypt(value, encryptedKey, sub),
|
|
155
|
-
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub)
|
|
156
|
-
// create, hash, verify to be handled within
|
|
157
|
-
}
|
|
158
|
-
export const oneTimeToken = {
|
|
159
|
-
type: 'token',
|
|
160
|
-
entropy: 20,
|
|
161
|
-
charPool: characterPoolSize.numeric,
|
|
162
|
-
otp: true,
|
|
163
|
-
expire: 30
|
|
164
|
-
// create, hash, verify to be handled within
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// aka lookup secret
|
|
168
|
-
export const recoveryCode = {
|
|
169
|
-
type: 'secret',
|
|
170
|
-
entropy: 112,
|
|
171
|
-
charPool: characterPoolSize.alphaNumeric,
|
|
172
|
-
otp: true,
|
|
173
|
-
create: async () => randomAlphaNumeric(recoveryCode.entropy),
|
|
174
|
-
encode: async (value, encryptedKey, sub) =>
|
|
175
|
-
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
|
|
176
|
-
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
|
|
177
|
-
verify: async (value, hash) => verifySecretHash(hash, value)
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export const outOfBandToken = {
|
|
181
|
-
type: 'token',
|
|
182
|
-
entropy: 20,
|
|
183
|
-
charPool: characterPoolSize.numeric,
|
|
184
|
-
otp: true,
|
|
185
|
-
expire: 10 * 60,
|
|
186
|
-
create: async () => randomNumeric(outOfBandToken.entropy),
|
|
187
|
-
encode: async (value, encryptedKey, sub) =>
|
|
188
|
-
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
|
|
189
|
-
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
|
|
190
|
-
verify: async (value, hash) => verifySecretHash(hash, value)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
export const accessToken = {
|
|
194
|
-
type: 'secret',
|
|
195
|
-
entropy: 112,
|
|
196
|
-
charPool: characterPoolSize.alphaNumeric,
|
|
197
|
-
otp: false,
|
|
198
|
-
expire: 30 * 24 * 60 * 60, // allow override from user
|
|
199
|
-
create: async () => randomAlphaNumeric(accessToken.entropy),
|
|
200
|
-
encode: async (value, encryptedKey, sub) =>
|
|
201
|
-
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
|
|
202
|
-
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
|
|
203
|
-
verify: async (value, hash) => verifySecretHash(hash, value)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// *** Helpers *** //
|
|
207
|
-
export const characterLengthToEntropy = (
|
|
208
|
-
characterLength,
|
|
209
|
-
characterPoolSize
|
|
210
|
-
) => {
|
|
211
|
-
// log_2(characterPoolSize^characterLength)
|
|
212
|
-
return Math.round(Math.log2(characterPoolSize ** characterLength))
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export const entropyToCharacterLength = (bits, characterPoolSize) => {
|
|
216
|
-
// bits*ln(2)/ln(characterPoolSize)
|
|
217
|
-
return Math.round((bits * Math.LN2) / Math.log(characterPoolSize))
|
|
135
|
+
create: (prefix) =>
|
|
136
|
+
(prefix ? prefix + '_' : '') + randomAlphaNumeric(randomId.minLength)
|
|
218
137
|
}
|
|
219
138
|
|
|
220
139
|
// *** Digests *** //
|
|
221
|
-
export const createDigest =
|
|
140
|
+
export const createDigest = (value, { algorithm } = {}) => {
|
|
222
141
|
algorithm ??= options.digestAlgorithm
|
|
223
|
-
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
142
|
+
const hash = checksum(algorithm).update(value).digest('hex')
|
|
143
|
+
const digest = `${algorithm}:${hash}`
|
|
144
|
+
return digest
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const createEncryptedDigest = (value, { algorithm } = {}) => {
|
|
148
|
+
const digest = createDigest(value, { algorithm })
|
|
149
|
+
// encrypting using the symetricEncryptionKey instread of
|
|
150
|
+
// the row encryptionKey to allow lookup, must have fixed iv
|
|
151
|
+
return symetricEncrypt(digest, {
|
|
152
|
+
encryptionKey: options.symetricEncryptionKey,
|
|
153
|
+
sub: '',
|
|
154
|
+
encoding: options.symetricEncryptionEncoding,
|
|
155
|
+
iv: Buffer.from(
|
|
156
|
+
options.symetricEncryptionKey.substring(
|
|
157
|
+
0,
|
|
158
|
+
symetricEncryptionEncodingLengths.iv
|
|
159
|
+
),
|
|
160
|
+
options.symetricEncryptionEncoding
|
|
161
|
+
)
|
|
162
|
+
})
|
|
228
163
|
}
|
|
229
164
|
|
|
230
165
|
// *** Hashing *** //
|
|
@@ -249,124 +184,156 @@ export const verifySecretHash = async (hash, value) => {
|
|
|
249
184
|
// *** Encryption *** //
|
|
250
185
|
const authTagLength = 16
|
|
251
186
|
|
|
252
|
-
export const makeSymetricKey = (
|
|
253
|
-
if (!options.
|
|
187
|
+
export const makeSymetricKey = (sub) => {
|
|
188
|
+
if (!options.symetricEncryptionKey) {
|
|
254
189
|
return { encryptionKey: '', encryptedKey: '' }
|
|
255
190
|
}
|
|
256
|
-
const encryptionKey =
|
|
257
|
-
const encryptedKey =
|
|
258
|
-
encryptionKey,
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
)
|
|
191
|
+
const encryptionKey = randomSymetricEncryptionKey()
|
|
192
|
+
const encryptedKey = symetricEncrypt(encryptionKey, {
|
|
193
|
+
encryptionKey: options.symetricEncryptionKey,
|
|
194
|
+
sub,
|
|
195
|
+
decoding: 'base64',
|
|
196
|
+
encoding: options.symetricEncryptionEncoding
|
|
197
|
+
})
|
|
264
198
|
return { encryptionKey, encryptedKey }
|
|
265
199
|
}
|
|
266
200
|
|
|
267
|
-
//
|
|
268
|
-
export const
|
|
269
|
-
|
|
201
|
+
// sub add context to encryption
|
|
202
|
+
export const symetricEncryptFields = (
|
|
203
|
+
values,
|
|
204
|
+
{ encryptedKey, encryptionKey, sub },
|
|
205
|
+
fields = []
|
|
206
|
+
) => {
|
|
207
|
+
if (encryptedKey) {
|
|
208
|
+
encryptionKey ??= symetricDecryptKey(encryptedKey, {
|
|
209
|
+
sub
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
if (!encryptionKey) return values
|
|
213
|
+
const encryptedValues = structuredClone(values)
|
|
270
214
|
for (const key of fields) {
|
|
271
|
-
|
|
215
|
+
encryptedValues[key] &&= symetricEncrypt(encryptedValues[key], {
|
|
216
|
+
encryptionKey,
|
|
217
|
+
sub
|
|
218
|
+
})
|
|
272
219
|
}
|
|
273
|
-
return
|
|
220
|
+
return encryptedValues
|
|
274
221
|
}
|
|
275
222
|
|
|
276
|
-
export const
|
|
277
|
-
if (!encryptedKey) return data
|
|
278
|
-
|
|
279
|
-
const encryptionKey = __decryptKey(encryptedKey, assocData)
|
|
280
|
-
return __encrypt(
|
|
281
|
-
data,
|
|
282
|
-
Buffer.from(encryptionKey, 'hex'),
|
|
283
|
-
assocData,
|
|
284
|
-
'utf8',
|
|
285
|
-
'hex'
|
|
286
|
-
)
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const __encrypt = (
|
|
223
|
+
export const symetricEncrypt = (
|
|
290
224
|
data,
|
|
291
|
-
encryptionKey,
|
|
292
|
-
assocData,
|
|
293
|
-
decoding = 'utf8',
|
|
294
|
-
encoding = 'hex'
|
|
225
|
+
{ encryptedKey, encryptionKey, sub, decoding, encoding, iv }
|
|
295
226
|
) => {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
227
|
+
if (encryptedKey) {
|
|
228
|
+
encryptionKey ??= symetricDecryptKey(encryptedKey, {
|
|
229
|
+
sub
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
if (!encryptionKey) return data
|
|
233
|
+
decoding ??= 'utf8'
|
|
234
|
+
encoding ??= options.symetricEncryptionEncoding
|
|
235
|
+
iv ??= randomBytes(12) // 96 bits
|
|
236
|
+
|
|
237
|
+
const cipher = createCipheriv(
|
|
238
|
+
options.symetricEncryptionMethod,
|
|
239
|
+
Buffer.from(encryptionKey, 'base64'),
|
|
240
|
+
iv,
|
|
241
|
+
{
|
|
242
|
+
authTagLength
|
|
243
|
+
}
|
|
244
|
+
)
|
|
245
|
+
cipher.setAAD(Buffer.from(sub ?? '', 'utf8'))
|
|
301
246
|
const encryptedData =
|
|
302
247
|
cipher.update(data, decoding, encoding) + cipher.final(encoding)
|
|
303
248
|
const authTag = cipher.getAuthTag()
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
249
|
+
|
|
250
|
+
const encryptedDataPacket =
|
|
251
|
+
iv.toString(encoding) + authTag.toString(encoding) + encryptedData
|
|
252
|
+
|
|
253
|
+
return encryptedDataPacket
|
|
309
254
|
}
|
|
310
255
|
|
|
311
|
-
export const
|
|
312
|
-
|
|
256
|
+
export const symetricDecryptFields = (
|
|
257
|
+
encryptedValues,
|
|
258
|
+
{ encryptedKey, encryptionKey, sub },
|
|
259
|
+
fields = []
|
|
260
|
+
) => {
|
|
261
|
+
if (encryptedKey) {
|
|
262
|
+
encryptionKey ??= symetricDecryptKey(encryptedKey, {
|
|
263
|
+
sub
|
|
264
|
+
})
|
|
265
|
+
}
|
|
266
|
+
if (!encryptionKey) return encryptedValues
|
|
267
|
+
const values = structuredClone(encryptedValues)
|
|
313
268
|
for (const key of fields) {
|
|
314
|
-
values[key] &&=
|
|
269
|
+
values[key] &&= symetricDecrypt(values[key], {
|
|
270
|
+
encryptionKey,
|
|
271
|
+
sub
|
|
272
|
+
})
|
|
315
273
|
}
|
|
316
274
|
return values
|
|
317
275
|
}
|
|
318
276
|
|
|
319
|
-
export const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
'hex',
|
|
327
|
-
'utf8'
|
|
328
|
-
)
|
|
329
|
-
return data
|
|
277
|
+
export const symetricDecryptKey = (encryptedKey, { sub } = {}) => {
|
|
278
|
+
return symetricDecrypt(encryptedKey, {
|
|
279
|
+
encryptionKey: options.symetricEncryptionKey,
|
|
280
|
+
sub,
|
|
281
|
+
decoding: options.symetricEncryptionEncoding,
|
|
282
|
+
encoding: 'base64'
|
|
283
|
+
})
|
|
330
284
|
}
|
|
331
285
|
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
Buffer.from(options.encryptionSharedKey, 'hex'),
|
|
336
|
-
assocData,
|
|
337
|
-
'hex',
|
|
338
|
-
'hex'
|
|
339
|
-
)
|
|
340
|
-
|
|
341
|
-
const __decrypt = (
|
|
342
|
-
data,
|
|
343
|
-
encryptionKey,
|
|
344
|
-
assocData,
|
|
345
|
-
decoding = 'hex',
|
|
346
|
-
encoding = 'utf8'
|
|
286
|
+
export const symetricDecrypt = (
|
|
287
|
+
encryptedDataPacket,
|
|
288
|
+
{ encryptedKey, encryptionKey, sub, decoding, encoding }
|
|
347
289
|
) => {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
290
|
+
if (encryptedKey) {
|
|
291
|
+
encryptionKey ??= symetricDecryptKey(encryptedKey, {
|
|
292
|
+
sub
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
if (!encryptionKey) return encryptedDataPacket
|
|
296
|
+
decoding ??= options.symetricEncryptionEncoding
|
|
297
|
+
encoding ??= 'utf8'
|
|
298
|
+
|
|
299
|
+
const iv = Buffer.from(
|
|
300
|
+
encryptedDataPacket.substring(0, symetricEncryptionEncodingLengths.iv),
|
|
301
|
+
decoding
|
|
302
|
+
)
|
|
303
|
+
const authTag = Buffer.from(
|
|
304
|
+
encryptedDataPacket.substring(
|
|
305
|
+
symetricEncryptionEncodingLengths.iv,
|
|
306
|
+
symetricEncryptionEncodingLengths.ivAndAuthTag
|
|
307
|
+
),
|
|
308
|
+
decoding
|
|
309
|
+
)
|
|
310
|
+
const encryptedData = Buffer.from(
|
|
311
|
+
encryptedDataPacket.substring(
|
|
312
|
+
symetricEncryptionEncodingLengths.ivAndAuthTag
|
|
313
|
+
),
|
|
314
|
+
decoding
|
|
315
|
+
)
|
|
351
316
|
|
|
352
317
|
const decipher = createDecipheriv(
|
|
353
|
-
options.
|
|
354
|
-
encryptionKey,
|
|
318
|
+
options.symetricEncryptionMethod,
|
|
319
|
+
Buffer.from(encryptionKey, 'base64'),
|
|
355
320
|
iv,
|
|
356
321
|
{
|
|
357
322
|
authTagLength
|
|
358
323
|
}
|
|
359
324
|
)
|
|
360
|
-
decipher.setAAD(Buffer.from(
|
|
325
|
+
decipher.setAAD(Buffer.from(sub ?? '', 'utf8'))
|
|
326
|
+
|
|
361
327
|
decipher.setAuthTag(authTag)
|
|
362
|
-
|
|
328
|
+
const data =
|
|
363
329
|
decipher.update(encryptedData, decoding, encoding) +
|
|
364
330
|
decipher.final(encoding)
|
|
365
|
-
|
|
331
|
+
return data
|
|
366
332
|
}
|
|
367
333
|
|
|
368
334
|
// *** Signatures *** //
|
|
369
|
-
|
|
335
|
+
// asymmetricKeyPairType
|
|
336
|
+
export const makeAsymmetricKeys = async () => {
|
|
370
337
|
const { publicKey, privateKey } = await generateKeyPair('ec', {
|
|
371
338
|
namedCurve: 'P-384', // P-512
|
|
372
339
|
paramEncoding: 'named',
|
|
@@ -376,44 +343,32 @@ export const makeAsymmetricKeys = async (encryptionKey) => {
|
|
|
376
343
|
},
|
|
377
344
|
privateKeyEncoding: {
|
|
378
345
|
type: 'sec1',
|
|
379
|
-
format: 'pem'
|
|
380
|
-
//
|
|
381
|
-
cipher: options.
|
|
382
|
-
passphrase: encryptionKey
|
|
346
|
+
format: 'pem'
|
|
347
|
+
// Encryption done at another level for consistency
|
|
348
|
+
// cipher: options.asymetricEncryptionMethod,
|
|
349
|
+
// passphrase: encryptionKey,
|
|
383
350
|
}
|
|
384
351
|
})
|
|
385
352
|
return { publicKey, privateKey }
|
|
386
353
|
}
|
|
387
354
|
|
|
388
|
-
export const makeSignature = (data, privateKey, algorithm =
|
|
389
|
-
|
|
355
|
+
export const makeSignature = async (data, privateKey, { algorithm } = {}) => {
|
|
356
|
+
algorithm ??= options.digestAlgorithm
|
|
357
|
+
return (await sign(algorithm, Buffer.from(data), privateKey)).toString(
|
|
358
|
+
options.signatureEncoding
|
|
359
|
+
)
|
|
390
360
|
}
|
|
391
|
-
export const verifySignature = (
|
|
361
|
+
export const verifySignature = async (
|
|
392
362
|
data,
|
|
393
363
|
publicKey,
|
|
394
364
|
signature,
|
|
395
|
-
algorithm =
|
|
365
|
+
{ algorithm } = {}
|
|
396
366
|
) => {
|
|
397
|
-
|
|
367
|
+
algorithm ??= options.digestAlgorithm
|
|
368
|
+
return await verify(
|
|
398
369
|
algorithm,
|
|
399
370
|
Buffer.from(data),
|
|
400
371
|
publicKey,
|
|
401
|
-
Buffer.from(signature,
|
|
372
|
+
Buffer.from(signature, options.signatureEncoding)
|
|
402
373
|
)
|
|
403
374
|
}
|
|
404
|
-
// const assocData = 'sub'
|
|
405
|
-
// const input = 'data'
|
|
406
|
-
//
|
|
407
|
-
// const {encryptedKey} = makeSymetricKey(assocData)
|
|
408
|
-
// const encryptedData = encrypt(input, encryptedKey, assocData)
|
|
409
|
-
// const output = decrypt(encryptedData, encryptedKey, assocData)
|
|
410
|
-
// console.log(assocData, input, output, encryptedData)
|
|
411
|
-
|
|
412
|
-
// const assocData = 'tlSIcVp1XEF'
|
|
413
|
-
// const encryptionKey =
|
|
414
|
-
// 'b9ebfdab6603dd8e151138ec8298aaf8b49bf6d38480f9864e9e72e65c4d8b59.b9f9bc89d7f3ce4aa1e1af469d10508b.aa1b6a682fcc186e867a9cfd'
|
|
415
|
-
// const value =
|
|
416
|
-
// '76ebdea75ea529c16f1645d5435413934cbb97378293776cb446331e6d8323865a808b7f0822cd4f36ef6cca57354ea7c955c94d232a55038321184f4294971315fda4b8dc2f33422bbd3a5c978edc0f5bf019f818f0c92f7c570bdc49fe2f71df3856655198b6723fa52697b9225b2e79f04ed80cd35ef61a8e1e06c3751d3f9caa40aa9a41528ce5ab1bfae8ecff0feab784497ccbf2c7dca4461c6b9a74d1ded6233dc38ad75d0fe2f57cfda2bd1d59ed1f8c53de911cf8071dc7cefde7870c303137586bc0c5f85fe93505d9056994a4d8e59df2027b564c303aa723e98ae5934b15039f90d21827ac22b824593f334466622582a2857f04d8bf450310a9924060d113d2ece0259da68376b31a3c2e8a0b79e81f8efe96bf2b7a6b5b1841edca2566a0a89ab80ee6d6ac2ed24e788ad318a1238dfa5b9e965d256935d3aeafe52f986a530b2f9225bbe7a73b43e6515ce789701f501546239460bccc46285f3fcff0c54c2b6df820334a974abea81460fd4ac99a13cb88e296a2275f80f4c783757319417078458977461dd5e101f9f9b7d303578379dc15ee1b549e3fcceffffba895ecc8291d1f82e150ce8e10640fd79f2e0666167ef7d529cd28cb76a72bf2b3e239dbb845bedd39f42c938bbe711542231fb835edb547d919b3c695272f4cd43640eb3bed2b86948278e5aeeb67ef932ef08332730e75752fd5bd247e2dd30470fc493362d2ecac72237dfbf0a1694f8e6b8c18ab24c71eb723a3f610a056a73fea90f4f8660bfc6dec46fa52a55a3d72a5c9b97770bff7d52ea91b97cc643d915341642e201918fcb088986e9134e133519c6575048e3f840622e569fc5fa66e4f0c31681c9de4b1b1f6acb179728bc0a9f9950bf2e0c8d49da835298013977a2f97d7052bcbedbc39b875bbf9a0622ede589810175ab2455ea72a8274659af7066a2c97ba3aacce84531d8685d08b15d3619038997ae459ac916a88bedaf619f574236d1b86b02f29edaf78684c91c6ee45199b861359ee374fecb95be8a85dc012991d22564adeb2ec95f160574858fa010c40ce74077a08f0e681eb4b10a0093e885f115a12469a82b07012ad4e20b836d5711358c47969af89d09c9299edc540321fe897312ca49c9e8f93d50dc68394d0cc1d108f38e3534821b979c405c1f526a3b57651897873aeeb0909b176fb2034f9fdb1d43a8003809af29ff0bdc456a593029c4526b270908ac94b620a7cb8b7da20a6421e0f2671c899c20d78e1e0a1c3ab0b92948c90fca3c36d21a5e53143bc7e061773251364f8ce3b8992b6313c9604dc19c018b9efc6ca8f7c1fe039077fd76775461d1fa3968f51be7e340e3f09b40d6b44c77a7cbf7d71cad2ddea1121b31d27470a38cda8f8d38170a0d8f0b02d56f8e97b2134597d6111ea6670545bd4f7b4e4d6d7cf2fb5b787a68ba8ee84775f06db195f47282d9646dfbdbb57701e2ef5a432b621cb54a70e94de6b7a3f95bfe3c50793e08a27f76f832070fb2d23ed19dcd75f798aeff0c3e76d8215d43e1a8b7e643bffa243691adf2abcd52f465390e92e0344e2f452673f12d50e2ead0496ba76504ae0f78c81f2a2b5a64b39d3fb35f39f178b9a1587d1f07ed28e7696bfc8740f429f6cc35c964ae04ef39c1070eab6415be3aab887ff6192ac4d49ba9b169221b47d73c55f6010dda07a77cbcf54b779d380591786ba70c6d7000af57034539487f801aeb5c097939682e1530bc26c0b030bcab327ddaaaac5c9b969301b6d4ed7f601a46e8431e8cae3800c80c42686fee3b7625742798ced5976d163d2c8aad1bdac36c001e4e246fdb7b4c6850ed99554cfc0c07b7c7acc7659fd042989cc291ec2bd03a59e439860a5c48e430780f9d85c9acb3e85e741e73163f29afb7c43f143e7ada99a4670cbbe2210bfce8d36ee86.a5237f2d798c9f98b26a676794d3c3f6.0f15d0f53657150aeb1462d5'
|
|
417
|
-
// const output = decrypt(value, encryptionKey, assocData)
|
|
418
|
-
// console.log(output)
|
|
419
|
-
// *** TOTP *** //
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1auth/crypto",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.33",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=
|
|
7
|
+
"node": ">=20"
|
|
8
8
|
},
|
|
9
9
|
"engineStrict": true,
|
|
10
10
|
"publishConfig": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"url": "https://github.com/willfarrell/1auth/issues"
|
|
45
45
|
},
|
|
46
46
|
"homepage": "https://github.com/willfarrell/1auth",
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "14b8c5bd83728c460fdcc4c3af5ae5c3c2bb9007",
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@node-rs/argon2": "1.8.3"
|
|
50
50
|
}
|