feat: 增强SaaS后端功能与安全性

refactor: 重构数据库连接使用PostgreSQL替代SQLite
feat(auth): 增加JWT验证的audience和issuer检查
feat(crypto): 添加AES-256-GCM字段加密支持
feat(api): 集成utoipa实现OpenAPI文档
fix(admin): 修复配置项表单验证逻辑
style: 统一代码格式与类型定义
docs: 更新技术栈文档说明PostgreSQL
This commit is contained in:
iven
2026-03-31 00:12:53 +08:00
parent 4d8d560d1f
commit 44256a511c
177 changed files with 9731 additions and 948 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"rustc_fingerprint":5915500824126575890,"outputs":{"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\szend\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n","stderr":""},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.93.1 (01f6ddf75 2026-02-11)\nbinary: rustc\ncommit-hash: 01f6ddf7588f42ae2d7eb0a2f21d44e8e96674cf\ncommit-date: 2026-02-11\nhost: x86_64-pc-windows-msvc\nrelease: 1.93.1\nLLVM version: 21.1.8\n","stderr":""}},"successes":{}}
{"rustc_fingerprint":5915500824126575890,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.93.1 (01f6ddf75 2026-02-11)\nbinary: rustc\ncommit-hash: 01f6ddf7588f42ae2d7eb0a2f21d44e8e96674cf\ncommit-date: 2026-02-11\nhost: x86_64-pc-windows-msvc\nrelease: 1.93.1\nLLVM version: 21.1.8\n","stderr":""},"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\szend\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n","stderr":""}},"successes":{}}

View File

@@ -0,0 +1 @@
{"rustc_vv":"rustc 1.93.1 (01f6ddf75 2026-02-11)\nbinary: rustc\ncommit-hash: 01f6ddf7588f42ae2d7eb0a2f21d44e8e96674cf\ncommit-date: 2026-02-11\nhost: x86_64-pc-windows-msvc\nrelease: 1.93.1\nLLVM version: 21.1.8\n"}

0
target/doc/.lock Normal file
View File

2
target/doc/crates.js Normal file
View File

@@ -0,0 +1,2 @@
window.ALL_CRATES = ["totp_rs"];
//{"start":21,"fragment_lengths":[9]}

1
target/doc/help.html Normal file
View File

@@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Documentation for Rustdoc"><title>Help</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="./static.files/${f}">`).join(""))</script><link rel="stylesheet" href="./static.files/normalize-9960930a.css"><link rel="stylesheet" href="./static.files/rustdoc-ca0dd0c4.css"><meta name="rustdoc-vars" data-root-path="./" data-static-root-path="./static.files/" data-current-crate="totp_rs" data-themes="" data-resource-suffix="" data-rustdoc-version="1.93.1 (01f6ddf75 2026-02-11)" data-channel="1.93.1" data-search-js="search-9e2438ea.js" data-stringdex-js="stringdex-a3946164.js" data-settings-js="settings-c38705f0.js" ><script src="./static.files/storage-e2aeef58.js"></script><script defer src="./static.files/main-a410ff4d.js"></script><noscript><link rel="stylesheet" href="./static.files/noscript-263c88ec.css"></noscript><link rel="alternate icon" type="image/png" href="./static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="./static.files/favicon-044be391.svg"></head><body class="rustdoc mod sys"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">All</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><a class="logo-container" href="./index.html"><img class="rust-logo" src="./static.files/rust-logo-9a9549ea.svg" alt="logo"></a><h2><a href="./index.html">Rustdoc</a><span class="version">1.93.1</span></h2></div><div class="version">(01f6ddf75 2026-02-11)</div><h2 class="location">Help</h2><div class="sidebar-elems"></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content"><div class="main-heading"><h1>Rustdoc help</h1><span class="out-of-band"><a id="back" href="javascript:void(0)" onclick="history.back();">Back</a></span></div><noscript><section><p>You need to enable JavaScript to use keyboard commands or search.</p><p>For more information, browse the <a href="https://doc.rust-lang.org/1.93.1/rustdoc/">rustdoc handbook</a>.</p></section></noscript></section></div></main></body></html>

View File

@@ -0,0 +1 @@
rn_("FQRAAACDXwBzAHQAdQB2AHcAeAB5AHoAc2AAewB8AH0AfgB/AIAAgQBzTQBkAGUAZgBnAGgAaQBqAAytAAJtAGZpcHN0CABbAA==")

View File

@@ -0,0 +1 @@
rn_("dQBAAAByhACSAJMAlACVAJYAlwCYAG1CAE4ATwBQAFEAUgBTAFQAQUAAAJAAogCjAK0AswD7AnJ3IUIAAKcAtAC2APsCZW8=")

View File

@@ -0,0 +1 @@
rn_("BQHAAAAKtQACkQBlaVFGAAClAK4ArwCwALEAsgCnAACFoFAAAAC1oJAAAACzoHAAAACrAsAG9EoAAAABAAwAAQA=")

View File

@@ -0,0 +1 @@
rn_("dQBAAAByhACSAJMAlACVAJYAlwCYAG1CAE4ATwBQAFEAUgBTAFQAQUAAAJAAogCjAK0AswABAAGgYAAAAKRyOzAAAAEAABEABAAfAAAAJgAHAF8AAABzAAcA+wNtcnc=")

View File

@@ -0,0 +1 @@
rn_("dQFAAAByhACSAJMAlACVAJYAlwCYAFJeAG4AbwBwAHEAcgBtbkIATgBPAFAAUQBSAFMAVAAFAEQAAAe3AGSfAFFDAAClAK4ArwCwALEAsgDzgQJpdAUBQQAAAWMAB6MAcnUkAAUAQAAAY2sAigCLAIwAjQCOAI8AciAAKwKgMAAAAKttdHUAQgAAcoQAkgCTAJQAlQCWAJcAmABtQgBOAE8AUABRAFIAUwBUACFDAACCAIMAhwCjAISgEAAAAFmgUAAAAIUBEQH1kAAAABIAAQAKAAYABQLAAAAAJQAEqwBTXgBuAG8AcABxAHIAbHN3VQFBAABDmQCaAJsAnACdAFelAK4ArwCwALEAsgBpdDkAPAA9AD4APwBAAAEAAaBgAAAApHI7MAAAAQAAEQAEAB8AAAAmAAcAXwAAAHMABwAFAcAAABFcAJ4AEqoAtwBlaQEAjaBAAAAAbWRBVk6hx6BgAAAAnqBAAAAAYqAAAAAAJWJv9PaXLw0csInIvjkW/rsltmfsxlIz6xdUJGJCF0X6vsIHLBPi/tc7YAxrmNMNQxp5HzswAAABAAAUAAUAIQAAAC4ABgBgAAAAewAGAJkABAA=")

View File

@@ -0,0 +1 @@
rn_("NQBAAAAAqQBzRQBGAFoAYQBVAUIAAEOZAJoAmwCcAJ0AV6UArgCvALAAsQCyAGl0OQA8AD0APgA/AEAAIUYAAIIAgwCHAAUBwAAAAIYAE5AAowBxcmcGAIegEAAAAFugcAAAAKagAAAAAKtRSQTzHQAAAAgAhgA=")

View File

@@ -0,0 +1 @@
rn_("AQAAOzAAAAEAABQABQAhAAAALgAGAGAAAAB7AAYAmQAEAAUAQwAAB7cAZJ8AYwADoBAAAABiZW5v86cAAAANAAIA")

View File

@@ -0,0 +1 @@
rn_("NQNCAAAFtQAJswADkQAHqwBicHN0SgBLAFcAWAAFAUAAAAanAAGoAGNlOwBTAISwQACqAA2gUAAAALQARAkBCgAAAA==")

View File

@@ -0,0 +1 @@
rd_("")

View File

@@ -0,0 +1 @@
rd_("gtotp_rs")

View File

@@ -0,0 +1 @@
rd_("CkWill check that to_bytes() returns the same. One secret \xe2\x80\xa6CjWill not check for issuer and account_name equality As \xe2\x80\xa6AmNon-encoded \xe2\x80\x9craw\xe2\x80\x9d secret.CmWill create a new instance of TOTP with given parameters. \xe2\x80\xa6BnGive the ttl (in seconds) of the current tokenmInvalid host.CcHMAC-SHA1 is the default algorithm of most TOTP \xe2\x80\xa6BeCouldn\xe2\x80\x99t decode step into a number.CnTOTP holds informations as to how to generate an auth code \xe2\x80\xa6AoReturns the argument unchanged.000000BaCalls <code>U::from(self)</code>.000000AmWill sign the given timestampCnNumber of steps allowed as network delay. 1 would mean one \xe2\x80\xa6ClDuration in seconds of a step. The recommended value per \xe2\x80\xa6CmWill check if token is valid given the provided timestamp \xe2\x80\xa6BaCharacters should only be digits.CaIssuer contains invalid character <code>:</code>.CiHMAC-SHA256. Supported in theory according to yubico. \xe2\x80\xa6CiHMAC-SHA512. Supported in theory according to yubico. \xe2\x80\xa6oInvalid scheme.AcWrong base32 input.CmShared secret between client and server to validate token \xe2\x80\xa6AlSet the <code>digits</code>.CmThe number of digits composing the auth code. Per rfc-4226\xe2\x80\xa6ChAs per rfc-4226 the secret should come from a strong \xe2\x80\xa6DoTry to transform a <code>Secret::Encoded</code> into a <code>Secret::Raw</code>AfBase32 encoded secret.Cbrfc-6238 compliant set of options to create a TOTPCmThis library permits the creation of 2FA authentification \xe2\x80\xa6CiWill generate a token given the provided timestamp in \xe2\x80\xa6BmGet the inner String value as a Vec of bytes.BkTry to create a TOTP from a Rfc6238 config.AbUnknown algorithm.CkAlgorithm enum holds the three standards algorithms for \xe2\x80\xa6CmSHA-1 is the most widespread algorithm used, and for totp \xe2\x80\xa6CkReturns the timestamp of the first second for the next stepBaInvalid secret size. (Too short?)EaTry to transforms a <code>Secret::Raw</code> into a <code>Secret::Encoded</code>.DkAccount name contains invalid character <code>:</code> or couldn\xe2\x80\x99t be \xe2\x80\xa6AeInvalid base32 input.BaDigits should be between 6 and 8.CgError returned when input is not compliant to rfc-6238.BiErrors returned mostly upon decoding URL.CmWill create a new instance of TOTP from the given Rfc6238 \xe2\x80\xa6ClImplementations MUST extract a 6-digit code at a minimum \xe2\x80\xa6ChWill check if token is valid by current system time, \xe2\x80\xa6CmWill create a new instance of TOTP with given parameters. \xe2\x80\xa6AiCouldn\xe2\x80\x99t decode issuer.CjThe length of the shared secret MUST be at least 128 bits.AkIssuers should be the same.BeDifferent ways secret parsing failed.BmGenerate a token from the current system timeCnWill return the base32 representation of the secret, which \xe2\x80\xa6CnReturns the timestamp of the first second of the next step \xe2\x80\xa6AnCouldn\xe2\x80\x99t parse account name.")

View File

@@ -0,0 +1 @@
rd_("Ah[99,13,100,28,163,135,0]Ah[99,13,100,88,180,135,0]Ag[99,13,100,88,76,135,0]Ai[99,13,100,145,164,135,0]Ai[99,13,100,100,132,135,0]Ah[99,13,100,100,37,135,0]Ae[99,15,100,88,76,0,0]Cg[99,13,100,28,163,92,0,\"impl-Display-for-Rfc6238Error\"]Ce[99,13,100,28,163,59,0,\"impl-Debug-for-Rfc6238Error\"]Af[99,13,100,28,94,59,0]Ck[99,13,100,88,180,92,0,\"impl-Display-for-SecretParseError\"]Ci[99,13,100,88,180,59,0,\"impl-Debug-for-SecretParseError\"]C`[99,13,100,88,76,92,0,\"impl-Display-for-Secret\"]Bn[99,13,100,88,76,59,0,\"impl-Debug-for-Secret\"]Cf[99,13,100,145,164,59,0,\"impl-Debug-for-TotpUrlError\"]Ch[99,13,100,145,164,92,0,\"impl-Display-for-TotpUrlError\"]Ce[99,13,100,100,132,92,0,\"impl-Display-for-Algorithm\"]Cc[99,13,100,100,132,59,0,\"impl-Debug-for-Algorithm\"]Bm[99,13,100,100,37,59,0,\"impl-Debug-for-TOTP\"]Bo[99,13,100,100,37,92,0,\"impl-Display-for-TOTP\"]Ae[99,13,100,28,94,0,0]Af[99,13,100,100,37,0,0]0Ag[99,15,100,145,164,0,0]Ag[99,15,100,100,132,0,0]1Ab[99,5,100,0,0,0,0]Ag[99,13,100,28,163,32,0]Af[99,13,100,28,94,32,0]Ag[99,13,100,88,180,32,0]Af[99,13,100,88,76,32,0]Ah[99,13,100,145,164,32,0]0Ah[99,13,100,100,132,32,0]Ag[99,13,100,100,37,32,0]Ag[99,13,100,28,163,34,0]Af[99,13,100,28,94,34,0]Ag[99,13,100,88,180,34,0]Af[99,13,100,88,76,34,0]Ah[99,13,100,145,164,34,0]Ah[99,13,100,100,132,34,0]Ag[99,13,100,100,37,34,0]Af[99,13,100,100,37,0,0]Af[99,14,100,100,37,0,0]01Af[99,13,100,28,94,58,0]Ag[99,13,100,88,180,58,0]Af[99,13,100,88,76,58,0]Ah[99,13,100,100,132,58,0]Ag[99,13,100,100,37,58,0]Ag[99,15,100,145,164,0,0]0Ag[99,15,100,100,132,0,0]011Ac[99,6,100,88,0,0,0]Ag[99,13,100,28,163,67,0]Af[99,13,100,28,94,67,0]Ag[99,13,100,88,180,67,0]Af[99,13,100,88,76,67,0]Ah[99,13,100,145,164,67,0]Ah[99,13,100,100,132,67,0]Ag[99,13,100,100,37,67,0]Ae[99,13,100,28,94,0,0]Af[99,14,100,100,37,0,0]0Ae[99,13,100,88,76,0,0]Ae[99,15,100,88,76,0,0]Ac[99,5,100,28,0,0,0]Ah[99,13,100,100,132,91,0]A`[99,3,0,0,0,0,0]Af[99,13,100,28,163,9,0]Ae[99,13,100,28,94,9,0]Af[99,13,100,88,180,9,0]Ae[99,13,100,88,76,9,0]Ag[99,13,100,145,164,9,0]Ag[99,13,100,100,132,9,0]Af[99,13,100,100,37,9,0]Af[99,13,100,100,37,0,0]<Af[99,13,100,28,94,95,0]Ag[99,13,100,88,180,95,0]Af[99,13,100,88,76,95,0]Ah[99,13,100,100,132,95,0]Ag[99,13,100,100,37,95,0]Ag[99,13,100,28,163,96,0]Af[99,13,100,28,94,96,0]Ag[99,13,100,88,180,96,0]Af[99,13,100,88,76,96,0]Ah[99,13,100,145,164,96,0]Ah[99,13,100,100,132,96,0]Ag[99,13,100,100,37,96,0]0Ag[99,13,100,28,163,97,0]Af[99,13,100,28,94,97,0]Ag[99,13,100,88,180,97,0]Af[99,13,100,88,76,97,0]Ah[99,13,100,145,164,97,0]Ah[99,13,100,100,132,97,0]Ag[99,13,100,100,37,97,0]Ag[99,15,100,145,164,0,0]Ab[99,6,100,0,0,0,0]Af[99,14,100,100,37,0,0]Af[99,13,100,100,37,0,0]Ah[99,13,100,28,163,108,0]Ah[99,13,100,88,180,108,0]Ag[99,13,100,88,76,108,0]Ai[99,13,100,145,164,108,0]Ai[99,13,100,100,132,108,0]Ah[99,13,100,100,37,108,0]9Ah[99,13,100,28,163,133,0]Ag[99,13,100,28,94,133,0]Ah[99,13,100,88,180,133,0]Ag[99,13,100,88,76,133,0]Ai[99,13,100,145,164,133,0]Ai[99,13,100,100,132,133,0]Ah[99,13,100,100,37,133,0]Af[99,13,100,28,94,95,0]Ag[99,13,100,88,180,95,0]Af[99,13,100,88,76,95,0]Ah[99,13,100,100,132,95,0]Ag[99,13,100,100,37,95,0]Ae[99,13,100,88,76,0,0]Ag[99,15,100,145,164,0,0]Af[99,15,100,88,180,0,0]1Ac[99,6,100,28,0,0,0]Ad[99,6,100,145,0,0,0]Af[99,13,100,100,37,0,0]Af[99,15,100,28,163,0,0]11Ae[99,13,100,28,94,0,0]616Ag[99,13,100,28,94,166,0]Ah[99,13,100,88,180,166,0]Ag[99,13,100,88,76,166,0]Ai[99,13,100,100,132,166,0]Ah[99,13,100,100,37,166,0]Ac[99,6,100,88,0,0,0]888<")

View File

@@ -0,0 +1 @@
rd_("Ba[\"{{{AAd{ADf}}{AAd{ADf}}}Dl}\",[]]Ba[\"{{{AAd{AFh}}{AAd{AFh}}}Dl}\",[]]Ao[\"{{{AAd{Ih}}{AAd{Ih}}}Dl}\",[]]Ba[\"{{{AAd{ADh}}{AAd{ADh}}}Dl}\",[]]Ba[\"{{{AAd{A@h}}{AAd{A@h}}}Dl}\",[]]Ao[\"{{{AAd{Dj}}{AAd{Dj}}}Dl}\",[]]Bc[\"{{{AAd{ADf}}{AAd{CbA@l}}}Hl}\",[]]0Bb[\"{{{AAd{Kl}}{AAd{CbA@l}}}Hl}\",[]]Bc[\"{{{AAd{AFh}}{AAd{CbA@l}}}Hl}\",[]]0Bb[\"{{{AAd{Ih}}{AAd{CbA@l}}}Hl}\",[]]0Bc[\"{{{AAd{ADh}}{AAd{CbA@l}}}Hl}\",[]]0Bc[\"{{{AAd{A@h}}{AAd{CbA@l}}}Hl}\",[]]0Bb[\"{{{AAd{Dj}}{AAd{CbA@l}}}Hl}\",[]]0Bb[\"{{Hd{Af{A`}}}{{Hn{KlADf}}}}\",[]]Bi[\"{{A@hHdA`Cn{Af{A`}}}{{Hn{DjADh}}}}\",[]]Ba[\"{{{AAd{Dj}}}{{Hn{CnAEl}}}}\",[]]A`[\"{cc{}}\",[\"T\"]]000o[\"{ADfADh}\",[]]111Aa[\"{{}c{}}\",[\"U\"]]000000B`[\"{{{AAd{Dj}}Cn}{{Af{A`}}}}\",[]]m[\"{DjA`}\",[]]m[\"{DjCn}\",[]]Ba[\"{{{AAd{Dj}}{AAd{Cj}}Cn}Dl}\",[]]Af[\"{{{AAd{Kl}}}Kl}\",[]]Ah[\"{{{AAd{AFh}}}AFh}\",[]]Af[\"{{{AAd{Ih}}}Ih}\",[]]Ah[\"{{{AAd{A@h}}}A@h}\",[]]Af[\"{{{AAd{Dj}}}Dj}\",[]]Ak[\"{AAd{{AAd{c}}}{}}\",[\"T\"]]000000Be[\"{{{AAd{CbKl}}Hd}{{Hn{GbADf}}}}\",[]]m[\"{DjHd}\",[]]m[\"{DjAf}\",[]]Ba[\"{{{AAd{Ih}}}{{Hn{IhAFh}}}}\",[]]n[\"{{}A@h}\",[]]n[\"{AAdIl}\",[]]000000Ah[\"{{{AAd{Dj}}Cn}Ij}\",[]]Bg[\"{{{AAd{Ih}}}{{Hn{{Af{A`}}AFh}}}}\",[]]Ab[\"{AAdc{}}\",[\"T\"]]0000An[\"{c{{Hn{e}}}{}{}}\",[\"U\",\"T\"]]000000Bh[\"{Kl{{Hn{Djc}}}{}}\",[\"TryFrom::Error\"]]Ai[\"{{}{{Hn{c}}}{}}\",[\"U\"]]000000n[\"{DjA@h}\",[]]Ah[\"{{{AAd{Dj}}Cn}Cn}\",[]]n[\"{AAdIj}\",[]]00000Be[\"{{{AAd{Cb}}}{{AAd{Cbc}}}{}}\",[\"T\"]]000000Ao[\"{{AAd{AAd{Cbc}}}Gb{}}\",[\"T\"]]0000Af[\"{{{AAd{Ih}}}Ih}\",[]]Ah[\"{Kl{{Hn{DjADh}}}}\",[]]Bj[\"{{{AAd{Dj}}{AAd{Cj}}}{{Hn{DlAEl}}}}\",[]]An[\"{{A@hHdA`Cn{Af{A`}}}Dj}\",[]]B`[\"{{{Af{A`}}}{{Hn{KlADf}}}}\",[]]Aj[\"{{AAd{Lf{CbA`}}}Gb}\",[]]0000Ba[\"{{{AAd{Dj}}}{{Hn{IjAEl}}}}\",[]]Af[\"{{{AAd{Dj}}}Ij}\",[]]Ba[\"{{{AAd{Dj}}}{{Hn{CnAEl}}}}\",[]]")

View File

@@ -0,0 +1 @@
rb_("RWIAOzAAAAEAAAYAAQAuAAYAOzAAAAEAABIABAAmAAMAKwACAG4ABAB7AAYAOzAAAAEAAA0AAgBOAAYAcwAGAAF6AAAAOzAAAAEAAAsAAQCSAAsAQWIAAAA7MAAAAQAABgABAHMABgA=")

View File

@@ -0,0 +1 @@
rd_("b()beq00000bu8cAnycRawcVeccfmt000000000000cmutcnew0crfccstrcttlcu64dFromdHostdIntodSHA1dStepdTOTPdbooldfrom0000000dinto000000dsigndskewdstepduniteCloneeDebugecheckeclone0000eusizefBorrowfDigitsfIssuerfResult0fSHA256fSHA512fSchemefSecret0fStringfTypeIdfborrow000000fdigits0fsecret0fto_rawgDefaultgDisplaygEncodedgRfc6238gToOwnedgTryFromgTryIntogdefaultgpointergtotp_rsgtype_id000000hToStringhgeneratehto_byteshto_owned0000htry_from0000000htry_into000000iAlgorithm0iBorrowMutiFormatteriPartialEqialgorithminext_stepireferenceito_string00000iurl_errorjSecretSizejborrow_mut000000jclone_into0000jto_encodedkAccountNamekParseBase32lDigitsNumberlRfc6238ErrorlTotpUrlErrorlfrom_rfc6238mCloneToUninitmInvalidDigitsmcheck_currentmnew_uncheckedmwith_defaultsnIssuerDecodingnSecretTooSmalloIssuerMistmatchoSystemTimeErroroclone_to_uninit0000A`SecretParseErrorA`generate_currentAaget_secret_base32Aanext_step_currentAcAccountNameDecoding")

View File

@@ -0,0 +1 @@
rd_("b()beq00000bu8canycrawcveccfmt000000000000cmutcnew0crfccstrcttlcu64dfromdhostdintodsha1dstepdtotpdbool666666664444444dsigndskew4dunitecloneedebugecheck22222eusizefborrowfdigitsfissuerfresult0fsha256fsha512fschemefsecret0fstringftypeid99999998822etorawgdefaultgdisplaygencodedgrfc6238gtoownedgtryfromgtryinto6gpointerftotprs:::::::htostringhgenerategtobytes77777666666665555555ialgorithm0iborrowmutiformatteripartialeq3hnextstepireference888888hurlerrorjsecretsize6666666icloneinto0000itoencodedkaccountnamekparsebase32ldigitsnumberlrfc6238errorltotpurlerrorkfromrfc6238mclonetouninitminvaliddigitslcheckcurrentlnewuncheckedlwithdefaultsnissuerdecodingnsecrettoosmalloissuermistmatchosystemtimeerror88888A`secretparseerrorogeneratecurrentogetsecretbase32onextstepcurrentAcaccountnamedecoding")

View File

@@ -0,0 +1 @@
rd_("f[1,\"\"]0A`[10,\"core::any\"]Am[5,\"alloc::vec\",\"alloc::vec\"]f[0,\"\"]m[2,\"totp_rs\"]44Ad[10,\"core::convert\"]0Ag[5,\"totp_rs\",\"totp_rs\"]66Ab[10,\"core::clone\"]A`[10,\"core::fmt\"]8Ac[10,\"core::borrow\"]Ak[8,\"core::fmt\",\"core::fmt\"]Ba[6,\"core::result\",\"core::result\"]Ao[6,\"totp_rs\",\"totp_rs::secret\"]Bc[5,\"alloc::string\",\"alloc::string\"]Ak[5,\"core::any\",\"core::any\"]:Ad[10,\"core::default\"]7Al[5,\"totp_rs\",\"totp_rs::rfc\"]Ad[10,\"alloc::borrow\"]<<f[1,\"\"]f[3,\"\"]Ad[10,\"alloc::string\"]Ag[6,\"totp_rs\",\"totp_rs\"]<Ak[5,\"core::fmt\",\"core::fmt\"]A`[10,\"core::cmp\"]5m[2,\"totp_rs\"]Al[6,\"totp_rs\",\"totp_rs::rfc\"]Bb[6,\"totp_rs\",\"totp_rs::url_error\"]Ab[10,\"core::clone\"]Ak[5,\"std::time\",\"std::time\"]Ao[6,\"totp_rs\",\"totp_rs::secret\"]")

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
rd_("Bo[\"\",\"AAAAAAA7MAAAAQAACQACAJkABACuAAQAAAFVAAAA\"]Fk[\"AAAAAAA7MAAAAQAABQACAKkAAACuAAQAARkAAAABqAAAAAABGgAAAA==\",\"AAAAAAABNQAAAAFtAAAAAAAAAAAAAAAAAAABNgAAAA==\"]Eg[\"AAAAAAABqQAAAAEZAAAAAagAAAAAARoAAAA=\",\"AAAAAAABNQAAAAFtAAAAAAAAAAAAAAAAAAABWAAAAA==\"]Eo[\"AAAAAAA7MAAAAQAAEAACAJIACwCuAAQAOzAAAAEAAAwAAQALAAwAAVUAAAA=\",\"AAAAAAA7MAAAAQAABgABAJIABgA=\"]Ao[\"AAAAAAAAATsAAAABpwAAAA==\",\"\"]Fg[\"AAAAAAJsAAAAiAAAAAE1AAAAATsAAAABqAAAAAABGgAAAA==\",\"AAAAAAGIAAAAAh0AAAC2AAAAAAAAAAAAAAAAAAAAATcAAAA=\"]Jk[\"AAAAAkAAAAC1AAAAAmwAAACIAAAABQYAAAAdAAAANQAAALQAAAC2AAAAAxYAAAAXAAAAOwAAAAGnAAAAAAAAAAAAAAAAAAU2AAAANwAAAFYAAABYAAAAhwAAAA==\",\"AAAAAUAAAAACegAAAKQAAAAAAAGoAAAAAAEaAAAA\"]Bo[\"\",\"AAAAAAA7MAAAAQAABQABAAEABQABOwAAAAGnAAAA\"]7Dk[\"AAAAAAAAARkAAAACVQAAAKgAAAAAARoAAAA=\",\"AAAAAAAAAAAAAAAAAAAAAAAAAVYAAAA=\"]Bc[\"\",\"AAAAAAAAOzAAAAEAAAwAAQALAAwA\"]Gm[\"\",\"AAA7MAAAAQAABgABAHsABgA7MAAAAQAABgABAHMABgACegAAAKQAAAAFHQAAAFkAAACpAAAAtAAAALYAAAACGQAAAG0AAAACVQAAAKcAAAAAARoAAAA=\",1]Do[\"AAAAAj4AAACeAAAAAAIDAAAAWQAAAAMQAAAAEQAAAG0AAAA=\",\"AAAAAj4AAACeAAAAAAFZAAAA\"]Cc[\"\",\"AAA7MAAAAQAABQABAIoABQABtQAAAAFsAAAAAbQAAAA=\"]Ao[\"\",\"AAA7MAAAAQAABgABAGQABgA=\"]Dk[\"AAAAATwAAAACegAAAKQAAAAAAQ0AAAABVQAAAA==\",\"AAAAATwAAAAAAakAAAABGQAAAA==\"]Bm[\"AAAAAAAFrgAAAK8AAACwAAAAsQAAALIAAAA=\",\"\",1]Fc[\"AAAAAT8AAAAAAQUAAAACFAAAABUAAAABqAAAAAABGgAAAA==\",\"AAFhAAAAAAE/AAAAAAAAAAAAAAAAAAAAAAABhwAAAA==\"]Bc[\"AAAAAAAAOzAAAAEAAAwAAQALAAwA\",\"\"]Oa[\"AAA7MAAAAQAAEQADAGQABgBuAAQAigAFADswAAABAAANAAQAPAAEAE4ABgCeAAAAtQAAAAJsAAAAiAAAADswAAABAAAbAAgAAQAFAB0AAAA1AAAAWQAAAJIACwCuAAQAtAAAALYAAAA7MAAAAQAADgADAAsADAA7AAAAbQAAAAJVAAAApwAAAA==\",\"AAAAOzAAAAEAAAYAAQBOAAYAADswAAABAAAGAAEAkgAGAA==\",1]Dc[\"AAABKgAAAAAAAQEAAAACCwAAAAwAAAA=\",\"AAAAAAABqQAAAAEZAAAAAVUAAAA=\"]Dc[\"AAAAAAABBAAAAAISAAAAEwAAAA==\",\"AAABKgAAAAABpAAAAAAAAAABGgAAAA==\"]Bg[\"\",\"AAAAAAADHQAAALQAAAC2AAAAAAGnAAAA\"]Dc[\"AAAAAT0AAAAAAQIAAAACDgAAAA8AAAA=\",\"AAAAAT0AAAAAAVkAAAABbQAAAA==\"]")

1
target/doc/settings.html Normal file
View File

@@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Settings of Rustdoc"><title>Settings</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="./static.files/${f}">`).join(""))</script><link rel="stylesheet" href="./static.files/normalize-9960930a.css"><link rel="stylesheet" href="./static.files/rustdoc-ca0dd0c4.css"><meta name="rustdoc-vars" data-root-path="./" data-static-root-path="./static.files/" data-current-crate="totp_rs" data-themes="" data-resource-suffix="" data-rustdoc-version="1.93.1 (01f6ddf75 2026-02-11)" data-channel="1.93.1" data-search-js="search-9e2438ea.js" data-stringdex-js="stringdex-a3946164.js" data-settings-js="settings-c38705f0.js" ><script src="./static.files/storage-e2aeef58.js"></script><script defer src="./static.files/main-a410ff4d.js"></script><noscript><link rel="stylesheet" href="./static.files/noscript-263c88ec.css"></noscript><link rel="alternate icon" type="image/png" href="./static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="./static.files/favicon-044be391.svg"></head><body class="rustdoc mod sys"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">All</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><a class="logo-container" href="./index.html"><img class="rust-logo" src="./static.files/rust-logo-9a9549ea.svg" alt="logo"></a><h2><a href="./index.html">Rustdoc</a><span class="version">1.93.1</span></h2></div><div class="version">(01f6ddf75 2026-02-11)</div><h2 class="location">Settings</h2><div class="sidebar-elems"></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content"><div class="main-heading"><h1>Rustdoc settings</h1><span class="out-of-band"><a id="back" href="javascript:void(0)" onclick="history.back();">Back</a></span></div><noscript><section>You need to enable JavaScript be able to update your settings.</section></noscript><script defer src="./static.files/settings-c38705f0.js"></script></section></div></main></body></html>

2
target/doc/src-files.js Normal file
View File

@@ -0,0 +1,2 @@
createSrcSidebar('[["totp_rs",["",[],["custom_providers.rs","lib.rs","rfc.rs","secret.rs","url_error.rs"]]]]');
//{"start":19,"fragment_lengths":[88]}

View File

@@ -0,0 +1,63 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `C:\Users\szend\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\totp-rs-5.7.1\src\custom_providers.rs`."><title>custom_providers.rs - source</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../static.files/rustdoc-ca0dd0c4.css"><meta name="rustdoc-vars" data-root-path="../../" data-static-root-path="../../static.files/" data-current-crate="totp_rs" data-themes="" data-resource-suffix="" data-rustdoc-version="1.93.1 (01f6ddf75 2026-02-11)" data-channel="1.93.1" data-search-js="search-9e2438ea.js" data-stringdex-js="stringdex-a3946164.js" data-settings-js="settings-c38705f0.js" ><script src="../../static.files/storage-e2aeef58.js"></script><script defer src="../../static.files/src-script-813739b1.js"></script><script defer src="../../src-files.js"></script><script defer src="../../static.files/main-a410ff4d.js"></script><noscript><link rel="stylesheet" href="../../static.files/noscript-263c88ec.css"></noscript><link rel="alternate icon" type="image/png" href="../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../static.files/favicon-044be391.svg"></head><body class="rustdoc src"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="src-sidebar-title"><h2>Files</h2></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><section id="main-content" class="content"><div class="main-heading"><h1><div class="sub-heading">totp_rs/</div>custom_providers.rs</h1><rustdoc-toolbar></rustdoc-toolbar></div><div class="example-wrap digits-2"><pre class="rust"><code><a href=#1 id=1 data-nosnippet>1</a><span class="attr">#[cfg(feature = <span class="string">"steam"</span>)]
<a href=#2 id=2 data-nosnippet>2</a></span><span class="kw">use crate</span>::{Algorithm, TOTP};
<a href=#3 id=3 data-nosnippet>3</a>
<a href=#4 id=4 data-nosnippet>4</a><span class="attr">#[cfg(feature = <span class="string">"steam"</span>)]
<a href=#5 id=5 data-nosnippet>5</a>#[cfg_attr(docsrs, doc(cfg(feature = <span class="string">"steam"</span>)))]
<a href=#6 id=6 data-nosnippet>6</a></span><span class="kw">impl </span>TOTP {
<a href=#7 id=7 data-nosnippet>7</a> <span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#8 id=8 data-nosnippet>8</a> </span><span class="doccomment">/// Will create a new instance of TOTP using the Steam algorithm with given parameters. See [the doc](struct.TOTP.html#fields) for reference as to how to choose those values
<a href=#9 id=9 data-nosnippet>9</a> ///
<a href=#10 id=10 data-nosnippet>10</a> /// # Description
<a href=#11 id=11 data-nosnippet>11</a> /// * `secret`: expect a non-encoded value, to pass in base32 string use `Secret::Encoded(String)`
<a href=#12 id=12 data-nosnippet>12</a> ///
<a href=#13 id=13 data-nosnippet>13</a> /// # Example
<a href=#14 id=14 data-nosnippet>14</a> ///
<a href=#15 id=15 data-nosnippet>15</a> /// ```rust
<a href=#16 id=16 data-nosnippet>16</a> /// use totp_rs::{Secret, TOTP};
<a href=#17 id=17 data-nosnippet>17</a> /// let secret = Secret::Encoded("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".into());
<a href=#18 id=18 data-nosnippet>18</a> /// let totp = TOTP::new_steam(secret.to_bytes().unwrap(), "username".into());
<a href=#19 id=19 data-nosnippet>19</a> /// ```
<a href=#20 id=20 data-nosnippet>20</a> </span><span class="kw">pub fn </span>new_steam(secret: Vec&lt;u8&gt;, account_name: String) -&gt; TOTP {
<a href=#21 id=21 data-nosnippet>21</a> <span class="self">Self</span>::new_unchecked(
<a href=#22 id=22 data-nosnippet>22</a> Algorithm::Steam,
<a href=#23 id=23 data-nosnippet>23</a> <span class="number">5</span>,
<a href=#24 id=24 data-nosnippet>24</a> <span class="number">1</span>,
<a href=#25 id=25 data-nosnippet>25</a> <span class="number">30</span>,
<a href=#26 id=26 data-nosnippet>26</a> secret,
<a href=#27 id=27 data-nosnippet>27</a> <span class="prelude-val">Some</span>(<span class="string">"Steam"</span>.into()),
<a href=#28 id=28 data-nosnippet>28</a> account_name,
<a href=#29 id=29 data-nosnippet>29</a> )
<a href=#30 id=30 data-nosnippet>30</a> }
<a href=#31 id=31 data-nosnippet>31</a>
<a href=#32 id=32 data-nosnippet>32</a> <span class="attr">#[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#33 id=33 data-nosnippet>33</a> </span><span class="doccomment">/// Will create a new instance of TOTP using the Steam algorithm with given parameters. See [the doc](struct.TOTP.html#fields) for reference as to how to choose those values
<a href=#34 id=34 data-nosnippet>34</a> ///
<a href=#35 id=35 data-nosnippet>35</a> /// # Description
<a href=#36 id=36 data-nosnippet>36</a> /// * `secret`: expect a non-encoded value, to pass in base32 string use `Secret::Encoded(String)`
<a href=#37 id=37 data-nosnippet>37</a> ///
<a href=#38 id=38 data-nosnippet>38</a> /// # Example
<a href=#39 id=39 data-nosnippet>39</a> ///
<a href=#40 id=40 data-nosnippet>40</a> /// ```rust
<a href=#41 id=41 data-nosnippet>41</a> /// use totp_rs::{Secret, TOTP};
<a href=#42 id=42 data-nosnippet>42</a> /// let secret = Secret::Encoded("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".to_string());
<a href=#43 id=43 data-nosnippet>43</a> /// let totp = TOTP::new_steam(secret.to_bytes().unwrap());
<a href=#44 id=44 data-nosnippet>44</a> /// ```
<a href=#45 id=45 data-nosnippet>45</a> </span><span class="kw">pub fn </span>new_steam(secret: Vec&lt;u8&gt;) -&gt; TOTP {
<a href=#46 id=46 data-nosnippet>46</a> <span class="self">Self</span>::new_unchecked(Algorithm::Steam, <span class="number">5</span>, <span class="number">1</span>, <span class="number">30</span>, secret)
<a href=#47 id=47 data-nosnippet>47</a> }
<a href=#48 id=48 data-nosnippet>48</a>}
<a href=#49 id=49 data-nosnippet>49</a>
<a href=#50 id=50 data-nosnippet>50</a><span class="attr">#[cfg(all(test, feature = <span class="string">"steam"</span>))]
<a href=#51 id=51 data-nosnippet>51</a></span><span class="kw">mod </span>test {
<a href=#52 id=52 data-nosnippet>52</a> <span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#53 id=53 data-nosnippet>53</a> </span><span class="kw">use super</span>::<span class="kw-2">*</span>;
<a href=#54 id=54 data-nosnippet>54</a>
<a href=#55 id=55 data-nosnippet>55</a> <span class="attr">#[test]
<a href=#56 id=56 data-nosnippet>56</a> #[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#57 id=57 data-nosnippet>57</a> </span><span class="kw">fn </span>get_url_steam() {
<a href=#58 id=58 data-nosnippet>58</a> <span class="kw">let </span>totp = TOTP::new_steam(<span class="string">"TestSecretSuperSecret"</span>.into(), <span class="string">"constantoine"</span>.into());
<a href=#59 id=59 data-nosnippet>59</a> <span class="kw">let </span>url = totp.get_url();
<a href=#60 id=60 data-nosnippet>60</a> <span class="macro">assert_eq!</span>(url.as_str(), <span class="string">"otpauth://steam/Steam:constantoine?secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&amp;digits=5&amp;algorithm=SHA1&amp;issuer=Steam"</span>);
<a href=#61 id=61 data-nosnippet>61</a> }
<a href=#62 id=62 data-nosnippet>62</a>}
</code></pre></div></section></main></body></html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,412 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `C:\Users\szend\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\totp-rs-5.7.1\src\rfc.rs`."><title>rfc.rs - source</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../static.files/rustdoc-ca0dd0c4.css"><meta name="rustdoc-vars" data-root-path="../../" data-static-root-path="../../static.files/" data-current-crate="totp_rs" data-themes="" data-resource-suffix="" data-rustdoc-version="1.93.1 (01f6ddf75 2026-02-11)" data-channel="1.93.1" data-search-js="search-9e2438ea.js" data-stringdex-js="stringdex-a3946164.js" data-settings-js="settings-c38705f0.js" ><script src="../../static.files/storage-e2aeef58.js"></script><script defer src="../../static.files/src-script-813739b1.js"></script><script defer src="../../src-files.js"></script><script defer src="../../static.files/main-a410ff4d.js"></script><noscript><link rel="stylesheet" href="../../static.files/noscript-263c88ec.css"></noscript><link rel="alternate icon" type="image/png" href="../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../static.files/favicon-044be391.svg"></head><body class="rustdoc src"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="src-sidebar-title"><h2>Files</h2></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><section id="main-content" class="content"><div class="main-heading"><h1><div class="sub-heading">totp_rs/</div>rfc.rs</h1><rustdoc-toolbar></rustdoc-toolbar></div><div class="example-wrap digits-3"><pre class="rust"><code><a href=#1 id=1 data-nosnippet>1</a><span class="kw">use </span><span class="kw">crate</span>::Algorithm;
<a href=#2 id=2 data-nosnippet>2</a><span class="kw">use </span><span class="kw">crate</span>::TotpUrlError;
<a href=#3 id=3 data-nosnippet>3</a><span class="kw">use </span><span class="kw">crate</span>::TOTP;
<a href=#4 id=4 data-nosnippet>4</a>
<a href=#5 id=5 data-nosnippet>5</a><span class="attr">#[cfg(feature = <span class="string">"serde_support"</span>)]
<a href=#6 id=6 data-nosnippet>6</a></span><span class="kw">use </span>serde::{Deserialize, Serialize};
<a href=#7 id=7 data-nosnippet>7</a>
<a href=#8 id=8 data-nosnippet>8</a><span class="attr">#[cfg(feature = <span class="string">"zeroize"</span>)]
<a href=#9 id=9 data-nosnippet>9</a></span><span class="kw">use </span>zeroize;
<a href=#10 id=10 data-nosnippet>10</a>
<a href=#11 id=11 data-nosnippet>11</a><span class="doccomment">/// Error returned when input is not compliant to [rfc-6238](https://tools.ietf.org/html/rfc6238).
<a href=#12 id=12 data-nosnippet>12</a></span><span class="attr">#[derive(Debug, Eq, PartialEq)]
<a href=#13 id=13 data-nosnippet>13</a></span><span class="kw">pub enum </span>Rfc6238Error {
<a href=#14 id=14 data-nosnippet>14</a> <span class="doccomment">/// Implementations MUST extract a 6-digit code at a minimum and possibly 7 and 8-digit code.
<a href=#15 id=15 data-nosnippet>15</a> </span>InvalidDigits(usize),
<a href=#16 id=16 data-nosnippet>16</a> <span class="doccomment">/// The length of the shared secret MUST be at least 128 bits.
<a href=#17 id=17 data-nosnippet>17</a> </span>SecretTooSmall(usize),
<a href=#18 id=18 data-nosnippet>18</a>}
<a href=#19 id=19 data-nosnippet>19</a>
<a href=#20 id=20 data-nosnippet>20</a><span class="kw">impl </span>std::error::Error <span class="kw">for </span>Rfc6238Error {}
<a href=#21 id=21 data-nosnippet>21</a>
<a href=#22 id=22 data-nosnippet>22</a><span class="kw">impl </span>std::fmt::Display <span class="kw">for </span>Rfc6238Error {
<a href=#23 id=23 data-nosnippet>23</a> <span class="kw">fn </span>fmt(<span class="kw-2">&amp;</span><span class="self">self</span>, f: <span class="kw-2">&amp;mut </span>std::fmt::Formatter&lt;<span class="lifetime">'_</span>&gt;) -&gt; std::fmt::Result {
<a href=#24 id=24 data-nosnippet>24</a> <span class="kw">match </span><span class="self">self </span>{
<a href=#25 id=25 data-nosnippet>25</a> Rfc6238Error::InvalidDigits(digits) =&gt; <span class="macro">write!</span>(
<a href=#26 id=26 data-nosnippet>26</a> f,
<a href=#27 id=27 data-nosnippet>27</a> <span class="string">"Implementations MUST extract a 6-digit code at a minimum and possibly 7 and 8-digit code. {} digits is not allowed"</span>,
<a href=#28 id=28 data-nosnippet>28</a> digits,
<a href=#29 id=29 data-nosnippet>29</a> ),
<a href=#30 id=30 data-nosnippet>30</a> Rfc6238Error::SecretTooSmall(bits) =&gt; <span class="macro">write!</span>(
<a href=#31 id=31 data-nosnippet>31</a> f,
<a href=#32 id=32 data-nosnippet>32</a> <span class="string">"The length of the shared secret MUST be at least 128 bits. {} bits is not enough"</span>,
<a href=#33 id=33 data-nosnippet>33</a> bits,
<a href=#34 id=34 data-nosnippet>34</a> ),
<a href=#35 id=35 data-nosnippet>35</a> }
<a href=#36 id=36 data-nosnippet>36</a> }
<a href=#37 id=37 data-nosnippet>37</a>}
<a href=#38 id=38 data-nosnippet>38</a>
<a href=#39 id=39 data-nosnippet>39</a><span class="comment">// Check that the number of digits is RFC-compliant.
<a href=#40 id=40 data-nosnippet>40</a>// (between 6 and 8 inclusive).
<a href=#41 id=41 data-nosnippet>41</a></span><span class="kw">pub fn </span>assert_digits(digits: <span class="kw-2">&amp;</span>usize) -&gt; <span class="prelude-ty">Result</span>&lt;(), Rfc6238Error&gt; {
<a href=#42 id=42 data-nosnippet>42</a> <span class="kw">if </span>!(<span class="kw-2">&amp;</span><span class="number">6</span>..=<span class="kw-2">&amp;</span><span class="number">8</span>).contains(<span class="kw-2">&amp;</span>digits) {
<a href=#43 id=43 data-nosnippet>43</a> <span class="prelude-val">Err</span>(Rfc6238Error::InvalidDigits(<span class="kw-2">*</span>digits))
<a href=#44 id=44 data-nosnippet>44</a> } <span class="kw">else </span>{
<a href=#45 id=45 data-nosnippet>45</a> <span class="prelude-val">Ok</span>(())
<a href=#46 id=46 data-nosnippet>46</a> }
<a href=#47 id=47 data-nosnippet>47</a>}
<a href=#48 id=48 data-nosnippet>48</a>
<a href=#49 id=49 data-nosnippet>49</a><span class="comment">// Check that the secret is AT LEAST 128 bits long, as per the RFC's requirements.
<a href=#50 id=50 data-nosnippet>50</a>// It is still RECOMMENDED to have an at least 160 bits long secret.
<a href=#51 id=51 data-nosnippet>51</a></span><span class="kw">pub fn </span>assert_secret_length(secret: <span class="kw-2">&amp;</span>[u8]) -&gt; <span class="prelude-ty">Result</span>&lt;(), Rfc6238Error&gt; {
<a href=#52 id=52 data-nosnippet>52</a> <span class="kw">if </span>secret.as_ref().len() &lt; <span class="number">16 </span>{
<a href=#53 id=53 data-nosnippet>53</a> <span class="prelude-val">Err</span>(Rfc6238Error::SecretTooSmall(secret.as_ref().len() * <span class="number">8</span>))
<a href=#54 id=54 data-nosnippet>54</a> } <span class="kw">else </span>{
<a href=#55 id=55 data-nosnippet>55</a> <span class="prelude-val">Ok</span>(())
<a href=#56 id=56 data-nosnippet>56</a> }
<a href=#57 id=57 data-nosnippet>57</a>}
<a href=#58 id=58 data-nosnippet>58</a>
<a href=#59 id=59 data-nosnippet>59</a><span class="doccomment">/// [rfc-6238](https://tools.ietf.org/html/rfc6238) compliant set of options to create a [TOTP](struct.TOTP.html)
<a href=#60 id=60 data-nosnippet>60</a>///
<a href=#61 id=61 data-nosnippet>61</a>/// # Example
<a href=#62 id=62 data-nosnippet>62</a>/// ```
<a href=#63 id=63 data-nosnippet>63</a>/// use totp_rs::{Rfc6238, TOTP};
<a href=#64 id=64 data-nosnippet>64</a>///
<a href=#65 id=65 data-nosnippet>65</a>/// let mut rfc = Rfc6238::with_defaults(
<a href=#66 id=66 data-nosnippet>66</a>/// "totp-sercret-123".as_bytes().to_vec()
<a href=#67 id=67 data-nosnippet>67</a>/// ).unwrap();
<a href=#68 id=68 data-nosnippet>68</a>///
<a href=#69 id=69 data-nosnippet>69</a>/// // optional, set digits, issuer, account_name
<a href=#70 id=70 data-nosnippet>70</a>/// rfc.digits(8).unwrap();
<a href=#71 id=71 data-nosnippet>71</a>///
<a href=#72 id=72 data-nosnippet>72</a>/// let totp = TOTP::from_rfc6238(rfc).unwrap();
<a href=#73 id=73 data-nosnippet>73</a>/// ```
<a href=#74 id=74 data-nosnippet>74</a></span><span class="attr">#[derive(Debug, Clone)]
<a href=#75 id=75 data-nosnippet>75</a>#[cfg_attr(feature = <span class="string">"serde_support"</span>, derive(Serialize, Deserialize))]
<a href=#76 id=76 data-nosnippet>76</a>#[cfg_attr(feature = <span class="string">"zeroize"</span>, derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
<a href=#77 id=77 data-nosnippet>77</a></span><span class="kw">pub struct </span>Rfc6238 {
<a href=#78 id=78 data-nosnippet>78</a> <span class="doccomment">/// SHA-1
<a href=#79 id=79 data-nosnippet>79</a> </span><span class="attr">#[cfg_attr(feature = <span class="string">"zeroize"</span>, zeroize(skip))]
<a href=#80 id=80 data-nosnippet>80</a> </span>algorithm: Algorithm,
<a href=#81 id=81 data-nosnippet>81</a> <span class="doccomment">/// The number of digits composing the auth code. Per [rfc-4226](https://tools.ietf.org/html/rfc4226#section-5.3), this can oscilate between 6 and 8 digits.
<a href=#82 id=82 data-nosnippet>82</a> </span>digits: usize,
<a href=#83 id=83 data-nosnippet>83</a> <span class="doccomment">/// The recommended value per [rfc-6238](https://tools.ietf.org/html/rfc6238#section-5.2) is 1.
<a href=#84 id=84 data-nosnippet>84</a> </span>skew: u8,
<a href=#85 id=85 data-nosnippet>85</a> <span class="doccomment">/// The recommended value per [rfc-6238](https://tools.ietf.org/html/rfc6238#section-5.2) is 30 seconds.
<a href=#86 id=86 data-nosnippet>86</a> </span>step: u64,
<a href=#87 id=87 data-nosnippet>87</a> <span class="doccomment">/// As per [rfc-4226](https://tools.ietf.org/html/rfc4226#section-4) the secret should come from a strong source, most likely a CSPRNG. It should be at least 128 bits, but 160 are recommended.
<a href=#88 id=88 data-nosnippet>88</a> </span>secret: Vec&lt;u8&gt;,
<a href=#89 id=89 data-nosnippet>89</a> <span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#90 id=90 data-nosnippet>90</a> #[cfg_attr(docsrs, doc(cfg(feature = <span class="string">"otpauth"</span>)))]
<a href=#91 id=91 data-nosnippet>91</a> </span><span class="doccomment">/// The "Github" part of "Github:constantoine@github.com". Must not contain a colon `:`
<a href=#92 id=92 data-nosnippet>92</a> /// For example, the name of your service/website.
<a href=#93 id=93 data-nosnippet>93</a> /// Not mandatory, but strongly recommended!
<a href=#94 id=94 data-nosnippet>94</a> </span>issuer: <span class="prelude-ty">Option</span>&lt;String&gt;,
<a href=#95 id=95 data-nosnippet>95</a> <span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#96 id=96 data-nosnippet>96</a> #[cfg_attr(docsrs, doc(cfg(feature = <span class="string">"otpauth"</span>)))]
<a href=#97 id=97 data-nosnippet>97</a> </span><span class="doccomment">/// The "constantoine@github.com" part of "Github:constantoine@github.com". Must not contain a colon `:`.
<a href=#98 id=98 data-nosnippet>98</a> /// For example, the name of your user's account.
<a href=#99 id=99 data-nosnippet>99</a> </span>account_name: String,
<a href=#100 id=100 data-nosnippet>100</a>}
<a href=#101 id=101 data-nosnippet>101</a>
<a href=#102 id=102 data-nosnippet>102</a><span class="kw">impl </span>Rfc6238 {
<a href=#103 id=103 data-nosnippet>103</a> <span class="doccomment">/// Create an [rfc-6238](https://tools.ietf.org/html/rfc6238) compliant set of options that can be turned into a [TOTP](struct.TOTP.html).
<a href=#104 id=104 data-nosnippet>104</a> ///
<a href=#105 id=105 data-nosnippet>105</a> /// # Errors
<a href=#106 id=106 data-nosnippet>106</a> ///
<a href=#107 id=107 data-nosnippet>107</a> /// will return a [Rfc6238Error](enum.Rfc6238Error.html) when
<a href=#108 id=108 data-nosnippet>108</a> /// - `digits` is lower than 6 or higher than 8.
<a href=#109 id=109 data-nosnippet>109</a> /// - `secret` is smaller than 128 bits (16 characters).
<a href=#110 id=110 data-nosnippet>110</a> </span><span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#111 id=111 data-nosnippet>111</a> #[allow(unused_mut)]
<a href=#112 id=112 data-nosnippet>112</a> </span><span class="kw">pub fn </span>new(
<a href=#113 id=113 data-nosnippet>113</a> digits: usize,
<a href=#114 id=114 data-nosnippet>114</a> <span class="kw-2">mut </span>secret: Vec&lt;u8&gt;,
<a href=#115 id=115 data-nosnippet>115</a> issuer: <span class="prelude-ty">Option</span>&lt;String&gt;,
<a href=#116 id=116 data-nosnippet>116</a> account_name: String,
<a href=#117 id=117 data-nosnippet>117</a> ) -&gt; <span class="prelude-ty">Result</span>&lt;Rfc6238, Rfc6238Error&gt; {
<a href=#118 id=118 data-nosnippet>118</a> <span class="kw">let </span>validate = || {
<a href=#119 id=119 data-nosnippet>119</a> assert_digits(<span class="kw-2">&amp;</span>digits)<span class="question-mark">?</span>;
<a href=#120 id=120 data-nosnippet>120</a> assert_secret_length(secret.as_ref())<span class="question-mark">?</span>;
<a href=#121 id=121 data-nosnippet>121</a>
<a href=#122 id=122 data-nosnippet>122</a> <span class="comment">// NOTE: Unfortunate lack of issuer and account_name checks.
<a href=#123 id=123 data-nosnippet>123</a> // Will be fixed in 6.0 cause it would be breaking anyway.
<a href=#124 id=124 data-nosnippet>124</a>
<a href=#125 id=125 data-nosnippet>125</a> </span><span class="prelude-val">Ok</span>(())
<a href=#126 id=126 data-nosnippet>126</a> };
<a href=#127 id=127 data-nosnippet>127</a>
<a href=#128 id=128 data-nosnippet>128</a> <span class="kw">if let </span><span class="prelude-val">Err</span>(e) = validate() {
<a href=#129 id=129 data-nosnippet>129</a> <span class="attr">#[cfg(feature = <span class="string">"zeroize"</span>)]
<a href=#130 id=130 data-nosnippet>130</a> </span>zeroize::Zeroize::zeroize(<span class="kw-2">&amp;mut </span>secret);
<a href=#131 id=131 data-nosnippet>131</a>
<a href=#132 id=132 data-nosnippet>132</a> <span class="kw">return </span><span class="prelude-val">Err</span>(e);
<a href=#133 id=133 data-nosnippet>133</a> }
<a href=#134 id=134 data-nosnippet>134</a>
<a href=#135 id=135 data-nosnippet>135</a> <span class="prelude-val">Ok</span>(Rfc6238 {
<a href=#136 id=136 data-nosnippet>136</a> algorithm: Algorithm::SHA1,
<a href=#137 id=137 data-nosnippet>137</a> digits,
<a href=#138 id=138 data-nosnippet>138</a> skew: <span class="number">1</span>,
<a href=#139 id=139 data-nosnippet>139</a> step: <span class="number">30</span>,
<a href=#140 id=140 data-nosnippet>140</a> secret,
<a href=#141 id=141 data-nosnippet>141</a> issuer,
<a href=#142 id=142 data-nosnippet>142</a> account_name,
<a href=#143 id=143 data-nosnippet>143</a> })
<a href=#144 id=144 data-nosnippet>144</a> }
<a href=#145 id=145 data-nosnippet>145</a> <span class="attr">#[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#146 id=146 data-nosnippet>146</a> #[allow(unused_mut)]
<a href=#147 id=147 data-nosnippet>147</a> </span><span class="kw">pub fn </span>new(digits: usize, <span class="kw-2">mut </span>secret: Vec&lt;u8&gt;) -&gt; <span class="prelude-ty">Result</span>&lt;Rfc6238, Rfc6238Error&gt; {
<a href=#148 id=148 data-nosnippet>148</a> <span class="kw">let </span>validate = || {
<a href=#149 id=149 data-nosnippet>149</a> <span class="kw">crate</span>::rfc::assert_digits(<span class="kw-2">&amp;</span>digits)<span class="question-mark">?</span>;
<a href=#150 id=150 data-nosnippet>150</a> <span class="kw">crate</span>::rfc::assert_secret_length(secret.as_ref())<span class="question-mark">?</span>;
<a href=#151 id=151 data-nosnippet>151</a> <span class="prelude-val">Ok</span>(())
<a href=#152 id=152 data-nosnippet>152</a> };
<a href=#153 id=153 data-nosnippet>153</a>
<a href=#154 id=154 data-nosnippet>154</a> <span class="kw">if let </span><span class="prelude-val">Err</span>(e) = validate() {
<a href=#155 id=155 data-nosnippet>155</a> <span class="attr">#[cfg(feature = <span class="string">"zeroize"</span>)]
<a href=#156 id=156 data-nosnippet>156</a> </span>zeroize::Zeroize::zeroize(<span class="kw-2">&amp;mut </span>secret);
<a href=#157 id=157 data-nosnippet>157</a>
<a href=#158 id=158 data-nosnippet>158</a> <span class="kw">return </span><span class="prelude-val">Err</span>(e);
<a href=#159 id=159 data-nosnippet>159</a> }
<a href=#160 id=160 data-nosnippet>160</a>
<a href=#161 id=161 data-nosnippet>161</a> <span class="prelude-val">Ok</span>(Rfc6238 {
<a href=#162 id=162 data-nosnippet>162</a> algorithm: Algorithm::SHA1,
<a href=#163 id=163 data-nosnippet>163</a> digits,
<a href=#164 id=164 data-nosnippet>164</a> skew: <span class="number">1</span>,
<a href=#165 id=165 data-nosnippet>165</a> step: <span class="number">30</span>,
<a href=#166 id=166 data-nosnippet>166</a> secret,
<a href=#167 id=167 data-nosnippet>167</a> })
<a href=#168 id=168 data-nosnippet>168</a> }
<a href=#169 id=169 data-nosnippet>169</a>
<a href=#170 id=170 data-nosnippet>170</a> <span class="doccomment">/// Create an [rfc-6238](https://tools.ietf.org/html/rfc6238) compliant set of options that can be turned into a [TOTP](struct.TOTP.html),
<a href=#171 id=171 data-nosnippet>171</a> /// with a default value of 6 for `digits`, None `issuer` and an empty account.
<a href=#172 id=172 data-nosnippet>172</a> ///
<a href=#173 id=173 data-nosnippet>173</a> /// # Errors
<a href=#174 id=174 data-nosnippet>174</a> ///
<a href=#175 id=175 data-nosnippet>175</a> /// will return a [Rfc6238Error](enum.Rfc6238Error.html) when
<a href=#176 id=176 data-nosnippet>176</a> /// - `digits` is lower than 6 or higher than 8.
<a href=#177 id=177 data-nosnippet>177</a> /// - `secret` is smaller than 128 bits (16 characters).
<a href=#178 id=178 data-nosnippet>178</a> </span><span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#179 id=179 data-nosnippet>179</a> </span><span class="kw">pub fn </span>with_defaults(secret: Vec&lt;u8&gt;) -&gt; <span class="prelude-ty">Result</span>&lt;Rfc6238, Rfc6238Error&gt; {
<a href=#180 id=180 data-nosnippet>180</a> Rfc6238::new(<span class="number">6</span>, secret, <span class="prelude-val">Some</span>(<span class="string">""</span>.to_string()), <span class="string">""</span>.to_string())
<a href=#181 id=181 data-nosnippet>181</a> }
<a href=#182 id=182 data-nosnippet>182</a>
<a href=#183 id=183 data-nosnippet>183</a> <span class="attr">#[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#184 id=184 data-nosnippet>184</a> </span><span class="kw">pub fn </span>with_defaults(secret: Vec&lt;u8&gt;) -&gt; <span class="prelude-ty">Result</span>&lt;Rfc6238, Rfc6238Error&gt; {
<a href=#185 id=185 data-nosnippet>185</a> Rfc6238::new(<span class="number">6</span>, secret)
<a href=#186 id=186 data-nosnippet>186</a> }
<a href=#187 id=187 data-nosnippet>187</a>
<a href=#188 id=188 data-nosnippet>188</a> <span class="doccomment">/// Set the `digits`.
<a href=#189 id=189 data-nosnippet>189</a> </span><span class="kw">pub fn </span>digits(<span class="kw-2">&amp;mut </span><span class="self">self</span>, value: usize) -&gt; <span class="prelude-ty">Result</span>&lt;(), Rfc6238Error&gt; {
<a href=#190 id=190 data-nosnippet>190</a> assert_digits(<span class="kw-2">&amp;</span>value)<span class="question-mark">?</span>;
<a href=#191 id=191 data-nosnippet>191</a> <span class="self">self</span>.digits = value;
<a href=#192 id=192 data-nosnippet>192</a> <span class="prelude-val">Ok</span>(())
<a href=#193 id=193 data-nosnippet>193</a> }
<a href=#194 id=194 data-nosnippet>194</a>
<a href=#195 id=195 data-nosnippet>195</a> <span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#196 id=196 data-nosnippet>196</a> #[cfg_attr(docsrs, doc(cfg(feature = <span class="string">"otpauth"</span>)))]
<a href=#197 id=197 data-nosnippet>197</a> </span><span class="doccomment">/// Set the `issuer`.
<a href=#198 id=198 data-nosnippet>198</a> </span><span class="kw">pub fn </span>issuer(<span class="kw-2">&amp;mut </span><span class="self">self</span>, value: String) {
<a href=#199 id=199 data-nosnippet>199</a> <span class="self">self</span>.issuer = <span class="prelude-val">Some</span>(value);
<a href=#200 id=200 data-nosnippet>200</a> }
<a href=#201 id=201 data-nosnippet>201</a>
<a href=#202 id=202 data-nosnippet>202</a> <span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#203 id=203 data-nosnippet>203</a> #[cfg_attr(docsrs, doc(cfg(feature = <span class="string">"otpauth"</span>)))]
<a href=#204 id=204 data-nosnippet>204</a> </span><span class="doccomment">/// Set the `account_name`.
<a href=#205 id=205 data-nosnippet>205</a> </span><span class="kw">pub fn </span>account_name(<span class="kw-2">&amp;mut </span><span class="self">self</span>, value: String) {
<a href=#206 id=206 data-nosnippet>206</a> <span class="self">self</span>.account_name = value;
<a href=#207 id=207 data-nosnippet>207</a> }
<a href=#208 id=208 data-nosnippet>208</a>}
<a href=#209 id=209 data-nosnippet>209</a>
<a href=#210 id=210 data-nosnippet>210</a><span class="attr">#[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#211 id=211 data-nosnippet>211</a></span><span class="kw">impl </span>TryFrom&lt;Rfc6238&gt; <span class="kw">for </span>TOTP {
<a href=#212 id=212 data-nosnippet>212</a> <span class="kw">type </span>Error = TotpUrlError;
<a href=#213 id=213 data-nosnippet>213</a>
<a href=#214 id=214 data-nosnippet>214</a> <span class="doccomment">/// Try to create a [TOTP](struct.TOTP.html) from a [Rfc6238](struct.Rfc6238.html) config.
<a href=#215 id=215 data-nosnippet>215</a> </span><span class="kw">fn </span>try_from(<span class="kw-2">mut </span>rfc: Rfc6238) -&gt; <span class="prelude-ty">Result</span>&lt;<span class="self">Self</span>, <span class="self">Self</span>::Error&gt; {
<a href=#216 id=216 data-nosnippet>216</a> TOTP::new(
<a href=#217 id=217 data-nosnippet>217</a> rfc.algorithm,
<a href=#218 id=218 data-nosnippet>218</a> rfc.digits,
<a href=#219 id=219 data-nosnippet>219</a> rfc.skew,
<a href=#220 id=220 data-nosnippet>220</a> rfc.step,
<a href=#221 id=221 data-nosnippet>221</a> std::mem::take(<span class="kw-2">&amp;mut </span>rfc.secret),
<a href=#222 id=222 data-nosnippet>222</a> )
<a href=#223 id=223 data-nosnippet>223</a> }
<a href=#224 id=224 data-nosnippet>224</a>}
<a href=#225 id=225 data-nosnippet>225</a>
<a href=#226 id=226 data-nosnippet>226</a><span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#227 id=227 data-nosnippet>227</a></span><span class="kw">impl </span>TryFrom&lt;Rfc6238&gt; <span class="kw">for </span>TOTP {
<a href=#228 id=228 data-nosnippet>228</a> <span class="kw">type </span>Error = TotpUrlError;
<a href=#229 id=229 data-nosnippet>229</a>
<a href=#230 id=230 data-nosnippet>230</a> <span class="doccomment">/// Try to create a [TOTP](struct.TOTP.html) from a [Rfc6238](struct.Rfc6238.html) config.
<a href=#231 id=231 data-nosnippet>231</a> </span><span class="kw">fn </span>try_from(<span class="kw-2">mut </span>rfc: Rfc6238) -&gt; <span class="prelude-ty">Result</span>&lt;<span class="self">Self</span>, <span class="self">Self</span>::Error&gt; {
<a href=#232 id=232 data-nosnippet>232</a> TOTP::new(
<a href=#233 id=233 data-nosnippet>233</a> rfc.algorithm,
<a href=#234 id=234 data-nosnippet>234</a> rfc.digits,
<a href=#235 id=235 data-nosnippet>235</a> rfc.skew,
<a href=#236 id=236 data-nosnippet>236</a> rfc.step,
<a href=#237 id=237 data-nosnippet>237</a> std::mem::take(<span class="kw-2">&amp;mut </span>rfc.secret),
<a href=#238 id=238 data-nosnippet>238</a> std::mem::take(<span class="kw-2">&amp;mut </span>rfc.issuer),
<a href=#239 id=239 data-nosnippet>239</a> std::mem::take(<span class="kw-2">&amp;mut </span>rfc.account_name),
<a href=#240 id=240 data-nosnippet>240</a> )
<a href=#241 id=241 data-nosnippet>241</a> }
<a href=#242 id=242 data-nosnippet>242</a>}
<a href=#243 id=243 data-nosnippet>243</a>
<a href=#244 id=244 data-nosnippet>244</a><span class="attr">#[cfg(test)]
<a href=#245 id=245 data-nosnippet>245</a></span><span class="kw">mod </span>tests {
<a href=#246 id=246 data-nosnippet>246</a> <span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#247 id=247 data-nosnippet>247</a> </span><span class="kw">use </span><span class="kw">crate</span>::TotpUrlError;
<a href=#248 id=248 data-nosnippet>248</a>
<a href=#249 id=249 data-nosnippet>249</a> <span class="kw">use super</span>::{Rfc6238, TOTP};
<a href=#250 id=250 data-nosnippet>250</a>
<a href=#251 id=251 data-nosnippet>251</a> <span class="attr">#[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#252 id=252 data-nosnippet>252</a> </span><span class="kw">use </span><span class="kw">super</span>::Rfc6238Error;
<a href=#253 id=253 data-nosnippet>253</a>
<a href=#254 id=254 data-nosnippet>254</a> <span class="attr">#[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#255 id=255 data-nosnippet>255</a> </span><span class="kw">use </span><span class="kw">crate</span>::Secret;
<a href=#256 id=256 data-nosnippet>256</a>
<a href=#257 id=257 data-nosnippet>257</a> <span class="kw">const </span>GOOD_SECRET: <span class="kw-2">&amp;</span>str = <span class="string">"01234567890123456789"</span>;
<a href=#258 id=258 data-nosnippet>258</a> <span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#259 id=259 data-nosnippet>259</a> </span><span class="kw">const </span>ISSUER: <span class="prelude-ty">Option</span>&lt;<span class="kw-2">&amp;</span>str&gt; = <span class="prelude-val">None</span>;
<a href=#260 id=260 data-nosnippet>260</a> <span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#261 id=261 data-nosnippet>261</a> </span><span class="kw">const </span>ACCOUNT: <span class="kw-2">&amp;</span>str = <span class="string">"valid-account"</span>;
<a href=#262 id=262 data-nosnippet>262</a> <span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#263 id=263 data-nosnippet>263</a> </span><span class="kw">const </span>INVALID_ACCOUNT: <span class="kw-2">&amp;</span>str = <span class="string">":invalid-account"</span>;
<a href=#264 id=264 data-nosnippet>264</a>
<a href=#265 id=265 data-nosnippet>265</a> <span class="attr">#[test]
<a href=#266 id=266 data-nosnippet>266</a> #[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#267 id=267 data-nosnippet>267</a> </span><span class="kw">fn </span>new_rfc_digits() {
<a href=#268 id=268 data-nosnippet>268</a> <span class="kw">for </span>x <span class="kw">in </span><span class="number">0</span>..=<span class="number">20 </span>{
<a href=#269 id=269 data-nosnippet>269</a> <span class="kw">let </span>rfc = Rfc6238::new(x, GOOD_SECRET.into());
<a href=#270 id=270 data-nosnippet>270</a> <span class="kw">if </span>!(<span class="number">6</span>..=<span class="number">8</span>).contains(<span class="kw-2">&amp;</span>x) {
<a href=#271 id=271 data-nosnippet>271</a> <span class="macro">assert!</span>(rfc.is_err());
<a href=#272 id=272 data-nosnippet>272</a> <span class="macro">assert!</span>(<span class="macro">matches!</span>(rfc.unwrap_err(), Rfc6238Error::InvalidDigits(<span class="kw">_</span>)));
<a href=#273 id=273 data-nosnippet>273</a> } <span class="kw">else </span>{
<a href=#274 id=274 data-nosnippet>274</a> <span class="macro">assert!</span>(rfc.is_ok());
<a href=#275 id=275 data-nosnippet>275</a> }
<a href=#276 id=276 data-nosnippet>276</a> }
<a href=#277 id=277 data-nosnippet>277</a> }
<a href=#278 id=278 data-nosnippet>278</a>
<a href=#279 id=279 data-nosnippet>279</a> <span class="attr">#[test]
<a href=#280 id=280 data-nosnippet>280</a> #[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#281 id=281 data-nosnippet>281</a> </span><span class="kw">fn </span>new_rfc_secret() {
<a href=#282 id=282 data-nosnippet>282</a> <span class="kw">let </span><span class="kw-2">mut </span>secret = String::from(<span class="string">""</span>);
<a href=#283 id=283 data-nosnippet>283</a> <span class="kw">for _ in </span><span class="number">0</span>..=<span class="number">20 </span>{
<a href=#284 id=284 data-nosnippet>284</a> secret = <span class="macro">format!</span>(<span class="string">"{}{}"</span>, secret, <span class="string">"0"</span>);
<a href=#285 id=285 data-nosnippet>285</a> <span class="kw">let </span>rfc = Rfc6238::new(<span class="number">6</span>, secret.as_bytes().to_vec());
<a href=#286 id=286 data-nosnippet>286</a> <span class="kw">let </span>rfc_default = Rfc6238::with_defaults(secret.as_bytes().to_vec());
<a href=#287 id=287 data-nosnippet>287</a> <span class="kw">if </span>secret.len() &lt; <span class="number">16 </span>{
<a href=#288 id=288 data-nosnippet>288</a> <span class="macro">assert!</span>(rfc.is_err());
<a href=#289 id=289 data-nosnippet>289</a> <span class="macro">assert!</span>(<span class="macro">matches!</span>(rfc.unwrap_err(), Rfc6238Error::SecretTooSmall(<span class="kw">_</span>)));
<a href=#290 id=290 data-nosnippet>290</a> <span class="macro">assert!</span>(rfc_default.is_err());
<a href=#291 id=291 data-nosnippet>291</a> <span class="macro">assert!</span>(<span class="macro">matches!</span>(
<a href=#292 id=292 data-nosnippet>292</a> rfc_default.unwrap_err(),
<a href=#293 id=293 data-nosnippet>293</a> Rfc6238Error::SecretTooSmall(<span class="kw">_</span>)
<a href=#294 id=294 data-nosnippet>294</a> ));
<a href=#295 id=295 data-nosnippet>295</a> } <span class="kw">else </span>{
<a href=#296 id=296 data-nosnippet>296</a> <span class="macro">assert!</span>(rfc.is_ok());
<a href=#297 id=297 data-nosnippet>297</a> <span class="macro">assert!</span>(rfc_default.is_ok());
<a href=#298 id=298 data-nosnippet>298</a> }
<a href=#299 id=299 data-nosnippet>299</a> }
<a href=#300 id=300 data-nosnippet>300</a> }
<a href=#301 id=301 data-nosnippet>301</a>
<a href=#302 id=302 data-nosnippet>302</a> <span class="attr">#[test]
<a href=#303 id=303 data-nosnippet>303</a> #[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#304 id=304 data-nosnippet>304</a> </span><span class="kw">fn </span>rfc_to_totp_ok() {
<a href=#305 id=305 data-nosnippet>305</a> <span class="kw">let </span>rfc = Rfc6238::new(<span class="number">8</span>, GOOD_SECRET.into()).unwrap();
<a href=#306 id=306 data-nosnippet>306</a> <span class="kw">let </span>totp = TOTP::try_from(rfc);
<a href=#307 id=307 data-nosnippet>307</a> <span class="macro">assert!</span>(totp.is_ok());
<a href=#308 id=308 data-nosnippet>308</a> <span class="kw">let </span>otp = totp.unwrap();
<a href=#309 id=309 data-nosnippet>309</a> <span class="macro">assert_eq!</span>(<span class="kw-2">&amp;</span>otp.secret, GOOD_SECRET.as_bytes());
<a href=#310 id=310 data-nosnippet>310</a> <span class="macro">assert_eq!</span>(otp.algorithm, <span class="kw">crate</span>::Algorithm::SHA1);
<a href=#311 id=311 data-nosnippet>311</a> <span class="macro">assert_eq!</span>(otp.digits, <span class="number">8</span>);
<a href=#312 id=312 data-nosnippet>312</a> <span class="macro">assert_eq!</span>(otp.skew, <span class="number">1</span>);
<a href=#313 id=313 data-nosnippet>313</a> <span class="macro">assert_eq!</span>(otp.step, <span class="number">30</span>)
<a href=#314 id=314 data-nosnippet>314</a> }
<a href=#315 id=315 data-nosnippet>315</a>
<a href=#316 id=316 data-nosnippet>316</a> <span class="attr">#[test]
<a href=#317 id=317 data-nosnippet>317</a> #[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#318 id=318 data-nosnippet>318</a> </span><span class="kw">fn </span>rfc_to_totp_ok_2() {
<a href=#319 id=319 data-nosnippet>319</a> <span class="kw">let </span>rfc = Rfc6238::with_defaults(
<a href=#320 id=320 data-nosnippet>320</a> Secret::Encoded(<span class="string">"KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ"</span>.to_string())
<a href=#321 id=321 data-nosnippet>321</a> .to_bytes()
<a href=#322 id=322 data-nosnippet>322</a> .unwrap(),
<a href=#323 id=323 data-nosnippet>323</a> )
<a href=#324 id=324 data-nosnippet>324</a> .unwrap();
<a href=#325 id=325 data-nosnippet>325</a> <span class="kw">let </span>totp = TOTP::try_from(rfc);
<a href=#326 id=326 data-nosnippet>326</a> <span class="macro">assert!</span>(totp.is_ok());
<a href=#327 id=327 data-nosnippet>327</a> <span class="kw">let </span>otp = totp.unwrap();
<a href=#328 id=328 data-nosnippet>328</a> <span class="macro">assert_eq!</span>(otp.algorithm, <span class="kw">crate</span>::Algorithm::SHA1);
<a href=#329 id=329 data-nosnippet>329</a> <span class="macro">assert_eq!</span>(otp.digits, <span class="number">6</span>);
<a href=#330 id=330 data-nosnippet>330</a> <span class="macro">assert_eq!</span>(otp.skew, <span class="number">1</span>);
<a href=#331 id=331 data-nosnippet>331</a> <span class="macro">assert_eq!</span>(otp.step, <span class="number">30</span>)
<a href=#332 id=332 data-nosnippet>332</a> }
<a href=#333 id=333 data-nosnippet>333</a>
<a href=#334 id=334 data-nosnippet>334</a> <span class="attr">#[test]
<a href=#335 id=335 data-nosnippet>335</a> #[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#336 id=336 data-nosnippet>336</a> </span><span class="kw">fn </span>rfc_to_totp_fail() {
<a href=#337 id=337 data-nosnippet>337</a> <span class="kw">let </span>rfc = Rfc6238::new(
<a href=#338 id=338 data-nosnippet>338</a> <span class="number">8</span>,
<a href=#339 id=339 data-nosnippet>339</a> GOOD_SECRET.as_bytes().to_vec(),
<a href=#340 id=340 data-nosnippet>340</a> ISSUER.map(str::to_string),
<a href=#341 id=341 data-nosnippet>341</a> INVALID_ACCOUNT.to_string(),
<a href=#342 id=342 data-nosnippet>342</a> )
<a href=#343 id=343 data-nosnippet>343</a> .unwrap();
<a href=#344 id=344 data-nosnippet>344</a> <span class="kw">let </span>totp = TOTP::try_from(rfc);
<a href=#345 id=345 data-nosnippet>345</a> <span class="macro">assert!</span>(totp.is_err());
<a href=#346 id=346 data-nosnippet>346</a> <span class="macro">assert!</span>(<span class="macro">matches!</span>(totp.unwrap_err(), TotpUrlError::AccountName(<span class="kw">_</span>)))
<a href=#347 id=347 data-nosnippet>347</a> }
<a href=#348 id=348 data-nosnippet>348</a>
<a href=#349 id=349 data-nosnippet>349</a> <span class="attr">#[test]
<a href=#350 id=350 data-nosnippet>350</a> #[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#351 id=351 data-nosnippet>351</a> </span><span class="kw">fn </span>rfc_to_totp_ok() {
<a href=#352 id=352 data-nosnippet>352</a> <span class="kw">let </span>rfc = Rfc6238::new(
<a href=#353 id=353 data-nosnippet>353</a> <span class="number">8</span>,
<a href=#354 id=354 data-nosnippet>354</a> GOOD_SECRET.as_bytes().to_vec(),
<a href=#355 id=355 data-nosnippet>355</a> ISSUER.map(str::to_string),
<a href=#356 id=356 data-nosnippet>356</a> ACCOUNT.to_string(),
<a href=#357 id=357 data-nosnippet>357</a> )
<a href=#358 id=358 data-nosnippet>358</a> .unwrap();
<a href=#359 id=359 data-nosnippet>359</a> <span class="kw">let </span>totp = TOTP::try_from(rfc);
<a href=#360 id=360 data-nosnippet>360</a> <span class="macro">assert!</span>(totp.is_ok());
<a href=#361 id=361 data-nosnippet>361</a> }
<a href=#362 id=362 data-nosnippet>362</a>
<a href=#363 id=363 data-nosnippet>363</a> <span class="attr">#[test]
<a href=#364 id=364 data-nosnippet>364</a> #[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#365 id=365 data-nosnippet>365</a> </span><span class="kw">fn </span>rfc_with_default_set_values() {
<a href=#366 id=366 data-nosnippet>366</a> <span class="kw">let </span><span class="kw-2">mut </span>rfc = Rfc6238::with_defaults(GOOD_SECRET.as_bytes().to_vec()).unwrap();
<a href=#367 id=367 data-nosnippet>367</a> <span class="kw">let </span>ok = rfc.digits(<span class="number">8</span>);
<a href=#368 id=368 data-nosnippet>368</a> <span class="macro">assert!</span>(ok.is_ok());
<a href=#369 id=369 data-nosnippet>369</a> <span class="macro">assert_eq!</span>(rfc.account_name, <span class="string">""</span>);
<a href=#370 id=370 data-nosnippet>370</a> <span class="macro">assert_eq!</span>(rfc.issuer, <span class="prelude-val">Some</span>(<span class="string">""</span>.to_string()));
<a href=#371 id=371 data-nosnippet>371</a> rfc.issuer(<span class="string">"Github"</span>.to_string());
<a href=#372 id=372 data-nosnippet>372</a> rfc.account_name(<span class="string">"constantoine"</span>.to_string());
<a href=#373 id=373 data-nosnippet>373</a> <span class="macro">assert_eq!</span>(rfc.account_name, <span class="string">"constantoine"</span>);
<a href=#374 id=374 data-nosnippet>374</a> <span class="macro">assert_eq!</span>(rfc.issuer, <span class="prelude-val">Some</span>(<span class="string">"Github"</span>.to_string()));
<a href=#375 id=375 data-nosnippet>375</a> <span class="macro">assert_eq!</span>(rfc.digits, <span class="number">8</span>)
<a href=#376 id=376 data-nosnippet>376</a> }
<a href=#377 id=377 data-nosnippet>377</a>
<a href=#378 id=378 data-nosnippet>378</a> <span class="attr">#[test]
<a href=#379 id=379 data-nosnippet>379</a> #[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#380 id=380 data-nosnippet>380</a> </span><span class="kw">fn </span>rfc_with_default_set_values() {
<a href=#381 id=381 data-nosnippet>381</a> <span class="kw">let </span><span class="kw-2">mut </span>rfc = Rfc6238::with_defaults(GOOD_SECRET.as_bytes().to_vec()).unwrap();
<a href=#382 id=382 data-nosnippet>382</a> <span class="kw">let </span>fail = rfc.digits(<span class="number">4</span>);
<a href=#383 id=383 data-nosnippet>383</a> <span class="macro">assert!</span>(fail.is_err());
<a href=#384 id=384 data-nosnippet>384</a> <span class="macro">assert!</span>(<span class="macro">matches!</span>(fail.unwrap_err(), Rfc6238Error::InvalidDigits(<span class="kw">_</span>)));
<a href=#385 id=385 data-nosnippet>385</a> <span class="macro">assert_eq!</span>(rfc.digits, <span class="number">6</span>);
<a href=#386 id=386 data-nosnippet>386</a> <span class="kw">let </span>ok = rfc.digits(<span class="number">8</span>);
<a href=#387 id=387 data-nosnippet>387</a> <span class="macro">assert!</span>(ok.is_ok());
<a href=#388 id=388 data-nosnippet>388</a> <span class="macro">assert_eq!</span>(rfc.digits, <span class="number">8</span>)
<a href=#389 id=389 data-nosnippet>389</a> }
<a href=#390 id=390 data-nosnippet>390</a>
<a href=#391 id=391 data-nosnippet>391</a> <span class="attr">#[test]
<a href=#392 id=392 data-nosnippet>392</a> #[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#393 id=393 data-nosnippet>393</a> </span><span class="kw">fn </span>digits_error() {
<a href=#394 id=394 data-nosnippet>394</a> <span class="kw">let </span>error = <span class="kw">crate</span>::Rfc6238Error::InvalidDigits(<span class="number">9</span>);
<a href=#395 id=395 data-nosnippet>395</a> <span class="macro">assert_eq!</span>(
<a href=#396 id=396 data-nosnippet>396</a> error.to_string(),
<a href=#397 id=397 data-nosnippet>397</a> <span class="string">"Implementations MUST extract a 6-digit code at a minimum and possibly 7 and 8-digit code. 9 digits is not allowed"</span>.to_string()
<a href=#398 id=398 data-nosnippet>398</a> )
<a href=#399 id=399 data-nosnippet>399</a> }
<a href=#400 id=400 data-nosnippet>400</a>
<a href=#401 id=401 data-nosnippet>401</a> <span class="attr">#[test]
<a href=#402 id=402 data-nosnippet>402</a> #[cfg(not(feature = <span class="string">"otpauth"</span>))]
<a href=#403 id=403 data-nosnippet>403</a> </span><span class="kw">fn </span>secret_length_error() {
<a href=#404 id=404 data-nosnippet>404</a> <span class="kw">let </span>error = Rfc6238Error::SecretTooSmall(<span class="number">120</span>);
<a href=#405 id=405 data-nosnippet>405</a> <span class="macro">assert_eq!</span>(
<a href=#406 id=406 data-nosnippet>406</a> error.to_string(),
<a href=#407 id=407 data-nosnippet>407</a> <span class="string">"The length of the shared secret MUST be at least 128 bits. 120 bits is not enough"
<a href=#408 id=408 data-nosnippet>408</a> </span>.to_string()
<a href=#409 id=409 data-nosnippet>409</a> )
<a href=#410 id=410 data-nosnippet>410</a> }
<a href=#411 id=411 data-nosnippet>411</a>}
</code></pre></div></section></main></body></html>

View File

@@ -0,0 +1,283 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `C:\Users\szend\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\totp-rs-5.7.1\src\secret.rs`."><title>secret.rs - source</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../static.files/rustdoc-ca0dd0c4.css"><meta name="rustdoc-vars" data-root-path="../../" data-static-root-path="../../static.files/" data-current-crate="totp_rs" data-themes="" data-resource-suffix="" data-rustdoc-version="1.93.1 (01f6ddf75 2026-02-11)" data-channel="1.93.1" data-search-js="search-9e2438ea.js" data-stringdex-js="stringdex-a3946164.js" data-settings-js="settings-c38705f0.js" ><script src="../../static.files/storage-e2aeef58.js"></script><script defer src="../../static.files/src-script-813739b1.js"></script><script defer src="../../src-files.js"></script><script defer src="../../static.files/main-a410ff4d.js"></script><noscript><link rel="stylesheet" href="../../static.files/noscript-263c88ec.css"></noscript><link rel="alternate icon" type="image/png" href="../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../static.files/favicon-044be391.svg"></head><body class="rustdoc src"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="src-sidebar-title"><h2>Files</h2></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><section id="main-content" class="content"><div class="main-heading"><h1><div class="sub-heading">totp_rs/</div>secret.rs</h1><rustdoc-toolbar></rustdoc-toolbar></div><div class="example-wrap digits-3"><pre class="rust"><code><a href=#1 id=1 data-nosnippet>1</a><span class="doccomment">//! Representation of a secret either a "raw" \[u8\] or "base 32" encoded String
<a href=#2 id=2 data-nosnippet>2</a>//!
<a href=#3 id=3 data-nosnippet>3</a>//! # Examples
<a href=#4 id=4 data-nosnippet>4</a>//!
<a href=#5 id=5 data-nosnippet>5</a>//! - Create a TOTP from a "raw" secret
<a href=#6 id=6 data-nosnippet>6</a>//! ```
<a href=#7 id=7 data-nosnippet>7</a>//! # #[cfg(not(feature = "otpauth"))] {
<a href=#8 id=8 data-nosnippet>8</a>//! use totp_rs::{Secret, TOTP, Algorithm};
<a href=#9 id=9 data-nosnippet>9</a>//!
<a href=#10 id=10 data-nosnippet>10</a>//! let secret = [
<a href=#11 id=11 data-nosnippet>11</a>//! 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x2d, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x73, 0x65,
<a href=#12 id=12 data-nosnippet>12</a>//! 0x63, 0x72, 0x65, 0x74, 0x2d, 0x31, 0x32, 0x33,
<a href=#13 id=13 data-nosnippet>13</a>//! ];
<a href=#14 id=14 data-nosnippet>14</a>//! let secret_raw = Secret::Raw(secret.to_vec());
<a href=#15 id=15 data-nosnippet>15</a>//! let totp_raw = TOTP::new(
<a href=#16 id=16 data-nosnippet>16</a>//! Algorithm::SHA1,
<a href=#17 id=17 data-nosnippet>17</a>//! 6,
<a href=#18 id=18 data-nosnippet>18</a>//! 1,
<a href=#19 id=19 data-nosnippet>19</a>//! 30,
<a href=#20 id=20 data-nosnippet>20</a>//! secret_raw.to_bytes().unwrap(),
<a href=#21 id=21 data-nosnippet>21</a>//! ).unwrap();
<a href=#22 id=22 data-nosnippet>22</a>//!
<a href=#23 id=23 data-nosnippet>23</a>//! println!("code from raw secret:\t{}", totp_raw.generate_current().unwrap());
<a href=#24 id=24 data-nosnippet>24</a>//! # }
<a href=#25 id=25 data-nosnippet>25</a>//! ```
<a href=#26 id=26 data-nosnippet>26</a>//!
<a href=#27 id=27 data-nosnippet>27</a>//! - Create a TOTP from a base32 encoded secret
<a href=#28 id=28 data-nosnippet>28</a>//! ```
<a href=#29 id=29 data-nosnippet>29</a>//! # #[cfg(not(feature = "otpauth"))] {
<a href=#30 id=30 data-nosnippet>30</a>//! use totp_rs::{Secret, TOTP, Algorithm};
<a href=#31 id=31 data-nosnippet>31</a>//!
<a href=#32 id=32 data-nosnippet>32</a>//! let secret_b32 = Secret::Encoded(String::from("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG"));
<a href=#33 id=33 data-nosnippet>33</a>//! let totp_b32 = TOTP::new(
<a href=#34 id=34 data-nosnippet>34</a>//! Algorithm::SHA1,
<a href=#35 id=35 data-nosnippet>35</a>//! 6,
<a href=#36 id=36 data-nosnippet>36</a>//! 1,
<a href=#37 id=37 data-nosnippet>37</a>//! 30,
<a href=#38 id=38 data-nosnippet>38</a>//! secret_b32.to_bytes().unwrap(),
<a href=#39 id=39 data-nosnippet>39</a>//! ).unwrap();
<a href=#40 id=40 data-nosnippet>40</a>//!
<a href=#41 id=41 data-nosnippet>41</a>//! println!("code from base32:\t{}", totp_b32.generate_current().unwrap());
<a href=#42 id=42 data-nosnippet>42</a>//! # }
<a href=#43 id=43 data-nosnippet>43</a>//!
<a href=#44 id=44 data-nosnippet>44</a>//! ```
<a href=#45 id=45 data-nosnippet>45</a>//! - Create a TOTP from a Generated Secret
<a href=#46 id=46 data-nosnippet>46</a>//! ```
<a href=#47 id=47 data-nosnippet>47</a>//! # #[cfg(all(feature = "gen_secret", not(feature = "otpauth")))] {
<a href=#48 id=48 data-nosnippet>48</a>//! use totp_rs::{Secret, TOTP, Algorithm};
<a href=#49 id=49 data-nosnippet>49</a>//!
<a href=#50 id=50 data-nosnippet>50</a>//! let secret_b32 = Secret::default();
<a href=#51 id=51 data-nosnippet>51</a>//! let totp_b32 = TOTP::new(
<a href=#52 id=52 data-nosnippet>52</a>//! Algorithm::SHA1,
<a href=#53 id=53 data-nosnippet>53</a>//! 6,
<a href=#54 id=54 data-nosnippet>54</a>//! 1,
<a href=#55 id=55 data-nosnippet>55</a>//! 30,
<a href=#56 id=56 data-nosnippet>56</a>//! secret_b32.to_bytes().unwrap(),
<a href=#57 id=57 data-nosnippet>57</a>//! ).unwrap();
<a href=#58 id=58 data-nosnippet>58</a>//!
<a href=#59 id=59 data-nosnippet>59</a>//! println!("code from base32:\t{}", totp_b32.generate_current().unwrap());
<a href=#60 id=60 data-nosnippet>60</a>//! # }
<a href=#61 id=61 data-nosnippet>61</a>//! ```
<a href=#62 id=62 data-nosnippet>62</a>//! - Create a TOTP from a Generated Secret 2
<a href=#63 id=63 data-nosnippet>63</a>//! ```
<a href=#64 id=64 data-nosnippet>64</a>//! # #[cfg(all(feature = "gen_secret", not(feature = "otpauth")))] {
<a href=#65 id=65 data-nosnippet>65</a>//! use totp_rs::{Secret, TOTP, Algorithm};
<a href=#66 id=66 data-nosnippet>66</a>//!
<a href=#67 id=67 data-nosnippet>67</a>//! let secret_b32 = Secret::generate_secret();
<a href=#68 id=68 data-nosnippet>68</a>//! let totp_b32 = TOTP::new(
<a href=#69 id=69 data-nosnippet>69</a>//! Algorithm::SHA1,
<a href=#70 id=70 data-nosnippet>70</a>//! 6,
<a href=#71 id=71 data-nosnippet>71</a>//! 1,
<a href=#72 id=72 data-nosnippet>72</a>//! 30,
<a href=#73 id=73 data-nosnippet>73</a>//! secret_b32.to_bytes().unwrap(),
<a href=#74 id=74 data-nosnippet>74</a>//! ).unwrap();
<a href=#75 id=75 data-nosnippet>75</a>//!
<a href=#76 id=76 data-nosnippet>76</a>//! println!("code from base32:\t{}", totp_b32.generate_current().unwrap());
<a href=#77 id=77 data-nosnippet>77</a>//! # }
<a href=#78 id=78 data-nosnippet>78</a>//! ```
<a href=#79 id=79 data-nosnippet>79</a>
<a href=#80 id=80 data-nosnippet>80</a></span><span class="kw">use </span>base32::{<span class="self">self</span>, Alphabet};
<a href=#81 id=81 data-nosnippet>81</a>
<a href=#82 id=82 data-nosnippet>82</a><span class="kw">use </span>constant_time_eq::constant_time_eq;
<a href=#83 id=83 data-nosnippet>83</a>
<a href=#84 id=84 data-nosnippet>84</a><span class="doccomment">/// Different ways secret parsing failed.
<a href=#85 id=85 data-nosnippet>85</a></span><span class="attr">#[derive(Debug, Clone, PartialEq, Eq)]
<a href=#86 id=86 data-nosnippet>86</a></span><span class="kw">pub enum </span>SecretParseError {
<a href=#87 id=87 data-nosnippet>87</a> <span class="doccomment">/// Invalid base32 input.
<a href=#88 id=88 data-nosnippet>88</a> </span>ParseBase32,
<a href=#89 id=89 data-nosnippet>89</a>}
<a href=#90 id=90 data-nosnippet>90</a>
<a href=#91 id=91 data-nosnippet>91</a><span class="kw">impl </span>std::error::Error <span class="kw">for </span>SecretParseError {}
<a href=#92 id=92 data-nosnippet>92</a>
<a href=#93 id=93 data-nosnippet>93</a><span class="kw">impl </span>std::fmt::Display <span class="kw">for </span>SecretParseError {
<a href=#94 id=94 data-nosnippet>94</a> <span class="kw">fn </span>fmt(<span class="kw-2">&amp;</span><span class="self">self</span>, f: <span class="kw-2">&amp;mut </span>std::fmt::Formatter&lt;<span class="lifetime">'_</span>&gt;) -&gt; std::fmt::Result {
<a href=#95 id=95 data-nosnippet>95</a> <span class="kw">match </span><span class="self">self </span>{
<a href=#96 id=96 data-nosnippet>96</a> SecretParseError::ParseBase32 =&gt; <span class="macro">write!</span>(f, <span class="string">"Could not decode base32 secret."</span>),
<a href=#97 id=97 data-nosnippet>97</a> }
<a href=#98 id=98 data-nosnippet>98</a> }
<a href=#99 id=99 data-nosnippet>99</a>}
<a href=#100 id=100 data-nosnippet>100</a>
<a href=#101 id=101 data-nosnippet>101</a><span class="kw">impl </span>std::error::Error <span class="kw">for </span>Secret {}
<a href=#102 id=102 data-nosnippet>102</a>
<a href=#103 id=103 data-nosnippet>103</a><span class="doccomment">/// Shared secret between client and server to validate token against/generate token from.
<a href=#104 id=104 data-nosnippet>104</a></span><span class="attr">#[derive(Debug, Clone, Eq)]
<a href=#105 id=105 data-nosnippet>105</a>#[cfg_attr(feature = <span class="string">"zeroize"</span>, derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
<a href=#106 id=106 data-nosnippet>106</a></span><span class="kw">pub enum </span>Secret {
<a href=#107 id=107 data-nosnippet>107</a> <span class="doccomment">/// Non-encoded "raw" secret.
<a href=#108 id=108 data-nosnippet>108</a> </span>Raw(Vec&lt;u8&gt;),
<a href=#109 id=109 data-nosnippet>109</a> <span class="doccomment">/// Base32 encoded secret.
<a href=#110 id=110 data-nosnippet>110</a> </span>Encoded(String),
<a href=#111 id=111 data-nosnippet>111</a>}
<a href=#112 id=112 data-nosnippet>112</a>
<a href=#113 id=113 data-nosnippet>113</a><span class="kw">impl </span>PartialEq <span class="kw">for </span>Secret {
<a href=#114 id=114 data-nosnippet>114</a> <span class="doccomment">/// Will check that to_bytes() returns the same.
<a href=#115 id=115 data-nosnippet>115</a> /// One secret can be Raw, and the other Encoded.
<a href=#116 id=116 data-nosnippet>116</a> </span><span class="kw">fn </span>eq(<span class="kw-2">&amp;</span><span class="self">self</span>, other: <span class="kw-2">&amp;</span><span class="self">Self</span>) -&gt; bool {
<a href=#117 id=117 data-nosnippet>117</a> constant_time_eq(<span class="kw-2">&amp;</span><span class="self">self</span>.to_bytes().unwrap(), <span class="kw-2">&amp;</span>other.to_bytes().unwrap())
<a href=#118 id=118 data-nosnippet>118</a> }
<a href=#119 id=119 data-nosnippet>119</a>}
<a href=#120 id=120 data-nosnippet>120</a>
<a href=#121 id=121 data-nosnippet>121</a><span class="attr">#[cfg(feature = <span class="string">"gen_secret"</span>)]
<a href=#122 id=122 data-nosnippet>122</a>#[cfg_attr(docsrs, doc(cfg(feature = <span class="string">"gen_secret"</span>)))]
<a href=#123 id=123 data-nosnippet>123</a></span><span class="kw">impl </span>Default <span class="kw">for </span>Secret {
<a href=#124 id=124 data-nosnippet>124</a> <span class="kw">fn </span>default() -&gt; <span class="self">Self </span>{
<a href=#125 id=125 data-nosnippet>125</a> Secret::generate_secret()
<a href=#126 id=126 data-nosnippet>126</a> }
<a href=#127 id=127 data-nosnippet>127</a>}
<a href=#128 id=128 data-nosnippet>128</a>
<a href=#129 id=129 data-nosnippet>129</a><span class="kw">impl </span>Secret {
<a href=#130 id=130 data-nosnippet>130</a> <span class="doccomment">/// Get the inner String value as a Vec of bytes.
<a href=#131 id=131 data-nosnippet>131</a> </span><span class="kw">pub fn </span>to_bytes(<span class="kw-2">&amp;</span><span class="self">self</span>) -&gt; <span class="prelude-ty">Result</span>&lt;Vec&lt;u8&gt;, SecretParseError&gt; {
<a href=#132 id=132 data-nosnippet>132</a> <span class="kw">match </span><span class="self">self </span>{
<a href=#133 id=133 data-nosnippet>133</a> Secret::Raw(s) =&gt; <span class="prelude-val">Ok</span>(s.to_vec()),
<a href=#134 id=134 data-nosnippet>134</a> Secret::Encoded(s) =&gt; <span class="kw">match </span>base32::decode(Alphabet::Rfc4648 { padding: <span class="bool-val">false </span>}, s) {
<a href=#135 id=135 data-nosnippet>135</a> <span class="prelude-val">Some</span>(bytes) =&gt; <span class="prelude-val">Ok</span>(bytes),
<a href=#136 id=136 data-nosnippet>136</a> <span class="prelude-val">None </span>=&gt; <span class="prelude-val">Err</span>(SecretParseError::ParseBase32),
<a href=#137 id=137 data-nosnippet>137</a> },
<a href=#138 id=138 data-nosnippet>138</a> }
<a href=#139 id=139 data-nosnippet>139</a> }
<a href=#140 id=140 data-nosnippet>140</a>
<a href=#141 id=141 data-nosnippet>141</a> <span class="doccomment">/// Try to transform a `Secret::Encoded` into a `Secret::Raw`
<a href=#142 id=142 data-nosnippet>142</a> </span><span class="kw">pub fn </span>to_raw(<span class="kw-2">&amp;</span><span class="self">self</span>) -&gt; <span class="prelude-ty">Result</span>&lt;<span class="self">Self</span>, SecretParseError&gt; {
<a href=#143 id=143 data-nosnippet>143</a> <span class="kw">match </span><span class="self">self </span>{
<a href=#144 id=144 data-nosnippet>144</a> Secret::Raw(<span class="kw">_</span>) =&gt; <span class="prelude-val">Ok</span>(<span class="self">self</span>.clone()),
<a href=#145 id=145 data-nosnippet>145</a> Secret::Encoded(s) =&gt; <span class="kw">match </span>base32::decode(Alphabet::Rfc4648 { padding: <span class="bool-val">false </span>}, s) {
<a href=#146 id=146 data-nosnippet>146</a> <span class="prelude-val">Some</span>(buf) =&gt; <span class="prelude-val">Ok</span>(Secret::Raw(buf)),
<a href=#147 id=147 data-nosnippet>147</a> <span class="prelude-val">None </span>=&gt; <span class="prelude-val">Err</span>(SecretParseError::ParseBase32),
<a href=#148 id=148 data-nosnippet>148</a> },
<a href=#149 id=149 data-nosnippet>149</a> }
<a href=#150 id=150 data-nosnippet>150</a> }
<a href=#151 id=151 data-nosnippet>151</a>
<a href=#152 id=152 data-nosnippet>152</a> <span class="doccomment">/// Try to transforms a `Secret::Raw` into a `Secret::Encoded`.
<a href=#153 id=153 data-nosnippet>153</a> </span><span class="kw">pub fn </span>to_encoded(<span class="kw-2">&amp;</span><span class="self">self</span>) -&gt; <span class="self">Self </span>{
<a href=#154 id=154 data-nosnippet>154</a> <span class="kw">match </span><span class="self">self </span>{
<a href=#155 id=155 data-nosnippet>155</a> Secret::Raw(s) =&gt; {
<a href=#156 id=156 data-nosnippet>156</a> Secret::Encoded(base32::encode(Alphabet::Rfc4648 { padding: <span class="bool-val">false </span>}, s))
<a href=#157 id=157 data-nosnippet>157</a> }
<a href=#158 id=158 data-nosnippet>158</a> Secret::Encoded(<span class="kw">_</span>) =&gt; <span class="self">self</span>.clone(),
<a href=#159 id=159 data-nosnippet>159</a> }
<a href=#160 id=160 data-nosnippet>160</a> }
<a href=#161 id=161 data-nosnippet>161</a>
<a href=#162 id=162 data-nosnippet>162</a> <span class="doccomment">/// Generate a CSPRNG binary value of 160 bits,
<a href=#163 id=163 data-nosnippet>163</a> /// the recomended size from [rfc-4226](https://www.rfc-editor.org/rfc/rfc4226#section-4).
<a href=#164 id=164 data-nosnippet>164</a> ///
<a href=#165 id=165 data-nosnippet>165</a> /// &gt; The length of the shared secret MUST be at least 128 bits.
<a href=#166 id=166 data-nosnippet>166</a> /// &gt; This document RECOMMENDs a shared secret length of 160 bits.
<a href=#167 id=167 data-nosnippet>167</a> ///
<a href=#168 id=168 data-nosnippet>168</a> /// ⚠️ The generated secret is not guaranteed to be a valid UTF-8 sequence.
<a href=#169 id=169 data-nosnippet>169</a> </span><span class="attr">#[cfg(feature = <span class="string">"gen_secret"</span>)]
<a href=#170 id=170 data-nosnippet>170</a> #[cfg_attr(docsrs, doc(cfg(feature = <span class="string">"gen_secret"</span>)))]
<a href=#171 id=171 data-nosnippet>171</a> </span><span class="kw">pub fn </span>generate_secret() -&gt; Secret {
<a href=#172 id=172 data-nosnippet>172</a> <span class="kw">use </span>rand::Rng;
<a href=#173 id=173 data-nosnippet>173</a>
<a href=#174 id=174 data-nosnippet>174</a> <span class="kw">let </span><span class="kw-2">mut </span>rng = rand::rng();
<a href=#175 id=175 data-nosnippet>175</a> <span class="kw">let </span><span class="kw-2">mut </span>secret: [u8; <span class="number">20</span>] = Default::default();
<a href=#176 id=176 data-nosnippet>176</a> rng.fill(<span class="kw-2">&amp;mut </span>secret[..]);
<a href=#177 id=177 data-nosnippet>177</a> Secret::Raw(secret.to_vec())
<a href=#178 id=178 data-nosnippet>178</a> }
<a href=#179 id=179 data-nosnippet>179</a>}
<a href=#180 id=180 data-nosnippet>180</a>
<a href=#181 id=181 data-nosnippet>181</a><span class="kw">impl </span>std::fmt::Display <span class="kw">for </span>Secret {
<a href=#182 id=182 data-nosnippet>182</a> <span class="kw">fn </span>fmt(<span class="kw-2">&amp;</span><span class="self">self</span>, f: <span class="kw-2">&amp;mut </span>std::fmt::Formatter&lt;<span class="lifetime">'_</span>&gt;) -&gt; std::fmt::Result {
<a href=#183 id=183 data-nosnippet>183</a> <span class="kw">match </span><span class="self">self </span>{
<a href=#184 id=184 data-nosnippet>184</a> Secret::Raw(bytes) =&gt; {
<a href=#185 id=185 data-nosnippet>185</a> <span class="kw">for </span>b <span class="kw">in </span>bytes {
<a href=#186 id=186 data-nosnippet>186</a> <span class="macro">write!</span>(f, <span class="string">"{:02x}"</span>, b)<span class="question-mark">?</span>;
<a href=#187 id=187 data-nosnippet>187</a> }
<a href=#188 id=188 data-nosnippet>188</a> <span class="prelude-val">Ok</span>(())
<a href=#189 id=189 data-nosnippet>189</a> }
<a href=#190 id=190 data-nosnippet>190</a> Secret::Encoded(s) =&gt; <span class="macro">write!</span>(f, <span class="string">"{}"</span>, s),
<a href=#191 id=191 data-nosnippet>191</a> }
<a href=#192 id=192 data-nosnippet>192</a> }
<a href=#193 id=193 data-nosnippet>193</a>}
<a href=#194 id=194 data-nosnippet>194</a>
<a href=#195 id=195 data-nosnippet>195</a><span class="attr">#[cfg(test)]
<a href=#196 id=196 data-nosnippet>196</a></span><span class="kw">mod </span>tests {
<a href=#197 id=197 data-nosnippet>197</a> <span class="kw">use </span><span class="kw">super</span>::Secret;
<a href=#198 id=198 data-nosnippet>198</a>
<a href=#199 id=199 data-nosnippet>199</a> <span class="kw">const </span>BASE32: <span class="kw-2">&amp;</span>str = <span class="string">"OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG"</span>;
<a href=#200 id=200 data-nosnippet>200</a> <span class="kw">const </span>BYTES: [u8; <span class="number">23</span>] = [
<a href=#201 id=201 data-nosnippet>201</a> <span class="number">0x70</span>, <span class="number">0x6c</span>, <span class="number">0x61</span>, <span class="number">0x69</span>, <span class="number">0x6e</span>, <span class="number">0x2d</span>, <span class="number">0x73</span>, <span class="number">0x74</span>, <span class="number">0x72</span>, <span class="number">0x69</span>, <span class="number">0x6e</span>, <span class="number">0x67</span>, <span class="number">0x2d</span>, <span class="number">0x73</span>, <span class="number">0x65</span>,
<a href=#202 id=202 data-nosnippet>202</a> <span class="number">0x63</span>, <span class="number">0x72</span>, <span class="number">0x65</span>, <span class="number">0x74</span>, <span class="number">0x2d</span>, <span class="number">0x31</span>, <span class="number">0x32</span>, <span class="number">0x33</span>,
<a href=#203 id=203 data-nosnippet>203</a> ];
<a href=#204 id=204 data-nosnippet>204</a> <span class="kw">const </span>BYTES_DISPLAY: <span class="kw-2">&amp;</span>str = <span class="string">"706c61696e2d737472696e672d7365637265742d313233"</span>;
<a href=#205 id=205 data-nosnippet>205</a>
<a href=#206 id=206 data-nosnippet>206</a> <span class="attr">#[test]
<a href=#207 id=207 data-nosnippet>207</a> </span><span class="kw">fn </span>secret_display() {
<a href=#208 id=208 data-nosnippet>208</a> <span class="kw">let </span>base32_str = String::from(BASE32);
<a href=#209 id=209 data-nosnippet>209</a> <span class="kw">let </span>secret_raw = Secret::Raw(BYTES.to_vec());
<a href=#210 id=210 data-nosnippet>210</a> <span class="kw">let </span>secret_base32 = Secret::Encoded(base32_str);
<a href=#211 id=211 data-nosnippet>211</a> <span class="macro">println!</span>(<span class="string">"{}"</span>, secret_raw);
<a href=#212 id=212 data-nosnippet>212</a> <span class="macro">assert_eq!</span>(secret_raw.to_string(), BYTES_DISPLAY.to_string());
<a href=#213 id=213 data-nosnippet>213</a> <span class="macro">assert_eq!</span>(secret_base32.to_string(), BASE32.to_string());
<a href=#214 id=214 data-nosnippet>214</a> }
<a href=#215 id=215 data-nosnippet>215</a>
<a href=#216 id=216 data-nosnippet>216</a> <span class="attr">#[test]
<a href=#217 id=217 data-nosnippet>217</a> </span><span class="kw">fn </span>secret_convert_base32_raw() {
<a href=#218 id=218 data-nosnippet>218</a> <span class="kw">let </span>base32_str = String::from(BASE32);
<a href=#219 id=219 data-nosnippet>219</a> <span class="kw">let </span>secret_raw = Secret::Raw(BYTES.to_vec());
<a href=#220 id=220 data-nosnippet>220</a> <span class="kw">let </span>secret_base32 = Secret::Encoded(base32_str);
<a href=#221 id=221 data-nosnippet>221</a>
<a href=#222 id=222 data-nosnippet>222</a> <span class="macro">assert_eq!</span>(<span class="kw-2">&amp;</span>secret_raw.to_encoded(), <span class="kw-2">&amp;</span>secret_base32);
<a href=#223 id=223 data-nosnippet>223</a> <span class="macro">assert_eq!</span>(<span class="kw-2">&amp;</span>secret_raw.to_raw().unwrap(), <span class="kw-2">&amp;</span>secret_raw);
<a href=#224 id=224 data-nosnippet>224</a>
<a href=#225 id=225 data-nosnippet>225</a> <span class="macro">assert_eq!</span>(<span class="kw-2">&amp;</span>secret_base32.to_raw().unwrap(), <span class="kw-2">&amp;</span>secret_raw);
<a href=#226 id=226 data-nosnippet>226</a> <span class="macro">assert_eq!</span>(<span class="kw-2">&amp;</span>secret_base32.to_encoded(), <span class="kw-2">&amp;</span>secret_base32);
<a href=#227 id=227 data-nosnippet>227</a> }
<a href=#228 id=228 data-nosnippet>228</a>
<a href=#229 id=229 data-nosnippet>229</a> <span class="attr">#[test]
<a href=#230 id=230 data-nosnippet>230</a> </span><span class="kw">fn </span>secret_as_bytes() {
<a href=#231 id=231 data-nosnippet>231</a> <span class="kw">let </span>base32_str = String::from(BASE32);
<a href=#232 id=232 data-nosnippet>232</a> <span class="macro">assert_eq!</span>(
<a href=#233 id=233 data-nosnippet>233</a> Secret::Raw(BYTES.to_vec()).to_bytes().unwrap(),
<a href=#234 id=234 data-nosnippet>234</a> BYTES.to_vec()
<a href=#235 id=235 data-nosnippet>235</a> );
<a href=#236 id=236 data-nosnippet>236</a> <span class="macro">assert_eq!</span>(
<a href=#237 id=237 data-nosnippet>237</a> Secret::Encoded(base32_str).to_bytes().unwrap(),
<a href=#238 id=238 data-nosnippet>238</a> BYTES.to_vec()
<a href=#239 id=239 data-nosnippet>239</a> );
<a href=#240 id=240 data-nosnippet>240</a> }
<a href=#241 id=241 data-nosnippet>241</a>
<a href=#242 id=242 data-nosnippet>242</a> <span class="attr">#[test]
<a href=#243 id=243 data-nosnippet>243</a> </span><span class="kw">fn </span>secret_from_string() {
<a href=#244 id=244 data-nosnippet>244</a> <span class="kw">let </span>raw: Secret = Secret::Raw(<span class="string">"TestSecretSuperSecret"</span>.as_bytes().to_vec());
<a href=#245 id=245 data-nosnippet>245</a> <span class="kw">let </span>encoded: Secret = Secret::Encoded(<span class="string">"KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ"</span>.to_string());
<a href=#246 id=246 data-nosnippet>246</a> <span class="macro">assert_eq!</span>(raw.to_encoded(), encoded);
<a href=#247 id=247 data-nosnippet>247</a> <span class="macro">assert_eq!</span>(raw, encoded.to_raw().unwrap());
<a href=#248 id=248 data-nosnippet>248</a> }
<a href=#249 id=249 data-nosnippet>249</a>
<a href=#250 id=250 data-nosnippet>250</a> <span class="attr">#[test]
<a href=#251 id=251 data-nosnippet>251</a> #[cfg(feature = <span class="string">"gen_secret"</span>)]
<a href=#252 id=252 data-nosnippet>252</a> </span><span class="kw">fn </span>secret_gen_secret() {
<a href=#253 id=253 data-nosnippet>253</a> <span class="kw">let </span>sec = Secret::generate_secret();
<a href=#254 id=254 data-nosnippet>254</a>
<a href=#255 id=255 data-nosnippet>255</a> <span class="macro">assert!</span>(<span class="macro">matches!</span>(sec, Secret::Raw(<span class="kw">_</span>)));
<a href=#256 id=256 data-nosnippet>256</a> <span class="macro">assert_eq!</span>(sec.to_bytes().unwrap().len(), <span class="number">20</span>);
<a href=#257 id=257 data-nosnippet>257</a> }
<a href=#258 id=258 data-nosnippet>258</a>
<a href=#259 id=259 data-nosnippet>259</a> <span class="attr">#[test]
<a href=#260 id=260 data-nosnippet>260</a> #[cfg(feature = <span class="string">"gen_secret"</span>)]
<a href=#261 id=261 data-nosnippet>261</a> </span><span class="kw">fn </span>secret_gen_default() {
<a href=#262 id=262 data-nosnippet>262</a> <span class="kw">let </span>sec = Secret::default();
<a href=#263 id=263 data-nosnippet>263</a>
<a href=#264 id=264 data-nosnippet>264</a> <span class="macro">assert!</span>(<span class="macro">matches!</span>(sec, Secret::Raw(<span class="kw">_</span>)));
<a href=#265 id=265 data-nosnippet>265</a> <span class="macro">assert_eq!</span>(sec.to_bytes().unwrap().len(), <span class="number">20</span>);
<a href=#266 id=266 data-nosnippet>266</a> }
<a href=#267 id=267 data-nosnippet>267</a>
<a href=#268 id=268 data-nosnippet>268</a> <span class="attr">#[test]
<a href=#269 id=269 data-nosnippet>269</a> #[cfg(feature = <span class="string">"gen_secret"</span>)]
<a href=#270 id=270 data-nosnippet>270</a> </span><span class="kw">fn </span>secret_empty() {
<a href=#271 id=271 data-nosnippet>271</a> <span class="kw">let </span>non_ascii = <span class="macro">vec!</span>[<span class="number">240</span>, <span class="number">159</span>, <span class="number">146</span>, <span class="number">150</span>];
<a href=#272 id=272 data-nosnippet>272</a> <span class="kw">let </span>sec = Secret::Encoded(std::str::from_utf8(<span class="kw-2">&amp;</span>non_ascii).unwrap().to_owned());
<a href=#273 id=273 data-nosnippet>273</a>
<a href=#274 id=274 data-nosnippet>274</a> <span class="kw">let </span>to_r = sec.to_raw();
<a href=#275 id=275 data-nosnippet>275</a>
<a href=#276 id=276 data-nosnippet>276</a> <span class="macro">assert!</span>(to_r.is_err());
<a href=#277 id=277 data-nosnippet>277</a>
<a href=#278 id=278 data-nosnippet>278</a> <span class="kw">let </span>to_b = sec.to_bytes();
<a href=#279 id=279 data-nosnippet>279</a>
<a href=#280 id=280 data-nosnippet>280</a> <span class="macro">assert!</span>(to_b.is_err());
<a href=#281 id=281 data-nosnippet>281</a> }
<a href=#282 id=282 data-nosnippet>282</a>}
</code></pre></div></section></main></body></html>

View File

@@ -0,0 +1,248 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `C:\Users\szend\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\totp-rs-5.7.1\src\url_error.rs`."><title>url_error.rs - source</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../static.files/rustdoc-ca0dd0c4.css"><meta name="rustdoc-vars" data-root-path="../../" data-static-root-path="../../static.files/" data-current-crate="totp_rs" data-themes="" data-resource-suffix="" data-rustdoc-version="1.93.1 (01f6ddf75 2026-02-11)" data-channel="1.93.1" data-search-js="search-9e2438ea.js" data-stringdex-js="stringdex-a3946164.js" data-settings-js="settings-c38705f0.js" ><script src="../../static.files/storage-e2aeef58.js"></script><script defer src="../../static.files/src-script-813739b1.js"></script><script defer src="../../src-files.js"></script><script defer src="../../static.files/main-a410ff4d.js"></script><noscript><link rel="stylesheet" href="../../static.files/noscript-263c88ec.css"></noscript><link rel="alternate icon" type="image/png" href="../../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../../static.files/favicon-044be391.svg"></head><body class="rustdoc src"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="src-sidebar-title"><h2>Files</h2></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><section id="main-content" class="content"><div class="main-heading"><h1><div class="sub-heading">totp_rs/</div>url_error.rs</h1><rustdoc-toolbar></rustdoc-toolbar></div><div class="example-wrap digits-3"><pre class="rust"><code><a href=#1 id=1 data-nosnippet>1</a><span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#2 id=2 data-nosnippet>2</a></span><span class="kw">use </span>url::ParseError;
<a href=#3 id=3 data-nosnippet>3</a>
<a href=#4 id=4 data-nosnippet>4</a><span class="kw">use </span><span class="kw">crate</span>::Rfc6238Error;
<a href=#5 id=5 data-nosnippet>5</a>
<a href=#6 id=6 data-nosnippet>6</a><span class="doccomment">/// Errors returned mostly upon decoding URL.
<a href=#7 id=7 data-nosnippet>7</a></span><span class="attr">#[derive(Debug, Eq, PartialEq)]
<a href=#8 id=8 data-nosnippet>8</a></span><span class="kw">pub enum </span>TotpUrlError {
<a href=#9 id=9 data-nosnippet>9</a> <span class="doccomment">/// Couldn't decode URL.
<a href=#10 id=10 data-nosnippet>10</a> </span><span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#11 id=11 data-nosnippet>11</a> #[cfg_attr(docsrs, doc(cfg(feature = <span class="string">"otpauth"</span>)))]
<a href=#12 id=12 data-nosnippet>12</a> </span>Url(ParseError),
<a href=#13 id=13 data-nosnippet>13</a> <span class="doccomment">/// Invalid scheme.
<a href=#14 id=14 data-nosnippet>14</a> </span>Scheme(String),
<a href=#15 id=15 data-nosnippet>15</a> <span class="doccomment">/// Invalid host.
<a href=#16 id=16 data-nosnippet>16</a> </span>Host(String),
<a href=#17 id=17 data-nosnippet>17</a> <span class="doccomment">/// Wrong base32 input.
<a href=#18 id=18 data-nosnippet>18</a> </span>Secret(String),
<a href=#19 id=19 data-nosnippet>19</a> <span class="doccomment">/// Invalid secret size. (Too short?)
<a href=#20 id=20 data-nosnippet>20</a> </span>SecretSize(usize),
<a href=#21 id=21 data-nosnippet>21</a> <span class="doccomment">/// Unknown algorithm.
<a href=#22 id=22 data-nosnippet>22</a> </span>Algorithm(String),
<a href=#23 id=23 data-nosnippet>23</a> <span class="doccomment">/// Characters should only be digits.
<a href=#24 id=24 data-nosnippet>24</a> </span>Digits(String),
<a href=#25 id=25 data-nosnippet>25</a> <span class="doccomment">/// Digits should be between 6 and 8.
<a href=#26 id=26 data-nosnippet>26</a> </span>DigitsNumber(usize),
<a href=#27 id=27 data-nosnippet>27</a> <span class="doccomment">/// Couldn't decode step into a number.
<a href=#28 id=28 data-nosnippet>28</a> </span>Step(String),
<a href=#29 id=29 data-nosnippet>29</a> <span class="doccomment">/// Issuer contains invalid character `:`.
<a href=#30 id=30 data-nosnippet>30</a> </span>Issuer(String),
<a href=#31 id=31 data-nosnippet>31</a> <span class="doccomment">/// Couldn't decode issuer.
<a href=#32 id=32 data-nosnippet>32</a> </span>IssuerDecoding(String),
<a href=#33 id=33 data-nosnippet>33</a> <span class="doccomment">/// Issuers should be the same.
<a href=#34 id=34 data-nosnippet>34</a> </span>IssuerMistmatch(String, String),
<a href=#35 id=35 data-nosnippet>35</a> <span class="doccomment">/// Account name contains invalid character `:` or couldn't be decoded.
<a href=#36 id=36 data-nosnippet>36</a> </span>AccountName(String),
<a href=#37 id=37 data-nosnippet>37</a> <span class="doccomment">/// Couldn't parse account name.
<a href=#38 id=38 data-nosnippet>38</a> </span>AccountNameDecoding(String),
<a href=#39 id=39 data-nosnippet>39</a>}
<a href=#40 id=40 data-nosnippet>40</a>
<a href=#41 id=41 data-nosnippet>41</a><span class="kw">impl </span>std::error::Error <span class="kw">for </span>TotpUrlError {}
<a href=#42 id=42 data-nosnippet>42</a>
<a href=#43 id=43 data-nosnippet>43</a><span class="kw">impl </span>std::fmt::Display <span class="kw">for </span>TotpUrlError {
<a href=#44 id=44 data-nosnippet>44</a> <span class="kw">fn </span>fmt(<span class="kw-2">&amp;</span><span class="self">self</span>, f: <span class="kw-2">&amp;mut </span>std::fmt::Formatter&lt;<span class="lifetime">'_</span>&gt;) -&gt; std::fmt::Result {
<a href=#45 id=45 data-nosnippet>45</a> <span class="kw">match </span><span class="self">self </span>{
<a href=#46 id=46 data-nosnippet>46</a> TotpUrlError::AccountName(name) =&gt; <span class="macro">write!</span>(
<a href=#47 id=47 data-nosnippet>47</a> f,
<a href=#48 id=48 data-nosnippet>48</a> <span class="string">"Account Name can't contain a colon. \"{}\" contains a colon"</span>,
<a href=#49 id=49 data-nosnippet>49</a> name
<a href=#50 id=50 data-nosnippet>50</a> ),
<a href=#51 id=51 data-nosnippet>51</a> TotpUrlError::AccountNameDecoding(name) =&gt; <span class="macro">write!</span>(
<a href=#52 id=52 data-nosnippet>52</a> f,
<a href=#53 id=53 data-nosnippet>53</a> <span class="string">"Couldn't URL decode \"{}\""</span>,
<a href=#54 id=54 data-nosnippet>54</a> name
<a href=#55 id=55 data-nosnippet>55</a> ),
<a href=#56 id=56 data-nosnippet>56</a> TotpUrlError::Algorithm(algo) =&gt; <span class="macro">write!</span>(
<a href=#57 id=57 data-nosnippet>57</a> f,
<a href=#58 id=58 data-nosnippet>58</a> <span class="string">"Algorithm can only be SHA1, SHA256 or SHA512, not \"{}\""</span>,
<a href=#59 id=59 data-nosnippet>59</a> algo
<a href=#60 id=60 data-nosnippet>60</a> ),
<a href=#61 id=61 data-nosnippet>61</a> TotpUrlError::Digits(digits) =&gt; <span class="macro">write!</span>(
<a href=#62 id=62 data-nosnippet>62</a> f,
<a href=#63 id=63 data-nosnippet>63</a> <span class="string">"Could not parse \"{}\" as a number."</span>,
<a href=#64 id=64 data-nosnippet>64</a> digits,
<a href=#65 id=65 data-nosnippet>65</a> ),
<a href=#66 id=66 data-nosnippet>66</a> TotpUrlError::DigitsNumber(digits) =&gt; <span class="macro">write!</span>(
<a href=#67 id=67 data-nosnippet>67</a> f,
<a href=#68 id=68 data-nosnippet>68</a> <span class="string">"Implementations MUST extract a 6-digit code at a minimum and possibly 7 and 8-digit code. {} digits is not allowed"</span>,
<a href=#69 id=69 data-nosnippet>69</a> digits,
<a href=#70 id=70 data-nosnippet>70</a> ),
<a href=#71 id=71 data-nosnippet>71</a> TotpUrlError::Host(host) =&gt; <span class="macro">write!</span>(
<a href=#72 id=72 data-nosnippet>72</a> f,
<a href=#73 id=73 data-nosnippet>73</a> <span class="string">"Host should be totp, not \"{}\""</span>,
<a href=#74 id=74 data-nosnippet>74</a> host
<a href=#75 id=75 data-nosnippet>75</a> ),
<a href=#76 id=76 data-nosnippet>76</a> TotpUrlError::Issuer(issuer) =&gt; <span class="macro">write!</span>(
<a href=#77 id=77 data-nosnippet>77</a> f,
<a href=#78 id=78 data-nosnippet>78</a> <span class="string">"Issuer can't contain a colon. \"{}\" contains a colon"</span>,
<a href=#79 id=79 data-nosnippet>79</a> issuer
<a href=#80 id=80 data-nosnippet>80</a> ),
<a href=#81 id=81 data-nosnippet>81</a> TotpUrlError::IssuerDecoding(issuer) =&gt; <span class="macro">write!</span>(
<a href=#82 id=82 data-nosnippet>82</a> f,
<a href=#83 id=83 data-nosnippet>83</a> <span class="string">"Couldn't URL decode \"{}\""</span>,
<a href=#84 id=84 data-nosnippet>84</a> issuer
<a href=#85 id=85 data-nosnippet>85</a> ),
<a href=#86 id=86 data-nosnippet>86</a> TotpUrlError::IssuerMistmatch(path_issuer, issuer) =&gt; <span class="macro">write!</span>(
<a href=#87 id=87 data-nosnippet>87</a> f,
<a href=#88 id=88 data-nosnippet>88</a> <span class="string">"An issuer \"{}\" could be retrieved from the path, but a different issuer \"{}\" was found in the issuer URL parameter"</span>,
<a href=#89 id=89 data-nosnippet>89</a> path_issuer,
<a href=#90 id=90 data-nosnippet>90</a> issuer,
<a href=#91 id=91 data-nosnippet>91</a> ),
<a href=#92 id=92 data-nosnippet>92</a> TotpUrlError::Scheme(scheme) =&gt; <span class="macro">write!</span>(
<a href=#93 id=93 data-nosnippet>93</a> f,
<a href=#94 id=94 data-nosnippet>94</a> <span class="string">"Scheme should be otpauth, not \"{}\""</span>,
<a href=#95 id=95 data-nosnippet>95</a> scheme
<a href=#96 id=96 data-nosnippet>96</a> ),
<a href=#97 id=97 data-nosnippet>97</a> TotpUrlError::Secret(secret) =&gt; <span class="macro">write!</span>(
<a href=#98 id=98 data-nosnippet>98</a> f,
<a href=#99 id=99 data-nosnippet>99</a> <span class="string">"Secret \"{}\" is not a valid non-padded base32 string"</span>,
<a href=#100 id=100 data-nosnippet>100</a> secret,
<a href=#101 id=101 data-nosnippet>101</a> ),
<a href=#102 id=102 data-nosnippet>102</a> TotpUrlError::SecretSize(bits) =&gt; <span class="macro">write!</span>(
<a href=#103 id=103 data-nosnippet>103</a> f,
<a href=#104 id=104 data-nosnippet>104</a> <span class="string">"The length of the shared secret MUST be at least 128 bits. {} bits is not enough"</span>,
<a href=#105 id=105 data-nosnippet>105</a> bits,
<a href=#106 id=106 data-nosnippet>106</a> ),
<a href=#107 id=107 data-nosnippet>107</a> TotpUrlError::Step(step) =&gt; <span class="macro">write!</span>(
<a href=#108 id=108 data-nosnippet>108</a> f,
<a href=#109 id=109 data-nosnippet>109</a> <span class="string">"Could not parse \"{}\" as a number."</span>,
<a href=#110 id=110 data-nosnippet>110</a> step,
<a href=#111 id=111 data-nosnippet>111</a> ),
<a href=#112 id=112 data-nosnippet>112</a> <span class="attr">#[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#113 id=113 data-nosnippet>113</a> </span>TotpUrlError::Url(e) =&gt; <span class="macro">write!</span>(
<a href=#114 id=114 data-nosnippet>114</a> f,
<a href=#115 id=115 data-nosnippet>115</a> <span class="string">"Error parsing URL: {}"</span>,
<a href=#116 id=116 data-nosnippet>116</a> e
<a href=#117 id=117 data-nosnippet>117</a> )
<a href=#118 id=118 data-nosnippet>118</a> }
<a href=#119 id=119 data-nosnippet>119</a> }
<a href=#120 id=120 data-nosnippet>120</a>}
<a href=#121 id=121 data-nosnippet>121</a>
<a href=#122 id=122 data-nosnippet>122</a><span class="kw">impl </span>From&lt;Rfc6238Error&gt; <span class="kw">for </span>TotpUrlError {
<a href=#123 id=123 data-nosnippet>123</a> <span class="kw">fn </span>from(e: Rfc6238Error) -&gt; <span class="self">Self </span>{
<a href=#124 id=124 data-nosnippet>124</a> <span class="kw">match </span>e {
<a href=#125 id=125 data-nosnippet>125</a> Rfc6238Error::InvalidDigits(digits) =&gt; TotpUrlError::DigitsNumber(digits),
<a href=#126 id=126 data-nosnippet>126</a> Rfc6238Error::SecretTooSmall(bits) =&gt; TotpUrlError::SecretSize(bits),
<a href=#127 id=127 data-nosnippet>127</a> }
<a href=#128 id=128 data-nosnippet>128</a> }
<a href=#129 id=129 data-nosnippet>129</a>}
<a href=#130 id=130 data-nosnippet>130</a>
<a href=#131 id=131 data-nosnippet>131</a><span class="attr">#[cfg(test)]
<a href=#132 id=132 data-nosnippet>132</a></span><span class="kw">mod </span>tests {
<a href=#133 id=133 data-nosnippet>133</a> <span class="kw">use </span><span class="kw">crate</span>::TotpUrlError;
<a href=#134 id=134 data-nosnippet>134</a>
<a href=#135 id=135 data-nosnippet>135</a> <span class="attr">#[test]
<a href=#136 id=136 data-nosnippet>136</a> </span><span class="kw">fn </span>account_name() {
<a href=#137 id=137 data-nosnippet>137</a> <span class="kw">let </span>error = TotpUrlError::AccountName(<span class="string">"Laziz:"</span>.to_string());
<a href=#138 id=138 data-nosnippet>138</a> <span class="macro">assert_eq!</span>(
<a href=#139 id=139 data-nosnippet>139</a> error.to_string(),
<a href=#140 id=140 data-nosnippet>140</a> <span class="string">"Account Name can't contain a colon. \"Laziz:\" contains a colon"
<a href=#141 id=141 data-nosnippet>141</a> </span>)
<a href=#142 id=142 data-nosnippet>142</a> }
<a href=#143 id=143 data-nosnippet>143</a>
<a href=#144 id=144 data-nosnippet>144</a> <span class="attr">#[test]
<a href=#145 id=145 data-nosnippet>145</a> </span><span class="kw">fn </span>account_name_decoding() {
<a href=#146 id=146 data-nosnippet>146</a> <span class="kw">let </span>error = TotpUrlError::AccountNameDecoding(<span class="string">"Laz&amp;iz"</span>.to_string());
<a href=#147 id=147 data-nosnippet>147</a> <span class="macro">assert_eq!</span>(
<a href=#148 id=148 data-nosnippet>148</a> error.to_string(),
<a href=#149 id=149 data-nosnippet>149</a> <span class="string">"Couldn't URL decode \"Laz&amp;iz\""</span>.to_string()
<a href=#150 id=150 data-nosnippet>150</a> )
<a href=#151 id=151 data-nosnippet>151</a> }
<a href=#152 id=152 data-nosnippet>152</a>
<a href=#153 id=153 data-nosnippet>153</a> <span class="attr">#[test]
<a href=#154 id=154 data-nosnippet>154</a> </span><span class="kw">fn </span>algorithm() {
<a href=#155 id=155 data-nosnippet>155</a> <span class="kw">let </span>error = TotpUrlError::Algorithm(<span class="string">"SIKE"</span>.to_string());
<a href=#156 id=156 data-nosnippet>156</a> <span class="macro">assert_eq!</span>(
<a href=#157 id=157 data-nosnippet>157</a> error.to_string(),
<a href=#158 id=158 data-nosnippet>158</a> <span class="string">"Algorithm can only be SHA1, SHA256 or SHA512, not \"SIKE\""</span>.to_string()
<a href=#159 id=159 data-nosnippet>159</a> )
<a href=#160 id=160 data-nosnippet>160</a> }
<a href=#161 id=161 data-nosnippet>161</a>
<a href=#162 id=162 data-nosnippet>162</a> <span class="attr">#[test]
<a href=#163 id=163 data-nosnippet>163</a> </span><span class="kw">fn </span>digits() {
<a href=#164 id=164 data-nosnippet>164</a> <span class="kw">let </span>error = TotpUrlError::Digits(<span class="string">"six"</span>.to_string());
<a href=#165 id=165 data-nosnippet>165</a> <span class="macro">assert_eq!</span>(
<a href=#166 id=166 data-nosnippet>166</a> error.to_string(),
<a href=#167 id=167 data-nosnippet>167</a> <span class="string">"Could not parse \"six\" as a number."</span>.to_string()
<a href=#168 id=168 data-nosnippet>168</a> )
<a href=#169 id=169 data-nosnippet>169</a> }
<a href=#170 id=170 data-nosnippet>170</a>
<a href=#171 id=171 data-nosnippet>171</a> <span class="attr">#[test]
<a href=#172 id=172 data-nosnippet>172</a> </span><span class="kw">fn </span>digits_number() {
<a href=#173 id=173 data-nosnippet>173</a> <span class="kw">let </span>error = TotpUrlError::DigitsNumber(<span class="number">5</span>);
<a href=#174 id=174 data-nosnippet>174</a> <span class="macro">assert_eq!</span>(error.to_string(), <span class="string">"Implementations MUST extract a 6-digit code at a minimum and possibly 7 and 8-digit code. 5 digits is not allowed"</span>.to_string())
<a href=#175 id=175 data-nosnippet>175</a> }
<a href=#176 id=176 data-nosnippet>176</a>
<a href=#177 id=177 data-nosnippet>177</a> <span class="attr">#[test]
<a href=#178 id=178 data-nosnippet>178</a> </span><span class="kw">fn </span>host() {
<a href=#179 id=179 data-nosnippet>179</a> <span class="kw">let </span>error = TotpUrlError::Host(<span class="string">"hotp"</span>.to_string());
<a href=#180 id=180 data-nosnippet>180</a> <span class="macro">assert_eq!</span>(
<a href=#181 id=181 data-nosnippet>181</a> error.to_string(),
<a href=#182 id=182 data-nosnippet>182</a> <span class="string">"Host should be totp, not \"hotp\""</span>.to_string()
<a href=#183 id=183 data-nosnippet>183</a> )
<a href=#184 id=184 data-nosnippet>184</a> }
<a href=#185 id=185 data-nosnippet>185</a>
<a href=#186 id=186 data-nosnippet>186</a> <span class="attr">#[test]
<a href=#187 id=187 data-nosnippet>187</a> </span><span class="kw">fn </span>issuer() {
<a href=#188 id=188 data-nosnippet>188</a> <span class="kw">let </span>error = TotpUrlError::Issuer(<span class="string">"Iss:uer"</span>.to_string());
<a href=#189 id=189 data-nosnippet>189</a> <span class="macro">assert_eq!</span>(
<a href=#190 id=190 data-nosnippet>190</a> error.to_string(),
<a href=#191 id=191 data-nosnippet>191</a> <span class="string">"Issuer can't contain a colon. \"Iss:uer\" contains a colon"</span>.to_string()
<a href=#192 id=192 data-nosnippet>192</a> )
<a href=#193 id=193 data-nosnippet>193</a> }
<a href=#194 id=194 data-nosnippet>194</a>
<a href=#195 id=195 data-nosnippet>195</a> <span class="attr">#[test]
<a href=#196 id=196 data-nosnippet>196</a> </span><span class="kw">fn </span>issuer_decoding() {
<a href=#197 id=197 data-nosnippet>197</a> <span class="kw">let </span>error = TotpUrlError::IssuerDecoding(<span class="string">"iss&amp;uer"</span>.to_string());
<a href=#198 id=198 data-nosnippet>198</a> <span class="macro">assert_eq!</span>(
<a href=#199 id=199 data-nosnippet>199</a> error.to_string(),
<a href=#200 id=200 data-nosnippet>200</a> <span class="string">"Couldn't URL decode \"iss&amp;uer\""</span>.to_string()
<a href=#201 id=201 data-nosnippet>201</a> )
<a href=#202 id=202 data-nosnippet>202</a> }
<a href=#203 id=203 data-nosnippet>203</a>
<a href=#204 id=204 data-nosnippet>204</a> <span class="attr">#[test]
<a href=#205 id=205 data-nosnippet>205</a> </span><span class="kw">fn </span>issuer_mismatch() {
<a href=#206 id=206 data-nosnippet>206</a> <span class="kw">let </span>error = TotpUrlError::IssuerMistmatch(<span class="string">"Google"</span>.to_string(), <span class="string">"Github"</span>.to_string());
<a href=#207 id=207 data-nosnippet>207</a> <span class="macro">assert_eq!</span>(error.to_string(), <span class="string">"An issuer \"Google\" could be retrieved from the path, but a different issuer \"Github\" was found in the issuer URL parameter"</span>.to_string())
<a href=#208 id=208 data-nosnippet>208</a> }
<a href=#209 id=209 data-nosnippet>209</a>
<a href=#210 id=210 data-nosnippet>210</a> <span class="attr">#[test]
<a href=#211 id=211 data-nosnippet>211</a> </span><span class="kw">fn </span>scheme() {
<a href=#212 id=212 data-nosnippet>212</a> <span class="kw">let </span>error = TotpUrlError::Scheme(<span class="string">"https"</span>.to_string());
<a href=#213 id=213 data-nosnippet>213</a> <span class="macro">assert_eq!</span>(
<a href=#214 id=214 data-nosnippet>214</a> error.to_string(),
<a href=#215 id=215 data-nosnippet>215</a> <span class="string">"Scheme should be otpauth, not \"https\""</span>.to_string()
<a href=#216 id=216 data-nosnippet>216</a> )
<a href=#217 id=217 data-nosnippet>217</a> }
<a href=#218 id=218 data-nosnippet>218</a>
<a href=#219 id=219 data-nosnippet>219</a> <span class="attr">#[test]
<a href=#220 id=220 data-nosnippet>220</a> </span><span class="kw">fn </span>secret() {
<a href=#221 id=221 data-nosnippet>221</a> <span class="kw">let </span>error = TotpUrlError::Secret(<span class="string">"YoLo"</span>.to_string());
<a href=#222 id=222 data-nosnippet>222</a> <span class="macro">assert_eq!</span>(
<a href=#223 id=223 data-nosnippet>223</a> error.to_string(),
<a href=#224 id=224 data-nosnippet>224</a> <span class="string">"Secret \"YoLo\" is not a valid non-padded base32 string"</span>.to_string()
<a href=#225 id=225 data-nosnippet>225</a> )
<a href=#226 id=226 data-nosnippet>226</a> }
<a href=#227 id=227 data-nosnippet>227</a>
<a href=#228 id=228 data-nosnippet>228</a> <span class="attr">#[test]
<a href=#229 id=229 data-nosnippet>229</a> </span><span class="kw">fn </span>secret_size() {
<a href=#230 id=230 data-nosnippet>230</a> <span class="kw">let </span>error = TotpUrlError::SecretSize(<span class="number">112</span>);
<a href=#231 id=231 data-nosnippet>231</a> <span class="macro">assert_eq!</span>(
<a href=#232 id=232 data-nosnippet>232</a> error.to_string(),
<a href=#233 id=233 data-nosnippet>233</a> <span class="string">"The length of the shared secret MUST be at least 128 bits. 112 bits is not enough"
<a href=#234 id=234 data-nosnippet>234</a> </span>.to_string()
<a href=#235 id=235 data-nosnippet>235</a> )
<a href=#236 id=236 data-nosnippet>236</a> }
<a href=#237 id=237 data-nosnippet>237</a>
<a href=#238 id=238 data-nosnippet>238</a> <span class="attr">#[test]
<a href=#239 id=239 data-nosnippet>239</a> #[cfg(feature = <span class="string">"otpauth"</span>)]
<a href=#240 id=240 data-nosnippet>240</a> </span><span class="kw">fn </span>step() {
<a href=#241 id=241 data-nosnippet>241</a> <span class="kw">let </span>error = TotpUrlError::Url(url::ParseError::EmptyHost);
<a href=#242 id=242 data-nosnippet>242</a> <span class="macro">assert_eq!</span>(
<a href=#243 id=243 data-nosnippet>243</a> error.to_string(),
<a href=#244 id=244 data-nosnippet>244</a> <span class="string">"Error parsing URL: empty host"</span>.to_string()
<a href=#245 id=245 data-nosnippet>245</a> )
<a href=#246 id=246 data-nosnippet>246</a> }
<a href=#247 id=247 data-nosnippet>247</a>}
</code></pre></div></section></main></body></html>

View File

@@ -0,0 +1,71 @@
# REUSE-IgnoreStart
These documentation pages include resources by third parties. This copyright
file applies only to those resources. The following third party resources are
included, and carry their own copyright notices and license terms:
* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2):
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
with Reserved Font Name Fira Sans.
Copyright (c) 2014, Telefonica S.A.
Licensed under the SIL Open Font License, Version 1.1.
See FiraSans-LICENSE.txt.
* rustdoc.css, main.js, and playpen.js:
Copyright 2015 The Rust Developers.
Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or
the MIT license (LICENSE-MIT.txt) at your option.
* normalize.css:
Copyright (c) Nicolas Gallagher and Jonathan Neal.
Licensed under the MIT license (see LICENSE-MIT.txt).
* Source Code Pro (SourceCodePro-Regular.ttf.woff2,
SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2):
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/),
with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark
of Adobe Systems Incorporated in the United States and/or other countries.
Licensed under the SIL Open Font License, Version 1.1.
See SourceCodePro-LICENSE.txt.
* Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2,
SourceSerif4-It.ttf.woff2, SourceSerif4-Semibold.ttf.woff2):
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name
'Source'. All Rights Reserved. Source is a trademark of Adobe in the United
States and/or other countries.
Licensed under the SIL Open Font License, Version 1.1.
See SourceSerif4-LICENSE.md.
* Nanum Barun Gothic Font (NanumBarunGothic.woff2)
Copyright 2010, NAVER Corporation (http://www.nhncorp.com)
with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic,
NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen,
Naver NanumPen, Naver NanumGothicEco, NanumGothicEco,
Naver NanumMyeongjoEco, NanumMyeongjoEco, Naver NanumGothicLight,
NanumGothicLight, NanumBarunGothic, Naver NanumBarunGothic.
https://hangeul.naver.com/2017/nanum
https://github.com/hiun/NanumBarunGothic
Licensed under the SIL Open Font License, Version 1.1.
See NanumBarunGothic-LICENSE.txt.
* Rust logos (rust-logo.svg, favicon.svg, favicon-32x32.png)
Copyright 2025 Rust Foundation.
Licensed under the Creative Commons Attribution license (CC-BY).
https://rustfoundation.org/policy/rust-trademark-policy/
This copyright file is intended to be distributed with rustdoc output.
# REUSE-IgnoreEnd

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,98 @@
// REUSE-IgnoreStart
Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
with Reserved Font Name < Fira >,
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
// REUSE-IgnoreEnd

Binary file not shown.

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,103 @@
// REUSE-IgnoreStart
Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/),
with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic,
NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen,
Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco,
NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic,
Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
// REUSE-IgnoreEnd

