@lowerdeck/sign 1.0.0
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/.turbo/turbo-build.log +13 -0
- package/.turbo/turbo-test.log +73 -0
- package/README.md +59 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.modern.js +2 -0
- package/dist/index.modern.js.map +1 -0
- package/dist/index.module.js +2 -0
- package/dist/index.module.js.map +1 -0
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/sign.d.ts +20 -0
- package/dist/sign.d.ts.map +1 -0
- package/package.json +34 -0
- package/src/index.ts +1 -0
- package/src/sign.test.ts +87 -0
- package/src/sign.ts +90 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
[0m[2m[35m$[0m [2m[1mmicrobundle[0m
|
|
3
|
+
No name was provided for external module '@lowerdeck/base62' in output.globals – guessing 'base62'
|
|
4
|
+
No name was provided for external module '@lowerdeck/id' in output.globals – guessing 'id'
|
|
5
|
+
[34mBuild "@lowerdeck/sign" to dist:[39m
|
|
6
|
+
[32m683 B[39m: [37mindex.cjs[39m.gz
|
|
7
|
+
[32m608 B[39m: [37mindex.cjs[39m.br
|
|
8
|
+
[32m581 B[39m: [37mindex.modern.js[39m.gz
|
|
9
|
+
[32m532 B[39m: [37mindex.modern.js[39m.br
|
|
10
|
+
[32m694 B[39m: [37mindex.module.js[39m.gz
|
|
11
|
+
[32m628 B[39m: [37mindex.module.js[39m.br
|
|
12
|
+
[32m776 B[39m: [37mindex.umd.js[39m.gz
|
|
13
|
+
[32m700 B[39m: [37mindex.umd.js[39m.br
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
|
|
2
|
+
[0m[2m[35m$[0m [2m[1mvitest run --passWithNoTests[0m
|
|
3
|
+
[?25l
|
|
4
|
+
[1m[46m RUN [49m[22m [36mv3.2.4 [39m[90m/Users/tobias/code/metorial/metorial-enterprise/oss/src/packages/backend/sign[39m
|
|
5
|
+
|
|
6
|
+
[?2026h
|
|
7
|
+
[1m[33m ❯ [39m[22msrc/sign.test.ts[2m [queued][22m
|
|
8
|
+
|
|
9
|
+
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
10
|
+
[2m Tests [22m[1m[32m0 passed[39m[22m[90m (0)[39m
|
|
11
|
+
[2m Start at [22m10:23:52
|
|
12
|
+
[2m Duration [22m101ms
|
|
13
|
+
[?2026l[?2026h[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K
|
|
14
|
+
[1m[33m ❯ [39m[22msrc/sign.test.ts[2m 1/4[22m
|
|
15
|
+
|
|
16
|
+
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
17
|
+
[2m Tests [22m[1m[32m1 passed[39m[22m[90m (4)[39m
|
|
18
|
+
[2m Start at [22m10:23:52
|
|
19
|
+
[2m Duration [22m306ms
|
|
20
|
+
[?2026l[?2026h[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K
|
|
21
|
+
[1m[33m ❯ [39m[22msrc/sign.test.ts[2m 1/4[22m
|
|
22
|
+
|
|
23
|
+
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
24
|
+
[2m Tests [22m[1m[32m1 passed[39m[22m[90m (4)[39m
|
|
25
|
+
[2m Start at [22m10:23:52
|
|
26
|
+
[2m Duration [22m910ms
|
|
27
|
+
[?2026l[?2026h[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K
|
|
28
|
+
[1m[33m ❯ [39m[22msrc/sign.test.ts[2m 1/4[22m
|
|
29
|
+
|
|
30
|
+
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
31
|
+
[2m Tests [22m[1m[32m1 passed[39m[22m[90m (4)[39m
|
|
32
|
+
[2m Start at [22m10:23:52
|
|
33
|
+
[2m Duration [22m1.92s
|
|
34
|
+
[?2026l[?2026h[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K
|
|
35
|
+
[1m[33m ❯ [39m[22msrc/sign.test.ts[2m 2/4[22m
|
|
36
|
+
|
|
37
|
+
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
38
|
+
[2m Tests [22m[1m[32m2 passed[39m[22m[90m (4)[39m
|
|
39
|
+
[2m Start at [22m10:23:52
|
|
40
|
+
[2m Duration [22m2.22s
|
|
41
|
+
[?2026l[?2026h[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K
|
|
42
|
+
[1m[33m ❯ [39m[22msrc/sign.test.ts[2m 2/4[22m
|
|
43
|
+
|
|
44
|
+
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
45
|
+
[2m Tests [22m[1m[32m2 passed[39m[22m[90m (4)[39m
|
|
46
|
+
[2m Start at [22m10:23:52
|
|
47
|
+
[2m Duration [22m2.93s
|
|
48
|
+
[?2026l[?2026h[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K
|
|
49
|
+
[1m[33m ❯ [39m[22msrc/sign.test.ts[2m 2/4[22m
|
|
50
|
+
|
|
51
|
+
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
52
|
+
[2m Tests [22m[1m[32m2 passed[39m[22m[90m (4)[39m
|
|
53
|
+
[2m Start at [22m10:23:52
|
|
54
|
+
[2m Duration [22m3.94s
|
|
55
|
+
[?2026l[?2026h[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K
|
|
56
|
+
[1m[33m ❯ [39m[22msrc/sign.test.ts[2m 3/4[22m
|
|
57
|
+
|
|
58
|
+
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
59
|
+
[2m Tests [22m[1m[32m3 passed[39m[22m[90m (4)[39m
|
|
60
|
+
[2m Start at [22m10:23:52
|
|
61
|
+
[2m Duration [22m4.24s
|
|
62
|
+
[?2026l[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K [32m✓[39m src/sign.test.ts [2m([22m[2m4 tests[22m[2m)[22m[33m 4008[2mms[22m[39m
|
|
63
|
+
[32m✓[39m sign[2m > [22msign + verify[32m 3[2mms[22m[39m
|
|
64
|
+
[33m[2m✓[22m[39m sign[2m > [22mexpired [33m 2002[2mms[22m[39m
|
|
65
|
+
[33m[2m✓[22m[39m sign[2m > [22macceptExpired [33m 2003[2mms[22m[39m
|
|
66
|
+
[32m✓[39m sign[2m > [22mstart with prefix[32m 0[2mms[22m[39m
|
|
67
|
+
|
|
68
|
+
[2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m
|
|
69
|
+
[2m Tests [22m [1m[32m4 passed[39m[22m[90m (4)[39m
|
|
70
|
+
[2m Start at [22m 10:23:52
|
|
71
|
+
[2m Duration [22m 4.31s[2m (transform 77ms, setup 0ms, collect 96ms, tests 4.01s, environment 0ms, prepare 53ms)[22m
|
|
72
|
+
|
|
73
|
+
[?25h
|
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# `@lowerdeck/sign`
|
|
2
|
+
|
|
3
|
+
HMAC signing and verification with expiration support. Uses HMAC-SHA512 and base62 encoding for secure, compact signatures.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @lowerdeck/sign
|
|
9
|
+
yarn add @lowerdeck/sign
|
|
10
|
+
bun add @lowerdeck/sign
|
|
11
|
+
pnpm add @lowerdeck/sign
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
### With Expiration
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { signature } from '@lowerdeck/sign';
|
|
20
|
+
|
|
21
|
+
const signer = signature({
|
|
22
|
+
prefix: 'token',
|
|
23
|
+
expirationMs: 3600000, // 1 hour
|
|
24
|
+
key: 'your-secret-key'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Sign data with expiration
|
|
28
|
+
const signed = await signer.sign('user-123');
|
|
29
|
+
console.log(signed); // 'token_xyz123abc...'
|
|
30
|
+
|
|
31
|
+
// Verify signature
|
|
32
|
+
const verified = await signer.verify(signed);
|
|
33
|
+
if (verified) {
|
|
34
|
+
console.log('Valid signature');
|
|
35
|
+
} else {
|
|
36
|
+
console.log('Invalid or expired signature');
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Without Expiration
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { signatureBasic } from '@lowerdeck/sign';
|
|
44
|
+
|
|
45
|
+
// Simple signing without expiration
|
|
46
|
+
const signed = await signatureBasic.sign('data', 'secret-key');
|
|
47
|
+
console.log(signed); // 'data.xyz123abc...'
|
|
48
|
+
|
|
49
|
+
const isValid = await signatureBasic.verify(signed, 'secret-key');
|
|
50
|
+
console.log(isValid); // true
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
This project is licensed under the Apache License 2.0.
|
|
56
|
+
|
|
57
|
+
<div align="center">
|
|
58
|
+
<sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
|
|
59
|
+
</div>
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=require("@lowerdeck/base62"),r=require("@lowerdeck/id"),t=new Map,n=function(e){try{if(t.has(e))return Promise.resolve(t.get(e));var r=(new TextEncoder).encode(e);return Promise.resolve(crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-512"},!0,["sign","verify"])).then(function(r){return t.set(e,r),r})}catch(e){return Promise.reject(e)}},i=function(e){return parseInt(e,36)},o=function(e){return(new TextEncoder).encode(e)},s={sign:function(r,t){try{var i=crypto.subtle,s=i.sign;return Promise.resolve(n(t)).then(function(t){return Promise.resolve(s.call(i,"HMAC",t,o(r))).then(function(r){return e.base62.encode(new Uint8Array(r))})})}catch(e){return Promise.reject(e)}},verify:function(r,t,i){try{var s=e.base62.decodeRaw(t),c=crypto.subtle,u=c.verify;return Promise.resolve(n(i)).then(function(e){return u.call(c,"HMAC",e,s,o(r))})}catch(e){return Promise.reject(e)}}};exports.signature=function(t){return{sign:function(i){var s=i.data,c=i.expirationMs,u=void 0===c?t.expirationMs:c;try{return Promise.resolve(n(t.key)).then(function(n){var i=Date.now()+u,c=r.generatePlainId(10),a=JSON.stringify([c,s,i]);return Promise.resolve(crypto.subtle.sign("HMAC",n,o(a))).then(function(r){var n=e.base62.encode(new Uint8Array(r));return""+t.prefix+n+"_"+function(e){return e.toString(36)}(i)+"_"+c})})}catch(e){return Promise.reject(e)}},verify:function(r){var s=r.data,c=r.signature,u=r.acceptExpired,a=void 0!==u&&u;try{return Promise.resolve(n(t.key)).then(function(r){var n=c.slice(t.prefix.length).split("_"),u=n[1],f=n[2],v=e.base62.decodeRaw(n[0]),l=JSON.stringify([f,s,i(u)]);return Promise.resolve(crypto.subtle.verify("HMAC",r,v,o(l))).then(function(e){return!!e&&(!!a||Date.now()<i(u))})})}catch(e){return Promise.reject(e)}}}},exports.signatureBasic=s;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/sign.ts"],"sourcesContent":["import { base62 } from '@lowerdeck/base62';\nimport { generatePlainId } from '@lowerdeck/id';\n\nlet importedKeys = new Map<string, CryptoKey>();\nlet importKey = async (keyStr: string) => {\n if (importedKeys.has(keyStr)) return importedKeys.get(keyStr)!;\n\n let encoder = new TextEncoder();\n let secretKeyData = encoder.encode(keyStr);\n let ck = await crypto.subtle.importKey(\n 'raw',\n secretKeyData,\n { name: 'HMAC', hash: 'SHA-512' },\n true,\n ['sign', 'verify']\n );\n\n importedKeys.set(keyStr, ck);\n\n return ck;\n};\n\nlet exp = {\n stringify: (expiry: number) => expiry.toString(36),\n parse: (expiry: string) => parseInt(expiry, 36)\n};\n\nlet encode = (data: string) => new TextEncoder().encode(data);\n\nexport let signature = (opts: { prefix: string; expirationMs: number; key: string }) => ({\n sign: async ({\n data,\n expirationMs = opts.expirationMs\n }: {\n data: string;\n\n expirationMs?: number;\n }) => {\n let key = await importKey(opts.key);\n let expiry = Date.now() + expirationMs;\n\n let id = generatePlainId(10);\n\n let dataToAuthenticate = JSON.stringify([id, data, expiry]);\n let signature = await crypto.subtle.sign('HMAC', key, encode(dataToAuthenticate));\n\n let hmac = base62.encode(new Uint8Array(signature));\n\n return `${opts.prefix}${hmac}_${exp.stringify(expiry)}_${id}`;\n },\n\n verify: async ({\n data,\n signature,\n acceptExpired = false\n }: {\n data: string;\n signature: string;\n acceptExpired?: boolean;\n }) => {\n let key = await importKey(opts.key);\n\n let sigStr = signature.slice(opts.prefix.length);\n let [encodedHmac, expiry, id] = sigStr.split('_');\n\n let hmac = base62.decodeRaw(encodedHmac);\n\n let dataToAuthenticate = JSON.stringify([id, data, exp.parse(expiry)]);\n\n let isValid = await crypto.subtle.verify('HMAC', key, hmac, encode(dataToAuthenticate));\n\n if (!isValid) return false;\n if (acceptExpired) return true;\n\n return Date.now() < exp.parse(expiry);\n }\n});\n\nexport let signatureBasic = {\n sign: async (data: string, key: string) => {\n let signature = await crypto.subtle.sign('HMAC', await importKey(key), encode(data));\n let hmac = base62.encode(new Uint8Array(signature));\n return hmac;\n },\n\n verify: async (data: string, signature: string, key: string) => {\n let hmac = base62.decodeRaw(signature);\n return crypto.subtle.verify('HMAC', await importKey(key), hmac, encode(data));\n }\n};\n"],"names":["importedKeys","Map","importKey","keyStr","has","Promise","resolve","get","secretKeyData","TextEncoder","encode","crypto","subtle","name","hash","then","ck","set","e","reject","exp","expiry","parseInt","data","signatureBasic","sign","key","_crypto$subtle","_sign","_importKey","call","signature","base62","Uint8Array","verify","hmac","decodeRaw","_crypto$subtle2","_verify","_importKey2","opts","_ref","_ref$expirationMs","expirationMs","Date","now","id","generatePlainId","dataToAuthenticate","JSON","stringify","prefix","toString","_ref2","_ref2$acceptExpired","acceptExpired","_sigStr$split","slice","length","split","isValid"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{base62 as e}from"@lowerdeck/base62";import{generatePlainId as t}from"@lowerdeck/id";let r=new Map,a=async e=>{if(r.has(e))return r.get(e);let t=(new TextEncoder).encode(e),a=await crypto.subtle.importKey("raw",t,{name:"HMAC",hash:"SHA-512"},!0,["sign","verify"]);return r.set(e,a),a},i=e=>parseInt(e,36),n=e=>(new TextEncoder).encode(e),s=r=>({sign:async({data:i,expirationMs:s=r.expirationMs})=>{let o=await a(r.key),c=Date.now()+s,y=t(10),w=JSON.stringify([y,i,c]),p=await crypto.subtle.sign("HMAC",o,n(w)),d=e.encode(new Uint8Array(p));return`${r.prefix}${d}_${(e=>e.toString(36))(c)}_${y}`},verify:async({data:t,signature:s,acceptExpired:o=!1})=>{let c=await a(r.key),y=s.slice(r.prefix.length),[w,p,d]=y.split("_"),l=e.decodeRaw(w),f=JSON.stringify([d,t,i(p)]);return!!await crypto.subtle.verify("HMAC",c,l,n(f))&&(!!o||Date.now()<i(p))}}),o={sign:async(t,r)=>{let i=await crypto.subtle.sign("HMAC",await a(r),n(t));return e.encode(new Uint8Array(i))},verify:async(t,r,i)=>{let s=e.decodeRaw(r);return crypto.subtle.verify("HMAC",await a(i),s,n(t))}};export{s as signature,o as signatureBasic};
|
|
2
|
+
//# sourceMappingURL=index.modern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.modern.js","sources":["../src/sign.ts"],"sourcesContent":["import { base62 } from '@lowerdeck/base62';\nimport { generatePlainId } from '@lowerdeck/id';\n\nlet importedKeys = new Map<string, CryptoKey>();\nlet importKey = async (keyStr: string) => {\n if (importedKeys.has(keyStr)) return importedKeys.get(keyStr)!;\n\n let encoder = new TextEncoder();\n let secretKeyData = encoder.encode(keyStr);\n let ck = await crypto.subtle.importKey(\n 'raw',\n secretKeyData,\n { name: 'HMAC', hash: 'SHA-512' },\n true,\n ['sign', 'verify']\n );\n\n importedKeys.set(keyStr, ck);\n\n return ck;\n};\n\nlet exp = {\n stringify: (expiry: number) => expiry.toString(36),\n parse: (expiry: string) => parseInt(expiry, 36)\n};\n\nlet encode = (data: string) => new TextEncoder().encode(data);\n\nexport let signature = (opts: { prefix: string; expirationMs: number; key: string }) => ({\n sign: async ({\n data,\n expirationMs = opts.expirationMs\n }: {\n data: string;\n\n expirationMs?: number;\n }) => {\n let key = await importKey(opts.key);\n let expiry = Date.now() + expirationMs;\n\n let id = generatePlainId(10);\n\n let dataToAuthenticate = JSON.stringify([id, data, expiry]);\n let signature = await crypto.subtle.sign('HMAC', key, encode(dataToAuthenticate));\n\n let hmac = base62.encode(new Uint8Array(signature));\n\n return `${opts.prefix}${hmac}_${exp.stringify(expiry)}_${id}`;\n },\n\n verify: async ({\n data,\n signature,\n acceptExpired = false\n }: {\n data: string;\n signature: string;\n acceptExpired?: boolean;\n }) => {\n let key = await importKey(opts.key);\n\n let sigStr = signature.slice(opts.prefix.length);\n let [encodedHmac, expiry, id] = sigStr.split('_');\n\n let hmac = base62.decodeRaw(encodedHmac);\n\n let dataToAuthenticate = JSON.stringify([id, data, exp.parse(expiry)]);\n\n let isValid = await crypto.subtle.verify('HMAC', key, hmac, encode(dataToAuthenticate));\n\n if (!isValid) return false;\n if (acceptExpired) return true;\n\n return Date.now() < exp.parse(expiry);\n }\n});\n\nexport let signatureBasic = {\n sign: async (data: string, key: string) => {\n let signature = await crypto.subtle.sign('HMAC', await importKey(key), encode(data));\n let hmac = base62.encode(new Uint8Array(signature));\n return hmac;\n },\n\n verify: async (data: string, signature: string, key: string) => {\n let hmac = base62.decodeRaw(signature);\n return crypto.subtle.verify('HMAC', await importKey(key), hmac, encode(data));\n }\n};\n"],"names":["importedKeys","Map","importKey","async","has","keyStr","get","secretKeyData","TextEncoder","encode","ck","crypto","subtle","name","hash","set","exp","expiry","parseInt","data","signature","opts","sign","expirationMs","key","Date","now","id","generatePlainId","dataToAuthenticate","JSON","stringify","hmac","base62","Uint8Array","prefix","toString","verify","acceptExpired","sigStr","slice","length","encodedHmac","split","decodeRaw","signatureBasic"],"mappings":"2FAGA,IAAIA,EAAe,IAAIC,IACnBC,EAAYC,UACd,GAAIH,EAAaI,IAAIC,GAAS,OAAOL,EAAaM,IAAID,GAEtD,IACIE,GADU,IAAIC,aACUC,OAAOJ,GAC/BK,QAAWC,OAAOC,OAAOV,UAC3B,MACAK,EACA,CAAEM,KAAM,OAAQC,KAAM,YACtB,EACA,CAAC,OAAQ,WAKX,OAFAd,EAAae,IAAIV,EAAQK,GAElBA,GAGLM,EAEMC,GAAmBC,SAASD,EAAQ,IAG1CR,EAAUU,IAAiB,IAAIX,aAAcC,OAAOU,GAE7CC,EAAaC,IAAiE,CACvFC,KAAMnB,OACJgB,OACAI,aAAAA,EAAeF,EAAKE,iBAMpB,IAAIC,QAAYtB,EAAUmB,EAAKG,KAC3BP,EAASQ,KAAKC,MAAQH,EAEtBI,EAAKC,EAAgB,IAErBC,EAAqBC,KAAKC,UAAU,CAACJ,EAAIR,EAAMF,IAC/CG,QAAkBT,OAAOC,OAAOU,KAAK,OAAQE,EAAKf,EAAOoB,IAEzDG,EAAOC,EAAOxB,OAAO,IAAIyB,WAAWd,IAExC,MAAO,GAAGC,EAAKc,SAASH,KAzBdf,IAAmBA,EAAOmB,SAAS,IAyBbpB,CAAcC,MAAWU,KAG3DU,OAAQlC,OACNgB,OACAC,YACAkB,cAAAA,GAAgB,MAMhB,IAAId,QAAYtB,EAAUmB,EAAKG,KAE3Be,EAASnB,EAAUoB,MAAMnB,EAAKc,OAAOM,SACpCC,EAAazB,EAAQU,GAAMY,EAAOI,MAAM,KAEzCX,EAAOC,EAAOW,UAAUF,GAExBb,EAAqBC,KAAKC,UAAU,CAACJ,EAAIR,EAAMH,EAAUC,KAI7D,cAFoBN,OAAOC,OAAOyB,OAAO,OAAQb,EAAKQ,EAAMvB,EAAOoB,QAG/DS,GAEGb,KAAKC,MAAQV,EAAUC,OAIvB4B,EAAiB,CAC1BvB,KAAMnB,MAAOgB,EAAcK,KACzB,IAAIJ,QAAkBT,OAAOC,OAAOU,KAAK,aAAcpB,EAAUsB,GAAMf,EAAOU,IAE9E,OADWc,EAAOxB,OAAO,IAAIyB,WAAWd,KAI1CiB,OAAQlC,MAAOgB,EAAcC,EAAmBI,KAC9C,IAAIQ,EAAOC,EAAOW,UAAUxB,GAC5B,OAAOT,OAAOC,OAAOyB,OAAO,aAAcnC,EAAUsB,GAAMQ,EAAMvB,EAAOU"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{base62 as e}from"@lowerdeck/base62";import{generatePlainId as r}from"@lowerdeck/id";var t=new Map,n=function(e){try{if(t.has(e))return Promise.resolve(t.get(e));var r=(new TextEncoder).encode(e);return Promise.resolve(crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-512"},!0,["sign","verify"])).then(function(r){return t.set(e,r),r})}catch(e){return Promise.reject(e)}},o=function(e){return parseInt(e,36)},i=function(e){return(new TextEncoder).encode(e)},c=function(t){return{sign:function(o){var c=o.data,u=o.expirationMs,s=void 0===u?t.expirationMs:u;try{return Promise.resolve(n(t.key)).then(function(n){var o=Date.now()+s,u=r(10),a=JSON.stringify([u,c,o]);return Promise.resolve(crypto.subtle.sign("HMAC",n,i(a))).then(function(r){var n=e.encode(new Uint8Array(r));return""+t.prefix+n+"_"+function(e){return e.toString(36)}(o)+"_"+u})})}catch(e){return Promise.reject(e)}},verify:function(r){var c=r.data,u=r.signature,s=r.acceptExpired,a=void 0!==s&&s;try{return Promise.resolve(n(t.key)).then(function(r){var n=u.slice(t.prefix.length).split("_"),s=n[1],f=n[2],v=e.decodeRaw(n[0]),l=JSON.stringify([f,c,o(s)]);return Promise.resolve(crypto.subtle.verify("HMAC",r,v,i(l))).then(function(e){return!!e&&(!!a||Date.now()<o(s))})})}catch(e){return Promise.reject(e)}}}},u={sign:function(r,t){try{var o=crypto.subtle,c=o.sign;return Promise.resolve(n(t)).then(function(t){return Promise.resolve(c.call(o,"HMAC",t,i(r))).then(function(r){return e.encode(new Uint8Array(r))})})}catch(e){return Promise.reject(e)}},verify:function(r,t,o){try{var c=e.decodeRaw(t),u=crypto.subtle,s=u.verify;return Promise.resolve(n(o)).then(function(e){return s.call(u,"HMAC",e,c,i(r))})}catch(e){return Promise.reject(e)}}};export{c as signature,u as signatureBasic};
|
|
2
|
+
//# sourceMappingURL=index.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.module.js","sources":["../src/sign.ts"],"sourcesContent":["import { base62 } from '@lowerdeck/base62';\nimport { generatePlainId } from '@lowerdeck/id';\n\nlet importedKeys = new Map<string, CryptoKey>();\nlet importKey = async (keyStr: string) => {\n if (importedKeys.has(keyStr)) return importedKeys.get(keyStr)!;\n\n let encoder = new TextEncoder();\n let secretKeyData = encoder.encode(keyStr);\n let ck = await crypto.subtle.importKey(\n 'raw',\n secretKeyData,\n { name: 'HMAC', hash: 'SHA-512' },\n true,\n ['sign', 'verify']\n );\n\n importedKeys.set(keyStr, ck);\n\n return ck;\n};\n\nlet exp = {\n stringify: (expiry: number) => expiry.toString(36),\n parse: (expiry: string) => parseInt(expiry, 36)\n};\n\nlet encode = (data: string) => new TextEncoder().encode(data);\n\nexport let signature = (opts: { prefix: string; expirationMs: number; key: string }) => ({\n sign: async ({\n data,\n expirationMs = opts.expirationMs\n }: {\n data: string;\n\n expirationMs?: number;\n }) => {\n let key = await importKey(opts.key);\n let expiry = Date.now() + expirationMs;\n\n let id = generatePlainId(10);\n\n let dataToAuthenticate = JSON.stringify([id, data, expiry]);\n let signature = await crypto.subtle.sign('HMAC', key, encode(dataToAuthenticate));\n\n let hmac = base62.encode(new Uint8Array(signature));\n\n return `${opts.prefix}${hmac}_${exp.stringify(expiry)}_${id}`;\n },\n\n verify: async ({\n data,\n signature,\n acceptExpired = false\n }: {\n data: string;\n signature: string;\n acceptExpired?: boolean;\n }) => {\n let key = await importKey(opts.key);\n\n let sigStr = signature.slice(opts.prefix.length);\n let [encodedHmac, expiry, id] = sigStr.split('_');\n\n let hmac = base62.decodeRaw(encodedHmac);\n\n let dataToAuthenticate = JSON.stringify([id, data, exp.parse(expiry)]);\n\n let isValid = await crypto.subtle.verify('HMAC', key, hmac, encode(dataToAuthenticate));\n\n if (!isValid) return false;\n if (acceptExpired) return true;\n\n return Date.now() < exp.parse(expiry);\n }\n});\n\nexport let signatureBasic = {\n sign: async (data: string, key: string) => {\n let signature = await crypto.subtle.sign('HMAC', await importKey(key), encode(data));\n let hmac = base62.encode(new Uint8Array(signature));\n return hmac;\n },\n\n verify: async (data: string, signature: string, key: string) => {\n let hmac = base62.decodeRaw(signature);\n return crypto.subtle.verify('HMAC', await importKey(key), hmac, encode(data));\n }\n};\n"],"names":["importedKeys","Map","importKey","keyStr","has","Promise","resolve","get","secretKeyData","TextEncoder","encode","crypto","subtle","name","hash","then","ck","set","e","reject","exp","expiry","parseInt","data","signature","opts","sign","_ref","_ref$expirationMs","expirationMs","key","Date","now","id","generatePlainId","dataToAuthenticate","JSON","stringify","hmac","base62","Uint8Array","prefix","toString","verify","_ref2","_ref2$acceptExpired","acceptExpired","_sigStr$split","slice","length","split","decodeRaw","isValid","signatureBasic","_crypto$subtle","_sign","_importKey","call","_crypto$subtle2","_verify","_importKey2"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@lowerdeck/base62"),require("@lowerdeck/id")):"function"==typeof define&&define.amd?define(["exports","@lowerdeck/base62","@lowerdeck/id"],r):r((e||self).sign={},e.base62,e.id)}(this,function(e,r,n){var t=new Map,i=function(e){try{if(t.has(e))return Promise.resolve(t.get(e));var r=(new TextEncoder).encode(e);return Promise.resolve(crypto.subtle.importKey("raw",r,{name:"HMAC",hash:"SHA-512"},!0,["sign","verify"])).then(function(r){return t.set(e,r),r})}catch(e){return Promise.reject(e)}},o=function(e){return parseInt(e,36)},s=function(e){return(new TextEncoder).encode(e)},c={sign:function(e,n){try{var t=crypto.subtle,o=t.sign;return Promise.resolve(i(n)).then(function(n){return Promise.resolve(o.call(t,"HMAC",n,s(e))).then(function(e){return r.base62.encode(new Uint8Array(e))})})}catch(e){return Promise.reject(e)}},verify:function(e,n,t){try{var o=r.base62.decodeRaw(n),c=crypto.subtle,u=c.verify;return Promise.resolve(i(t)).then(function(r){return u.call(c,"HMAC",r,o,s(e))})}catch(e){return Promise.reject(e)}}};e.signature=function(e){return{sign:function(t){var o=t.data,c=t.expirationMs,u=void 0===c?e.expirationMs:c;try{return Promise.resolve(i(e.key)).then(function(t){var i=Date.now()+u,c=n.generatePlainId(10),a=JSON.stringify([c,o,i]);return Promise.resolve(crypto.subtle.sign("HMAC",t,s(a))).then(function(n){var t=r.base62.encode(new Uint8Array(n));return""+e.prefix+t+"_"+function(e){return e.toString(36)}(i)+"_"+c})})}catch(e){return Promise.reject(e)}},verify:function(n){var t=n.data,c=n.signature,u=n.acceptExpired,a=void 0!==u&&u;try{return Promise.resolve(i(e.key)).then(function(n){var i=c.slice(e.prefix.length).split("_"),u=i[1],f=i[2],d=r.base62.decodeRaw(i[0]),l=JSON.stringify([f,t,o(u)]);return Promise.resolve(crypto.subtle.verify("HMAC",n,d,s(l))).then(function(e){return!!e&&(!!a||Date.now()<o(u))})})}catch(e){return Promise.reject(e)}}}},e.signatureBasic=c});
|
|
2
|
+
//# sourceMappingURL=index.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/sign.ts"],"sourcesContent":["import { base62 } from '@lowerdeck/base62';\nimport { generatePlainId } from '@lowerdeck/id';\n\nlet importedKeys = new Map<string, CryptoKey>();\nlet importKey = async (keyStr: string) => {\n if (importedKeys.has(keyStr)) return importedKeys.get(keyStr)!;\n\n let encoder = new TextEncoder();\n let secretKeyData = encoder.encode(keyStr);\n let ck = await crypto.subtle.importKey(\n 'raw',\n secretKeyData,\n { name: 'HMAC', hash: 'SHA-512' },\n true,\n ['sign', 'verify']\n );\n\n importedKeys.set(keyStr, ck);\n\n return ck;\n};\n\nlet exp = {\n stringify: (expiry: number) => expiry.toString(36),\n parse: (expiry: string) => parseInt(expiry, 36)\n};\n\nlet encode = (data: string) => new TextEncoder().encode(data);\n\nexport let signature = (opts: { prefix: string; expirationMs: number; key: string }) => ({\n sign: async ({\n data,\n expirationMs = opts.expirationMs\n }: {\n data: string;\n\n expirationMs?: number;\n }) => {\n let key = await importKey(opts.key);\n let expiry = Date.now() + expirationMs;\n\n let id = generatePlainId(10);\n\n let dataToAuthenticate = JSON.stringify([id, data, expiry]);\n let signature = await crypto.subtle.sign('HMAC', key, encode(dataToAuthenticate));\n\n let hmac = base62.encode(new Uint8Array(signature));\n\n return `${opts.prefix}${hmac}_${exp.stringify(expiry)}_${id}`;\n },\n\n verify: async ({\n data,\n signature,\n acceptExpired = false\n }: {\n data: string;\n signature: string;\n acceptExpired?: boolean;\n }) => {\n let key = await importKey(opts.key);\n\n let sigStr = signature.slice(opts.prefix.length);\n let [encodedHmac, expiry, id] = sigStr.split('_');\n\n let hmac = base62.decodeRaw(encodedHmac);\n\n let dataToAuthenticate = JSON.stringify([id, data, exp.parse(expiry)]);\n\n let isValid = await crypto.subtle.verify('HMAC', key, hmac, encode(dataToAuthenticate));\n\n if (!isValid) return false;\n if (acceptExpired) return true;\n\n return Date.now() < exp.parse(expiry);\n }\n});\n\nexport let signatureBasic = {\n sign: async (data: string, key: string) => {\n let signature = await crypto.subtle.sign('HMAC', await importKey(key), encode(data));\n let hmac = base62.encode(new Uint8Array(signature));\n return hmac;\n },\n\n verify: async (data: string, signature: string, key: string) => {\n let hmac = base62.decodeRaw(signature);\n return crypto.subtle.verify('HMAC', await importKey(key), hmac, encode(data));\n }\n};\n"],"names":["importedKeys","Map","importKey","keyStr","has","Promise","resolve","get","secretKeyData","TextEncoder","encode","crypto","subtle","name","hash","then","ck","set","e","reject","exp","expiry","parseInt","data","signatureBasic","sign","key","_crypto$subtle","_sign","_importKey","call","signature","base62","Uint8Array","verify","hmac","decodeRaw","_crypto$subtle2","_verify","_importKey2","opts","_ref","_ref$expirationMs","expirationMs","Date","now","id","generatePlainId","dataToAuthenticate","JSON","stringify","prefix","toString","_ref2","_ref2$acceptExpired","acceptExpired","_sigStr$split","slice","length","split","isValid"],"mappings":"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"}
|
package/dist/sign.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare let signature: (opts: {
|
|
2
|
+
prefix: string;
|
|
3
|
+
expirationMs: number;
|
|
4
|
+
key: string;
|
|
5
|
+
}) => {
|
|
6
|
+
sign: ({ data, expirationMs }: {
|
|
7
|
+
data: string;
|
|
8
|
+
expirationMs?: number;
|
|
9
|
+
}) => Promise<string>;
|
|
10
|
+
verify: ({ data, signature, acceptExpired }: {
|
|
11
|
+
data: string;
|
|
12
|
+
signature: string;
|
|
13
|
+
acceptExpired?: boolean;
|
|
14
|
+
}) => Promise<boolean>;
|
|
15
|
+
};
|
|
16
|
+
export declare let signatureBasic: {
|
|
17
|
+
sign: (data: string, key: string) => Promise<string>;
|
|
18
|
+
verify: (data: string, signature: string, key: string) => Promise<boolean>;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=sign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../src/sign.ts"],"names":[],"mappings":"AA6BA,eAAO,IAAI,SAAS,GAAI,MAAM;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;mCAI9E;QACD,IAAI,EAAE,MAAM,CAAC;QAEb,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB;iDAkBE;QACD,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB;CAiBD,CAAC;AAEH,eAAO,IAAI,cAAc;iBACJ,MAAM,OAAO,MAAM;mBAMjB,MAAM,aAAa,MAAM,OAAO,MAAM;CAI5D,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lowerdeck/sign",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"author": "Tobias Herber",
|
|
8
|
+
"license": "Apache 2",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"source": "src/index.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
"require": "./dist/index.cjs",
|
|
13
|
+
"default": "./dist/index.modern.js"
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.cjs",
|
|
16
|
+
"module": "./dist/index.module.js",
|
|
17
|
+
"types": "dist/index.d.ts",
|
|
18
|
+
"unpkg": "./dist/index.umd.js",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "vitest run --passWithNoTests",
|
|
21
|
+
"lint": "prettier src/**/*.ts --check",
|
|
22
|
+
"build": "microbundle"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@lowerdeck/base62": "^1.0.0",
|
|
26
|
+
"@lowerdeck/id": "^1.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"microbundle": "^0.15.1",
|
|
30
|
+
"@lowerdeck/tsconfig": "^1.0.0",
|
|
31
|
+
"typescript": "5.8.2",
|
|
32
|
+
"vitest": "^3.1.2"
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './sign';
|
package/src/sign.test.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { signature } from './sign';
|
|
3
|
+
|
|
4
|
+
describe('sign', () => {
|
|
5
|
+
test('sign + verify', async () => {
|
|
6
|
+
let key = 'Luw6icRYtGAiWuWp3Qen';
|
|
7
|
+
let data = 'test';
|
|
8
|
+
|
|
9
|
+
let sign = await signature({ prefix: 'abc_', expirationMs: 1000, key }).sign({
|
|
10
|
+
data
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
expect(
|
|
14
|
+
await signature({ prefix: 'abc_', expirationMs: 1000, key }).verify({
|
|
15
|
+
data,
|
|
16
|
+
signature: sign
|
|
17
|
+
})
|
|
18
|
+
).toBeTruthy();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('expired', async () => {
|
|
22
|
+
let key = 'Luw6icRYtGAiWuWp3Qen';
|
|
23
|
+
let data = 'test';
|
|
24
|
+
|
|
25
|
+
let sign = await signature({ prefix: 'abc_', expirationMs: 1000, key }).sign({
|
|
26
|
+
data
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(
|
|
30
|
+
await signature({ prefix: 'abc_', expirationMs: 1000, key }).verify({
|
|
31
|
+
data,
|
|
32
|
+
signature: sign
|
|
33
|
+
})
|
|
34
|
+
).toBeTruthy();
|
|
35
|
+
|
|
36
|
+
await new Promise(resolve => setTimeout(resolve, 1000 * 2));
|
|
37
|
+
|
|
38
|
+
expect(
|
|
39
|
+
await signature({ prefix: 'abc_', expirationMs: 1000, key }).verify({
|
|
40
|
+
data,
|
|
41
|
+
signature: sign
|
|
42
|
+
})
|
|
43
|
+
).toBeFalsy();
|
|
44
|
+
}, 10_000);
|
|
45
|
+
|
|
46
|
+
test('acceptExpired', async () => {
|
|
47
|
+
let key = 'Luw6icRYtGAiWuWp3Qen';
|
|
48
|
+
let data = 'test';
|
|
49
|
+
|
|
50
|
+
let sign = await signature({ prefix: 'abc_', expirationMs: 1000, key }).sign({
|
|
51
|
+
data
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(
|
|
55
|
+
await signature({ prefix: 'abc_', expirationMs: 1000, key }).verify({
|
|
56
|
+
data,
|
|
57
|
+
signature: sign
|
|
58
|
+
})
|
|
59
|
+
).toBeTruthy();
|
|
60
|
+
|
|
61
|
+
await new Promise(resolve => setTimeout(resolve, 1000 * 2));
|
|
62
|
+
|
|
63
|
+
expect(
|
|
64
|
+
await signature({ prefix: 'abc_', expirationMs: 1000, key }).verify({
|
|
65
|
+
data,
|
|
66
|
+
signature: sign,
|
|
67
|
+
acceptExpired: true
|
|
68
|
+
})
|
|
69
|
+
).toBeTruthy();
|
|
70
|
+
expect(
|
|
71
|
+
await signature({ prefix: 'abc_', expirationMs: 1000, key }).verify({
|
|
72
|
+
data,
|
|
73
|
+
signature: sign
|
|
74
|
+
})
|
|
75
|
+
).toBeFalsy();
|
|
76
|
+
}, 10_000);
|
|
77
|
+
|
|
78
|
+
test('start with prefix', async () => {
|
|
79
|
+
let key = 'Luw6icRYtGAiWuWp3Qen';
|
|
80
|
+
let data = 'test';
|
|
81
|
+
|
|
82
|
+
let sign = await signature({ prefix: 'abc_', expirationMs: 1000, key }).sign({
|
|
83
|
+
data
|
|
84
|
+
});
|
|
85
|
+
expect(sign.startsWith('abc_')).toBeTruthy();
|
|
86
|
+
});
|
|
87
|
+
});
|
package/src/sign.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { base62 } from '@lowerdeck/base62';
|
|
2
|
+
import { generatePlainId } from '@lowerdeck/id';
|
|
3
|
+
|
|
4
|
+
let importedKeys = new Map<string, CryptoKey>();
|
|
5
|
+
let importKey = async (keyStr: string) => {
|
|
6
|
+
if (importedKeys.has(keyStr)) return importedKeys.get(keyStr)!;
|
|
7
|
+
|
|
8
|
+
let encoder = new TextEncoder();
|
|
9
|
+
let secretKeyData = encoder.encode(keyStr);
|
|
10
|
+
let ck = await crypto.subtle.importKey(
|
|
11
|
+
'raw',
|
|
12
|
+
secretKeyData,
|
|
13
|
+
{ name: 'HMAC', hash: 'SHA-512' },
|
|
14
|
+
true,
|
|
15
|
+
['sign', 'verify']
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
importedKeys.set(keyStr, ck);
|
|
19
|
+
|
|
20
|
+
return ck;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
let exp = {
|
|
24
|
+
stringify: (expiry: number) => expiry.toString(36),
|
|
25
|
+
parse: (expiry: string) => parseInt(expiry, 36)
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
let encode = (data: string) => new TextEncoder().encode(data);
|
|
29
|
+
|
|
30
|
+
export let signature = (opts: { prefix: string; expirationMs: number; key: string }) => ({
|
|
31
|
+
sign: async ({
|
|
32
|
+
data,
|
|
33
|
+
expirationMs = opts.expirationMs
|
|
34
|
+
}: {
|
|
35
|
+
data: string;
|
|
36
|
+
|
|
37
|
+
expirationMs?: number;
|
|
38
|
+
}) => {
|
|
39
|
+
let key = await importKey(opts.key);
|
|
40
|
+
let expiry = Date.now() + expirationMs;
|
|
41
|
+
|
|
42
|
+
let id = generatePlainId(10);
|
|
43
|
+
|
|
44
|
+
let dataToAuthenticate = JSON.stringify([id, data, expiry]);
|
|
45
|
+
let signature = await crypto.subtle.sign('HMAC', key, encode(dataToAuthenticate));
|
|
46
|
+
|
|
47
|
+
let hmac = base62.encode(new Uint8Array(signature));
|
|
48
|
+
|
|
49
|
+
return `${opts.prefix}${hmac}_${exp.stringify(expiry)}_${id}`;
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
verify: async ({
|
|
53
|
+
data,
|
|
54
|
+
signature,
|
|
55
|
+
acceptExpired = false
|
|
56
|
+
}: {
|
|
57
|
+
data: string;
|
|
58
|
+
signature: string;
|
|
59
|
+
acceptExpired?: boolean;
|
|
60
|
+
}) => {
|
|
61
|
+
let key = await importKey(opts.key);
|
|
62
|
+
|
|
63
|
+
let sigStr = signature.slice(opts.prefix.length);
|
|
64
|
+
let [encodedHmac, expiry, id] = sigStr.split('_');
|
|
65
|
+
|
|
66
|
+
let hmac = base62.decodeRaw(encodedHmac);
|
|
67
|
+
|
|
68
|
+
let dataToAuthenticate = JSON.stringify([id, data, exp.parse(expiry)]);
|
|
69
|
+
|
|
70
|
+
let isValid = await crypto.subtle.verify('HMAC', key, hmac, encode(dataToAuthenticate));
|
|
71
|
+
|
|
72
|
+
if (!isValid) return false;
|
|
73
|
+
if (acceptExpired) return true;
|
|
74
|
+
|
|
75
|
+
return Date.now() < exp.parse(expiry);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export let signatureBasic = {
|
|
80
|
+
sign: async (data: string, key: string) => {
|
|
81
|
+
let signature = await crypto.subtle.sign('HMAC', await importKey(key), encode(data));
|
|
82
|
+
let hmac = base62.encode(new Uint8Array(signature));
|
|
83
|
+
return hmac;
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
verify: async (data: string, signature: string, key: string) => {
|
|
87
|
+
let hmac = base62.decodeRaw(signature);
|
|
88
|
+
return crypto.subtle.verify('HMAC', await importKey(key), hmac, encode(data));
|
|
89
|
+
}
|
|
90
|
+
};
|