@bsv/sdk 1.1.6 → 1.1.8
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/dist/cjs/mod.js +1 -0
- package/dist/cjs/mod.js.map +1 -1
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/primitives/Hash.js +38 -1
- package/dist/cjs/src/primitives/Hash.js.map +1 -1
- package/dist/cjs/src/totp/index.js +18 -0
- package/dist/cjs/src/totp/index.js.map +1 -0
- package/dist/cjs/src/totp/totp.js +92 -0
- package/dist/cjs/src/totp/totp.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/mod.js +1 -0
- package/dist/esm/mod.js.map +1 -1
- package/dist/esm/src/primitives/Hash.js +38 -0
- package/dist/esm/src/primitives/Hash.js.map +1 -1
- package/dist/esm/src/totp/index.js +2 -0
- package/dist/esm/src/totp/index.js.map +1 -0
- package/dist/esm/src/totp/totp.js +85 -0
- package/dist/esm/src/totp/totp.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/mod.d.ts +1 -0
- package/dist/types/mod.d.ts.map +1 -1
- package/dist/types/src/primitives/Hash.d.ts +9 -0
- package/dist/types/src/primitives/Hash.d.ts.map +1 -1
- package/dist/types/src/totp/index.d.ts +2 -0
- package/dist/types/src/totp/index.d.ts.map +1 -0
- package/dist/types/src/totp/totp.d.ts +42 -0
- package/dist/types/src/totp/totp.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/primitives.md +26 -9
- package/mod.ts +1 -0
- package/package.json +11 -1
- package/src/primitives/Hash.ts +46 -0
- package/src/totp/__tests/totp.test.ts +80 -0
- package/src/totp/index.ts +1 -0
- package/src/totp/totp.ts +138 -0
package/src/totp/totp.ts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { SHA1HMAC, SHA256HMAC, SHA512HMAC } from '../primitives/Hash.js'
|
|
2
|
+
import BigNumber from '../primitives/BigNumber.js'
|
|
3
|
+
|
|
4
|
+
export type TOTPAlgorithm = 'SHA-1' | 'SHA-256' | 'SHA-512'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options for TOTP generation.
|
|
8
|
+
* @param {number} [digits=6] - The number of digits in the OTP.
|
|
9
|
+
* @param {TOTPAlgorithm} [algorithm="SHA-1"] - Algorithm used for hashing.
|
|
10
|
+
* @param {number} [period=30] - The time period for OTP validity in seconds.
|
|
11
|
+
* @param {number} [timestamp=Date.now()] - The current timestamp.
|
|
12
|
+
*/
|
|
13
|
+
export interface TOTPOptions {
|
|
14
|
+
digits?: number
|
|
15
|
+
algorithm?: TOTPAlgorithm
|
|
16
|
+
period?: number
|
|
17
|
+
timestamp?: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Options for TOTP validation.
|
|
22
|
+
* @param {number} [skew=1] - The number of time periods to check before and after the current time period.
|
|
23
|
+
*/
|
|
24
|
+
export type TOTPValidateOptions = TOTPOptions & {
|
|
25
|
+
skew?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class TOTP {
|
|
29
|
+
/**
|
|
30
|
+
* Generates a Time-based One-Time Password (TOTP).
|
|
31
|
+
* @param {number[]} secret - The secret key for TOTP.
|
|
32
|
+
* @param {TOTPOptions} options - Optional parameters for TOTP.
|
|
33
|
+
* @returns {string} The generated TOTP.
|
|
34
|
+
*/
|
|
35
|
+
static generate (secret: number[], options?: TOTPOptions): string {
|
|
36
|
+
const _options = this.withDefaultOptions(options)
|
|
37
|
+
|
|
38
|
+
const counter = this.getCounter(_options.timestamp, _options.period)
|
|
39
|
+
const otp = generateHOTP(secret, counter, _options)
|
|
40
|
+
return otp
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validates a Time-based One-Time Password (TOTP).
|
|
45
|
+
* @param {number[]} secret - The secret key for TOTP.
|
|
46
|
+
* @param {string} passcode - The passcode to validate.
|
|
47
|
+
* @param {TOTPValidateOptions} options - Optional parameters for TOTP validation.
|
|
48
|
+
* @returns {boolean} A boolean indicating whether the passcode is valid.
|
|
49
|
+
*/
|
|
50
|
+
static validate (
|
|
51
|
+
secret: number[],
|
|
52
|
+
passcode: string,
|
|
53
|
+
options?: TOTPValidateOptions
|
|
54
|
+
): boolean {
|
|
55
|
+
const _options = this.withDefaultValidateOptions(options)
|
|
56
|
+
passcode = passcode.trim()
|
|
57
|
+
if (passcode.length != _options.digits) {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const counter = this.getCounter(_options.timestamp, _options.period)
|
|
62
|
+
|
|
63
|
+
const counters = [counter]
|
|
64
|
+
for (let i = 1; i <= _options.skew; i++) {
|
|
65
|
+
counters.push(counter + i)
|
|
66
|
+
counters.push(counter - i)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const c of counters) {
|
|
70
|
+
if (passcode === generateHOTP(secret, c, _options)) {
|
|
71
|
+
return true
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private static getCounter (timestamp: number, period: number): number {
|
|
79
|
+
const epochSeconds = Math.floor(timestamp / 1000)
|
|
80
|
+
const counter = Math.floor(epochSeconds / period)
|
|
81
|
+
return counter
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private static withDefaultOptions (
|
|
85
|
+
options?: TOTPOptions
|
|
86
|
+
): Required<TOTPOptions> {
|
|
87
|
+
return {
|
|
88
|
+
digits: 2,
|
|
89
|
+
algorithm: 'SHA-1',
|
|
90
|
+
period: 30,
|
|
91
|
+
timestamp: Date.now(),
|
|
92
|
+
...options
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private static withDefaultValidateOptions (
|
|
97
|
+
options?: TOTPValidateOptions
|
|
98
|
+
): Required<TOTPValidateOptions> {
|
|
99
|
+
return { skew: 1, ...this.withDefaultOptions(options) }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function generateHOTP (
|
|
104
|
+
secret: number[],
|
|
105
|
+
counter: number,
|
|
106
|
+
options: Required<TOTPOptions>
|
|
107
|
+
): string {
|
|
108
|
+
const timePad = new BigNumber(counter).toArray('be', 8)
|
|
109
|
+
console.log({ timePad })
|
|
110
|
+
const hmac = calcHMAC(secret, timePad, options.algorithm)
|
|
111
|
+
const signature = hmac.digest()
|
|
112
|
+
|
|
113
|
+
// RFC 4226 https://datatracker.ietf.org/doc/html/rfc4226#section-5.4
|
|
114
|
+
const offset = signature[signature.length - 1] & 0x0f // offset is the last byte in the hmac
|
|
115
|
+
const fourBytesRange = signature.slice(offset, offset + 4) // starting from offset, get 4 bytes
|
|
116
|
+
const mask = 0x7fffffff // 32-bit number with a leading 0 followed by 31 ones [0111 (...) 1111]
|
|
117
|
+
const masked = new BigNumber(fourBytesRange).toNumber() & mask
|
|
118
|
+
|
|
119
|
+
const otp = masked.toString().slice(-options.digits)
|
|
120
|
+
return otp
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function calcHMAC (
|
|
124
|
+
secret: number[],
|
|
125
|
+
timePad: number[],
|
|
126
|
+
algorithm: TOTPAlgorithm
|
|
127
|
+
) {
|
|
128
|
+
switch (algorithm) {
|
|
129
|
+
case 'SHA-1':
|
|
130
|
+
return new SHA1HMAC(secret).update(timePad)
|
|
131
|
+
case 'SHA-256':
|
|
132
|
+
return new SHA256HMAC(secret).update(timePad)
|
|
133
|
+
case 'SHA-512':
|
|
134
|
+
return new SHA512HMAC(secret).update(timePad)
|
|
135
|
+
default:
|
|
136
|
+
throw new Error('unsupported HMAC algorithm')
|
|
137
|
+
}
|
|
138
|
+
}
|