android-sdd 1.0.0

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.
Files changed (176) hide show
  1. package/dist/index.js +143 -0
  2. package/package.json +27 -0
  3. package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
  4. package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
  5. package/skills/Android Platform/Configuration/SKILL.md +201 -0
  6. package/skills/Android Platform/Filesystem/SKILL.md +216 -0
  7. package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
  8. package/skills/Android Platform/Manifest/SKILL.md +226 -0
  9. package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
  10. package/skills/Android Platform/Resources/SKILL.md +234 -0
  11. package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
  12. package/skills/Android Platform/State Restoration/SKILL.md +210 -0
  13. package/skills/Architecture/Bounded Context/SKILL.md +207 -0
  14. package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
  15. package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
  16. package/skills/Architecture/Entity Design/SKILL.md +243 -0
  17. package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
  18. package/skills/Architecture/MVI/SKILL.md +224 -0
  19. package/skills/Architecture/MVVM/SKILL.md +198 -0
  20. package/skills/Architecture/Modularization/SKILL.md +194 -0
  21. package/skills/Architecture/Offline First/SKILL.md +249 -0
  22. package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
  23. package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
  24. package/skills/Architecture/State Management/SKILL.md +229 -0
  25. package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
  26. package/skills/Architecture/Use Case Design/SKILL.md +244 -0
  27. package/skills/Architecture/Value Object/SKILL.md +226 -0
  28. package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
  29. package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
  30. package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
  31. package/skills/Build System/Build Cache/SKILL.md +233 -0
  32. package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
  33. package/skills/Build System/Build Variant/SKILL.md +215 -0
  34. package/skills/Build System/Convention Plugin/SKILL.md +288 -0
  35. package/skills/Build System/Dependency Management/SKILL.md +261 -0
  36. package/skills/Build System/Gradle/SKILL.md +284 -0
  37. package/skills/Build System/Incremental Build/SKILL.md +199 -0
  38. package/skills/Build System/KAPT/SKILL.md +198 -0
  39. package/skills/Build System/KSP/SKILL.md +263 -0
  40. package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
  41. package/skills/Build System/Specialized/C++/SKILL.md +308 -0
  42. package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
  43. package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
  44. package/skills/Build System/Version Catalog/SKILL.md +304 -0
  45. package/skills/Concurrency/Background Processing/SKILL.md +185 -0
  46. package/skills/Concurrency/Channel/SKILL.md +207 -0
  47. package/skills/Concurrency/Coroutine/SKILL.md +200 -0
  48. package/skills/Concurrency/Flow/SKILL.md +179 -0
  49. package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
  50. package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
  51. package/skills/Concurrency/StateFlow/SKILL.md +175 -0
  52. package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
  53. package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
  54. package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
  55. package/skills/Core Language/DSL/SKILL.md +186 -0
  56. package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
  57. package/skills/Core Language/Immutability/SKILL.md +156 -0
  58. package/skills/Core Language/KMP/SKILL.md +182 -0
  59. package/skills/Core Language/Kotlin/SKILL.md +187 -0
  60. package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
  61. package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
  62. package/skills/Core Language/Serialization/SKILL.md +191 -0
  63. package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
  64. package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
  65. package/skills/Data Layer/DAO/SKILL.md +225 -0
  66. package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
  67. package/skills/Data Layer/DataStore/SKILL.md +264 -0
  68. package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
  69. package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
  70. package/skills/Data Layer/File Storage/SKILL.md +247 -0
  71. package/skills/Data Layer/Indexing/SKILL.md +184 -0
  72. package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
  73. package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
  74. package/skills/Data Layer/Migration/SKILL.md +243 -0
  75. package/skills/Data Layer/Paging/SKILL.md +264 -0
  76. package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
  77. package/skills/Data Layer/Room/SKILL.md +244 -0
  78. package/skills/Data Layer/SQLite/SKILL.md +255 -0
  79. package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
  80. package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
  81. package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
  82. package/skills/Dependency Injection/Koin/SKILL.md +282 -0
  83. package/skills/Developer Experience/Detekt/SKILL.md +272 -0
  84. package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
  85. package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
  86. package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
  87. package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
  88. package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
  89. package/skills/Media/Audio/SKILL.md +257 -0
  90. package/skills/Media/Camera/SKILL.md +229 -0
  91. package/skills/Media/CameraX/SKILL.md +295 -0
  92. package/skills/Media/ExoPlayer/SKILL.md +258 -0
  93. package/skills/Media/Video/SKILL.md +228 -0
  94. package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
  95. package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
  96. package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
  97. package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
  98. package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
  99. package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
  100. package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
  101. package/skills/Navigation/Navigation/SKILL.md +215 -0
  102. package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
  103. package/skills/Networking/API Contract/SKILL.md +220 -0
  104. package/skills/Networking/Authentication/SKILL.md +210 -0
  105. package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
  106. package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
  107. package/skills/Networking/Ktor/SKILL.md +219 -0
  108. package/skills/Networking/Multipart Upload/SKILL.md +213 -0
  109. package/skills/Networking/OkHttp/SKILL.md +193 -0
  110. package/skills/Networking/REST/SKILL.md +178 -0
  111. package/skills/Networking/Rate Limiting/SKILL.md +170 -0
  112. package/skills/Networking/Retrofit/SKILL.md +241 -0
  113. package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
  114. package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
  115. package/skills/Networking/WebSocket/SKILL.md +224 -0
  116. package/skills/Observability/Crash Reporting/SKILL.md +219 -0
  117. package/skills/Observability/Logging/SKILL.md +168 -0
  118. package/skills/Observability/Metrics/SKILL.md +227 -0
  119. package/skills/Observability/Structured Logging/SKILL.md +234 -0
  120. package/skills/Performance/ANR Prevention/SKILL.md +192 -0
  121. package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
  122. package/skills/Performance/App Startup/SKILL.md +183 -0
  123. package/skills/Performance/Baseline Profile/SKILL.md +205 -0
  124. package/skills/Performance/Battery Optimization/SKILL.md +192 -0
  125. package/skills/Performance/Benchmark/SKILL.md +182 -0
  126. package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
  127. package/skills/Performance/Compose Optimization/SKILL.md +187 -0
  128. package/skills/Performance/Heap Management/SKILL.md +184 -0
  129. package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
  130. package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
  131. package/skills/Performance/Rendering Performance/SKILL.md +205 -0
  132. package/skills/Performance/Startup Optimization/SKILL.md +219 -0
  133. package/skills/Security/Biometric/SKILL.md +224 -0
  134. package/skills/Security/Certificate Transparency/SKILL.md +158 -0
  135. package/skills/Security/Cryptography/SKILL.md +244 -0
  136. package/skills/Security/Encrypted Storage/SKILL.md +273 -0
  137. package/skills/Security/Frida Detection/SKILL.md +230 -0
  138. package/skills/Security/Hook Detection/SKILL.md +197 -0
  139. package/skills/Security/Keystore/SKILL.md +272 -0
  140. package/skills/Security/Network Security Config/SKILL.md +186 -0
  141. package/skills/Security/Obfuscation/SKILL.md +226 -0
  142. package/skills/Security/Proguard/SKILL.md +202 -0
  143. package/skills/Security/R8/SKILL.md +234 -0
  144. package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
  145. package/skills/Security/Root Detection/SKILL.md +220 -0
  146. package/skills/Security/Secure Networking/SKILL.md +220 -0
  147. package/skills/System Integration/AlarmManager/SKILL.md +182 -0
  148. package/skills/System Integration/App Widget/SKILL.md +182 -0
  149. package/skills/System Integration/Deep Link/SKILL.md +187 -0
  150. package/skills/System Integration/Foreground Service/SKILL.md +212 -0
  151. package/skills/System Integration/Notification/SKILL.md +237 -0
  152. package/skills/System Integration/WorkManager/SKILL.md +256 -0
  153. package/skills/System Integration/clipboard/SKILL.md +155 -0
  154. package/skills/System Integration/share-intent/SKILL.md +182 -0
  155. package/skills/Testing/Compose Testing/SKILL.md +296 -0
  156. package/skills/Testing/Espresso/SKILL.md +292 -0
  157. package/skills/Testing/Fake Data/SKILL.md +245 -0
  158. package/skills/Testing/Integration Testing/SKILL.md +288 -0
  159. package/skills/Testing/Mocking/SKILL.md +229 -0
  160. package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
  161. package/skills/Testing/UI Testing/SKILL.md +293 -0
  162. package/skills/Testing/Unit Testing/SKILL.md +309 -0
  163. package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
  164. package/skills/UI System/Compose/SKILL.md +296 -0
  165. package/skills/UI System/Compose Animation/SKILL.md +281 -0
  166. package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
  167. package/skills/UI System/Compose Navigation/SKILL.md +255 -0
  168. package/skills/UI System/Compose Performance/SKILL.md +274 -0
  169. package/skills/UI System/Design System/SKILL.md +217 -0
  170. package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
  171. package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
  172. package/skills/UI System/Loading Strategy/SKILL.md +254 -0
  173. package/skills/UI System/Material 3/SKILL.md +279 -0
  174. package/skills/UI System/RTL/SKILL.md +179 -0
  175. package/src/index.ts +182 -0
  176. package/tsconfig.json +19 -0
