hfs 0.51.1 → 0.52.0-alpha2
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/admin/assets/index-V0udjUIR.js +808 -0
- package/admin/assets/{index-49472c6b.css → index-rD-EYdVL.css} +1 -1
- package/{frontend/assets/sha512-l_aqBm0S.js → admin/assets/sha512-kdrIq7UN.js} +1 -1
- package/admin/index.html +2 -3
- package/frontend/assets/index-EdGoLuWB.css +1 -0
- package/frontend/assets/index-r8i8c_PX.js +101 -0
- package/{admin/assets/sha512-c9d3d113.js → frontend/assets/sha512-4UUUqqOn.js} +1 -1
- package/frontend/index.html +2 -2
- package/package.json +9 -7
- package/src/adminApis.js +4 -86
- package/src/api.accounts.js +12 -15
- package/src/api.auth.js +9 -35
- package/src/api.log.js +74 -0
- package/src/api.monitor.js +5 -12
- package/src/api.net.js +1 -1
- package/src/api.vfs.js +4 -4
- package/src/apiMiddleware.js +1 -1
- package/src/auth.js +17 -8
- package/src/block.js +1 -1
- package/src/commands.js +1 -3
- package/src/const.js +1 -1
- package/src/cross.js +25 -7
- package/src/customHtml.js +1 -1
- package/src/ddns.js +53 -0
- package/src/github.js +2 -2
- package/src/langs/hfs-lang-it.json +10 -1
- package/src/log.js +9 -7
- package/src/middlewares.js +14 -10
- package/src/misc.js +4 -0
- package/src/nat.js +4 -8
- package/src/perm.js +28 -34
- package/src/plugins.js +3 -3
- package/src/serveGuiFiles.js +2 -1
- package/src/util-http.js +12 -8
- package/admin/assets/index-30c70443.js +0 -808
- package/frontend/assets/index-Bsm0eh8l.js +0 -101
- package/frontend/assets/index-tdvGCFPn.css +0 -1
- package/src/api.helpers.js +0 -28
- package/src/crypt.js +0 -21
- package/src/pbkdf2.js +0 -74
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@charset "UTF-8";.ariaOnly{position:absolute;clipPath:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}:root{height:100dvh;--bg: #fff;--text: #555;--ghost-contrast: #8882;--ghost-contrast-alt: #eee;--faint-contrast: #8884;--mild-contrast: #8886;--good-contrast: #000a;--button-bg: #6080aa;--button-text: #eaeaea;--focus-color: #468;--separator: " – "}@supports (content: ""/""){:root{--separator: " – " / ""}}:root .highlightedText,:root .file-menu a:hover{color:#0006;text-shadow:0 0 3px rgba(0,0,0,.4)}:root .theme-dark{--bg: #000;--text: #999;--ghost-contrast-alt: #181818;--good-contrast: #fffa;--button-bg: #345;--button-text: #999;color-scheme:dark}:root .theme-dark .highlightedText,:root .theme-dark .file-menu a:hover,:root .file-menu .theme-dark a:hover{color:#fff;text-shadow:0 0 3px #fff}:root .theme-dark a{color:#8ac}:root .theme-dark .dialog-closer{background:#633}:root .theme-dark .dialog-icon{color:#ccc}:root .theme-dark .dialog-icon .icon{color:#aaa}:root .theme-dark .dialog-backdrop{background:#333b}:root .theme-dark .error-msg{color:#b88;background-color:#623}:root .theme-dark button.toggled{color:#eee}body{background-color:var(--bg);margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,button,select,input{font-size:12pt}.list-wrapper{max-width:54em;margin:auto;min-height:100vh;display:flex;flex-direction:column}body,input{color:var(--text)}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}input:not([type=checkbox],[type=range]),select,textarea{padding:.3em .4em;border-radius:.5em;background:var(--bg);border-color:var(--mild-contrast);color:var(--good-contrast);max-width:100%;box-sizing:border-box;width:100%}input[type=range]{width:calc(100% - 1px)}input[type=checkbox]{transform:scale(1.7);accent-color:var(--button-bg)}textarea{font-size:14pt}label input[type=checkbox]{margin-right:.8em}select{text-align:center}.hidden{display:none!important}[class^=fa-]:before,[class*=" fa-"]:before{margin:0}.icon{font-size:1.2em;height:1.2em;width:1.4em;display:inline-block;text-align:center}img.file-icon{height:1em}.file-icon{background-size:contain;background-repeat:no-repeat;background-position:center;vertical-align:text-bottom}.icon.mirror:before{transform:scaleX(-1)}a{text-decoration:none;color:var(--button-bg)}button{background-color:var(--button-bg);color:var(--button-text);padding:.5em;border:transparent;text-decoration:none;border-radius:.3em;vertical-align:middle;cursor:pointer;transition:background-color .5s}button:hover{outline:1px solid var(--mild-contrast)}button[disabled]{background-color:var(--faint-contrast)}button.toggled{color:#fff;text-shadow:0 0 3px #fff}button:focus-visible,.breadcrumb:focus-visible{outline:3px solid var(--focus-color)}a>button{width:100%;height:100%}input:focus-visible,select:focus-visible,ul a:focus-visible{border-radius:.3em;border-color:transparent;outline:2px solid var(--focus-color)}.icon-button,ul.dir li .entry-panel .file-menu-button{font-size:.7em;padding:.2em .4em;margin-left:.4em;vertical-align:bottom}.error-msg{background-color:#faa;color:#833;padding:.5em 1em}.hide-back,.upload-toolbar,header{background-color:var(--bg)}header{position:sticky;top:0;padding:.2em .1em;z-index:3}kbd{background-color:#eee;border-radius:3px;border:1px solid #b4b4b4;box-shadow:0 1px 1px #0003,0 2px #ffffffb3 inset;color:#333;display:inline-block;font-size:.85em;font-weight:700;line-height:1;padding:2px 4px;white-space:nowrap;margin-right:.5em}.before-sliding{width:0!important;flex:0!important;margin:0!important;height:0!important;padding:0!important;overflow:hidden!important;transition:flex .5s}.show-sliding{transition:flex .5s;overflow:clip;flex:1;white-space:nowrap}.ani-working{animation:1s blink infinite}@keyframes blink{0%{opacity:1}50%{opacity:.2}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.spinner,.icon.spinner:before{animation:1.5s spin infinite linear;display:inline-flex;justify-content:center;align-items:center;width:min-content}.breadcrumb{padding:.1em .6em .2em;line-height:1.8em;border-radius:.7em;background-color:var(--button-bg);color:var(--button-text);border-top:1px solid #666;margin-right:-.1em}.breadcrumb:nth-child(-n+3) .icon{padding:0 .2em}#folder-stats,#filter-bar>span{font-size:90%}#folder-stats{margin-left:.5em;line-height:2em;float:right}#folder-stats .icon{margin-right:.3em}#filter{flex:1;box-sizing:border-box}#filter-bar{display:flex;align-items:center;gap:.8em;margin:.2em 0 0;padding:2px 0 1px 3px;height:1.8em}#filter-bar input[type=checkbox]{margin-top:.3em}#filter-bar span:empty{display:none}ul.dir{flex:1;padding:0;margin:0;clear:both}ul.dir>p{text-align:center}ul.dir li{display:block;list-style-type:none;padding:.3em .3em .4em;border-bottom:1px solid var(--faint-contrast)}ul.dir li:nth-of-type(odd){background-color:var(--ghost-contrast)}ul.dir li input[type=checkbox]{margin-right:1em}@media (hover: none){ul.dir li .link-wrapper .popup-menu-button{display:none}}@media (hover: hover){ul.dir li .link-wrapper:not(:hover) .popup-menu-button{display:none}ul.dir li .link-wrapper:hover{padding:1em;margin:-1em}}ul.dir li .link-wrapper a:last-of-type{word-break:break-word;padding-right:.3em}ul.dir li .link-wrapper a .icon{margin-right:.5em;display:inline-block;text-align:center}ul.dir li .link-wrapper a img.icon{vertical-align:bottom;position:relative;top:1px}ul.dir li .link-wrapper a:hover{text-decoration:underline}ul.dir li .entry-panel{float:right;padding-top:.3em;display:flex;align-items:center}ul.dir li .entry-panel .file-menu-button{margin:-3px 0 -3px .4em}ul.dir li .entry-panel .entry-details{font-size:90%;margin-left:4px;font-variant-numeric:tabular-nums}ul.dir li .entry-panel .entry-details .entry-size-unit{margin-left:.3em}ul.dir li>div:last-of-type{clear:both}ul.dir li.page-separator{margin-top:.7em;position:relative}ul.dir li.page-separator:before{content:attr(label);position:absolute;top:-1.5em;font-size:smaller;margin-left:calc(50% - 1em);opacity:.9}.entry-comment{display:inline;white-space:pre-wrap;word-break:break-word}.entry-comment:before,.entry-comment:after{font-size:1.5em;font-family:serif;line-height:1px;position:relative}.entry-comment:before{content:"“";margin-left:.5em;margin-right:.1em;top:.2em}.entry-comment:after{content:"„";top:-.1em;margin-left:.1em}#menu-bar{display:flex;justify-content:space-evenly;flex-wrap:wrap}#menu-bar>*{flex:1;margin:.1em}#menu-bar>*:first-child{margin-left:0}#menu-bar>*:last-child{margin-right:0}#menu-bar button{padding:min(1vh,.5em) 0}#searched{margin:.2em}#user-panel{display:flex;flex-direction:column;gap:1em}#user-panel a>button{width:100%}button .label{cursor:inherit;margin-left:.4em}.dialog-backdrop.working{font-size:5em;animation:1s fade-in}.dialog-content{padding:.2em}.dialog-alert .dialog-content{text-align:center}.dialog-alert .dialog-content p{text-align:left;display:inline-block}.dialog{min-width:11em;--color: var(--button-bg)}.dialog-icon .icon{margin-left:-1px;font-size:95%;margin-top:.4em;border-radius:.6em 0}#paging{position:sticky;bottom:0;display:flex;gap:.1em;background-color:var(--bg);padding:0 .2em .2em}#paging>button{z-index:1}#paging button{box-shadow:0 0 .3em .3em #0003}#paging #paging-middle{padding:0 .5em;margin:0 -.3em;display:flex;gap:.5em;flex:1;overflow-x:auto}#paging #paging-middle>button{flex:1;padding-top:0;padding-bottom:0}#paging button{background:var(--button-bg);text-align:center;white-space:nowrap;padding:.5em}.upload-toolbar{position:sticky;top:-4px}.upload-progress:before{content:var(--separator)}.entry-size:after{content:var(--separator)}.upload-progress{min-width:4em;display:inline-block;margin-left:.5em}.upload-list{margin-top:.5em}.upload-list td:nth-child(1){width:0}.upload-list td:nth-child(2){text-align:right;width:0;white-space:nowrap;padding-left:.5em}.upload-list td:nth-child(3){padding:.2em .5em;word-break:break-word}.nowrap{white-space:nowrap}.dialog-login form{display:flex;flex-direction:column;gap:1.2em}.dialog-login label{display:block;margin-bottom:.5em;margin-left:.1em}.miss-perm{margin:0 .3em}.popup-menu-button{font-size:.8em;padding:.2em .3em;position:absolute;opacity:.8;white-space:nowrap}.popup-menu-button:hover{opacity:1}.file-dialog .dialog{min-width:13em}.file-dialog-properties{word-break:break-word;line-height:1.5em;margin:0}.file-dialog-properties dt{font-weight:700}.file-dialog-properties dd{margin-left:1.5em}.file-menu{margin-top:1em;padding-top:1em;border-top:1px solid var(--faint-contrast);display:flex;flex-direction:column;gap:.7em 1em;justify-content:space-between}.file-menu a{display:flex;align-items:flex-start}.file-menu a label{margin-top:.1em}.file-menu a label small{display:block}.file-menu a .icon{margin-right:.5em}.unauthorized{text-align:center}#clipBar{position:sticky;bottom:0;background:var(--bg);box-shadow:0 0 .3em .3em var(--bg);display:flex;align-items:center;gap:.5em}.tiles-mode{max-width:none;margin:0 1em;--tile-size: 5;--name-lines: 3;--name-height: calc(4.7em + var(--tile-size) * .4em + 1.2em * var(--name-lines));--tile-width: calc(5em + 1em * var(--tile-size))}.tiles-mode ul.dir{display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--tile-width),1fr));grid-auto-rows:calc(var(--name-height) + 1.3em);gap:0 20px}.tiles-mode ul.dir li{text-align:center;position:relative;display:flex;flex-direction:column;border-bottom:none;overflow-x:clip;padding:.5em 0 0}.tiles-mode ul.dir li .link-wrapper:not(:hover){max-height:var(--name-height);display:block;display:-webkit-box;overflow:hidden;-webkit-line-clamp:calc(var(--name-lines) + 1);-webkit-box-orient:vertical}.tiles-mode ul.dir li .link-wrapper a:last-of-type{padding:0}.tiles-mode ul.dir li .link-wrapper a span{display:block}.tiles-mode ul.dir li .link-wrapper a .icon.icon{font-size:calc(1.2rem + .6rem * var(--tile-size))}.tiles-mode ul.dir li .link-wrapper a img.icon{width:auto;height:1em;padding:.1em 0}.tiles-mode ul.dir li .link-wrapper a .icon{font-size:4rem;display:block;margin:auto}.tiles-mode ul.dir li .link-wrapper:hover{overflow:visible;display:block;z-index:1}.tiles-mode ul.dir li .link-wrapper:hover .icon:before{text-decoration:none}.tiles-mode ul.dir li .link-wrapper:hover{padding:0;margin:0}.tiles-mode ul.dir li:nth-of-type(odd){background-color:var(--bg)}.tiles-mode ul.dir li .entry-panel{justify-content:center;font-size:10pt}.tiles-mode ul.dir li .entry-details{font-size:80%}.tiles-mode ul.dir li.page-separator:before{content:""}.tiles-mode ul.dir li input[type=checkbox]{margin:0;position:absolute;top:.3em;right:1em}.tiles-mode ul.dir li .link-wrapper a{display:inline}.tiles-mode ul.dir li:hover{--bg: var(--ghost-contrast-alt);background:var(--bg)}.tiles-mode ul.dir li:hover .link-wrapper,.tiles-mode ul.dir li:hover .entry-panel{z-index:1;background:var(--bg)}.tiles-mode ul.dir li:hover input[type=checkbox]{z-index:2}.tiles-mode ul.dir li:hover .entry-panel{padding-bottom:.3em}.tiles-mode .entry-size:after{content:none}.tiles-mode .entry-ts{display:none}.tiles-mode .popup-menu-button{position:absolute;top:0;left:0}.tiles-mode #filter-bar{margin-bottom:1em}.tiles-mode #paging{z-index:1}#root .file-show{--nav-size: min(25vh, 25vw)}#root .file-show>div{height:100%;width:100%}#root .file-show .showing-container{width:100%;height:100%;display:flex;justify-content:center;align-items:center}#root .file-show .showing{max-width:calc(100% - var(--nav-size) * 2);max-height:100%}#root .file-show audio.showing{min-width:60%}#root .file-show audio.showing,#root .file-show img.showing{max-width:100%}#root .file-show .main{flex:1;position:relative;max-height:100%;overflow:hidden}#root .file-show .freeY .main,#root .file-show .fullWidth .main{overflow-y:auto}#root .file-show .freeY .showing,#root .file-show .fullWidth .showing{max-height:initial;margin:auto}#root .file-show .freeY .showing-container,#root .file-show .fullWidth .showing-container{overflow:auto;align-items:flex-start}#root .file-show .fullWidth .showing-container,#root .file-show .fullWidth .showing{width:100%}#root .file-show .nav{position:absolute;width:.5em;font-size:var(--nav-size);cursor:pointer;opacity:.3;-webkit-text-stroke:2px black;user-select:none;transition:opacity .3s}#root .file-show .nav:hover{opacity:.7}#root .file-show .nav.nav-hidden{opacity:0}#root .file-show .bar{padding:.5em 1em;background:var(--bg);opacity:.8;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:.5em 1.5em}#root .file-show .bar .entry-details{font-size:smaller}#root .file-show .bar .entry-ts{display:inherit;font-size:smaller}#root .file-show .bar .entry-size:after{display:none}.file-show-help kbd{margin:.5em}.file-show-help kbd:first-of-type{margin-top:1em}@media (min-width: 42em){body{scrollbar-width:thin;scrollbar-color:var(--button-bg) var(--ghost-contrast)}body::-webkit-scrollbar{width:12px}body::-webkit-scrollbar-track{background:var(--ghost-contrast)}body::-webkit-scrollbar-thumb{background-color:var(--button-bg);border-radius:20px;border:1px solid var(--ghost-contrast)}button{padding:.5em 1em}}@media (max-width: 42em){:root{--ghost-contrast: #8883}body,button,select{font-size:14pt}#menu-bar button .label,#filter-bar button .label,#clipBar button .label{display:none}#filter-bar{margin-top:.4em}#filter-bar button{width:17.6vw;height:2.3em}.breadcrumb{word-break:break-all}.breadcrumb .icon{font-size:24px}#root>.tiles-mode{margin:0}}@media (max-height: 600px){.file-dialog .dialog-content{display:flex;gap:3em;margin:1em}.file-dialog .dialog-content .file-menu{margin-top:0;padding-top:0;border-top:none;margin-left:2em;padding-left:2em;border-left:1px solid var(--faint-contrast)}}@media (pointer: coarse){#root>.tiles-mode .file-menu-button{font-size:1em;margin-top:.3em}}.dialog-backdrop{position:fixed;inset:0;background:#8886;backdrop-filter:blur(2px);display:flex;justify-content:center;align-items:center;z-index:1000}.dialog{background:#fff;background:var(--bg);padding:max(.5em,1vw,2vh);padding-top:0;border-radius:1em;position:relative;margin:0 3vw;overflow:hidden;max-height:calc(100vh - 2em);display:flex;flex-direction:column;justify-content:center}.dialog-icon{color:#fff;background-color:var(--color);position:absolute;top:0;width:2em;height:1.8em;text-align:center;border-radius:.8em 0}.dialog-icon-text{display:flex;align-items:center;justify-content:center}.dialog-title{font-size:120%;margin:.3em 0;padding:0 .5em;min-height:1.2em}.dialog-closer~.dialog-title{margin-right:2em}.dialog-type~.dialog-title{margin-left:2em}.dialog-icon~.dialog-title{text-align:center}.dialog-closer{border-radius:0 .8em;right:0;padding:0;background-color:#c88}.dialog-icon~.dialog-content{margin-top:1em}.dialog-type{left:0;top:0;overflow:hidden;opacity:.7}.dialog-content{overflow:auto;max-height:calc(100vh - 4.5em)}.dialog-content p{white-space:pre-wrap;margin:.5em 0}.dialog-confirm .dialog-content button{margin-top:1em}.dialog-alert-info{--color: #282 }.dialog-alert-warning{--color: #c91 }.dialog-alert-error{--color: #822}@media (max-width: 42em){.dialog-icon{font-size:120%}.dialog-icon~.dialog-content{margin-top:1.5em}}.dialog-prompt label{display:block;margin:.5em .1em}
|
package/src/api.helpers.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.changeSrpHelper = exports.changePasswordHelper = void 0;
|
|
5
|
-
const perm_1 = require("./perm");
|
|
6
|
-
const apiMiddleware_1 = require("./apiMiddleware");
|
|
7
|
-
const const_1 = require("./const");
|
|
8
|
-
async function changePasswordHelper(account, newPassword) {
|
|
9
|
-
if (!newPassword) // clear text version
|
|
10
|
-
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'missing parameters');
|
|
11
|
-
await (0, perm_1.updateAccount)(account, account => {
|
|
12
|
-
account.password = newPassword;
|
|
13
|
-
});
|
|
14
|
-
return {};
|
|
15
|
-
}
|
|
16
|
-
exports.changePasswordHelper = changePasswordHelper;
|
|
17
|
-
async function changeSrpHelper(account, salt, verifier) {
|
|
18
|
-
if (perm_1.allowClearTextLogin.get())
|
|
19
|
-
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_ACCEPTABLE);
|
|
20
|
-
if (!salt || !verifier)
|
|
21
|
-
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'missing parameters');
|
|
22
|
-
await (0, perm_1.updateAccount)(account, account => {
|
|
23
|
-
(0, perm_1.saveSrpInfo)(account, salt, verifier);
|
|
24
|
-
delete account.hashed_password; // remove leftovers
|
|
25
|
-
});
|
|
26
|
-
return {};
|
|
27
|
-
}
|
|
28
|
-
exports.changeSrpHelper = changeSrpHelper;
|
package/src/crypt.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
-
};
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.verifyPassword = exports.hashPassword = void 0;
|
|
8
|
-
// simple wrapper
|
|
9
|
-
// @ts-ignore
|
|
10
|
-
const pbkdf2_1 = require("./pbkdf2");
|
|
11
|
-
const assert_1 = __importDefault(require("assert"));
|
|
12
|
-
async function hashPassword(s) {
|
|
13
|
-
return 'p2:' + await (0, pbkdf2_1.pbkdf2)(s);
|
|
14
|
-
}
|
|
15
|
-
exports.hashPassword = hashPassword;
|
|
16
|
-
async function verifyPassword(hashed, given) {
|
|
17
|
-
const i = hashed.indexOf(':');
|
|
18
|
-
(0, assert_1.default)(i > 0, 'bad hashed');
|
|
19
|
-
return await (0, pbkdf2_1.pbkdf2Verify)(hashed.slice(i + 1), given); // for the time being we totally ignore the "method" part
|
|
20
|
-
}
|
|
21
|
-
exports.verifyPassword = verifyPassword;
|
package/src/pbkdf2.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.pbkdf2Verify = exports.pbkdf2 = void 0;
|
|
4
|
-
// @ts-nocheck
|
|
5
|
-
const node_crypto_1 = require("node:crypto");
|
|
6
|
-
// FROM https://gist.github.com/chrisveness/770ee96945ec12ac84f134bf538d89fb
|
|
7
|
-
/**
|
|
8
|
-
* Returns PBKDF2 derived key from supplied password.
|
|
9
|
-
*
|
|
10
|
-
* Stored key can subsequently be used to verify that a password matches the original password used
|
|
11
|
-
* to derive the key, using pbkdf2Verify().
|
|
12
|
-
*
|
|
13
|
-
* @param {String} password - Password to be hashed using key derivation function.
|
|
14
|
-
* @param {Number} [iterations=1e6] - Number of iterations of HMAC function to apply.
|
|
15
|
-
* @returns {String} Derived key as base64 string.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* const key = await pbkdf2('pāşšŵōřđ'); // eg 'djAxBRKXWNWPyXgpKWHld8SWJA9CQFmLyMbNet7Rle5RLKJAkBCllLfM6tPFa7bAis0lSTiB'
|
|
19
|
-
*/
|
|
20
|
-
async function pbkdf2(password, iterations = 1e6) {
|
|
21
|
-
const pwUtf8 = new TextEncoder().encode(password); // encode pw as UTF-8
|
|
22
|
-
const pwKey = await node_crypto_1.webcrypto.subtle.importKey('raw', pwUtf8, 'PBKDF2', false, ['deriveBits']); // create pw key
|
|
23
|
-
const saltUint8 = node_crypto_1.webcrypto.getRandomValues(new Uint8Array(16)); // get random salt
|
|
24
|
-
const params = { name: 'PBKDF2', hash: 'SHA-256', salt: saltUint8, iterations: iterations }; // pbkdf2 params
|
|
25
|
-
const keyBuffer = await node_crypto_1.webcrypto.subtle.deriveBits(params, pwKey, 256); // derive key
|
|
26
|
-
const keyArray = Array.from(new Uint8Array(keyBuffer)); // key as byte array
|
|
27
|
-
const saltArray = Array.from(new Uint8Array(saltUint8)); // salt as byte array
|
|
28
|
-
const iterHex = ('000000' + iterations.toString(16)).slice(-6); // iter’n count as hex
|
|
29
|
-
const iterArray = iterHex.match(/.{2}/g).map(byte => parseInt(byte, 16)); // iter’ns as byte array
|
|
30
|
-
const compositeArray = [].concat(saltArray, iterArray, keyArray); // combined array
|
|
31
|
-
const compositeStr = compositeArray.map(byte => String.fromCharCode(byte)).join(''); // combined as string
|
|
32
|
-
// encode as base64
|
|
33
|
-
return btoa('v01' + compositeStr); // return composite key
|
|
34
|
-
}
|
|
35
|
-
exports.pbkdf2 = pbkdf2;
|
|
36
|
-
/**
|
|
37
|
-
* Verifies whether the supplied password matches the password previously used to generate the key.
|
|
38
|
-
*
|
|
39
|
-
* @param {String} key - Key previously generated with pbkdf2().
|
|
40
|
-
* @param {String} password - Password to be matched against previously derived key.
|
|
41
|
-
* @returns {boolean} Whether password matches key.
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* const match = await pbkdf2Verify(key, 'pāşšŵōřđ'); // true
|
|
45
|
-
*/
|
|
46
|
-
async function pbkdf2Verify(key, password) {
|
|
47
|
-
let compositeStr = null; // composite key is salt, iteration count, and derived key
|
|
48
|
-
try {
|
|
49
|
-
compositeStr = atob(key);
|
|
50
|
-
}
|
|
51
|
-
catch (e) {
|
|
52
|
-
throw new Error('Invalid key');
|
|
53
|
-
} // decode from base64
|
|
54
|
-
const version = compositeStr.slice(0, 3); // 3 bytes
|
|
55
|
-
const saltStr = compositeStr.slice(3, 19); // 16 bytes (128 bits)
|
|
56
|
-
const iterStr = compositeStr.slice(19, 22); // 3 bytes
|
|
57
|
-
const keyStr = compositeStr.slice(22, 54); // 32 bytes (256 bits)
|
|
58
|
-
if (version !== 'v01')
|
|
59
|
-
throw new Error('Invalid key');
|
|
60
|
-
// -- recover salt & iterations from stored (composite) key
|
|
61
|
-
const saltUint8 = new Uint8Array(saltStr.match(/./g).map(ch => ch.charCodeAt(0))); // salt as Uint8Array
|
|
62
|
-
// note: cannot use TextEncoder().encode(saltStr) as it generates UTF-8
|
|
63
|
-
const iterHex = iterStr.match(/./g).map(ch => ch.charCodeAt(0).toString(16)).join(''); // iter’n count as hex
|
|
64
|
-
const iterations = parseInt(iterHex, 16); // iter’ns
|
|
65
|
-
// -- generate new key from stored salt & iterations and supplied password
|
|
66
|
-
const pwUtf8 = new TextEncoder().encode(password); // encode pw as UTF-8
|
|
67
|
-
const pwKey = await node_crypto_1.webcrypto.subtle.importKey('raw', pwUtf8, 'PBKDF2', false, ['deriveBits']); // create pw key
|
|
68
|
-
const params = { name: 'PBKDF2', hash: 'SHA-256', salt: saltUint8, iterations: iterations }; // pbkdf params
|
|
69
|
-
const keyBuffer = await node_crypto_1.webcrypto.subtle.deriveBits(params, pwKey, 256); // derive key
|
|
70
|
-
const keyArray = Array.from(new Uint8Array(keyBuffer)); // key as byte array
|
|
71
|
-
const keyStrNew = keyArray.map(byte => String.fromCharCode(byte)).join(''); // key as string
|
|
72
|
-
return keyStrNew === keyStr; // test if newly generated key matches stored key
|
|
73
|
-
}
|
|
74
|
-
exports.pbkdf2Verify = pbkdf2Verify;
|