Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
430a6fd
feat: add aadhaar support to the ts sdk
Nesopie Sep 11, 2025
08ccf79
feat: aadhaar support to go sdk
Vishalkulkarni45 Sep 12, 2025
6e7c29a
chore: refactor
Vishalkulkarni45 Sep 15, 2025
b1dc0ba
move clearPassportData, markCurrentDocumentAsRegistered, reStorePassp…
shazarre Sep 11, 2025
761fb2f
Move self app store to mobile sdk (#1040)
aaronmgdr Sep 11, 2025
b335e66
chore(mobile-sdk-alpha): remove unused tslib dependency (#1053)
aaronmgdr Sep 11, 2025
e1a5825
remove unused imports (#1055)
aaronmgdr Sep 11, 2025
846956d
fix: sha256 signed attr tests (#1058)
Nesopie Sep 12, 2025
712e780
fix mock screen launch (#1059)
transphorm Sep 12, 2025
74605c0
Hotfix: Belgium ID cards (#1061)
seshanthS Sep 12, 2025
f7e9c40
fix: OFAC trees not found (#1060)
transphorm Sep 12, 2025
9ff1ae9
[SELF-723] feat: add structured NFC and Proof logging (#1048)
transphorm Sep 13, 2025
049874b
skip on dev (#1063)
transphorm Sep 13, 2025
08264e5
don't get fancy just disable (#1064)
transphorm Sep 13, 2025
dd681ac
saw it building so gonna try (#1065)
transphorm Sep 13, 2025
b2c89e0
chore: bump v2.6.5 rd2 (#1067)
transphorm Sep 13, 2025
77626c4
chore: update tooling dependencies (#1069)
transphorm Sep 14, 2025
9ccce41
chore: minor fixes across monorepo (#1068)
transphorm Sep 14, 2025
4e97eec
fix yarn build; add workflow ci (#1075)
transphorm Sep 16, 2025
a2c6969
feat: add functions for disclosing aadhaar attributes (#1033)
Nesopie Sep 16, 2025
55a0edc
chore: update monorepo artifacts (#1079)
transphorm Sep 16, 2025
57a40a8
chore: update hub contract address
Nesopie Sep 17, 2025
43a0b81
Merge branch 'dev' into feat/aadhaar-sdk
Nesopie Sep 17, 2025
9a67634
format
Nesopie Sep 17, 2025
09f1b90
fix: add aadhaar in AllIds
Vishalkulkarni45 Sep 17, 2025
0428ac4
chore: bump to v1.1.0-beta
Nesopie Sep 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sdk/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@selfxyz/core",
"version": "1.0.8",
"version": "1.1.0-beta.0",
"repository": {
"type": "git",
"url": "https://github.com/selfxyz/self"
Expand Down
50 changes: 35 additions & 15 deletions sdk/core/src/SelfBackendVerifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const CELO_MAINNET_RPC_URL = 'https://forno.celo.org';
const CELO_TESTNET_RPC_URL = 'https://alfajores-forno.celo-testnet.org';

const IDENTITY_VERIFICATION_HUB_ADDRESS = '0xe57F4773bd9c9d8b6Cd70431117d353298B9f5BF';
const IDENTITY_VERIFICATION_HUB_ADDRESS_STAGING = '0x68c931C9a534D37aa78094877F46fE46a49F1A51';
const IDENTITY_VERIFICATION_HUB_ADDRESS_STAGING = '0x16ECBA51e18a4a7e61fdC417f0d47AFEeDfbed74';

export class SelfBackendVerifier {
protected scope: string;
Expand Down Expand Up @@ -217,20 +217,40 @@ export class SelfBackendVerifier {
});
}

const circuitTimestampYy = [
2,
0,
publicSignals[discloseIndices[attestationId].currentDateIndex],
publicSignals[discloseIndices[attestationId].currentDateIndex + 1],
];
const circuitTimestampMm = [
publicSignals[discloseIndices[attestationId].currentDateIndex + 2],
publicSignals[discloseIndices[attestationId].currentDateIndex + 3],
];
const circuitTimestampDd = [
publicSignals[discloseIndices[attestationId].currentDateIndex + 4],
publicSignals[discloseIndices[attestationId].currentDateIndex + 5],
];
let circuitTimestampYy: number[];
let circuitTimestampMm: number[];
let circuitTimestampDd: number[];
if (attestationId === 3) {
circuitTimestampYy = String(publicSignals[discloseIndices[attestationId].currentDateIndex])
.split('')
.map(Number);
circuitTimestampMm = String(
publicSignals[discloseIndices[attestationId].currentDateIndex + 1]
)
.split('')
.map(Number);
circuitTimestampDd = String(
publicSignals[discloseIndices[attestationId].currentDateIndex + 2]
)
.split('')
.map(Number);
} else {
circuitTimestampYy = [
2,
0,
+publicSignals[discloseIndices[attestationId].currentDateIndex],
+publicSignals[discloseIndices[attestationId].currentDateIndex + 1],
];
circuitTimestampMm = [
+publicSignals[discloseIndices[attestationId].currentDateIndex + 2],
+publicSignals[discloseIndices[attestationId].currentDateIndex + 3],
];
circuitTimestampDd = [
+publicSignals[discloseIndices[attestationId].currentDateIndex + 4],
+publicSignals[discloseIndices[attestationId].currentDateIndex + 5],
];
}

const circuitTimestamp = new Date(
Number(circuitTimestampYy.join('')),
Number(circuitTimestampMm.join('')) - 1,
Expand Down
33 changes: 33 additions & 0 deletions sdk/core/src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ export const discloseIndices = {
userIdentifierIndex: 20,
passportNoSmtRootIndex: 99,
},
3: {
revealedDataPackedIndex: 2,
forbiddenCountriesListPackedIndex: 6,
nullifierIndex: 0,
attestationIdIndex: 10,
merkleRootIndex: 16,
currentDateIndex: 11,
namedobSmtRootIndex: 14,
nameyobSmtRootIndex: 15,
scopeIndex: 17,
userIdentifierIndex: 18,
passportNoSmtRootIndex: 99,
},
} as const;

type RevealedDataFields =
Expand Down Expand Up @@ -84,6 +97,26 @@ export const revealedDataIndices: Record<
ofacStart: 92,
ofacEnd: 93,
},
3: {
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,
},
} as const;

const allIdEntries = Object.keys(discloseIndices).map(
Expand Down
104 changes: 77 additions & 27 deletions sdk/core/src/utils/id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ import { discloseIndices, revealedDataIndices } from './constants.js';
import { AttestationId, GenericDiscloseOutput } from 'src/types/types.js';
import { getRevealedDataBytes } from './proof.js';

/**
* Removes null bytes (\x00) from a string
* @param str - The string to clean
* @returns The string with null bytes removed
*/
export const removeNullBytes = (str: string): string => {
return str.replace(/\x00/g, '');
};

export const formatRevealedDataPacked = (
attestationId: AttestationId,
publicSignals: string[]
Expand All @@ -12,7 +21,7 @@ export const formatRevealedDataPacked = (
const nullifier = publicSignals[discloseIndices[attestationId].nullifierIndex];
const forbiddenCountriesListPacked = publicSignals.slice(
discloseIndices[attestationId].forbiddenCountriesListPackedIndex,
discloseIndices[attestationId].forbiddenCountriesListPackedIndex + 3
discloseIndices[attestationId].forbiddenCountriesListPackedIndex + 4
);
const issuingState = revealedDataPackedString
.subarray(
Expand All @@ -35,48 +44,89 @@ export const formatRevealedDataPacked = (
revealedDataIndices[attestationId].idNumberEnd + 1
)
.toString('utf-8');
const nationality = revealedDataPackedString
.subarray(
revealedDataIndices[attestationId].nationalityStart,
revealedDataIndices[attestationId].nationalityEnd + 1
)
.toString('utf-8');
const dateOfBirth = revealedDataPackedString
.subarray(
revealedDataIndices[attestationId].dateOfBirthStart,
revealedDataIndices[attestationId].dateOfBirthEnd + 1

let nationality: string;
if (attestationId === 3) {
nationality = 'IND';
} else {
nationality = revealedDataPackedString
.subarray(
revealedDataIndices[attestationId].nationalityStart,
revealedDataIndices[attestationId].nationalityEnd + 1
)
.toString('utf-8');
}
let dateOfBirth: string;
if (attestationId === 3) {
dateOfBirth = new Array(
revealedDataPackedString.subarray(
revealedDataIndices[attestationId].dateOfBirthStart,
revealedDataIndices[attestationId].dateOfBirthEnd + 1
)
)
.toString('utf-8');
.map(Number)
.map(String)
.join('');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Aadhaar Date Parsing Array Error

The dateOfBirth processing for Aadhaar attestations uses new Array() with a Uint8Array subarray. This creates an array containing the subarray as a single element, rather than an array of its individual byte values, leading to incorrect date of birth formatting.

Fix in Cursor Fix in Web

} else {
Comment on lines +60 to +70
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

❓ Verification inconclusive

Aadhaar DOB parsing is broken (new Array(...) over a TypedArray).

new Array(typedArray) creates a 1‑element array, yielding "NaN" DOB. Use Array.from over the subarray.

Apply:

-  dateOfBirth = new Array(
-    revealedDataPackedString.subarray(
-      revealedDataIndices[attestationId].dateOfBirthStart,
-      revealedDataIndices[attestationId].dateOfBirthEnd + 1
-    )
-  )
-    .map(Number)
-    .map(String)
-    .join('');
+  {
+    const dobBytes = revealedDataPackedString.subarray(
+      revealedDataIndices[attestationId].dateOfBirthStart,
+      revealedDataIndices[attestationId].dateOfBirthEnd + 1
+    );
+    dateOfBirth = Array.from(dobBytes, (b) => String(b)).join('');
+  }

If bytes are ASCII digits, switch mapper to String.fromCharCode(b). Please confirm which encoding the circuit emits for Aadhaar DOB.


Critical: Fix Aadhaar DOB parsing — convert TypedArray with Array.from

File: sdk/core/src/utils/id.ts Lines: 60-70

new Array(typedArray) creates a 1-element array (producing "NaN" DOB). Convert the subarray to a real array/string with Array.from or spread. Apply the diff below.

-  dateOfBirth = new Array(
-    revealedDataPackedString.subarray(
-      revealedDataIndices[attestationId].dateOfBirthStart,
-      revealedDataIndices[attestationId].dateOfBirthEnd + 1
-    )
-  )
-    .map(Number)
-    .map(String)
-    .join('');
+  {
+    const dobBytes = revealedDataPackedString.subarray(
+      revealedDataIndices[attestationId].dateOfBirthStart,
+      revealedDataIndices[attestationId].dateOfBirthEnd + 1
+    );
+    dateOfBirth = Array.from(dobBytes, (b) => String(b)).join('');
+  }

If bytes are ASCII digits, use String.fromCharCode(b) in the mapper. Confirm which encoding the circuit emits for Aadhaar DOB.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (attestationId === 3) {
dateOfBirth = new Array(
revealedDataPackedString.subarray(
revealedDataIndices[attestationId].dateOfBirthStart,
revealedDataIndices[attestationId].dateOfBirthEnd + 1
)
)
.toString('utf-8');
.map(Number)
.map(String)
.join('');
} else {
if (attestationId === 3) {
{
const dobBytes = revealedDataPackedString.subarray(
revealedDataIndices[attestationId].dateOfBirthStart,
revealedDataIndices[attestationId].dateOfBirthEnd + 1
);
dateOfBirth = Array.from(dobBytes, (b) => String(b)).join('');
}
} else {
🤖 Prompt for AI Agents
In sdk/core/src/utils/id.ts around lines 60-70, the current code uses new
Array(typedArray) which creates a single-element array and yields "NaN" DOB;
replace that by converting the TypedArray subarray to a real array with
Array.from(...) or [...subarray] before mapping. If the circuit emits raw byte
codes for ASCII digits, map each byte with String.fromCharCode(b) and join to
produce the DOB string (or if it emits numeric digit values, use
Array.from(...).map(Number).map(String).join('')). Ensure you operate on the
subarray result directly and assign the joined string to dateOfBirth.

dateOfBirth = revealedDataPackedString
.subarray(
revealedDataIndices[attestationId].dateOfBirthStart,
revealedDataIndices[attestationId].dateOfBirthEnd + 1
)
.toString('utf-8');
}
const gender = revealedDataPackedString
.subarray(
revealedDataIndices[attestationId].genderStart,
revealedDataIndices[attestationId].genderEnd + 1
)
.toString('utf-8');
const expiryDate = revealedDataPackedString
.subarray(
revealedDataIndices[attestationId].expiryDateStart,
revealedDataIndices[attestationId].expiryDateEnd + 1
)
.toString('utf-8');
const olderThan = Buffer.from(
revealedDataPackedString.subarray(
revealedDataIndices[attestationId].olderThanStart,
revealedDataIndices[attestationId].olderThanEnd + 1
)
).toString('utf-8');
let expiryDate: string;
if (attestationId === 3) {
expiryDate = 'UNAVAILABLE';
} else {
expiryDate = revealedDataPackedString
.subarray(
revealedDataIndices[attestationId].expiryDateStart,
revealedDataIndices[attestationId].expiryDateEnd + 1
)
.toString('utf-8');
}
let olderThan: string;
if (attestationId === 3) {
olderThan = revealedDataPackedString
.subarray(
revealedDataIndices[attestationId].olderThanStart,
revealedDataIndices[attestationId].olderThanEnd + 1
)
.toString('utf-8');
} else {
olderThan = revealedDataPackedString
.subarray(
revealedDataIndices[attestationId].olderThanStart,
revealedDataIndices[attestationId].olderThanEnd + 1
)[0]
.toString()
.padStart(2, '0');
}
const ofac = Array.from(
revealedDataPackedString.subarray(
revealedDataIndices[attestationId].ofacStart,
revealedDataIndices[attestationId].ofacEnd + 1
)
).map(Boolean);
)
.map(Boolean)
.map((x) => !x);

if (ofac.length < 3) {
ofac.unshift(false);
}

return {
nullifier: nullifier.toString(),
forbiddenCountriesListPacked: forbiddenCountriesListPacked,
issuingState: issuingState,
name: name,
issuingState: removeNullBytes(issuingState),
name: removeNullBytes(name),
idNumber: idNumber,
nationality: nationality,
dateOfBirth: dateOfBirth,
Expand Down
3 changes: 3 additions & 0 deletions sdk/core/src/utils/proof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export function getRevealedDataPublicSignalsLength(attestationId: AttestationId)
return 93 / 31;
case 2:
return Math.ceil(94 / 31);
case 3:
return Math.ceil(119 / 31);
default:
throw new ProofError(`Invalid attestation ID: ${attestationId}`);
}
Expand All @@ -25,6 +27,7 @@ export function getRevealedDataPublicSignalsLength(attestationId: AttestationId)
export const bytesCount: Record<AttestationId, number[]> = {
1: [31, 31, 31],
2: [31, 31, 31, 1],
3: [31, 31, 31, 26],
};

/**
Expand Down
4 changes: 2 additions & 2 deletions sdk/sdk-go/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ type VcAndDiscloseProof struct {

// VerificationConfig represents the configuration for verification
type VerificationConfig struct {
MinimumAge int `json:"minimumAge,omitempty"`
MinimumAge int `json:"minimumAge,omitempty"`
ExcludedCountries []common.Country3LetterCode `json:"excludedCountries,omitempty"`
Ofac bool `json:"ofac,omitempty"`
Ofac bool `json:"ofac,omitempty"`
}

// IsValidDetails contains the validation results
Expand Down
Loading
Loading