diff --git a/app/src/hooks/useMockDataForm.ts b/app/src/hooks/useMockDataForm.ts
new file mode 100644
index 000000000..6c85be7a4
--- /dev/null
+++ b/app/src/hooks/useMockDataForm.ts
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
+// SPDX-License-Identifier: BUSL-1.1
+// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
+
+import { useState } from 'react';
+
+export const useMockDataForm = () => {
+ const [age, setAge] = useState(21);
+ const [expiryYears, setExpiryYears] = useState(5);
+ const [selectedCountry, setSelectedCountry] = useState('USA');
+ const [selectedAlgorithm, setSelectedAlgorithm] = useState(
+ 'sha256 rsa 65537 2048',
+ );
+ const [selectedDocumentType, setSelectedDocumentType] = useState<
+ 'mock_passport' | 'mock_id_card'
+ >('mock_passport');
+ const [isInOfacList, setIsInOfacList] = useState(true);
+
+ const resetFormValues = () => {
+ setAge(21);
+ setExpiryYears(5);
+ setIsInOfacList(true);
+ setSelectedDocumentType('mock_passport');
+ setSelectedAlgorithm('sha256 rsa 65537 2048');
+ setSelectedCountry('USA');
+ };
+
+ const handleCountrySelect = (countryCode: string) => {
+ setSelectedCountry(countryCode);
+ };
+
+ const handleAlgorithmSelect = (algorithm: string) => {
+ setSelectedAlgorithm(algorithm);
+ };
+
+ const handleDocumentTypeSelect = (
+ documentType: 'mock_passport' | 'mock_id_card',
+ ) => {
+ setSelectedDocumentType(documentType);
+ };
+
+ return {
+ age,
+ setAge,
+ expiryYears,
+ setExpiryYears,
+ selectedCountry,
+ handleCountrySelect,
+ selectedAlgorithm,
+ handleAlgorithmSelect,
+ selectedDocumentType,
+ handleDocumentTypeSelect,
+ isInOfacList,
+ setIsInOfacList,
+ resetFormValues,
+ };
+};
+
+export default useMockDataForm;
diff --git a/app/src/navigation/devTools.ts b/app/src/navigation/devTools.ts
index a7606ff55..6d6b447e6 100644
--- a/app/src/navigation/devTools.ts
+++ b/app/src/navigation/devTools.ts
@@ -27,7 +27,7 @@ const devScreens = {
CreateMock: {
screen: MockDataScreen,
options: {
- title: 'Mock Passport',
+ title: 'Mock Document',
headerStyle: {
backgroundColor: black,
},
diff --git a/app/src/screens/dev/MockDataScreen.tsx b/app/src/screens/dev/MockDataScreen.tsx
index e3569d4c6..b8219f370 100644
--- a/app/src/screens/dev/MockDataScreen.tsx
+++ b/app/src/screens/dev/MockDataScreen.tsx
@@ -23,19 +23,17 @@ import { useNavigation } from '@react-navigation/native';
import { ChevronDown, Minus, Plus, X } from '@tamagui/lucide-icons';
import { countryCodes } from '@selfxyz/common/constants';
-import type { IdDocInput } from '@selfxyz/common/utils';
-import { getSKIPEM } from '@selfxyz/common/utils/csca';
import {
- generateMockDSC,
- genMockIdDoc,
- initPassportDataParsing,
-} from '@selfxyz/common/utils/passports';
-import { useSelfClient } from '@selfxyz/mobile-sdk-alpha';
+ generateMockDocument,
+ signatureAlgorithmToStrictSignatureAlgorithm,
+ useSelfClient,
+} from '@selfxyz/mobile-sdk-alpha';
import { MockDataEvents } from '@selfxyz/mobile-sdk-alpha/constants/analytics';
import { PrimaryButton } from '@/components/buttons/PrimaryButton';
import ButtonsContainer from '@/components/ButtonsContainer';
import { Caption } from '@/components/typography/Caption';
+import { useMockDataForm } from '@/hooks/useMockDataForm';
import SelfDevCard from '@/images/card-dev.svg';
import IdIcon from '@/images/icons/id_icon.svg';
import NoteIcon from '@/images/icons/note.svg';
@@ -61,92 +59,7 @@ const documentTypes = {
mock_id_card: 'ID Card',
};
-const signatureAlgorithmToStrictSignatureAlgorithm = {
- 'sha256 rsa 65537 4096': ['sha256', 'sha256', 'rsa_sha256_65537_4096'],
- 'sha1 rsa 65537 2048': ['sha1', 'sha1', 'rsa_sha1_65537_2048'],
- 'sha256 brainpoolP256r1': [
- 'sha256',
- 'sha256',
- 'ecdsa_sha256_brainpoolP256r1_256',
- ],
- 'sha384 brainpoolP384r1': [
- 'sha384',
- 'sha384',
- 'ecdsa_sha384_brainpoolP384r1_384',
- ],
- 'sha384 secp384r1': ['sha384', 'sha384', 'ecdsa_sha384_secp384r1_384'],
- 'sha256 rsa 65537 2048': ['sha256', 'sha256', 'rsa_sha256_65537_2048'],
- 'sha256 rsa 3 2048': ['sha256', 'sha256', 'rsa_sha256_3_2048'],
- 'sha256 rsa 65537 3072': ['sha256', 'sha256', 'rsa_sha256_65537_3072'],
- 'sha256 rsa 3 4096': ['sha256', 'sha256', 'rsa_sha256_3_4096'],
- 'sha384 rsa 65537 4096': ['sha384', 'sha384', 'rsa_sha384_65537_4096'],
- 'sha512 rsa 65537 2048': ['sha512', 'sha512', 'rsa_sha512_65537_2048'],
- 'sha512 rsa 65537 4096': ['sha512', 'sha512', 'rsa_sha512_65537_4096'],
- 'sha1 rsa 65537 4096': ['sha1', 'sha1', 'rsa_sha1_65537_4096'],
- 'sha256 rsapss 3 2048': ['sha256', 'sha256', 'rsapss_sha256_3_2048'],
- 'sha256 rsapss 3 3072': ['sha256', 'sha256', 'rsapss_sha256_3_3072'],
- 'sha256 rsapss 65537 3072': ['sha256', 'sha256', 'rsapss_sha256_65537_3072'],
- 'sha256 rsapss 65537 4096': ['sha256', 'sha256', 'rsapss_sha256_65537_4096'],
- 'sha384 rsapss 65537 2048': ['sha384', 'sha384', 'rsapss_sha384_65537_2048'],
- 'sha384 rsapss 65537 3072': ['sha384', 'sha384', 'rsapss_sha384_65537_3072'],
- 'sha512 rsapss 65537 2048': ['sha512', 'sha512', 'rsapss_sha512_65537_2048'],
- 'sha512 rsapss 65537 4096': ['sha512', 'sha512', 'rsapss_sha512_65537_4096'],
- 'sha1 secp256r1': ['sha1', 'sha1', 'ecdsa_sha1_secp256r1_256'],
- 'sha224 secp224r1': ['sha224', 'sha224', 'ecdsa_sha224_secp224r1_224'],
- 'sha256 secp256r1': ['sha256', 'sha256', 'ecdsa_sha256_secp256r1_256'],
- 'sha256 secp384r1': ['sha256', 'sha256', 'ecdsa_sha256_secp384r1_384'],
- 'sha1 brainpoolP224r1': ['sha1', 'sha1', 'ecdsa_sha1_brainpoolP224r1_224'],
- 'sha1 brainpoolP256r1': ['sha1', 'sha1', 'ecdsa_sha1_brainpoolP256r1_256'],
- 'sha224 brainpoolP224r1': [
- 'sha224',
- 'sha224',
- 'ecdsa_sha224_brainpoolP224r1_224',
- ],
- 'sha256 brainpoolP224r1': [
- 'sha256',
- 'sha256',
- 'ecdsa_sha256_brainpoolP224r1_224',
- ],
- 'sha384 brainpoolP256r1': [
- 'sha384',
- 'sha384',
- 'ecdsa_sha384_brainpoolP256r1_256',
- ],
- 'sha512 brainpoolP256r1': [
- 'sha512',
- 'sha512',
- 'ecdsa_sha512_brainpoolP256r1_256',
- ],
- 'sha512 brainpoolP384r1': [
- 'sha512',
- 'sha512',
- 'ecdsa_sha512_brainpoolP384r1_384',
- ],
- 'sha512 poland': ['sha512', 'sha512', 'rsa_sha256_65537_4096'],
- 'not existing': ['sha512', 'sha384', 'rsa_sha256_65537_4096'],
-} as const;
-
-const formatDateToYYMMDD = (date: Date): string => {
- return (
- date.toISOString().slice(2, 4) +
- date.toISOString().slice(5, 7) +
- date.toISOString().slice(8, 10)
- ).toString();
-};
-
-const getBirthDateFromAge = (age: number): string => {
- const date = new Date();
- date.setFullYear(date.getFullYear() - age);
- return formatDateToYYMMDD(date);
-};
-
-const getExpiryDateFromYears = (years: number): string => {
- const date = new Date();
- date.setFullYear(date.getFullYear() + years);
- return formatDateToYYMMDD(date);
-};
-
-const MockPassportTitleCard = () => {
+const MockDocumentTitleCard = () => {
return (
{
- Generate mock passport data
+ Generate mock document data
- Configure data parameters to generate a mock passport for testing
+ Configure data parameters to generate a mock document for testing
purposes on the Self Protocol.
@@ -195,7 +108,7 @@ const HeroBanner = () => {
/>
-
+
= ({
const MockDataScreen: React.FC = () => {
const { trackEvent } = useSelfClient();
const navigation = useNavigation();
- const [age, setAge] = useState(21);
- const [expiryYears, setExpiryYears] = useState(5);
+ const {
+ age,
+ setAge,
+ expiryYears,
+ setExpiryYears,
+ selectedCountry,
+ handleCountrySelect,
+ selectedAlgorithm,
+ handleAlgorithmSelect,
+ selectedDocumentType,
+ handleDocumentTypeSelect,
+ isInOfacList,
+ setIsInOfacList,
+ resetFormValues,
+ } = useMockDataForm();
const [isGenerating, setIsGenerating] = useState(false);
- const [isInOfacList, setIsInOfacList] = useState(true);
- const [selectedDocumentType, setSelectedDocumentType] = useState<
- 'mock_passport' | 'mock_id_card'
- >('mock_passport');
- const [selectedCountry, setSelectedCountry] = useState('USA');
- const [selectedAlgorithm, setSelectedAlgorithm] = useState(
- 'sha256 rsa 65537 2048',
- );
const [isCountrySheetOpen, setCountrySheetOpen] = useState(false);
const [isAlgorithmSheetOpen, setAlgorithmSheetOpen] = useState(false);
const [isDocumentTypeSheetOpen, setDocumentTypeSheetOpen] = useState(false);
- const resetFormValues = () => {
- setAge(21);
- setExpiryYears(5);
- setIsInOfacList(true);
- setSelectedDocumentType('mock_passport');
- setSelectedAlgorithm('sha256 rsa 65537 2048');
- setSelectedCountry('USA');
- };
-
- const handleCountrySelect = (countryCode: string) => {
- setSelectedCountry(countryCode);
- setCountrySheetOpen(false);
- };
-
- const handleAlgorithmSelect = (algorithm: string) => {
- setSelectedAlgorithm(algorithm);
- setAlgorithmSheetOpen(false);
- };
-
- const handleDocumentTypeSelect = (
- documentType: 'mock_passport' | 'mock_id_card',
- ) => {
- setSelectedDocumentType(documentType);
- setDocumentTypeSheetOpen(false);
- };
-
const handleGenerate = useCallback(async () => {
setIsGenerating(true);
try {
- const randomPassportNumber = Math.random()
- .toString(36)
- .substring(2, 11)
- .replace(/[^a-z0-9]/gi, '')
- .toUpperCase();
- const algorithmMapping =
- signatureAlgorithmToStrictSignatureAlgorithm[
- selectedAlgorithm as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm
- ];
- const dgHashAlgo = algorithmMapping[0];
- const eContentHashAlgo = algorithmMapping[1];
- const signatureTypeForGeneration = algorithmMapping[2];
-
- const idDocInput: Partial = {
- nationality: selectedCountry as IdDocInput['nationality'],
- idType: selectedDocumentType as IdDocInput['idType'],
- dgHashAlgo: dgHashAlgo as IdDocInput['dgHashAlgo'],
- eContentHashAlgo: eContentHashAlgo as IdDocInput['eContentHashAlgo'],
- signatureType:
- signatureTypeForGeneration as IdDocInput['signatureType'],
- expiryDate: getExpiryDateFromYears(expiryYears),
- passportNumber: randomPassportNumber,
- };
-
- let dobForGeneration: string;
- if (isInOfacList) {
- dobForGeneration = '541007';
- idDocInput.lastName = 'HENAO MONTOYA';
- idDocInput.firstName = 'ARCANGEL DE JESUS';
- } else {
- dobForGeneration = getBirthDateFromAge(age);
- }
- idDocInput.birthDate = dobForGeneration;
- let mockDSC, rawMockData;
- try {
- mockDSC = await generateMockDSC(
- idDocInput.signatureType || 'rsa_sha256_65537_2048',
- );
- rawMockData = genMockIdDoc(idDocInput, mockDSC);
- } catch (error) {
- console.warn(
- 'Falling back to default mock DSC. Error during mock DSC generation:',
- error,
- );
- rawMockData = genMockIdDoc(idDocInput);
- }
- const skiPem = await getSKIPEM('staging');
- const parsedMockData = initPassportDataParsing(rawMockData, skiPem);
+ const parsedMockData = await generateMockDocument({
+ age,
+ expiryYears,
+ isInOfacList,
+ selectedAlgorithm,
+ selectedCountry,
+ selectedDocumentType,
+ });
await storePassportData(parsedMockData);
navigation.navigate('ConfirmBelongingScreen', {});
} catch (error) {
@@ -375,7 +227,7 @@ const MockDataScreen: React.FC = () => {
- Mock Passport Parameters
+ Mock Document Parameters
{
-
+
{
{isGenerating ? (
) : (
- 'Generate Mock Passport'
+ 'Generate Mock Document'
)}
diff --git a/packages/mobile-sdk-alpha/src/browser.ts b/packages/mobile-sdk-alpha/src/browser.ts
index 3abd1e49a..74670430f 100644
--- a/packages/mobile-sdk-alpha/src/browser.ts
+++ b/packages/mobile-sdk-alpha/src/browser.ts
@@ -52,6 +52,8 @@ export { defaultConfig } from './config/defaults';
/** @deprecated Use createSelfClient().extractMRZInfo or import from './mrz' */
export { extractMRZInfo, formatDateToYYMMDD, scanMRZ } from './mrz';
+export { generateMockDocument, signatureAlgorithmToStrictSignatureAlgorithm } from './mock/generator';
+
export { getAllDocuments, hasAnyValidRegisteredDocument, loadSelectedDocument } from './documents/utils';
// Core functions
diff --git a/packages/mobile-sdk-alpha/src/index.ts b/packages/mobile-sdk-alpha/src/index.ts
index 04b6365e4..fbc9879c4 100644
--- a/packages/mobile-sdk-alpha/src/index.ts
+++ b/packages/mobile-sdk-alpha/src/index.ts
@@ -86,6 +86,8 @@ export { extractMRZInfo } from './mrz';
export { formatDateToYYMMDD, scanMRZ } from './mrz';
+export { generateMockDocument, signatureAlgorithmToStrictSignatureAlgorithm } from './mock/generator';
+
// Documents utils
export { getAllDocuments, hasAnyValidRegisteredDocument, loadSelectedDocument } from './documents/utils';
diff --git a/packages/mobile-sdk-alpha/src/mock/generator.ts b/packages/mobile-sdk-alpha/src/mock/generator.ts
new file mode 100644
index 000000000..4b6aff240
--- /dev/null
+++ b/packages/mobile-sdk-alpha/src/mock/generator.ts
@@ -0,0 +1,119 @@
+// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
+// SPDX-License-Identifier: BUSL-1.1
+// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
+
+import type { IdDocInput } from '@selfxyz/common/utils';
+import { getSKIPEM } from '@selfxyz/common/utils/csca';
+import { generateMockDSC, genMockIdDoc, initPassportDataParsing } from '@selfxyz/common/utils/passports';
+
+export interface GenerateMockDocumentOptions {
+ age: number;
+ expiryYears: number;
+ isInOfacList: boolean;
+ selectedAlgorithm: string;
+ selectedCountry: string;
+ selectedDocumentType: 'mock_passport' | 'mock_id_card';
+}
+
+const formatDateToYYMMDD = (date: Date): string => {
+ return (date.toISOString().slice(2, 4) + date.toISOString().slice(5, 7) + date.toISOString().slice(8, 10)).toString();
+};
+
+const getBirthDateFromAge = (age: number): string => {
+ const date = new Date();
+ date.setFullYear(date.getFullYear() - age);
+ return formatDateToYYMMDD(date);
+};
+
+const getExpiryDateFromYears = (years: number): string => {
+ const date = new Date();
+ date.setFullYear(date.getFullYear() + years);
+ return formatDateToYYMMDD(date);
+};
+
+export async function generateMockDocument({
+ age,
+ expiryYears,
+ isInOfacList,
+ selectedAlgorithm,
+ selectedCountry,
+ selectedDocumentType,
+}: GenerateMockDocumentOptions) {
+ const randomPassportNumber = Math.random()
+ .toString(36)
+ .substring(2, 11)
+ .replace(/[^a-z0-9]/gi, '')
+ .toUpperCase();
+ const [dgHashAlgo, eContentHashAlgo, signatureTypeForGeneration] =
+ signatureAlgorithmToStrictSignatureAlgorithm[
+ selectedAlgorithm as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm
+ ];
+
+ const idDocInput: Partial = {
+ nationality: selectedCountry as IdDocInput['nationality'],
+ idType: selectedDocumentType as IdDocInput['idType'],
+ dgHashAlgo: dgHashAlgo as IdDocInput['dgHashAlgo'],
+ eContentHashAlgo: eContentHashAlgo as IdDocInput['eContentHashAlgo'],
+ signatureType: signatureTypeForGeneration as IdDocInput['signatureType'],
+ expiryDate: getExpiryDateFromYears(expiryYears),
+ passportNumber: randomPassportNumber,
+ };
+
+ let dobForGeneration: string;
+ if (isInOfacList) {
+ dobForGeneration = '541007';
+ idDocInput.lastName = 'HENAO MONTOYA';
+ idDocInput.firstName = 'ARCANGEL DE JESUS';
+ } else {
+ dobForGeneration = getBirthDateFromAge(age);
+ }
+ idDocInput.birthDate = dobForGeneration;
+
+ let mockDSC, rawMockData;
+ try {
+ mockDSC = await generateMockDSC(idDocInput.signatureType || 'rsa_sha256_65537_2048');
+ rawMockData = genMockIdDoc(idDocInput, mockDSC);
+ } catch (error) {
+ console.warn('Falling back to default mock DSC. Error during mock DSC generation:', error);
+ rawMockData = genMockIdDoc(idDocInput);
+ }
+ const skiPem = await getSKIPEM('staging');
+ return initPassportDataParsing(rawMockData, skiPem);
+}
+
+export const signatureAlgorithmToStrictSignatureAlgorithm = {
+ 'sha256 rsa 65537 4096': ['sha256', 'sha256', 'rsa_sha256_65537_4096'],
+ 'sha1 rsa 65537 2048': ['sha1', 'sha1', 'rsa_sha1_65537_2048'],
+ 'sha256 brainpoolP256r1': ['sha256', 'sha256', 'ecdsa_sha256_brainpoolP256r1_256'],
+ 'sha384 brainpoolP384r1': ['sha384', 'sha384', 'ecdsa_sha384_brainpoolP384r1_384'],
+ 'sha384 secp384r1': ['sha384', 'sha384', 'ecdsa_sha384_secp384r1_384'],
+ 'sha256 rsa 65537 2048': ['sha256', 'sha256', 'rsa_sha256_65537_2048'],
+ 'sha256 rsa 3 2048': ['sha256', 'sha256', 'rsa_sha256_3_2048'],
+ 'sha256 rsa 65537 3072': ['sha256', 'sha256', 'rsa_sha256_65537_3072'],
+ 'sha256 rsa 3 4096': ['sha256', 'sha256', 'rsa_sha256_3_4096'],
+ 'sha384 rsa 65537 4096': ['sha384', 'sha384', 'rsa_sha384_65537_4096'],
+ 'sha512 rsa 65537 2048': ['sha512', 'sha512', 'rsa_sha512_65537_2048'],
+ 'sha512 rsa 65537 4096': ['sha512', 'sha512', 'rsa_sha512_65537_4096'],
+ 'sha1 rsa 65537 4096': ['sha1', 'sha1', 'rsa_sha1_65537_4096'],
+ 'sha256 rsapss 3 2048': ['sha256', 'sha256', 'rsapss_sha256_3_2048'],
+ 'sha256 rsapss 3 3072': ['sha256', 'sha256', 'rsapss_sha256_3_3072'],
+ 'sha256 rsapss 65537 3072': ['sha256', 'sha256', 'rsapss_sha256_65537_3072'],
+ 'sha256 rsapss 65537 4096': ['sha256', 'sha256', 'rsapss_sha256_65537_4096'],
+ 'sha384 rsapss 65537 2048': ['sha384', 'sha384', 'rsapss_sha384_65537_2048'],
+ 'sha384 rsapss 65537 3072': ['sha384', 'sha384', 'rsapss_sha384_65537_3072'],
+ 'sha512 rsapss 65537 2048': ['sha512', 'sha512', 'rsapss_sha512_65537_2048'],
+ 'sha512 rsapss 65537 4096': ['sha512', 'sha512', 'rsapss_sha512_65537_4096'],
+ 'sha1 secp256r1': ['sha1', 'sha1', 'ecdsa_sha1_secp256r1_256'],
+ 'sha224 secp224r1': ['sha224', 'sha224', 'ecdsa_sha224_secp224r1_224'],
+ 'sha256 secp256r1': ['sha256', 'sha256', 'ecdsa_sha256_secp256r1_256'],
+ 'sha256 secp384r1': ['sha256', 'sha256', 'ecdsa_sha256_secp384r1_384'],
+ 'sha1 brainpoolP224r1': ['sha1', 'sha1', 'ecdsa_sha1_brainpoolP224r1_224'],
+ 'sha1 brainpoolP256r1': ['sha1', 'sha1', 'ecdsa_sha1_brainpoolP256r1_256'],
+ 'sha224 brainpoolP224r1': ['sha224', 'sha224', 'ecdsa_sha224_brainpoolP224r1_224'],
+ 'sha256 brainpoolP224r1': ['sha256', 'sha256', 'ecdsa_sha256_brainpoolP224r1_224'],
+ 'sha384 brainpoolP256r1': ['sha384', 'sha384', 'ecdsa_sha384_brainpoolP256r1_256'],
+ 'sha512 brainpoolP256r1': ['sha512', 'sha512', 'ecdsa_sha512_brainpoolP256r1_256'],
+ 'sha512 brainpoolP384r1': ['sha512', 'sha512', 'ecdsa_sha512_brainpoolP384r1_384'],
+ 'sha512 poland': ['sha512', 'sha512', 'rsa_sha256_65537_4096'],
+ 'not existing': ['sha512', 'sha384', 'rsa_sha256_65537_4096'],
+} as const;
diff --git a/packages/mobile-sdk-alpha/tests/mock/generator.test.ts b/packages/mobile-sdk-alpha/tests/mock/generator.test.ts
new file mode 100644
index 000000000..6f88a8223
--- /dev/null
+++ b/packages/mobile-sdk-alpha/tests/mock/generator.test.ts
@@ -0,0 +1,547 @@
+// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
+// SPDX-License-Identifier: BUSL-1.1
+// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
+
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+
+import { generateMockDocument, signatureAlgorithmToStrictSignatureAlgorithm } from '../../src/mock/generator';
+
+// Mock the external dependencies
+vi.mock('@selfxyz/common/utils/csca', () => ({
+ getSKIPEM: vi.fn(),
+}));
+
+vi.mock('@selfxyz/common/utils/passports', () => ({
+ generateMockDSC: vi.fn(),
+ genMockIdDoc: vi.fn(),
+ initPassportDataParsing: vi.fn(),
+}));
+
+// Import the mocked functions after the mocks are defined
+let getSKIPEM: any;
+let generateMockDSC: any;
+let genMockIdDoc: any;
+let initPassportDataParsing: any;
+
+// These will be imported after the mocks are set up
+
+describe('Mock Generator Helper Functions', () => {
+ describe('Date formatting and calculation', () => {
+ it('should format date to YYMMDD correctly', () => {
+ // Test with a known date
+ const testDate = new Date('2024-03-15T10:30:00.000Z');
+ const expected = '240315'; // YY=24, MM=03, DD=15
+
+ // Since the helper function is not exported, we'll test through the main function
+ // and verify the format in the results
+ expect(
+ testDate.toISOString().slice(2, 4) + testDate.toISOString().slice(5, 7) + testDate.toISOString().slice(8, 10),
+ ).toBe(expected);
+ });
+
+ it('should calculate birth date from age correctly', () => {
+ const currentYear = new Date().getFullYear();
+ const testAge = 25;
+ const expectedBirthYear = currentYear - testAge;
+
+ // We'll verify this through the main function since the helper is not exported
+ const birthDate = new Date();
+ birthDate.setFullYear(birthDate.getFullYear() - testAge);
+ const expectedFormat = (
+ birthDate.toISOString().slice(2, 4) +
+ birthDate.toISOString().slice(5, 7) +
+ birthDate.toISOString().slice(8, 10)
+ ).toString();
+
+ expect(expectedFormat).toMatch(/^\d{6}$/);
+ expect(parseInt(expectedFormat.slice(0, 2)) + 2000).toBeCloseTo(expectedBirthYear, 0);
+ });
+
+ it('should calculate expiry date from years correctly', () => {
+ const currentYear = new Date().getFullYear();
+ const testYears = 10;
+ const expectedExpiryYear = currentYear + testYears;
+
+ const expiryDate = new Date();
+ expiryDate.setFullYear(expiryDate.getFullYear() + testYears);
+ const expectedFormat = (
+ expiryDate.toISOString().slice(2, 4) +
+ expiryDate.toISOString().slice(5, 7) +
+ expiryDate.toISOString().slice(8, 10)
+ ).toString();
+
+ expect(expectedFormat).toMatch(/^\d{6}$/);
+ expect(parseInt(expectedFormat.slice(0, 2)) + 2000).toBeCloseTo(expectedExpiryYear, 0);
+ });
+ });
+});
+
+describe('generateMockDocument', () => {
+ beforeEach(async () => {
+ vi.clearAllMocks();
+
+ // Import the mocked functions
+ const csca = await import('@selfxyz/common/utils/csca');
+ const passports = await import('@selfxyz/common/utils/passports');
+ getSKIPEM = csca.getSKIPEM;
+ generateMockDSC = passports.generateMockDSC;
+ genMockIdDoc = passports.genMockIdDoc;
+ initPassportDataParsing = passports.initPassportDataParsing;
+
+ // Setup default mocks with proper types
+ vi.mocked(getSKIPEM).mockResolvedValue({ 'mock-key': 'mock-ski-pem' });
+ vi.mocked(generateMockDSC).mockResolvedValue({ privateKeyPem: 'mock-private-key', dsc: 'mock-dsc' });
+ vi.mocked(genMockIdDoc).mockReturnValue({
+ dataGroupHashes: {},
+ eContent: new Uint8Array(),
+ encryptedDigest: new Uint8Array(),
+ } as any);
+ vi.mocked(initPassportDataParsing).mockResolvedValue({
+ dataGroupHashes: {},
+ eContent: new Uint8Array(),
+ encryptedDigest: new Uint8Array(),
+ } as any);
+ });
+
+ const defaultOptions = {
+ age: 30,
+ expiryYears: 10,
+ isInOfacList: false,
+ selectedAlgorithm: 'sha256 rsa 65537 2048',
+ selectedCountry: 'US',
+ selectedDocumentType: 'mock_passport' as const,
+ };
+
+ it('should generate mock passport with default options', async () => {
+ const result = await generateMockDocument(defaultOptions);
+
+ expect(result).toBeDefined();
+ expect(getSKIPEM).toHaveBeenCalledWith('staging');
+ expect(generateMockDSC).toHaveBeenCalledWith('rsa_sha256_65537_2048');
+ expect(genMockIdDoc).toHaveBeenCalledWith(
+ expect.objectContaining({
+ nationality: 'US',
+ idType: 'mock_passport',
+ dgHashAlgo: 'sha256',
+ eContentHashAlgo: 'sha256',
+ signatureType: 'rsa_sha256_65537_2048',
+ }),
+ expect.objectContaining({ privateKeyPem: expect.any(String), dsc: expect.any(String) }),
+ );
+ expect(initPassportDataParsing).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.objectContaining({ 'mock-key': expect.any(String) }),
+ );
+ });
+
+ it('should generate mock ID card when document type is mock_id_card', async () => {
+ const options = { ...defaultOptions, selectedDocumentType: 'mock_id_card' as const };
+
+ await generateMockDocument(options);
+
+ expect(genMockIdDoc).toHaveBeenCalledWith(
+ expect.objectContaining({
+ idType: 'mock_id_card',
+ }),
+ expect.objectContaining({ privateKeyPem: expect.any(String), dsc: expect.any(String) }),
+ );
+ });
+
+ it('should use OFAC-listed person data when isInOfacList is true', async () => {
+ const options = { ...defaultOptions, isInOfacList: true };
+
+ await generateMockDocument(options);
+
+ expect(genMockIdDoc).toHaveBeenCalledWith(
+ expect.objectContaining({
+ lastName: 'HENAO MONTOYA',
+ firstName: 'ARCANGEL DE JESUS',
+ birthDate: '541007', // Fixed OFAC DOB
+ }),
+ expect.objectContaining({ privateKeyPem: expect.any(String), dsc: expect.any(String) }),
+ );
+ });
+
+ it('should calculate age-based birth date when not in OFAC list', async () => {
+ const options = { ...defaultOptions, age: 25, isInOfacList: false };
+
+ await generateMockDocument(options);
+
+ const callArgs = vi.mocked(genMockIdDoc).mock.calls[0][0];
+ expect(callArgs.birthDate).toMatch(/^\d{6}$/);
+ expect(callArgs.birthDate).not.toBe('541007'); // Should not be the OFAC DOB
+ });
+
+ it('should generate random passport numbers', async () => {
+ await generateMockDocument(defaultOptions);
+ await generateMockDocument(defaultOptions);
+
+ const call1Args = vi.mocked(genMockIdDoc).mock.calls[0]?.[0];
+ const call2Args = vi.mocked(genMockIdDoc).mock.calls[1]?.[0];
+
+ expect(call1Args).toBeDefined();
+ expect(call2Args).toBeDefined();
+ expect(call1Args!.passportNumber).toMatch(/^[A-Z0-9]{9}$/);
+ expect(call2Args!.passportNumber).toMatch(/^[A-Z0-9]{9}$/);
+ // Random numbers should be different
+ expect(call1Args!.passportNumber).not.toBe(call2Args!.passportNumber);
+ });
+
+ it('should handle different signature algorithms', async () => {
+ const algorithms = ['sha256 rsa 65537 4096', 'sha1 rsa 65537 2048', 'sha256 brainpoolP256r1', 'sha384 secp384r1'];
+
+ for (const algorithm of algorithms) {
+ const options = { ...defaultOptions, selectedAlgorithm: algorithm };
+ await generateMockDocument(options);
+
+ const expectedMapping =
+ signatureAlgorithmToStrictSignatureAlgorithm[
+ algorithm as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm
+ ];
+ expect(generateMockDSC).toHaveBeenCalledWith(expectedMapping[2]);
+ }
+ });
+
+ it('should set expiry date based on years parameter', async () => {
+ const options = { ...defaultOptions, expiryYears: 5 };
+
+ await generateMockDocument(options);
+
+ const callArgs = vi.mocked(genMockIdDoc).mock.calls[0]?.[0];
+ expect(callArgs).toBeDefined();
+ expect(callArgs!.expiryDate).toMatch(/^\d{6}$/);
+
+ // Verify the year is approximately correct (within 1 year due to timing)
+ const currentYear = new Date().getFullYear();
+ const expiryYear = 2000 + parseInt(callArgs!.expiryDate!.slice(0, 2));
+ expect(expiryYear).toBeGreaterThanOrEqual(currentYear + 4);
+ expect(expiryYear).toBeLessThanOrEqual(currentYear + 6);
+ });
+
+ it('should fall back to default DSC when generateMockDSC fails', async () => {
+ vi.mocked(generateMockDSC).mockRejectedValueOnce(new Error('DSC generation failed'));
+
+ const result = await generateMockDocument(defaultOptions);
+
+ expect(result).toBeDefined();
+ expect(genMockIdDoc).toHaveBeenCalledWith(
+ expect.objectContaining({
+ signatureType: 'rsa_sha256_65537_2048',
+ }),
+ // Note: no second argument (mock DSC) when falling back
+ );
+ });
+
+ it('should handle various countries', async () => {
+ const countries = ['US', 'GB', 'DE', 'FR', 'JP'];
+
+ for (const country of countries) {
+ const options = { ...defaultOptions, selectedCountry: country };
+ await generateMockDocument(options);
+
+ const callArgs = vi.mocked(genMockIdDoc).mock.calls[vi.mocked(genMockIdDoc).mock.calls.length - 1]?.[0];
+ expect(callArgs).toBeDefined();
+ expect(callArgs!.nationality).toBe(country);
+ }
+ });
+
+ it('should preserve all required ID document fields', async () => {
+ await generateMockDocument(defaultOptions);
+
+ const callArgs = vi.mocked(genMockIdDoc).mock.calls[0][0];
+
+ expect(callArgs).toEqual(
+ expect.objectContaining({
+ nationality: expect.any(String),
+ idType: expect.any(String),
+ dgHashAlgo: expect.any(String),
+ eContentHashAlgo: expect.any(String),
+ signatureType: expect.any(String),
+ expiryDate: expect.any(String),
+ passportNumber: expect.any(String),
+ birthDate: expect.any(String),
+ }),
+ );
+ });
+});
+
+describe('signatureAlgorithmToStrictSignatureAlgorithm', () => {
+ it('should contain all expected signature algorithms', () => {
+ const expectedAlgorithms = [
+ 'sha256 rsa 65537 4096',
+ 'sha1 rsa 65537 2048',
+ 'sha256 brainpoolP256r1',
+ 'sha384 brainpoolP384r1',
+ 'sha384 secp384r1',
+ 'sha256 rsa 65537 2048',
+ 'sha256 rsa 3 2048',
+ 'sha256 rsa 65537 3072',
+ 'sha256 rsa 3 4096',
+ 'sha384 rsa 65537 4096',
+ 'sha512 rsa 65537 2048',
+ 'sha512 rsa 65537 4096',
+ 'sha1 rsa 65537 4096',
+ 'sha256 rsapss 3 2048',
+ 'sha256 rsapss 3 3072',
+ 'sha256 rsapss 65537 3072',
+ 'sha256 rsapss 65537 4096',
+ 'sha384 rsapss 65537 2048',
+ 'sha384 rsapss 65537 3072',
+ 'sha512 rsapss 65537 2048',
+ 'sha512 rsapss 65537 4096',
+ 'sha1 secp256r1',
+ 'sha224 secp224r1',
+ 'sha256 secp256r1',
+ 'sha256 secp384r1',
+ 'sha1 brainpoolP224r1',
+ 'sha1 brainpoolP256r1',
+ 'sha224 brainpoolP224r1',
+ 'sha256 brainpoolP224r1',
+ 'sha384 brainpoolP256r1',
+ 'sha512 brainpoolP256r1',
+ 'sha512 brainpoolP384r1',
+ 'sha512 poland',
+ 'not existing',
+ ];
+
+ expectedAlgorithms.forEach(algorithm => {
+ expect(signatureAlgorithmToStrictSignatureAlgorithm).toHaveProperty(algorithm);
+ });
+ });
+
+ it('should map algorithms to correct tuple format', () => {
+ const testCases = [
+ {
+ input: 'sha256 rsa 65537 2048',
+ expected: ['sha256', 'sha256', 'rsa_sha256_65537_2048'],
+ },
+ {
+ input: 'sha384 brainpoolP384r1',
+ expected: ['sha384', 'sha384', 'ecdsa_sha384_brainpoolP384r1_384'],
+ },
+ {
+ input: 'sha256 rsapss 65537 4096',
+ expected: ['sha256', 'sha256', 'rsapss_sha256_65537_4096'],
+ },
+ {
+ input: 'sha1 secp256r1',
+ expected: ['sha1', 'sha1', 'ecdsa_sha1_secp256r1_256'],
+ },
+ ];
+
+ testCases.forEach(({ input, expected }) => {
+ expect(
+ signatureAlgorithmToStrictSignatureAlgorithm[
+ input as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm
+ ],
+ ).toEqual(expected);
+ });
+ });
+
+ it('should have consistent hash algorithms in tuples', () => {
+ Object.entries(signatureAlgorithmToStrictSignatureAlgorithm).forEach(([key, [dgHashAlgo, eContentHashAlgo]]) => {
+ // For most algorithms, dgHashAlgo and eContentHashAlgo should be the same
+ if (key !== 'not existing') {
+ expect(dgHashAlgo).toBe(eContentHashAlgo);
+ }
+ });
+ });
+
+ it('should have valid signature types in tuples', () => {
+ const validSignatureTypes = [
+ 'rsa_sha256_65537_4096',
+ 'rsa_sha1_65537_2048',
+ 'ecdsa_sha256_brainpoolP256r1_256',
+ 'ecdsa_sha384_brainpoolP384r1_384',
+ 'ecdsa_sha384_secp384r1_384',
+ 'rsa_sha256_65537_2048',
+ 'rsa_sha256_3_2048',
+ 'rsa_sha256_65537_3072',
+ 'rsa_sha256_3_4096',
+ 'rsa_sha384_65537_4096',
+ 'rsa_sha512_65537_2048',
+ 'rsa_sha512_65537_4096',
+ 'rsa_sha1_65537_4096',
+ 'rsapss_sha256_3_2048',
+ 'rsapss_sha256_3_3072',
+ 'rsapss_sha256_65537_3072',
+ 'rsapss_sha256_65537_4096',
+ 'rsapss_sha384_65537_2048',
+ 'rsapss_sha384_65537_3072',
+ 'rsapss_sha512_65537_2048',
+ 'rsapss_sha512_65537_4096',
+ 'ecdsa_sha1_secp256r1_256',
+ 'ecdsa_sha224_secp224r1_224',
+ 'ecdsa_sha256_secp256r1_256',
+ 'ecdsa_sha256_secp384r1_384',
+ 'ecdsa_sha1_brainpoolP224r1_224',
+ 'ecdsa_sha1_brainpoolP256r1_256',
+ 'ecdsa_sha224_brainpoolP224r1_224',
+ 'ecdsa_sha256_brainpoolP224r1_224',
+ 'ecdsa_sha384_brainpoolP256r1_256',
+ 'ecdsa_sha512_brainpoolP256r1_256',
+ 'ecdsa_sha512_brainpoolP384r1_384',
+ ];
+
+ Object.values(signatureAlgorithmToStrictSignatureAlgorithm).forEach(([, , signatureType]) => {
+ expect(validSignatureTypes).toContain(signatureType);
+ });
+ });
+});
+
+describe('generateMockDocument integration', () => {
+ const defaultOptions = {
+ age: 30,
+ expiryYears: 10,
+ isInOfacList: false,
+ selectedAlgorithm: 'sha256 rsa 65537 2048',
+ selectedCountry: 'US',
+ selectedDocumentType: 'mock_passport' as const,
+ };
+
+ beforeEach(async () => {
+ vi.clearAllMocks();
+
+ // Import the mocked functions
+ const csca = await import('@selfxyz/common/utils/csca');
+ const passports = await import('@selfxyz/common/utils/passports');
+ getSKIPEM = csca.getSKIPEM;
+ generateMockDSC = passports.generateMockDSC;
+ genMockIdDoc = passports.genMockIdDoc;
+ initPassportDataParsing = passports.initPassportDataParsing;
+
+ // Setup default mocks with proper types
+ vi.mocked(getSKIPEM).mockResolvedValue({ 'mock-key': 'mock-ski-pem' });
+ vi.mocked(generateMockDSC).mockResolvedValue({ privateKeyPem: 'mock-private-key', dsc: 'mock-dsc' });
+ vi.mocked(genMockIdDoc).mockReturnValue({
+ dataGroupHashes: {},
+ eContent: new Uint8Array(),
+ encryptedDigest: new Uint8Array(),
+ } as any);
+ vi.mocked(initPassportDataParsing).mockResolvedValue({
+ dataGroupHashes: {},
+ eContent: new Uint8Array(),
+ encryptedDigest: new Uint8Array(),
+ } as any);
+ });
+
+ it('should handle successful DSC generation path', async () => {
+ const result = await generateMockDocument(defaultOptions);
+
+ expect(generateMockDSC).toHaveBeenCalledWith('rsa_sha256_65537_2048');
+ expect(genMockIdDoc).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.objectContaining({ privateKeyPem: expect.any(String), dsc: expect.any(String) }),
+ );
+ expect(result).toBeDefined();
+ });
+
+ it('should handle DSC generation failure and fall back gracefully', async () => {
+ const mockError = new Error('DSC generation failed');
+ vi.mocked(generateMockDSC).mockRejectedValueOnce(mockError);
+
+ const result = await generateMockDocument(defaultOptions);
+
+ expect(generateMockDSC).toHaveBeenCalledWith('rsa_sha256_65537_2048');
+ // Should call genMockIdDoc without DSC (fallback mode)
+ expect(genMockIdDoc).toHaveBeenCalledWith(expect.any(Object));
+ expect(result).toBeDefined();
+ });
+
+ it('should generate different passport numbers on subsequent calls', async () => {
+ await generateMockDocument(defaultOptions);
+ await generateMockDocument(defaultOptions);
+
+ const call1Args = vi.mocked(genMockIdDoc).mock.calls[0]?.[0];
+ const call2Args = vi.mocked(genMockIdDoc).mock.calls[1]?.[0];
+
+ expect(call1Args).toBeDefined();
+ expect(call2Args).toBeDefined();
+ expect(call1Args!.passportNumber).not.toBe(call2Args!.passportNumber);
+ expect(call1Args!.passportNumber).toMatch(/^[A-Z0-9]+$/);
+ expect(call2Args!.passportNumber).toMatch(/^[A-Z0-9]+$/);
+ });
+
+ it('should handle various edge cases for age', async () => {
+ const edgeCases = [0, 1, 18, 100, 120];
+
+ for (const age of edgeCases) {
+ const options = { ...defaultOptions, age };
+ await generateMockDocument(options);
+
+ const callArgs = vi.mocked(genMockIdDoc).mock.calls[vi.mocked(genMockIdDoc).mock.calls.length - 1]?.[0];
+ expect(callArgs).toBeDefined();
+ expect(callArgs!.birthDate).toMatch(/^\d{6}$/);
+ }
+ });
+
+ it('should handle various expiry years', async () => {
+ const expiryYears = [1, 5, 10, 15, 30];
+
+ for (const years of expiryYears) {
+ const options = { ...defaultOptions, expiryYears: years };
+ await generateMockDocument(options);
+
+ const callArgs = vi.mocked(genMockIdDoc).mock.calls[vi.mocked(genMockIdDoc).mock.calls.length - 1]?.[0];
+ expect(callArgs).toBeDefined();
+ expect(callArgs!.expiryDate).toMatch(/^\d{6}$/);
+ }
+ });
+
+ it('should use correct hash algorithms from mapping', async () => {
+ const testAlgorithm = 'sha384 brainpoolP384r1';
+ const options = { ...defaultOptions, selectedAlgorithm: testAlgorithm };
+
+ await generateMockDocument(options);
+
+ const callArgs = vi.mocked(genMockIdDoc).mock.calls[0]?.[0];
+ expect(callArgs).toBeDefined();
+ expect(callArgs!.dgHashAlgo).toBe('sha384');
+ expect(callArgs!.eContentHashAlgo).toBe('sha384');
+ expect(callArgs!.signatureType).toBe('ecdsa_sha384_brainpoolP384r1_384');
+ });
+
+ it('should always call getSKIPEM with staging parameter', async () => {
+ await generateMockDocument(defaultOptions);
+
+ expect(getSKIPEM).toHaveBeenCalledWith('staging');
+ expect(getSKIPEM).toHaveBeenCalledTimes(1);
+ });
+
+ it('should maintain document field completeness', async () => {
+ const options = {
+ ...defaultOptions,
+ age: 42,
+ expiryYears: 7,
+ selectedCountry: 'CA',
+ selectedDocumentType: 'mock_id_card' as const,
+ };
+
+ await generateMockDocument(options);
+
+ const callArgs = vi.mocked(genMockIdDoc).mock.calls[0]?.[0];
+
+ // Verify all required fields are present
+ expect(callArgs).toBeDefined();
+ expect(callArgs!.nationality).toBe('CA');
+ expect(callArgs!.idType).toBe('mock_id_card');
+ expect(callArgs!.dgHashAlgo).toBeDefined();
+ expect(callArgs!.eContentHashAlgo).toBeDefined();
+ expect(callArgs!.signatureType).toBeDefined();
+ expect(callArgs!.expiryDate).toBeDefined();
+ expect(callArgs!.passportNumber).toBeDefined();
+ expect(callArgs!.birthDate).toBeDefined();
+ });
+
+ it('should handle special algorithm edge case', async () => {
+ const options = { ...defaultOptions, selectedAlgorithm: 'not existing' };
+
+ await generateMockDocument(options);
+
+ const callArgs = vi.mocked(genMockIdDoc).mock.calls[0]?.[0];
+ expect(callArgs).toBeDefined();
+ expect(callArgs!.dgHashAlgo).toBe('sha512');
+ expect(callArgs!.eContentHashAlgo).toBe('sha384');
+ expect(callArgs!.signatureType).toBe('rsa_sha256_65537_4096');
+ });
+});