@1auth/crypto 0.0.0-alpha.32 → 0.0.0-alpha.34
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 +265 -283
- 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,134 @@ 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)
|
|
135
|
+
create: (prefix) =>
|
|
136
|
+
(prefix ? prefix + '_' : '') + randomAlphaNumeric(randomId.minLength)
|
|
178
137
|
}
|
|
179
138
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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)
|
|
139
|
+
// *** Digests *** //
|
|
140
|
+
export const createChecksum = (value, { algorithm } = {}) => {
|
|
141
|
+
algorithm ??= options.digestAlgorithm
|
|
142
|
+
return checksum(algorithm).update(value).digest('hex')
|
|
191
143
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
144
|
+
export const createDigest = (value, { algorithm } = {}) => {
|
|
145
|
+
algorithm ??= options.digestAlgorithm
|
|
146
|
+
const checksum = createChecksum(value, { algorithm })
|
|
147
|
+
return `${algorithm}:${checksum}`
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// TODO evaluate non-iv encryption approach?
|
|
151
|
+
// Use encryption as pepper to allow easier rotation of pepper
|
|
152
|
+
export const createEncryptedDigest = (value, { algorithm } = {}) => {
|
|
153
|
+
const digest = createDigest(value, { algorithm })
|
|
154
|
+
// encrypting using the symetricEncryptionKey instread of
|
|
155
|
+
// the row encryptionKey to allow lookup, must have fixed iv
|
|
156
|
+
return symetricEncrypt(digest, {
|
|
157
|
+
encryptionKey: options.symetricEncryptionKey,
|
|
158
|
+
sub: '',
|
|
159
|
+
encoding: options.symetricEncryptionEncoding,
|
|
160
|
+
iv: Buffer.from(
|
|
161
|
+
options.symetricEncryptionKey.substring(
|
|
162
|
+
0,
|
|
163
|
+
symetricEncryptionEncodingLengths.iv
|
|
164
|
+
),
|
|
165
|
+
options.symetricEncryptionEncoding
|
|
166
|
+
)
|
|
167
|
+
})
|
|
204
168
|
}
|
|
205
169
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
170
|
+
export const rotateDigestEncryption = (
|
|
171
|
+
encryptedValue,
|
|
172
|
+
encryptionKey,
|
|
173
|
+
newEncryptionKey
|
|
210
174
|
) => {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
// bits*ln(2)/ln(characterPoolSize)
|
|
217
|
-
return Math.round((bits * Math.LN2) / Math.log(characterPoolSize))
|
|
218
|
-
}
|
|
175
|
+
const digest = symetricDecrypt(encryptedValue, {
|
|
176
|
+
encryptionKey,
|
|
177
|
+
sub: '',
|
|
178
|
+
encoding: options.symetricEncryptionEncoding
|
|
179
|
+
})
|
|
219
180
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
181
|
+
return symetricEncrypt(digest, {
|
|
182
|
+
encryptionKey: newEncryptionKey,
|
|
183
|
+
sub: '',
|
|
184
|
+
encoding: options.symetricEncryptionEncoding,
|
|
185
|
+
iv: Buffer.from(
|
|
186
|
+
newEncryptionKey.substring(0, symetricEncryptionEncodingLengths.iv),
|
|
187
|
+
options.symetricEncryptionEncoding
|
|
188
|
+
)
|
|
189
|
+
})
|
|
228
190
|
}
|
|
229
191
|
|
|
230
192
|
// *** Hashing *** //
|
|
@@ -249,124 +211,156 @@ export const verifySecretHash = async (hash, value) => {
|
|
|
249
211
|
// *** Encryption *** //
|
|
250
212
|
const authTagLength = 16
|
|
251
213
|
|
|
252
|
-
export const makeSymetricKey = (
|
|
253
|
-
if (!options.
|
|
214
|
+
export const makeSymetricKey = (sub) => {
|
|
215
|
+
if (!options.symetricEncryptionKey) {
|
|
254
216
|
return { encryptionKey: '', encryptedKey: '' }
|
|
255
217
|
}
|
|
256
|
-
const encryptionKey =
|
|
257
|
-
const encryptedKey =
|
|
258
|
-
encryptionKey,
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
)
|
|
218
|
+
const encryptionKey = randomSymetricEncryptionKey()
|
|
219
|
+
const encryptedKey = symetricEncrypt(encryptionKey, {
|
|
220
|
+
encryptionKey: options.symetricEncryptionKey,
|
|
221
|
+
sub,
|
|
222
|
+
decoding: 'base64',
|
|
223
|
+
encoding: options.symetricEncryptionEncoding
|
|
224
|
+
})
|
|
264
225
|
return { encryptionKey, encryptedKey }
|
|
265
226
|
}
|
|
266
227
|
|
|
267
|
-
//
|
|
268
|
-
export const
|
|
269
|
-
|
|
228
|
+
// sub add context to encryption
|
|
229
|
+
export const symetricEncryptFields = (
|
|
230
|
+
values,
|
|
231
|
+
{ encryptedKey, encryptionKey, sub },
|
|
232
|
+
fields = []
|
|
233
|
+
) => {
|
|
234
|
+
if (encryptedKey) {
|
|
235
|
+
encryptionKey ??= symetricDecryptKey(encryptedKey, {
|
|
236
|
+
sub
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
if (!encryptionKey) return values
|
|
240
|
+
const encryptedValues = structuredClone(values)
|
|
270
241
|
for (const key of fields) {
|
|
271
|
-
|
|
242
|
+
encryptedValues[key] &&= symetricEncrypt(encryptedValues[key], {
|
|
243
|
+
encryptionKey,
|
|
244
|
+
sub
|
|
245
|
+
})
|
|
272
246
|
}
|
|
273
|
-
return
|
|
247
|
+
return encryptedValues
|
|
274
248
|
}
|
|
275
249
|
|
|
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 = (
|
|
250
|
+
export const symetricEncrypt = (
|
|
290
251
|
data,
|
|
291
|
-
encryptionKey,
|
|
292
|
-
assocData,
|
|
293
|
-
decoding = 'utf8',
|
|
294
|
-
encoding = 'hex'
|
|
252
|
+
{ encryptedKey, encryptionKey, sub, decoding, encoding, iv }
|
|
295
253
|
) => {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
254
|
+
if (encryptedKey) {
|
|
255
|
+
encryptionKey ??= symetricDecryptKey(encryptedKey, {
|
|
256
|
+
sub
|
|
257
|
+
})
|
|
258
|
+
}
|
|
259
|
+
if (!encryptionKey) return data
|
|
260
|
+
decoding ??= 'utf8'
|
|
261
|
+
encoding ??= options.symetricEncryptionEncoding
|
|
262
|
+
iv ??= randomBytes(12) // 96 bits
|
|
263
|
+
|
|
264
|
+
const cipher = createCipheriv(
|
|
265
|
+
options.symetricEncryptionMethod,
|
|
266
|
+
Buffer.from(encryptionKey, 'base64'),
|
|
267
|
+
iv,
|
|
268
|
+
{
|
|
269
|
+
authTagLength
|
|
270
|
+
}
|
|
271
|
+
)
|
|
272
|
+
cipher.setAAD(Buffer.from(sub ?? '', 'utf8'))
|
|
301
273
|
const encryptedData =
|
|
302
274
|
cipher.update(data, decoding, encoding) + cipher.final(encoding)
|
|
303
275
|
const authTag = cipher.getAuthTag()
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
276
|
+
|
|
277
|
+
const encryptedDataPacket =
|
|
278
|
+
iv.toString(encoding) + authTag.toString(encoding) + encryptedData
|
|
279
|
+
|
|
280
|
+
return encryptedDataPacket
|
|
309
281
|
}
|
|
310
282
|
|
|
311
|
-
export const
|
|
312
|
-
|
|
283
|
+
export const symetricDecryptFields = (
|
|
284
|
+
encryptedValues,
|
|
285
|
+
{ encryptedKey, encryptionKey, sub },
|
|
286
|
+
fields = []
|
|
287
|
+
) => {
|
|
288
|
+
if (encryptedKey) {
|
|
289
|
+
encryptionKey ??= symetricDecryptKey(encryptedKey, {
|
|
290
|
+
sub
|
|
291
|
+
})
|
|
292
|
+
}
|
|
293
|
+
if (!encryptionKey) return encryptedValues
|
|
294
|
+
const values = structuredClone(encryptedValues)
|
|
313
295
|
for (const key of fields) {
|
|
314
|
-
values[key] &&=
|
|
296
|
+
values[key] &&= symetricDecrypt(values[key], {
|
|
297
|
+
encryptionKey,
|
|
298
|
+
sub
|
|
299
|
+
})
|
|
315
300
|
}
|
|
316
301
|
return values
|
|
317
302
|
}
|
|
318
303
|
|
|
319
|
-
export const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
'hex',
|
|
327
|
-
'utf8'
|
|
328
|
-
)
|
|
329
|
-
return data
|
|
304
|
+
export const symetricDecryptKey = (encryptedKey, { sub } = {}) => {
|
|
305
|
+
return symetricDecrypt(encryptedKey, {
|
|
306
|
+
encryptionKey: options.symetricEncryptionKey,
|
|
307
|
+
sub,
|
|
308
|
+
decoding: options.symetricEncryptionEncoding,
|
|
309
|
+
encoding: 'base64'
|
|
310
|
+
})
|
|
330
311
|
}
|
|
331
312
|
|
|
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'
|
|
313
|
+
export const symetricDecrypt = (
|
|
314
|
+
encryptedDataPacket,
|
|
315
|
+
{ encryptedKey, encryptionKey, sub, decoding, encoding }
|
|
347
316
|
) => {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
317
|
+
if (encryptedKey) {
|
|
318
|
+
encryptionKey ??= symetricDecryptKey(encryptedKey, {
|
|
319
|
+
sub
|
|
320
|
+
})
|
|
321
|
+
}
|
|
322
|
+
if (!encryptionKey) return encryptedDataPacket
|
|
323
|
+
decoding ??= options.symetricEncryptionEncoding
|
|
324
|
+
encoding ??= 'utf8'
|
|
325
|
+
|
|
326
|
+
const iv = Buffer.from(
|
|
327
|
+
encryptedDataPacket.substring(0, symetricEncryptionEncodingLengths.iv),
|
|
328
|
+
decoding
|
|
329
|
+
)
|
|
330
|
+
const authTag = Buffer.from(
|
|
331
|
+
encryptedDataPacket.substring(
|
|
332
|
+
symetricEncryptionEncodingLengths.iv,
|
|
333
|
+
symetricEncryptionEncodingLengths.ivAndAuthTag
|
|
334
|
+
),
|
|
335
|
+
decoding
|
|
336
|
+
)
|
|
337
|
+
const encryptedData = Buffer.from(
|
|
338
|
+
encryptedDataPacket.substring(
|
|
339
|
+
symetricEncryptionEncodingLengths.ivAndAuthTag
|
|
340
|
+
),
|
|
341
|
+
decoding
|
|
342
|
+
)
|
|
351
343
|
|
|
352
344
|
const decipher = createDecipheriv(
|
|
353
|
-
options.
|
|
354
|
-
encryptionKey,
|
|
345
|
+
options.symetricEncryptionMethod,
|
|
346
|
+
Buffer.from(encryptionKey, 'base64'),
|
|
355
347
|
iv,
|
|
356
348
|
{
|
|
357
349
|
authTagLength
|
|
358
350
|
}
|
|
359
351
|
)
|
|
360
|
-
decipher.setAAD(Buffer.from(
|
|
352
|
+
decipher.setAAD(Buffer.from(sub ?? '', 'utf8'))
|
|
353
|
+
|
|
361
354
|
decipher.setAuthTag(authTag)
|
|
362
|
-
|
|
355
|
+
const data =
|
|
363
356
|
decipher.update(encryptedData, decoding, encoding) +
|
|
364
357
|
decipher.final(encoding)
|
|
365
|
-
|
|
358
|
+
return data
|
|
366
359
|
}
|
|
367
360
|
|
|
368
361
|
// *** Signatures *** //
|
|
369
|
-
|
|
362
|
+
// asymmetricKeyPairType
|
|
363
|
+
export const makeAsymmetricKeys = async () => {
|
|
370
364
|
const { publicKey, privateKey } = await generateKeyPair('ec', {
|
|
371
365
|
namedCurve: 'P-384', // P-512
|
|
372
366
|
paramEncoding: 'named',
|
|
@@ -376,44 +370,32 @@ export const makeAsymmetricKeys = async (encryptionKey) => {
|
|
|
376
370
|
},
|
|
377
371
|
privateKeyEncoding: {
|
|
378
372
|
type: 'sec1',
|
|
379
|
-
format: 'pem'
|
|
380
|
-
//
|
|
381
|
-
cipher: options.
|
|
382
|
-
passphrase: encryptionKey
|
|
373
|
+
format: 'pem'
|
|
374
|
+
// Encryption done at another level for consistency
|
|
375
|
+
// cipher: options.asymetricEncryptionMethod,
|
|
376
|
+
// passphrase: encryptionKey,
|
|
383
377
|
}
|
|
384
378
|
})
|
|
385
379
|
return { publicKey, privateKey }
|
|
386
380
|
}
|
|
387
381
|
|
|
388
|
-
export const makeSignature = (data, privateKey, algorithm =
|
|
389
|
-
|
|
382
|
+
export const makeSignature = async (data, privateKey, { algorithm } = {}) => {
|
|
383
|
+
algorithm ??= options.digestAlgorithm
|
|
384
|
+
return (await sign(algorithm, Buffer.from(data), privateKey)).toString(
|
|
385
|
+
options.signatureEncoding
|
|
386
|
+
)
|
|
390
387
|
}
|
|
391
|
-
export const verifySignature = (
|
|
388
|
+
export const verifySignature = async (
|
|
392
389
|
data,
|
|
393
390
|
publicKey,
|
|
394
391
|
signature,
|
|
395
|
-
algorithm =
|
|
392
|
+
{ algorithm } = {}
|
|
396
393
|
) => {
|
|
397
|
-
|
|
394
|
+
algorithm ??= options.digestAlgorithm
|
|
395
|
+
return await verify(
|
|
398
396
|
algorithm,
|
|
399
397
|
Buffer.from(data),
|
|
400
398
|
publicKey,
|
|
401
|
-
Buffer.from(signature,
|
|
399
|
+
Buffer.from(signature, options.signatureEncoding)
|
|
402
400
|
)
|
|
403
401
|
}
|
|
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.34",
|
|
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": "c88105a99efd7f3de80795736d6194e52ef465b4",
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@node-rs/argon2": "1.8.3"
|
|
50
50
|
}
|