Skip to content

[Design Proposal] Stateless, DB-less, secure, and failsafe method for generating and verifying Dynamic Secrets / rotating verification codes

License

Notifications You must be signed in to change notification settings

DISTREAT/Rotkeappchen

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rotkeappchen - Dynamic Secrets

Rotkeappchen is a design proposal for a stateless, DB-less, secure, and failsafe method for generating and verifying Dynamic Secrets / rotating verification codes.

The proposed design may apply to all tasks that require verifying requests. Such tasks may include e-mail verification, CAPTCHA Challenges, and Service Tickets.

This repository contains a PoC Zig library. In addition, I suggest you take a look at my other related repositories:

  • CAPTCHA-System: Fully-fledged implementation of Rotkeappchen for CAPTCHA challenges.
  • CAPTCHA-Generator: Bun-compatible typescript library for generating CAPTCHA challenges using ImageMagick.
  • Rotkeappchen-rs: Rust implementation of the Rotkeappchen algorithm.

Note: Build using Zig version 0.11.0.

A New Approach to Dynamic Secrets

Inspired by TOTP, I propose a draft for an algorithm that generates and verifies the validity of verification codes.

The proposed algorithm has the advantage of being able to regenerate the same output which in return allows for an easy validation of rotating authentication tokens.

On one hand the advantage of following this idea is that it uses solely cryptographic algorithms, thus guaranteeing stability, efficiency, and error resistance. On the other hand, its security depends on the so-called request-identifying salt.

Algorithm

The goal of the algorithm is to generate a token that should persist in a variable time frame so that calculating the algorithm again results in the same token.

H(s  p  t / tx + o)

where

Parameter Description
H is the hashing algorithm
s is the request-identifying salt, identifying the request made
p is the shared secret, protecting against calculability by a third party
t is the epoch as specified in seconds since the Unix epoch
tx is the duration after which the digest should rotate
o is the factor to subtract from or add to as to calculate ahead or recalculate previous digests (defaults to 0)
Operator Description
Concatentation
⌊x⌋ Floor

The returned hash digest may be further processed (ex. truncated and encoded) as seen fit.

To overcome the issue of synchronization, we suggest (similar to RFC 4226) calculating a window of s, in this case as a look-back buffer though (ex. calculating a digest for o=-1 and o=0). The reason is that if the server calculates a token and sends it over to the client, but the counter increments directly afterward, then the server is still capable of verifying the validity of the last token. Note that this is also the reason why the time frame tx does not equal the expiration time of a generated digest.

Security requirements and concerns

Hashing Algorithm

Unlike HOTP, I propose using a hashing algorithm that is resistant to Length Extension Attacks, such as SHA-3, Blake3, or truncated versions of SHA-2. This issue should not exist if the resulting digest is truncated manually.

In addition, and due to the nature of the application, collision resistance is not a primary goal. Nonetheless, more collisions will result in better guesswork of a potential attacker. Choosing a hashing algorithm with good Avalanche Effect should reduce the probability of collisions when truncating.

One essential attribute is to choose a hashing algorithm with good preimage resistance to prevent an attacker from finding the secret since the other input parameters are retrievable. Once again, truncating will add redundancy which in return should in theory prevent finding the secret in the first place.

Request-identifying Salt

This parameter is crucial to security and easy to mess up. Because the request-identifying salt is likely stored client-side and passed with requests made, it should be designed per request and per request only. This implies that instead of using a random salt stored client-side (as a session token), use a salt that changes with each request (ex. registration of users -> unique username requested).

Example

To prevent users from mass-registering unique usernames, every submission will require a solved CAPTCHA challenge.

Hereby, the challenge should be generated using Rotkeappchen with the username as the request-identifying salt, because therefore every username generates a unique challenge. Contrary, if the session was used as a request-identifying salt, an attacker could easily use a solved challenge for registering multiple usernames.

Shared secret

A good requirement is having a lengthy secret to mitigate the risk of Brute Force, I suppose a length above 32 bytes should be more than sufficient. This secret is to be stored with the highest security and should never be disclosed.

In case the secret is disclosed nonetheless, make sure to change it immediately.

This secret is to be thought of as a pepper and is the backing force, preventing calculability by a third party.

Time frame

The time frame in which a digest persists should be long enough that it does not interfere with the usability, but also relatively short to prevent the request-identifying salt from being used for a DDoS/DoS Attack.

Window Size

The window size used for mitigating desynchronization should be small, to reduce the amount of calculation made and prevent a DDoS/DoS Attack.

Example integration

Suppose a simple website where users can submit a newsletter subscription via their e-mail address.

Before a user submits their e-mail address, a request is sent to the server along with the e-mail address provided to retrieve a CAPTCHA challenge.

The server now interprets the request and uses the proposed algorithm to generate a hash digest using the user's e-mail address as a request-identifying salt. Right after, the digest is truncated to the first 3 bytes, converted to hexadecimal, and converted into an OCR-proof image representation.

The user is now confronted with the server's response, including the CAPTCHA challenge.

Once the user solves the challenge, they will submit their newsletter subscription request along with the challenge solution. The server now calculates the same algorithm for o=0, trying o=-1 if the digest does not match the solution when truncated and converted to hexadecimal.

In case the solution does not match then the request will be denied, otherwise, the server further processes the request proofing the captcha as solved.

About

[Design Proposal] Stateless, DB-less, secure, and failsafe method for generating and verifying Dynamic Secrets / rotating verification codes

Topics

Resources

License

Stars

Watchers

Forks

Languages