@1auth/crypto 0.0.0-alpha.31 → 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 -285
- package/package.json +3 -3
package/index.js
CHANGED
|
@@ -5,26 +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
|
-
|
|
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',
|
|
19
24
|
digestAlgorithm: 'sha3-384',
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
+
}
|
|
25
42
|
}
|
|
26
43
|
|
|
27
|
-
const
|
|
44
|
+
export const getOptions = () => options
|
|
28
45
|
|
|
29
46
|
// *** entropy *** //
|
|
30
47
|
// export const characterPoolSize = (value) => {
|
|
@@ -42,188 +59,107 @@ const generateKeyPair = promisify(generateKeyPairCallback)
|
|
|
42
59
|
// return max - min
|
|
43
60
|
// }
|
|
44
61
|
|
|
45
|
-
// ***
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
hex: 16,
|
|
51
|
-
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))
|
|
52
67
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
63
116
|
}
|
|
64
117
|
|
|
65
|
-
export const
|
|
66
|
-
|
|
67
|
-
bits,
|
|
68
|
-
characterPoolSize.base64
|
|
69
|
-
)
|
|
70
|
-
return randomBytes(characterLength + 1)
|
|
71
|
-
.toString('base64')
|
|
72
|
-
.replace('=', '')
|
|
118
|
+
export const randomAlphaNumeric = (characterLength) => {
|
|
119
|
+
return randomCharacters(characterLength, charactersAlphaNumeric)
|
|
73
120
|
}
|
|
74
121
|
|
|
75
|
-
export const randomNumeric = (
|
|
76
|
-
|
|
77
|
-
bits,
|
|
78
|
-
characterPoolSize.numeric
|
|
79
|
-
)
|
|
80
|
-
const max = 10 ** characterLength
|
|
81
|
-
let value = Math.floor(Math.random() * max).toString()
|
|
82
|
-
if (value === max.toString()) {
|
|
83
|
-
return randomNumeric(bits)
|
|
84
|
-
}
|
|
85
|
-
while (value.length < characterLength) value = '0' + value
|
|
86
|
-
return value
|
|
122
|
+
export const randomNumeric = (characterLength) => {
|
|
123
|
+
return randomCharacters(characterLength, charactersNumeric)
|
|
87
124
|
}
|
|
88
125
|
|
|
89
|
-
export const
|
|
90
|
-
|
|
91
|
-
return randomBytes(characterLength).toString('hex')
|
|
126
|
+
export const randomSymetricEncryptionKey = () => {
|
|
127
|
+
return randomBytes(32).toString('base64') // 256 bits
|
|
92
128
|
}
|
|
93
129
|
|
|
94
130
|
// *** configs *** //
|
|
95
131
|
export const randomId = {
|
|
96
132
|
type: 'id',
|
|
97
|
-
|
|
98
|
-
charPool: characterPoolSize.alphaNumeric,
|
|
133
|
+
minLength: entropyToCharacterLength(64, charactersAlphaNumeric.length),
|
|
99
134
|
// TODO update to use https://github.com/jetpack-io/typeid
|
|
100
|
-
create:
|
|
101
|
-
(prefix ? prefix + '_' : '') + randomAlphaNumeric(randomId.
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export const subject = {
|
|
105
|
-
type: 'id',
|
|
106
|
-
entropy: 64,
|
|
107
|
-
charPool: characterPoolSize.alphaNumeric,
|
|
108
|
-
create: async (prefix) =>
|
|
109
|
-
(prefix ? prefix + '_' : '') + randomAlphaNumeric(subject.entropy)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export const session = {
|
|
113
|
-
type: 'id',
|
|
114
|
-
entropy: 128, // ASVS 3.2.2
|
|
115
|
-
charPool: characterPoolSize.alphaNumeric,
|
|
116
|
-
expire: 15 * 60,
|
|
117
|
-
create: async (prefix) =>
|
|
118
|
-
(prefix ? prefix + '_' : '') + randomAlphaNumeric(session.entropy)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export const passwordSecret = {
|
|
122
|
-
type: 'secret',
|
|
123
|
-
entropy: 64,
|
|
124
|
-
charPool: characterPoolSize.keyboard,
|
|
125
|
-
otp: false,
|
|
126
|
-
encode: async (value, encryptedKey, sub) =>
|
|
127
|
-
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
|
|
128
|
-
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
|
|
129
|
-
verify: async (value, hash) => verifySecretHash(hash, value)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// aka Password/Credential Recovery
|
|
133
|
-
export const passwordToken = {
|
|
134
|
-
type: 'token',
|
|
135
|
-
entropy: 20,
|
|
136
|
-
charPool: characterPoolSize.numeric,
|
|
137
|
-
otp: true,
|
|
138
|
-
expire: 30,
|
|
139
|
-
create: async () => randomNumeric(passwordToken.entropy),
|
|
140
|
-
encode: async (value, encryptedKey, sub) =>
|
|
141
|
-
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
|
|
142
|
-
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
|
|
143
|
-
verify: async (value, hash) => verifySecretHash(hash, value)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export const oneTimeSecret = {
|
|
147
|
-
type: 'secret',
|
|
148
|
-
entropy: 64, //
|
|
149
|
-
charPool: characterPoolSize.base64,
|
|
150
|
-
otp: false,
|
|
151
|
-
expire: null,
|
|
152
|
-
create: async () => randomBase64(oneTimeSecret.entropy),
|
|
153
|
-
encode: async (value, encryptedKey, sub) => encrypt(value, encryptedKey, sub),
|
|
154
|
-
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub)
|
|
155
|
-
// create, hash, verify to be handled within
|
|
156
|
-
}
|
|
157
|
-
export const oneTimeToken = {
|
|
158
|
-
type: 'token',
|
|
159
|
-
entropy: 20,
|
|
160
|
-
charPool: characterPoolSize.numeric,
|
|
161
|
-
otp: true,
|
|
162
|
-
expire: 30
|
|
163
|
-
// create, hash, verify to be handled within
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// aka lookup secret
|
|
167
|
-
export const recoveryCode = {
|
|
168
|
-
type: 'secret',
|
|
169
|
-
entropy: 112,
|
|
170
|
-
charPool: characterPoolSize.alphaNumeric,
|
|
171
|
-
otp: true,
|
|
172
|
-
create: async () => randomAlphaNumeric(recoveryCode.entropy),
|
|
173
|
-
encode: async (value, encryptedKey, sub) =>
|
|
174
|
-
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
|
|
175
|
-
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
|
|
176
|
-
verify: async (value, hash) => verifySecretHash(hash, value)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export const outOfBandToken = {
|
|
180
|
-
type: 'token',
|
|
181
|
-
entropy: 20,
|
|
182
|
-
charPool: characterPoolSize.numeric,
|
|
183
|
-
otp: true,
|
|
184
|
-
expire: 10 * 60,
|
|
185
|
-
create: async () => randomNumeric(outOfBandToken.entropy),
|
|
186
|
-
encode: async (value, encryptedKey, sub) =>
|
|
187
|
-
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
|
|
188
|
-
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
|
|
189
|
-
verify: async (value, hash) => verifySecretHash(hash, value)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
export const accessToken = {
|
|
193
|
-
type: 'secret',
|
|
194
|
-
entropy: 112,
|
|
195
|
-
charPool: characterPoolSize.alphaNumeric,
|
|
196
|
-
otp: false,
|
|
197
|
-
expire: 30 * 24 * 60 * 60, // allow override from user
|
|
198
|
-
create: async () => randomAlphaNumeric(accessToken.entropy),
|
|
199
|
-
encode: async (value, encryptedKey, sub) =>
|
|
200
|
-
createSecretHash(value).then((hash) => encrypt(hash, encryptedKey, sub)),
|
|
201
|
-
decode: async (value, encryptedKey, sub) => decrypt(value, encryptedKey, sub),
|
|
202
|
-
verify: async (value, hash) => verifySecretHash(hash, value)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// *** Helpers *** //
|
|
206
|
-
export const characterLengthToEntropy = (
|
|
207
|
-
characterLength,
|
|
208
|
-
characterPoolSize
|
|
209
|
-
) => {
|
|
210
|
-
// log_2(characterPoolSize^characterLength)
|
|
211
|
-
return Math.round(Math.log2(characterPoolSize ** characterLength))
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
export const entropyToCharacterLength = (bits, characterPoolSize) => {
|
|
215
|
-
// bits*ln(2)/ln(characterPoolSize)
|
|
216
|
-
return Math.round((bits * Math.LN2) / Math.log(characterPoolSize))
|
|
135
|
+
create: (prefix) =>
|
|
136
|
+
(prefix ? prefix + '_' : '') + randomAlphaNumeric(randomId.minLength)
|
|
217
137
|
}
|
|
218
138
|
|
|
219
139
|
// *** Digests *** //
|
|
220
|
-
export const createDigest =
|
|
140
|
+
export const createDigest = (value, { algorithm } = {}) => {
|
|
221
141
|
algorithm ??= options.digestAlgorithm
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
+
})
|
|
227
163
|
}
|
|
228
164
|
|
|
229
165
|
// *** Hashing *** //
|
|
@@ -248,124 +184,156 @@ export const verifySecretHash = async (hash, value) => {
|
|
|
248
184
|
// *** Encryption *** //
|
|
249
185
|
const authTagLength = 16
|
|
250
186
|
|
|
251
|
-
export const makeSymetricKey = (
|
|
252
|
-
if (!options.
|
|
187
|
+
export const makeSymetricKey = (sub) => {
|
|
188
|
+
if (!options.symetricEncryptionKey) {
|
|
253
189
|
return { encryptionKey: '', encryptedKey: '' }
|
|
254
190
|
}
|
|
255
|
-
const encryptionKey =
|
|
256
|
-
const encryptedKey =
|
|
257
|
-
encryptionKey,
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
)
|
|
191
|
+
const encryptionKey = randomSymetricEncryptionKey()
|
|
192
|
+
const encryptedKey = symetricEncrypt(encryptionKey, {
|
|
193
|
+
encryptionKey: options.symetricEncryptionKey,
|
|
194
|
+
sub,
|
|
195
|
+
decoding: 'base64',
|
|
196
|
+
encoding: options.symetricEncryptionEncoding
|
|
197
|
+
})
|
|
263
198
|
return { encryptionKey, encryptedKey }
|
|
264
199
|
}
|
|
265
200
|
|
|
266
|
-
//
|
|
267
|
-
export const
|
|
268
|
-
|
|
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)
|
|
269
214
|
for (const key of fields) {
|
|
270
|
-
|
|
215
|
+
encryptedValues[key] &&= symetricEncrypt(encryptedValues[key], {
|
|
216
|
+
encryptionKey,
|
|
217
|
+
sub
|
|
218
|
+
})
|
|
271
219
|
}
|
|
272
|
-
return
|
|
220
|
+
return encryptedValues
|
|
273
221
|
}
|
|
274
222
|
|
|
275
|
-
export const
|
|
276
|
-
if (!encryptedKey) return data
|
|
277
|
-
|
|
278
|
-
const encryptionKey = __decryptKey(encryptedKey, assocData)
|
|
279
|
-
return __encrypt(
|
|
280
|
-
data,
|
|
281
|
-
Buffer.from(encryptionKey, 'hex'),
|
|
282
|
-
assocData,
|
|
283
|
-
'utf8',
|
|
284
|
-
'hex'
|
|
285
|
-
)
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const __encrypt = (
|
|
223
|
+
export const symetricEncrypt = (
|
|
289
224
|
data,
|
|
290
|
-
encryptionKey,
|
|
291
|
-
assocData,
|
|
292
|
-
decoding = 'utf8',
|
|
293
|
-
encoding = 'hex'
|
|
225
|
+
{ encryptedKey, encryptionKey, sub, decoding, encoding, iv }
|
|
294
226
|
) => {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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'))
|
|
300
246
|
const encryptedData =
|
|
301
247
|
cipher.update(data, decoding, encoding) + cipher.final(encoding)
|
|
302
248
|
const authTag = cipher.getAuthTag()
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
249
|
+
|
|
250
|
+
const encryptedDataPacket =
|
|
251
|
+
iv.toString(encoding) + authTag.toString(encoding) + encryptedData
|
|
252
|
+
|
|
253
|
+
return encryptedDataPacket
|
|
308
254
|
}
|
|
309
255
|
|
|
310
|
-
export const
|
|
311
|
-
|
|
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)
|
|
312
268
|
for (const key of fields) {
|
|
313
|
-
values[key] &&=
|
|
269
|
+
values[key] &&= symetricDecrypt(values[key], {
|
|
270
|
+
encryptionKey,
|
|
271
|
+
sub
|
|
272
|
+
})
|
|
314
273
|
}
|
|
315
274
|
return values
|
|
316
275
|
}
|
|
317
276
|
|
|
318
|
-
export const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
'hex',
|
|
326
|
-
'utf8'
|
|
327
|
-
)
|
|
328
|
-
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
|
+
})
|
|
329
284
|
}
|
|
330
285
|
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
Buffer.from(options.encryptionSharedKey, 'hex'),
|
|
335
|
-
assocData,
|
|
336
|
-
'hex',
|
|
337
|
-
'hex'
|
|
338
|
-
)
|
|
339
|
-
|
|
340
|
-
const __decrypt = (
|
|
341
|
-
data,
|
|
342
|
-
encryptionKey,
|
|
343
|
-
assocData,
|
|
344
|
-
decoding = 'hex',
|
|
345
|
-
encoding = 'utf8'
|
|
286
|
+
export const symetricDecrypt = (
|
|
287
|
+
encryptedDataPacket,
|
|
288
|
+
{ encryptedKey, encryptionKey, sub, decoding, encoding }
|
|
346
289
|
) => {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
+
)
|
|
350
316
|
|
|
351
317
|
const decipher = createDecipheriv(
|
|
352
|
-
options.
|
|
353
|
-
encryptionKey,
|
|
318
|
+
options.symetricEncryptionMethod,
|
|
319
|
+
Buffer.from(encryptionKey, 'base64'),
|
|
354
320
|
iv,
|
|
355
321
|
{
|
|
356
322
|
authTagLength
|
|
357
323
|
}
|
|
358
324
|
)
|
|
359
|
-
decipher.setAAD(Buffer.from(
|
|
325
|
+
decipher.setAAD(Buffer.from(sub ?? '', 'utf8'))
|
|
326
|
+
|
|
360
327
|
decipher.setAuthTag(authTag)
|
|
361
|
-
|
|
328
|
+
const data =
|
|
362
329
|
decipher.update(encryptedData, decoding, encoding) +
|
|
363
330
|
decipher.final(encoding)
|
|
364
|
-
|
|
331
|
+
return data
|
|
365
332
|
}
|
|
366
333
|
|
|
367
334
|
// *** Signatures *** //
|
|
368
|
-
|
|
335
|
+
// asymmetricKeyPairType
|
|
336
|
+
export const makeAsymmetricKeys = async () => {
|
|
369
337
|
const { publicKey, privateKey } = await generateKeyPair('ec', {
|
|
370
338
|
namedCurve: 'P-384', // P-512
|
|
371
339
|
paramEncoding: 'named',
|
|
@@ -375,44 +343,32 @@ export const makeAsymmetricKeys = async (encryptionKey) => {
|
|
|
375
343
|
},
|
|
376
344
|
privateKeyEncoding: {
|
|
377
345
|
type: 'sec1',
|
|
378
|
-
format: 'pem'
|
|
379
|
-
//
|
|
380
|
-
cipher: options.
|
|
381
|
-
passphrase: encryptionKey
|
|
346
|
+
format: 'pem'
|
|
347
|
+
// Encryption done at another level for consistency
|
|
348
|
+
// cipher: options.asymetricEncryptionMethod,
|
|
349
|
+
// passphrase: encryptionKey,
|
|
382
350
|
}
|
|
383
351
|
})
|
|
384
352
|
return { publicKey, privateKey }
|
|
385
353
|
}
|
|
386
354
|
|
|
387
|
-
export const makeSignature = (data, privateKey, algorithm =
|
|
388
|
-
|
|
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
|
+
)
|
|
389
360
|
}
|
|
390
|
-
export const verifySignature = (
|
|
361
|
+
export const verifySignature = async (
|
|
391
362
|
data,
|
|
392
363
|
publicKey,
|
|
393
364
|
signature,
|
|
394
|
-
algorithm =
|
|
365
|
+
{ algorithm } = {}
|
|
395
366
|
) => {
|
|
396
|
-
|
|
367
|
+
algorithm ??= options.digestAlgorithm
|
|
368
|
+
return await verify(
|
|
397
369
|
algorithm,
|
|
398
370
|
Buffer.from(data),
|
|
399
371
|
publicKey,
|
|
400
|
-
Buffer.from(signature,
|
|
372
|
+
Buffer.from(signature, options.signatureEncoding)
|
|
401
373
|
)
|
|
402
374
|
}
|
|
403
|
-
// const assocData = 'sub'
|
|
404
|
-
// const input = 'data'
|
|
405
|
-
//
|
|
406
|
-
// const {encryptedKey} = makeSymetricKey(assocData)
|
|
407
|
-
// const encryptedData = encrypt(input, encryptedKey, assocData)
|
|
408
|
-
// const output = decrypt(encryptedData, encryptedKey, assocData)
|
|
409
|
-
// console.log(assocData, input, output, encryptedData)
|
|
410
|
-
|
|
411
|
-
// const assocData = 'tlSIcVp1XEF'
|
|
412
|
-
// const encryptionKey =
|
|
413
|
-
// 'b9ebfdab6603dd8e151138ec8298aaf8b49bf6d38480f9864e9e72e65c4d8b59.b9f9bc89d7f3ce4aa1e1af469d10508b.aa1b6a682fcc186e867a9cfd'
|
|
414
|
-
// const value =
|
|
415
|
-
// '76ebdea75ea529c16f1645d5435413934cbb97378293776cb446331e6d8323865a808b7f0822cd4f36ef6cca57354ea7c955c94d232a55038321184f4294971315fda4b8dc2f33422bbd3a5c978edc0f5bf019f818f0c92f7c570bdc49fe2f71df3856655198b6723fa52697b9225b2e79f04ed80cd35ef61a8e1e06c3751d3f9caa40aa9a41528ce5ab1bfae8ecff0feab784497ccbf2c7dca4461c6b9a74d1ded6233dc38ad75d0fe2f57cfda2bd1d59ed1f8c53de911cf8071dc7cefde7870c303137586bc0c5f85fe93505d9056994a4d8e59df2027b564c303aa723e98ae5934b15039f90d21827ac22b824593f334466622582a2857f04d8bf450310a9924060d113d2ece0259da68376b31a3c2e8a0b79e81f8efe96bf2b7a6b5b1841edca2566a0a89ab80ee6d6ac2ed24e788ad318a1238dfa5b9e965d256935d3aeafe52f986a530b2f9225bbe7a73b43e6515ce789701f501546239460bccc46285f3fcff0c54c2b6df820334a974abea81460fd4ac99a13cb88e296a2275f80f4c783757319417078458977461dd5e101f9f9b7d303578379dc15ee1b549e3fcceffffba895ecc8291d1f82e150ce8e10640fd79f2e0666167ef7d529cd28cb76a72bf2b3e239dbb845bedd39f42c938bbe711542231fb835edb547d919b3c695272f4cd43640eb3bed2b86948278e5aeeb67ef932ef08332730e75752fd5bd247e2dd30470fc493362d2ecac72237dfbf0a1694f8e6b8c18ab24c71eb723a3f610a056a73fea90f4f8660bfc6dec46fa52a55a3d72a5c9b97770bff7d52ea91b97cc643d915341642e201918fcb088986e9134e133519c6575048e3f840622e569fc5fa66e4f0c31681c9de4b1b1f6acb179728bc0a9f9950bf2e0c8d49da835298013977a2f97d7052bcbedbc39b875bbf9a0622ede589810175ab2455ea72a8274659af7066a2c97ba3aacce84531d8685d08b15d3619038997ae459ac916a88bedaf619f574236d1b86b02f29edaf78684c91c6ee45199b861359ee374fecb95be8a85dc012991d22564adeb2ec95f160574858fa010c40ce74077a08f0e681eb4b10a0093e885f115a12469a82b07012ad4e20b836d5711358c47969af89d09c9299edc540321fe897312ca49c9e8f93d50dc68394d0cc1d108f38e3534821b979c405c1f526a3b57651897873aeeb0909b176fb2034f9fdb1d43a8003809af29ff0bdc456a593029c4526b270908ac94b620a7cb8b7da20a6421e0f2671c899c20d78e1e0a1c3ab0b92948c90fca3c36d21a5e53143bc7e061773251364f8ce3b8992b6313c9604dc19c018b9efc6ca8f7c1fe039077fd76775461d1fa3968f51be7e340e3f09b40d6b44c77a7cbf7d71cad2ddea1121b31d27470a38cda8f8d38170a0d8f0b02d56f8e97b2134597d6111ea6670545bd4f7b4e4d6d7cf2fb5b787a68ba8ee84775f06db195f47282d9646dfbdbb57701e2ef5a432b621cb54a70e94de6b7a3f95bfe3c50793e08a27f76f832070fb2d23ed19dcd75f798aeff0c3e76d8215d43e1a8b7e643bffa243691adf2abcd52f465390e92e0344e2f452673f12d50e2ead0496ba76504ae0f78c81f2a2b5a64b39d3fb35f39f178b9a1587d1f07ed28e7696bfc8740f429f6cc35c964ae04ef39c1070eab6415be3aab887ff6192ac4d49ba9b169221b47d73c55f6010dda07a77cbcf54b779d380591786ba70c6d7000af57034539487f801aeb5c097939682e1530bc26c0b030bcab327ddaaaac5c9b969301b6d4ed7f601a46e8431e8cae3800c80c42686fee3b7625742798ced5976d163d2c8aad1bdac36c001e4e246fdb7b4c6850ed99554cfc0c07b7c7acc7659fd042989cc291ec2bd03a59e439860a5c48e430780f9d85c9acb3e85e741e73163f29afb7c43f143e7ada99a4670cbbe2210bfce8d36ee86.a5237f2d798c9f98b26a676794d3c3f6.0f15d0f53657150aeb1462d5'
|
|
416
|
-
// const output = decrypt(value, encryptionKey, assocData)
|
|
417
|
-
// console.log(output)
|
|
418
|
-
// *** 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
|
}
|