Skip to content

Instantly share code, notes, and snippets.

@dchest
Created July 28, 2013 09:41
Show Gist options
  • Select an option

  • Save dchest/6098083 to your computer and use it in GitHub Desktop.

Select an option

Save dchest/6098083 to your computer and use it in GitHub Desktop.

Technical details

I. Generating seed

  1. Generate 16-byte Seed using CSPRNG or a source of truly random numbers.
  2. Output Seed for a user and/or a program to store.

II. Generating derived passwords

  1. Ask user for master password MasterPassword and Identifier (see section III). Load Seed from storage or ask user to input it.
  2. Calculate 16-byte string CP where:
    CP := scrypt(MasterPassword)
    with constant salt "Generapasswordus" and user-provided cost parameters.
  3. XOR each byte of CP with each byte of Seed:
    Key := CPSeed
    to acquire 16-byte Key.
  4. Calculate derived stream:
    DerivedStream := PBKDF2-HMAC-SHA256(Key, Identifier)
    with 1 iteration (using Key as key, Identifier as salt).
  5. Encode DerivedStream as described in a section IV and output it.

III. Encoding identifiers

  1. Ask user for two strings: account name (e.g. email, login) Name and place identifier (e.g. website address) Place, and one 32-bit integer Counter encoded into 4 bytes in big endian byte order.
  2. Treat Alphabet, a user-specified alphabet for encoding derived password (see section IV), as an array of bytes.
  3. Treat AlphabetByteLength, NameByteLength, PlaceByteLength as 32-bit integer, representing byte lengths of the respective inputs, encoded into 4 bytes in big endian byte order.
  4. Concatenate values and separator characters (shown in quotes) in the following way:
    Identifier :=
        AlphabetByteLength || ":" || Alphabet || "," ||
        NameByteLength || ":" || Name || "," ||
        PlaceByteLength || ":" || Place || "," ||
        Counter

IV. Encoding derived passwords

  1. Treat DerivedStream from section II as a stream of pseudorandom bytes. Treat Alphabet, a user-specified encoding alphabet of maximum 256 characters, as an array of characters and AlphabetLength as the length of this array. DerivedPasswordLength is the user-specified length of derived password.
  2. Calculate UpperBound as:
    UpperBound := 256 − (256 mod AlphabetLength)
  3. Treat each byte of DerivedStream that is less than UpperBound, modulo AlphabetLength, as an index into Alphabet array and append the value at this index to DerivedPassword until its length reaches DerivedPasswordLength:
    while (length of DerivedPassword) < DerivedPasswordLength do
       b := next byte of DerivedStream
       if b < UpperBound then
          append to DerivedPassword value of Alphabet[b mod AlphabetLength]
       end if
    end while
  4. Output DerivedPassword as the final derived password string.

V. Changing master password

  1. Ask user for old master password MasterPasswordold and new master password MasterPasswordnew. Load Seed from storage or ask user to input it.
  2. Calculate two 16-byte strings CPold and CPnew where:
    CPold := scrypt(MasterPasswordold)
    CPnew := scrypt(MasterPasswordnew)
    with constant salt "Generapasswordus" and user-provided cost parameters.
  3. XOR each byte of CPold with each byte of CPnew:
    Diff := CPoldCPnew
    to acquire 16-byte difference Diff.
  4. XOR each byte of Seed with each byte of Diff:
    Seednew := SeedDiff
  5. Output Seednew as a new seed. Tell the user to use MasterPasswordnew and Seednew from now on.
  6. Destroy Seed if the program stores it, or tell the user to destroy it.

VI. Encoding of seed for display or entry

  1. Append to Seed 1-byte checksum:
    SeedWithChecksum := Seed || CRC-8(Seed)
    to acquire 17-byte array.
  2. Encode SeedWithChecksum in Base-32:
    EncodedSeed := base32(SeedWithChecksum)
    to acquire 28-character string. (Base-32 encoding uses alphabet specified in RFC-4648: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", or in lower letter case).
  3. Split EncodedSeed into 7 groups of 4 characters each separated with space and output the result.

Decoding must accept characters in any letter case and ignore characters outside of the Base-32 alphabet (including space). Decoders must verify the checksum.

VI. Notes

  • Character encoding is UTF-8.
  • Maximum number of characters in Alphabet is 256.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment