205 lines
6.6 KiB
Go
205 lines
6.6 KiB
Go
|
|
||
|
// 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
|
||
|
}
|
||
|
|
||
|
|
||
|
|