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,506 @@
|
|
|
1
|
+
# Backend Alignment Specification
|
|
2
|
+
|
|
3
|
+
**Purpose:** This document is written by the frontend team for the backend team.
|
|
4
|
+
It defines exactly what the backend must implement so that the encrypted React Native
|
|
5
|
+
client can integrate without ambiguity.
|
|
6
|
+
|
|
7
|
+
This specification covers two key exchange models. Share **only the relevant section**
|
|
8
|
+
with the backend team based on the model selected.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## PART A — RSA Model (Key Wrapping)
|
|
13
|
+
|
|
14
|
+
The React Native app uses **envelope encryption** on every mutating API call
|
|
15
|
+
(POST / PUT / PATCH). Each request payload is encrypted with a one-time
|
|
16
|
+
AES-256-GCM key. That AES key is then wrapped with the server's RSA public key.
|
|
17
|
+
The backend must unwrap the AES key with its RSA private key, decrypt the payload,
|
|
18
|
+
process it, and — if the response also carries sensitive data — encrypt the response
|
|
19
|
+
in the same envelope format.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 2. Crypto Algorithms — Non-Negotiable
|
|
24
|
+
|
|
25
|
+
| Parameter | Required Value | Notes |
|
|
26
|
+
|------------------|------------------------------|-------------------------------------------------------|
|
|
27
|
+
| Symmetric cipher | AES-256-GCM | 256-bit key, authenticated mode |
|
|
28
|
+
| AES key size | 32 bytes | Generated fresh per request on the client |
|
|
29
|
+
| IV / Nonce size | 12 bytes | Generated fresh per request, never reused |
|
|
30
|
+
| Auth tag size | 16 bytes (128 bits) | Appended to ciphertext by the client |
|
|
31
|
+
| Asymmetric cipher| RSA-OAEP (mandatory) | PKCS1 v1.5 only for legacy systems with written approval — not acceptable for new deployments in 2026. |
|
|
32
|
+
| RSA key size | Minimum 2048 bits | 4096 recommended for new deployments |
|
|
33
|
+
| Hash for OAEP | SHA-256 | |
|
|
34
|
+
| Encoding | Base64 (standard, no line breaks) | All binary fields use Base64 |
|
|
35
|
+
|
|
36
|
+
> **Do not** negotiate algorithm parameters at runtime. Both sides must use the
|
|
37
|
+
> same agreed algorithm. If there is a mismatch, decryption will fail with an
|
|
38
|
+
> auth tag error.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 3. RSA Public Key Endpoint
|
|
43
|
+
|
|
44
|
+
The client fetches the RSA public key at startup (or when the cached key expires).
|
|
45
|
+
|
|
46
|
+
### 3.1 Request
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
GET /api/security/public-key
|
|
50
|
+
Authorization: Bearer <access_token> (if the route is authenticated)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 3.2 Response — 200 OK
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"public_key": "<PEM or Base64-encoded DER RSA public key>",
|
|
58
|
+
"key_id": "v1",
|
|
59
|
+
"expires_at": "2026-07-01T00:00:00Z"
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Field | Type | Required | Description |
|
|
64
|
+
|--------------|--------|----------|--------------------------------------------------------------|
|
|
65
|
+
| `public_key` | string | yes | PEM (`-----BEGIN PUBLIC KEY-----…`) or Base64 DER |
|
|
66
|
+
| `key_id` | string | yes | Version tag used for key rotation tracking |
|
|
67
|
+
| `expires_at` | string | no | ISO 8601 UTC. Client uses this to schedule key refresh. |
|
|
68
|
+
|
|
69
|
+
### 3.3 Format Contract
|
|
70
|
+
|
|
71
|
+
- PEM is **preferred** because it is self-describing.
|
|
72
|
+
- If Base64 DER is returned, communicate this to the frontend team explicitly.
|
|
73
|
+
- The key must be a **public key** (`BEGIN PUBLIC KEY`), not a certificate (`BEGIN CERTIFICATE`).
|
|
74
|
+
|
|
75
|
+
### 3.4 Error Responses
|
|
76
|
+
|
|
77
|
+
| HTTP Status | When to return |
|
|
78
|
+
|-------------|----------------------------------------|
|
|
79
|
+
| 401 | Token invalid or missing |
|
|
80
|
+
| 503 | Key temporarily unavailable (HSM down) |
|
|
81
|
+
|
|
82
|
+
The client retries with bounded back-off (default 3 attempts). If the key
|
|
83
|
+
cannot be fetched, the client **blocks** all encrypted calls and surfaces an error.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 4. Encrypted Request Contract
|
|
88
|
+
|
|
89
|
+
Every POST / PUT / PATCH body the client sends will have this shape. The original
|
|
90
|
+
JSON payload is replaced entirely.
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"data": "<Base64(ciphertext + auth_tag)>",
|
|
95
|
+
"key": "<Base64(RSA-OAEP encrypted AES key)>",
|
|
96
|
+
"iv": "<Base64(12-byte IV)>",
|
|
97
|
+
"nonce": "<appId>^<unix_timestamp_ms>.<random_hex_16>",
|
|
98
|
+
"key_id": "v1"
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 4.1 Field Descriptions
|
|
103
|
+
|
|
104
|
+
| Field | Type | Description |
|
|
105
|
+
|----------|--------|------------------------------------------------------------------------------------|
|
|
106
|
+
| `data` | string | Base64 of `AES-256-GCM(plaintext_json)`. The **last 16 bytes are the auth tag** — appended by the AES-GCM library automatically. |
|
|
107
|
+
| `key` | string | Base64 of `RSA-OAEP(aes_key_bytes)`. Encrypted with the public key returned by the key endpoint. |
|
|
108
|
+
| `iv` | string | Base64 of the 12-byte IV used for AES-GCM. |
|
|
109
|
+
| `nonce` | string | Anti-replay identifier. Format: `<appId>^<epoch_ms>.<16-char random hex>`. |
|
|
110
|
+
| `key_id` | string | Identifies which RSA key pair to use for unwrapping. Must match the key endpoint `key_id`. |
|
|
111
|
+
|
|
112
|
+
### 4.2 Step-by-Step Backend Decryption
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
1. Read `key_id` → select the matching RSA private key from secure storage.
|
|
116
|
+
2. Base64-decode `key` → decrypt with RSA-OAEP (SHA-256) using the private key → raw 32-byte AES key.
|
|
117
|
+
3. Base64-decode `iv` → 12-byte IV.
|
|
118
|
+
4. Base64-decode `data` → ciphertext_with_tag.
|
|
119
|
+
- Last 16 bytes = auth tag.
|
|
120
|
+
- Remaining bytes = ciphertext.
|
|
121
|
+
5. Decrypt with AES-256-GCM(key, iv, ciphertext, tag).
|
|
122
|
+
- If auth tag verification FAILS → return 400 (see Section 7).
|
|
123
|
+
6. Parse decrypted bytes as UTF-8 JSON → this is the original request payload.
|
|
124
|
+
7. Validate nonce (see Section 6).
|
|
125
|
+
8. Process the business logic with the original payload.
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 4.3 Content-Type
|
|
129
|
+
|
|
130
|
+
The client sends `Content-Type: application/json`. The encrypted envelope is still valid JSON.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 5. Encrypted Response Contract (Required When Response Contains Sensitive Data)
|
|
135
|
+
|
|
136
|
+
The backend **MUST** encrypt responses using the **same AES key and a freshly
|
|
137
|
+
generated IV** from the request envelope. There is no alternative key derivation.
|
|
138
|
+
|
|
139
|
+
### Why the same AES key?
|
|
140
|
+
|
|
141
|
+
The client stores `requestId → { aesKey, iv }` in memory for the lifetime of
|
|
142
|
+
that request. It expects to decrypt the response with the same `aesKey`. The
|
|
143
|
+
response must use a **new IV** (not the request IV) to avoid IV reuse.
|
|
144
|
+
|
|
145
|
+
### Response Envelope
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"data": "<Base64(ciphertext + auth_tag)>",
|
|
150
|
+
"iv": "<Base64(new 12-byte IV, generated server-side for this response)>",
|
|
151
|
+
"nonce": "<server_generated_nonce>"
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
| Field | Type | Description |
|
|
156
|
+
|---------|--------|---------------------------------------------------------------------------------|
|
|
157
|
+
| `data` | string | Base64 of `AES-256-GCM(response_json)`. Last 16 bytes are the auth tag. |
|
|
158
|
+
| `iv` | string | Base64 of a **new** 12-byte IV generated by the server for this response only. |
|
|
159
|
+
| `nonce` | string | Server-generated nonce for audit/logging. Not validated by client. |
|
|
160
|
+
|
|
161
|
+
> **Mandatory:** The server must generate a fresh IV for every response. Reusing
|
|
162
|
+
> the request IV with the same AES key is a critical security violation.
|
|
163
|
+
|
|
164
|
+
### Step-by-Step Server-Side Response Encryption
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
1. Retrieve the AES key decrypted from the incoming request's `key` field.
|
|
168
|
+
2. Generate a new cryptographically random 12-byte IV.
|
|
169
|
+
3. Serialize the response JSON to UTF-8 bytes.
|
|
170
|
+
4. Encrypt with AES-256-GCM(aes_key, new_iv, response_bytes) → ciphertext + auth_tag.
|
|
171
|
+
5. Concatenate: encrypted_data = ciphertext || auth_tag.
|
|
172
|
+
6. Return { data: base64(encrypted_data), iv: base64(new_iv), nonce: server_nonce }.
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 6. Nonce / Anti-Replay Validation
|
|
178
|
+
|
|
179
|
+
### Format
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
<appId>^<unix_timestamp_ms>.<16-char random hex>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Example: `com.myapp^1745189241337.a3f9c2e1d8b047f6`
|
|
186
|
+
|
|
187
|
+
### Backend Validation Rules
|
|
188
|
+
|
|
189
|
+
1. **Parse** the nonce: split on `^`, then split the right side on `.`.
|
|
190
|
+
2. **Timestamp check:** reject if `|server_time_ms - timestamp_ms| > 300000` (5-minute window).
|
|
191
|
+
3. **Uniqueness check (mandatory):** store each accepted nonce in Redis (or a distributed cache)
|
|
192
|
+
with TTL = replay window. A local in-process map is **not** acceptable in multi-instance deployments.
|
|
193
|
+
Reject immediately if the nonce already exists.
|
|
194
|
+
4. **App ID check** (optional): validate `appId` matches a registered client identifier.
|
|
195
|
+
|
|
196
|
+
> **Production requirement:** Nonce validation is **not optional**.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 7. Error Response Contract for Crypto Failures
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"error": {
|
|
205
|
+
"code": "DECRYPTION_FAILED",
|
|
206
|
+
"message": "Request payload could not be decrypted."
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Error Codes the Client Handles
|
|
212
|
+
|
|
213
|
+
| HTTP Status | `error.code` | Client Behaviour |
|
|
214
|
+
|-------------|------------------------|------------------------------------------------------------------------------------|
|
|
215
|
+
| 400 | `DECRYPTION_FAILED` | Surface generic error. Do NOT expose crypto details. |
|
|
216
|
+
| 400 | `INVALID_NONCE` | Surface generic error. Log for investigation. |
|
|
217
|
+
| 400 | `EXPIRED_NONCE` | Surface "request expired" message to user. |
|
|
218
|
+
| 401 | `KEY_ID_NOT_FOUND` | Fetch fresh RSA public key, then **retry the original request exactly once**. |
|
|
219
|
+
| 422 | `PAYLOAD_PARSE_ERROR` | Surface generic error. Do not retry automatically. |
|
|
220
|
+
|
|
221
|
+
> Never include the plaintext payload, raw cipher bytes, or key material in error responses.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## 8. RSA Key Rotation
|
|
226
|
+
|
|
227
|
+
1. Support **multiple active key pairs** simultaneously (minimum: current + previous).
|
|
228
|
+
2. Each key pair has a unique `key_id`.
|
|
229
|
+
3. When rotating: publish new public key, keep old private key active for a grace period (recommended: 24 hours).
|
|
230
|
+
4. The client identifies which key to use via the `key_id` field in the request.
|
|
231
|
+
5. On `KEY_ID_NOT_FOUND` (401), the client fetches a fresh public key and retries the request once.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## 9. HTTPS and SSL Pinning
|
|
236
|
+
|
|
237
|
+
- All endpoints **must** be served over HTTPS (TLS 1.2+; TLS 1.3 strongly recommended).
|
|
238
|
+
- **SSL pinning is strongly recommended** for high-security environments.
|
|
239
|
+
- Provide SHA-256 fingerprints of leaf and/or intermediate CA certificate to the frontend team.
|
|
240
|
+
- When rotating certificates, provide the new fingerprint **at least 2 weeks before** expiry.
|
|
241
|
+
- Pin both current and backup fingerprints on the client to avoid rotation outages.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 10. Endpoints That Accept Encrypted Payloads
|
|
246
|
+
|
|
247
|
+
| Method | Encrypted by Default | Override Possible |
|
|
248
|
+
|--------|----------------------|--------------------|
|
|
249
|
+
| POST | Yes | Yes (exclude list) |
|
|
250
|
+
| PUT | Yes | Yes (exclude list) |
|
|
251
|
+
| PATCH | Yes | Yes (exclude list) |
|
|
252
|
+
| GET | No | No |
|
|
253
|
+
| DELETE | No | No |
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 11. Backend Implementation Checklist
|
|
258
|
+
|
|
259
|
+
Hand this checklist to the backend developer. All items must be confirmed before
|
|
260
|
+
integration testing begins.
|
|
261
|
+
|
|
262
|
+
### Cryptography
|
|
263
|
+
|
|
264
|
+
- [ ] RSA-OAEP (SHA-256) private key decryption implemented.
|
|
265
|
+
- [ ] AES-256-GCM decryption with auth tag verification implemented.
|
|
266
|
+
- [ ] Auth tag failure returns `400 DECRYPTION_FAILED` without leaking details.
|
|
267
|
+
- [ ] Decryption logic covered by unit tests (valid case + tampered tag case).
|
|
268
|
+
|
|
269
|
+
### Key Management
|
|
270
|
+
|
|
271
|
+
- [ ] RSA key pair generated with minimum 2048-bit key (4096 preferred).
|
|
272
|
+
- [ ] Private key stored in a secrets manager or HSM — never in code or `.env` committed to version control.
|
|
273
|
+
- [ ] Public key endpoint (`GET /api/security/public-key`) implemented and returns `public_key` + `key_id`.
|
|
274
|
+
- [ ] `key_id` is stable and versioned (e.g., `v1`, `v2`).
|
|
275
|
+
- [ ] Key rotation plan documented with grace period defined.
|
|
276
|
+
|
|
277
|
+
### Nonce
|
|
278
|
+
|
|
279
|
+
- [ ] Nonce parsing implemented (format: `appId^timestamp.random`).
|
|
280
|
+
- [ ] Timestamp window check implemented (±5 minutes or agreed window).
|
|
281
|
+
- [ ] Nonce stored in **Redis** (or distributed cache) with TTL = replay window. Local in-process map is not acceptable in multi-instance deployments.
|
|
282
|
+
- [ ] Nonce uniqueness enforced: duplicate nonce rejected with `400 INVALID_NONCE`.
|
|
283
|
+
- [ ] `INVALID_NONCE` and `EXPIRED_NONCE` error codes returned correctly.
|
|
284
|
+
|
|
285
|
+
### Transport and SSL
|
|
286
|
+
|
|
287
|
+
- [ ] All endpoints served over HTTPS (TLS 1.2+, TLS 1.3 preferred).
|
|
288
|
+
- [ ] SSL certificate SHA-256 fingerprint(s) provided to the frontend team.
|
|
289
|
+
- [ ] Certificate rotation schedule documented and communicated.
|
|
290
|
+
- [ ] No crypto material appears in response bodies or server logs.
|
|
291
|
+
- [ ] Error responses follow the agreed `{ error: { code, message } }` shape.
|
|
292
|
+
|
|
293
|
+
### Response Encryption
|
|
294
|
+
|
|
295
|
+
- [ ] Response envelope format matches Section 5.
|
|
296
|
+
- [ ] Response encrypted with the **same AES key** from the incoming request.
|
|
297
|
+
- [ ] Response uses a **freshly generated IV** — never the request IV.
|
|
298
|
+
- [ ] Response auth tag verified by client confirmed during integration test.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## 12. Questions for the Backend Team to Answer Before Integration
|
|
303
|
+
|
|
304
|
+
Before the frontend begins wiring up the interceptor, the backend team must
|
|
305
|
+
confirm the following:
|
|
306
|
+
|
|
307
|
+
1. What is the exact URL for the public key endpoint?
|
|
308
|
+
2. Is the public key returned as PEM or Base64 DER?
|
|
309
|
+
3. What is the `key_id` value for the first key pair?
|
|
310
|
+
4. What is the `expires_at` value (or is it omitted)?
|
|
311
|
+
5. Is the public key endpoint authenticated (requires Bearer token)?
|
|
312
|
+
6. Should responses be encrypted? If yes, which endpoints? (Default rule: use same AES key as request, fresh IV per response.)
|
|
313
|
+
7. What is the agreed nonce replay window (default: 5 minutes)?
|
|
314
|
+
8. Is SSL pinning enabled? If yes, provide the SHA-256 leaf certificate fingerprint and the backup fingerprint for rotation.
|
|
315
|
+
9. Is there an exclude list for endpoints that should not receive encrypted payloads?
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## 13. Integration Test Scenarios
|
|
320
|
+
|
|
321
|
+
| Scenario | Expected Outcome |
|
|
322
|
+
|--------------------------------------------------|------------------------------------------|
|
|
323
|
+
| Valid encrypted POST request | 200, business response returned |
|
|
324
|
+
| Valid encrypted PUT request | 200, business response returned |
|
|
325
|
+
| Tampered `data` field (auth tag mismatch) | 400, `DECRYPTION_FAILED` |
|
|
326
|
+
| Replayed request (same nonce sent twice) | 400, `INVALID_NONCE` |
|
|
327
|
+
| Request with timestamp > 5 min in the past | 400, `EXPIRED_NONCE` |
|
|
328
|
+
| Unknown `key_id` in request | 401, `KEY_ID_NOT_FOUND` → client retries |
|
|
329
|
+
| Public key endpoint returns 503 | Client blocks and surfaces error |
|
|
330
|
+
| GET request (unencrypted) | Passes through normally |
|
|
331
|
+
| Plaintext POST to encrypted endpoint (no `data` field) | 400 or 422, clear error |
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
*Applies to: react-native approaches 4 (Production JS), 5 (Production Native), and 6 (EC mode).
|
|
336
|
+
Keep in sync with `references/architecture.md`.*
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## PART B — EC Model (ECDH + HKDF-SHA256)
|
|
341
|
+
|
|
342
|
+
**Use this section when `ENCRYPTION_MODEL=ec` is selected.**
|
|
343
|
+
Share PART B (not PART A) with the backend team.
|
|
344
|
+
|
|
345
|
+
### B.1 Overview
|
|
346
|
+
|
|
347
|
+
The client uses **Elliptic Curve Diffie-Hellman (ECDH)** for key exchange. Each request:
|
|
348
|
+
1. Client generates a fresh ephemeral P-256 key pair.
|
|
349
|
+
2. ECDH between the ephemeral private key and the server’s **static** EC public key → 32-byte shared secret.
|
|
350
|
+
3. HKDF-SHA256(IKM=sharedSecret, salt=nonceBytes, info=`'enc'`) → 32-byte AES key.
|
|
351
|
+
4. AES-256-GCM encrypts the payload.
|
|
352
|
+
5. The derived AES key is **never transmitted**. Only the 65-byte uncompressed ephemeral public key is sent.
|
|
353
|
+
|
|
354
|
+
This is the generic per-request EC contract. Some backends instead use a session-init EC contract. If your backend does that, you must explicitly provide that variant to the frontend team before implementation.
|
|
355
|
+
|
|
356
|
+
### B.1a Session-Init EC Variant Checklist
|
|
357
|
+
|
|
358
|
+
If the backend uses an init-session flow instead of per-request ephemeral encryption, confirm all of these explicitly:
|
|
359
|
+
|
|
360
|
+
- Init endpoint and request/response schema
|
|
361
|
+
- Whether `encrypted_shared_aes_key` is authoritative, optional, or intentionally ignored by the reference client
|
|
362
|
+
- Exact HKDF `info` string
|
|
363
|
+
- Exact AAD bytes required for AES-GCM, if any
|
|
364
|
+
- Whether encrypted response payload is standard base64 or base64url
|
|
365
|
+
- Where the response IV comes from: explicit `iv`, `nonce`, or some other field
|
|
366
|
+
- Which `device_id` must be reused across init, key derivation, AAD, and encrypted API calls
|
|
367
|
+
|
|
368
|
+
Example from a real backend integrated in this project:
|
|
369
|
+
|
|
370
|
+
- HKDF `info`: `HDFC-LI-Session:v1|{keyId}|{deviceId}`
|
|
371
|
+
- AAD: `{"api_ver":"v1","device_id":"DEV1001","key_id":"LI-DEV1001-EMP1005"}`
|
|
372
|
+
- `encryptedResponse`: standard base64 of `ciphertext || auth_tag`
|
|
373
|
+
- `server_ecdh_pub` and `salt`: base64url with padding omitted
|
|
374
|
+
|
|
375
|
+
### B.2 Crypto Algorithms — Non-Negotiable (EC mode)
|
|
376
|
+
|
|
377
|
+
| Parameter | Required Value | Notes |
|
|
378
|
+
|-----------|---------------|-------|
|
|
379
|
+
| Key exchange | ECDH P-256 (secp256r1) | Server holds a static EC key pair |
|
|
380
|
+
| Key derivation | HKDF-SHA256 | salt = nonce bytes; info = `'enc'` (UTF-8); output = 32 bytes |
|
|
381
|
+
| Symmetric cipher | AES-256-GCM | 256-bit key, 12-byte IV, 16-byte auth tag |
|
|
382
|
+
| Public key format | Uncompressed P-256 point | 65 bytes: `0x04 \|\| X(32) \|\| Y(32)`, base64-encoded |
|
|
383
|
+
| Encoding | Base64 (standard, no line breaks) | All binary fields |
|
|
384
|
+
|
|
385
|
+
### B.3 EC Public Key Endpoint
|
|
386
|
+
|
|
387
|
+
```
|
|
388
|
+
GET /api/security/ec-public-key
|
|
389
|
+
Authorization: Bearer <access_token>
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Response — 200 OK:**
|
|
393
|
+
|
|
394
|
+
```json
|
|
395
|
+
{
|
|
396
|
+
"public_key": "<base64(65-byte uncompressed P-256 public key)>",
|
|
397
|
+
"key_id": "ec-v1",
|
|
398
|
+
"expires_at": "2026-07-01T00:00:00Z"
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
| Field | Required | Description |
|
|
403
|
+
|-------|----------|-------------|
|
|
404
|
+
| `public_key` | yes | base64 of the 65-byte uncompressed EC point (`0x04` prefix). Must NOT be a compressed point (33 bytes). |
|
|
405
|
+
| `key_id` | yes | Version tag for key rotation. |
|
|
406
|
+
| `expires_at` | no | ISO 8601 UTC. Client uses this to schedule refresh. |
|
|
407
|
+
|
|
408
|
+
> **Validation**: The client will reject any key where `base64decode(public_key).length !== 65` or `base64decode(public_key)[0] !== 0x04`.
|
|
409
|
+
|
|
410
|
+
### B.4 Encrypted Request Envelope (EC mode)
|
|
411
|
+
|
|
412
|
+
```json
|
|
413
|
+
{
|
|
414
|
+
"data": "<base64(ciphertext + 16-byte GCM auth_tag)>",
|
|
415
|
+
"ephemeral_public_key": "<base64(65-byte uncompressed P-256 ephemeral public key)>",
|
|
416
|
+
"iv": "<base64(12-byte IV)>",
|
|
417
|
+
"nonce": "<appId>^<unix_timestamp_ms>.<random_hex_16>",
|
|
418
|
+
"key_id": "ec-v1"
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
**No `key` field** — the AES key is derived independently by both sides via ECDH+HKDF. It is never transmitted.
|
|
423
|
+
|
|
424
|
+
### B.5 Step-by-Step Backend Decryption (EC mode)
|
|
425
|
+
|
|
426
|
+
```
|
|
427
|
+
1. Base64-decode `ephemeral_public_key` → 65-byte uncompressed P-256 point.
|
|
428
|
+
Reject if length != 65 or first byte != 0x04.
|
|
429
|
+
|
|
430
|
+
2. Read `key_id` → select the matching server EC private key.
|
|
431
|
+
|
|
432
|
+
3. ECDH: sharedSecret = serverPrivateKey.computeSecret(ephemeralPublicKey)
|
|
433
|
+
→ 32-byte shared secret.
|
|
434
|
+
|
|
435
|
+
4. HKDF-SHA256:
|
|
436
|
+
- IKM = sharedSecret (32 bytes)
|
|
437
|
+
- salt = UTF-8 bytes of the nonce string (entire nonce field value)
|
|
438
|
+
- info = 'enc' (UTF-8 bytes: 0x65, 0x6E, 0x63)
|
|
439
|
+
- output length = 32 bytes
|
|
440
|
+
PRK = HMAC-SHA256(salt, IKM)
|
|
441
|
+
OKM = HMAC-SHA256(PRK, info || 0x01)[0:32]
|
|
442
|
+
→ aesKey (32 bytes)
|
|
443
|
+
|
|
444
|
+
5. Base64-decode `iv` → 12-byte IV.
|
|
445
|
+
|
|
446
|
+
6. Base64-decode `data` → ciphertext_with_tag.
|
|
447
|
+
- Last 16 bytes = GCM auth tag.
|
|
448
|
+
- Remaining bytes = ciphertext.
|
|
449
|
+
|
|
450
|
+
7. AES-256-GCM-Decrypt(key=aesKey, iv=iv, ciphertext, tag).
|
|
451
|
+
If auth tag FAILS → return 400 DECRYPTION_FAILED.
|
|
452
|
+
|
|
453
|
+
8. Parse decrypted bytes as UTF-8 JSON → original request payload.
|
|
454
|
+
|
|
455
|
+
9. Validate nonce (same rules as RSA mode, Section 6 of PART A).
|
|
456
|
+
|
|
457
|
+
10. Process business logic.
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### B.6 Response Encryption (EC mode)
|
|
461
|
+
|
|
462
|
+
The server encrypts responses using the **same derived AES key** (from ECDH+HKDF) and a **fresh server-generated IV**:
|
|
463
|
+
|
|
464
|
+
```json
|
|
465
|
+
{
|
|
466
|
+
"data": "<base64(ciphertext + 16-byte GCM auth_tag)>",
|
|
467
|
+
"iv": "<base64(new 12-byte IV, server-generated for this response)>",
|
|
468
|
+
"nonce": "<server_generated_nonce>"
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
> **Critical**: The response IV must be freshly generated by the server. Never reuse the request IV.
|
|
473
|
+
|
|
474
|
+
### B.7 EC Key Rotation
|
|
475
|
+
|
|
476
|
+
1. Server generates a new P-256 key pair.
|
|
477
|
+
2. Publishes the new uncompressed public key at the endpoint with a new `key_id`.
|
|
478
|
+
3. Keeps the old private key active for a grace period (24 hours recommended) to handle in-flight requests.
|
|
479
|
+
4. After the grace period, retires the old private key.
|
|
480
|
+
5. Client: on `KEY_ID_NOT_FOUND` (401), fetches a fresh EC public key and retries the original request exactly once.
|
|
481
|
+
|
|
482
|
+
### B.8 Backend Implementation Checklist (EC mode)
|
|
483
|
+
|
|
484
|
+
- [ ] P-256 static key pair generated and private key stored securely (HSM or secrets manager).
|
|
485
|
+
- [ ] EC public key endpoint (`GET /api/security/ec-public-key`) returns 65-byte uncompressed point, base64-encoded.
|
|
486
|
+
- [ ] ECDH implemented: `sharedSecret = serverPrivateKey.computeSharedSecret(clientEphemeralPublicKey)`.
|
|
487
|
+
- [ ] HKDF-SHA256 implemented exactly as specified: salt=nonce bytes, info=`'enc'`, output=32 bytes.
|
|
488
|
+
- [ ] AES-256-GCM decryption with auth tag verification implemented.
|
|
489
|
+
- [ ] Response encrypted with derived AES key + fresh server IV.
|
|
490
|
+
- [ ] Nonce validation implemented (same as RSA mode).
|
|
491
|
+
- [ ] `KEY_ID_NOT_FOUND` (401) returned when `key_id` does not match any active key pair.
|
|
492
|
+
- [ ] Auth tag failure returns `400 DECRYPTION_FAILED` without leaking crypto details.
|
|
493
|
+
- [ ] Integration test: valid ECDH request → correct decryption → encrypted response → client decrypts correctly.
|
|
494
|
+
|
|
495
|
+
### B.9 Questions for the Backend Team (EC mode)
|
|
496
|
+
|
|
497
|
+
1. What is the exact URL for the EC public key endpoint?
|
|
498
|
+
2. Confirm the EC public key will be returned as base64 of the 65-byte uncompressed P-256 point.
|
|
499
|
+
3. What is the initial `key_id` value?
|
|
500
|
+
4. Is HKDF confirmed as the key derivation function (not direct SHA-256 of the shared secret)?
|
|
501
|
+
5. Should responses be encrypted? If yes, which endpoints?
|
|
502
|
+
6. What is the nonce replay window (default: 5 minutes)?
|
|
503
|
+
7. If this is a session-init EC backend rather than per-request EC, what exact HKDF `info` string must the client use?
|
|
504
|
+
8. If AES-GCM AAD is required, what exact canonical bytes must the client send and verify?
|
|
505
|
+
9. Are response payload bytes standard base64 or base64url?
|
|
506
|
+
10. Where does the response IV come from for decryption?
|