TEMP_Tightener Metrics - zwettemaan/TightenerDocs GitHub Wiki

GUIDs and hashes

Tightener relies on a good hashing algorithm which can consume data and spits out an 128-bit hash. These hashes are treated like they are GUIDs.

In the Tightener eco-system, when we talk about GUIDs we might either talk randomly generated GUIDs or calculated hashes in GUID format.

GUID types:

  • machine GUID
  • local GUID
  • hardware GUID
  • allowance GUID
  • user GUID

Tightener repository

There is a central Tightener server which serves as a repository for GUIDs.

The server contains very little data. All it stores are GUIDs and relations between GUIDs.

No personal information is stored: no names, passwords, allowances, licenses...

The Tightener repository contains just the following tables:

machines: machineGUID localGUID timestamp

allowances: allowance GUID machine GUID

ownership: allowance GUID user GUID

Machine GUID

To work in the Tightener context, a computer needs a stable, globally unique GUID.

This will be called a 'machine GUID'.

The machine GUID must be unique, and will be fairly stable.

A machine GUID can be forcibly changed due to special circumstances.

For example, cloning a virtual computer, or restoring an older image backup onto replacement hardware - the cloned machine will be forced to accept a new machine GUID.

User GUID

A user GUID is calculated as a hash of a JSON file. There should be no lookup table that links user GUID back to user info.

The user can generate a JSON file and a password

userInfo: { ...user info... ...random salt... }

password: ....

These files are stored on the user's computer and possibly by a commercial partner. This info is not stored in the Tightener repository.

Hardware GUID

Tightener can calculate a hardware GUID for the computer it is running on.

The only purpose of the hardware GUID is to detect a possible configuration difference in the computer or its surroundings.

The hardware GUID is not used to identify computers and the hardware GUID does not need to be globally unique.

This calculated GUID is a hash which combines a bunch of external factors: things like Ethernet MAC address, RAM size, computer name, operating system and version,...

These external factors will be chosen to be somewhat stable. Changes should occur occasionally, over time spans of months, weeks or days. A calculated hardware GUID will remain the same for some period of time.

No issues occur even if there is a non-negligible chance that two computers have the same hardware GUID.

Assigning Machine GUIDs

Each computer will be represented by two randomly generated GUID: a local GUID and a machine GUID.

Machine GUID are permanent in most circumstances.

Once assigned to a computer, they remain associated with that computer even when the hardware GUID or local GUID change.

In some edge cases, a machine might be forced to give up its hardware GUID and will be assigned a new one.

Local GUID are impermanent. Each time a config change gets detected by way of a hardware GUID change, a new local GUID will be generated.

When a computer starts using Tightener, it initializes the following variables from a preferences file (if any):

prefs = readPrefs()
previousLocalGUID = prefs.localGUID
previousRepoTimestamp = prefs.repoTimestamp
previousHardwareGUID = prefs.hardwareGUID
hardwareGUID = RecalculateHardwareGUID()
if (! previousLocalGUID) {
    localGUID = NewRandomGUID()
} 
else if (hardwareGUID != previousHardwareGUID) {
    localGUID = NewRandomGUID()
} 
else {
     localGUID = previousLocalGUID
}

In words, this pseudocode means: 'unless the hardwareGUID changes, retain the existing localGUID'.

Then the computer will send a message to the Tightener repository with the following data:

(previousLocalGUID, previousRepoTimestamp, localGUID)

The repository will perform a look up in the machines table. On the repository the following pseudocode executes:

machineRecord = findMachineRecord(localGUID);

if (machineRecord)
    if (machineRecord.timestamp != previousRepoTimestamp) {
        machineRecord = undefined
    }
}

newRepoTimestamp = now()

if (! machineRecord) {

    machineGUID = NewRandomGUID()
    localGUID = NewRandomGUID()
    appendMachineRecord(
        localGUID,
        { 
            timestamp: newRepoTimestamp,
            machineGUID: machineGUID
        }
    )
}
else {

    machineGUID = machineRecord.machineGUID
    updateMachineRecord(
        localGUID,
        { 
            timestamp: newRepoTimestamp,
        })
    )
}

sendReply({
    localGUID: localGUID,
    timestamp: newRepoTimestamp
})

The computer receives this localGUID (which might differ from the value it sent), and time stamp and saves them to the preferences file:

localGUID = reply.localGUID
repoTimestamp = reply.timestamp

prefs.localGUID = localGUID
prefs.repoTimestamp = repoTimestamp
prefs.hardwareGUID = hardwareGUID

savePrefs(prefs)

Allowance slots

The repo has a table with allowance slots. Each such slot is identified by a random allowance GUID.

Allowances can represent all kinds of things - a license to run software, an ownership certificate,...

If someone wants to allow a second party some favour or feature, they will encode this into a JSON file.

The details of the JSON file are not determined by Tightener.

In this JSON file, they will then also store an allowance GUID, and then 'seal' the JSON file by hashing and electronically signing it.

They can register this the allowance file hash in the Tightener repo.

This allowance file can then be handed to the second party.

Tightener can verify that this file is genuine by checking the hash and the signature.

When the software needs to verify that something is allowed, it will read the JSON file, extract the allowance GUID, and send the local GUID and the allowance GUID to the repo. The repo then responds with true or false if the allowance is linked to the machine.

If the allowance has not been used yet, the machine becomes associated with it.