seekia/internal/profiles/createProfiles/createProfiles.go

205 lines
6.6 KiB
Go
Raw Normal View History

// createProfiles provides a function to create user profiles
package createProfiles
import "seekia/internal/appValues"
import "seekia/internal/cryptography/blake3"
import "seekia/internal/cryptography/edwardsKeys"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/profiles/profileFormat"
import "seekia/internal/profiles/readProfiles"
import messagepack "github.com/vmihailenco/msgpack/v5"
import "strings"
import "time"
import "errors"
func CreateProfile(identityPublicKey [32]byte, identityPrivateKey [64]byte, inputProfileMap map[string]string)([]byte, error){
networkTypeString, exists := inputProfileMap["NetworkType"]
if (exists == false){
return nil, errors.New("CreateProfile called with profileMap missing NetworkType")
}
networkType, err := helpers.ConvertNetworkTypeStringToByte(networkTypeString)
if (err != nil){
return nil, errors.New("CreateProfile called with profileMap containing invalid NetworkType: " + networkTypeString)
}
profileType, exists := inputProfileMap["ProfileType"]
if (exists == false) {
return nil, errors.New("CreateProfile called with profile map missing profile type.")
}
if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){
return nil, errors.New("CreateProfile called with profile map containing invalid profileType: " + profileType)
}
networkTypeEncoded, err := encoding.EncodeMessagePackBytes(networkType)
if (err != nil){ return nil, err }
profileTypeEncoded, err := encoding.EncodeMessagePackBytes(profileType)
if (err != nil) { return nil, err }
profileVersion, err := appValues.GetProfileVersion(profileType)
if (err != nil) { return nil, err }
profileVersionEncoded, err := encoding.EncodeMessagePackBytes(profileVersion)
if (err != nil) { return nil, err }
identityPublicKeyEncoded, err := encoding.EncodeMessagePackBytes(identityPublicKey)
if (err != nil) { return nil, err }
broadcastTime := time.Now().Unix()
broadcastTimeEncoded, err := encoding.EncodeMessagePackBytes(broadcastTime)
if (err != nil) { return nil, err }
profileContentMap := map[int]messagepack.RawMessage{
51: networkTypeEncoded,
1: profileVersionEncoded,
2: identityPublicKeyEncoded,
3: profileTypeEncoded,
4: broadcastTimeEncoded,
}
getProfileIsDisabledBool := func()bool{
iAmDisabled, exists := inputProfileMap["Disabled"]
if (exists == true && iAmDisabled == "Yes"){
return true
}
return false
}
profileIsDisabled := getProfileIsDisabledBool()
if (profileIsDisabled == true){
trueEncoded, err := encoding.EncodeMessagePackBytes(true)
if (err != nil) { return nil, err }
profileContentMap[5] = trueEncoded
}
if (profileIsDisabled == false){
for attributeName, attributeValue := range inputProfileMap{
if (attributeName == ""){
return nil, errors.New("CreateProfile called with profileMap containing empty key.")
}
if (attributeValue == ""){
return nil, errors.New("CreateProfile called with profileMap containing empty value.")
}
if (attributeName == "BroadcastTime" || attributeName == "ProfileVersion"){
// This function should be called with profile maps which do not contain these attributes.
return nil, errors.New("CreateProfile called with profileMap containing unwanted attribute: " + attributeName)
}
if (attributeName == "NetworkType" || attributeName == "ProfileType" || attributeName == "Disabled"){
// These are attributes we already added to the profileContentMap
continue
}
getAttributeEncodedBytes := func()(messagepack.RawMessage, error){
if (attributeName == "NaclKey" || attributeName == "KyberKey"){
// We encode the raw bytes of the keys to save space
keyBytes, err := encoding.DecodeBase64StringToBytes(attributeValue)
if (err != nil){
return nil, errors.New("CreateProfile called with profileMap containing invalid " + attributeName + ": " + attributeValue)
}
keyBytesEncoded, err := encoding.EncodeMessagePackBytes(keyBytes)
if (err != nil){ return nil, err }
return keyBytesEncoded, nil
}
if (attributeName == "Photos"){
// We encode the raw bytes a list of []byte
base64PhotosList := strings.Split(attributeValue, "+")
rawPhotoBytesList := make([][]byte, 0, len(base64PhotosList))
for _, base64Photo := range base64PhotosList{
base64Bytes, err := encoding.DecodeBase64StringToBytes(base64Photo)
if (err != nil) {
return nil, errors.New("CreateProfile called with profile containing invalid photos attribute: Item not Base64: " + base64Photo)
}
rawPhotoBytesList = append(rawPhotoBytesList, base64Bytes)
}
rawPhotoBytesListEncoded, err := encoding.EncodeMessagePackBytes(rawPhotoBytesList)
if (err != nil){ return nil, err }
return rawPhotoBytesListEncoded, nil
}
//TODO: Add more attributes
// We can encode all numbers and hex-encoded strings using messagepack to reduce size
// //TODO: Encode base pairs using a number
// There are 36 total base pair possibilities
// This will reduce the base pair encoded size by 1/3
// This will be significant once each profile has thousands of base pairs
// We encode the attribute value as a string
attributeEncoded, err := encoding.EncodeMessagePackBytes(attributeValue)
if (err != nil){ return nil, err }
return attributeEncoded, nil
}
attributeEncoded, err := getAttributeEncodedBytes()
if (err != nil) { return nil, err }
attributeIdentifier, err := profileFormat.GetAttributeIdentifierFromAttributeName(attributeName)
if (err != nil){
return nil, errors.New("CreateProfile failed: " + err.Error())
}
profileContentMap[attributeIdentifier] = attributeEncoded
}
}
profileContentMessagepack, err := encoding.EncodeMessagePackBytes(profileContentMap)
if (err != nil) {
return nil, errors.New("CreateProfile failed: " + err.Error())
}
contentHash, err := blake3.Get32ByteBlake3Hash(profileContentMessagepack)
if (err != nil) { return nil, err }
profileSignature := edwardsKeys.CreateSignature(identityPrivateKey, contentHash)
signatureEncoded, err := encoding.EncodeMessagePackBytes(profileSignature)
if (err != nil) { return nil, err }
profileSlice := []messagepack.RawMessage{signatureEncoded, profileContentMessagepack}
profileBytes, err := encoding.EncodeMessagePackBytes(profileSlice)
if (err != nil) { return nil, err }
profileIsValid, err := readProfiles.VerifyProfile(profileBytes)
if (err != nil){ return nil, err }
if (profileIsValid == false){
return nil, errors.New("CreateProfile failed: Invalid result profile.")
}
return profileBytes, nil
}