View File

@@ -0,0 +1,97 @@
// REUSE-IgnoreStart
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
// REUSE-IgnoreEnd

View File

@@ -0,0 +1,98 @@
<!-- REUSE-IgnoreStart -->
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries.
Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name Source. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
<!-- REUSE-IgnoreEnd -->

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;">
<defs>
<style type="text/css"><![CDATA[
#logo {
fill-rule: nonzero;
}
#logo-teeth {
stroke: #000000;
stroke-width: 0.92px;
}
@media (prefers-color-scheme: dark) {
#logo {
fill: #FFFFFF;
fill-rule: nonzero;
}
#logo-teeth {
fill: #FFFFFF;
stroke: #FFFFFF;
stroke-width: 0.92px;
}
}
]]></style>
</defs>
<path id="logo" d="M15.993,1.54c-7.972,0 -14.461,6.492 -14.461,14.462c0,7.969 6.492,14.461 14.461,14.461c7.97,0 14.462,-6.492 14.462,-14.461c0,-7.97 -6.492,-14.462 -14.462,-14.462Zm-0.021,1.285c0.511,0.013 0.924,0.439 0.924,0.951c0,0.522 -0.43,0.952 -0.952,0.952c-0.522,0 -0.951,-0.43 -0.951,-0.952c0,0 0,0 0,0c0,-0.522 0.429,-0.952 0.951,-0.952c0.01,0 0.019,0.001 0.028,0.001Zm2.178,1.566c3.379,0.633 6.313,2.723 8.016,5.709l-1.123,2.533c-0.193,0.438 0.006,0.952 0.44,1.147l2.16,0.958c0.067,0.675 0.076,1.355 0.025,2.031l-1.202,0c-0.12,0 -0.169,0.08 -0.169,0.196l0,0.551c0,1.297 -0.731,1.582 -1.373,1.652c-0.612,0.07 -1.288,-0.257 -1.374,-0.63c-0.361,-2.029 -0.961,-2.46 -1.909,-3.21c1.178,-0.746 2.401,-1.85 2.401,-3.325c0,-1.594 -1.092,-2.597 -1.835,-3.09c-1.046,-0.688 -2.203,-0.826 -2.515,-0.826l-12.421,0c1.717,-1.918 4.02,-3.218 6.55,-3.696l1.466,1.536c0.33,0.346 0.878,0.361 1.223,0.028l1.64,-1.564Zm-13.522,7.043c0.511,0.015 0.924,0.44 0.924,0.951c0,0.522 -0.43,0.952 -0.952,0.952c-0.522,0 -0.951,-0.43 -0.951,-0.952c0,0 0,0 0,0c0,-0.522 0.429,-0.951 0.951,-0.951c0.009,0 0.019,0 0.028,0Zm22.685,0.043c0.511,0.015 0.924,0.44 0.924,0.951c0,0.522 -0.43,0.952 -0.952,0.952c-0.522,0 -0.951,-0.43 -0.951,-0.952c0,0 0,0 0,0c0,-0.522 0.429,-0.952 0.951,-0.952c0.01,0 0.019,0 0.028,0.001Zm-20.892,0.153l1.658,0l0,7.477l-3.347,0c-0.414,-1.452 -0.542,-2.97 -0.38,-4.47l2.05,-0.912c0.438,-0.195 0.637,-0.706 0.441,-1.144l-0.422,-0.951Zm6.92,0.079l3.949,0c0.205,0 1.441,0.236 1.441,1.163c0,0.768 -0.948,1.043 -1.728,1.043l-3.665,0l0.003,-2.206Zm0,5.373l3.026,0c0.275,0 1.477,0.079 1.86,1.615c0.119,0.471 0.385,2.007 0.566,2.499c0.18,0.551 0.911,1.652 1.691,1.652l4.938,0c-0.331,0.444 -0.693,0.863 -1.083,1.255l-2.01,-0.432c-0.468,-0.101 -0.93,0.199 -1.031,0.667l-0.477,2.228c-3.104,1.406 -6.672,1.389 -9.762,-0.046l-0.478,-2.228c-0.101,-0.468 -0.56,-0.767 -1.028,-0.667l-1.967,0.423c-0.365,-0.377 -0.704,-0.778 -1.016,-1.2l9.567,0c0.107,0 0.181,-0.018 0.181,-0.119l0,-3.384c0,-0.097 -0.074,-0.119 -0.181,-0.119l-2.799,0l0.003,-2.144Zm-4.415,7.749c0.512,0.015 0.924,0.44 0.924,0.951c0,0.522 -0.429,0.952 -0.951,0.952c-0.522,0 -0.952,-0.43 -0.952,-0.952c0,0 0,0 0,0c0,-0.522 0.43,-0.952 0.952,-0.952c0.009,0 0.018,0.001 0.027,0.001Zm14.089,0.043c0.511,0.015 0.924,0.439 0.923,0.951c0,0.522 -0.429,0.952 -0.951,0.952c-0.522,0 -0.951,-0.43 -0.951,-0.952c0,0 0,0 0,0c0,-0.522 0.429,-0.952 0.951,-0.952c0.009,0 0.018,0 0.028,0.001Z"/><path id="logo-teeth" d="M29.647,16.002c0,7.49 -6.163,13.653 -13.654,13.653c-7.49,0 -13.654,-6.163 -13.654,-13.653c0,-7.491 6.164,-13.654 13.654,-13.654c7.491,0 13.654,6.163 13.654,13.654Zm-0.257,-1.319l2.13,1.319l-2.13,1.318l1.83,1.71l-2.344,0.878l1.463,2.035l-2.475,0.404l1.04,2.282l-2.506,-0.089l0.575,2.442l-2.441,-0.576l0.089,2.506l-2.283,-1.04l-0.403,2.475l-2.035,-1.462l-0.878,2.343l-1.71,-1.829l-1.319,2.129l-1.318,-2.129l-1.71,1.829l-0.878,-2.343l-2.035,1.462l-0.404,-2.475l-2.282,1.04l0.089,-2.506l-2.442,0.576l0.575,-2.442l-2.505,0.089l1.04,-2.282l-2.475,-0.404l1.462,-2.035l-2.343,-0.878l1.829,-1.71l-2.129,-1.318l2.129,-1.319l-1.829,-1.71l2.343,-0.878l-1.462,-2.035l2.475,-0.404l-1.04,-2.282l2.505,0.089l-0.575,-2.441l2.442,0.575l-0.089,-2.506l2.282,1.04l0.404,-2.475l2.035,1.463l0.878,-2.344l1.71,1.83l1.318,-2.13l1.319,2.13l1.71,-1.83l0.878,2.344l2.035,-1.463l0.403,2.475l2.283,-1.04l-0.089,2.506l2.441,-0.575l-0.575,2.441l2.506,-0.089l-1.04,2.282l2.475,0.404l-1.463,2.035l2.344,0.878l-1.83,1.71Z"/></svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,61 @@
<svg version="1.1" height="106" width="106" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="logo" transform="translate(53, 53)">
<path id="r" transform="translate(0.5, 0.5)" stroke="black" stroke-width="1" stroke-linejoin="round" d="
M -9,-15 H 4 C 12,-15 12,-7 4,-7 H -9 Z
M -40,22 H 0 V 11 H -9 V 3 H 1 C 12,3 6,22 15,22 H 40
V 3 H 34 V 5 C 34,13 25,12 24,7 C 23,2 19,-2 18,-2 C 33,-10 24,-26 12,-26 H -35
V -15 H -25 V 11 H -40 Z" />
<g id="gear" mask="url(#holes)">
<circle r="43" fill="none" stroke="black" stroke-width="9" />
<g id="cogs">
<polygon id="cog" stroke="black" stroke-width="3" stroke-linejoin="round" points="46,3 51,0 46,-3" />
<use xlink:href="#cog" transform="rotate(11.25)" />
<use xlink:href="#cog" transform="rotate(22.50)" />
<use xlink:href="#cog" transform="rotate(33.75)" />
<use xlink:href="#cog" transform="rotate(45.00)" />
<use xlink:href="#cog" transform="rotate(56.25)" />
<use xlink:href="#cog" transform="rotate(67.50)" />
<use xlink:href="#cog" transform="rotate(78.75)" />
<use xlink:href="#cog" transform="rotate(90.00)" />
<use xlink:href="#cog" transform="rotate(101.25)" />
<use xlink:href="#cog" transform="rotate(112.50)" />
<use xlink:href="#cog" transform="rotate(123.75)" />
<use xlink:href="#cog" transform="rotate(135.00)" />
<use xlink:href="#cog" transform="rotate(146.25)" />
<use xlink:href="#cog" transform="rotate(157.50)" />
<use xlink:href="#cog" transform="rotate(168.75)" />
<use xlink:href="#cog" transform="rotate(180.00)" />
<use xlink:href="#cog" transform="rotate(191.25)" />
<use xlink:href="#cog" transform="rotate(202.50)" />
<use xlink:href="#cog" transform="rotate(213.75)" />
<use xlink:href="#cog" transform="rotate(225.00)" />
<use xlink:href="#cog" transform="rotate(236.25)" />
<use xlink:href="#cog" transform="rotate(247.50)" />
<use xlink:href="#cog" transform="rotate(258.75)" />
<use xlink:href="#cog" transform="rotate(270.00)" />
<use xlink:href="#cog" transform="rotate(281.25)" />
<use xlink:href="#cog" transform="rotate(292.50)" />
<use xlink:href="#cog" transform="rotate(303.75)" />
<use xlink:href="#cog" transform="rotate(315.00)" />
<use xlink:href="#cog" transform="rotate(326.25)" />
<use xlink:href="#cog" transform="rotate(337.50)" />
<use xlink:href="#cog" transform="rotate(348.75)" />
</g>
<g id="mounts">
<polygon id="mount" stroke="black" stroke-width="6" stroke-linejoin="round" points="-7,-42 0,-35 7,-42" />
<use xlink:href="#mount" transform="rotate(72)" />
<use xlink:href="#mount" transform="rotate(144)" />
<use xlink:href="#mount" transform="rotate(216)" />
<use xlink:href="#mount" transform="rotate(288)" />
</g>
</g>
<mask id="holes">
<rect x="-60" y="-60" width="120" height="120" fill="white"/>
<circle id="hole" cy="-40" r="3" />
<use xlink:href="#hole" transform="rotate(72)" />
<use xlink:href="#hole" transform="rotate(144)" />
<use xlink:href="#hole" transform="rotate(216)" />
<use xlink:href="#hole" transform="rotate(288)" />
</mask>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
"use strict";(function(){const DEFAULT_MAX_LINES=5;const HIDDEN_MAX_LINES=10;function scrollToLoc(elt,loc,isHidden){const lines=elt.querySelectorAll("[data-nosnippet]");let scrollOffset;const maxLines=isHidden?HIDDEN_MAX_LINES:DEFAULT_MAX_LINES;if(loc[1]-loc[0]>maxLines){const line=Math.max(0,loc[0]-1);scrollOffset=lines[line].offsetTop;}else{const halfHeight=elt.offsetHeight/2;const offsetTop=lines[loc[0]].offsetTop;const lastLine=lines[loc[1]];const offsetBot=lastLine.offsetTop+lastLine.offsetHeight;const offsetMid=(offsetTop+offsetBot)/2;scrollOffset=offsetMid-halfHeight;}nonnull(lines[0].parentElement).scrollTo(0,scrollOffset);nonnull(elt.querySelector(".rust")).scrollTo(0,scrollOffset);}function createScrapeButton(parent,className,content){const button=document.createElement("button");button.className=className;button.title=content;parent.insertBefore(button,parent.firstChild);return button;}window.updateScrapedExample=(example,buttonHolder)=>{let locIndex=0;const highlights=Array.prototype.slice.call(example.querySelectorAll(".highlight"));const link=nonnull(example.querySelector(".scraped-example-title a"));let expandButton=null;if(!example.classList.contains("expanded")){expandButton=createScrapeButton(buttonHolder,"expand","Show all");}const isHidden=nonnull(example.parentElement).classList.contains("more-scraped-examples");const locs=example.locs;if(locs.length>1){const next=createScrapeButton(buttonHolder,"next","Next usage");const prev=createScrapeButton(buttonHolder,"prev","Previous usage");const onChangeLoc=changeIndex=>{removeClass(highlights[locIndex],"focus");changeIndex();scrollToLoc(example,locs[locIndex][0],isHidden);addClass(highlights[locIndex],"focus");const url=locs[locIndex][1];const title=locs[locIndex][2];link.href=url;link.innerHTML=title;};prev.addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex-1+locs.length)%locs.length;});});next.addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex+1)%locs.length;});});}if(expandButton){expandButton.addEventListener("click",()=>{if(hasClass(example,"expanded")){removeClass(example,"expanded");removeClass(expandButton,"collapse");expandButton.title="Show all";scrollToLoc(example,locs[0][0],isHidden);}else{addClass(example,"expanded");addClass(expandButton,"collapse");expandButton.title="Show single example";}});}};function setupLoc(example,isHidden){const locs_str=nonnull(example.attributes.getNamedItem("data-locs")).textContent;const locs=JSON.parse(nonnull(nonnull(locs_str)));example.locs=locs;scrollToLoc(example,locs[0][0],isHidden);}const firstExamples=document.querySelectorAll(".scraped-example-list > .scraped-example");onEachLazy(firstExamples,el=>setupLoc(el,false));onEachLazy(document.querySelectorAll(".more-examples-toggle"),toggle=>{onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"),button=>{button.addEventListener("click",()=>{toggle.open=false;});});const moreExamples=toggle.querySelectorAll(".scraped-example");toggle.querySelector("summary").addEventListener("click",()=>{setTimeout(()=>{onEachLazy(moreExamples,el=>setupLoc(el,true));});},{once:true});});})();

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,17 @@
"use strict";(function(){const isSettingsPage=window.location.pathname.endsWith("/settings.html");function changeSetting(settingName,value){if(settingName==="theme"){const useSystem=value==="system preference"?"true":"false";updateLocalStorage("use-system-theme",useSystem);}updateLocalStorage(settingName,""+value);switch(settingName){case"theme":case"preferred-dark-theme":case"preferred-light-theme":updateTheme();updateLightAndDark();break;case"line-numbers":if(value===true){const f=window.rustdoc_add_line_numbers_to_examples;if(f!==undefined){f();}}else{const f=window.rustdoc_remove_line_numbers_from_examples;if(f!==undefined){f();}}break;case"hide-sidebar":if(value===true){addClass(document.documentElement,"hide-sidebar");}else{removeClass(document.documentElement,"hide-sidebar");}break;case"hide-toc":if(value===true){addClass(document.documentElement,"hide-toc");}else{removeClass(document.documentElement,"hide-toc");}break;case"hide-modnav":if(value===true){addClass(document.documentElement,"hide-modnav");}else{removeClass(document.documentElement,"hide-modnav");}break;case"sans-serif-fonts":if(value===true){addClass(document.documentElement,"sans-serif");}else{removeClass(document.documentElement,"sans-serif");}break;case"word-wrap-source-code":if(value===true){addClass(document.documentElement,"word-wrap-source-code");}else{removeClass(document.documentElement,"word-wrap-source-code");}break;}}function showLightAndDark(){removeClass(document.getElementById("preferred-light-theme"),"hidden");removeClass(document.getElementById("preferred-dark-theme"),"hidden");}function hideLightAndDark(){addClass(document.getElementById("preferred-light-theme"),"hidden");addClass(document.getElementById("preferred-dark-theme"),"hidden");}function updateLightAndDark(){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||(useSystem===null&&getSettingValue("theme")===null)){showLightAndDark();}else{hideLightAndDark();}}function setEvents(settingsElement){updateLightAndDark();onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"),toggle=>{const settingId=toggle.id;const settingValue=getSettingValue(settingId);if(settingValue!==null){toggle.checked=settingValue==="true";}toggle.onchange=()=>{changeSetting(toggle.id,toggle.checked);};});onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"),elem=>{const settingId=elem.name;let settingValue=getSettingValue(settingId);if(settingId==="theme"){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||settingValue===null){settingValue=useSystem==="false"?"light":"system preference";}}if(settingValue!==null&&settingValue!=="null"){elem.checked=settingValue===elem.value;}elem.addEventListener("change",()=>{changeSetting(elem.name,elem.value);});},);}function buildSettingsPageSections(settings){let output="";for(const setting of settings){const js_data_name=setting["js_name"];const setting_name=setting["name"];if(setting["options"]!==undefined){output+=`\
<div class="setting-line" id="${js_data_name}">
<div class="setting-radio-name">${setting_name}</div>
<div class="setting-radio-choices">`;onEach(setting["options"],option=>{const checked=option===setting["default"]?" checked":"";const full=`${js_data_name}-${option.replace(/ /g,"-")}`;output+=`\
<label for="${full}" class="setting-radio">
<input type="radio" name="${js_data_name}"
id="${full}" value="${option}"${checked}>
<span>${option}</span>
</label>`;});output+=`\
</div>
</div>`;}else{const checked=setting["default"]===true?" checked":"";output+=`\
<div class="setting-line">\
<label class="setting-check">\
<input type="checkbox" id="${js_data_name}"${checked}>\
<span>${setting_name}</span>\
</label>\
</div>`;}}return output;}function buildSettingsPage(){const theme_list=getVar("themes");const theme_names=(theme_list===null?"":theme_list).split(",").filter(t=>t);theme_names.push("light","dark","ayu");const settings=[{"name":"Theme","js_name":"theme","default":"system preference","options":theme_names.concat("system preference"),},{"name":"Preferred light theme","js_name":"preferred-light-theme","default":"light","options":theme_names,},{"name":"Preferred dark theme","js_name":"preferred-dark-theme","default":"dark","options":theme_names,},{"name":"Auto-hide item contents for large items","js_name":"auto-hide-large-items","default":true,},{"name":"Auto-hide item methods' documentation","js_name":"auto-hide-method-docs","default":false,},{"name":"Auto-hide trait implementation documentation","js_name":"auto-hide-trait-implementations","default":false,},{"name":"Directly go to item in search if there is only one result","js_name":"go-to-only-result","default":false,},{"name":"Show line numbers on code examples","js_name":"line-numbers","default":false,},{"name":"Hide persistent navigation bar","js_name":"hide-sidebar","default":false,},{"name":"Hide table of contents","js_name":"hide-toc","default":false,},{"name":"Hide module navigation","js_name":"hide-modnav","default":false,},{"name":"Disable keyboard shortcuts","js_name":"disable-shortcuts","default":false,},{"name":"Use sans serif fonts","js_name":"sans-serif-fonts","default":false,},{"name":"Word wrap source code","js_name":"word-wrap-source-code","default":false,},];const elementKind=isSettingsPage?"section":"div";const innerHTML=`<div class="settings">${buildSettingsPageSections(settings)}</div>`;const el=document.createElement(elementKind);el.id="settings";if(!isSettingsPage){el.className="popover";}el.innerHTML=innerHTML;if(isSettingsPage){const mainElem=document.getElementById(MAIN_ID);if(mainElem!==null){mainElem.appendChild(el);}}else{el.setAttribute("tabindex","-1");onEachLazy(document.querySelectorAll(".settings-menu"),menu=>{if(menu.offsetWidth!==0){menu.appendChild(el);return true;}});}return el;}const settingsMenu=buildSettingsPage();function displaySettings(){settingsMenu.style.display="";onEachLazy(document.querySelectorAll(".settings-menu"),menu=>{if(menu.offsetWidth!==0){if(!menu.contains(settingsMenu)&&settingsMenu.parentElement){settingsMenu.parentElement.removeChild(settingsMenu);menu.appendChild(settingsMenu);}return true;}});onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"),el=>{const val=getSettingValue(el.id);const checked=val==="true";if(checked!==el.checked&&val!==null){el.checked=checked;}});}function settingsBlurHandler(event){const isInPopover=onEachLazy(document.querySelectorAll(".settings-menu, .help-menu"),menu=>{return menu.contains(document.activeElement)||menu.contains(event.relatedTarget);},);if(!isInPopover){window.hidePopoverMenus();}}if(!isSettingsPage){const settingsMenu=nonnull(document.getElementById("settings"));onEachLazy(document.querySelectorAll(".settings-menu"),settingsButton=>{settingsButton.querySelector("a").onclick=event=>{if(!(event.target instanceof Element)||settingsMenu.contains(event.target)){return;}event.preventDefault();const shouldDisplaySettings=settingsMenu.style.display==="none";window.hideAllModals(false);if(shouldDisplaySettings){displaySettings();}};settingsButton.onblur=settingsBlurHandler;settingsButton.querySelector("a").onblur=settingsBlurHandler;});onEachLazy(settingsMenu.querySelectorAll("input"),el=>{el.onblur=settingsBlurHandler;});settingsMenu.onblur=settingsBlurHandler;}setTimeout(()=>{setEvents(settingsMenu);if(!isSettingsPage){displaySettings();}onEachLazy(document.querySelectorAll(".settings-menu"),settingsButton=>{removeClass(settingsButton,"rotate");});},0);})();

View File

@@ -0,0 +1 @@
"use strict";(function(){const rootPath=getVar("root-path");const NAME_OFFSET=0;const DIRS_OFFSET=1;const FILES_OFFSET=2;const RUSTDOC_MOBILE_BREAKPOINT=700;function closeSidebarIfMobile(){if(window.innerWidth<RUSTDOC_MOBILE_BREAKPOINT){updateLocalStorage("source-sidebar-show","false");}}function createDirEntry(elem,parent,fullPath,hasFoundFile){const dirEntry=document.createElement("details");const summary=document.createElement("summary");dirEntry.className="dir-entry";fullPath+=elem[NAME_OFFSET]+"/";summary.innerText=elem[NAME_OFFSET];dirEntry.appendChild(summary);const folders=document.createElement("div");folders.className="folders";if(elem[DIRS_OFFSET]){for(const dir of elem[DIRS_OFFSET]){if(createDirEntry(dir,folders,fullPath,false)){dirEntry.open=true;hasFoundFile=true;}}}dirEntry.appendChild(folders);const files=document.createElement("div");files.className="files";if(elem[FILES_OFFSET]){const w=window.location.href.split("#")[0];for(const file_text of elem[FILES_OFFSET]){const file=document.createElement("a");file.innerText=file_text;file.href=rootPath+"src/"+fullPath+file_text+".html";file.addEventListener("click",closeSidebarIfMobile);if(!hasFoundFile&&w===file.href){file.className="selected";dirEntry.open=true;hasFoundFile=true;}files.appendChild(file);}}dirEntry.appendChild(files);parent.appendChild(dirEntry);return hasFoundFile;}window.rustdocCloseSourceSidebar=()=>{removeClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","false");};window.rustdocShowSourceSidebar=()=>{addClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","true");};window.rustdocToggleSrcSidebar=()=>{if(document.documentElement.classList.contains("src-sidebar-expanded")){window.rustdocCloseSourceSidebar();}else{window.rustdocShowSourceSidebar();}};function createSrcSidebar(srcIndexStr){const container=nonnull(document.querySelector("nav.sidebar"));const sidebar=document.createElement("div");sidebar.id="src-sidebar";const srcIndex=new Map(JSON.parse(srcIndexStr));let hasFoundFile=false;for(const[key,source]of srcIndex){source[NAME_OFFSET]=key;hasFoundFile=createDirEntry(source,sidebar,"",hasFoundFile);}container.appendChild(sidebar);const selected_elem=sidebar.getElementsByClassName("selected")[0];if(typeof selected_elem!=="undefined"){selected_elem.focus();}}function highlightSrcLines(){const match=window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);if(!match){return;}let from=parseInt(match[1],10);let to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10);}if(to<from){const tmp=to;to=from;from=tmp;}const from_s=""+from;let elem=document.getElementById(from_s);if(!elem){return;}const x=document.getElementById(from_s);if(x){x.scrollIntoView();}onEachLazy(document.querySelectorAll("a[data-nosnippet]"),e=>{removeClass(e,"line-highlighted");});for(let i=from;i<=to;++i){elem=document.getElementById(""+i);if(!elem){break;}addClass(elem,"line-highlighted");}}const handleSrcHighlight=(function(){let prev_line_id=0;const set_fragment=name=>{const x=window.scrollX,y=window.scrollY;if(browserSupportsHistoryApi()){history.replaceState(null,"","#"+name);highlightSrcLines();}else{location.replace("#"+name);}window.scrollTo(x,y);};return ev=>{let cur_line_id=parseInt(ev.target.id,10);if(isNaN(cur_line_id)||ev.ctrlKey||ev.altKey||ev.metaKey){return;}ev.preventDefault();if(ev.shiftKey&&prev_line_id){if(prev_line_id>cur_line_id){const tmp=prev_line_id;prev_line_id=cur_line_id;cur_line_id=tmp;}set_fragment(prev_line_id+"-"+cur_line_id);}else{prev_line_id=cur_line_id;set_fragment(""+cur_line_id);}};}());window.addEventListener("hashchange",highlightSrcLines);onEachLazy(document.querySelectorAll("a[data-nosnippet]"),el=>{el.addEventListener("click",handleSrcHighlight);});highlightSrcLines();window.createSrcSidebar=createSrcSidebar;})();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="List of all items in this crate"><title>List of all items in this crate</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-ca0dd0c4.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="totp_rs" data-themes="" data-resource-suffix="" data-rustdoc-version="1.93.1 (01f6ddf75 2026-02-11)" data-channel="1.93.1" data-search-js="search-9e2438ea.js" data-stringdex-js="stringdex-a3946164.js" data-settings-js="settings-c38705f0.js" ><script src="../static.files/storage-e2aeef58.js"></script><script defer src="../static.files/main-a410ff4d.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-263c88ec.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc mod sys"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">All</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../totp_rs/index.html">totp_rs</a><span class="version">5.7.1</span></h2></div><div class="sidebar-elems"><section id="rustdoc-toc"><h3><a href="#structs">Crate Items</a></h3><ul class="block"><li><a href="#structs" title="Structs">Structs</a></li><li><a href="#enums" title="Enums">Enums</a></li></ul></section><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content"><div class="main-heading"><h1>List of all items</h1><rustdoc-toolbar></rustdoc-toolbar></div><h3 id="structs">Structs</h3><ul class="all-items"><li><a href="struct.Rfc6238.html">Rfc6238</a></li><li><a href="struct.TOTP.html">TOTP</a></li></ul><h3 id="enums">Enums</h3><ul class="all-items"><li><a href="enum.Algorithm.html">Algorithm</a></li><li><a href="enum.Rfc6238Error.html">Rfc6238Error</a></li><li><a href="enum.Secret.html">Secret</a></li><li><a href="enum.SecretParseError.html">SecretParseError</a></li><li><a href="enum.TotpUrlError.html">TotpUrlError</a></li></ul></section></div></main></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,35 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="This library permits the creation of 2FA authentification tokens per TOTP, the verification of said tokens, with configurable time skew, validity time of each token, algorithm and number of digits! Default features are kept as low-dependency as possible to ensure small binaries and short compilation time"><title>totp_rs - Rust</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"href="../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../static.files/rustdoc-ca0dd0c4.css"><meta name="rustdoc-vars" data-root-path="../" data-static-root-path="../static.files/" data-current-crate="totp_rs" data-themes="" data-resource-suffix="" data-rustdoc-version="1.93.1 (01f6ddf75 2026-02-11)" data-channel="1.93.1" data-search-js="search-9e2438ea.js" data-stringdex-js="stringdex-a3946164.js" data-settings-js="settings-c38705f0.js" ><script src="../static.files/storage-e2aeef58.js"></script><script defer src="../crates.js"></script><script defer src="../static.files/main-a410ff4d.js"></script><noscript><link rel="stylesheet" href="../static.files/noscript-263c88ec.css"></noscript><link rel="alternate icon" type="image/png" href="../static.files/favicon-32x32-eab170b8.png"><link rel="icon" type="image/svg+xml" href="../static.files/favicon-044be391.svg"></head><body class="rustdoc mod crate"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><rustdoc-topbar><h2><a href="#">Crate totp_rs</a></h2></rustdoc-topbar><nav class="sidebar"><div class="sidebar-crate"><h2><a href="../totp_rs/index.html">totp_rs</a><span class="version">5.7.1</span></h2></div><div class="sidebar-elems"><ul class="block"><li><a id="all-types" href="all.html">All Items</a></li></ul><section id="rustdoc-toc"><h3><a href="#">Sections</a></h3><ul class="block top-toc"><li><a href="#examples" title="Examples">Examples</a></li></ul><h3><a href="#structs">Crate Items</a></h3><ul class="block"><li><a href="#structs" title="Structs">Structs</a></li><li><a href="#enums" title="Enums">Enums</a></li></ul></section><div id="rustdoc-modnav"></div></div></nav><div class="sidebar-resizer" title="Drag to resize sidebar"></div><main><div class="width-limiter"><section id="main-content" class="content"><div class="main-heading"><h1>Crate <span>totp_rs</span>&nbsp;<button id="copy-path" title="Copy item path to clipboard">Copy item path</button></h1><rustdoc-toolbar></rustdoc-toolbar><span class="sub-heading"><a class="src" href="../src/totp_rs/lib.rs.html#1-1368">Source</a> </span></div><details class="toggle top-doc" open><summary class="hideme"><span>Expand description</span></summary><div class="docblock"><p>This library permits the creation of 2FA authentification tokens per TOTP, the verification of said tokens, with configurable time skew, validity time of each token, algorithm and number of digits! Default features are kept as low-dependency as possible to ensure small binaries and short compilation time</p>
<p>Be aware that some authenticator apps will accept the <code>SHA256</code>
and <code>SHA512</code> algorithms but silently fallback to <code>SHA1</code> which will
make the <code>check()</code> function fail due to mismatched algorithms.</p>
<p>Use the <code>SHA1</code> algorithm to avoid this problem.</p>
<h2 id="examples"><a class="doc-anchor" href="#examples">§</a>Examples</h2>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>std::time::SystemTime;
<span class="kw">use </span>totp_rs::{Algorithm, TOTP, Secret};
<span class="kw">let </span>totp = TOTP::new(
Algorithm::SHA1,
<span class="number">6</span>,
<span class="number">1</span>,
<span class="number">30</span>,
Secret::Raw(<span class="string">"TestSecretSuperSecret"</span>.as_bytes().to_vec()).to_bytes().unwrap(),
<span class="prelude-val">Some</span>(<span class="string">"Github"</span>.to_string()),
<span class="string">"constantoine@github.com"</span>.to_string(),
).unwrap();
<span class="kw">let </span>token = totp.generate_current().unwrap();
<span class="macro">println!</span>(<span class="string">"{}"</span>, token);</code></pre></div>
<div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">use </span>totp_rs::{Algorithm, TOTP};
<span class="kw">let </span>totp = TOTP::new(
Algorithm::SHA1,
<span class="number">6</span>,
<span class="number">1</span>,
<span class="number">30</span>,
<span class="string">"supersecret_topsecret"</span>.as_bytes().to_vec(),
<span class="prelude-val">Some</span>(<span class="string">"Github"</span>.to_string()),
<span class="string">"constantoine@github.com"</span>.to_string(),
).unwrap();
<span class="kw">let </span>url = totp.get_url();
<span class="macro">println!</span>(<span class="string">"{}"</span>, url);
<span class="kw">let </span>code = totp.get_qr_base64().unwrap();
<span class="macro">println!</span>(<span class="string">"{}"</span>, code);</code></pre></div></div></details><h2 id="structs" class="section-header">Structs<a href="#structs" class="anchor">§</a></h2><dl class="item-table"><dt><a class="struct" href="struct.Rfc6238.html" title="struct totp_rs::Rfc6238">Rfc6238</a></dt><dd><a href="https://tools.ietf.org/html/rfc6238">rfc-6238</a> compliant set of options to create a <a href="struct.TOTP.html">TOTP</a></dd><dt><a class="struct" href="struct.TOTP.html" title="struct totp_rs::TOTP">TOTP</a></dt><dd>TOTP holds informations as to how to generate an auth code and validate it. Its <a href="struct.TOTP.html#structfield.secret">secret</a> field is sensitive data, treat it accordingly</dd></dl><h2 id="enums" class="section-header">Enums<a href="#enums" class="anchor">§</a></h2><dl class="item-table"><dt><a class="enum" href="enum.Algorithm.html" title="enum totp_rs::Algorithm">Algorithm</a></dt><dd>Algorithm enum holds the three standards algorithms for TOTP as per the <a href="https://tools.ietf.org/html/rfc6238#appendix-A">reference implementation</a></dd><dt><a class="enum" href="enum.Rfc6238Error.html" title="enum totp_rs::Rfc6238Error">Rfc6238<wbr>Error</a></dt><dd>Error returned when input is not compliant to <a href="https://tools.ietf.org/html/rfc6238">rfc-6238</a>.</dd><dt><a class="enum" href="enum.Secret.html" title="enum totp_rs::Secret">Secret</a></dt><dd>Shared secret between client and server to validate token against/generate token from.</dd><dt><a class="enum" href="enum.SecretParseError.html" title="enum totp_rs::SecretParseError">Secret<wbr>Parse<wbr>Error</a></dt><dd>Different ways secret parsing failed.</dd><dt><a class="enum" href="enum.TotpUrlError.html" title="enum totp_rs::TotpUrlError">Totp<wbr>UrlError</a></dt><dd>Errors returned mostly upon decoding URL.</dd></dl></section></div></main></body></html>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="refresh" content="0;URL=../../totp_rs/enum.Rfc6238Error.html">
<title>Redirection</title>
</head>
<body>
<p>Redirecting to <a href="../../totp_rs/enum.Rfc6238Error.html">../../totp_rs/enum.Rfc6238Error.html</a>...</p>
<script>location.replace("../../totp_rs/enum.Rfc6238Error.html" + location.search + location.hash);</script>
</body>
</html>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="refresh" content="0;URL=../../totp_rs/struct.Rfc6238.html">
<title>Redirection</title>
</head>
<body>
<p>Redirecting to <a href="../../totp_rs/struct.Rfc6238.html">../../totp_rs/struct.Rfc6238.html</a>...</p>
<script>location.replace("../../totp_rs/struct.Rfc6238.html" + location.search + location.hash);</script>
</body>
</html>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="refresh" content="0;URL=../../totp_rs/enum.Secret.html">
<title>Redirection</title>
</head>
<body>
<p>Redirecting to <a href="../../totp_rs/enum.Secret.html">../../totp_rs/enum.Secret.html</a>...</p>
<script>location.replace("../../totp_rs/enum.Secret.html" + location.search + location.hash);</script>
</body>
</html>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="refresh" content="0;URL=../../totp_rs/enum.SecretParseError.html">
<title>Redirection</title>
</head>
<body>
<p>Redirecting to <a href="../../totp_rs/enum.SecretParseError.html">../../totp_rs/enum.SecretParseError.html</a>...</p>
<script>location.replace("../../totp_rs/enum.SecretParseError.html" + location.search + location.hash);</script>
</body>
</html>

