Skip to content

Instantly share code, notes, and snippets.

@bleonard252
Last active August 7, 2020 03:02
Show Gist options
  • Save bleonard252/6df468ff456cc7cba5d5d385b397358b to your computer and use it in GitHub Desktop.
Save bleonard252/6df468ff456cc7cba5d5d385b397358b to your computer and use it in GitHub Desktop.
Generic Anonymous ID
Display the source blob
Display the rendered blob
Raw
{
"metadata": {
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3-final"
},
"orig_nbformat": 2,
"kernelspec": {
"name": "python37364bit4c243d5d864b415090032e0ea949f88e",
"display_name": "Python 3.7.3 64-bit"
}
},
"nbformat": 4,
"nbformat_minor": 2,
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Anonymous IDs\n",
"\"Anonymous IDs\" are fingerprint resistant IDs generated using a timestamp and a \"real ID.\" If you know the *real ID*, you can check whether or not that *real ID* was the one used to generate the ID. More information at the bottom."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generate ID\n",
"Click play on the cell below to generate an ID. For best practice, make sure it begins with `{` and ends with `}`, even if it has a `}{` in the middle. This is good for the timestamp salt."
]
},
{
"cell_type": "code",
"execution_count": 0,
"outputs": [],
"metadata": {
"tags": []
},
"source": [
"import hashlib\n",
"from datetime import datetime\n",
"\n",
"inVersionCode = 10 \n",
" # The version code.\n",
"inRealId = input(\"Real ID: \")\n",
" # The \"real ID\" used to generate the ID.\n",
"inTimestamp = int(datetime.timestamp(datetime.now())) - 1577836800\n",
" # The timestamp used to generate the ID.\n",
"\n",
"def vc10HashAlgorithm(realid: str):\n",
" \"\"\"\n",
" Applies the version code 10 hash algorithm to realid. Returns a string to keep leading zeroes.\n",
" * realid is the source ID.\n",
" * msFromEpoch is the number of milliseconds since the 2020 epoch.\n",
" \"\"\"\n",
" out = hashlib.sha256(realid.encode('utf-8')).hexdigest()\n",
" print(\"ID Hash in SHA256: 0x\"+out)\n",
" out = str(int(out, base=16))\n",
" out = out.zfill(78)\n",
" return out\n",
"\n",
"print(\"10\"+vc10HashAlgorithm(inRealId+str(inTimestamp))+str(inTimestamp))\n",
"#print(str(vc10HashAlgorithm(inRealId+str(inTimestamp))))\n",
"#print(str(inTimestamp))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Analyze ID\n",
"Paste an ID into the form below and click \"Submit\" to test it."
]
},
{
"cell_type": "code",
"execution_count": 0,
"outputs": [],
"metadata": {},
"source": [
"%%html\n",
"<input id=\"anonyid\"><input type=\"button\" onclick=\"analyzeId()\" value=\"Submit\">\n",
"<div id=\"id-analysis\">Submit an ID above.</div>\n",
"<script>\n",
" function analyzeId() {\n",
" var aid = document.getElementById(\"anonyid\").value;\n",
" var outsect = document.getElementById(\"id-analysis\");\n",
" var checks = \"\";\n",
" //Length check\n",
" if (aid.substring(0,2) == \"10\" && aid.length < 81) checks = checks + `<p><strong><font color='orange'>WARNING</font>: ID is not of sufficient length!</strong></p>`;\n",
" //Output depending on version code\n",
" if (aid.substring(0,2) == \"10\") outsect.innerHTML = /*html*/`\n",
" <p><strong>Version Code:</strong> ${aid.substring(0,2)}</p>\n",
" <p><strong>ID Hash in decimal:</strong> ${aid.substring(2,78+2)}</p>\n",
" <!--p><strong>ID Hash in hex:</strong> 0x${parseFloat(aid.substring(2,78+2)).toString(16)}</p-->\n",
" <p><strong>Timestamp:</strong> ${aid.substring(78+2)}</p>\n",
" <p><strong>Date/Time of ID creation:</strong> ${new Date(parseInt(aid.substring(78+2)+\"000\")+1577836800000)}</p>\n",
" ${checks}\n",
" `;\n",
" else outsect.innerHTML = `Version code is not valid for this form.`;\n",
" }\n",
"</script>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Technical Details\n",
"When using generic anonymous IDs (GAIDs), any *real ID* can be used. This ID goes through an algorithm that may change depending on the version code. The version code is the first two digits, or more depending on those first two digits (version codes can introduce longer version codes, but they can't start with a version code that doesn't support it).\n",
"\n",
"Version codes indicate what the formatting, algorithm, and epoch, among other things, are for a given GAID. If a version introduces a longer version code, it must begin with a new version code. For example, if new version A introduces a four digit version code, that version, and its others, cannot use a version code beginning with `10`, because that code has been used by a version with a shorter version code in its format.\n",
"\n",
"Whether or not the *real ID* is enclosed does not matter. The *real ID* can be anything, including usernames, UUIDs, email addresses, phone numbers, or a picture of your cat.\n",
"\n",
"GAID is one implementation of this idea. Feel free to make your own derivations. Credit is appreciated.\n",
"\n",
"### Version Code `10`\n",
"The initial version. The epoch is January 1, 2020 at midnight UTC.\n",
"\n",
"#### Version Code `10` Hash Algorithm\n",
"1. Suffix the \"real ID\" with the timestamp. This should look like: `{6b8b3e88-3b75-4282-a3fb-c9d9c016aa28}18929398`\n",
"2. Hash that using SHA-256.\n",
"3. Convert the hex digest (i.e. `502367d66086...`) to base 10 (decimal). A really long number should appear (77 or 78 digits).\n",
"4. Pad that number to 78 digits using zeroes at the front. At maximum, one zero should be added.\n",
"\n",
"#### Format\n",
"- Version code: always `10` (length: 2 digits)\n",
"- Invisible ID: created using the Version Code `10` Hash Algorithm (length: 78 digits)\n",
"- Timestamp: a UTC timestamp, NO decimal points (length: at least one digit)"
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment