mirror of
https://github.com/selfxyz/self.git
synced 2026-02-19 02:24:25 -05:00
* feat: selfrica circuit and tests * chore: remove unused code * feat: test for ofac,date and olderthan * fix: public signal constant * feat: add contract tests * feat: helper function to gen TEE input * feat: gen circuit inputs with signature * feat: seralized base64 * fix: DateIsLessFullYear componenet * feat: register circuit for selfrica * feat: selfrica disclose circuit and test * fix: common module error * feat: add more test and fix constant * fix: commitment calculation * feat: selfrica contracts * test: selfrica register using unified circuit * feat: register persona and selfrica circuit * feat: selfrica circuit and tests * chore: remove unused code * feat: test for ofac,date and olderthan * fix: public signal constant * feat: add contract tests * feat: helper function to gen TEE input * feat: gen circuit inputs with signature * feat: seralized base64 * fix: DateIsLessFullYear componenet * feat: register circuit for selfrica * feat: selfrica disclose circuit and test * fix: common module error * feat: add more test and fix constant * fix: commitment calculation * feat: selfrica contracts * test: selfrica register using unified circuit * feat: register persona and selfrica circuit * refactor: contract size reduction for IdentityVerificationHubImplV2 export function logic to external libs, reduce compiler runs to 200, update deploy scripts to link new libs * feat: disclose circuit for persona * feat: update persona ofac trees * feat; register circuit for selfper * feat: disclose test for selfper * chore: refactor * chore : remove unused circuits * chore: rename selfper to kyc * chore: update comments * feat: constrain s to be 251 bit * feat: add range check on majority ASCII and comments * feat: range check on neg_r_inv * chore: remove is pk zero constrain * merge dev * feat: add registerPubkey function to Selfrica with GCPJWT Verification * test: add testing for GCPJWT verification on Selfrica * fix: script that calls register_selfrica circuits (ptau:14 -> ptau:15) * fix: get remaining Selfrica tests working with proper import paths * refactor: store pubkeys as string also add some comment code for registerPubkey function * refactor: remove registerPubkeyCommitment function some tests now skipped as awaiting changes to how pubkeys are stored (string instead of uint256) * feat: use hex decoding for the pubkey commitment * test: adjust tests for pubkey being string again * fix: remove old references to registerPubkey * docs: add full natspec for IdentityRegistrySelfricaImplV1 * docs: update files in rest of the repo for Selfrica attestation type * test: fix broken tests * fix: builds and move to kyc from selfrica * fix: constrain r_inv, Rx, s, T * feat: eddsa * feat: add onlyTEE check to registerPubkeyCommitment onlyOwner is able to change onlyTEE * refactor: update gcpRootCAPubkeyHash to be changeable by owner * feat: add events for update functions * style: move functions to be near other similar functions * fix: kyc happy flow * fix: all contract tests passing | fix: timestamp conversion with Date(), migrate to V2 for endToEnd test, scope formatting, fix register aadhaar issue by using block.timestamp instead of Date.now(), fix changed getter function name, enable MockGCPJWTVerifier with updated file paths, add missing LeanIMT import, fix user identifier format * audit: bind key offset-value offset and ensure image_digest only occurs once in the payload * fix: constrain bracket * chore: update comment * audit: hardcode attestation id * audit: make sure R and pubkey are on the curve * audit: ensure pubkey is within bounds * fix: all contract tests passing * feat: change max length to 99 from 74 * audit: don't check sha256 padding * audit: check the last window as well * audit: single occurance for eat_nonce and image_digest * audit: check if the certs are expired * audit: add the timestamp check to the contract * audit: make sure the person is less than 255 years of age * audit fixes * chore: yarn.lock * fix: build fixes * fix: aadhaar timestamp * lint * fix: types * format --------- Co-authored-by: vishal <vishalkoolkarni0045@gmail.com> Co-authored-by: Evi Nova <tranquil_flow@protonmail.com>
532 lines
17 KiB
Go
532 lines
17 KiB
Go
package self
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"math"
|
|
"math/big"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/selfxyz/self/sdk/sdk-go/common"
|
|
"golang.org/x/crypto/ripemd160"
|
|
)
|
|
|
|
// Constants for attestation types
|
|
const (
|
|
Passport AttestationId = 1
|
|
EUCard AttestationId = 2
|
|
Aadhaar AttestationId = 3
|
|
SelfricaIdCard AttestationId = 4
|
|
)
|
|
|
|
// DiscloseIndicesEntry defines the indices for different data fields in the public signals
|
|
type DiscloseIndicesEntry struct {
|
|
RevealedDataPackedIndex int
|
|
ForbiddenCountriesListPackedIndex int
|
|
NullifierIndex int
|
|
AttestationIdIndex int
|
|
MerkleRootIndex int
|
|
CurrentDateIndex int
|
|
NamedobSmtRootIndex int
|
|
NameyobSmtRootIndex int
|
|
ScopeIndex int
|
|
UserIdentifierIndex int
|
|
PassportNoSmtRootIndex int
|
|
}
|
|
|
|
// DiscloseIndices maps attestation IDs to their respective index configurations
|
|
var DiscloseIndices = map[AttestationId]DiscloseIndicesEntry{
|
|
Passport: {
|
|
RevealedDataPackedIndex: 0,
|
|
ForbiddenCountriesListPackedIndex: 3,
|
|
NullifierIndex: 7,
|
|
AttestationIdIndex: 8,
|
|
MerkleRootIndex: 9,
|
|
CurrentDateIndex: 10,
|
|
NamedobSmtRootIndex: 17,
|
|
NameyobSmtRootIndex: 18,
|
|
ScopeIndex: 19,
|
|
UserIdentifierIndex: 20,
|
|
PassportNoSmtRootIndex: 16,
|
|
},
|
|
EUCard: {
|
|
RevealedDataPackedIndex: 0,
|
|
ForbiddenCountriesListPackedIndex: 4,
|
|
NullifierIndex: 8,
|
|
AttestationIdIndex: 9,
|
|
MerkleRootIndex: 10,
|
|
CurrentDateIndex: 11,
|
|
NamedobSmtRootIndex: 17,
|
|
NameyobSmtRootIndex: 18,
|
|
ScopeIndex: 19,
|
|
UserIdentifierIndex: 20,
|
|
PassportNoSmtRootIndex: 99,
|
|
},
|
|
Aadhaar: {
|
|
RevealedDataPackedIndex: 2,
|
|
ForbiddenCountriesListPackedIndex: 6,
|
|
NullifierIndex: 0,
|
|
AttestationIdIndex: 10,
|
|
MerkleRootIndex: 16,
|
|
CurrentDateIndex: 11,
|
|
NamedobSmtRootIndex: 14,
|
|
NameyobSmtRootIndex: 15,
|
|
ScopeIndex: 17,
|
|
UserIdentifierIndex: 18,
|
|
PassportNoSmtRootIndex: 99,
|
|
},
|
|
// Selfrica ID Card - see CircuitConstantsV2.sol for layout documentation
|
|
SelfricaIdCard: {
|
|
RevealedDataPackedIndex: 0,
|
|
ForbiddenCountriesListPackedIndex: 9,
|
|
NullifierIndex: 13,
|
|
AttestationIdIndex: 29,
|
|
MerkleRootIndex: 17,
|
|
CurrentDateIndex: 21,
|
|
NamedobSmtRootIndex: 18,
|
|
NameyobSmtRootIndex: 19,
|
|
ScopeIndex: 16,
|
|
UserIdentifierIndex: 20,
|
|
PassportNoSmtRootIndex: 99,
|
|
},
|
|
}
|
|
|
|
// Field names for revealed data
|
|
const (
|
|
IssuingState string = "issuingState"
|
|
Name string = "name"
|
|
IdNumber string = "idNumber"
|
|
Nationality string = "nationality"
|
|
DateOfBirth string = "dateOfBirth"
|
|
Gender string = "gender"
|
|
ExpiryDate string = "expiryDate"
|
|
OlderThan string = "olderThan"
|
|
Ofac string = "ofac"
|
|
)
|
|
|
|
// RevealedDataIndicesEntry defines the start and end indices for different data fields
|
|
type RevealedDataIndicesEntry struct {
|
|
IssuingStateStart int
|
|
IssuingStateEnd int
|
|
NameStart int
|
|
NameEnd int
|
|
IdNumberStart int
|
|
IdNumberEnd int
|
|
NationalityStart int
|
|
NationalityEnd int
|
|
DateOfBirthStart int
|
|
DateOfBirthEnd int
|
|
GenderStart int
|
|
GenderEnd int
|
|
ExpiryDateStart int
|
|
ExpiryDateEnd int
|
|
OlderThanStart int
|
|
OlderThanEnd int
|
|
OfacStart int
|
|
OfacEnd int
|
|
}
|
|
|
|
// RevealedDataIndices maps attestation IDs to their data field indices
|
|
var RevealedDataIndices = map[AttestationId]RevealedDataIndicesEntry{
|
|
Passport: {
|
|
IssuingStateStart: 2,
|
|
IssuingStateEnd: 4,
|
|
NameStart: 5,
|
|
NameEnd: 43,
|
|
IdNumberStart: 44,
|
|
IdNumberEnd: 52,
|
|
NationalityStart: 54,
|
|
NationalityEnd: 56,
|
|
DateOfBirthStart: 57,
|
|
DateOfBirthEnd: 62,
|
|
GenderStart: 64,
|
|
GenderEnd: 64,
|
|
ExpiryDateStart: 65,
|
|
ExpiryDateEnd: 70,
|
|
OlderThanStart: 88,
|
|
OlderThanEnd: 89,
|
|
OfacStart: 90,
|
|
OfacEnd: 92,
|
|
},
|
|
EUCard: {
|
|
IssuingStateStart: 2,
|
|
IssuingStateEnd: 4,
|
|
NameStart: 60,
|
|
NameEnd: 89,
|
|
IdNumberStart: 5,
|
|
IdNumberEnd: 13,
|
|
NationalityStart: 45,
|
|
NationalityEnd: 47,
|
|
DateOfBirthStart: 30,
|
|
DateOfBirthEnd: 35,
|
|
GenderStart: 37,
|
|
GenderEnd: 37,
|
|
ExpiryDateStart: 38,
|
|
ExpiryDateEnd: 43,
|
|
OlderThanStart: 90,
|
|
OlderThanEnd: 91,
|
|
OfacStart: 92,
|
|
OfacEnd: 93,
|
|
},
|
|
Aadhaar: {
|
|
IssuingStateStart: 81,
|
|
IssuingStateEnd: 111,
|
|
NameStart: 9,
|
|
NameEnd: 70,
|
|
IdNumberStart: 71,
|
|
IdNumberEnd: 74,
|
|
NationalityStart: 999,
|
|
NationalityEnd: 999,
|
|
DateOfBirthStart: 1,
|
|
DateOfBirthEnd: 8,
|
|
GenderStart: 0,
|
|
GenderEnd: 0,
|
|
ExpiryDateStart: 999,
|
|
ExpiryDateEnd: 999,
|
|
OlderThanStart: 118,
|
|
OlderThanEnd: 118,
|
|
OfacStart: 116,
|
|
OfacEnd: 117,
|
|
},
|
|
}
|
|
|
|
// AllIds contains all valid attestation IDs
|
|
var AllIds = map[AttestationId]bool{
|
|
Passport: true,
|
|
EUCard: true,
|
|
Aadhaar: true,
|
|
SelfricaIdCard: true,
|
|
}
|
|
|
|
// BytesCount maps attestation IDs to their respective byte counts
|
|
var BytesCount = map[AttestationId][]int{
|
|
Passport: {31, 31, 31},
|
|
EUCard: {31, 31, 31, 1},
|
|
Aadhaar: {31, 31, 31, 26},
|
|
SelfricaIdCard: {31, 31, 31, 31, 31, 31, 31, 31, 31},
|
|
}
|
|
|
|
// trimU0000 filters out null characters (\u0000) from a slice of strings
|
|
func trimU0000(unpackedReveal []string) []string {
|
|
var result []string
|
|
for _, value := range unpackedReveal {
|
|
if value != "\u0000" {
|
|
result = append(result, value)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// UnpackForbiddenCountriesList unpacks a list of packed forbidden country codes into an array of 3-character country codes.
|
|
//
|
|
// Parameters:
|
|
// - forbiddenCountriesListPacked: A slice of packed strings representing forbidden countries
|
|
//
|
|
// Returns:
|
|
// - A slice of 3-character country codes extracted from the packed input
|
|
func UnpackForbiddenCountriesList(forbiddenCountriesListPacked []string) []string {
|
|
// Unpack the revealed data using the unpackReveal function
|
|
unpacked := common.UnpackReveal(forbiddenCountriesListPacked, "id")
|
|
trimmed := trimU0000(unpacked)
|
|
|
|
var countries []string
|
|
|
|
// Join all trimmed strings to work with characters
|
|
joined := strings.Join(trimmed, "")
|
|
|
|
// Extract 3-character country codes
|
|
for i := 0; i < len(joined); i += 3 {
|
|
if i+3 <= len(joined) {
|
|
countryCode := joined[i : i+3]
|
|
if len(countryCode) == 3 {
|
|
countries = append(countries, countryCode)
|
|
}
|
|
}
|
|
}
|
|
|
|
return countries
|
|
}
|
|
|
|
// CastToUserIdentifier converts a big integer to user identifier string based on the specified type
|
|
func CastToUserIdentifier(bigInt *big.Int, userIdType UserIDType) string {
|
|
switch userIdType {
|
|
case UserIDTypeHex:
|
|
return CastToAddress(bigInt)
|
|
case UserIDTypeUUID:
|
|
return CastToUUID(bigInt)
|
|
default:
|
|
return bigInt.String()
|
|
}
|
|
}
|
|
|
|
// CastToAddress converts big integer to hex address format (0x + 40 hex chars)
|
|
func CastToAddress(bigInt *big.Int) string {
|
|
hexStr := bigInt.Text(16) // Convert to hex without 0x prefix
|
|
// Pad to 40 characters (20 bytes = 40 hex chars)
|
|
if len(hexStr) < 40 {
|
|
hexStr = fmt.Sprintf("%040s", hexStr)
|
|
}
|
|
return "0x" + hexStr
|
|
}
|
|
|
|
// CastToUUID converts big integer to UUID format
|
|
func CastToUUID(bigInt *big.Int) string {
|
|
hexStr := bigInt.Text(16) // Convert to hex without 0x prefix
|
|
// Pad to 32 characters
|
|
if len(hexStr) < 32 {
|
|
hexStr = fmt.Sprintf("%032s", hexStr)
|
|
}
|
|
// Format as UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
return fmt.Sprintf("%s-%s-%s-%s-%s",
|
|
hexStr[0:8], hexStr[8:12], hexStr[12:16], hexStr[16:20], hexStr[20:32])
|
|
}
|
|
|
|
// CalculateUserIdentifierHash generates a deterministic user identifier hash from the provided context data.
|
|
//
|
|
// The function computes a SHA-256 hash of the input buffer, then applies a RIPEMD-160 hash to the result.
|
|
// The final output is a hexadecimal string, left-padded with zeros to 40 characters and prefixed with "0x".
|
|
//
|
|
// Parameters:
|
|
// - userContextData: The byte slice containing user context data to hash
|
|
//
|
|
// Returns:
|
|
// - A 40-character hexadecimal user identifier string prefixed with "0x"
|
|
func CalculateUserIdentifierHash(userContextData []byte) string {
|
|
// Compute SHA-256 hash
|
|
sha256Hasher := sha256.New()
|
|
sha256Hasher.Write(userContextData)
|
|
sha256Hash := sha256Hasher.Sum(nil)
|
|
|
|
// Compute RIPEMD-160 hash of the SHA-256 hash
|
|
ripemdHasher := ripemd160.New()
|
|
ripemdHasher.Write(sha256Hash)
|
|
ripemdHash := ripemdHasher.Sum(nil)
|
|
|
|
hexString := fmt.Sprintf("%x", ripemdHash)
|
|
|
|
// Pad with leading zeros to ensure 40 hex chars
|
|
if len(hexString) < 40 {
|
|
hexString = fmt.Sprintf("%040s", hexString)
|
|
}
|
|
|
|
return "0x" + hexString
|
|
}
|
|
|
|
// PublicSignals represents an array of numeric strings, equivalent to snarkjs PublicSignals
|
|
type PublicSignals []string
|
|
|
|
// GetRevealedDataPublicSignalsLength returns the number of public signals containing
|
|
// revealed data for the specified attestation ID.
|
|
//
|
|
// Returns an error if the attestation ID is not supported.
|
|
//
|
|
// Parameters:
|
|
// - attestationId: The attestation ID for which to determine the number of revealed data public signals
|
|
//
|
|
// Returns:
|
|
// - The number of public signals corresponding to revealed data
|
|
// - An error if the attestation ID is invalid
|
|
func GetRevealedDataPublicSignalsLength(attestationId AttestationId) (int, error) {
|
|
switch attestationId {
|
|
case Passport:
|
|
return int(93 / 31), nil
|
|
case EUCard:
|
|
return int(math.Ceil(94.0 / 31.0)), nil
|
|
case Aadhaar:
|
|
return int(math.Ceil(119.0 / 31.0)), nil
|
|
case SelfricaIdCard:
|
|
return 9, nil
|
|
default:
|
|
return 0, fmt.Errorf("invalid attestation ID: %d", attestationId)
|
|
}
|
|
}
|
|
|
|
// GetRevealedDataBytes extracts and returns the revealed data bytes from the public signals
|
|
// for a given attestation ID.
|
|
//
|
|
// Iterates over the relevant public signals, unpacks each into its constituent bytes according
|
|
// to the attestation's byte structure, and accumulates all revealed bytes into a single array.
|
|
//
|
|
// Parameters:
|
|
// - attestationId: The attestation ID specifying the format of revealed data
|
|
// - publicSignals: The array of public signals containing packed revealed data
|
|
//
|
|
// Returns:
|
|
// - An array of bytes representing the revealed data for the specified attestation
|
|
// - An error if the attestation ID is invalid or if there's an issue processing the signals
|
|
func GetRevealedDataBytes(attestationId AttestationId, publicSignals PublicSignals) ([]int, error) {
|
|
// Get the length of revealed data public signals
|
|
length, err := GetRevealedDataPublicSignalsLength(attestationId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Get the disclose indices for this attestation ID
|
|
discloseIndices, exists := DiscloseIndices[attestationId]
|
|
if !exists {
|
|
return nil, fmt.Errorf("disclose indices not found for attestation ID: %d", attestationId)
|
|
}
|
|
|
|
// Get the bytes count for this attestation ID
|
|
bytesCount, exists := BytesCount[attestationId]
|
|
if !exists {
|
|
return nil, fmt.Errorf("bytes count not found for attestation ID: %d", attestationId)
|
|
}
|
|
|
|
var bytes []int
|
|
|
|
for i := 0; i < length; i++ {
|
|
signalIndex := discloseIndices.RevealedDataPackedIndex + i
|
|
|
|
publicSignal := new(big.Int)
|
|
publicSignal, success := publicSignal.SetString(publicSignals[signalIndex], 10)
|
|
if !success {
|
|
return nil, fmt.Errorf("failed to parse public signal at index %d: %s", signalIndex, publicSignals[signalIndex])
|
|
}
|
|
|
|
// Extract bytes from the public signal
|
|
for j := 0; j < bytesCount[i]; j++ {
|
|
// Extract the least significant byte (equivalent to publicSignal & 0xffn)
|
|
byteVal := new(big.Int)
|
|
byteVal.And(publicSignal, big.NewInt(0xff))
|
|
bytes = append(bytes, int(byteVal.Int64()))
|
|
|
|
publicSignal.Rsh(publicSignal, 8)
|
|
}
|
|
}
|
|
|
|
return bytes, nil
|
|
}
|
|
|
|
// FormatRevealedDataPacked extracts and formats revealed data from public signals
|
|
func FormatRevealedDataPacked(attestationID AttestationId, publicSignals PublicSignals) (GenericDiscloseOutput, error) {
|
|
revealedDataPacked, err := GetRevealedDataBytes(attestationID, publicSignals)
|
|
|
|
if err != nil {
|
|
return GenericDiscloseOutput{}, err
|
|
}
|
|
|
|
discloseIndices, exists := DiscloseIndices[attestationID]
|
|
if !exists {
|
|
return GenericDiscloseOutput{}, fmt.Errorf("disclose indices not found for attestation ID: %d", attestationID)
|
|
}
|
|
|
|
// Convert revealedDataPacked ([]int) to byte array for string operations
|
|
revealedDataPackedBytes := make([]byte, len(revealedDataPacked))
|
|
for i, b := range revealedDataPacked {
|
|
revealedDataPackedBytes[i] = byte(b)
|
|
}
|
|
|
|
// Get revealed data indices for this attestation ID
|
|
revealedDataIndices, exists := RevealedDataIndices[attestationID]
|
|
if !exists {
|
|
return GenericDiscloseOutput{}, fmt.Errorf("revealed data indices not found for attestation ID: %d", attestationID)
|
|
}
|
|
|
|
// Extract nullifier
|
|
nullifier := publicSignals[discloseIndices.NullifierIndex]
|
|
|
|
// Extract forbidden countries list packed
|
|
fcStartIndex := discloseIndices.ForbiddenCountriesListPackedIndex
|
|
forbiddenCountriesListPacked := publicSignals[fcStartIndex : fcStartIndex+4]
|
|
|
|
// Extract issuing state
|
|
issuingState := string(revealedDataPackedBytes[revealedDataIndices.IssuingStateStart : revealedDataIndices.IssuingStateEnd+1])
|
|
|
|
// Extract name with cleaning (equivalent to regex replacements and trim)
|
|
nameRaw := string(revealedDataPackedBytes[revealedDataIndices.NameStart : revealedDataIndices.NameEnd+1])
|
|
name := cleanName(nameRaw)
|
|
|
|
// Extract ID number
|
|
idNumber := string(revealedDataPackedBytes[revealedDataIndices.IdNumberStart : revealedDataIndices.IdNumberEnd+1])
|
|
|
|
// Extract nationality
|
|
nationality := ""
|
|
if attestationID == Aadhaar {
|
|
nationality = "IND"
|
|
} else {
|
|
nationality = string(revealedDataPackedBytes[revealedDataIndices.NationalityStart : revealedDataIndices.NationalityEnd+1])
|
|
}
|
|
|
|
// Extract date of birth
|
|
var dateOfBirth string
|
|
if attestationID == Aadhaar {
|
|
dobBytes := revealedDataPackedBytes[revealedDataIndices.DateOfBirthStart : revealedDataIndices.DateOfBirthEnd+1]
|
|
var dobStrings []string
|
|
for _, b := range dobBytes {
|
|
dobStrings = append(dobStrings, fmt.Sprintf("%d", int(b)))
|
|
}
|
|
dateOfBirth = strings.Join(dobStrings, "")
|
|
} else {
|
|
dateOfBirth = string(revealedDataPackedBytes[revealedDataIndices.DateOfBirthStart : revealedDataIndices.DateOfBirthEnd+1])
|
|
}
|
|
|
|
// Extract gender
|
|
gender := string(revealedDataPackedBytes[revealedDataIndices.GenderStart : revealedDataIndices.GenderEnd+1])
|
|
|
|
// Extract expiry date
|
|
var expiryDate string
|
|
if attestationID == Aadhaar {
|
|
expiryDate = "UNAVAILABLE"
|
|
} else {
|
|
expiryDate = string(revealedDataPackedBytes[revealedDataIndices.ExpiryDateStart : revealedDataIndices.ExpiryDateEnd+1])
|
|
}
|
|
|
|
// Extract minimum age (olderThan)
|
|
var minimumAge string
|
|
if attestationID == Aadhaar {
|
|
firstByte := revealedDataPackedBytes[revealedDataIndices.OlderThanStart]
|
|
minimumAge = fmt.Sprintf("%02d", int(firstByte))
|
|
} else {
|
|
minimumAge = string(revealedDataPackedBytes[revealedDataIndices.OlderThanStart : revealedDataIndices.OlderThanEnd+1])
|
|
}
|
|
|
|
// Extract OFAC data and convert to boolean array
|
|
ofacBytes := revealedDataPackedBytes[revealedDataIndices.OfacStart : revealedDataIndices.OfacEnd+1]
|
|
ofac := make([]bool, len(ofacBytes))
|
|
for i, b := range ofacBytes {
|
|
ofac[i] = !(b != 0)
|
|
}
|
|
|
|
if len(ofac) < 3 {
|
|
ofac = append([]bool{false}, ofac...)
|
|
}
|
|
|
|
// Return the structured output
|
|
return GenericDiscloseOutput{
|
|
Nullifier: nullifier,
|
|
ForbiddenCountriesListPacked: forbiddenCountriesListPacked,
|
|
IssuingState: removeNullBytes(issuingState),
|
|
Name: removeNullBytes(name),
|
|
IdNumber: idNumber,
|
|
Nationality: nationality,
|
|
DateOfBirth: dateOfBirth,
|
|
Gender: gender,
|
|
ExpiryDate: expiryDate,
|
|
MinimumAge: minimumAge,
|
|
Ofac: ofac,
|
|
}, nil
|
|
}
|
|
|
|
// removeNullBytes removes null bytes (\x00) from a string
|
|
func removeNullBytes(str string) string {
|
|
return strings.ReplaceAll(str, "\x00", "")
|
|
}
|
|
|
|
// cleanName cleans the name string equivalent to the TypeScript regex operations
|
|
// .replace(/([A-Z])<+([A-Z])/g, '$1 $2').replace(/</g, ").trim()
|
|
func cleanName(nameRaw string) string {
|
|
// Replace pattern ([A-Z])<+([A-Z]) with '$1 $2'
|
|
re1 := regexp.MustCompile(`([A-Z])<+([A-Z])`)
|
|
name := re1.ReplaceAllString(nameRaw, "$1 $2")
|
|
|
|
// Replace all remaining '<' characters
|
|
name = strings.ReplaceAll(name, "<", "")
|
|
|
|
// Trim whitespace
|
|
name = strings.TrimSpace(name)
|
|
|
|
return name
|
|
}
|