View File

@@ -0,0 +1 @@
window.SIDEBAR_ITEMS = {"enum":["Algorithm","Rfc6238Error","Secret","SecretParseError","TotpUrlError"],"struct":["Rfc6238","TOTP"]};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="refresh" content="0;URL=../../totp_rs/enum.TotpUrlError.html">
<title>Redirection</title>
</head>
<body>
<p>Redirecting to <a href="../../totp_rs/enum.TotpUrlError.html">../../totp_rs/enum.TotpUrlError.html</a>...</p>
<script>location.replace("../../totp_rs/enum.TotpUrlError.html" + location.search + location.hash);</script>
</body>
</html>

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/clone/trait.Clone.html\" title=\"trait core::clone::Clone\">Clone</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/clone/trait.Clone.html\" title=\"trait core::clone::Clone\">Clone</a> for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/clone/trait.Clone.html\" title=\"trait core::clone::Clone\">Clone</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/clone/trait.Clone.html\" title=\"trait core::clone::Clone\">Clone</a> for <a class=\"struct\" href=\"totp_rs/struct.Rfc6238.html\" title=\"struct totp_rs::Rfc6238\">Rfc6238</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/clone/trait.Clone.html\" title=\"trait core::clone::Clone\">Clone</a> for <a class=\"struct\" href=\"totp_rs/struct.TOTP.html\" title=\"struct totp_rs::TOTP\">TOTP</a>"]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[1266]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/cmp/trait.Eq.html\" title=\"trait core::cmp::Eq\">Eq</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/cmp/trait.Eq.html\" title=\"trait core::cmp::Eq\">Eq</a> for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/cmp/trait.Eq.html\" title=\"trait core::cmp::Eq\">Eq</a> for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/cmp/trait.Eq.html\" title=\"trait core::cmp::Eq\">Eq</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/cmp/trait.Eq.html\" title=\"trait core::cmp::Eq\">Eq</a> for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>"]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[1228]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/cmp/trait.PartialEq.html\" title=\"trait core::cmp::PartialEq\">PartialEq</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/cmp/trait.PartialEq.html\" title=\"trait core::cmp::PartialEq\">PartialEq</a> for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/cmp/trait.PartialEq.html\" title=\"trait core::cmp::PartialEq\">PartialEq</a> for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/cmp/trait.PartialEq.html\" title=\"trait core::cmp::PartialEq\">PartialEq</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/cmp/trait.PartialEq.html\" title=\"trait core::cmp::PartialEq\">PartialEq</a> for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/cmp/trait.PartialEq.html\" title=\"trait core::cmp::PartialEq\">PartialEq</a> for <a class=\"struct\" href=\"totp_rs/struct.TOTP.html\" title=\"struct totp_rs::TOTP\">TOTP</a>"]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[1582]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/convert/trait.From.html\" title=\"trait core::convert::From\">From</a>&lt;<a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>&gt; for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>"]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[392]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/convert/trait.TryFrom.html\" title=\"trait core::convert::TryFrom\">TryFrom</a>&lt;<a class=\"struct\" href=\"totp_rs/struct.Rfc6238.html\" title=\"struct totp_rs::Rfc6238\">Rfc6238</a>&gt; for <a class=\"struct\" href=\"totp_rs/struct.TOTP.html\" title=\"struct totp_rs::TOTP\">TOTP</a>"]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[374]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/default/trait.Default.html\" title=\"trait core::default::Default\">Default</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>"]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[273]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/error/trait.Error.html\" title=\"trait core::error::Error\">Error</a> for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/error/trait.Error.html\" title=\"trait core::error::Error\">Error</a> for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/error/trait.Error.html\" title=\"trait core::error::Error\">Error</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/error/trait.Error.html\" title=\"trait core::error::Error\">Error</a> for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>"]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[1043]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Debug.html\" title=\"trait core::fmt::Debug\">Debug</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Debug.html\" title=\"trait core::fmt::Debug\">Debug</a> for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Debug.html\" title=\"trait core::fmt::Debug\">Debug</a> for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Debug.html\" title=\"trait core::fmt::Debug\">Debug</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Debug.html\" title=\"trait core::fmt::Debug\">Debug</a> for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Debug.html\" title=\"trait core::fmt::Debug\">Debug</a> for <a class=\"struct\" href=\"totp_rs/struct.Rfc6238.html\" title=\"struct totp_rs::Rfc6238\">Rfc6238</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Debug.html\" title=\"trait core::fmt::Debug\">Debug</a> for <a class=\"struct\" href=\"totp_rs/struct.TOTP.html\" title=\"struct totp_rs::TOTP\">TOTP</a>"]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[1756]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Display.html\" title=\"trait core::fmt::Display\">Display</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Display.html\" title=\"trait core::fmt::Display\">Display</a> for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Display.html\" title=\"trait core::fmt::Display\">Display</a> for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Display.html\" title=\"trait core::fmt::Display\">Display</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Display.html\" title=\"trait core::fmt::Display\">Display</a> for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/fmt/trait.Display.html\" title=\"trait core::fmt::Display\">Display</a> for <a class=\"struct\" href=\"totp_rs/struct.TOTP.html\" title=\"struct totp_rs::TOTP\">TOTP</a>"]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[1546]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Copy.html\" title=\"trait core::marker::Copy\">Copy</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>"]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[262]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Freeze.html\" title=\"trait core::marker::Freeze\">Freeze</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>",1,["totp_rs::Algorithm"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Freeze.html\" title=\"trait core::marker::Freeze\">Freeze</a> for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>",1,["totp_rs::rfc::Rfc6238Error"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Freeze.html\" title=\"trait core::marker::Freeze\">Freeze</a> for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>",1,["totp_rs::secret::Secret"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Freeze.html\" title=\"trait core::marker::Freeze\">Freeze</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>",1,["totp_rs::secret::SecretParseError"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Freeze.html\" title=\"trait core::marker::Freeze\">Freeze</a> for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>",1,["totp_rs::url_error::TotpUrlError"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Freeze.html\" title=\"trait core::marker::Freeze\">Freeze</a> for <a class=\"struct\" href=\"totp_rs/struct.Rfc6238.html\" title=\"struct totp_rs::Rfc6238\">Rfc6238</a>",1,["totp_rs::rfc::Rfc6238"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Freeze.html\" title=\"trait core::marker::Freeze\">Freeze</a> for <a class=\"struct\" href=\"totp_rs/struct.TOTP.html\" title=\"struct totp_rs::TOTP\">TOTP</a>",1,["totp_rs::TOTP"]]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[2034]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Send.html\" title=\"trait core::marker::Send\">Send</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>",1,["totp_rs::Algorithm"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Send.html\" title=\"trait core::marker::Send\">Send</a> for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>",1,["totp_rs::rfc::Rfc6238Error"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Send.html\" title=\"trait core::marker::Send\">Send</a> for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>",1,["totp_rs::secret::Secret"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Send.html\" title=\"trait core::marker::Send\">Send</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>",1,["totp_rs::secret::SecretParseError"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Send.html\" title=\"trait core::marker::Send\">Send</a> for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>",1,["totp_rs::url_error::TotpUrlError"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Send.html\" title=\"trait core::marker::Send\">Send</a> for <a class=\"struct\" href=\"totp_rs/struct.Rfc6238.html\" title=\"struct totp_rs::Rfc6238\">Rfc6238</a>",1,["totp_rs::rfc::Rfc6238"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Send.html\" title=\"trait core::marker::Send\">Send</a> for <a class=\"struct\" href=\"totp_rs/struct.TOTP.html\" title=\"struct totp_rs::TOTP\">TOTP</a>",1,["totp_rs::TOTP"]]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[1992]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.StructuralPartialEq.html\" title=\"trait core::marker::StructuralPartialEq\">StructuralPartialEq</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.StructuralPartialEq.html\" title=\"trait core::marker::StructuralPartialEq\">StructuralPartialEq</a> for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.StructuralPartialEq.html\" title=\"trait core::marker::StructuralPartialEq\">StructuralPartialEq</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>"],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.StructuralPartialEq.html\" title=\"trait core::marker::StructuralPartialEq\">StructuralPartialEq</a> for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>"]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[1228]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Sync.html\" title=\"trait core::marker::Sync\">Sync</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>",1,["totp_rs::Algorithm"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Sync.html\" title=\"trait core::marker::Sync\">Sync</a> for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>",1,["totp_rs::rfc::Rfc6238Error"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Sync.html\" title=\"trait core::marker::Sync\">Sync</a> for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>",1,["totp_rs::secret::Secret"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Sync.html\" title=\"trait core::marker::Sync\">Sync</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>",1,["totp_rs::secret::SecretParseError"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Sync.html\" title=\"trait core::marker::Sync\">Sync</a> for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>",1,["totp_rs::url_error::TotpUrlError"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Sync.html\" title=\"trait core::marker::Sync\">Sync</a> for <a class=\"struct\" href=\"totp_rs/struct.Rfc6238.html\" title=\"struct totp_rs::Rfc6238\">Rfc6238</a>",1,["totp_rs::rfc::Rfc6238"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Sync.html\" title=\"trait core::marker::Sync\">Sync</a> for <a class=\"struct\" href=\"totp_rs/struct.TOTP.html\" title=\"struct totp_rs::TOTP\">TOTP</a>",1,["totp_rs::TOTP"]]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[1992]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Unpin.html\" title=\"trait core::marker::Unpin\">Unpin</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>",1,["totp_rs::Algorithm"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Unpin.html\" title=\"trait core::marker::Unpin\">Unpin</a> for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>",1,["totp_rs::rfc::Rfc6238Error"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Unpin.html\" title=\"trait core::marker::Unpin\">Unpin</a> for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>",1,["totp_rs::secret::Secret"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Unpin.html\" title=\"trait core::marker::Unpin\">Unpin</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>",1,["totp_rs::secret::SecretParseError"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Unpin.html\" title=\"trait core::marker::Unpin\">Unpin</a> for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>",1,["totp_rs::url_error::TotpUrlError"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Unpin.html\" title=\"trait core::marker::Unpin\">Unpin</a> for <a class=\"struct\" href=\"totp_rs/struct.Rfc6238.html\" title=\"struct totp_rs::Rfc6238\">Rfc6238</a>",1,["totp_rs::rfc::Rfc6238"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/marker/trait.Unpin.html\" title=\"trait core::marker::Unpin\">Unpin</a> for <a class=\"struct\" href=\"totp_rs/struct.TOTP.html\" title=\"struct totp_rs::TOTP\">TOTP</a>",1,["totp_rs::TOTP"]]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[2013]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl UnsafeUnpin for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>",1,["totp_rs::Algorithm"]],["impl UnsafeUnpin for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>",1,["totp_rs::rfc::Rfc6238Error"]],["impl UnsafeUnpin for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>",1,["totp_rs::secret::Secret"]],["impl UnsafeUnpin for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>",1,["totp_rs::secret::SecretParseError"]],["impl UnsafeUnpin for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>",1,["totp_rs::url_error::TotpUrlError"]],["impl UnsafeUnpin for <a class=\"struct\" href=\"totp_rs/struct.Rfc6238.html\" title=\"struct totp_rs::Rfc6238\">Rfc6238</a>",1,["totp_rs::rfc::Rfc6238"]],["impl UnsafeUnpin for <a class=\"struct\" href=\"totp_rs/struct.TOTP.html\" title=\"struct totp_rs::TOTP\">TOTP</a>",1,["totp_rs::TOTP"]]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[1145]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.RefUnwindSafe.html\" title=\"trait core::panic::unwind_safe::RefUnwindSafe\">RefUnwindSafe</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>",1,["totp_rs::Algorithm"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.RefUnwindSafe.html\" title=\"trait core::panic::unwind_safe::RefUnwindSafe\">RefUnwindSafe</a> for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>",1,["totp_rs::rfc::Rfc6238Error"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.RefUnwindSafe.html\" title=\"trait core::panic::unwind_safe::RefUnwindSafe\">RefUnwindSafe</a> for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>",1,["totp_rs::secret::Secret"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.RefUnwindSafe.html\" title=\"trait core::panic::unwind_safe::RefUnwindSafe\">RefUnwindSafe</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>",1,["totp_rs::secret::SecretParseError"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.RefUnwindSafe.html\" title=\"trait core::panic::unwind_safe::RefUnwindSafe\">RefUnwindSafe</a> for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>",1,["totp_rs::url_error::TotpUrlError"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.RefUnwindSafe.html\" title=\"trait core::panic::unwind_safe::RefUnwindSafe\">RefUnwindSafe</a> for <a class=\"struct\" href=\"totp_rs/struct.Rfc6238.html\" title=\"struct totp_rs::Rfc6238\">Rfc6238</a>",1,["totp_rs::rfc::Rfc6238"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.RefUnwindSafe.html\" title=\"trait core::panic::unwind_safe::RefUnwindSafe\">RefUnwindSafe</a> for <a class=\"struct\" href=\"totp_rs/struct.TOTP.html\" title=\"struct totp_rs::TOTP\">TOTP</a>",1,["totp_rs::TOTP"]]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[2342]}

View File

@@ -0,0 +1,9 @@
(function() {
var implementors = Object.fromEntries([["totp_rs",[["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.UnwindSafe.html\" title=\"trait core::panic::unwind_safe::UnwindSafe\">UnwindSafe</a> for <a class=\"enum\" href=\"totp_rs/enum.Algorithm.html\" title=\"enum totp_rs::Algorithm\">Algorithm</a>",1,["totp_rs::Algorithm"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.UnwindSafe.html\" title=\"trait core::panic::unwind_safe::UnwindSafe\">UnwindSafe</a> for <a class=\"enum\" href=\"totp_rs/enum.Rfc6238Error.html\" title=\"enum totp_rs::Rfc6238Error\">Rfc6238Error</a>",1,["totp_rs::rfc::Rfc6238Error"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.UnwindSafe.html\" title=\"trait core::panic::unwind_safe::UnwindSafe\">UnwindSafe</a> for <a class=\"enum\" href=\"totp_rs/enum.Secret.html\" title=\"enum totp_rs::Secret\">Secret</a>",1,["totp_rs::secret::Secret"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.UnwindSafe.html\" title=\"trait core::panic::unwind_safe::UnwindSafe\">UnwindSafe</a> for <a class=\"enum\" href=\"totp_rs/enum.SecretParseError.html\" title=\"enum totp_rs::SecretParseError\">SecretParseError</a>",1,["totp_rs::secret::SecretParseError"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.UnwindSafe.html\" title=\"trait core::panic::unwind_safe::UnwindSafe\">UnwindSafe</a> for <a class=\"enum\" href=\"totp_rs/enum.TotpUrlError.html\" title=\"enum totp_rs::TotpUrlError\">TotpUrlError</a>",1,["totp_rs::url_error::TotpUrlError"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.UnwindSafe.html\" title=\"trait core::panic::unwind_safe::UnwindSafe\">UnwindSafe</a> for <a class=\"struct\" href=\"totp_rs/struct.Rfc6238.html\" title=\"struct totp_rs::Rfc6238\">Rfc6238</a>",1,["totp_rs::rfc::Rfc6238"]],["impl <a class=\"trait\" href=\"https://doc.rust-lang.org/1.93.1/core/panic/unwind_safe/trait.UnwindSafe.html\" title=\"trait core::panic::unwind_safe::UnwindSafe\">UnwindSafe</a> for <a class=\"struct\" href=\"totp_rs/struct.TOTP.html\" title=\"struct totp_rs::TOTP\">TOTP</a>",1,["totp_rs::TOTP"]]]]]);
if (window.register_implementors) {
window.register_implementors(implementors);
} else {
window.pending_implementors = implementors;
}
})()
//{"start":57,"fragment_lengths":[2279]}