Birth date hashes

The Update Account and Bulk Update Account endpoints accept birthdates in a few different formats for privacy purposes.

Our Okta integration can also accept birthdates in a profile property birthdate. This profile property can be in any of the following formats.

If a birthdate is set prior to the account being bound to a subject, then the birthdate must match the birthdate on their ID.

Plaintext

You can specify a birthdate in RFC 3339 date format, i.e. YYYY-MM-DD. For example, if the person was born on January 2, 2006, then you would specify 2006-01-02.

SHA256 Hash

This option uses a one-way hashing algorithm to obscure the birthdate data when it is stored in or transferred through an intermediary system, such as Okta, prior to reaching Nametag.

While privacy is improved compared to the plain-text in Option 1, because the range of candidate birthdate values is computationally small, this option only protects against casual observers. It is possible to determine the plain-text birthdate from a straightforward brute-force effort.

When using hashed values, data must be represented in PHC String Format. An example of the basic format is shown here:

$sha256$SALT$HASH

or

$sha256$HASH

Salting

You can optionally mix a salt value into your hash. A good salt value is one that is unique for each user, and preferably random.

Other suggested salt values are any of:

  • randomly generated bytes
  • user’s email address
  • your directory’s immutable identifier for the account (e.g. the UPN in Entra ID)
  • the Nametag identifier for the account

If you provide a salt, it must be at least 12 bytes an no more than 64 bytes in length. Salt values must be base64-encoded as described in PHC.

Generating the hash

To generate a salted hash, prepend the un-encoded salt before the birthdate. Next assemble the birthdate into the format specified in the Plaintext section (above) and convert to UTF-8 bytes. Create a byte stream consisting of the bytes from the salt followed immediately by the bytes from the birthdate. (If both your salt and birthdate values are already string text, this can be simplified by concatenating the birthdate after the salt, then converting to UTF-8 bytes.)

Finally, apply the hash algorithm to the concatenated bytes. The result of the hash is another stream of bytes; use Base64-encoding to transform the hash output back to ASCII characters, and exclude any trailing Base64 padding characters (=).

Example birthdate:
1970-01-01
Example salt:
user@example.com
Data prior to hashing:
user@example.com1970-01-01
Hash:
A3NAedY2+nPm666JDVsA34TQLVCLmzok4E8uemN2nkk

The salt value must be communicated within the PHC String as the $ symbol followed by the Base64-encoded stream of the salt bytes as used when generating the hash value. Any trailing Base64 padding characters (=) must be excluded. This value appears after the algorithm identifer and immediately prior to the encoded hash, separated by the $ symbol.

Completing the above example, the final output value to Nametag is:

Example birthdate:
1970-01-01
Example salt:
user@example.com
Base64-encoded salt:
dXNlckBleGFtcGxlLmNvbQ
Value for Nametag:
$sha256$dXNlckBleGFtcGxlLmNvbQ$A3NAedY2+nPm666JDVsA34TQLVCLmzok4E8uemN2nkk

Code examples

In each of the example programs below, the expected output is:

$sha256$dXNlckBleGFtcGxlLmNvbQ$A3NAedY2+nPm666JDVsA34TQLVCLmzok4E8uemN2nkk

Bash + OpenSSL

#!/bin/bash
SALT="user@example.com"
SALT_B64=$(echo -n "${SALT}" | base64)
BIRTHDATE="1970-01-01"
BYTES_TO_HASH="${SALT}${BIRTHDATE}"
HASH=$(echo -n "${BYTES_TO_HASH}" | openssl dgst -binary -sha256 | base64)
VALUE="\$sha256\$${SALT_B64//=/}\$${HASH//=/}"
echo "${VALUE}"

Go

package main

import (
	"crypto/sha256"
	"encoding/base64"
	"fmt"
)

func main() {
	salt := "user@example.com"
	birthdate := "1970-01-01"
	bytesToHash := []byte(salt + birthdate)
	hash := sha256.Sum256(bytesToHash)
	value := "$sha256" +
		"$" + base64.RawStdEncoding.EncodeToString([]byte(salt)) +
		"$" + base64.RawStdEncoding.EncodeToString(hash[:])
	fmt.Println(value)
}

HMAC-SHA256

This option builds on SHA256 by adding a shared secret to protect and authenticate the hashed birthdate. The hashed content is the same as Option 2, but instead of SHA256, we use HMAC SHA256.

Generating a shared secret

Specify a shared secret for your directory using the Nametag API.

SECRET=$(echo -n "ThisIsMySecret" | base64)
curl -H "Authorization: Bearer API-KEY" \
  https://nametag.co/api/directories/DIRECTORY-ID \
  -X PATCH \
  -d "{\"birth_date_hmac_secret\": \"$SECRET\"}"

Using HMAC

When using an HMAC algorithm, the HMAC input is the same salted input bytes as described in Option 2. The shared secret must be given to the HMAC algorithm along with, but separate from, the salted input bytes.

Example

Extending the example from Option 2, but with a secret known only to the source system and Nametag:

Example birthdate
1970-01-01
Example salt:
user@example.com
Base64-encoded salt:
dXNlckBleGFtcGxlLmNvbQ
Shared secret:
ThisIsMySecret
Algorithm:
HMAC SHA-256
Input to algorithm:
user@example.com1970-01-01
Output to Nametag:
$hs256$dXNlckBleGFtcGxlLmNvbQ$s9mfjPMiytKcyqgfKdh7TYba0TlmgNC5BznkA3PyM40

HMAC Code examples

In each of the example programs below, the expected output is:

$hs256$dXNlckBleGFtcGxlLmNvbQ$s9mfjPMiytKcyqgfKdh7TYba0TlmgNC5BznkA3PyM40

Bash + OpenSSL

#!/bin/bash
SALT="user@example.com"
SALT_B64=$(echo -n "${SALT}" | base64)
SHARED_SECRET="ThisIsMySecret"
SHARED_SECRET_HEX=$(echo -n "${SHARED_SECRET}" | xxd -p -u | tr -d '\n')
BIRTHDATE="1970-01-01"
BYTES_TO_HASH="${SALT}${BIRTHDATE}"
HASH=$(echo -n "${BYTES_TO_HASH}" | openssl dgst -binary -sha256 -mac hmac -macopt "hexkey:${SHARED_SECRET_HEX}" | base64)
VALUE="\$hs256\$${SALT_B64//=/}\$${HASH//=/}"
echo "${VALUE}"

Go

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
)

func main() {
	secret := []byte("ThisIsMySecret")
	salt := "user@example.com"
	birthDate := "1970-01-01"
	bytesToHash := []byte(salt + birthDate)
	mac := hmac.New(sha256.New, secret)
	mac.Write(bytesToHash)
	hash := mac.Sum(nil)
	value := "$hs256" +
		"$" + base64.RawStdEncoding.EncodeToString([]byte(salt)) +
		"$" + base64.RawStdEncoding.EncodeToString(hash)
	fmt.Println(value)
}