// identity provides functions for creating and reading identity keys and identity hashes package identity // Identity Keys are Edwards Keys used for identity purposes. import "seekia/internal/encoding" import "seekia/internal/cryptography/blake3" import "seekia/internal/cryptography/edwardsKeys" import "bytes" import "slices" import cryptoRand "crypto/rand" import mathRand "math/rand/v2" import "errors" func VerifyIdentityKeyHex(identityKey string)bool{ isValid := edwardsKeys.VerifyPublicKeyHex(identityKey) if (isValid == false){ return false } return true } func VerifyIdentityHash(identityHash [16]byte, identityTypeProvided bool, identityType string)(bool, error){ if (identityTypeProvided == true){ if (identityType != "Mate" && identityType != "Host" && identityType != "Moderator"){ return false, errors.New("VerifyIdentityHash called with invalid provided identityType: " + identityType) } } userIdentityType, err := GetIdentityTypeFromIdentityHash(identityHash) if (err != nil){ return false, nil } if (identityTypeProvided == false){ return true, nil } if (userIdentityType != identityType){ return false, nil } return true, nil } func GetIdentityTypeFromIdentityHash(identityHash [16]byte)(string, error){ identityTypeByte := identityHash[15] if (identityTypeByte == 1){ return "Mate", nil } if (identityTypeByte == 2){ return "Host", nil } if (identityTypeByte == 3){ return "Moderator", nil } identityHashHex := encoding.EncodeBytesToHexString(identityHash[:]) return "", errors.New("GetIdentityTypeFromIdentityHash called with invalid identity hash: " + identityHashHex) } //Outputs: // -[16]byte: Bytes representation of identity hash // -string: Identity type of identity hash // -error func ReadIdentityHashString(identityHash string)([16]byte, string, error){ if (len(identityHash) != 27){ return [16]byte{}, "", errors.New("ReadIdentityHashString called with invalid identity hash: Invalid length: " + identityHash) } identityKeyHashString := identityHash[:24] checksumString := identityHash[24:26] identityTypeCharacter := string(identityHash[26]) identityKeyHashBytes, err := encoding.DecodeBase32StringToBytes(identityKeyHashString) if (err != nil) { return [16]byte{}, "", errors.New("ReadIdentityHashString called with invalid identity hash: Contains invalid characters: " + identityHash + ": " + err.Error()) } if (len(identityKeyHashBytes) != 15) { return [16]byte{}, "", errors.New("ReadIdentityHashString called with invalid identity hash: Invalid bytes length: " + identityHash) } checksumByte, err := encoding.DecodeHexStringToBytes(checksumString) if (err != nil){ return [16]byte{}, "", errors.New("ReadIdentityHashString called with invalid identity hash: Invalid checksum: Not Hex: " + identityHash) } if (len(checksumByte) != 1){ return [16]byte{}, "", errors.New("ReadIdentityHashString called with invalid identity hash: Invalid checksum: Invalid Length: " + identityHash) } expectedChecksumByte, err := blake3.GetBlake3HashAsBytes(1, identityKeyHashBytes) if (err != nil) { return [16]byte{}, "", err } areEqual := bytes.Equal(checksumByte, expectedChecksumByte) if (areEqual == false){ return [16]byte{}, "", errors.New("ReadIdentityHashString called with invalid identity hash: Invalid checksum: " + identityHash) } getIdentityTypeNameAndByte := func()(string, byte, error){ if (identityTypeCharacter == "m"){ return "Mate", 1, nil } if (identityTypeCharacter == "h"){ return "Host", 2, nil } if (identityTypeCharacter == "r"){ return "Moderator", 3, nil } return "", 0, errors.New("ReadIdentityHashString called with invalid identity hash: Invalid identityType character: " + identityTypeCharacter + " from " + identityHash) } identityType, identityTypeByte, err := getIdentityTypeNameAndByte() if (err != nil) { return [16]byte{}, "", err } identityHashBytes := append(identityKeyHashBytes, identityTypeByte) identityHashBytesArray := [16]byte(identityHashBytes) return identityHashBytesArray, identityType, nil } //Outputs: // -string: Encoded identity hash // -string: Identity type // -error func EncodeIdentityHashBytesToString(identityHashBytes [16]byte)(string, string, error){ identityKeyHashBytes := identityHashBytes[:15] identityTypeByte := identityHashBytes[15] checksumByte, err := blake3.GetBlake3HashAsBytes(1, identityKeyHashBytes) if (err != nil) { return "", "", err } identityKeyHashString := encoding.EncodeBytesToBase32String(identityKeyHashBytes) checksumByteHex := encoding.EncodeBytesToHexString(checksumByte) //Outputs: // -string: Identity type // -string: Identity type character // -error getIdentityTypeWithCharacter := func()(string, string, error){ if (identityTypeByte == 1){ return "Mate", "m", nil } if (identityTypeByte == 2){ return "Host", "h", nil } if (identityTypeByte == 3){ return "Moderator", "r", nil } return "", "", errors.New("EncodeIdentityHashBytesToString called with invalid identityHashBytes: Contains invalid identityTypeByte.") } identityType, identityTypeCharacter, err := getIdentityTypeWithCharacter() if (err != nil) { return "", "", err } identityHash := identityKeyHashString + checksumByteHex + identityTypeCharacter return identityHash, identityType, nil } func ConvertIdentityKeyToIdentityHash(identityKey [32]byte, identityType string)([16]byte, error){ if (identityType != "Mate" && identityType != "Host" && identityType != "Moderator"){ return [16]byte{}, errors.New("ConvertIdentityKeyToIdentityHash called with invalid identityType: " + identityType) } identityKeyHashBytes, err := blake3.GetBlake3HashAsBytes(15, identityKey[:]) if (err != nil) { return [16]byte{}, err } getIdentityTypeByte := func()byte{ if (identityType == "Mate"){ return 1 } if (identityType == "Host"){ return 2 } return 3 } identityTypeByte := getIdentityTypeByte() identityHashBytes := append(identityKeyHashBytes, identityTypeByte) identityHashArray := [16]byte(identityHashBytes) return identityHashArray, nil } func GetNewRandomIdentityHash(identityTypeProvided bool, identityType string)([16]byte, error){ identityKeyHashBytes := make([]byte, 15) _, err := cryptoRand.Read(identityKeyHashBytes[:]) if (err != nil){ return [16]byte{}, err } getIdentityTypeByte := func()(byte, error){ if (identityTypeProvided == false){ randomIndex := mathRand.IntN(3) if (randomIndex == 0){ return 1, nil } if (randomIndex == 1){ return 2, nil } // randomIndex == 2 return 3, nil } if (identityType == "Mate"){ return 1, nil } if (identityType == "Host"){ return 2, nil } if (identityType == "Moderator"){ return 3, nil } return 0, errors.New("GetNewRandomIdentityHash called with invalid identityType: " + identityType) } identityTypeByte, err := getIdentityTypeByte() if (err != nil) { return [16]byte{}, err } identityHashBytes := append(identityKeyHashBytes, identityTypeByte) identityHashArray := [16]byte(identityHashBytes) return identityHashArray, nil } func GetPublicPrivateIdentityKeysFromSeedPhraseHash(inputSeedPhraseHash [32]byte)([32]byte, [64]byte, error){ // This is the salt used to derive identity hashes from seed phrase hashes // It is the string "identitykeysseed" decoded from base32 to bytes. identityKeysSalt := []byte{64, 200, 217, 162, 120, 81, 49, 41, 16, 131} hashInput := slices.Concat(inputSeedPhraseHash[:], identityKeysSalt) seedBytes, err := blake3.Get32ByteBlake3Hash(hashInput) if (err != nil) { return [32]byte{}, [64]byte{}, err } publicKeyArray, privateKeyArray := edwardsKeys.GetSeededEdwardsPublicAndPrivateKeys(seedBytes) return publicKeyArray, privateKeyArray, nil } func GetNewRandomPublicPrivateIdentityKeys()([32]byte, [64]byte, error){ publicKeyArray, privateKeyArray, err := edwardsKeys.GetNewRandomPublicAndPrivateEdwardsKeys() if (err != nil) { return [32]byte{}, [64]byte{}, err } return publicKeyArray, privateKeyArray, nil } func GetIdentityHashFromSeedPhraseHash(seedPhraseHash [32]byte, identityType string)([16]byte, error){ publicIdentityKey, _, err := GetPublicPrivateIdentityKeysFromSeedPhraseHash(seedPhraseHash) if (err != nil) { return [16]byte{}, err } identityHash, err := ConvertIdentityKeyToIdentityHash(publicIdentityKey, identityType) if (err != nil) { return [16]byte{}, err } return identityHash, nil } // We use this function to generate custom identity hashes // This is faster, because we only have to generate the first 10 bytes of the identity hash, and we don't have to calculate the checksum func GetIdentityHash16CharacterPrefixFromSeedPhraseHash(seedPhraseHash [32]byte)(string, error){ publicIdentityKey, _, err := GetPublicPrivateIdentityKeysFromSeedPhraseHash(seedPhraseHash) if (err != nil) { return "", err } identityKeyHashBytes, err := blake3.GetBlake3HashAsBytes(10, publicIdentityKey[:]) if (err != nil) { return "", err } identityHashPrefix := encoding.EncodeBytesToBase32String(identityKeyHashBytes) return identityHashPrefix, nil }