devendra-skill-package 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/skills/boiler-project-generator/SKILL.md +103 -0
- package/.github/skills/boiler-project-generator/assets/templates/android/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/angular/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/flutter/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/ios/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/api-setup.ts +76 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/component-primary-button.tsx +29 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/env-config.env +4 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-auth-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-dashboard-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-profile-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/hook-use-app-boot.ts +11 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/navigation-setup.tsx +25 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/project-structure.md +24 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-hooks.ts +5 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-session-slice.ts +29 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-store.ts +15 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/theme-index.ts +23 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/utils-helpers.ts +7 -0
- package/.github/skills/boiler-project-generator/metadata.yaml +29 -0
- package/.github/skills/boiler-project-generator/references/android.md +19 -0
- package/.github/skills/boiler-project-generator/references/angular.md +14 -0
- package/.github/skills/boiler-project-generator/references/flutter.md +17 -0
- package/.github/skills/boiler-project-generator/references/ios.md +17 -0
- package/.github/skills/boiler-project-generator/references/react-native.md +103 -0
- package/.github/skills/boiler-project-generator/references/react.md +17 -0
- package/.github/skills/boiler-project-generator/scripts/angular.js +43 -0
- package/.github/skills/boiler-project-generator/scripts/generate-project.mjs +107 -0
- package/.github/skills/boiler-project-generator/scripts/init.js +105 -0
- package/.github/skills/boiler-project-generator/scripts/react-native.js +1041 -0
- package/.github/skills/boiler-project-generator/scripts/react.js +32 -0
- package/.github/skills/clean-architecture-generator/SKILL.md +235 -0
- package/.github/skills/clean-architecture-generator/references/architecture-core.md +126 -0
- package/.github/skills/clean-architecture-generator/references/platform-angular.md +374 -0
- package/.github/skills/clean-architecture-generator/references/platform-flutter.md +366 -0
- package/.github/skills/clean-architecture-generator/references/platform-react-native.md +590 -0
- package/.github/skills/clean-architecture-generator/references/platform-react.md +261 -0
- package/.github/skills/code-test-coverage-analyzer/SKILL.md +1358 -0
- package/.github/skills/code-test-coverage-analyzer/assets/config-questionnaire.md +118 -0
- package/.github/skills/code-test-coverage-analyzer/assets/conflict-resolution-protocol.md +99 -0
- package/.github/skills/code-test-coverage-analyzer/assets/coverage-calculation-rules.md +294 -0
- package/.github/skills/code-test-coverage-analyzer/assets/multi-strategy-search.md +145 -0
- package/.github/skills/code-test-coverage-analyzer/assets/output-file-contracts.md +651 -0
- package/.github/skills/code-test-coverage-analyzer/assets/status-definitions.md +273 -0
- package/.github/skills/code-test-coverage-analyzer/examples/example-invocation.md +208 -0
- package/.github/skills/code-test-coverage-analyzer/references/android-patterns.md +229 -0
- package/.github/skills/code-test-coverage-analyzer/references/angular-patterns.md +177 -0
- package/.github/skills/code-test-coverage-analyzer/references/backend-patterns.md +383 -0
- package/.github/skills/code-test-coverage-analyzer/references/flutter-patterns.md +254 -0
- package/.github/skills/code-test-coverage-analyzer/references/ios-patterns.md +228 -0
- package/.github/skills/code-test-coverage-analyzer/references/react-native-patterns.md +431 -0
- package/.github/skills/code-test-coverage-analyzer/references/react-patterns.md +206 -0
- package/.github/skills/code-test-coverage-analyzer/scripts/detect-platform.ps1 +261 -0
- package/.github/skills/code-test-coverage-analyzer/scripts/scan-repository.ps1 +242 -0
- package/.github/skills/code-test-coverage-analyzer/templates/brd-compliance-row.md +107 -0
- package/.github/skills/code-test-coverage-analyzer/templates/test-case-row.md +130 -0
- package/.github/skills/code-test-coverage-analyzer/templates/user-story-compliance-row.md +170 -0
- package/.github/skills/custom-font-integrator/SKILL.md +373 -0
- package/.github/skills/custom-font-integrator/assets/FontTestScreen.js +150 -0
- package/.github/skills/custom-font-integrator/references/example-info-plist.xml +21 -0
- package/.github/skills/custom-font-integrator/scripts/setup-fonts.sh +99 -0
- package/.github/skills/encryption-decryption-utility/Instructions.md +282 -0
- package/.github/skills/encryption-decryption-utility/README.md +222 -0
- package/.github/skills/encryption-decryption-utility/SKILL.md +645 -0
- package/.github/skills/encryption-decryption-utility/assets/encryption-config-template.json +13 -0
- package/.github/skills/encryption-decryption-utility/assets/platform-matrix.json +180 -0
- package/.github/skills/encryption-decryption-utility/metadata.yaml +51 -0
- package/.github/skills/encryption-decryption-utility/references/android-crypto.md +157 -0
- package/.github/skills/encryption-decryption-utility/references/angular-crypto.md +130 -0
- package/.github/skills/encryption-decryption-utility/references/approach-1-webcrypto.md +95 -0
- package/.github/skills/encryption-decryption-utility/references/approach-2-rsa-aes-cbc.md +111 -0
- package/.github/skills/encryption-decryption-utility/references/approach-3-aes-sharedkey.md +114 -0
- package/.github/skills/encryption-decryption-utility/references/architecture.md +233 -0
- package/.github/skills/encryption-decryption-utility/references/backend-alignment.md +506 -0
- package/.github/skills/encryption-decryption-utility/references/dependency-guidance.md +234 -0
- package/.github/skills/encryption-decryption-utility/references/ec-implementation.md +832 -0
- package/.github/skills/encryption-decryption-utility/references/error-handling-playbook.md +234 -0
- package/.github/skills/encryption-decryption-utility/references/flutter-crypto.md +105 -0
- package/.github/skills/encryption-decryption-utility/references/ios-crypto.md +338 -0
- package/.github/skills/encryption-decryption-utility/references/native-rsa-implementation.md +373 -0
- package/.github/skills/encryption-decryption-utility/references/node-crypto.md +86 -0
- package/.github/skills/encryption-decryption-utility/references/react-crypto.md +105 -0
- package/.github/skills/encryption-decryption-utility/references/react-native-crypto.md +1001 -0
- package/.github/skills/encryption-decryption-utility/references/react-web-crypto.md +668 -0
- package/.github/skills/encryption-decryption-utility/references/security-checklist.md +176 -0
- package/.github/skills/encryption-decryption-utility/references/web-crypto.md +74 -0
- package/.github/skills/encryption-decryption-utility/scripts/detect.js +128 -0
- package/.github/skills/encryption-decryption-utility/scripts/install.js +1713 -0
- package/.github/skills/encryption-decryption-utility/scripts/setup.js +800 -0
- package/.github/skills/encryption-decryption-utility/template.md +241 -0
- package/.github/skills/encryption-decryption-utility/templates/config-questionnaire.md +467 -0
- package/.github/skills/encryption-decryption-utility/templates/delivery-checklist.md +206 -0
- package/.github/skills/encryption-decryption-utility/templates/implementation-plan.md +304 -0
- package/.github/skills/feature-generator/SKILL.MD +2741 -0
- package/.github/skills/feature-generator/assets/fingerprint-schema.json +385 -0
- package/.github/skills/feature-generator/assets/spec-template.md +172 -0
- package/.github/skills/feature-generator/assets/transform-rules.json +82 -0
- package/.github/skills/feature-generator/references/api-envelope-patterns.md +327 -0
- package/.github/skills/feature-generator/references/screen-logic-patterns.md +399 -0
- package/.github/skills/feature-generator/references/state-library-patterns.md +464 -0
- package/.github/skills/figma-ui-mapper/README.md +34 -0
- package/.github/skills/figma-ui-mapper/SKILL.md +101 -0
- package/.github/skills/figma-ui-mapper/assets/templates/component-map-template.md +30 -0
- package/.github/skills/figma-ui-mapper/assets/templates/generation-report-template.md +49 -0
- package/.github/skills/figma-ui-mapper/metadata.yaml +22 -0
- package/.github/skills/figma-ui-mapper/references/android.md +11 -0
- package/.github/skills/figma-ui-mapper/references/angular.md +18 -0
- package/.github/skills/figma-ui-mapper/references/capacitor.md +18 -0
- package/.github/skills/figma-ui-mapper/references/common.md +23 -0
- package/.github/skills/figma-ui-mapper/references/expo.md +18 -0
- package/.github/skills/figma-ui-mapper/references/flutter.md +18 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/angular/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/capacitor/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/expo/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/flutter/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/ionic/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/kotlin-multiplatform/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/native-android/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/native-ios/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/next/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/nuxt/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/react/rules.md +19 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/react-native/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/svelte/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/vue/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/ionic.md +18 -0
- package/.github/skills/figma-ui-mapper/references/ios.md +11 -0
- package/.github/skills/figma-ui-mapper/references/kotlin-multiplatform.md +18 -0
- package/.github/skills/figma-ui-mapper/references/native-android.md +18 -0
- package/.github/skills/figma-ui-mapper/references/native-ios.md +18 -0
- package/.github/skills/figma-ui-mapper/references/next.md +18 -0
- package/.github/skills/figma-ui-mapper/references/nuxt.md +18 -0
- package/.github/skills/figma-ui-mapper/references/react-figma-guideline.md +21 -0
- package/.github/skills/figma-ui-mapper/references/react-native.md +18 -0
- package/.github/skills/figma-ui-mapper/references/react.md +18 -0
- package/.github/skills/figma-ui-mapper/references/script-usage.md +46 -0
- package/.github/skills/figma-ui-mapper/references/svelte.md +18 -0
- package/.github/skills/figma-ui-mapper/references/vue.md +18 -0
- package/.github/skills/figma-ui-mapper/references/workflow.md +74 -0
- package/.github/skills/figma-ui-mapper/scripts/generate-page-through-figma.js +635 -0
- package/.github/skills/figma-ui-mapper/scripts/generate-react-page-through-figma.js +4 -0
- package/.github/skills/frontend-task-breakdown/SKILL.md +734 -0
- package/.github/skills/md-file-converter/SKILL.md +291 -0
- package/.github/skills/md-file-converter/config/formats.json +114 -0
- package/.github/skills/md-file-converter/extractors/extract-docx.sh +120 -0
- package/.github/skills/md-file-converter/extractors/extract-eml.py +108 -0
- package/.github/skills/md-file-converter/extractors/extract-pdf.sh +97 -0
- package/.github/skills/md-file-converter/extractors/extract-pptx.py +143 -0
- package/.github/skills/md-file-converter/extractors/extract-xlsx.py +175 -0
- package/.github/skills/md-file-converter/scripts/check-tools.sh +47 -0
- package/.github/skills/md-file-converter/scripts/extract-archive.sh +83 -0
- package/.github/skills/md-file-converter/scripts/extract-ocr.sh +77 -0
- package/.github/skills/md-file-converter/scripts/get-metadata.sh +57 -0
- package/.github/skills/md-file-converter/templates/output.md +73 -0
- package/.github/skills/md-file-converter/templates/unsupported-object.md +6 -0
- package/.github/skills/mockAPI-contract-generator/SKILL.md +607 -0
- package/.github/skills/test-case-validator/CHANGELOG.md +155 -0
- package/.github/skills/test-case-validator/CONFIGURATION.md +130 -0
- package/.github/skills/test-case-validator/EXAMPLES.md +50 -0
- package/.github/skills/test-case-validator/QUALITY_SCORING.md +98 -0
- package/.github/skills/test-case-validator/README.md +111 -0
- package/.github/skills/test-case-validator/REPORT_TEMPLATE.md +116 -0
- package/.github/skills/test-case-validator/SKILL.md +209 -0
- package/.github/skills/test-case-validator/SYSTEM_PROMPT.md +127 -0
- package/.github/skills/test-case-validator/TRACEABILITY_ENGINE.md +152 -0
- package/.github/skills/test-case-validator/VALIDATION_RULES.md +411 -0
- package/.github/skills/test-case-validator/config/default.config.yaml +62 -0
- package/.github/skills/test-case-validator/docs/ARCHITECTURE.md +66 -0
- package/.github/skills/test-case-validator/docs/EXTENDING.md +85 -0
- package/.github/skills/test-case-validator/docs/FAQ.md +88 -0
- package/.github/skills/test-case-validator/docs/VERSIONING.md +55 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-brd.md +43 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-test-cases.csv +9 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-user-story.md +29 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/output-report.md +303 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/input-spec.md +43 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/input-test-files-index.md +56 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/output-report.md +274 -0
- package/.github/skills/test-case-validator/prompts/00-applicability-resolution.md +61 -0
- package/.github/skills/test-case-validator/prompts/01-extraction-user-story.md +58 -0
- package/.github/skills/test-case-validator/prompts/02-extraction-brd.md +64 -0
- package/.github/skills/test-case-validator/prompts/03-extraction-test-cases.md +92 -0
- package/.github/skills/test-case-validator/prompts/04-traceability-matrix.md +41 -0
- package/.github/skills/test-case-validator/prompts/05-coverage-analysis.md +46 -0
- package/.github/skills/test-case-validator/prompts/06-missing-scenario-detection.md +32 -0
- package/.github/skills/test-case-validator/prompts/07-duplicate-detection.md +36 -0
- package/.github/skills/test-case-validator/prompts/08-risk-analysis.md +39 -0
- package/.github/skills/test-case-validator/prompts/09-gated-wiring-validation.md +53 -0
- package/.github/skills/test-case-validator/prompts/10-observation-report.md +56 -0
- package/.github/skills/test-case-validator/prompts/11-final-report-assembly.md +57 -0
- package/.github/skills/test-case-validator/templates/coverage-table.md +25 -0
- package/.github/skills/test-case-validator/templates/executive-summary.md +38 -0
- package/.github/skills/test-case-validator/templates/final-summary-block.md +87 -0
- package/.github/skills/test-case-validator/templates/observation-entry.md +41 -0
- package/.github/skills/test-case-validator/templates/rtm-row.md +25 -0
- package/.github/skills/validation-engine-builder/README.md +50 -0
- package/.github/skills/validation-engine-builder/SKILL.md +241 -0
- package/.github/skills/validation-engine-builder/references/core.md +158 -0
- package/.github/skills/validation-engine-builder/references/platform-angular.md +166 -0
- package/.github/skills/validation-engine-builder/references/platform-flutter.md +118 -0
- package/.github/skills/validation-engine-builder/references/platform-react-native.md +170 -0
- package/.github/skills/validation-engine-builder/references/platform-react.md +94 -0
- package/.github/skills/validation-engine-builder/scripts/apply-to-project.js +80 -0
- package/.github/skills/validation-engine-builder/scripts/generate-angular.js +271 -0
- package/.github/skills/validation-engine-builder/scripts/generate-flutter.js +298 -0
- package/.github/skills/validation-engine-builder/scripts/generate-react-native.js +217 -0
- package/.github/skills/validation-engine-builder/scripts/generate-react.js +163 -0
- package/.github/skills/validation-engine-builder/scripts/install-deps.js +53 -0
- package/.github/skills/validation-engine-builder/scripts/shared/detect-platform.js +68 -0
- package/.github/skills/validation-engine-builder/scripts/shared/npm-deps.js +103 -0
- package/.github/skills/validation-engine-builder/scripts/shared/rhf-hook.js +43 -0
- package/.github/skills/validation-engine-builder/scripts/shared/ts-messages-regex.js +62 -0
- package/.github/skills/validation-engine-builder/scripts/shared/util.js +44 -0
- package/.github/skills/validation-engine-builder/scripts/shared/yup-schemas.js +80 -0
- package/.github/skills/validation-engine-builder/template.md +73 -0
- package/.github/skills/validation-engine-builder/templates/messages.json +22 -0
- package/.github/skills/validation-engine-builder/templates/regex.json +16 -0
- package/.github/skills/validation-engine-builder/templates/schemas.json +48 -0
- package/bin/index.js +145 -0
- package/package.json +22 -0
- package/readme.md +0 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Error Handling Playbook
|
|
2
|
+
|
|
3
|
+
Applies to all platforms and approaches unless tagged otherwise.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Public Key Retrieval Failure (Approaches 1, 2, 4, 5, 6)
|
|
8
|
+
|
|
9
|
+
- Retry according to configured policy (default: 3 attempts).
|
|
10
|
+
- If still unavailable after all retries, block all encrypted API calls.
|
|
11
|
+
- Return a deterministic error message to the caller — no sensitive data.
|
|
12
|
+
- Log the failure reason at debug level only (no key material in the log).
|
|
13
|
+
|
|
14
|
+
## Missing Request Key Mapping (Approaches 4, 5, 6 — React Native production)
|
|
15
|
+
|
|
16
|
+
- Treat as non-recoverable for that response.
|
|
17
|
+
- Attempt a controlled key refresh for the active key service (RSA or EC, depending on `ENCRYPTION_MODEL`) if configuration allows.
|
|
18
|
+
- Fail securely without leaking partial decryption data.
|
|
19
|
+
- Trigger auth/session recovery policy at the application level.
|
|
20
|
+
|
|
21
|
+
## Invalid Auth Tag (Approaches 1, 4, 5, 6 — AES-GCM only)
|
|
22
|
+
|
|
23
|
+
- Assume tampering or key mismatch — do not silently ignore.
|
|
24
|
+
- Invalidate the active session immediately.
|
|
25
|
+
- Trigger force logout or equivalent session reset policy.
|
|
26
|
+
- Never surface the raw decryption error to the user — show a generic message.
|
|
27
|
+
|
|
28
|
+
For EC integrations, check these mismatch classes before changing algorithms:
|
|
29
|
+
|
|
30
|
+
- Per-request EC was implemented but the backend actually expects a session-init EC flow.
|
|
31
|
+
- HKDF `info` string is wrong.
|
|
32
|
+
- Base64url fields (`server_ecdh_pub`, `salt`) were decoded without normalizing `-`, `_`, and padding.
|
|
33
|
+
- AAD bytes do not exactly match the backend's canonical JSON.
|
|
34
|
+
- The `device_id` used for AAD or HKDF context does not match the session-init `device_id`.
|
|
35
|
+
- Response payload is standard base64 but the client decoded it as base64url.
|
|
36
|
+
- Response IV source is wrong (`iv` field vs `nonce` vs embedded payload bytes).
|
|
37
|
+
|
|
38
|
+
### EC Session-Init Auth Tag Mismatch Checklist
|
|
39
|
+
|
|
40
|
+
- Verify the client sends the real generated `client_ecdh_pub` from the same ECDH instance used for key derivation.
|
|
41
|
+
- Verify whether `encrypted_shared_aes_key` must be decrypted or ignored.
|
|
42
|
+
- Verify both required APIs are called in the correct order when the backend expects session init plus token creation.
|
|
43
|
+
- Log the exact AAD string sent into AES-GCM.
|
|
44
|
+
- Log which `device_id` is used in session init, HKDF context, and encrypted API calls.
|
|
45
|
+
- Log the exact nonce string, nonce length, and the backend's expected nonce format.
|
|
46
|
+
- Log whether the encrypted response contains `+` or `/` to distinguish standard base64 from base64url.
|
|
47
|
+
- Confirm whether the nonce is raw UTF-8, base64-encoded IV bytes, or only a session identifier.
|
|
48
|
+
|
|
49
|
+
### React Native / Hermes Practical Checks
|
|
50
|
+
|
|
51
|
+
- If base64 output differs from a reference Flutter or backend client, verify the JS encoder path. A safe helper using `btoa()` may be more reliable than assuming `Buffer.toString('base64')` behavior.
|
|
52
|
+
- If AES-GCM inputs are sliced out of a larger buffer and native crypto keeps failing unexpectedly, test with copied byte arrays instead of shared typed-array views.
|
|
53
|
+
|
|
54
|
+
### Resolution Code for Common EC Failures
|
|
55
|
+
|
|
56
|
+
**Base64url normalization** — apply to `server_ecdh_pub`, `salt`, and any base64url field before decoding:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
function urlSafeBase64ToStd(input: string): string {
|
|
60
|
+
const normalized = input.replace(/-/g, '+').replace(/_/g, '/');
|
|
61
|
+
return normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '=');
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Hermes-safe base64 encoding** — use instead of `Buffer.toString('base64')`:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
function toBase64(buf: Uint8Array): string {
|
|
69
|
+
const bytes = new Uint8Array(buf);
|
|
70
|
+
let binary = '';
|
|
71
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
72
|
+
binary += String.fromCharCode(bytes[i]);
|
|
73
|
+
}
|
|
74
|
+
return btoa(binary);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**byteCopy for Buffer views** — use before passing sliced buffers to quick-crypto:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
function byteCopy(buf: Buffer | Uint8Array, start?: number, end?: number): Uint8Array {
|
|
82
|
+
const view = start !== undefined ? buf.slice(start, end) : buf;
|
|
83
|
+
return new Uint8Array(view);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Canonical AAD builder** — keys must be alphabetically sorted:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
function buildSessionAad(keyId: string, deviceId: string, apiVersion = 'v1'): Buffer {
|
|
91
|
+
return Buffer.from(
|
|
92
|
+
`{"api_ver":"${apiVersion}","device_id":"${deviceId}","key_id":"${keyId}"}`,
|
|
93
|
+
'utf8',
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Dead Code Cleanup Warning
|
|
99
|
+
|
|
100
|
+
After debugging encryption issues, remove these probe/debug functions if they were added during troubleshooting:
|
|
101
|
+
- `unwrapSessionKey` — only needed if the backend's `encrypted_shared_aes_key` must be decrypted
|
|
102
|
+
- `tryAesGcmDecrypt` — debugging helper for probing multiple HKDF variants
|
|
103
|
+
- `hkdfExpandOnly`, `hkdfSwapped` — alternative KDF implementations tried during debugging
|
|
104
|
+
- Multi-strategy `sessionDecrypt` probe loops — once the correct IV/AAD combination is confirmed, hardcode it and remove the loop
|
|
105
|
+
- Any `console.log` that outputs decrypted plaintext, shared secrets, or AES key hex
|
|
106
|
+
|
|
107
|
+
## Decryption Parse Failure (Approaches 4, 5)
|
|
108
|
+
|
|
109
|
+
- Catch all parsing exceptions individually.
|
|
110
|
+
- Return a sanitized error object to the caller.
|
|
111
|
+
- Never log the plaintext payload or any key material.
|
|
112
|
+
|
|
113
|
+
## Storage Failure — Keychain (Approaches 4, 5)
|
|
114
|
+
|
|
115
|
+
- If a keychain write or read fails, abort the sensitive operation.
|
|
116
|
+
- Surface an actionable error for retry.
|
|
117
|
+
- Do not fall back to insecure storage (`AsyncStorage`, `localStorage`, plain files).
|
|
118
|
+
- Treat a missing token that should exist as a session-expired state — force re-login.
|
|
119
|
+
|
|
120
|
+
## Shared Key Not Set (Approach 3)
|
|
121
|
+
|
|
122
|
+
- Fail at startup / first encrypt attempt with a clear configuration error.
|
|
123
|
+
- Do not send the plaintext payload as a fallback.
|
|
124
|
+
- Instruct the developer to set the shared key in the env/config file.
|
|
125
|
+
|
|
126
|
+
## Build Failure After File Generation
|
|
127
|
+
|
|
128
|
+
- Retry up to 3 times (setup.js `MAX_BUILD_RETRIES`).
|
|
129
|
+
- After 3 failures, print the first 5 lines of build stderr and exit with code 4.
|
|
130
|
+
- Do not delete generated files on build failure — allow the developer to inspect them.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Android C++ / CMake / Ninja Build Failures (Approaches 4, 6 — After Installing Crypto Packages)
|
|
135
|
+
|
|
136
|
+
### Symptom
|
|
137
|
+
`ninja: build stopped: subcommand failed` during Android build, or CMake errors referencing `NitroModules` or `autolinking.cpp`.
|
|
138
|
+
|
|
139
|
+
### Root Cause
|
|
140
|
+
`react-native-quick-crypto` >= 1.0 depends on `react-native-nitro-modules` as a **peer dependency**. It is NOT auto-installed reliably.
|
|
141
|
+
|
|
142
|
+
### Resolution
|
|
143
|
+
1. Install missing peer deps: `npm install react-native-nitro-modules react-native-worklets`
|
|
144
|
+
2. Clean Android build cache:
|
|
145
|
+
```powershell
|
|
146
|
+
Remove-Item "android\app\.cxx","android\app\build","android\build" -Recurse -Force
|
|
147
|
+
cd android; .\gradlew.bat --stop; .\gradlew.bat clean
|
|
148
|
+
```
|
|
149
|
+
3. Rebuild: `.\gradlew.bat app:installDebug`
|
|
150
|
+
4. If still failing: verify NDK version in `android/build.gradle` matches an installed NDK, and CMake 3.22.1 is installed in Android SDK.
|
|
151
|
+
|
|
152
|
+
| Error message | Cause | Fix |
|
|
153
|
+
|---|---|---|
|
|
154
|
+
| `ninja: build stopped: subcommand failed` | Missing/wrong `react-native-nitro-modules` | `npm install react-native-nitro-modules@^0.35.4` |
|
|
155
|
+
| `CMake Error: could not find NitroModules` | Nitro modules not installed | `npm install react-native-nitro-modules` |
|
|
156
|
+
| `autolinking.cpp` build failure | Stale .cxx cache after adding native modules | Delete `.cxx` dir + rebuild |
|
|
157
|
+
| `error: use of undeclared identifier` (C++) | NDK version mismatch | Delete `.cxx` dir + check NDK version |
|
|
158
|
+
|
|
159
|
+
### Stale .cxx Cache After Native Module Changes
|
|
160
|
+
|
|
161
|
+
- **Symptom:** Build worked before but fails after adding/removing/upgrading a native module.
|
|
162
|
+
- **Cause:** CMake caches the previous native module list in `android/app/.cxx/`.
|
|
163
|
+
- **Resolution:** Delete `android/app/.cxx/` and rebuild. Required every time a package with C++ native code is installed or upgraded.
|
|
164
|
+
|
|
165
|
+
### Missing NDK or Wrong Version
|
|
166
|
+
|
|
167
|
+
- **Symptom:** CMake error about missing NDK or toolchain.
|
|
168
|
+
- **Cause:** `ndkVersion` in `android/build.gradle` doesn't match any installed NDK.
|
|
169
|
+
- **Resolution:** Install the correct NDK version via Android SDK Manager, or update `ndkVersion` to match what's installed.
|
|
170
|
+
|
|
171
|
+
### react-native-quick-base64 Version Mismatch
|
|
172
|
+
|
|
173
|
+
- **Symptom:** Runtime crash or missing method error related to base64.
|
|
174
|
+
- **Cause:** quick-crypto lists `react-native-quick-base64@>=2.1.0` as a peer dep. Version conflicts can cause issues.
|
|
175
|
+
- **Resolution:** `npm ls react-native-quick-base64` — if missing or wrong version, install explicitly.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## React Native Native Mode — Additional Failure Paths (Approach 5)
|
|
180
|
+
|
|
181
|
+
### Native Module Not Registered
|
|
182
|
+
|
|
183
|
+
- **Symptom:** `NativeModules.NativeCryptoModule` is `undefined` at runtime.
|
|
184
|
+
- **Cause:** Package not registered in `MainApplication` (Android) or bridge file not linked in Xcode (iOS).
|
|
185
|
+
- **Resolution:** Verify registration, run `pod install`, clean build, and restart the app.
|
|
186
|
+
- **Fail behavior:** Block all encrypted calls; surface a clear setup error, not a cryptic JS crash.
|
|
187
|
+
|
|
188
|
+
### Keychain / Token Storage Unavailable
|
|
189
|
+
|
|
190
|
+
- **Symptom:** `react-native-keychain` read/write throws an error or returns `null` for a token that should exist.
|
|
191
|
+
- **Common cause:** Biometric policy change, device security policy reset, first run before any token was stored, or keychain access group misconfiguration.
|
|
192
|
+
- **Resolution:** Treat as a session-expired state. Force the user to log in again to re-establish token storage.
|
|
193
|
+
- **Fail behavior:** Show a clear "session expired, please log in" message. Do not silently proceed with a missing token.
|
|
194
|
+
|
|
195
|
+
### Native Encrypt / Decrypt Rejection
|
|
196
|
+
|
|
197
|
+
- **Symptom:** Native module `reject` callback fires with an error code (`ERR_ENCRYPT`, `ERR_DECRYPT`, `ERR_RSA_ENCRYPT`).
|
|
198
|
+
- **Common causes:** Invalid key length (must be 32 bytes for AES-256), corrupted base64 input, IV length mismatch.
|
|
199
|
+
- **Resolution:** Validate all inputs on the JS side before calling the native module. Log the error code but never the key material or plaintext.
|
|
200
|
+
- **Fail behavior:** Treat as a non-recoverable crypto error for that request. Do not retry with different parameters.
|
|
201
|
+
|
|
202
|
+
### AES Key Lifecycle Mismatch
|
|
203
|
+
|
|
204
|
+
- **Symptom:** Decryption succeeds but produces garbage; auth tag verification fails.
|
|
205
|
+
- **Common cause:** JS interceptor passed a different key into `decryptAES` than was used in `encryptAES`, or the request map entry was cleared before the response arrived.
|
|
206
|
+
- **Resolution:** Ensure the request map stores the exact `aesKeyBase64` used for encryption and passes it unchanged into decryption. Clear only after decryption completes.
|
|
207
|
+
|
|
208
|
+
### KEY_ID_NOT_FOUND — Retry Policy
|
|
209
|
+
|
|
210
|
+
- **Trigger:** Server returns HTTP 401 with `error.code = "KEY_ID_NOT_FOUND"`.
|
|
211
|
+
- **Action:** Call `invalidateRsaKey()`, then retry the original request **exactly once**.
|
|
212
|
+
- **Fail behavior:** If the retry also returns `KEY_ID_NOT_FOUND`, surface a generic error to the user and stop. Never enter a retry loop.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Angular-Specific (Approaches 1, 2, 3)
|
|
217
|
+
|
|
218
|
+
- Interceptor errors must call `catchError` and return `next.handle(req)` (original request, unencrypted) — never block the HTTP pipeline.
|
|
219
|
+
- `from(this.encryptionService.encrypt(...))` failures must be caught before `switchMap` — do not let them propagate as unhandled Observable errors.
|
|
220
|
+
|
|
221
|
+
## Node.js Middleware (Approaches 2, 3)
|
|
222
|
+
|
|
223
|
+
- Middleware catch block must call `next()` (not `next(err)`) to avoid Express error handler interception that could expose stack traces.
|
|
224
|
+
- Never re-throw inside the middleware.
|
|
225
|
+
|
|
226
|
+
## Android OkHttp Interceptor (Approaches 2, 3)
|
|
227
|
+
|
|
228
|
+
- `chain.proceed(original)` must be called in the catch block to ensure the original request is forwarded even when encryption fails.
|
|
229
|
+
- Never call `chain.proceed` more than once per request.
|
|
230
|
+
|
|
231
|
+
## iOS URLRequest Mutator (Approaches 1, 2, 3)
|
|
232
|
+
|
|
233
|
+
- Return the original `request` unmodified on any failure — never return `nil` or a partially mutated request.
|
|
234
|
+
- All Swift throws must be caught within the function; do not propagate to the caller.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Platform: Flutter (Dart)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Flutter uses a **Dio Interceptor** (`EncryptionInterceptor`) to transparently encrypt request bodies.
|
|
6
|
+
Crypto operations use the `encrypt` + `pointycastle` packages.
|
|
7
|
+
Supports Approaches 2 and 3 only (Approach 1 requires browser Web Crypto, unavailable in Dart).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## File Naming Convention
|
|
12
|
+
|
|
13
|
+
| Role | Path |
|
|
14
|
+
|----------------|-----------------------------------------------------------------|
|
|
15
|
+
| Config | `lib/core/encryption/encryption_config.dart` |
|
|
16
|
+
| Service | `lib/core/encryption/encryption_service.dart` |
|
|
17
|
+
| Interceptor | `lib/core/encryption/encryption_interceptor.dart` |
|
|
18
|
+
| Module wiring | `lib/core/network/dio_client.dart` |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Module Wiring (Dio)
|
|
23
|
+
|
|
24
|
+
```dart
|
|
25
|
+
// In dio_client.dart
|
|
26
|
+
Dio buildDio() {
|
|
27
|
+
final dio = Dio();
|
|
28
|
+
dio.interceptors.add(EncryptionInterceptor());
|
|
29
|
+
// ENCRYPTION_INTERCEPTOR_PLACEHOLDER
|
|
30
|
+
return dio;
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The `// ENCRYPTION_INTERCEPTOR_PLACEHOLDER` comment is the marker used by install.js `patchFile()`.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Interceptor Pattern (Dart)
|
|
39
|
+
|
|
40
|
+
```dart
|
|
41
|
+
class EncryptionInterceptor extends Interceptor {
|
|
42
|
+
@override
|
|
43
|
+
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
|
44
|
+
if (!EncryptionConfig.encryptionEnabled) return handler.next(options);
|
|
45
|
+
if (EncryptionConfig.excludeUrls.any((prefix) => options.path.startsWith(prefix))) {
|
|
46
|
+
return handler.next(options);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
final body = options.data;
|
|
50
|
+
if (body == null || body is FormData) return handler.next(options);
|
|
51
|
+
final plaintext = body is String ? body : jsonEncode(body);
|
|
52
|
+
final encrypted = EncryptionService.encrypt(plaintext);
|
|
53
|
+
if (encrypted != null) options.data = encrypted;
|
|
54
|
+
} catch (_) {}
|
|
55
|
+
handler.next(options);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## URL Exclusion Pattern
|
|
63
|
+
|
|
64
|
+
```dart
|
|
65
|
+
if (EncryptionConfig.excludeUrls.any((prefix) => options.path.startsWith(prefix))) {
|
|
66
|
+
return handler.next(options);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Dependencies (pubspec.yaml)
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
dependencies:
|
|
76
|
+
encrypt: ^5.0.0
|
|
77
|
+
pointycastle: ^3.7.0
|
|
78
|
+
dio: ^5.0.0
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
flutter pub get
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Key Derivation (Approach 3 — Dart)
|
|
88
|
+
|
|
89
|
+
```dart
|
|
90
|
+
import 'package:pointycastle/digests/sha256.dart';
|
|
91
|
+
|
|
92
|
+
Key deriveKey(String sharedKey) {
|
|
93
|
+
final digest = SHA256Digest();
|
|
94
|
+
final bytes = Uint8List.fromList(utf8.encode(sharedKey));
|
|
95
|
+
return Key(digest.process(bytes));
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Build Command
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
flutter build apk --release
|
|
105
|
+
```
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# Platform: iOS (Swift)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
iOS uses a **URLRequest mutator** (`EncryptionInterceptor`) called before requests are dispatched.
|
|
6
|
+
Supports Approaches 1, 2, 3 using **CryptoKit** (built-in, Swift 5.4+) or **CommonCrypto**.
|
|
7
|
+
|
|
8
|
+
> File locations are determined at runtime by `assets/platform-matrix.json` for this platform.
|
|
9
|
+
> Do not hardcode file paths in skill instructions or generated code.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## File Naming Convention
|
|
14
|
+
|
|
15
|
+
| Role | Path |
|
|
16
|
+
|----------------|----------------------------------------------------------|
|
|
17
|
+
| Config | `Sources/Encryption/EncryptionConfig.swift` |
|
|
18
|
+
| Service | `Sources/Encryption/EncryptionService.swift` |
|
|
19
|
+
| Interceptor | `Sources/Encryption/EncryptionInterceptor.swift` |
|
|
20
|
+
| Module wiring | `Sources/Network/NetworkManager.swift` |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## MANDATORY INTERACTIVE PROMPT SEQUENCE (Fresh Implementation)
|
|
25
|
+
|
|
26
|
+
> **This section applies whenever no existing centralized `EncryptionService` is found in the project.**
|
|
27
|
+
> **It is not optional. It is not skippable. It runs before Step 3 of the skill and before any file is generated.**
|
|
28
|
+
> The agent MUST ask each question below, wait for the user's answer, and record the response before proceeding.
|
|
29
|
+
> Do NOT infer, assume, or pre-fill any answer. Do NOT proceed to implementation until every question is answered.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
### Prompt 1 — Encryption approach
|
|
34
|
+
|
|
35
|
+
Ask the user exactly this question and wait for their answer:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
Which encryption approach should be used for this iOS project?
|
|
39
|
+
|
|
40
|
+
1 — RSA-OAEP + AES-256-GCM (asymmetric hybrid; requires RSA public key PEM)
|
|
41
|
+
2 — RSA + AES-256-CBC (asymmetric hybrid; requires RSA public key PEM)
|
|
42
|
+
3 — AES-256-CBC shared key (symmetric; requires a shared secret string of 32+ characters)
|
|
43
|
+
|
|
44
|
+
Enter 1, 2, or 3:
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- Do not proceed until the user answers with 1, 2, or 3.
|
|
48
|
+
- Do not default to any option if the user does not answer immediately.
|
|
49
|
+
- If the user answers outside 1–3, re-ask.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
### Prompt 2 — Key material
|
|
54
|
+
|
|
55
|
+
After Prompt 1 is answered, ask the appropriate follow-up:
|
|
56
|
+
|
|
57
|
+
**If approach 1 or 2:**
|
|
58
|
+
```
|
|
59
|
+
Provide the RSA public key PEM that will be used to encrypt the AES session key.
|
|
60
|
+
The server must hold the corresponding private key to decrypt it.
|
|
61
|
+
|
|
62
|
+
Paste the PEM (begins with -----BEGIN PUBLIC KEY----- or -----BEGIN RSA PUBLIC KEY-----):
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**If approach 3:**
|
|
66
|
+
```
|
|
67
|
+
Provide the shared secret string (minimum 32 characters).
|
|
68
|
+
This exact same secret must also be configured on the server.
|
|
69
|
+
|
|
70
|
+
Enter shared secret:
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**If the user says they want to reuse an existing Keychain entry:**
|
|
74
|
+
```
|
|
75
|
+
Provide the Keychain account name under service 'com.protect.common-storage'
|
|
76
|
+
that holds the key material:
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- Do not proceed until the user provides actual key material.
|
|
80
|
+
- Do not accept a placeholder string (e.g. "MY_KEY", "test", "12345678901234567890123456789012").
|
|
81
|
+
- Validate: shared keys must be ≥ 32 characters; PEM strings must contain valid PEM headers.
|
|
82
|
+
- If validation fails, re-ask immediately.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### Prompt 3 — Backend decryption scope
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
Will a backend server decrypt the data encrypted by this service?
|
|
90
|
+
|
|
91
|
+
yes — server decryption is in scope; I confirm the server uses the same algorithm and key
|
|
92
|
+
no — local/demo only; encryption and decryption happen within this app only
|
|
93
|
+
|
|
94
|
+
Enter yes or no:
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- If `yes`: confirm the server's expected payload format matches the approach (see approach reference files).
|
|
98
|
+
- If `no`: the generated `EncryptionConfig` must include a comment stating the scope is local/demo only.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### Prompt 4 — HMAC signing
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
Should requests include an HMAC signature for integrity verification?
|
|
106
|
+
|
|
107
|
+
yes — HMAC-SHA256 signing enabled (requires ENCRYPTION_HMAC_SECRET environment variable at runtime)
|
|
108
|
+
no — no HMAC signing
|
|
109
|
+
|
|
110
|
+
Enter yes or no:
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
### Prompt 5 — URL exclusions
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
Are there any URL prefixes that should NOT be encrypted?
|
|
119
|
+
(e.g. /api/health, /api/upload for multipart endpoints)
|
|
120
|
+
|
|
121
|
+
Enter comma-separated prefixes, or press Enter to skip:
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
> **Only after all five prompts are answered may implementation proceed.**
|
|
127
|
+
> Record all answers. Pass them into the config object at Step 6.
|
|
128
|
+
> Never silently reuse a previous session's answers for a new fresh implementation.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## MANDATORY Pre-Implementation Rules
|
|
133
|
+
|
|
134
|
+
> **HARD STOP. These rules are non-negotiable and sequential.**
|
|
135
|
+
> **No encryption, decryption, key-management, or Keychain code may be written until every rule below is fully satisfied and explicitly confirmed.**
|
|
136
|
+
> If any rule cannot be satisfied from available information, stop immediately and ask the user for the missing detail. Do not proceed with a default, a guess, or a "placeholder" value.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### Rule 1 — Algorithm and approach must be explicitly confirmed before any code is written
|
|
141
|
+
|
|
142
|
+
**Never assume, infer, or default to any encryption algorithm** (AES-GCM, AES-CBC, RSA-OAEP, etc.).
|
|
143
|
+
|
|
144
|
+
The approach (1, 2, or 3 as defined in the skill) **must be stated explicitly** by the user or present in a project-level definition before a single line of cryptographic code is generated.
|
|
145
|
+
|
|
146
|
+
**Prohibited behaviours:**
|
|
147
|
+
- Choosing AES-GCM because it is "modern" or "recommended"
|
|
148
|
+
- Choosing AES-CBC because an existing backend uses it
|
|
149
|
+
- Defaulting to approach 3 because no public key was provided
|
|
150
|
+
- Proceeding with a comment such as `// TODO: confirm algorithm` and writing code anyway
|
|
151
|
+
|
|
152
|
+
**Required action:** If the approach is not confirmed, stop and ask:
|
|
153
|
+
> "Which encryption approach should be used (1 = RSA-OAEP + AES-256-GCM, 2 = RSA + AES-256-CBC, 3 = AES-256-CBC shared key)? This must be confirmed before implementation."
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### Rule 2 — Key material source must be explicitly confirmed; random keys are forbidden for AES
|
|
158
|
+
|
|
159
|
+
**Never generate a random symmetric key** (`SymmetricKey(size: .bits256)`, `SecRandomCopyBytes`, or any equivalent) as the actual encryption key in any context where data must be consistently decrypted — by the server, another device, or a future app session.
|
|
160
|
+
|
|
161
|
+
A randomly generated key that exists only on the current device makes cross-party decryption impossible and corrupts any data already encrypted with a different session key.
|
|
162
|
+
|
|
163
|
+
**The only permitted uses of random material are:**
|
|
164
|
+
- Per-message nonces/IVs (random each encryption call — this is correct and required)
|
|
165
|
+
- Ephemeral session keys in hybrid encryption where the key itself is RSA-encrypted and transmitted (Approach 1/2 only, and only when the server can unwrap it)
|
|
166
|
+
|
|
167
|
+
**The actual root key must come from one of these explicitly confirmed sources:**
|
|
168
|
+
|
|
169
|
+
| Confirmed source | Approach | Condition |
|
|
170
|
+
|---|---|---|
|
|
171
|
+
| User-supplied `sharedKey` string | 3 | Same key is configured on the server; confirmed by user |
|
|
172
|
+
| User-supplied `publicKeyPem` | 1 or 2 | Server holds the matching private key; confirmed by user |
|
|
173
|
+
| Existing Keychain entry by account name | Any | The same key is known to the decrypting party; confirmed by user |
|
|
174
|
+
|
|
175
|
+
**If key material is not confirmed**, stop and ask:
|
|
176
|
+
> "What is the source of the encryption key? (shared secret string / RSA public key PEM / existing Keychain entry) This must be confirmed before implementation."
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
### Rule 3 — Scan for existing encryption and Keychain implementations before creating anything new
|
|
181
|
+
|
|
182
|
+
**Before writing any new file**, the centralized-module detection step (Step 2 of the skill) **must** be run against the project. If an existing `EncryptionService`, `CryptoService`, or equivalent is found:
|
|
183
|
+
|
|
184
|
+
- **Do not create a new file.** The existing service is the canonical implementation.
|
|
185
|
+
- **Do not modify the algorithm or key strategy** of the existing service unless the user explicitly requests a migration with a stated reason.
|
|
186
|
+
- **Do not create a parallel Keychain account** for the same logical key. Use the namespace already established in the project (`com.protect.common-storage`).
|
|
187
|
+
- Only generate the missing wiring layer (interceptor, config registration) around the existing service.
|
|
188
|
+
|
|
189
|
+
**Similarly, if a Keychain wrapper or storage abstraction already exists in the project:**
|
|
190
|
+
- Use it directly. Do not write new `SecItemAdd` / `SecItemCopyMatching` calls unless the existing abstraction is confirmed absent.
|
|
191
|
+
- Do not introduce a second service namespace or a duplicate account key for the same secret.
|
|
192
|
+
|
|
193
|
+
**Failure to scan first causes:**
|
|
194
|
+
- Duplicate `EncryptionService` files with different keys — data encrypted by one cannot be decrypted by the other
|
|
195
|
+
- Conflicting Keychain entries for the same logical secret
|
|
196
|
+
- Silent encryption failures at runtime when the wrong service is called
|
|
197
|
+
|
|
198
|
+
**Required action:** Run `detect.js` (or equivalent grep scan) and report findings before generating any file.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### Rule 4 — All new files must align with the existing project architecture; duplication is a defect
|
|
203
|
+
|
|
204
|
+
**Every generated file must conform to:**
|
|
205
|
+
- The existing folder structure (e.g. `Data/Security/` for service implementations)
|
|
206
|
+
- The naming conventions already present in the project
|
|
207
|
+
- The established Keychain service namespace: `com.protect.common-storage`
|
|
208
|
+
- The existing dependency set — do not introduce new third-party packages unless the approach explicitly requires one and no built-in alternative exists
|
|
209
|
+
|
|
210
|
+
**Duplication is not a workaround.** If the existing architecture cannot support the required encryption approach without modification, stop and raise the conflict to the user rather than creating a parallel structure alongside it.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
### Rule 5 — Backend decryption capability must be confirmed before client-side encryption is implemented
|
|
215
|
+
|
|
216
|
+
Encrypting data that the server cannot decrypt renders the API non-functional. Before writing any encryption code:
|
|
217
|
+
|
|
218
|
+
- Confirm the server's decryption algorithm, key format, and expected payload shape
|
|
219
|
+
- Confirm the server holds the correct key counterpart (private key for asymmetric; same shared key for symmetric)
|
|
220
|
+
- If backend confirmation is not available, the implementation must be explicitly scoped as a demo/local-only build with no server integration — this must be stated in code comments and config
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Pre-Implementation Validation Gate
|
|
225
|
+
|
|
226
|
+
> **All five items below must be checked BEFORE any code is written.**
|
|
227
|
+
> If any item cannot be checked, STOP and collect the missing information.
|
|
228
|
+
|
|
229
|
+
- [ ] **Algorithm confirmed** — approach (1, 2, or 3) stated explicitly by user or project definition; not assumed
|
|
230
|
+
- [ ] **Key material source confirmed** — shared key / RSA public key PEM / Keychain entry; exact source identified
|
|
231
|
+
- [ ] **Existing implementation scan completed** — `detect.js` or equivalent grep run; findings documented; no duplicate will be created
|
|
232
|
+
- [ ] **Architecture alignment verified** — target folder, naming convention, and Keychain namespace (`com.protect.common-storage`) checked against existing project files
|
|
233
|
+
- [ ] **Backend decryption confirmed** — server algorithm, key format, and payload shape agreed; or scope explicitly limited to local/demo use
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Centralized Module Detection (iOS-specific signals)
|
|
238
|
+
|
|
239
|
+
`detect.js` uses the following signals to determine whether a **centralized** encryption service already exists:
|
|
240
|
+
|
|
241
|
+
- A dedicated encryption service file that exports both `encrypt` and `decrypt` methods
|
|
242
|
+
- The file is the **only** encryption entry point (no ad-hoc encrypt/decrypt calls outside it)
|
|
243
|
+
- The interceptor or network layer already calls this service by reference
|
|
244
|
+
|
|
245
|
+
If these conditions are all true → `centralized: true` → **reuse automatically** (State A).
|
|
246
|
+
|
|
247
|
+
If encryption code exists but is scattered across multiple non-service files, or the service exists but the interceptor does not → `centralized: false` → **prompt user** (State B).
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Module Wiring
|
|
252
|
+
|
|
253
|
+
```swift
|
|
254
|
+
// In NetworkManager.swift
|
|
255
|
+
func makeRequest(_ url: URL, body: Data?) async throws -> (Data, URLResponse) {
|
|
256
|
+
var request = URLRequest(url: url)
|
|
257
|
+
request.httpMethod = "POST"
|
|
258
|
+
request.httpBody = body
|
|
259
|
+
request = EncryptionInterceptor.intercept(request: request)
|
|
260
|
+
// ENCRYPTION_INTERCEPTOR_PLACEHOLDER
|
|
261
|
+
return try await URLSession.shared.data(for: request)
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The `// ENCRYPTION_INTERCEPTOR_PLACEHOLDER` comment is the marker used by install.js `patchFile()`.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Interceptor Pattern (Swift)
|
|
270
|
+
|
|
271
|
+
```swift
|
|
272
|
+
struct EncryptionInterceptor {
|
|
273
|
+
static func intercept(request: URLRequest) -> URLRequest {
|
|
274
|
+
guard EncryptionConfig.encryptionEnabled else { return request }
|
|
275
|
+
guard let body = request.httpBody else { return request }
|
|
276
|
+
guard let plaintext = String(data: body, encoding: .utf8) else { return request }
|
|
277
|
+
guard let encrypted = EncryptionService.encrypt(plaintext) else { return request }
|
|
278
|
+
guard let encData = encrypted.data(using: .utf8) else { return request }
|
|
279
|
+
|
|
280
|
+
var modified = request
|
|
281
|
+
modified.httpBody = encData
|
|
282
|
+
return modified
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## URL Exclusion Pattern
|
|
290
|
+
|
|
291
|
+
```swift
|
|
292
|
+
let url = request.url?.absoluteString ?? ""
|
|
293
|
+
let excluded = EncryptionConfig.excludeUrls
|
|
294
|
+
if excluded.contains(where: { url.hasPrefix($0) }) {
|
|
295
|
+
return request
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Approach 1 (CryptoKit — AES-GCM)
|
|
302
|
+
|
|
303
|
+
> **Key source constraint:** `symmetricKey` must be derived from user-supplied key material only — a shared secret string hashed to 256 bits, or a session key unwrapped from an RSA-encrypted envelope. `SymmetricKey(size: .bits256)` (random key) **must never be used** as the production root key.
|
|
304
|
+
|
|
305
|
+
```swift
|
|
306
|
+
import CryptoKit
|
|
307
|
+
let sealedBox = try AES.GCM.seal(data, using: symmetricKey)
|
|
308
|
+
let combined = sealedBox.combined!
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Approach 3 (CommonCrypto — AES-CBC)
|
|
312
|
+
|
|
313
|
+
> **Key source constraint:** `keyBytes` must be derived from the user-supplied `sharedKey` (e.g. via SHA-256 hashing of the shared secret string). Random byte generation **must never be used** for the root key in any production or cross-party context.
|
|
314
|
+
|
|
315
|
+
```swift
|
|
316
|
+
import CommonCrypto
|
|
317
|
+
var outBytes = [UInt8](repeating: 0, count: dataIn.count + kCCBlockSizeAES128)
|
|
318
|
+
var numBytesEncrypted = 0
|
|
319
|
+
CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES),
|
|
320
|
+
CCOptions(kCCOptionPKCS7Padding), keyBytes, kCCKeySizeAES256,
|
|
321
|
+
ivBytes, dataIn, dataIn.count, &outBytes, outBytes.count, &numBytesEncrypted)
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Dependencies
|
|
327
|
+
|
|
328
|
+
- Approach 1: `CryptoKit` (built-in, iOS 13+)
|
|
329
|
+
- Approach 2: `CryptoSwift` via Swift Package Manager
|
|
330
|
+
- Approach 3: `CommonCrypto` (built-in)
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Build Command
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
xcodebuild -scheme YourScheme -configuration Debug build
|
|
338
|
+
```
|