From 29e75412187b3800318e9eb8545bc869161fa670 Mon Sep 17 00:00:00 2001 From: Irvin Date: Fri, 15 Nov 2024 19:49:27 +0700 Subject: [PATCH] Add ZKLogin SDK Utils. #119 --- .../Code/Sui.ZKLogin/SDK/Utils.cs | 111 ++++++++++++++++-- 1 file changed, 102 insertions(+), 9 deletions(-) diff --git a/Assets/Sui-Unity-SDK/Code/Sui.ZKLogin/SDK/Utils.cs b/Assets/Sui-Unity-SDK/Code/Sui.ZKLogin/SDK/Utils.cs index feddade..77639a5 100644 --- a/Assets/Sui-Unity-SDK/Code/Sui.ZKLogin/SDK/Utils.cs +++ b/Assets/Sui-Unity-SDK/Code/Sui.ZKLogin/SDK/Utils.cs @@ -1,22 +1,115 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - +using System; +using System.Linq; +using System.Numerics; +using Sui.Cryptography.Ed25519; namespace Sui.ZKLogin.SDK { - public class Utils : MonoBehaviour + public static class Utils { - // Start is called before the first frame update - void Start() + private const int MAX_KEY_CLAIM_NAME_LENGTH = 32; + private const int MAX_KEY_CLAIM_VALUE_LENGTH = 115; + private const int MAX_AUD_VALUE_LENGTH = 145; + private const int PACK_WIDTH = 248; + + // Note: This method would depend on your PublicKey implementation + public static string GetExtendedEphemeralPublicKey(PublicKey publicKey) + { + return publicKey.ToSuiPublicKey(); + } + + /// + /// Splits an array into chunks of size chunkSize. If the array is not evenly + /// divisible by chunkSize, the first chunk will be smaller than chunkSize. + /// + public static T[][] ChunkArray(T[] array, int chunkSize) + { + var reversed = array.Reverse().ToArray(); + int chunksCount = (int)Math.Ceiling(array.Length / (double)chunkSize); + var chunks = new T[chunksCount][]; + + for (int i = 0; i < chunksCount; i++) + { + chunks[i] = reversed.Skip(i * chunkSize) + .Take(chunkSize) + .Reverse() + .ToArray(); + } + + Array.Reverse(chunks); + return chunks; + } + + private static BigInteger BytesBEToBigInt(byte[] bytes) { + if (bytes.Length == 0) + return BigInteger.Zero; + string hex = BitConverter.ToString(bytes).Replace("-", ""); + return BigInteger.Parse("0" + hex, System.Globalization.NumberStyles.HexNumber); } - // Update is called once per frame - void Update() + /// + /// Hashes an ASCII string to a field element + /// + public static BigInteger HashASCIIStrToField(string str, int maxSize) { + if (str.Length > maxSize) + { + throw new Exception($"String {str} is longer than {maxSize} chars"); + } + // Pad the string with null characters + // NOTE in the TypeScript implementation they pad it with `zeroes` + var strPadded = str.PadRight(maxSize, '\0') + .Select(c => (byte)c) + .ToArray(); + + int chunkSize = PACK_WIDTH / 8; + var packed = ChunkArray(strPadded, chunkSize) + .Select(chunk => BytesBEToBigInt(chunk)) + .ToArray(); + + return PoseidonHasher.PoseidonHash(packed); + } + + public static BigInteger GenAddressSeed( + string salt, + string name, + string value, + string aud, + int maxNameLength = MAX_KEY_CLAIM_NAME_LENGTH, + int maxValueLength = MAX_KEY_CLAIM_VALUE_LENGTH, + int maxAudLength = MAX_AUD_VALUE_LENGTH) + { + var saltBigInt = BigInteger.Parse(salt); + + return PoseidonHasher.PoseidonHash(new[] + { + HashASCIIStrToField(name, maxNameLength), + HashASCIIStrToField(value, maxValueLength), + HashASCIIStrToField(aud, maxAudLength), + PoseidonHasher.PoseidonHash(new[] { saltBigInt }) + }); + } + + // Overload for when salt is already a BigInteger + public static BigInteger GenAddressSeed( + BigInteger salt, + string name, + string value, + string aud, + int maxNameLength = MAX_KEY_CLAIM_NAME_LENGTH, + int maxValueLength = MAX_KEY_CLAIM_VALUE_LENGTH, + int maxAudLength = MAX_AUD_VALUE_LENGTH) + { + return PoseidonHasher.PoseidonHash(new[] + { + HashASCIIStrToField(name, maxNameLength), + HashASCIIStrToField(value, maxValueLength), + HashASCIIStrToField(aud, maxAudLength), + PoseidonHasher.PoseidonHash(new[] { salt }) + }); } } } \ No newline at end of file