Git Product home page Git Product logo

base62-token.js's Introduction

Generate & Verify GitHub-style & npm-style Base62 Tokens

Works in Vanilla JS (Browsers), Node.js, and Webpack.

See the online Base62 token generator & verifier in action:

Install

Browser

<script src="https://unpkg.com/crc-32"></script>
<script src="https://unpkg.com/base62-token"></script>
var Base62Token = window.Base62Token;

Node.js / Webpack

npm install --save base62-token
var Base62Token = require("base62-token");

Usage

var dict = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var b62Token = Base62Token.create(dict);

var token = b62Token.generate("abc_", 30);
var verified = b62Token.verify(token);

API

Base62Token.generateDictionary(); // Return the Lexographic (a.k.a. GMP)
                                  // Base62 dictionary.

Base62Token.create(dictionary);   // Creates a token generator and verifier
                                  // 'dictionary' is any 62-char alphabet.
                                  // Returns a generator / verifier instance.

b62Token.generate(prefix, length); // Returns token string.
b62Token.verify(token);            // Returns true / false.
Base62Token.BITS_PER_CHARACTER    // 5.954196310386876
                                  // For reference: Base64 is an even 6

Base62Token.calcMinChars(bitlen); // calculate the minimum number of chars
                                  // needed to guarantee the target entropy.
                                  // ex: 173-bit entropy needs 30 chars

Base62Token.calcMinBits(charlen); // calculate the minimum entropy guaranteed
                                  // by the given number of characters
                                  // ex: 30 chars guarantees 178-bit entropy.

Base62Token.checksum(dict, str);  // generates an (unsigned) CRC-32 checksum
                                  // for the given string (where each char is
                                  // treated as a single byte).
                                  // Returns the Base62 encoded unsigned int.

Base62Token.encode(dict, n, pad); // encode a 32-bit int (i.e. CRC-32 checksum)
                                  // as Base62 in the given dictionary, with a
                                  // default pad of 6 (guarantees 32-bits).

Base62 Token Spec

GitHub Token Breakdown

The 40-character tokens are broken down into 3 consecutive parts:

pre_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxcccccc

  • Prefix: 4-char (ex: ghx_)
  • Entropy: 30-char (178-bits + leading 0 padding)
    • BITS_PER_CHAR = Math.log(62) / Math.log(2) // about 5.9541
    • BITS_PER_CHAR * 30 // about 178.6258
  • Checksum: 6-char CRC32 (32-bits, 4 bytes, 6 base62 characters)
    • (of entropy-only, not prefix)
    • BITS_PER_CHAR * 5 // about 35.7251
Prefix Entropy Checksum
pre_ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx cccccc

See

Pseudocode

const DICT = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
const PREFIX_LEN = 4
const CHECKSUM_LEN = 6

func GenerateBase62Token(prefix string, len int) string {
    entropy := []string{}
    for 0..len {
        index := math.RandomInt(62) // 0..61
        char := DICT[index]
        entropy = append(entropy, char)
    }
    chksum := crc32.Checksum(entropy) // uint32

    pad := CHECKSUM_LEN
    chksum62 := base62.Encode(DICT, chksum, pad)

    // ex: "ghp_" + "zQWBuTSOoRi4A9spHcVY5ncnsDkxkJ" + "0mLq17"
    return prefix + string(entropy) + chksum62
}

func VerifyBase62Token(token string) bool {
    // prefix is not used
    entropy := token[PREFIX_LEN:len(token)-CHECKSUM_LEN]
    chksum := base62.Decode(DICT, token[len(token)-CHECKSUM_LEN:]) // uint32

    return crc32.Checksum(entropy) == chksum
}

Standard Base62 Dictionaries

There are 3 widely-used, generic Base62 dictionaries, all of which are based on the alphanumeric character set (i.e. 0-9, A-Z, a-z).

For general encoding and decoding, you should use one of these:

  • Lexographic (digits, upper, lower)
    0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
  • BaseX (digits, lower, upper)
    0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
  • Truncated Base64 (upper, lower, digits)
    ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789

