package licensing import ( "crypto/sha256" "encoding/hex" "errors" "fmt" "strings" ) const machineHashDomain = "softbox.machine-hash.v1\x00" // ErrInvalidMachineGUID reports a missing or malformed Windows MachineGuid. // It intentionally contains no source value. var ErrInvalidMachineGUID = errors.New("machine GUID is invalid") // DeriveMachineHash returns the v1, machine-bound license hash for a normalized // Windows MachineGuid and the Windows directory volume serial number. It never // persists either source identifier. func DeriveMachineHash(machineGUID string, systemVolumeSerial uint32) (string, error) { normalizedGUID, err := normalizeMachineGUID(machineGUID) if err != nil { return "", err } input := machineHashDomain + normalizedGUID + "\x00" + fmt.Sprintf("%08x", systemVolumeSerial) sum := sha256.Sum256([]byte(input)) return hex.EncodeToString(sum[:]), nil } func normalizeMachineGUID(value string) (string, error) { value = strings.TrimSpace(value) if len(value) != 36 { return "", ErrInvalidMachineGUID } for index := 0; index < len(value); index++ { character := value[index] if character > 0x7f { return "", ErrInvalidMachineGUID } if index == 8 || index == 13 || index == 18 || index == 23 { if character != '-' { return "", ErrInvalidMachineGUID } continue } if !isASCIHex(character) { return "", ErrInvalidMachineGUID } } return strings.ToLower(value), nil } func isASCIHex(character byte) bool { return character >= '0' && character <= '9' || character >= 'a' && character <= 'f' || character >= 'A' && character <= 'F' }