Skip to main content

Generate and verify unique, signed short codes.

Project description

SignedID

Generate a short, opaque, url-safe, unique code whose provenance can be verified statelessly.

Usage

The most basic usage looks like this:

>>> import signed_id
>>> secret = 'shhhh!'
>>> signed_id.generate('my_unique_id', secret)
'e6bed2a1'
>>> signed_id.verify('e6bed2a1', secret)
True

Overview

We need to be able to assign a unique code to a user that has the following properties:

  • The code is unique to a user
  • The code does not obviously reveal information about a user
  • We can verify that we generated the code
  • We do not need to store the code anywhere to verify it
  • The code is succinct for embedding in SMS messages
  • The code is a simple string, safe to pass in URLs
  • Valid codes are difficult to guess

How it works

Code generation

Codes are generated with, at minimum, an arbitrary piece of data about an entity and a secret string.

The specific steps are:

  1. Codes are generated from a unique piece of information about an entity, such as a database ID.
  2. This information is made opaque--while preserving uniqueness--with a strong hash function.
  3. The hash is truncated according to generation parameters to make the code more succinct.
  4. The hashed input is then signed by computing an additional hash with a secret piece of information.
  5. The signature hash is also truncated for succinctness.
  6. The two truncated hashes are concatenated, encoded as a hex string, and returned.

Code verification

Verification works on the principle of proof-of-knowledge. Nominally this means that the secret used for generation must also be known for verification. In practice, the other generation parameters (hash function and byte lengths) also must be consistent across generation and validation.

The verification steps are:

  1. Decode the input from hex to a bytestring. If the input is not valid hex, verification fails.
  2. Parse the bytestring based on the byte length parameters to the input hash and the signature hash.
  3. Compute the expected signature hash based on the input hash, secret, and provided hash function.
  4. Truncate the expected signature based on the byte length parameter.
  5. Assert that the expected truncated signature matches the signature parsed from the code.

Notes

Code length

The code length is customizable, with the caveat that shorter codes have a higher risk for collision and guessability.

Codes contain exactly id_bytes + sig_bytes bytes of information. Since the code bytes are represented as hex, the code length is 2 * (id_bytes + sig_bytes).

Defaults

By default we use id_bytes=2, sig_bytes=2, so the codes are 8-character ASCII strings.

Collisions

The probability of a collision (non-unique code generation) is 1 / 256^id_bytes.

Brute-forcing

The probability of generating a valid signature for a certain ID is 1 / 256^sig_bytes.

The probability of generating a valid ID completely at random is correspondingly smaller.

Reversing the input hash

The input hash is not inherently secure. For example, if the input ID is a simple database ID, and someone had access to a handful of codes, it would not be difficult to infer the hash function and recover the input with a lookup table. If it's necessary to guard against this, a more complicated (ideally salted) input should be used. For example, instead of a database ID, use a composite input of the database ID and record creation time.

Even if the input hash is reversible, brute-forcing the signature would still often be impractical, as it is effectively salted with the secret.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

signed_id-0.1.0.tar.gz (4.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

signed_id-0.1.0-py3-none-any.whl (5.2 kB view details)

Uploaded Python 3

File details

Details for the file signed_id-0.1.0.tar.gz.

File metadata

  • Download URL: signed_id-0.1.0.tar.gz
  • Upload date:
  • Size: 4.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.0

File hashes

Hashes for signed_id-0.1.0.tar.gz
Algorithm Hash digest
SHA256 4fd2d2da901fc3c3d4312957fb050a037ea85f89549f6c92d3b4c1bee44d3ca9
MD5 cbd94bb0972e22eb04267b922a197bad
BLAKE2b-256 4938b88a68a72e676eec074808d4c746e79da285ed384f399c85aa1e9be38406

See more details on using hashes here.

File details

Details for the file signed_id-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: signed_id-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 5.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.0

File hashes

Hashes for signed_id-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d3dfd6fdc123b96b5e7208fa6d4dafe5132647068f870883c52cadf10bf4ea0d
MD5 afc8c972d2d094543dddd4716b934b8f
BLAKE2b-256 caa960e8813633834135d603c219ed826d1d89c51bd13edb3cd9b1401a8b39de

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page