<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/rss-style.xsl" type="text/xsl"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	     xmlns:dc="http://purl.org/dc/elements/1.1/"
	   xmlns:atom="http://www.w3.org/2005/Atom"
	     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>totp &#8211; Terence Eden’s Blog</title>
	<atom:link href="https://shkspr.mobi/blog/tag/totp/feed/" rel="self" type="application/rss+xml" />
	<link>https://shkspr.mobi/blog</link>
	<description>Regular nonsense about tech and its effects 🙃</description>
	<lastBuildDate>Fri, 01 Aug 2025 09:31:17 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://shkspr.mobi/blog/wp-content/uploads/2023/07/cropped-avatar-32x32.jpeg</url>
	<title>totp &#8211; Terence Eden’s Blog</title>
	<link>https://shkspr.mobi/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title><![CDATA[Some minor bugs in Proton's new Authenticator app]]></title>
		<link>https://shkspr.mobi/blog/2025/08/some-minor-bugs-in-protons-new-authenticator-app/</link>
					<comments>https://shkspr.mobi/blog/2025/08/some-minor-bugs-in-protons-new-authenticator-app/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 31 Aug 2025 11:34:55 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[2fa]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[MFA]]></category>
		<category><![CDATA[Proton]]></category>
		<category><![CDATA[totp]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=62350</guid>

					<description><![CDATA[I maintain a a test-suite for TOTP codes. It contains a bunch of codes which adhere to the specification, some of which stretch it to breaking point, and some that are completely invalid.  These codes are a good starting point for checking whether a 2FA / MFA app works correctly.  Proton have release a swish new authenticator app for Android, iOS, Mac, Linux and Windows. Sadly, their open source…]]></description>
										<content:encoded><![CDATA[<p>I maintain a <a href="https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/">a test-suite for TOTP codes</a>. It contains a bunch of codes which adhere to the specification, some of which stretch it to breaking point, and some that are completely invalid.  These codes are a good starting point for checking whether a 2FA / MFA app works correctly.</p>

<p>Proton have release a swish <a href="https://proton.me/authenticator">new authenticator app</a> for Android, iOS, Mac, Linux and Windows. Sadly, their <a href="https://github.com/protonpass/android-pass">open source repository</a> doesn't allow for bug reports so I'm blogging in public instead.</p>

<p>The good news is, the majority of tests pass. It accepts a wide range of acceptable codes and refuses to store most broken ones. There are a few niggles though.  None of these are severe security issues, but they probably ought to be fixed.</p>

<h2 id="very-long-codes"><a href="https://shkspr.mobi/blog/2025/08/some-minor-bugs-in-protons-new-authenticator-app/#very-long-codes">Very long codes</a></h2>

<p>The maximum number of digits which can be generated by the standard TOTP algorithm is 10.  Proton happily scans codes containing 1 - 9 digits, but complains about 10 digit codes.  So this fails:</p>

<p><code>otpauth://totp/issuer%3Aaccount%20name?secret=QWERTYUIOP&amp;digits=10&amp;issuer=issuer&amp;algorithm=SHA1&amp;period=30</code></p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/08/10digit.webp" alt="QR code for a 10 digit TOTP." width="360" height="360" class="aligncenter size-full wp-image-62370">

<p>The TOTP RFC says:</p>

<blockquote><p>Basically, the output of the HMAC-SHA-1 calculation is truncated to obtain user-friendly values</p>

<p><a href="https://datatracker.ietf.org/doc/html/rfc6238#section-1.2">1.2. Background</a></p></blockquote>

<p>But doesn't say how far to truncate.</p>

<p>There's nothing I can see in the spec that <em>prevents</em> an implementer using all 10.</p>

<p><strong>Risk:</strong> The user may not be able to store a valid code.</p>

<p><strong>Recommendation:</strong> Allow 10 digit codes.</p>

<h2 id="invalid-secrets"><a href="https://shkspr.mobi/blog/2025/08/some-minor-bugs-in-protons-new-authenticator-app/#invalid-secrets">Invalid Secrets</a></h2>

<p>Here we get to yet another <a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/">deficiency in the TOTP specification</a>.  How is a secret defined?</p>

<p>Google says the secret is:</p>

<blockquote><p>an arbitrary key value encoded in Base32 according to RFC 3548. The padding specified in RFC 3548 section 2.2 is not required and should be omitted.</p></blockquote>

<p>Whereas Apple says it is:</p>

<blockquote><p>An arbitrary key value encoded in Base32. Secrets should be at least 160 bits.</p></blockquote>

<p>Either way, <a href="https://www.rfc-editor.org/rfc/rfc3548#section-5">the Base32 alphabet</a> contains only uppercase letters and a few numbers.  What happens if we give it a secret like <code>QWERT!£$%^)*(YUIOP</code>?</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/08/invaid-secret.webp" alt="QR code for an invalid secret." width="360" height="360" class="aligncenter size-full wp-image-62371">

<p>Proton Authenticator just accepts it. It stores the full secret but I'm not sure how it generates the code based on it.</p>

<p><strong>Risk:</strong> The code may be generated incorrectly.</p>

<p><strong>Recommendation:</strong> Warn the user that the secret may be invalid and that a correct 2FA code cannot be guaranteed.</p>

<h2 id="issuer-mismatch"><a href="https://shkspr.mobi/blog/2025/08/some-minor-bugs-in-protons-new-authenticator-app/#issuer-mismatch">Issuer Mismatch</a></h2>

<p>In this example, the first issuer is example.com but the second issuer is microsoft.com</p>

<p><code>otpauth://totp/example.com%3Aaccount%20name?secret=QWERTYUIOP&amp;digits=6&amp;issuer=microsoft.com&amp;algorithm=SHA1&amp;period=30</code></p>

<p>What should the TOTP reader do with this? Proton chooses microsoft.com.</p>

<p>This is something which, again, is inconsistent between major providers.</p>

<p>Google says this parameter is:</p>

<blockquote><p><strong>Strongly Recommended</strong> The issuer parameter is a string value indicating the provider or service this account is associated with, URL-encoded according to RFC 3986. If the issuer parameter is absent, issuer information may be taken from the issuer prefix of the label. If both issuer parameter and issuer label prefix are present, they should be equal.</p></blockquote>

<p>Apple merely says:</p>

<blockquote><p>The domain of the site or app. The password manager uses this field to suggest credentials when setting up a new code generator.</p></blockquote>

<p>Yubico equivocates with</p>

<blockquote><p>The issuer parameter is recommended, but it can be absent. Also, the issuer parameter and issuer string in label should be equal.</p></blockquote>

<p><strong>Risk:</strong> The code may be displayed with the wrong issuer.</p>

<p><strong>Recommendation:</strong> Warn the user that there are multiple issuers. Let them choose which one is correct.</p>

<h2 id="dealing-with-defaults"><a href="https://shkspr.mobi/blog/2025/08/some-minor-bugs-in-protons-new-authenticator-app/#dealing-with-defaults">Dealing With Defaults</a></h2>

<p>What should a TOTP app do if there is missing information? Proton does the following:</p>

<ul>
<li>If the code has no number set for digits, it defaults to 6</li>
<li>If the code has no time set for period, it defaults to 30</li>
<li>If the code has no algorithm, it defaults to SHA1</li>
</ul>

<p><strong>Risk:</strong> Low. The user normally has to confirm with the issuer that the the TOTP code has been correctly stored.</p>

<p><strong>Recommendation:</strong> Let the user know that the code has missing data and may not be correct.</p>

<h2 id="odd-labels"><a href="https://shkspr.mobi/blog/2025/08/some-minor-bugs-in-protons-new-authenticator-app/#odd-labels">Odd Labels</a></h2>

<p>The label allows you to have multiple codes for the same service. For example <code>Big Bank:Personal Account</code> and <code>Big Bank:Family Savings</code>.  The Google spec is slightly confusing:</p>

<blockquote><p>The issuer prefix and account name should be separated by a literal or url-encoded colon, and optional spaces may precede the account name. Neither issuer nor account name may themselves contain a colon.</p></blockquote>

<p>What happens if there are spaces before the account name?</p>

<p><code>otpauth://totp/Spaces:%20%20%20%20%20%20%20%20%20%20%20%20test%40example.com?secret=QWERTYUIOP&amp;digits=6&amp;issuer=&amp;algorithm=SHA1&amp;period=30</code>
<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/08/spaces.webp" alt="QR code for a TOTP." width="400" height="400" class="aligncenter size-full wp-image-62374"></p>

<p>Proton strips the spaces (probably wise) but also removes the issuer.</p>

<p><strong>Risk:</strong> The user will not know which account the code is for.</p>

<p><strong>Recommendation:</strong> Keep the issuer.</p>

<h2 id="timeline"><a href="https://shkspr.mobi/blog/2025/08/some-minor-bugs-in-protons-new-authenticator-app/#timeline">Timeline</a></h2>

<p>These aren't particularly high severity bugs, nevertheless I like to give organisations a bit of time to respond.</p>

<ul>
<li>2025-07-31 - Discovered.</li>
<li>2025-08-01 - Disclosed <a href="https://bsky.app/profile/proton.me/post/3lvbnajumh22e">via a web form</a>.</li>
<li>2025-08-31 - Automatically published.</li>
</ul>

<h2 id="next-steps"><a href="https://shkspr.mobi/blog/2025/08/some-minor-bugs-in-protons-new-authenticator-app/#next-steps">Next Steps</a></h2>

<ul>
<li>If you're a user, <a href="https://codeberg.org/edent/TOTP_Test_Suite">please contribute tests</a> or give feedback.</li>
<li>If you're a developer, please check your app conforms to the specification.</li>
<li>If you're from a security company - wanna help me write up a proper RFC so this doesn't cause issues in the future?</li>
</ul>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=62350&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/08/some-minor-bugs-in-protons-new-authenticator-app/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Towards a test-suite for TOTP codes]]></title>
		<link>https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/</link>
					<comments>https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 02 Mar 2025 12:34:39 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[2fa]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[HTOP]]></category>
		<category><![CDATA[MFA]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[totp]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=58593</guid>

					<description><![CDATA[Because I&#039;m a massive nerd, I actually try to read specification documents. As I&#039;ve ranted ad nauseam before, the current TOTP spec is irresponsibly obsolete.  The three major implementations of the spec - Google, Apple, and Yubico - all subtly disagree on how it should be implemented. Every other MFA app has their own idiosyncratic variants. The official RFC is infuriatingly vague. That&#039;s no…]]></description>
										<content:encoded><![CDATA[<p>Because I'm a massive nerd, I <em>actually try to read</em> specification documents. As I've ranted <i lang="la">ad nauseam</i> before, the current TOTP<sup id="fnref:totp"><a href="https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/#fn:totp" class="footnote-ref" title="Time-based One Time Passwords. Not the TV show you remember from your youth, grandad." role="doc-noteref">0</a></sup> spec is <a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/">irresponsibly obsolete</a>.</p>

<p>The three major implementations of the spec - <a href="https://github.com/google/google-authenticator/wiki/Key-Uri-Format">Google</a>, <a href="https://developer.apple.com/documentation/authenticationservices/securing-logins-with-icloud-keychain-verification-codes#3795996">Apple</a>, and <a href="https://docs.yubico.com/yesdk/users-manual/application-oath/uri-string-format.html">Yubico</a> - all subtly disagree on how it should be implemented. Every other MFA app has their own idiosyncratic variants. The <a href="https://datatracker.ietf.org/doc/html/rfc6238">official RFC is infuriatingly vague</a>. That's no good for a security specification. Multiple implementations are great, multiple interpretations are not.</p>

<p>So I've <a href="https://edent.codeberg.page/TOTP_Test_Suite/">built a nascent test suite</a> - you can use it to see if your favourite app can correctly implement the TOTP standard.</p>

<p><a href="https://edent.codeberg.page/TOTP_Test_Suite/"><img src="https://shkspr.mobi/blog/wp-content/uploads/2025/03/TOTP-Tests-fs8.png" alt="Screenshot showing a QR code and numeric codes." width="1024" height="813" class="aligncenter size-full wp-image-58598"></a></p>

<p>Please do contribute tests and / or feedback.</p>

<p>Here's what the standard <em>actually</em> says - see if you can find apps which don't implement it correctly.</p>

<h2 id="background"><a href="https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/#background">Background</a></h2>

<p>Time-based One Time Passwords are based on HOTP - HMAC-Based One-Time Password.</p>

<p>HOTP uses counters; a new password is regularly generated. TOTP uses time as the counter. At the time of writing this post, there have been about 1,740,800,000 seconds since the UNIX Epoc. So a TOTP with an period of 30 seconds is on counter (1,740,800,000 ➗ 30) = 58,026,666.  Every 30 seconds, that counter increments by one.</p>

<h3 id="number-of-digits"><a href="https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/#number-of-digits">Number of digits</a></h3>

<p>How many digits should your 2FA token have? Google says 6 or 8. YubiCo graciously allows 7. Why those limits? Who knows!?</p>

<p><a href="https://datatracker.ietf.org/doc/html/rfc4226#section-5.4">The HOTP specification gives an <em>example</em> of 6 digits</a>.  The example generates a code of <code>0x50ef7f19</code> which, in decimal, is <code>1357872921</code>. It then takes the last 6 digits to produce the code <code>872921</code>.</p>

<p>The TOTP RFC says:</p>

<blockquote><p>Basically, the output of the HMAC-SHA-1 calculation is truncated to obtain user-friendly values</p>

<p><a href="https://datatracker.ietf.org/doc/html/rfc6238#section-1.2">1.2. Background</a></p></blockquote>

<p>But doesn't say how far to truncate.</p>

<p>There's nothing I can see in the spec that <em>prevents</em> an implementer using all 10.  The HOTP spec, however, <em>does</em> place a minimum requirement - but no maximum:</p>

<blockquote><p>Implementations MUST extract a 6-digit code at a minimum and possibly 7 and 8-digit code.
Depending on security requirements, Digit = 7 or more SHOULD be considered in order to extract a longer HOTP value.
<a href="https://datatracker.ietf.org/doc/html/rfc4226#section-5.3">RFC 4226 - 5.3. Generating an HOTP Value</a></p></blockquote>

<p>(As a minor point, the first digit is restricted to 0-2, so being 10 digits long isn't significantly stronger than 9 digits.)</p>

<p>Is a 4 digit code acceptable? The security might be weaker, but the usability is greater. Most apps will allow a <em>one</em> digit code to be returned. If no digits are specified, what should the default be?</p>

<h3 id="algorithm"><a href="https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/#algorithm">Algorithm</a></h3>

<p>The given algorithm in the HOTP spec is SHA-1.</p>

<blockquote><p>In order to create the HOTP value, we will use the HMAC-SHA-1 algorithm
<a href="https://datatracker.ietf.org/doc/html/rfc4226#section-5.2">RFC 4226 - 5.2.  Description</a></p></blockquote>

<p>As we now know, SHA-1 has some fundamental weaknesses. The spec comments (perhaps somewhat naïvely) about SHA-1:</p>

<blockquote><p>The new attacks on SHA-1 have no impact on the security of HMAC-SHA-1.
<a href="https://datatracker.ietf.org/doc/html/rfc4226#appendix-B.2">RFC 4226 - B.2.  HMAC-SHA-1 Status</a></p></blockquote>

<p>I daresay that's accurate. But the TOTP authors disagree and allow for some different algorithms to be used. The specification for HMAC says:</p>

<blockquote><p>HMAC can be used with <em>any</em> iterative cryptographic hash function, e.g., MD5, SHA-1
[Emphasis added]
<a href="https://datatracker.ietf.org/doc/html/rfc2104">RFC 2104 - HMAC: Keyed-Hashing for Message Authentication</a></p></blockquote>

<p>So most TOTP implementation allow SHA-1, SHA-256, and SHA-512.</p>

<blockquote><p>TOTP implementations MAY use HMAC-SHA-256 or HMAC-SHA-512 functions […] instead of the HMAC-SHA-1 function that has been specified for the HOTP computation
<a href="https://datatracker.ietf.org/doc/html/rfc6238#section-1.2">RFC 6238 - TOTP: Time-Based One-Time Password Algorithm</a></p></blockquote>

<p>But the HOTP spec goes on to say:</p>

<blockquote><p>Current candidates for such hash functions include SHA-1, MD5, RIPEMD-128/160.
These different realizations of HMAC will be denoted by HMAC-SHA1, HMAC-MD5, HMAC-RIPEMD
<a href="https://datatracker.ietf.org/doc/html/rfc2104#section-1">RFC 2104 - Introduction</a></p></blockquote>

<p>So, should your TOTP app be able to handle an MD5 HMAC, or even SHA3-384? Will it?  If no algorithm is specified, what should the default be?</p>

<h3 id="period"><a href="https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/#period">Period</a></h3>

<p>As discussed, this is what increments the counter for HOTP. The <a href="https://github.com/google/google-authenticator/wiki/Key-Uri-Format">Google Spec</a> says:</p>

<blockquote><p>The period parameter defines a period that a TOTP code will be valid for, in seconds. The default value is 30.</p></blockquote>

<p>The TOTP RFC says:</p>

<blockquote><p>We RECOMMEND a default time-step size of 30 seconds
<a href="https://datatracker.ietf.org/doc/html/rfc6238#section-5.2">5.2. Validation and Time-Step Size</a></p></blockquote>

<p>It doesn't make sense to have a negative number of second. But what about one second? What about a thousand? Lots of apps artificially restrict TOTP codes to 15, 30, or 60 seconds. But there's no specification to define a maximum or minimum value.</p>

<p>A user with mobility difficulties or on a high-latency connection probably wants a 5 minute validity period. Conversely, machine-to-machine communication can probably be done with a single-second (or lower) time period.</p>

<h3 id="secret"><a href="https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/#secret">Secret</a></h3>

<p>Google says the secret is</p>

<blockquote><p>an arbitrary key value encoded in Base32 according to RFC 3548. The padding specified in RFC 3548 section 2.2 is not required and should be omitted.</p></blockquote>

<p>Whereas Apple says it is:</p>

<blockquote><p>An arbitrary key value encoded in Base32. Secrets should be at least 160 bits.</p></blockquote>

<p>Can a shared secret be a single character? What about a thousand? Will padding characters cause a secret to be rejected or can they be safely stripped?</p>

<h3 id="label"><a href="https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/#label">Label</a></h3>

<p>The label allows you to have multiple codes for the same service. For example <code>Big Bank:Personal Account</code> and <code>Big Bank:Family Savings</code>.  The Google spec is slightly confusing:</p>

<blockquote><p>The issuer prefix and account name should be separated by a literal or url-encoded colon, and optional spaces may precede the account name. Neither issuer nor account name may themselves contain a colon.</p></blockquote>

<p>What happens if they are <em>not</em> URl encoded? What about Matrix accounts which use a colon in their account name? Why are spaces allowed to precede the account name? Is there any practical limit to the length of these strings?</p>

<p>If no label is specified, what should the default be?</p>

<h3 id="issuer"><a href="https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/#issuer">Issuer</a></h3>

<p>Google says this parameter is:</p>

<blockquote><p><strong>Strongly Recommended</strong> The issuer parameter is a string value indicating the provider or service this account is associated with, URL-encoded according to RFC 3986. If the issuer parameter is absent, issuer information may be taken from the issuer prefix of the label. If both issuer parameter and issuer label prefix are present, they should be equal.</p></blockquote>

<p>Apple merely says:</p>

<blockquote><p>The domain of the site or app. The password manager uses this field to suggest credentials when setting up a new code generator.</p></blockquote>

<p>Yubico equivocates with</p>

<blockquote><p>The issuer parameter is recommended, but it can be absent. Also, the issuer parameter and issuer string in label should be equal.</p></blockquote>

<p>If it isn't a domain, will Apple reject it? What happens if the issuer and the label don't match?</p>

<h2 id="next-steps"><a href="https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/#next-steps">Next Steps</a></h2>

<ul>
<li>If you're a user, <a href="https://codeberg.org/edent/TOTP_Test_Suite">please contribute tests</a> or give feedback.</li>
<li>If you're a developer, please check your app conforms to the specification.</li>
<li>If you're from Google, Apple, Yubico, or another security company - wanna help me write up a proper RFC so this doesn't cause issues in the future?</li>
</ul>

<div id="footnotes" role="doc-endnotes">
<hr>
<ol start="0">

<li id="fn:totp">
<p>Time-based One Time Passwords. Not the TV show you remember from your youth, grandad.&nbsp;<a href="https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/#fnref:totp" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=58593&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/03/towards-a-test-suite-for-totp-codes/feed/</wfw:commentRss>
			<slash:comments>11</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Using the Web Crypto API to Generate TOTP Codes in JavaScript Without 3rd Party Libraries]]></title>
		<link>https://shkspr.mobi/blog/2025/03/using-the-web-crypto-api-to-generate-totp-codes-in-javascript-without-3rd-party-libraries/</link>
					<comments>https://shkspr.mobi/blog/2025/03/using-the-web-crypto-api-to-generate-totp-codes-in-javascript-without-3rd-party-libraries/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 01 Mar 2025 12:34:57 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[totp]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=58536</guid>

					<description><![CDATA[The Web Crypto API is, thankfully, nothing to do with scammy cryptocurrencies. Instead, it provides access to powerful cryptographic features which were previously only available in 3rd party tools.  So, is it possible to build a TOTP code generator without using any external JS libraries? Yes! And it is (relatively) simple.  Here&#039;s the code that I&#039;ve written. It is slightly verbose and contains…]]></description>
										<content:encoded><![CDATA[<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API">Web Crypto API</a> is, thankfully, nothing to do with scammy cryptocurrencies. Instead, it provides access to powerful cryptographic features which were previously only available in 3rd party tools.</p>

<p>So, is it possible to build a TOTP<sup id="fnref:sigh"><a href="https://shkspr.mobi/blog/2025/03/using-the-web-crypto-api-to-generate-totp-codes-in-javascript-without-3rd-party-libraries/#fn:sigh" class="footnote-ref" title="*sigh* Please don't be the boring dolt who makes a joke about Top of The Pops. Yes, I know they share the same initialism. And, yes, it's funny how nonce means something different in cryptography…" role="doc-noteref">0</a></sup> code generator without using <em>any</em> external JS libraries? Yes! And it is (relatively) simple.</p>

<p>Here's the code that I've written. It is slightly verbose and contains a lot of logging so you can see what it is doing. I've annotated it with links to the various specifications so you can see where some of the decisions come from. I've compared the output to several popular TOTP code generators and it <em>appears</em> to match. You probably shouldn't use this in production, and you should audit it thoroughly.</p>

<p>I'm sure there are bugs to be fixed and performance enhancements to be made. Feel free to leave a comment here <a href="https://codeberg.org/edent/TOTP_Test_Suite">or on the repo</a> if you spot anything.</p>

<p>I consider this code to be trivial but, if it makes you happy, you may consider it licensed under MIT.</p>

<pre><code class="language-js">async function generateTOTP( 
    base32Secret = "QWERTY", 
    interval = 30, 
    length = 6, 
    algorithm = "SHA-1" ) {

    //  Are the interval and length valid?
    if ( interval &lt;  1 ) throw new Error( "Interval is too short" );
    if ( length   &lt;  1 ) throw new Error( "Length is too low"     );
    if ( length   &gt; 10 ) throw new Error( "Length is too high"    );

    //  Is the algorithm valid?
    //  https://datatracker.ietf.org/doc/html/rfc6238#section-1.2
    algorithm = algorithm.toUpperCase();
    if ( algorithm.match( "SHA-1|SHA-256|SHA-384|SHA-512" ) == null ) throw new Error( "Algorithm not known" );

    //  Decode the secret
    //  The Base32 Alphabet is specified at https://datatracker.ietf.org/doc/html/rfc4648#section-6
    const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
    let bits = "";

    //  Some secrets are padded with the `=` character. Remove padding.
    //  https://datatracker.ietf.org/doc/html/rfc3548#section-2.2
    base32Secret = base32Secret.replace( /=+$/, "" )

    //  Loop through the trimmed secret
    for ( let char of base32Secret ) {
        //  Ensure the secret's characters are upper case
        const value = alphabet.indexOf( char.toUpperCase() );

        //  If the character doesn't appear in the alphabet.
        if (value === -1) throw new Error( "Invalid Base32 character" );

        //  Binary representation of where the character is in the alphabet
        bits += value.toString( 2 ).padStart( 5, "0" );
    }

    //  Turn the bits into bytes
    let bytes = [];
    //  Loop through the bits, eight at a time
    for ( let i = 0; i &lt; bits.length; i += 8 ) {
        if ( bits.length - i &gt;= 8 ) {
                bytes.push( parseInt( bits.substring( i, i + 8 ), 2 ) );
        }
    }

    //  Turn those bytes into an array
    const decodedSecret = new Uint8Array( bytes );
    console.log( "decodedSecret is " + decodedSecret )

    //  Number of seconds since Unix Epoch
    const timeStamp = Date.now() / 1000; 
    console.log( "timeStamp is " + timeStamp )

    //  Number of intervals since Unix Epoch
    //  https://datatracker.ietf.org/doc/html/rfc6238#section-4.2
    const timeCounter = Math.floor( timeStamp / interval );
    console.log( "timeCounter is " + timeCounter )

    //  Number of intervals in hexadecimal
    const timeHex = timeCounter.toString( 16 );
    console.log( "timeHex is " + timeHex )

    //  Left-Pad with 0
    paddedHex = timeHex.toString(2).padStart( 16, "0" );
    console.log( "paddedHex is " + paddedHex )

    //  Set up a buffer to hold the data
    const timeBuffer = new ArrayBuffer( 8 );
    const timeView   = new DataView( timeBuffer );

    //  Take the hex string, split it into 2-character chunks 
    const timeBytes = paddedHex.match( /.{1,2}/g ).map(
        //  Convert to bytes
        byte =&gt; parseInt( byte, 16 )
    );

    //  Write each byte into timeBuffer.
    for ( let i = 0; i &lt; 8; i++ ) {
         timeView.setUint8(i, timeBytes[i]);
    }
    console.log( "timeView is ",  new Uint8Array( timeView   ) );
    console.log( "timeBuffer is", new Uint8Array( timeBuffer ) );

    //  Use Web Crypto API to generate the HMAC key
    //  https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey
    const key = await crypto.subtle.importKey(
        "raw",
        decodedSecret,
        { 
            name: "HMAC", 
            hash: algorithm 
        },
        false,
        ["sign"]
    );

    //  Sign the timeBuffer with the generated HMAC key
    //  https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/sign
    const signature = await crypto.subtle.sign( "HMAC", key, timeBuffer );

    //  Get HMAC as bytes
    const hmac = new Uint8Array( signature );
    console.log( "hmac is ", hmac );

    //  https://datatracker.ietf.org/doc/html/rfc4226#section-5.4
    //  Use the last byte to generate the offset
    const offset = hmac[ hmac.length - 1 ] &amp; 0x0f;
    console.log( "offset is " + offset )

    //  Bit Twiddling operations
    const binaryCode = 
        ( ( hmac[ offset     ] &amp; 0x7f ) &lt;&lt; 24 ) |
        ( ( hmac[ offset + 1 ] &amp; 0xff ) &lt;&lt; 16 ) |
        ( ( hmac[ offset + 2 ] &amp; 0xff ) &lt;&lt;  8 ) |
        ( ( hmac[ offset + 3 ] &amp; 0xff ) );

    //  Turn the binary code into a decimal string
    stringOTP = binaryCode.toString();
    console.log( "stringOTP is " + stringOTP );

    //  Count backwards from the last character for the length of the code
    otp = stringOTP.slice( -length) 
    console.log( "otp is " + otp );
    //  Pad with 0 to full length
    otp = otp.padStart( length, "0" );
    console.log( "padded otp is " + otp );

    //  All done!
    return otp;
}


// Generate a TOTP code
( async () =&gt; {
    console.log( await generateTOTP( "4FCDTLHR446DPFCKUA46UFIAYTQIDSZ2", 30, 6, "SHA-1" ) );
} )();
</code></pre>

<p>It works with the three specified algorithms, generating between 1 and 10 digits, and works with any positive integer interval. Not all combinations are sensible; <a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/">a one digit code valid for two minutes would be silly</a>. But it is up to you to use this responsibly.</p>

<p>I hope I've shown that you don't need to rely on 3rd party libraries to do your cryptography; you can do it all in the browser instead.</p>

<p>You can <a href="https://codeberg.org/edent/TOTP_Test_Suite">grab the code from Codeberg</a>.</p>

<div id="footnotes" role="doc-endnotes">
<hr>
<ol start="0">

<li id="fn:sigh">
<p><em>*sigh*</em> Please don't be the boring dolt who makes a joke about Top of The Pops. Yes, I know they share the same initialism. And, yes, it's funny how <code>nonce</code> means something different in cryptography compared to British English.&nbsp;<a href="https://shkspr.mobi/blog/2025/03/using-the-web-crypto-api-to-generate-totp-codes-in-javascript-without-3rd-party-libraries/#fnref:sigh" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=58536&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/03/using-the-web-crypto-api-to-generate-totp-codes-in-javascript-without-3rd-party-libraries/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[The least secure TOTP code possible]]></title>
		<link>https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/</link>
					<comments>https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 24 Feb 2025 12:34:05 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[rant]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[standards]]></category>
		<category><![CDATA[totp]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=58360</guid>

					<description><![CDATA[If you use Multi-Factor Authentication, you&#039;ll be well used to scanning in QR codes which allow you to share a secret code with a website. These are known as Time-based One Time Passwords (TOTP).  As I&#039;ve moaned about before, TOTP has never been properly standardised. It&#039;s a mish-mash of half-finished proposals with no active development, no test suite, and no-one looking after it. Which is…]]></description>
										<content:encoded><![CDATA[<p>If you use Multi-Factor Authentication, you'll be well used to scanning in QR codes which allow you to share a secret code with a website. These are known as Time-based One Time Passwords (TOTP<sup id="fnref:pop"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#fn:pop" class="footnote-ref" title="Yes! Just like Top of The Pops! The famous British TV show! Wow! I bet you're the first person in history to make that joke! Have a biscuit." role="doc-noteref">0</a></sup>).</p>

<p>As I've moaned about before, <a href="https://shkspr.mobi/blog/2022/05/why-is-there-no-formal-specification-for-otpauth-urls/">TOTP has never been properly standardised</a>. It's a mish-mash of half-finished proposals with no active development, no test suite, and no-one looking after it. Which is <em>exactly</em> what you want from a security specification, right?!</p>

<p>So let's try to find some edge-cases and see where things break down.</p>

<h2 id="one-punch-man"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#one-punch-man">One Punch Man</a></h2>

<p>This is possibly the <em>least</em> secure TOTP code I could create. Scan it and see whether your app will accept it.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/02/ultra.png" alt="QR code." width="350" height="350" class="aligncenter size-full wp-image-58361">

<p>What makes it so crap?  There are three things which protect you when using TOTP.</p>

<ol>
<li>The shared secret. In this case, it is <code>abcdefghijklmno</code> - OK, that's not the easiest thing to guess, but it isn't exactly complex.</li>
<li>The amount time the code is valid for before changing. Most TOTP codes last 30 seconds, this lasts 120.</li>
<li>The length of the code. Most codes are 6 digits long. In theory, the spec allows 8 digits. This is 1. Yup. A single digit.</li>
</ol>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/02/totp-bitwarden-fs8.png" alt="BitWarden showing a single digit for 119 seconds." width="504" height="378" class="aligncenter size-full wp-image-58380">

<p>If you were thick enough to use this<sup id="fnref:noooooo"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#fn:noooooo" class="footnote-ref" title="Please don't!" role="doc-noteref">1</a></sup>, an attacker would have a 1/10 chance of simply <em>guessing</em> your MFA code. If they saw you type it in, they'd have a couple of minutes in which to reuse it.</p>

<p>Can modern TOTP apps add this code? I <a href="https://mastodon.social/@Edent/114032994415288253">crowdsourced the answers</a>.</p>

<p>Surprisingly, a few apps accept it! Aegis, 1password, and BitWarden will happily store it and show you a 1 digit code for 120 seconds.</p>

<p>A few reject it. Authy, Google Authenticator, and OpenOTP claim the code is broken and won't add it.</p>

<p>But, weirdly, a few <em>interpret it incorrectly!</em> The native iOS app, Microsoft Authenticator, and KeepassXC store the code, but treat it as a 6 digit, 30 second code.</p>

<h2 id="do-the-right-thing"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#do-the-right-thing">Do The Right Thing</a></h2>

<p>What is the right thing to do in this case? The code is outside the (very loosely defined) specification. <a href="https://lawsofux.com/postels-law/">Postel's Law</a> tells us that we should try our best to interpret malformed data - which is what Aegis and BitWarden do.</p>

<p>But, in a security context, that could be dangerous. Perhaps rejecting a dodgy code makes more sense?</p>

<p>What is absolutely daft<sup id="fnref:stronger"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#fn:stronger" class="footnote-ref" title="I wanted to use the words &quot;utterly fucking stupid&quot; but I felt it was unprofessional." role="doc-noteref">2</a></sup> is ignoring the bits of the code you don't like and substituting your own data! Luckily, in a normal TOTP enrolment, the user has to enter a code to prove they've saved it correctly. Entering in a 6 digit code where only 1 is expected is likely to fail.</p>

<h2 id="were-only-human"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#were-only-human">We're Only Human</a></h2>

<p>A one-digit code is ridiculous. But what about the other extreme? Would a 128-digit code be acceptable? For a human, no; it would be impossible to type in correctly. For a machine with a shared secret, it possibly makes sense.</p>

<p>On a high-latency connection or with users who may have mobility difficulties, a multi-minute timeframe could be sensible. For something of extremely high security, sub-30 seconds may be necessary.</p>

<p>But, again, the specification hasn't evolved to meet user needs. It is stagnant and decaying.</p>

<h2 id="whats-next"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#whats-next">What's Next?</a></h2>

<p>There's an <a href="https://www.ietf.org/archive/id/draft-linuxgemini-otpauth-uri-00.html">draft proposal to tighten up to TOTP spec</a> which has expired.</p>

<p>It would be nice if the major security players came together to work out a <em>formal</em> and <em>complete</em> specification for this vital piece of security architecture. But I bet it won't ever happen.</p>

<ul>
<li><a href="https://github.com/google/google-authenticator/wiki/Key-Uri-Format">Google have archived their work on authentication standards</a>.</li>
<li><a href="https://developer.apple.com/documentation/authenticationservices/securing-logins-with-icloud-keychain-verification-codes#3795996">Apple points to Google's outdated spec</a>.</li>
<li><a href="https://docs.yubico.com/yesdk/users-manual/application-oath/uri-string-format.html">YubiCo's incompatible spec hasn't been updated in 4 years</a>.</li>
<li>The <a href="https://www.iana.org/assignments/uri-schemes/prov/otpauth">otpauth registration</a> lists a consortium called <a href="https://openauthentication.org">https://openauthentication.org</a> who don't appear to published anything in a decade.</li>
<li>Microsoft don't have any documentation whatsoever.</li>
</ul>

<p>So there you have it. We're told to rely on TOTP for our MFA - yet the major apps all disagree on how the standard should be implemented. This is a recipe for an eventual security disaster.</p>

<p>How do we fix it?</p>

<div id="footnotes" role="doc-endnotes">
<hr>
<ol start="0">

<li id="fn:pop">
<p>Yes! Just like Top of The Pops! The famous British TV show! Wow! I bet you're the first person in history to make that joke! Have a biscuit.&nbsp;<a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#fnref:pop" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:noooooo">
<p>Please don't!&nbsp;<a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#fnref:noooooo" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:stronger">
<p>I wanted to use the words "utterly fucking stupid" but I felt it was unprofessional.&nbsp;<a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#fnref:stronger" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=58360&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Should you enable TOTP *only* authentication?]]></title>
		<link>https://shkspr.mobi/blog/2024/10/should-you-enable-totp-only-authentication/</link>
					<comments>https://shkspr.mobi/blog/2024/10/should-you-enable-totp-only-authentication/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 04 Oct 2024 11:34:24 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[2fa]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[MFA]]></category>
		<category><![CDATA[totp]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=53016</guid>

					<description><![CDATA[Here&#039;s a &#34;fun&#34; thought experiment. Imagine a website which let you sign in using only your username and TOTP code.  No passwords. No magic links emailed to you. No FIDO tokens. No codes via SMS. Just a TOTP generated and displayed on your device.  Is that useful? Sensible? Practical?  It&#039;s certainly technically possible. Store the username, store the TOTP seed, done. Your users can now log in. …]]></description>
										<content:encoded><![CDATA[<p>Here's a "fun" thought experiment. Imagine a website which let you sign in using only your username and TOTP code.</p>

<p>No passwords. No magic links emailed to you. No FIDO tokens. No codes via SMS. <em>Just</em> a TOTP generated and displayed on your device.</p>

<p>Is that useful? Sensible? Practical?</p>

<p>It's certainly technically <em>possible</em>. Store the username, store the TOTP seed, done. Your users can now log in.</p>

<p>Is it <em>useful</em>? Well, it would force users to not reuse passwords they've used elsewhere. That prevents one class of security issue. If another service gets hacked, attackers can't use those credentials with your service. If you get hacked, there are no passwords stored.</p>

<p>As for <em>practical</em>? I already have 60 TOTP codes! (That's up from <a href="https://shkspr.mobi/blog/2020/08/i-have-4-2fa-coverage/">30 a few years ago</a>). Scrolling through those codes is no harder than scrolling through my password manager.</p>

<p>So, <em>sensible</em>? This all depends on your risk tolerance.</p>

<ul>
<li>A 6 digit TOTP code has a million combinations. If your service has no rate limiting, that's trivial for an attacker to brute-force.</li>
<li>An attacker <em>might</em> get lucky and score a literal one-in-a-million hit.</li>
<li>Shoulder surfing attacks are easier if the password is only 6 digits (although harder with a short time-window).</li>
</ul>

<p>Should you build an authentication mechanism like this?</p>

<p>Ehhhh… I'm going to go with "mostly no, except in limited circumstances".  It might make life slightly easier for some users.  But I feel inherently <em>icky</em> about having such a short password, even if it does regularly rotate.  If this is a low-value service without sensitive information, it <em>might</em> be useful. But for everything else, I think it is a silly ideas.</p>

<p><a href="https://mastodon.social/@Edent/113106727728186564">Further discussion on Mastodon</a>.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=53016&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/10/should-you-enable-totp-only-authentication/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Is it OK to share 2FA secrets?]]></title>
		<link>https://shkspr.mobi/blog/2024/08/is-it-ok-to-share-2fa-secrets/</link>
					<comments>https://shkspr.mobi/blog/2024/08/is-it-ok-to-share-2fa-secrets/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 11 Aug 2024 11:34:01 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[2fa]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[MFA]]></category>
		<category><![CDATA[totp]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=51197</guid>

					<description><![CDATA[Yeah. Yeah, I reckon so. Under the right circumstances.  Multi-Factor Authentication (MFA, 2FA, TOTP, whatever you want to call it) is pretty nifty. You scan a QR code and your phone will continually generate a set of one-time passwords which are synchronised with a remote server.  There&#039;s nothing stopping multiple people from scanning that QR code! They will each have the same password displayed …]]></description>
										<content:encoded><![CDATA[<p>Yeah. Yeah, I reckon so. Under the right circumstances.</p>

<p>Multi-Factor Authentication (MFA, 2FA, TOTP, whatever you want to call it) is pretty nifty. You scan a QR code and your phone will continually generate a set of one-time passwords which are synchronised with a remote server.</p>

<p>There's nothing stopping <em>multiple</em> people from scanning that QR code! They will each have the same password displayed at the same time.</p>

<p>I've found this to be useful in a few situations.</p>

<p>If my wife and I have access to the same account, and it doesn't allow individual sign-ins, then we share a username, password, and MFA code. That only works in a high trust environment. If your marriage is not high trust, you may need a different solution.</p>

<p>For a Big Work Project™ we had several people on-call. In case of emergency, someone would ring a "group hunt" number. Any one of the team could pick up. In order to authenticate both the caller and the answerer, all participants had the same TOTP code stored on their phones. That was more sensible than having a dozen different passwords.</p>

<p>There are risks, of course.</p>

<ul>
<li>Giving multiple people access to a system increases the risk one of them will be hacked, phished, or attacked.</li>
<li>Having a secret on multiple devices means multiple chances for it to be leaked.</li>
<li>Revoking and reissuing keys is more difficult with multiple people.</li>
<li>It <em>feels</em> icky.</li>
</ul>

<p>There's nothing which technically stops you backing up or sharing your MFA secrets. But you need to be really sure you understand the risks.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=51197&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/08/is-it-ok-to-share-2fa-secrets/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Can time-travellers use TOTP codes?]]></title>
		<link>https://shkspr.mobi/blog/2024/07/can-time-travellers-use-totp-codes/</link>
					<comments>https://shkspr.mobi/blog/2024/07/can-time-travellers-use-totp-codes/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 04 Jul 2024 11:34:57 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[Computer Science]]></category>
		<category><![CDATA[standards]]></category>
		<category><![CDATA[totp]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=51034</guid>

					<description><![CDATA[Imagine, just for a moment, you and your friends decide to travel in time. In order to make sure you can authenticate your communications with each other, you set up a shared Time-based One Time Password (TOTP).  The TOTP algorithm uses a Hash-based Message Authentication Code (HMAC). The hash is calculated from a shared key and a time-based component.  The key is a short string of characters.…]]></description>
										<content:encoded><![CDATA[<p>Imagine, just for a moment, you and your friends decide to travel in time. In order to make sure you can authenticate your communications with each other, you set up a shared Time-based One Time Password (<a href="https://en.wikipedia.org/wiki/Time-based_one-time_password">TOTP</a>).</p>

<p>The TOTP algorithm uses a Hash-based Message Authentication Code (<a href="https://en.wikipedia.org/wiki/HMAC">HMAC</a>). The hash is calculated from a shared key <em>and</em> a time-based component.</p>

<p>The key is a short string of characters. The time-based component is calculated as the number of seconds between now and the Unix Epoch. When is the Unix Epoch? 00:00:00 UTC on Thursday, 1 January 1970<sup id="fnref:uk"><a href="https://shkspr.mobi/blog/2024/07/can-time-travellers-use-totp-codes/#fn:uk" class="footnote-ref" title="As an aside, in 1970, the UK was on BST - British Standard Time rather than GMT / UTC." role="doc-noteref">0</a></sup>. It has been roughly 1.7 billion seconds since then. 64 bit computer systems can count up for another 290 billion <em>years</em><sup id="fnref:2038"><a href="https://shkspr.mobi/blog/2024/07/can-time-travellers-use-totp-codes/#fn:2038" class="footnote-ref" title="Or, if you're stuck using 32 bit time, until the year 2038." role="doc-noteref">1</a></sup>. So chrononauts journeying to the future should be fine.</p>

<p>But what about people travelling <em>backwards</em>?  You and your friends want to go and see The Beatles perform in 1966.  That's before 1970.  So the time-based component will be a <strong>negative</strong> number.</p>

<p>I've tried a bunch of different TOTP generators and fed them a variety of negative numbers. They all crashed.</p>

<p>So, no. TOTP doesn't work for anyone travelling backwards in the 4th dimension. Pity.</p>

<p>Is there a serious point to this?  Well, sort of.</p>

<p>Negative time is an unexpected input and leads to unusual behaviours.  Could a crash in HMAC generation lead to an exploit?</p>

<p>Standards get used in all sorts of places - including retrospectively. Should standards writers specifically account for inputs which occur in the past?</p>

<p>How should computers deal with <a href="https://mastodon.social/@tubetime/110811949233318077">"preposterous" times</a>?</p>

<p>What other common security tools fail if they're subjected to time-travel?</p>

<p>Which Beatles concert would you go to in 1966?</p>

<div id="footnotes" role="doc-endnotes">
<hr>
<ol start="0">

<li id="fn:uk">
<p>As an aside, in 1970, the UK was on BST - <a href="https://en.wikipedia.org/wiki/British_Summer_Time#Periods_of_deviation">British <em>Standard</em> Time</a> rather than GMT / UTC.&nbsp;<a href="https://shkspr.mobi/blog/2024/07/can-time-travellers-use-totp-codes/#fnref:uk" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:2038">
<p>Or, if you're stuck using 32 bit time, <a href="https://en.wikipedia.org/wiki/Year_2038_problem">until the year 2038</a>.&nbsp;<a href="https://shkspr.mobi/blog/2024/07/can-time-travellers-use-totp-codes/#fnref:2038" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=51034&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/07/can-time-travellers-use-totp-codes/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[How random are TOTP codes?]]></title>
		<link>https://shkspr.mobi/blog/2024/07/how-random-are-totp-codes/</link>
					<comments>https://shkspr.mobi/blog/2024/07/how-random-are-totp-codes/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 02 Jul 2024 11:34:22 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[algorithms]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[totp]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=51026</guid>

					<description><![CDATA[I&#039;m pretty sure that the 2FA codes generated by my bank&#039;s TOTP app have a bias towards the number 8 - because eight is an auspicious number.  But is that just my stupid meaty brain noticing patterns where none exist?  The TOTP algorithm uses HMAC, which in turn uses SHA-1. My aforementioned brain is not clever enough to understand how that works. Although bigger, meatier brains have assured me it …]]></description>
										<content:encoded><![CDATA[<p>I'm pretty sure that the 2FA codes generated by my bank's TOTP app have a bias towards the number 8 - because <a href="https://en.wikipedia.org/wiki/Chinese_numerology#Eight">eight is an auspicious number</a>.</p>

<p>But is that just my stupid meaty brain noticing patterns where none exist?</p>

<p>The TOTP algorithm uses <a href="https://en.wikipedia.org/wiki/HMAC-based_one-time_password#Definition">HMAC, which in turn uses SHA-1</a>. My aforementioned brain is not clever enough to understand how that works. Although bigger, meatier brains have assured me it is fine.</p>

<p>What happens if I sample, say, the next 10 TOTP codes and plot how often digits appear?</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/07/Figure_1-fs8.png" alt="Histogram of distributions. 8 is clearly higher than the rest." width="640" height="443" class="aligncenter size-full wp-image-51027">

<p>HOLY SHIT! CONSPIRACY CONFIRMED! WTAF?!?!?</p>

<p>Don't believe me? Here's the code:</p>

<pre><code class="language-python">import pyotp
import numpy as np
import matplotlib.pyplot as plt

totp = pyotp.TOTP('')

#   How many times to run this?
runs = 10

#   Initialize a NumPy array with shape to store the digits
digits_array = np.zeros( ( runs, 6), dtype=int)

#   Generate the digits
for i in range( runs ):
    number_str = totp.at( i * 30 )
    for j in range(6):
        digits_array[i, j] = int(number_str[j])

#   Analyse the entire array
all_digits = digits_array.flatten()

# Plot histogram for all digits
plt.figure()
plt.hist(all_digits, bins=np.arange(11) - 0.5, edgecolor='black', density=True)
plt.xticks(range(10))
plt.title('Digit Distribution for All Digits (' + f"{runs:,}" + ' runs)')
plt.xlabel('Digit')
plt.ylabel('Frequency')
plt.show()
</code></pre>

<p>OK, so maybe that's a coincidence. What happens if we check 100 generations?</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/07/Figure_100-fs8.png" alt="Histogram of 100 runs. Still significant variation, although flattened." width="640" height="443" class="aligncenter size-full wp-image-51028">

<p>Quite a lot of noise in there - but lucky number 8 isn't quite as prominent.</p>

<p>After 10,000 generations, the variation is all but gone.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/07/Figure_10000-fs8.png" alt="Histogram showing almost no variation." width="640" height="443" class="aligncenter size-full wp-image-51029">

<p>There are about 30 million seconds in a year. TOTP codes change every 30 seconds. Which means, in a year, you'll see about a million of them:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/07/Figure_1000000-fs8.png" alt="Histogram which is basically flat." width="640" height="443" class="aligncenter size-full wp-image-51030">

<p>Is it possible that a TOTP code could be formed which shows a clear bias to a specific number? Probably not.</p>

<p>I love being able to check the source code - but sometimes it's just as reassuring to measure the output.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=51026&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/07/how-random-are-totp-codes/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Password Resets in an Age of MFA]]></title>
		<link>https://shkspr.mobi/blog/2024/07/password-resets-in-an-age-of-mfa/</link>
					<comments>https://shkspr.mobi/blog/2024/07/password-resets-in-an-age-of-mfa/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 01 Jul 2024 11:34:05 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[2fa]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[MFA]]></category>
		<category><![CDATA[passwords]]></category>
		<category><![CDATA[totp]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=51014</guid>

					<description><![CDATA[Recently, WordPress got in contact with me to say they suspect that my password was exposed in some sort of data breach. Well, it&#039;s a day ending with a &#34;y&#34; - so of course some scumbag has pilfered my digital identity.  WordPress mandated that I change my password. But was that really necessary?  Firstly, the password was uniquely generated by my password manager. It isn&#039;t re-used anywhere else.…]]></description>
										<content:encoded><![CDATA[<p>Recently, WordPress got in contact with me to say they suspect that my password was exposed in some sort of data breach. Well, it's a day ending with a "y" - so of course some scumbag has pilfered my digital identity.</p>

<p>WordPress mandated that I change my password. But was that really necessary?</p>

<p>Firstly, the password was uniquely generated by my password manager<sup id="fnref:password"><a href="https://shkspr.mobi/blog/2024/07/password-resets-in-an-age-of-mfa/#fn:password" class="footnote-ref" title="It was w@&amp;7%GUznK#9^}<S5 if you must know." role="doc-noteref">0</a></sup>. It isn't re-used anywhere else. So there is no chance of hackers breaking in to my email, bank, or OnlyFans account<sup id="fnref:OF"><a href="https://shkspr.mobi/blog/2024/07/password-resets-in-an-age-of-mfa/#fn:OF" class="footnote-ref" title="Lots of weirdos want to buy videos of me recompiling Linux while in my pants. Who am I to judge?" role="doc-noteref">1</a></sup>.</p>

<p>Secondly, and more importantly, I have 2FA app which provides me with a TOTP code every time I want to log in. Even if the evil ne'erdowells have my username <em>and</em> password, they can't get in without the MFA code<sup id="fnref:2FA"><a href="https://shkspr.mobi/blog/2024/07/password-resets-in-an-age-of-mfa/#fn:2FA" class="footnote-ref" title="It is currently 194 685." role="doc-noteref">2</a></sup>.</p>

<p>So, should I change my password?</p>

<p>To understand this, it's worth considering the risks - both of action and inaction.</p>

<p>Changing a password isn't without risk.</p>

<ul>
<li>Perhaps some long-forgotten app or service relies on that password. If I change it, what will break?</li>
<li>Do I trust my password manager to give me a strong password?</li>
<li>What if the original email is a phishing attempt and I end up giving the baddies my credentials?</li>
<li>Can I be bothered spending the time maintaining this old account?</li>
</ul>

<p>As for the risk of inaction.</p>

<ul>
<li>Using my details, a miscreant <em>might</em> convince WordPress to disable MFA on my account.</li>
<li>If there was a breach, my MFA seed secret might also have been stolen.</li>
</ul>

<p>On balance… yeah, obviously I should change my password. It is a 30 second job with a decent password manager. But, I might argue, there isn't much <em>urgency</em> in doing so.</p>

<ul>
<li>A strong and unique password means there is no risk of collateral damage to other accounts.</li>
<li>The use of MFA adds an extra layer of protection which buys you time.</li>
</ul>

<p>Thankfully, we've moved on from the outdated advice to <a href="https://www.ncsc.gov.uk/collection/passwords/updating-your-approach#PasswordGuidance:UpdatingYourApproach-Don'tenforceregularpasswordexpiry">regularly change your password</a>.  Now we only have to change them when there's been a breach. Which, coincidentally, is every 30 days…</p>

<p>The future ain't what it used to be!</p>

<div id="footnotes" role="doc-endnotes">
<hr>
<ol start="0">

<li id="fn:password">
<p>It was <code>w@&amp;7%GUznK#9^}&lt;S5</code> if you must know.&nbsp;<a href="https://shkspr.mobi/blog/2024/07/password-resets-in-an-age-of-mfa/#fnref:password" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:OF">
<p>Lots of weirdos want to buy videos of me recompiling Linux while in my pants. Who am I to judge?&nbsp;<a href="https://shkspr.mobi/blog/2024/07/password-resets-in-an-age-of-mfa/#fnref:OF" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:2FA">
<p>It is currently <code>194 685</code>.&nbsp;<a href="https://shkspr.mobi/blog/2024/07/password-resets-in-an-age-of-mfa/#fnref:2FA" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=51014&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/07/password-resets-in-an-age-of-mfa/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[How to generate a Base32 TOTP secret string on a Mac]]></title>
		<link>https://shkspr.mobi/blog/2023/03/how-to-generate-a-base32-totp-secret-string-on-a-mac/</link>
					<comments>https://shkspr.mobi/blog/2023/03/how-to-generate-a-base32-totp-secret-string-on-a-mac/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 13 Mar 2023 12:34:20 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[qr]]></category>
		<category><![CDATA[totp]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=45094</guid>

					<description><![CDATA[I needed a way to generate a TOTP secret using a fairly locked-down Mac. No Brew. No NPM. No Python. No Prolog, COBOL, or FORTRAN. No Internet connection. Just whatever software is native to MacOS.  As I&#039;ve mentioned before, the TOTP specification is a stagnant wasteland. But it does have this to say about the secret:  The secret parameter is an arbitrary key value encoded in Base32 according to…]]></description>
										<content:encoded><![CDATA[<p>I needed a way to generate a TOTP secret using a fairly locked-down Mac. No Brew. No NPM. No Python. No Prolog, COBOL, or FORTRAN. No Internet connection. Just whatever software is native to MacOS.</p>

<p>As I've mentioned before, <a href="https://shkspr.mobi/blog/2022/05/why-is-there-no-formal-specification-for-otpauth-urls/">the TOTP specification is a stagnant wasteland</a>. But it does have this to say about the secret:</p>

<blockquote><p>The <code>secret</code> parameter is an arbitrary key value encoded in Base32 according to <a href="https://www.rfc-editor.org/rfc/rfc3548">RFC 3548</a>.</p></blockquote>

<p>The <a href="https://www.rfc-editor.org/rfc/rfc3548#section-5">Base32 alphabet</a> is pretty simple. The upper-case letters A - Z, and the numbers 3 - 7.</p>

<p>But that's only half the problem.</p>

<h2 id="how-long-should-the-secret-be"><a href="https://shkspr.mobi/blog/2023/03/how-to-generate-a-base32-totp-secret-string-on-a-mac/#how-long-should-the-secret-be">How long should the secret be?</a></h2>

<p>This is an area where <a href="https://www.rfc-editor.org/rfc/rfc6238">the official spec is silent</a>.  Around the web, you'll find people saying things like:</p>

<blockquote><p>The length in bytes of generated shared secrets. The minimum is 20 (or 160 bits), and the default is 32 (or 256 bits). In most use cases 32 is sufficient. Though some authenticators may have issues with more than the minimum. Our minimum is the recommended value in <a href="https://www.rfc-editor.org/rfc/rfc4226.html">RFC4226</a>, though technically according to the specification 16 bytes (or 128 bits) is the minimum.
<a href="https://www.authelia.com/configuration/second-factor/time-based-one-time-password/#secret_size">Authelia</a></p></blockquote>

<p>Although that's for the <strong>H</strong>OTP algorithm, rather than <strong>T</strong>OTP.</p>

<p>Looking through my 49(!) different TOTP codes, the shared secret ranges from 16 characters to 52 characters. No consistency whatsoever.</p>

<h2 id="the-code"><a href="https://shkspr.mobi/blog/2023/03/how-to-generate-a-base32-totp-secret-string-on-a-mac/#the-code">The Code</a></h2>

<p>Run this on your command line.</p>

<p><code>cat /dev/urandom | LC_ALL=C tr -dc 'A-Z3-7' | fold -w 24 | head -n 1</code></p>

<p>That generates a random-enough<sup id="fnref:random"><a href="https://shkspr.mobi/blog/2023/03/how-to-generate-a-base32-totp-secret-string-on-a-mac/#fn:random" class="footnote-ref" title="This is a shared secret. It doesn't need an infinite supply of high-quality entropy. Any decent login protection service should throttle incorrect attempts long before someone can brute-force it.…" role="doc-noteref">0</a></sup> string using the correct alphabet and truncated to a sensible length.</p>

<p>Let's go through each part of it.</p>

<p><code>cat /dev/urandom</code> spits out random data from your machine. You may wish to use <a href="https://linuxhint.com/dev_random_vs_dev_urandom/"><code>/dev/random</code></a> if you feel like it.</p>

<p><code>LC_ALL=C</code> sets the <a href="https://www.ibm.com/support/pages/what-lcall-variable">Locale to by ANSI bytes</a>.</p>

<p><code>tr</code> is "<a href="https://ss64.com/osx/tr.html">Translate, squeeze, and/or delete characters.</a>".</p>

<p><code>-dc 'A-Z3-7'</code> is <strong>d</strong>elete the <strong>c</strong>omplement (i.e. delete anything which <em>isn't</em> in the following string).</p>

<p><code>fold -w 24</code> is <a href="https://www.unix.com/man-page/osx/1/fold/">wrap the input text</a> with a line length of 24.</p>

<p><code>head -n 1</code> displays <a href="https://www.unix.com/man-page/osx/1/head/">the first line of the file</a>.</p>

<h2 id="next-steps"><a href="https://shkspr.mobi/blog/2023/03/how-to-generate-a-base32-totp-secret-string-on-a-mac/#next-steps">Next Steps</a></h2>

<p>Now... How do I locally generate a QR code without anything else being installed...?</p>

<div id="footnotes" role="doc-endnotes">
<hr>
<ol start="0">

<li id="fn:random">
<p>This is a shared secret. It doesn't <em>need</em> an infinite supply of high-quality entropy. Any decent login protection service should throttle incorrect attempts long before someone can brute-force it. Remember, TOTP codes are <em>not</em> random - they're deterministic product of the secret. Your adversary isn't someone who can crack your secure vault - it's someone who already has your username as password, but doesn't have your token generating device.&nbsp;<a href="https://shkspr.mobi/blog/2023/03/how-to-generate-a-base32-totp-secret-string-on-a-mac/#fnref:random" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=45094&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/03/how-to-generate-a-base32-totp-secret-string-on-a-mac/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Why is there no formal specification for otpauth URls?]]></title>
		<link>https://shkspr.mobi/blog/2022/05/why-is-there-no-formal-specification-for-otpauth-urls/</link>
					<comments>https://shkspr.mobi/blog/2022/05/why-is-there-no-formal-specification-for-otpauth-urls/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 08 May 2022 11:34:35 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[2fa]]></category>
		<category><![CDATA[qr]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[totp]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=42594</guid>

					<description><![CDATA[Yes yes, Cunningham&#039;s law etc etc!  I want to play around with 2FA codes. So, I started looking for the specification. Turns out, there isn&#039;t one. Not really.  IANA has a provisional registration - but no spec.  It links to an archived Google Wiki which, as we&#039;ll come on to, isn&#039;t sufficient.  There&#039;s some documentation from Yubico which is mostly a copy of the Google wiki with some incompatible…]]></description>
										<content:encoded><![CDATA[<p>Yes yes, Cunningham's law etc etc!</p>

<p>I want to play around with 2FA codes. So, I started looking for the specification. Turns out, there isn't one. Not really.</p>

<p><a href="https://www.iana.org/assignments/uri-schemes/prov/otpauth">IANA has a provisional <em>registration</em></a> - but no spec.</p>

<p>It links to an <a href="https://github.com/google/google-authenticator/wiki/Key-Uri-Format">archived Google Wiki</a> which, as we'll come on to, isn't sufficient.</p>

<p>There's some <a href="https://docs.yubico.com/yesdk/users-manual/application-oath/uri-string-format.html">documentation from Yubico</a> which is mostly a copy of the Google wiki with some incompatible tweaks.</p>

<p>The <a href="https://www1.auth.iij.jp/smartkey/en/uri_v1.html">Internet Initiative Japan has a <em>subtly</em> different spec</a> which includes an <code>icon</code> parameter not seen in any other.</p>

<p>Hidden halfway down the IETF tracker for <a href="https://datatracker.ietf.org/doc/draft-ietf-kitten-scram-2fa/">Extensions to Salted Challenge Response (SCRAM) for 2 factor authentication</a> is <em>yet another</em> specification which is incompatible with the others!</p>

<p>Oh, and <a href="https://web.archive.org/web/20230122203833/https://www.ibm.com/docs/en/sva/9.0.6?topic=authentication-configuring-totp-one-time-password-mechanism">IBM has yet another one</a>!</p>

<p>Of course, Apple have their own <a href="https://developer.apple.com/documentation/authenticationservices/securing_logins_with_icloud_keychain_verification_codes#3795996">special Apple-specific version</a>. It is identical to Google's, but uses the <code>apple-otpauth:</code> scheme. Because.</p>

<p>Here's a quick table comparing them:
<!--
|                   |            Google           |          Yubico         |              IIJ             | SCRAM        | IBM |
|------------------:|:---------------------------:|:-----------------------:|:----------------------------:|:------------:|:------------:|
|  `label` `issuer` | No encoding specified       | No encoding specified   | URI Encoding                 | N/A          | No encoding specified |
| `label` `account` | URI encoding                | URI encoding            | URI encoding                 | N/A          | No encoding specified |
|          `secret` | Arbitrary length Base32     | Arbitrary length Base32 | Arbitrary length Base32      | 40 * HEXCHAR | Not specified |
|       `algorithm` | SHA1 / SHA256 / SHA512      | SHA1 / SHA256 / SHA512  | SHA1 / SHA256 / SHA512 / **MD5** | -        | HmacSHA1, HmacSHA256, HmacSHA512 |
|          `digits` | 6 / 8                       | 6 / **7** / 8               | 6 / 8                        | -        | 6 - 9 characters or numbers |
|          `period` | Arbitrary number of seconds | 15 / 30 / 60            | Arbitrary number of seconds  | -            | Arbitrary number of seconds |
|          `issuer` | URL encoding                | URL encoding            | No encoding specified        | 1*CHAR       | No encoding specified |
|            `icon` | -                           | -                       | String                       | -            | - |
--></p>

<table>

<thead>
<tr>
<th align="right">Variable</th>
<th align="center">Google</th>
<th align="center">Yubico</th>
<th align="center">IIJ</th>
<th align="center">SCRAM</th>
<th align="center">IBM</th>
</tr>
</thead>
<tbody>
<tr>
<td align="right">label issuer</td>
<td align="center">No encoding specified</td>
<td align="center">No encoding specified</td>
<td align="center">URI Encoding</td>
<td align="center">N/A</td>
<td align="center">No encoding specified</td>
</tr>
<tr>
<td align="right">label account</td>
<td align="center">URI encoding</td>
<td align="center">URI encoding</td>
<td align="center">URI encoding</td>
<td align="center">N/A</td>
<td align="center">No encoding specified</td>
</tr>
<tr>
<td align="right">secret</td>
<td align="center">Arbitrary length Base32</td>
<td align="center">Arbitrary length Base32</td>
<td align="center">Arbitrary length Base32</td>
<td align="center">40 * HEXCHAR</td>
<td align="center">Not specified</td>
</tr>
<tr>
<td align="right">algorithm</td>
<td align="center">SHA1 / SHA256 / SHA512</td>
<td align="center">SHA1 / SHA256 / SHA512</td>
<td align="center">SHA1 / SHA256 / SHA512 / <strong>MD5</strong></td>
<td align="center">-</td>
<td align="center">HmacSHA1 / HmacSHA256 / HmacSHA512</td>
</tr>
<tr>
<td align="right">digits</td>
<td align="center">6 / 8</td>
<td align="center">6 / <strong>7</strong> / 8</td>
<td align="center">6 / 8</td>
<td align="center">-</td>
<td align="center">6 - 9 <strong>characters</strong> or numbers</td>
</tr>
<tr>
<td align="right">period</td>
<td align="center">Arbitrary number of seconds</td>
<td align="center">15 / 30 / 60</td>
<td align="center">Arbitrary number of seconds</td>
<td align="center">-</td>
<td align="center">Arbitrary number of seconds</td>
</tr>
<tr>
<td align="right">issuer</td>
<td align="center">URL encoding</td>
<td align="center">URL encoding</td>
<td align="center">No encoding specified</td>
<td align="center">1*CHAR</td>
<td align="center">No encoding specified</td>
</tr>
<tr>
<td align="right">icon</td>
<td align="center">-</td>
<td align="center">-</td>
<td align="center">String</td>
<td align="center">-</td>
<td align="center">-</td>
</tr>
</tbody>
</table>

<p>I tried creating a variety of codes on the fringes of the specification - more than 8 digits, lower than 15 second periods, weird issuers - to see what would happen if my trusting friends scanned them with their TOTP apps.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/05/WTAF.png" alt="A QR code." width="350" height="350" class="aligncenter size-full wp-image-42599">

<style>.social-embed{all:unset;display:block;}.social-embed * {all:unset;display:revert;}.social-embed::after{all:unset;}.social-embed::before{all:unset;}blockquote:not(*){all:unset;}.social-embed a{cursor:pointer;}blockquote.social-embed{box-sizing:border-box;border:.5px solid;width:550px;max-width:100%;font-family:sans-serif;margin:auto;margin-bottom:.5em;padding:1em;border-radius:1em;background-color:#FFF;color:#000;display:block;}.social-embed-header{display:flex;justify-content:space-between;}.social-embed-user{display:flex;position:relative;align-items:center;text-decoration:none;color:inherit;}.social-embed-avatar{width:3em;height:3em;margin-right:.5em;}.social-embed-avatar-circle{border-radius:50%;}.social-embed-avatar-square{border-radius:5%;}.social-embed-user-names-name{display:flex;align-items:center;font-weight:bold;margin:0;}.social-embed-text{margin-top:.5em;}.social-embed-footer{display:flex;align-items:center;justify-content:space-between;}.social-embed-logo{width:3em;}.social-embed-hr{border:.1px solid;margin:.5em 0 .5em 0;}.social-embed-meta{text-decoration:none !important;color:unset !important;}.social-embed-reply{display:block;}.social-embed-text a, .social-embed-footer time{color:blue;text-decoration:underline;}.social-embed-media, .social-embed-video{border-radius:1em;max-width:100%;}.social-embed-reply{font-size:.75em;display:block;}.social-embed-meter{width:100%;background:#0005;}</style>

<blockquote class="social-embed" id="social-embed-1522597758749880323" lang="en"><blockquote class="social-embed" id="social-embed-1522597542348926978" lang="en"><header class="social-embed-header"><a href="https://twitter.com/samnalty" class="social-embed-user"><img class="social-embed-avatar social-embed-avatar-circle" src="data:image/webp;base64,UklGRt4BAABXRUJQVlA4INIBAAAwCgCdASowADAAPrVIokqnJCMhrjv5mOAWiUAXZoHYyqQ0eAuLFQPefp2t0tF+xe/kNlepsH6/8TzPwSqnv3elmK12fKIlHfzuAq/Afb8g2Oq2z/VWt3N6yAAA/vNo84oq9c7uaDif8Ttje97m+ToovqRbZK4Ji8uX4H7sdFlcDpPn7sA0gmE/zTsAfycjXDV2uYyIIfbK1lNrEO+NTaFfxxHfX942TEZ/CFSGOB1D00iDvSiS8CF4B3v/d2/y23jeIHY6AAuWf9IxrmTLiNNHeQGcqdBHJyii0hajpueZks+6/Ixjb5v3i5SZy77+ul//Kp7VOFPZc7BBrsUgJjBZKPJCYyTLBgcfIt/+IrT2px6eU060ADdXeLL/5MilC4yJ84XaPSp3vzxlp/HskXunfb5XQ4dWxxSg7T3L4K40WyuE0idrfdk/GeoUnT2ox9cavFP0+4jfSHsDlRzqb1Ncaf4XQfryl8/fwQZ2tdROfnFCFQOUAqFkV25q3d6TDZ6TqCKNkpz/INc1FQv4pOxUQZ9TXhNFKxHQBHkrV5AAXRuf6mK1j+fDuDuhjHOeX+NI186Uzmfiwtwxe4beufuzjBgMoRuv9nVHYtcVbiw+AgAA" alt=""><div class="social-embed-user-names"><p class="social-embed-user-names-name">Sam Nalty</p>@samnalty</div></a><img class="social-embed-logo" alt="" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCmFyaWEtbGFiZWw9IlR3aXR0ZXIiIHJvbGU9ImltZyIKdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoCmQ9Im0wIDBINTEyVjUxMkgwIgpmaWxsPSIjZmZmIi8+PHBhdGggZmlsbD0iIzFkOWJmMCIgZD0ibTQ1OCAxNDBxLTIzIDEwLTQ1IDEyIDI1LTE1IDM0LTQzLTI0IDE0LTUwIDE5YTc5IDc5IDAgMDAtMTM1IDcycS0xMDEtNy0xNjMtODNhODAgODAgMCAwMDI0IDEwNnEtMTcgMC0zNi0xMHMtMyA2MiA2NCA3OXEtMTkgNS0zNiAxczE1IDUzIDc0IDU1cS01MCA0MC0xMTcgMzNhMjI0IDIyNCAwIDAwMzQ2LTIwMHEyMy0xNiA0MC00MSIvPjwvc3ZnPg=="></header><section class="social-embed-text"><small class="social-embed-reply"><a href="https://twitter.com/kerguio/status/1522592200764317697">Replying to @kerguio</a></small><a href="https://twitter.com/kerguio">@kerguio</a> <a href="https://twitter.com/edent">@edent</a> Weird, worked for me on 1password for android</section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/samnalty/status/1522597542348926978" aria-label="1 likes" class="social-embed-meta">❤️ 1</a><a href="https://twitter.com/samnalty/status/1522597542348926978" aria-label="0 replies" class="social-embed-meta">💬 0</a><a href="https://twitter.com/samnalty/status/1522597542348926978" aria-label="0 retweets" class="social-embed-meta">♻️ 0</a><a href="https://twitter.com/samnalty/status/1522597542348926978"><time datetime="2022-05-06T15:21:57.000Z">15:21 - Fri 06 May 2022</time></a></footer></blockquote><header class="social-embed-header"><a href="https://twitter.com/samnalty" class="social-embed-user"><img class="social-embed-avatar social-embed-avatar-circle" src="data:image/webp;base64,UklGRt4BAABXRUJQVlA4INIBAAAwCgCdASowADAAPrVIokqnJCMhrjv5mOAWiUAXZoHYyqQ0eAuLFQPefp2t0tF+xe/kNlepsH6/8TzPwSqnv3elmK12fKIlHfzuAq/Afb8g2Oq2z/VWt3N6yAAA/vNo84oq9c7uaDif8Ttje97m+ToovqRbZK4Ji8uX4H7sdFlcDpPn7sA0gmE/zTsAfycjXDV2uYyIIfbK1lNrEO+NTaFfxxHfX942TEZ/CFSGOB1D00iDvSiS8CF4B3v/d2/y23jeIHY6AAuWf9IxrmTLiNNHeQGcqdBHJyii0hajpueZks+6/Ixjb5v3i5SZy77+ul//Kp7VOFPZc7BBrsUgJjBZKPJCYyTLBgcfIt/+IrT2px6eU060ADdXeLL/5MilC4yJ84XaPSp3vzxlp/HskXunfb5XQ4dWxxSg7T3L4K40WyuE0idrfdk/GeoUnT2ox9cavFP0+4jfSHsDlRzqb1Ncaf4XQfryl8/fwQZ2tdROfnFCFQOUAqFkV25q3d6TDZ6TqCKNkpz/INc1FQv4pOxUQZ9TXhNFKxHQBHkrV5AAXRuf6mK1j+fDuDuhjHOeX+NI186Uzmfiwtwxe4beufuzjBgMoRuv9nVHYtcVbiw+AgAA" alt=""><div class="social-embed-user-names"><p class="social-embed-user-names-name">Sam Nalty</p>@samnalty</div></a><img class="social-embed-logo" alt="" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCmFyaWEtbGFiZWw9IlR3aXR0ZXIiIHJvbGU9ImltZyIKdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoCmQ9Im0wIDBINTEyVjUxMkgwIgpmaWxsPSIjZmZmIi8+PHBhdGggZmlsbD0iIzFkOWJmMCIgZD0ibTQ1OCAxNDBxLTIzIDEwLTQ1IDEyIDI1LTE1IDM0LTQzLTI0IDE0LTUwIDE5YTc5IDc5IDAgMDAtMTM1IDcycS0xMDEtNy0xNjMtODNhODAgODAgMCAwMDI0IDEwNnEtMTcgMC0zNi0xMHMtMyA2MiA2NCA3OXEtMTkgNS0zNiAxczE1IDUzIDc0IDU1cS01MCA0MC0xMTcgMzNhMjI0IDIyNCAwIDAwMzQ2LTIwMHEyMy0xNiA0MC00MSIvPjwvc3ZnPg=="></header><section class="social-embed-text"><small class="social-embed-reply"><a href="https://twitter.com/samnalty/status/1522597542348926978">Replying to @samnalty</a></small><a href="https://twitter.com/kerguio">@kerguio</a> <a href="https://twitter.com/edent">@edent</a> Even weirder, in the Android app it is a 10 digit OTP but on the windows app it's a normal 6 digit one.</section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/samnalty/status/1522597758749880323" aria-label="0 likes" class="social-embed-meta">❤️ 0</a><a href="https://twitter.com/samnalty/status/1522597758749880323" aria-label="2 replies" class="social-embed-meta">💬 2</a><a href="https://twitter.com/samnalty/status/1522597758749880323" aria-label="0 retweets" class="social-embed-meta">♻️ 0</a><a href="https://twitter.com/samnalty/status/1522597758749880323"><time datetime="2022-05-06T15:22:48.000Z">15:22 - Fri 06 May 2022</time></a></footer></blockquote>

<blockquote class="social-embed" id="social-embed-1522593948950310913" lang="en"><header class="social-embed-header"><a href="https://twitter.com/ben_nuttall" class="social-embed-user"><img class="social-embed-avatar social-embed-avatar-circle" src="data:image/webp;base64,UklGRuABAABXRUJQVlA4INQBAADQCQCdASowADAAPrVGnUqnI6KhsdM9+OAWiUAYC4F9/88AZUBi/iT0gUz/x2Yi1FvH8pq2qgkRxfVDXvlsquGPR233nNWgbjRPm3d5tEBY8WOtldy+DwAA/v34fHTGYdkcvM0NpyW5l9wXBDmJizXg/G78cyvDv/hDOMVphom5abaa+N0tCHH6Atw6JP/BnZnQ9+4bdDOHqsX8LyY/6z7Vb5lIiK5Hf8wmbpwHbOHKrPxOx4LkVB3C3Y1Uk8tsumTGiT8uvARYdIOs4daIZ8vZaXmuVrV78PrLysVkYNLl6q4rHMKZc4Mk1O6qaFaIbADyJeh0xRg5M2DWCKQ2wFsXn/oKXvO5lN2Vm+KlrJCkNL92nsAMUGUDOaDwqR40IHORon3f+esYpXN0TWg7NC4t900f/V7X19hanVskWTfhXN/R9at634o3kHU1HCGOgbGWHor/DMLFTeUFJ+CdEuarzqv0nc9P+OnpGI6wg7erI6NIQOeuD28SekqNZmjpsqbvvixRfW7Z8eHu//wtipY8+zP/0x6wT6+5sKxkpb4UqRQZ5/rIjGOGYQCt91SkKW3AHaFVtuzuMNz3UarAgVG0puJWkczvAj0sdp6DQ45edLE/gAA=" alt=""><div class="social-embed-user-names"><p class="social-embed-user-names-name">Ben Nuttall</p>@ben_nuttall</div></a><img class="social-embed-logo" alt="" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCmFyaWEtbGFiZWw9IlR3aXR0ZXIiIHJvbGU9ImltZyIKdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoCmQ9Im0wIDBINTEyVjUxMkgwIgpmaWxsPSIjZmZmIi8+PHBhdGggZmlsbD0iIzFkOWJmMCIgZD0ibTQ1OCAxNDBxLTIzIDEwLTQ1IDEyIDI1LTE1IDM0LTQzLTI0IDE0LTUwIDE5YTc5IDc5IDAgMDAtMTM1IDcycS0xMDEtNy0xNjMtODNhODAgODAgMCAwMDI0IDEwNnEtMTcgMC0zNi0xMHMtMyA2MiA2NCA3OXEtMTkgNS0zNiAxczE1IDUzIDc0IDU1cS01MCA0MC0xMTcgMzNhMjI0IDIyNCAwIDAwMzQ2LTIwMHEyMy0xNiA0MC00MSIvPjwvc3ZnPg=="></header><section class="social-embed-text"><small class="social-embed-reply"><a href="https://twitter.com/edent/status/1522592041150173190">Replying to @edent</a></small><a href="https://twitter.com/edent">@edent</a> Seems to work as my others do (in Google Authenticator), apart from the name is emojis and some RTL text</section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/ben_nuttall/status/1522593948950310913" aria-label="1 likes" class="social-embed-meta">❤️ 1</a><a href="https://twitter.com/ben_nuttall/status/1522593948950310913" aria-label="1 replies" class="social-embed-meta">💬 1</a><a href="https://twitter.com/ben_nuttall/status/1522593948950310913" aria-label="0 retweets" class="social-embed-meta">♻️ 0</a><a href="https://twitter.com/ben_nuttall/status/1522593948950310913"><time datetime="2022-05-06T15:07:40.000Z">15:07 - Fri 06 May 2022</time></a></footer></blockquote>

<blockquote class="social-embed" id="social-embed-1522592326509641736" lang="en"><header class="social-embed-header"><a href="https://twitter.com/kerguio" class="social-embed-user"><img class="social-embed-avatar social-embed-avatar-circle" src="data:image/webp;base64,UklGRgoCAABXRUJQVlA4IP4BAABQCwCdASowADAAPrVSoU2nJCMiJzgKSOAWiWkAE9efUuVVP0YgVprdB+q4wKaP4+tQT9U0xtjlubHIWJhQStYQS6H8gshCYHvQ7ePmiaXIrcC5qxugIucI8ZXMmacopZWPUoAA/v4u2vmGJ8xWI07tUFkaMDPwFq4SHNqEOAeceix6BTkrSTJfdMf12w9KYEZ3Hrkxnvfbv/zj8PPCG92ON8jbVkJJsFEghWl7plQo4trtEPUGqlBIZbjsehokBqafJe679sf7fqx4ife6Oe0CInR8fvrRkA1uBNCJ0/Nxxzy6fvkmF/Oiyd8ge2yCp9XyiY1n98gRvlS7TAIy0FDvldfTjdVywIdOKozXnHVYHO4bpcszFazlI8ta8Wg/RNTZ0T/bGPNZPFo7Yfi6ZrA0x77JNVarO39pJM8tugZA5Y83nZB5GMF3cp+wkrPeImEImZijJCYU0gKQJ8dkUM0r6eQ3t90zVXYgTfLxejej8OEOYu0nf0e8WPmtHvs8BEaEcq9uJ/fRO7k+Bb3/604NIT7G/q3r/D0AJq79tnYzcMImDDxt+bt7zFR6Q8Me3TTVo7yJdLXSUCPnWetlMwJ9vDd2thAnoYBisAS69mWsqlzv1B1Mk2zXOfxzVESQn9a43CHXA6hAun36kelfuPeC1Ge60Gfmp27gfNFAAAA=" alt=""><div class="social-embed-user-names"><p class="social-embed-user-names-name">Serge</p>@kerguio</div></a><img class="social-embed-logo" alt="" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCmFyaWEtbGFiZWw9IlR3aXR0ZXIiIHJvbGU9ImltZyIKdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoCmQ9Im0wIDBINTEyVjUxMkgwIgpmaWxsPSIjZmZmIi8+PHBhdGggZmlsbD0iIzFkOWJmMCIgZD0ibTQ1OCAxNDBxLTIzIDEwLTQ1IDEyIDI1LTE1IDM0LTQzLTI0IDE0LTUwIDE5YTc5IDc5IDAgMDAtMTM1IDcycS0xMDEtNy0xNjMtODNhODAgODAgMCAwMDI0IDEwNnEtMTcgMC0zNi0xMHMtMyA2MiA2NCA3OXEtMTkgNS0zNiAxczE1IDUzIDc0IDU1cS01MCA0MC0xMTcgMzNhMjI0IDIyNCAwIDAwMzQ2LTIwMHEyMy0xNiA0MC00MSIvPjwvc3ZnPg=="></header><section class="social-embed-text"><small class="social-embed-reply"><a href="https://twitter.com/edent/status/1522589408251334659">Replying to @edent</a></small><a href="https://twitter.com/edent">@edent</a> Keychain in iOS seemed to ingest it, but not sure where it went...</section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/kerguio/status/1522592326509641736" aria-label="1 likes" class="social-embed-meta">❤️ 1</a><a href="https://twitter.com/kerguio/status/1522592326509641736" aria-label="1 replies" class="social-embed-meta">💬 1</a><a href="https://twitter.com/kerguio/status/1522592326509641736" aria-label="0 retweets" class="social-embed-meta">♻️ 0</a><a href="https://twitter.com/kerguio/status/1522592326509641736"><time datetime="2022-05-06T15:01:13.000Z">15:01 - Fri 06 May 2022</time></a></footer></blockquote>

<blockquote class="social-embed" id="social-embed-1522605596247220224" lang="en"><header class="social-embed-header"><a href="https://twitter.com/Johninnit" class="social-embed-user"><img class="social-embed-avatar social-embed-avatar-circle" src="data:image/webp;base64,UklGRvIBAABXRUJQVlA4IOYBAAAwCgCdASowADAAPrVMoEwnJCMiJzgNsOAWiWoAnTOOfK/PX7uOAlMYtpVHyB2P97QjYhb+ry9tvRBMpAdz+98UEf37ZCZob6sKn6C+Bo32AZ93nSVkH37QuqAA/q49PU3rLu2tNJ9PCa1/7K/c6BEANQPLaPNIatWVu7F76L6IuGN/Gf/P2rc9i7DMmSjt3e476wdJYpWjejlRYlIJHtZ66CchOcac33VDloP7KNyBndWkJAShUSR0b0zsOL6w9pL8qsqT8WDBqsAN3y+Dz6uUA9fWrdSaUGmR0JG0wjf1xYV2lWNJuKxIzBq7c2eF8VpT+GlXlga77kcrjVXs2swDOghLAvBQE2ZqiUGD7vbgf8U0V5y6lf+pqPZTydbE+iVSeRHI9m0FVZMdAjYuLo19xAlRO3S/leprxG6qdEJZraLEkkFc/65ZsIXiejSjq4yS23Gk7B4q8vCzuGb5BPXXbtS1ccJejcEoIvRNB2H8YnliVS+Tg7vql2LAUmvvc977LHIOnClwFAGDXTyZB6fhfgH9LK1cW1rTL4Q4urck28ZFVMwdxDoJkdYkOsE50zpc+2HmWgCbbQySC0MKaef0XfrhTmfxjhZhLaPYY+6BVpndMPPO5kPXf8z1PjYHPGoGuBEAAAA=" alt=""><div class="social-embed-user-names"><p class="social-embed-user-names-name">John</p>@Johninnit</div></a><img class="social-embed-logo" alt="" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCmFyaWEtbGFiZWw9IlR3aXR0ZXIiIHJvbGU9ImltZyIKdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoCmQ9Im0wIDBINTEyVjUxMkgwIgpmaWxsPSIjZmZmIi8+PHBhdGggZmlsbD0iIzFkOWJmMCIgZD0ibTQ1OCAxNDBxLTIzIDEwLTQ1IDEyIDI1LTE1IDM0LTQzLTI0IDE0LTUwIDE5YTc5IDc5IDAgMDAtMTM1IDcycS0xMDEtNy0xNjMtODNhODAgODAgMCAwMDI0IDEwNnEtMTcgMC0zNi0xMHMtMyA2MiA2NCA3OXEtMTkgNS0zNiAxczE1IDUzIDc0IDU1cS01MCA0MC0xMTcgMzNhMjI0IDIyNCAwIDAwMzQ2LTIwMHEyMy0xNiA0MC00MSIvPjwvc3ZnPg=="></header><section class="social-embed-text"><small class="social-embed-reply"><a href="https://twitter.com/edent/status/1522589408251334659">Replying to @edent</a></small><a href="https://twitter.com/edent">@edent</a> Authy says "Token format invalid"</section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/Johninnit/status/1522605596247220224" aria-label="0 likes" class="social-embed-meta">❤️ 0</a><a href="https://twitter.com/Johninnit/status/1522605596247220224" aria-label="1 replies" class="social-embed-meta">💬 1</a><a href="https://twitter.com/Johninnit/status/1522605596247220224" aria-label="0 retweets" class="social-embed-meta">♻️ 0</a><a href="https://twitter.com/Johninnit/status/1522605596247220224"><time datetime="2022-05-06T15:53:57.000Z">15:53 - Fri 06 May 2022</time></a></footer></blockquote>

<iframe src="https://toot.me.uk/@revk/108255557953114821/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="400" allowfullscreen="allowfullscreen"></iframe>

<iframe src="https://mastodon.social/@andypiper/108255613228860659/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="400" allowfullscreen="allowfullscreen"></iframe>

<script src="https://mastodon.social/embed.js" async="async"></script>

<h2 id="why-is-this-a-problem"><a href="https://shkspr.mobi/blog/2022/05/why-is-there-no-formal-specification-for-otpauth-urls/#why-is-this-a-problem">Why is this a problem?</a></h2>

<p>Formally standardised specifications are a good thing. They mean that everyone is on a level playing-field and innovation can happen without actors enclosing the commons.</p>

<p>More prosaically, it means that users can be confident that any app will work with any code from any provider.  And implementers can have a forum where they can propose enhancements to the spec which won't break users' devices.</p>

<h2 id="where-are-the-specifications-deficient"><a href="https://shkspr.mobi/blog/2022/05/why-is-there-no-formal-specification-for-otpauth-urls/#where-are-the-specifications-deficient">Where are the specifications deficient?</a></h2>

<p>The URI encoding trips up a number of publishers - more on that in a later blog post.</p>

<p>Should there be a maximum or minimum length to the secret? Why? Why not?</p>

<p>Is it a wise idea to fix those algorithms? Does it matter if a weaker one is in there? Can they be deprecated?</p>

<p>Why just 6 or 8 digits? What's wrong with an arbitrary number of digits? What about using letters or other symbols?</p>

<p>The period is in seconds. Does that mean whole seconds? Can users reliably type in 8 digit codes which change every 15 seconds?</p>

<p>"Issuer" is in there twice. What if they don't match each other?</p>

<p>Do users want an "icon"? Should it be a Base64 encoded graphic? If so, what format? If not, is a URL sufficient?</p>

<p>I'm sure there are half-a-dozen more gripes you could come up with.</p>

<h2 id="the-future-kinda-sucks"><a href="https://shkspr.mobi/blog/2022/05/why-is-there-no-formal-specification-for-otpauth-urls/#the-future-kinda-sucks">The future kinda sucks</a></h2>

<p>I am <em>so</em> tired of Google getting bored halfway through designing something - and then expecting the rest of us to just figure out what they meant.</p>

<p>We're now in a situation where everyone is (rightly) pushing TOTP for 2FA, but there's no formal specification for how users can scan those codes into their apps. There's no way to propose changes. And there's no guarantee that a user will be able to reliably scan the codes they are given.</p>

<p>Bad times.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=42594&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/05/why-is-there-no-formal-specification-for-otpauth-urls/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
	</channel>
</rss>