@@ -0,0 +1,224 @@
1
+ ---
2
+ name: biometric
3
+ description: >
4
+ Biometric authentication for Android apps.
5
+ Load this skill when implementing fingerprint or face authentication,
6
+ using BiometricPrompt, tying biometric auth to Keystore keys,
7
+ handling fallback to device credentials, or checking biometric availability.
8
+ ---
9
+
10
+ # Biometric
11
+
12
+ ## Overview
13
+ Android's BiometricPrompt API provides a unified interface for fingerprint, face, and iris authentication. Biometric auth can be used standalone (gate access to a screen) or tied to a Keystore key (unlock a cryptographic operation). The latter is stronger — authentication is required each time the key is used.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Use **BiometricPrompt** — never use FingerprintManager directly (deprecated)
20
+ - **Crypto-based auth** (tied to Keystore key) is stronger than confirmation-only auth
21
+ - Always provide **fallback to device credentials** (PIN/pattern/password) for accessibility
22
+ - Handle `KeyPermanentlyInvalidatedException` — thrown when new biometrics are enrolled
23
+ - Check availability before showing biometric UI — degrade gracefully on unsupported devices
24
+
25
+ ---
26
+
27
+ ## Check Availability
28
+
29
+ ```kotlin
30
+ // ✅ Check if biometric is available before using it
31
+ class BiometricAvailabilityChecker @Inject constructor(
32
+ @ApplicationContext private val context: Context
33
+ ) {
34
+ sealed interface Availability {
35
+ data object Available : Availability
36
+ data object NotEnrolled : Availability
37
+ data object NotSupported : Availability
38
+ data object HardwareUnavailable : Availability
39
+ }
40
+
41
+ fun check(): Availability {
42
+ val manager = BiometricManager.from(context)
43
+ return when (manager.canAuthenticate(
44
+ BiometricManager.Authenticators.BIOMETRIC_STRONG or
45
+ BiometricManager.Authenticators.DEVICE_CREDENTIAL
46
+ )) {
47
+ BiometricManager.BIOMETRIC_SUCCESS -> Availability.Available
48
+ BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> Availability.NotEnrolled
49
+ BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> Availability.NotSupported
50
+ BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> Availability.HardwareUnavailable
51
+ else -> Availability.NotSupported
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Simple Biometric Prompt (Confirmation Only)
60
+
61
+ ```kotlin
62
+ // ✅ Gate screen access — no Keystore key involved
63
+ class BiometricAuthManager @Inject constructor() {
64
+
65
+ sealed interface AuthResult {
66
+ data object Success : AuthResult
67
+ data class Error(val message: String) : AuthResult
68
+ data object Cancelled : AuthResult
69
+ }
70
+
71
+ fun authenticate(
72
+ activity: FragmentActivity,
73
+ title: String = "Authenticate",
74
+ subtitle: String = "Use biometric or device credential",
75
+ onResult: (AuthResult) -> Unit
76
+ ) {
77
+ val executor = ContextCompat.getMainExecutor(activity)
78
+
79
+ val prompt = BiometricPrompt(activity, executor,
80
+ object : BiometricPrompt.AuthenticationCallback() {
81
+ override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
82
+ onResult(AuthResult.Success)
83
+ }
84
+ override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
85
+ if (errorCode == BiometricPrompt.ERROR_USER_CANCELED ||
86
+ errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
87
+ onResult(AuthResult.Cancelled)
88
+ } else {
89
+ onResult(AuthResult.Error(errString.toString()))
90
+ }
91
+ }
92
+ override fun onAuthenticationFailed() {
93
+ // Called on each failed attempt — not terminal; don't handle as final failure
94
+ }
95
+ }
96
+ )
97
+
98
+ val promptInfo = BiometricPrompt.PromptInfo.Builder()
99
+ .setTitle(title)
100
+ .setSubtitle(subtitle)
101
+ .setAllowedAuthenticators(
102
+ BiometricManager.Authenticators.BIOMETRIC_STRONG or
103
+ BiometricManager.Authenticators.DEVICE_CREDENTIAL
104
+ )
105
+ .build()
106
+
107
+ prompt.authenticate(promptInfo)
108
+ }
109
+ }
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Crypto-Based Biometric (Keystore-Tied)
115
+
116
+ ```kotlin
117
+ // ✅ Biometric auth that unlocks a Keystore key — stronger guarantee
118
+ class BiometricCryptoManager @Inject constructor(
119
+ private val keystoreCrypto: KeystoreCrypto
120
+ ) {
121
+ private val keyAlias = KeystoreAliases.BIOMETRIC_KEY
122
+
123
+ // ✅ Prepare cipher before showing prompt — pass to authenticate()
124
+ fun prepareCipher(): Cipher? {
125
+ return try {
126
+ val keyStore = KeyStore.getInstance("AndroidKeyStore").also { it.load(null) }
127
+ if (!keyStore.containsAlias(keyAlias)) generateBiometricBoundKey(keyAlias)
128
+
129
+ val key = keyStore.getKey(keyAlias, null) as SecretKey
130
+ Cipher.getInstance("AES/GCM/NoPadding").also {
131
+ it.init(Cipher.ENCRYPT_MODE, key)
132
+ }
133
+ } catch (e: KeyPermanentlyInvalidatedException) {
134
+ // New biometrics enrolled — delete and recreate key
135
+ KeyStore.getInstance("AndroidKeyStore").also { it.load(null) }.deleteEntry(keyAlias)
136
+ null
137
+ }
138
+ }
139
+
140
+ fun authenticate(
141
+ activity: FragmentActivity,
142
+ cipher: Cipher,
143
+ onSuccess: (Cipher) -> Unit,
144
+ onFailure: () -> Unit
145
+ ) {
146
+ val executor = ContextCompat.getMainExecutor(activity)
147
+ val cryptoObject = BiometricPrompt.CryptoObject(cipher)
148
+
149
+ val prompt = BiometricPrompt(activity, executor,
150
+ object : BiometricPrompt.AuthenticationCallback() {
151
+ override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
152
+ result.cryptoObject?.cipher?.let { onSuccess(it) } ?: onFailure()
153
+ }
154
+ override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
155
+ onFailure()
156
+ }
157
+ }
158
+ )
159
+
160
+ val promptInfo = BiometricPrompt.PromptInfo.Builder()
161
+ .setTitle("Confirm identity")
162
+ .setSubtitle("Touch sensor to continue")
163
+ .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
164
+ .setNegativeButtonText("Cancel")
165
+ .build()
166
+
167
+ prompt.authenticate(promptInfo, cryptoObject)
168
+ }
169
+ }
170
+ ```
171
+
172
+ ---
173
+
174
+ ## Compose Integration
175
+
176
+ ```kotlin
177
+ // ✅ Trigger biometric from Compose via Activity
178
+ @Composable
179
+ fun BiometricGatedScreen(
180
+ onAuthenticated: () -> Unit,
181
+ content: @Composable () -> Unit
182
+ ) {
183
+ val activity = LocalContext.current as FragmentActivity
184
+ val biometricManager = remember { BiometricAuthManager() }
185
+ var isAuthenticated by rememberSaveable { mutableStateOf(false) }
186
+
187
+ LaunchedEffect(Unit) {
188
+ biometricManager.authenticate(
189
+ activity = activity,
190
+ onResult = { result ->
191
+ if (result is BiometricAuthManager.AuthResult.Success) {
192
+ isAuthenticated = true
193
+ onAuthenticated()
194
+ }
195
+ }
196
+ )
197
+ }
198
+
199
+ if (isAuthenticated) {
200
+ content()
201
+ } else {
202
+ Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
203
+ CircularProgressIndicator()
204
+ }
205
+ }
206
+ }
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Anti-Patterns
212
+
213
+ - Using `FingerprintManager` — deprecated since API 28; use `BiometricPrompt`
214
+ - Not handling `KeyPermanentlyInvalidatedException` — crashes when user enrolls new fingerprint
215
+ - No fallback to device credential — locks out users with biometric issues
216
+ - Calling `authenticate()` from a background thread — must be called on main thread
217
+ - Storing sensitive data in plain storage after biometric success — biometric should unlock Keystore key
218
+
219
+ ---
220
+
221
+ ## Related Skills
222
+ - `keystore` — generating biometric-bound Keystore keys
223
+ - `encrypted-storage` — protecting data unlocked by biometric
224
+ - `cryptography` — cipher operations after biometric success
@@ -0,0 +1,158 @@
1
+ ---
2
+ name: certificate-transparency
3
+ description: >
4
+ Certificate Transparency (CT) enforcement for Android apps.
5
+ Load this skill when enforcing CT log verification for TLS connections,
6
+ understanding CT requirements on Android, or configuring OkHttp to
7
+ verify certificates are publicly logged.
8
+ ---
9
+
10
+ # Certificate Transparency
11
+
12
+ ## Overview
13
+ Certificate Transparency is a public logging framework that requires CAs to log every issued certificate to a public CT log. This makes mis-issued certificates detectable. On Android, CT enforcement is built into the OS (Android 7+) for system CAs, and can be enforced at the app level via OkHttp's `certificatetransparency` library for additional guarantees.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - **CT is enforced by Android OS** for system CA-signed certificates since Android 7+
20
+ - App-level CT enforcement provides stricter, per-host control
21
+ - CT does not replace certificate pinning — they address different threats
22
+ - CT verifies the certificate is in public logs — pinning verifies it's a specific certificate
23
+ - Use both for highest security: CT catches mis-issuance; pinning catches MITM
24
+
25
+ ---
26
+
27
+ ## Android OS Built-in CT
28
+
29
+ ```
30
+ Android 7.0+ enforces CT for:
31
+ - Certificates signed by public CAs trusted by the system
32
+ - Connections where the certificate chain includes a public CA
33
+
34
+ Android does NOT enforce CT for:
35
+ - Certificates in the user certificate store
36
+ - Privately issued certificates
37
+ - Certificates added via Network Security Config
38
+ ```
39
+
40
+ ---
41
+
42
+ ## App-Level CT with OkHttp (certificatetransparency-android)
43
+
44
+ ```kotlin
45
+ // dependency: com.appmattus.certificatetransparency:certificatetransparency-android
46
+
47
+ // ✅ Add CT verification to OkHttp
48
+ fun buildCtEnabledOkHttpClient(): OkHttpClient {
49
+ val ctInterceptor = certificateTransparencyInterceptor {
50
+ // ✅ Enforce CT for all hosts
51
+ +("*.*")
52
+
53
+ // ✅ Or enforce only for specific hosts
54
+ +("api.example.com")
55
+ +("*.example.com")
56
+
57
+ // ✅ Exclude hosts where CT is not applicable (e.g. internal/dev)
58
+ -("internal.dev.example.com")
59
+ }
60
+
61
+ return OkHttpClient.Builder()
62
+ .addNetworkInterceptor(ctInterceptor)
63
+ .build()
64
+ }
65
+
66
+ // ✅ Alternative: use CTHostnameVerifier
67
+ fun buildCtEnabledClient(): OkHttpClient {
68
+ val ctHostnameVerifier = certificateTransparencyHostnameVerifier {
69
+ +("api.example.com")
70
+ }
71
+
72
+ return OkHttpClient.Builder()
73
+ .hostnameVerifier(ctHostnameVerifier)
74
+ .build()
75
+ }
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Network Security Config + CT
81
+
82
+ ```xml
83
+ <!-- res/xml/network_security_config.xml -->
84
+ <?xml version="1.0" encoding="utf-8"?>
85
+ <network-security-config>
86
+ <base-config cleartextTrafficPermitted="false">
87
+ <trust-anchors>
88
+ <!-- ✅ System CAs — Android 7+ enforces CT for these -->
89
+ <certificates src="system" />
90
+ </trust-anchors>
91
+ </base-config>
92
+
93
+ <!-- ✅ For debug: allow user-added CAs without CT enforcement -->
94
+ <debug-overrides>
95
+ <trust-anchors>
96
+ <certificates src="system" />
97
+ <certificates src="user" />
98
+ </trust-anchors>
99
+ </debug-overrides>
100
+ </network-security-config>
101
+ ```
102
+
103
+ ---
104
+
105
+ ## CT vs Certificate Pinning
106
+
107
+ | | Certificate Transparency | Certificate Pinning |
108
+ |---|---|---|
109
+ | What it verifies | Certificate is publicly logged | Certificate matches expected value |
110
+ | Protects against | Mis-issued certificates by CAs | MITM with any valid certificate |
111
+ | Maintenance | Low (automatic) | High (pin rotation required) |
112
+ | Failure mode | Rejects unlogged certificates | Rejects wrong certificates |
113
+ | Use for | All public API connections | High-security endpoints |
114
+
115
+ ---
116
+
117
+ ## Hilt Integration
118
+
119
+ ```kotlin
120
+ @Module
121
+ @InstallIn(SingletonComponent::class)
122
+ object NetworkModule {
123
+
124
+ @Provides
125
+ @Singleton
126
+ fun provideOkHttpClient(
127
+ authInterceptor: AuthInterceptor,
128
+ certificatePinner: CertificatePinner
129
+ ): OkHttpClient {
130
+ val ctInterceptor = certificateTransparencyInterceptor {
131
+ +("api.example.com")
132
+ }
133
+
134
+ return OkHttpClient.Builder()
135
+ .addInterceptor(authInterceptor)
136
+ .addNetworkInterceptor(ctInterceptor) // ✅ CT after TLS handshake
137
+ .certificatePinner(certificatePinner)
138
+ .build()
139
+ }
140
+ }
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Anti-Patterns
146
+
147
+ - Assuming OS-level CT is sufficient for all use cases — app-level gives per-host control
148
+ - Disabling CT verification in production builds — removes mis-issuance protection
149
+ - Using CT as a replacement for certificate pinning — they complement each other
150
+ - Not testing CT enforcement — a misconfigured CT check can block all connections silently
151
+
152
+ ---
153
+
154
+ ## Related Skills
155
+ - `certificate-pinning` — pinning specific certificates against MITM
156
+ - `secure-networking` — overall TLS and network security
157
+ - `network-security-config` — Android XML-based network policy
158
+ - `okhttp` — OkHttp configuration and interceptors
@@ -0,0 +1,244 @@
1
+ ---
2
+ name: cryptography
3
+ description: >
4
+ Cryptographic operations for Android apps.
5
+ Load this skill when implementing hashing, symmetric encryption,
6
+ asymmetric encryption, digital signatures, key derivation,
7
+ HMAC, or secure random generation.
8
+ ---
9
+
10
+ # Cryptography
11
+
12
+ ## Overview
13
+ Android provides the standard Java Cryptography Architecture (JCA) with hardware-backed key storage via Keystore. This skill covers the correct algorithms and patterns for common cryptographic needs — encryption, hashing, signing, and key derivation — without relying on Keystore (see `keystore` skill for that).
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - **Use proven algorithms** — AES-GCM, ChaCha20-Poly1305, RSA-OAEP, SHA-256, PBKDF2
20
+ - **Never roll your own crypto** — use standard JCA or Tink
21
+ - **Always use authenticated encryption** (AES-GCM, not AES-CBC without MAC)
22
+ - **Never reuse IV/nonce** with the same key
23
+ - **Use Tink for high-level operations** — fewer footguns than raw JCA
24
+
25
+ ---
26
+
27
+ ## Symmetric Encryption (AES-GCM)
28
+
29
+ ```kotlin
30
+ object AesGcm {
31
+ private const val ALGORITHM = "AES/GCM/NoPadding"
32
+ private const val KEY_SIZE = 256
33
+ private const val IV_SIZE = 12 // 96 bits — standard for GCM
34
+ private const val TAG_SIZE = 128 // authentication tag bits
35
+
36
+ // ✅ Generate a random AES key (for non-Keystore use)
37
+ fun generateKey(): SecretKey {
38
+ val generator = KeyGenerator.getInstance("AES")
39
+ generator.init(KEY_SIZE)
40
+ return generator.generateKey()
41
+ }
42
+
43
+ // ✅ Encrypt — returns IV prepended to ciphertext
44
+ fun encrypt(key: SecretKey, plaintext: ByteArray): ByteArray {
45
+ val iv = ByteArray(IV_SIZE).also { SecureRandom().nextBytes(it) }
46
+ val cipher = Cipher.getInstance(ALGORITHM)
47
+ cipher.init(Cipher.ENCRYPT_MODE, key, GCMParameterSpec(TAG_SIZE, iv))
48
+ val ciphertext = cipher.doFinal(plaintext)
49
+ return iv + ciphertext // prepend IV for storage
50
+ }
51
+
52
+ // ✅ Decrypt — expects IV prepended to ciphertext
53
+ fun decrypt(key: SecretKey, data: ByteArray): ByteArray {
54
+ val iv = data.copyOfRange(0, IV_SIZE)
55
+ val ciphertext = data.copyOfRange(IV_SIZE, data.size)
56
+ val cipher = Cipher.getInstance(ALGORITHM)
57
+ cipher.init(Cipher.DECRYPT_MODE, key, GCMParameterSpec(TAG_SIZE, iv))
58
+ return cipher.doFinal(ciphertext)
59
+ }
60
+ }
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Hashing
66
+
67
+ ```kotlin
68
+ object Hashing {
69
+ // ✅ SHA-256 hash
70
+ fun sha256(input: ByteArray): ByteArray =
71
+ MessageDigest.getInstance("SHA-256").digest(input)
72
+
73
+ fun sha256Hex(input: String): String =
74
+ sha256(input.toByteArray(Charsets.UTF_8))
75
+ .joinToString("") { "%02x".format(it) }
76
+
77
+ // ✅ SHA-256 of a file (streaming — no OOM for large files)
78
+ fun sha256File(file: File): ByteArray {
79
+ val digest = MessageDigest.getInstance("SHA-256")
80
+ file.inputStream().use { stream ->
81
+ val buffer = ByteArray(8192)
82
+ var read: Int
83
+ while (stream.read(buffer).also { read = it } != -1) {
84
+ digest.update(buffer, 0, read)
85
+ }
86
+ }
87
+ return digest.digest()
88
+ }
89
+
90
+ // ✅ HMAC-SHA256 — for message authentication
91
+ fun hmacSha256(key: ByteArray, message: ByteArray): ByteArray {
92
+ val secretKey = SecretKeySpec(key, "HmacSHA256")
93
+ return Mac.getInstance("HmacSHA256").run {
94
+ init(secretKey)
95
+ doFinal(message)
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Password Hashing (PBKDF2)
104
+
105
+ ```kotlin
106
+ // ✅ Hash passwords for local verification — NOT for sending to server
107
+ object PasswordHasher {
108
+ private const val ITERATIONS = 100_000
109
+ private const val KEY_LENGTH = 256
110
+ private const val SALT_SIZE = 16
111
+ private const val ALGORITHM = "PBKDF2WithHmacSHA256"
112
+
113
+ data class HashedPassword(val hash: ByteArray, val salt: ByteArray) {
114
+ fun toStorageString(): String =
115
+ "${Base64.encodeToString(hash, Base64.NO_WRAP)}:${Base64.encodeToString(salt, Base64.NO_WRAP)}"
116
+
117
+ companion object {
118
+ fun fromStorageString(value: String): HashedPassword {
119
+ val (hashB64, saltB64) = value.split(":")
120
+ return HashedPassword(
121
+ hash = Base64.decode(hashB64, Base64.NO_WRAP),
122
+ salt = Base64.decode(saltB64, Base64.NO_WRAP)
123
+ )
124
+ }
125
+ }
126
+ }
127
+
128
+ fun hash(password: CharArray): HashedPassword {
129
+ val salt = ByteArray(SALT_SIZE).also { SecureRandom().nextBytes(it) }
130
+ val spec = PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH)
131
+ val factory = SecretKeyFactory.getInstance(ALGORITHM)
132
+ val hash = factory.generateSecret(spec).encoded
133
+ spec.clearPassword()
134
+ return HashedPassword(hash, salt)
135
+ }
136
+
137
+ fun verify(password: CharArray, stored: HashedPassword): Boolean {
138
+ val spec = PBEKeySpec(password, stored.salt, ITERATIONS, KEY_LENGTH)
139
+ val factory = SecretKeyFactory.getInstance(ALGORITHM)
140
+ val hash = factory.generateSecret(spec).encoded
141
+ spec.clearPassword()
142
+ return MessageDigest.isEqual(hash, stored.hash)
143
+ }
144
+ }
145
+ ```
146
+
147
+ ---
148
+
149
+ ## Key Derivation (HKDF)
150
+
151
+ ```kotlin
152
+ // ✅ Derive multiple keys from one master key
153
+ object KeyDerivation {
154
+ fun deriveKey(masterKey: ByteArray, info: String, outputLength: Int = 32): ByteArray {
155
+ // HKDF using HMAC-SHA256
156
+ val salt = ByteArray(32) // zero salt
157
+ val prk = Hashing.hmacSha256(salt, masterKey)
158
+ val infoBytes = info.toByteArray(Charsets.UTF_8)
159
+ val okm = Hashing.hmacSha256(prk, infoBytes + byteArrayOf(1))
160
+ return okm.copyOfRange(0, outputLength)
161
+ }
162
+ }
163
+
164
+ // Usage: derive separate keys for encryption and authentication from one master
165
+ val encryptionKey = KeyDerivation.deriveKey(masterKey, "encryption")
166
+ val authKey = KeyDerivation.deriveKey(masterKey, "authentication")
167
+ ```
168
+
169
+ ---
170
+
171
+ ## Secure Random
172
+
173
+ ```kotlin
174
+ // ✅ Always use SecureRandom for cryptographic purposes
175
+ object SecureRandomUtils {
176
+ fun randomBytes(size: Int): ByteArray =
177
+ ByteArray(size).also { SecureRandom().nextBytes(it) }
178
+
179
+ fun randomHex(size: Int): String =
180
+ randomBytes(size).joinToString("") { "%02x".format(it) }
181
+
182
+ fun randomBase64(size: Int): String =
183
+ Base64.encodeToString(randomBytes(size), Base64.NO_WRAP)
184
+ }
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Tink (Recommended for Production)
190
+
191
+ ```kotlin
192
+ // ✅ Google Tink — high-level crypto library, fewer footguns
193
+ // dependency: com.google.crypto.tink:tink-android
194
+
195
+ // Initialize once at app startup
196
+ TinkConfig.register()
197
+
198
+ // ✅ Authenticated encryption
199
+ val keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES256_GCM)
200
+ val aead = keysetHandle.getPrimitive(Aead::class.java)
201
+ val ciphertext = aead.encrypt(plaintext, associatedData)
202
+ val decrypted = aead.decrypt(ciphertext, associatedData)
203
+
204
+ // ✅ Store keysets securely with Keystore integration
205
+ val keysetHandle = AndroidKeysetManager.Builder()
206
+ .withSharedPref(context, "tink_keyset", "tink_prefs")
207
+ .withKeyTemplate(AeadKeyTemplates.AES256_GCM)
208
+ .withMasterKeyUri("android-keystore://tink_master_key")
209
+ .build()
210
+ .keysetHandle
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Algorithm Selection Guide
216
+
217
+ | Need | Algorithm | Avoid |
218
+ |---|---|---|
219
+ | Symmetric encryption | AES-256-GCM | AES-ECB, AES-CBC without MAC |
220
+ | Password hashing | PBKDF2 (100k+ iterations) | MD5, SHA-1, unsalted hash |
221
+ | General hashing | SHA-256 / SHA-3 | MD5, SHA-1 |
222
+ | Message authentication | HMAC-SHA256 | Custom MAC schemes |
223
+ | Asymmetric encryption | RSA-OAEP-SHA256 | RSA-PKCS1v1.5 |
224
+ | Digital signature | ECDSA P-256, Ed25519 | RSA with SHA-1 |
225
+ | Key agreement | ECDH P-256 | DH without authentication |
226
+
227
+ ---
228
+
229
+ ## Anti-Patterns
230
+
231
+ - `Random()` instead of `SecureRandom()` — predictable, not suitable for crypto
232
+ - AES-ECB mode — same plaintext block → same ciphertext; patterns visible
233
+ - Reusing IV with the same key in GCM — catastrophic key recovery attack
234
+ - MD5 or SHA-1 for security purposes — cryptographically broken
235
+ - Storing keys as hex strings in SharedPreferences — use Android Keystore
236
+ - Rolling custom encryption schemes — use standard JCA or Tink
237
+
238
+ ---
239
+
240
+ ## Related Skills
241
+ - `keystore` — hardware-backed key storage for Android
242
+ - `encrypted-storage` — applying crypto to local storage
243
+ - `biometric` — biometric-gated crypto operations
244
+ - `secure-networking` — TLS and certificate pinning