import { useThemeVars, NProgress, NImage } from 'naive-ui'; import { openBlock, createElementBlock, createElementVNode, defineComponent, toRefs, unref, withCtx, createTextVNode, toDisplayString, createVNode, withModifiers, useSSRContext, computed, ref, isRef } from 'vue'; import { a as __unplugin_components_0, u as useStyleStore, _ as _export_sfc } from './chunk-6003391e.js'; import { _ as _sfc_main$2 } from './chunk-8109fd17.js'; import { _ as __unplugin_components_3$1 } from './chunk-4e7a6a8d.js'; import { ssrRenderAttrs, ssrRenderComponent, ssrInterpolate, ssrRenderStyle } from 'vue/server-renderer'; import { useTimestamp } from '@vueuse/core'; import { u as useQRCode } from './chunk-aa632c49.js'; import { HmacSHA1, enc } from 'crypto-js'; import _ from 'lodash'; import { c as createToken } from './chunk-264f08b8.js'; import { u as useCopy } from './chunk-77c5cc16.js'; import { _ as _sfc_main$3 } from './chunk-de61ec1c.js'; import { c as computedRefreshable } from './chunk-cc665c88.js'; import 'pinia'; import './chunk-11f44f81.js'; import './chunk-35c3d701.js'; import 'qrcode'; import './chunk-95ec8cf7.js'; const _hoisted_1 = { viewBox: "0 0 24 24", width: "1.2em", height: "1.2em" }; const _hoisted_2 = /*#__PURE__*/createElementVNode("path", { fill: "currentColor", d: "M17.65 6.35A7.958 7.958 0 0 0 12 4a8 8 0 0 0-8 8a8 8 0 0 0 8 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18a6 6 0 0 1-6-6a6 6 0 0 1 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35Z" }, null, -1); const _hoisted_3 = [ _hoisted_2 ]; function render(_ctx, _cache) { return (openBlock(), createElementBlock("svg", _hoisted_1, _hoisted_3)) } const __unplugin_components_3 = { name: 'mdi-refresh', render }; /* vite-plugin-components disabled */ function hexToBytes(hex) { return (hex.match(/.{1,2}/g) ?? []).map((char) => Number.parseInt(char, 16)); } function computeHMACSha1(message, key) { return HmacSHA1(enc.Hex.parse(message), enc.Hex.parse(base32toHex(key))).toString(enc.Hex); } function base32toHex(base32) { const base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; const bits = base32.toUpperCase().replace(/=+$/, "").split("").map((value) => base32Chars.indexOf(value).toString(2).padStart(5, "0")).join(""); const hex = (bits.match(/.{1,8}/g) ?? []).map((chunk) => Number.parseInt(chunk, 2).toString(16).padStart(2, "0")).join(""); return hex; } function generateHOTP({ key, counter = 0 }) { const digest = computeHMACSha1(counter.toString(16).padStart(16, "0"), key); const bytes = hexToBytes(digest); const offset = bytes[19] & 15; const v = (bytes[offset] & 127) << 24 | (bytes[offset + 1] & 255) << 16 | (bytes[offset + 2] & 255) << 8 | bytes[offset + 3] & 255; const code = String(v % 1e6).padStart(6, "0"); return code; } function getCounterFromTime({ now, timeStep }) { return Math.floor(now / 1e3 / timeStep); } function generateTOTP({ key, now = Date.now(), timeStep = 30 }) { const counter = getCounterFromTime({ now, timeStep }); return generateHOTP({ key, counter }); } function buildKeyUri({ secret, app = "IT-Tools", account = "demo-user", algorithm = "SHA1", digits = 6, period = 30 }) { const params = { issuer: app, secret, algorithm, digits, period }; const paramsString = _(params).map((value, key) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&"); return `otpauth://totp/${encodeURIComponent(app)}:${encodeURIComponent(account)}?${paramsString}`; } function generateSecret() { return createToken({ length: 16, alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" }); } const _sfc_main$1 = /* @__PURE__ */ defineComponent({ __name: "token-display", __ssrInlineRender: true, props: { tokens: {} }, setup(__props) { const props = __props; const { copy: copyPrevious, isJustCopied: previousCopied } = useCopy({ createToast: false }); const { copy: copyCurrent, isJustCopied: currentCopied } = useCopy({ createToast: false }); const { copy: copyNext, isJustCopied: nextCopied } = useCopy({ createToast: false }); const { tokens } = toRefs(props); return (_ctx, _push, _parent, _attrs) => { const _component_c_tooltip = _sfc_main$2; const _component_c_button = __unplugin_components_0; _push(`
Previous
Current OTP
Next
`); _push(ssrRenderComponent(_component_c_tooltip, { tooltip: unref(previousCopied) ? "Copied !" : "Copy previous OTP", position: "bottom", "flex-1": "" }, { default: withCtx((_, _push2, _parent2, _scopeId) => { if (_push2) { _push2(ssrRenderComponent(_component_c_button, { "data-test-id": "previous-otp", "w-full": "", "important:h-12": "", "important:rounded-r-none": "", "important:font-mono": "", onClick: ($event) => unref(copyPrevious)(unref(tokens).previous) }, { default: withCtx((_2, _push3, _parent3, _scopeId2) => { if (_push3) { _push3(`${ssrInterpolate(unref(tokens).previous)}`); } else { return [ createTextVNode(toDisplayString(unref(tokens).previous), 1) ]; } }), _: 1 }, _parent2, _scopeId)); } else { return [ createVNode(_component_c_button, { "data-test-id": "previous-otp", "w-full": "", "important:h-12": "", "important:rounded-r-none": "", "important:font-mono": "", onClick: withModifiers(($event) => unref(copyPrevious)(unref(tokens).previous), ["prevent"]) }, { default: withCtx(() => [ createTextVNode(toDisplayString(unref(tokens).previous), 1) ]), _: 1 }, 8, ["onClick"]) ]; } }), _: 1 }, _parent)); _push(ssrRenderComponent(_component_c_tooltip, { tooltip: unref(currentCopied) ? "Copied !" : "Copy current OTP", position: "bottom", "flex-1": "", "flex-basis-5xl": "" }, { default: withCtx((_, _push2, _parent2, _scopeId) => { if (_push2) { _push2(ssrRenderComponent(_component_c_button, { "data-test-id": "current-otp", "w-full": "", "important:border-x": "1px solid gray op-40", "important:h-12": "", "important:rounded-0": "", "important:text-22px": "", onClick: ($event) => unref(copyCurrent)(unref(tokens).current) }, { default: withCtx((_2, _push3, _parent3, _scopeId2) => { if (_push3) { _push3(`${ssrInterpolate(unref(tokens).current)}`); } else { return [ createTextVNode(toDisplayString(unref(tokens).current), 1) ]; } }), _: 1 }, _parent2, _scopeId)); } else { return [ createVNode(_component_c_button, { "data-test-id": "current-otp", "w-full": "", "important:border-x": "1px solid gray op-40", "important:h-12": "", "important:rounded-0": "", "important:text-22px": "", onClick: withModifiers(($event) => unref(copyCurrent)(unref(tokens).current), ["prevent"]) }, { default: withCtx(() => [ createTextVNode(toDisplayString(unref(tokens).current), 1) ]), _: 1 }, 8, ["onClick"]) ]; } }), _: 1 }, _parent)); _push(ssrRenderComponent(_component_c_tooltip, { tooltip: unref(nextCopied) ? "Copied !" : "Copy next OTP", position: "bottom", "flex-1": "" }, { default: withCtx((_, _push2, _parent2, _scopeId) => { if (_push2) { _push2(ssrRenderComponent(_component_c_button, { "data-test-id": "next-otp", "w-full": "", "important:h-12": "", "important:rounded-l-none": "", onClick: ($event) => unref(copyNext)(unref(tokens).next) }, { default: withCtx((_2, _push3, _parent3, _scopeId2) => { if (_push3) { _push3(`${ssrInterpolate(unref(tokens).next)}`); } else { return [ createTextVNode(toDisplayString(unref(tokens).next), 1) ]; } }), _: 1 }, _parent2, _scopeId)); } else { return [ createVNode(_component_c_button, { "data-test-id": "next-otp", "w-full": "", "important:h-12": "", "important:rounded-l-none": "", onClick: withModifiers(($event) => unref(copyNext)(unref(tokens).next), ["prevent"]) }, { default: withCtx(() => [ createTextVNode(toDisplayString(unref(tokens).next), 1) ]), _: 1 }, 8, ["onClick"]) ]; } }), _: 1 }, _parent)); _push(`
`); }; } }); const _sfc_setup$1 = _sfc_main$1.setup; _sfc_main$1.setup = (props, ctx) => { const ssrContext = useSSRContext(); (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/tools/otp-code-generator-and-validator/token-display.vue"); return _sfc_setup$1 ? _sfc_setup$1(props, ctx) : void 0; }; const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "otp-code-generator-and-validator", __ssrInlineRender: true, setup(__props) { const now = useTimestamp(); const interval = computed(() => now.value / 1e3 % 30); const theme = useThemeVars(); const styleStore = useStyleStore(); const secret = ref(generateSecret()); function refreshSecret() { secret.value = generateSecret(); } const [tokens] = computedRefreshable( () => ({ previous: generateTOTP({ key: secret.value, now: now.value - 3e4 }), current: generateTOTP({ key: secret.value, now: now.value }), next: generateTOTP({ key: secret.value, now: now.value + 3e4 }) }), { throttle: 500 } ); const keyUri = computed(() => buildKeyUri({ secret: secret.value })); const { qrcode } = useQRCode({ text: keyUri, color: { background: computed(() => styleStore.isDarkTheme ? "#ffffff" : "#00000000"), foreground: "#000000" }, options: { width: 210 } }); const secretValidationRules = [ { message: "Secret should be a base32 string", validator: (value) => value.toUpperCase().match(/^[A-Z234567]+$/) }, { message: "Please set a secret", validator: (value) => value !== "" } ]; return (_ctx, _push, _parent, _attrs) => { const _component_c_input_text = __unplugin_components_3$1; const _component_c_tooltip = _sfc_main$2; const _component_c_button = __unplugin_components_0; const _component_icon_mdi_refresh = __unplugin_components_3; const _component_n_progress = NProgress; const _component_n_image = NImage; _push(`
`); _push(ssrRenderComponent(_component_c_input_text, { value: unref(secret), "onUpdate:value": ($event) => isRef(secret) ? secret.value = $event : null, label: "Secret", placeholder: "Paste your TOTP secret...", "mb-5": "", "validation-rules": secretValidationRules }, { suffix: withCtx((_, _push2, _parent2, _scopeId) => { if (_push2) { _push2(ssrRenderComponent(_component_c_tooltip, { tooltip: "Generate a new random secret" }, { default: withCtx((_2, _push3, _parent3, _scopeId2) => { if (_push3) { _push3(ssrRenderComponent(_component_c_button, { circle: "", variant: "text", size: "small", onClick: refreshSecret }, { default: withCtx((_3, _push4, _parent4, _scopeId3) => { if (_push4) { _push4(ssrRenderComponent(_component_icon_mdi_refresh, null, null, _parent4, _scopeId3)); } else { return [ createVNode(_component_icon_mdi_refresh) ]; } }), _: 1 }, _parent3, _scopeId2)); } else { return [ createVNode(_component_c_button, { circle: "", variant: "text", size: "small", onClick: refreshSecret }, { default: withCtx(() => [ createVNode(_component_icon_mdi_refresh) ]), _: 1 }) ]; } }), _: 1 }, _parent2, _scopeId)); } else { return [ createVNode(_component_c_tooltip, { tooltip: "Generate a new random secret" }, { default: withCtx(() => [ createVNode(_component_c_button, { circle: "", variant: "text", size: "small", onClick: refreshSecret }, { default: withCtx(() => [ createVNode(_component_icon_mdi_refresh) ]), _: 1 }) ]), _: 1 }) ]; } }), _: 1 }, _parent)); _push(`
`); _push(ssrRenderComponent(_sfc_main$1, { tokens: unref(tokens) }, null, _parent)); _push(ssrRenderComponent(_component_n_progress, { percentage: 100 * unref(interval) / 30, color: unref(theme).primaryColor, "show-indicator": false }, null, _parent)); _push(`
Next in ${ssrInterpolate(String(Math.floor(30 - unref(interval))).padStart(2, "0"))}s
`); _push(ssrRenderComponent(_component_n_image, { src: unref(qrcode) }, null, _parent)); _push(ssrRenderComponent(_component_c_button, { href: unref(keyUri), target: "_blank" }, { default: withCtx((_, _push2, _parent2, _scopeId) => { if (_push2) { _push2(` Open Key URI in new tab `); } else { return [ createTextVNode(" Open Key URI in new tab ") ]; } }), _: 1 }, _parent)); _push(`
`); _push(ssrRenderComponent(_sfc_main$3, { label: "Secret in hexadecimal", value: unref(base32toHex)(unref(secret)), readonly: "", placeholder: "Secret in hex will be displayed here", "mb-5": "" }, null, _parent)); _push(ssrRenderComponent(_sfc_main$3, { label: "Epoch", value: Math.floor(unref(now) / 1e3).toString(), readonly: "", "mb-5": "", placeholder: "Epoch in sec will be displayed here" }, null, _parent)); _push(`

Iteration

`); _push(ssrRenderComponent(_sfc_main$3, { value: String(unref(getCounterFromTime)({ now: unref(now), timeStep: 30 })), readonly: "", label: "Count:", "label-position": "left", "label-width": "90px", "label-align": "right", placeholder: "Iteration count will be displayed here" }, null, _parent)); _push(ssrRenderComponent(_sfc_main$3, { value: unref(getCounterFromTime)({ now: unref(now), timeStep: 30 }).toString(16).padStart(16, "0"), readonly: "", placeholder: "Iteration count in hex will be displayed here", "label-position": "left", "label-width": "90px", "label-align": "right", label: "Padded hex:" }, null, _parent)); _push(`
`); }; } }); /* unplugin-vue-components disabled */const otpCodeGeneratorAndValidator_vue_vue_type_style_index_0_scoped_388f1b9b_lang = ''; const _sfc_setup = _sfc_main.setup; _sfc_main.setup = (props, ctx) => { const ssrContext = useSSRContext(); (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue"); return _sfc_setup ? _sfc_setup(props, ctx) : void 0; }; const otpCodeGeneratorAndValidator = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-388f1b9b"]]); export { otpCodeGeneratorAndValidator as default };