GitHub and NPM use the Lexographic (a.k.a. GMP) Base62 alphabet.

Legal

For a business license and/or commercial support ($99/year), please contact Root.

Copyright 2022 AJ ONeal
Copyright 2022 Root

MPL-2.0 (Open Source) | Terms of Use | Privacy Policy

base62-token.js's People

Contributors

pehunter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

benbehringer

base62-token.js's Issues

allow arbitrary-length prefixes, delimited by `_`

Right now the token string split function is hard-coded to expect a 4-character prefix string (i.e. ghp_).

However, since the _ serves as a delimiter outside of the base62 alphabet, we should just split on that.

Easy beginner-level programmer task. Anyone is free to take it.

[SOLVED] How to Verify GitHub & NPM tokens

Update: Solved

Working Online Demo

https://therootcompany.github.io/base62-token.js/

https://base62.js.org/

JavaScript

https://github.com/therootcompany/base62-token.js

Pseudo-code Solution

const dict = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

// prefix is like 'ghp_', 'gho_', etc
func GenerateBase62Token(prefix string, len int) string {
    entropy := []
    for 0..len {
        index := math.RandomInt(62)
        entropy = append(entropy, dict[index])
    }
    chksum := crc32.Checksum(entropy) // uint32

    pad := 6
    // ex: "ghp_" + "zQWBuTSOoRi4A9spHcVY5ncnsDkxkJ" + "0mLq17"
    return prefix + string(entropy) + base62.Encode(dict, chksum, pad)
}

Original

Here I've provided 20 valid (but expired) Personal Access Tokens for inspection.

However, I doubt that it will be possible to determine exactly how GitHub generates and verifies the checksum.

If I were them I would save a randomized base62 dictionary as a server-side secret so that attackers still have to hit up against rate limits to check tokens rather than having the same advantage of checking them online.

I bet that they were at least as smart as me when they spec'd this out for themselves.

Edit: Duh. What we have here isn't some sort of hashing or encryption, it's a simple substitution. If they truly documented their process and did use a random dictionary (I did, in fact, check against the normal dictionaries... or at least I thought I did), then all we have to do is create a table of known 6-character crc32 checksums and see what matches up! (and check in both directions in case the bytes are swapped for efficiency).

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 | 00 48 21 52 01 07
                                         |  0  m  L  q  1  7
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 | 00 30 26 61 26 22
                                         |  0  U  Q  z  Q  M
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 | 02 49 24 02 09 43
                                         |  2  n  O  2  9  h
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 | 00 20 18 43 36 04
                                         |  0  K  I  h  a  4
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 | 02 39 11 13 32 60
                                         |  2  d  B  D  W  y


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 | 01 17 22 45 09 61
                                         |  1  H  M  j  9  z
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 | 00 56 00 23 58 04
                                         |  0  u  0  N  w  4
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 | 03 28 58 50 48 37
                                         |  3  S  w  o  m  b
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 | 00 24 18 02 16 04
                                         |  0  O  I  2  G  4
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 | 00 19 54 22 16 20
                                         |  0  J  s  M  G  K


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 | 04 02 05 52 35 28
                                         |  4  2  5  q  Z  S
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 | 04 04 42 02 14 01
                                         |  4  4  g  2  E  1
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 | 04 22 58 23 40 34
                                         |  4  M  w  N  e  Y
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 | 04 05 45 16 44 44
                                         |  4  5  j  G  i  i
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 | 01 39 38 60 61 35
                                         |  1  d  c  y  z  Z


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 | 03 21 12 11 56 56
                                         |  3  L  C  B  u  u
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 | 01 39 32 47 17 29
                                         |  1  d  W  l  H  T
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 | 02 39 48 20 39 54
                                         |  2  d  m  K  d  s
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 | 00 03 44 49 22 36
                                         |  0  3  i  n  M  a
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 | 01 47 39 60 12 17
                                         |  1  l  d  y  C  H

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.