@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.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 (264) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +266 -267
  5. package/package.json +2 -2
  6. package/pipeline/scripts/_notices.mjs +1 -1
  7. package/pipeline/skills/.skill-manifest.json +68 -68
  8. package/pipeline/skills/shared/README.md +70 -70
  9. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  10. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  11. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  12. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  13. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  14. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  15. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  16. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  17. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  18. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  19. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  20. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  21. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  22. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  23. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  24. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  25. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  26. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  27. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  28. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  29. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  30. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  31. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  32. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  34. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  35. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  36. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  37. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  38. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  39. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  40. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  41. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  42. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  43. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  44. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  45. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  46. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  47. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  48. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  49. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  50. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  51. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  52. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  53. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  54. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  55. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  56. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  57. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  58. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  59. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  60. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  61. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  62. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  63. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  64. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  65. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  66. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  67. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  68. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  69. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  70. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  71. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  72. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  73. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  74. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  75. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  76. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  77. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  78. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  79. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  80. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  81. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  82. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  83. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  84. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  85. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  86. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  87. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  88. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  89. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  90. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  91. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  92. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  93. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  94. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  95. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  96. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  97. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  98. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  99. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  100. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  101. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  102. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  103. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  104. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  105. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  106. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  107. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  108. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  109. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  111. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  112. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  113. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  114. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  115. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  116. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  117. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  118. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  119. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  120. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  121. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  122. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  123. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  124. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  125. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  126. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  127. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  128. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  129. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  130. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  131. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  132. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  133. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  134. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  135. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  136. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  137. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  138. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  139. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  140. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  141. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  142. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  143. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  144. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  145. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  146. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  147. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  148. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  149. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  150. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  151. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  152. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  153. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  154. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  155. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  156. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  157. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  158. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  159. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  160. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  161. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  162. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  163. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  164. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  165. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  166. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  167. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  168. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  169. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  170. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  171. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  172. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  173. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  174. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  175. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  176. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  177. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  178. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  179. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  180. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  181. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  182. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  183. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  184. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  185. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  186. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  187. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  188. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  189. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  190. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  191. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  192. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  193. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  194. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  195. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  196. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  197. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  198. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  199. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  200. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  201. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  202. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  203. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  204. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  205. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  206. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  207. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  208. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  209. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  210. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  211. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  212. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  213. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  214. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  215. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  216. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  217. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  218. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  219. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  220. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  221. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  222. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  223. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  224. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  225. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  226. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  227. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  228. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  229. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  230. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  231. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  232. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  233. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  234. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  235. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  236. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  237. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  238. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  239. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  240. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  241. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  242. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  243. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  244. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  245. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  246. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  247. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  248. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  249. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  250. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  251. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  252. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  253. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  254. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  255. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  256. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  257. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  258. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  259. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  260. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  261. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  262. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  263. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  264. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,530 +1,429 @@
1
- # CryptoKit Symmetric Cryptography
1
+ # CryptoKit: Hashing, MACs and Symmetric Encryption
2
2
 
3
- > **Scope:** SHA-2/SHA-3 hashing, HMAC authentication, AES-GCM and ChaChaPoly authenticated encryption, SymmetricKey management, nonce handling, key derivation (HKDF + PBKDF2), and CommonCrypto migration. iOS 13+ baseline; SHA-3 requires iOS 26+.
4
- >
5
- > **Key APIs:** `SHA256`, `SHA384`, `SHA512`, `SHA3_256` (iOS 26+), `HMAC`, `AES.GCM.seal/open`, `ChaChaPoly.seal/open`, `SymmetricKey`, `AES.GCM.Nonce`, `HKDF`, `SealedBox`
6
- >
7
- > **Cross-references:** [secure-enclave.md] for hardware-backed asymmetric keys · [cryptokit-public-key.md] for ECDSA/ECDH/HPKE · [credential-storage-patterns.md] for key storage in Keychain · [common-anti-patterns.md] for the top-5 AI mistakes including hardcoded keys and nonce reuse
3
+ This file covers the symmetric half of CryptoKit: digests (SHA-2 and SHA-3), HMAC,
4
+ the two AEAD ciphers (AES-GCM and ChaChaPoly), `SymmetricKey`, nonce handling, key
5
+ derivation with HKDF and PBKDF2, and moving old CommonCrypto code over. The baseline
6
+ is iOS 13; HKDF as a standalone type needs iOS 14; SHA-3 needs iOS 26.
8
7
 
9
- ---
8
+ Types you will meet below: `SHA256`, `SHA384`, `SHA512`, `SHA3_256`, `HMAC`,
9
+ `AES.GCM.seal` / `AES.GCM.open`, `ChaChaPoly.seal` / `ChaChaPoly.open`,
10
+ `SymmetricKey`, `AES.GCM.Nonce`, `HKDF`, and the per-cipher `SealedBox`.
10
11
 
11
12
  ## Contents
12
13
 
13
- - [Hashing: SHA-2 and SHA-3](#hashing-sha-2-and-sha-3)
14
- - [One-Shot Hashing](#one-shot-hashing)
15
- - [Streaming Hash for Large Files](#streaming-hash-for-large-files)
16
- - [SHA-3 with Availability Check](#sha-3-with-availability-check)
17
- - [Insecure Hash Functions](#insecure-hash-functions)
18
- - [HMAC: Message Authentication with Symmetric Keys](#hmac-message-authentication-with-symmetric-keys)
19
- - [AES-GCM: Authenticated Encryption in One Operation](#aes-gcm-authenticated-encryption-in-one-operation)
20
- - [Basic Encryption and Decryption](#basic-encryption-and-decryption)
21
- - [Associated Data (AAD)](#associated-data-aad)
22
- - [The Catastrophic Danger of Nonce Reuse](#the-catastrophic-danger-of-nonce-reuse)
23
- - [ChaChaPoly: Software-Friendly AEAD Alternative](#chachapoly-software-friendly-aead-alternative)
24
- - [Performance: AES-GCM vs ChaChaPoly on Apple Hardware](#performance-aes-gcm-vs-chachapoly-on-apple-hardware)
25
- - [Streaming Encryption Limitation](#streaming-encryption-limitation)
26
- - [SymmetricKey: Creation, Derivation, and Lifecycle](#symmetrickey-creation-derivation-and-lifecycle)
27
- - [Random Key Generation](#random-key-generation)
28
- - [Password-Based Key Derivation (PBKDF2 + HKDF)](#password-based-key-derivation-pbkdf2-hkdf)
29
- - [HKDF for High-Entropy Key Derivation](#hkdf-for-high-entropy-key-derivation)
30
- - [Key Storage and Hardcoding](#key-storage-and-hardcoding)
31
- - [Migrating from CommonCrypto to CryptoKit](#migrating-from-commoncrypto-to-cryptokit)
32
- - [Hashing: CC_SHA256 → SHA256](#hashing-ccsha256-sha256)
33
- - [Encryption: CCCrypt (AES-CBC) → AES.GCM](#encryption-cccrypt-aes-cbc-aesgcm)
34
- - [HMAC: CCHmac → HMAC](#hmac-cchmac-hmac)
35
- - [What to Keep in CommonCrypto](#what-to-keep-in-commoncrypto)
36
- - [AI Code Generator Mistakes](#ai-code-generator-mistakes)
37
- - [Quantum Considerations for Symmetric Cryptography](#quantum-considerations-for-symmetric-cryptography)
38
- - [OWASP Mapping](#owasp-mapping)
39
- - [Testing Guidance](#testing-guidance)
40
- - [WWDC and Reference Citations](#wwdc-and-reference-citations)
41
- - [Conclusion](#conclusion)
42
- - [Summary Checklist](#summary-checklist)
43
-
44
- ## Hashing: SHA-2 and SHA-3
45
-
46
- CryptoKit's hash functions follow a unified `HashFunction` protocol. The SHA-2 family (`SHA256`, `SHA384`, `SHA512`) ships with iOS 13+. The SHA-3 family (`SHA3_256`, `SHA3_384`, `SHA3_512`) requires **iOS 26+ / macOS 26+** (added via apple/swift-crypto PR #397, tagged [WWDC25]).
47
-
48
- The swift-crypto open-source package provides SHA-3 under `import Crypto` for older deployment targets. The Apple CryptoKit framework (`import CryptoKit`) requires iOS 26+ for SHA-3. Do not confuse package availability with framework availability.
49
-
50
- All hash functions produce digest types that conform to `Sequence` (of `UInt8`), `ContiguousBytes`, `Hashable`, and `CustomStringConvertible`. Digest equality checks use **constant-time comparison** internally to prevent timing side-channels.
51
-
52
- ### One-Shot Hashing
53
-
54
- **✅ Correct: SHA-256 hashing with hex output**
14
+ - [Digests](#digests)
15
+ - [HMAC](#hmac)
16
+ - [AES-GCM](#aes-gcm)
17
+ - [ChaChaPoly](#chachapoly)
18
+ - [SymmetricKey](#symmetrickey)
19
+ - [Moving off CommonCrypto](#moving-off-commoncrypto)
20
+ - [Frequent mistakes](#frequent-mistakes)
21
+ - [Post-quantum outlook](#post-quantum-outlook)
22
+ - [OWASP mapping](#owasp-mapping)
23
+ - [Tests worth writing](#tests-worth-writing)
24
+ - [Sources](#sources)
25
+ - [Checklist](#checklist)
55
26
 
56
- ```swift
57
- import CryptoKit
27
+ ## Digests
58
28
 
59
- let data = "Hello, CryptoKit".data(using: .utf8)!
60
- let digest = SHA256.hash(data: data)
29
+ Every hash type conforms to `HashFunction`.
61
30
 
62
- // Convert to hex string - Digest conforms to Sequence
63
- let hexString = digest.map { String(format: "%02x", $0) }.joined()
31
+ | Family | Types | Availability |
32
+ | --- | --- | --- |
33
+ | SHA-2 | `SHA256`, `SHA384`, `SHA512` | iOS 13+, macOS 10.15+ |
34
+ | SHA-3 | `SHA3_256`, `SHA3_384`, `SHA3_512` | iOS 26+, macOS 26+ (added alongside the WWDC25 release, mirrored in swift-crypto PR #397) |
64
35
 
65
- // Constant-time comparison
66
- let otherDigest = SHA256.hash(data: data)
67
- if digest == otherDigest {
68
- print("Integrity verified")
69
- }
70
- ```
36
+ The open-source swift-crypto package (`import Crypto`) ships SHA-3 for older
37
+ deployment targets, but that does not make it available in Apple's framework:
38
+ `import CryptoKit` still requires the iOS 26 SDK and runtime. Check which module
39
+ you import before assuming availability.
71
40
 
72
- Never rely on `.description` for hex output - Apple warns its format may change between OS versions.
41
+ A digest value is a `Sequence` of `UInt8`, conforms to `ContiguousBytes`,
42
+ `Hashable` and `CustomStringConvertible`, and its `==` compares in constant time.
73
43
 
74
- ### Streaming Hash for Large Files
44
+ ```swift
45
+ import CryptoKit
46
+ import Foundation
75
47
 
76
- **✅ Correct: Incremental hashing to avoid loading entire file into memory**
48
+ let manifest = Data("release-manifest-v4".utf8)
49
+ let fingerprint = SHA256.hash(data: manifest)
50
+ let hexFingerprint = fingerprint.map { String(format: "%02x", $0) }.joined()
77
51
 
78
- ```swift
79
- var hasher = SHA256()
80
- let fileHandle = try FileHandle(forReadingFrom: fileURL)
81
- while autoreleasepool(invoking: {
82
- let chunk = fileHandle.readData(ofLength: 1_048_576) // 1 MB chunks
83
- guard !chunk.isEmpty else { return false }
84
- hasher.update(data: chunk)
85
- return true
86
- }) {}
87
- let digest = hasher.finalize()
52
+ let expected = SHA256.hash(data: Data("release-manifest-v4".utf8))
53
+ let matches = fingerprint == expected
88
54
  ```
89
55
 
90
- All hash functions support `init()` → `update(data:)` → `finalize()`. The `autoreleasepool` wrapper prevents memory accumulation during chunk reads.
91
-
92
- ### SHA-3 with Availability Check
56
+ Build hex strings yourself as above. Do not rely on `.description`; Apple states
57
+ its format is not stable.
93
58
 
94
- **✅ Correct: SHA-3 with fallback (iOS 26+)**
59
+ Large inputs should be streamed so the whole file never sits in memory. Read in
60
+ chunks (1 MB works well) and wrap each read in `autoreleasepool` so the
61
+ temporary `Data` buffers are released per iteration:
95
62
 
96
63
  ```swift
97
- func computeHash(data: Data) -> String {
98
- if #available(iOS 26.0, macOS 26.0, *) {
99
- let digest = SHA3_256.hash(data: data)
100
- return digest.map { String(format: "%02x", $0) }.joined()
101
- } else {
102
- let digest = SHA256.hash(data: data)
103
- return digest.map { String(format: "%02x", $0) }.joined()
64
+ func digestOfFile(at url: URL) throws -> SHA256.Digest {
65
+ let handle = try FileHandle(forReadingFrom: url)
66
+ defer { try? handle.close() }
67
+ var hasher = SHA256()
68
+ var reachedEnd = false
69
+ while !reachedEnd {
70
+ try autoreleasepool {
71
+ let block = try handle.read(upToCount: 1_048_576) ?? Data()
72
+ if block.isEmpty {
73
+ reachedEnd = true
74
+ } else {
75
+ hasher.update(data: block)
76
+ }
77
+ }
104
78
  }
79
+ return hasher.finalize()
105
80
  }
106
81
  ```
107
82
 
108
- SHA-3 uses a completely different internal construction (Keccak sponge) from SHA-2 (Merkle-Damgård). The API surface is identical - only the type name changes. Adopt SHA-3 when compliance standards require it or for defense-in-depth against future SHA-2 structural weaknesses.
109
-
110
- ### Insecure Hash Functions
111
-
112
- **❌ Wrong: Using MD5 or SHA-1 for any security purpose**
83
+ SHA-3 uses the Keccak sponge construction rather than SHA-2's Merkle-Damgard
84
+ chaining, but the Swift API is the same. Reach for it when a compliance profile
85
+ names it or you want algorithm diversity. Gate it and fall back:
113
86
 
114
87
  ```swift
115
- // NEVER - MD5 collision resistance is ~2^18 operations (seconds on commodity hardware)
116
- let broken = Insecure.MD5.hash(data: data)
117
-
118
- // SHA-1 fell to chosen-prefix collisions in 2020 (~$45,000 GPU time)
119
- let alsoBroken = Insecure.SHA1.hash(data: data)
88
+ func contentTag(for payload: Data) -> Data {
89
+ if #available(iOS 26.0, macOS 26.0, *) {
90
+ return Data(SHA3_256.hash(data: payload))
91
+ }
92
+ return Data(SHA256.hash(data: payload))
93
+ }
120
94
  ```
121
95
 
122
- CryptoKit deliberately places both in the `Insecure` namespace as an API-level warning. Use `SHA256` minimum for all security purposes - it is equally fast on modern hardware and provides actual collision resistance.
123
-
124
- **Algorithm selection quick reference:**
96
+ Broken hashes live under `Insecure` on purpose. `Insecure.MD5` collisions take on
97
+ the order of 2^18 operations (seconds on a laptop), and `Insecure.SHA1` fell to a
98
+ practical chosen-prefix collision in 2020 costing roughly $45,000 of GPU time.
99
+ Neither belongs anywhere security matters; SHA-256 is the floor.
125
100
 
126
- | Algorithm | Type | Availability | Status | Use When |
127
- | --------- | --------------- | ------------ | ---------- | ------------------------------------------ |
128
- | SHA-256 | `SHA256` | iOS 13+ | Strong | Default for integrity, signing, HMAC |
129
- | SHA-384 | `SHA384` | iOS 13+ | Strong | Certificate chains, higher security margin |
130
- | SHA-512 | `SHA512` | iOS 13+ | Strong | Large data, performance on 64-bit |
131
- | SHA3-256 | `SHA3_256` | iOS 26+ | Strong | Compliance requiring SHA-3 |
132
- | SHA3-384 | `SHA3_384` | iOS 26+ | Strong | Future-proofing |
133
- | SHA3-512 | `SHA3_512` | iOS 26+ | Strong | High-security contexts |
134
- | MD5 | `Insecure.MD5` | iOS 13+ | **Broken** | Legacy non-security checksums only |
135
- | SHA-1 | `Insecure.SHA1` | iOS 13+ | **Broken** | Legacy non-security checksums only |
101
+ | Algorithm | Pick it for |
102
+ | --- | --- |
103
+ | SHA-256 | Default: integrity checks, signatures, HMAC |
104
+ | SHA-384 | Certificate chains, extra margin |
105
+ | SHA-512 | Large data, faster on 64-bit cores |
106
+ | SHA3-256 | Compliance requirements naming SHA-3 |
107
+ | SHA3-384 | Longer-term margin |
108
+ | SHA3-512 | Highest-assurance profiles |
109
+ | MD5, SHA-1 | Broken. Only for legacy, non-security checksums |
136
110
 
137
- ---
111
+ ## HMAC
138
112
 
139
- ## HMAC: Message Authentication with Symmetric Keys
140
-
141
- HMAC combines a hash function with a secret key to produce an authentication code. CryptoKit's `HMAC<H>` is generic over any `HashFunction`, provides constant-time verification, and supports both one-shot and streaming patterns.
142
-
143
- **✅ Correct: HMAC generation and verification**
113
+ `HMAC<H>` is generic over any `HashFunction`, supports one-shot and incremental
114
+ use, and verifies in constant time.
144
115
 
145
116
  ```swift
146
- import CryptoKit
147
-
148
- let key = SymmetricKey(size: .bits256)
149
- let message = "Transfer $500 to account 12345".data(using: .utf8)!
117
+ let webhookKey = SymmetricKey(size: .bits256)
118
+ let body = Data(#"{"event":"invoice.paid"}"#.utf8)
150
119
 
151
- // Generate authentication code
152
- let mac = HMAC<SHA256>.authenticationCode(for: message, using: key)
120
+ let tag = HMAC<SHA256>.authenticationCode(for: body, using: webhookKey)
121
+ let tagBytes = Data(tag)
153
122
 
154
- // Verify - constant-time comparison prevents timing attacks
155
- let isValid = HMAC<SHA256>.isValidAuthenticationCode(
156
- mac, authenticating: message, using: key
123
+ let authentic = HMAC<SHA256>.isValidAuthenticationCode(
124
+ tagBytes, authenticating: body, using: webhookKey
157
125
  )
158
-
159
- // Serialize MAC for transmission
160
- let macData = Data(mac)
161
126
  ```
162
127
 
163
- **Critical:** Always use `isValidAuthenticationCode(_:authenticating:using:)` for verification - never manually compare raw bytes with `==`. CryptoKit's method uses `safeCompare` internally, which runs in constant time regardless of how many bytes match, defeating timing side-channel attacks.
164
-
165
- The return type `HMAC<SHA256>.MAC` (alias for `HashedAuthenticationCode<SHA256>`) conforms to `ContiguousBytes`, `Sequence`, `Hashable`, and `CustomStringConvertible`.
166
-
167
- **Common HMAC use cases:** API request signing, webhook payload verification, data integrity in transit, token-based authentication schemes. HMAC proves authenticity and integrity - not confidentiality. For encryption, use AES-GCM or ChaChaPoly below.
168
-
169
- ---
128
+ Verification must go through `isValidAuthenticationCode(_:authenticating:using:)`,
129
+ which compares safely in constant time. Never compare MAC bytes with `==` on `Data`.
170
130
 
171
- ## AES-GCM: Authenticated Encryption in One Operation
131
+ `HMAC<SHA256>.MAC` is a typealias for `HashedAuthenticationCode<SHA256>`, which is
132
+ `ContiguousBytes`, `Sequence`, `Hashable` and `CustomStringConvertible`.
172
133
 
173
- AES-GCM is CryptoKit's primary symmetric cipher, providing **Authenticated Encryption with Associated Data (AEAD)** - confidentiality, integrity, and authenticity in a single `seal()` call. This eliminates the historically dangerous pattern of combining AES-CBC + HMAC manually.
134
+ Typical uses: signing API requests, verifying webhooks, integrity of data in transit,
135
+ token schemes. HMAC proves who produced the data and that it was not changed; it
136
+ does not hide the data.
174
137
 
175
- ### Basic Encryption and Decryption
138
+ ## AES-GCM
176
139
 
177
- **✅ Correct: AES-GCM encryption with automatic nonce**
140
+ AES-GCM is the cipher to use by default. It is an AEAD mode: one `seal()` call gives
141
+ confidentiality, integrity and authenticity, replacing the old AES-CBC plus
142
+ separate HMAC construction.
178
143
 
179
144
  ```swift
180
- import CryptoKit
181
-
182
- let key = SymmetricKey(size: .bits256)
183
- let plaintext = "Sensitive data".data(using: .utf8)!
145
+ let vaultKey = SymmetricKey(size: .bits256)
146
+ let note = Data("door code 4411".utf8)
184
147
 
185
- // Encrypt - CryptoKit auto-generates a random 12-byte nonce
186
- let sealedBox = try AES.GCM.seal(plaintext, using: key)
187
-
188
- // Serialize for storage/transmission: nonce(12) || ciphertext || tag(16)
189
- guard let combined = sealedBox.combined else {
190
- fatalError("Combined representation unavailable (non-standard nonce size)")
191
- }
148
+ let box = try AES.GCM.seal(note, using: vaultKey)
149
+ guard let stored = box.combined else { throw CryptoKitError.incorrectParameterSize }
192
150
 
193
- // Deserialize and decrypt
194
- let restoredBox = try AES.GCM.SealedBox(combined: combined)
195
- let decrypted = try AES.GCM.open(restoredBox, using: key)
151
+ let reopened = try AES.GCM.SealedBox(combined: stored)
152
+ let recovered = try AES.GCM.open(reopened, using: vaultKey)
196
153
  ```
197
154
 
198
- The `SealedBox` contains three components: a **12-byte nonce**, the **ciphertext** (same length as plaintext), and a **16-byte authentication tag**. The `combined` property is `Data?` (optional) because non-standard nonce sizes prevent combined representation. For ChaChaPoly, `combined` is non-optional.
155
+ `seal` picks a fresh random 12-byte nonce when you do not pass one. A sealed box
156
+ holds three parts: the 12-byte nonce, ciphertext of the same length as the
157
+ plaintext, and a 16-byte tag. `combined` lays them out as nonce, ciphertext, tag.
199
158
 
200
- ### Associated Data (AAD)
159
+ `AES.GCM.SealedBox.combined` is `Data?`: it is nil when the box was built with a
160
+ non-standard nonce length. `ChaChaPoly.SealedBox.combined` is not optional.
201
161
 
202
- **✅ Correct: Binding ciphertext to context with associated data**
162
+ ### Additional authenticated data
203
163
 
204
- ```swift
205
- let metadata = "user:42,action:payment".data(using: .utf8)!
206
- let sealedBox = try AES.GCM.seal(plaintext, using: key, authenticating: metadata)
164
+ Pass context that must match on decryption but need not be secret:
207
165
 
208
- // Decryption requires the same AAD - tampered metadata causes authenticationFailure
209
- let decrypted = try AES.GCM.open(sealedBox, using: key, authenticating: metadata)
166
+ ```swift
167
+ let context = Data("owner=81923|doc=receipt-77|v=2".utf8)
168
+ let bound = try AES.GCM.seal(note, using: vaultKey, authenticating: context)
169
+ let plain = try AES.GCM.open(bound, using: vaultKey, authenticating: context)
210
170
  ```
211
171
 
212
- Associated data is authenticated but **not encrypted**. Use it to bind ciphertext to context (user ID, timestamp, resource identifier) so encrypted data cannot be transplanted to a different context without detection.
172
+ AAD is authenticated but not encrypted. Binding a user id, resource id, version or
173
+ timestamp stops an attacker from moving a valid ciphertext into another record. A
174
+ mismatch makes `open` throw `CryptoKitError.authenticationFailure`.
213
175
 
214
- ### The Catastrophic Danger of Nonce Reuse
176
+ ### Nonce reuse
215
177
 
216
- **❌ CRITICAL: Never reuse a nonce with the same key**
178
+ This is the classic mistake:
217
179
 
218
180
  ```swift
219
- // CATASTROPHIC - enables FULL key recovery
220
- let staticNonce = try AES.GCM.Nonce(data: Data(repeating: 0, count: 12))
221
- let box1 = try AES.GCM.seal(message1, using: key, nonce: staticNonce)
222
- let box2 = try AES.GCM.seal(message2, using: key, nonce: staticNonce)
223
- // With C1 and C2, attacker computes: C1 ⊕ C2 = P1 ⊕ P2
181
+ // Broken: the same fixed nonce for every message
182
+ let fixedNonce = try AES.GCM.Nonce(data: Data(count: 12))
183
+ let first = try AES.GCM.seal(Data("alpha".utf8), using: vaultKey, nonce: fixedNonce)
184
+ let second = try AES.GCM.seal(Data("bravo".utf8), using: vaultKey, nonce: fixedNonce)
224
185
  ```
225
186
 
226
- Nonce reuse in AES-GCM is not "bad practice" - it is a **total cryptographic break** known as the "Forbidden Attack" (Joux, 2006):
227
-
228
- 1. **Plaintext recovery:** Identical nonce + key produces identical keystream. XORing two ciphertexts yields `P1 ⊕ P2`. If either plaintext is known or guessable, the other is immediately recovered.
229
- 2. **Authentication forgery:** GCM's authentication uses GHASH, a polynomial over GF(2^128) with a secret hash key `H = AES_k(0^128)`. Two messages sharing a nonce yield a polynomial equation solvable via Cantor-Zassenhaus root-finding to recover H. Once H is known, the attacker can **forge valid authentication tags for arbitrary messages**.
230
-
231
- A USENIX WOOT'16 study found 184 HTTPS servers reusing AES-GCM nonces in production, including financial institutions.
187
+ Two messages under one key and one nonce share a keystream, so XOR of the
188
+ ciphertexts equals XOR of the plaintexts. Worse, the authentication subkey
189
+ H = AES_k(0^128) can be recovered by root finding (Cantor-Zassenhaus), after which
190
+ tags can be forged for any message. This is the "forbidden attack" described by
191
+ Joux in 2006, and a 2016 USENIX WOOT study found 184 HTTPS servers, some at
192
+ financial institutions, reusing GCM nonces in the wild.
232
193
 
233
- **The fix:** Omit the `nonce:` parameter entirely. CryptoKit generates cryptographically random 12-byte nonces automatically, giving collision probability below 2^-32 after 2^32 encryptions under the same key. Only supply explicit nonces when interoperating with external systems that dictate nonce values.
194
+ The fix is to omit `nonce:`. With random 12-byte nonces the chance of any
195
+ collision stays below 2^-32 after 2^32 messages under one key. Supply an explicit
196
+ nonce only when an external protocol defines it, and then follow that protocol's
197
+ counter rules exactly.
234
198
 
235
- ---
199
+ ## ChaChaPoly
236
200
 
237
- ## ChaChaPoly: Software-Friendly AEAD Alternative
238
-
239
- ChaCha20-Poly1305 provides equivalent AEAD security with an identical API surface. It exists primarily for **software-only environments** where it delivers constant-time execution without hardware acceleration, eliminating cache-timing side channels that plague software AES implementations.
240
-
241
- **✅ Correct: ChaChaPoly encryption**
201
+ ChaCha20-Poly1305 gives the same AEAD guarantees with the same API shape; switching
202
+ is a type-name change.
242
203
 
243
204
  ```swift
244
- let key = SymmetricKey(size: .bits256)
245
- let sealedBox = try ChaChaPoly.seal(plaintext, using: key)
246
-
247
- // ChaChaPoly.SealedBox.combined is non-optional (unlike AES.GCM)
248
- let combined = sealedBox.combined
249
-
250
- // Decrypt
251
- let restoredBox = try ChaChaPoly.SealedBox(combined: combined)
252
- let decrypted = try ChaChaPoly.open(restoredBox, using: key)
205
+ let sealed = try ChaChaPoly.seal(note, using: vaultKey)
206
+ let wire = sealed.combined
207
+ let parsed = try ChaChaPoly.SealedBox(combined: wire)
208
+ let opened = try ChaChaPoly.open(parsed, using: vaultKey)
253
209
  ```
254
210
 
255
- The API mirrors AES-GCM exactly - same `seal`/`open` methods, same `SealedBox` structure. Switching between ciphers requires changing only the type name.
256
-
257
- ### Performance: AES-GCM vs ChaChaPoly on Apple Hardware
258
-
259
- On all Apple Silicon (A-series since A7, all M-series), **AES-GCM is significantly faster** due to dedicated hardware AES instructions:
260
-
261
- | Metric | AES-256-GCM | ChaChaPoly | Source |
262
- | ------------------- | ------------------------------------------------------------- | ----------- | ----------------------- |
263
- | Throughput (M2 Pro) | ~3-4 GB/s | ~1.5-2 GB/s | OpenSSL benchmarks |
264
- | Relative speed | 134%-236% faster | Baseline | Ashvardanian (2025) |
265
- | Apple internal use | Keychain encryption, file Data Protection, Watch↔iPhone comms | - | Platform Security Guide |
266
-
267
- **Default to AES-GCM on Apple hardware.** Choose ChaChaPoly when: targeting platforms without hardware AES acceleration, requiring guaranteed constant-time behavior independent of hardware, or interoperating with ChaCha20-based protocols (WireGuard, some TLS configurations).
211
+ Every Apple chip since A7, and every M-series chip, has AES instructions. Published
212
+ 2025 benchmarks on an M2 Pro (OpenSSL) put AES-256-GCM at roughly 3-4 GB/s and
213
+ ChaChaPoly at roughly 1.5-2 GB/s, AES being 134% to 236% faster depending on size.
214
+ Apple's own keychain, file Data Protection and Watch-to-iPhone links all use AES.
268
215
 
269
- ### Streaming Encryption Limitation
216
+ So: AES-GCM on Apple hardware. Choose ChaChaPoly when there is no hardware AES,
217
+ when you need guaranteed constant-time software (no table lookups, no cache-timing
218
+ side channel), or when a protocol such as WireGuard or a ChaCha20 TLS suite
219
+ specifies it.
270
220
 
271
- Neither `seal()` nor `open()` supports streaming - both operate on the full message in memory. For large files, implement a **chunked AEAD scheme** with unique nonces per chunk and a monotonic chunk index in AAD to prevent reordering attacks. Alternatively, use Apple's file-level Data Protection (AES-XTS via the hardware crypto engine) for at-rest file encryption.
221
+ Neither cipher streams. For large files, either split into chunks with a unique
222
+ nonce per chunk and the chunk index (monotonic) in the AAD so chunks cannot be
223
+ reordered or dropped silently, or lean on file Data Protection, which the hardware
224
+ engine applies with AES-XTS.
272
225
 
273
- ---
226
+ ## SymmetricKey
274
227
 
275
- ## SymmetricKey: Creation, Derivation, and Lifecycle
228
+ `SymmetricKey` zeroes its memory when deallocated (WWDC 2019 session 709), exposes
229
+ no `Data` property (only `withUnsafeBytes`), and validates its size.
276
230
 
277
- `SymmetricKey` is CryptoKit's opaque key container. It **zeroes memory on deallocation** (confirmed WWDC 2019-709 and Apple documentation), prevents accidental exposure (no `Data` property - only `withUnsafeBytes` access), and validates key sizes at construction.
231
+ `SymmetricKey(size: .bits256)` is 32 random bytes; `.bits128` and `.bits192` also
232
+ exist. Use 256 bits: Grover's algorithm halves effective strength, leaving AES-256
233
+ at 128 bits but AES-128 at 64 bits, which is not enough.
278
234
 
279
- ### Random Key Generation
280
-
281
- **✅ Correct: Cryptographically random key**
282
-
283
- ```swift
284
- let key = SymmetricKey(size: .bits256) // 32 bytes, cryptographically random
285
- // Also available: .bits128, .bits192
286
- ```
287
-
288
- For quantum resilience, prefer `.bits256`. Grover's algorithm halves effective symmetric key strength - AES-256 retains 128-bit security against quantum adversaries, while AES-128 drops to 64-bit (insufficient).
289
-
290
- ### Password-Based Key Derivation (PBKDF2 + HKDF)
291
-
292
- **❌ Wrong: Raw password as key material**
235
+ ### Passwords are not keys
293
236
 
294
237
  ```swift
295
- // NEVER - passwords have ~20-40 bits of entropy, not 256
296
- let key = SymmetricKey(data: "MyPassword123".data(using: .utf8)!)
297
- // Trivially brute-forceable via dictionary attack - no computational cost barrier, no salt
238
+ // Broken: a password is low-entropy, unsalted and free to guess
239
+ let weak = SymmetricKey(data: Data("summer2026".utf8))
298
240
  ```
299
241
 
300
- CryptoKit ships HKDF but **not** PBKDF2. For password-based key derivation, use CommonCrypto's `CCKeyDerivationPBKDF` first, then optionally HKDF for subkey derivation:
301
-
302
- **✅ Correct: Password → key via PBKDF2 + HKDF**
242
+ A human password carries around 20-40 bits of entropy, and this has no salt and no
243
+ work factor. CryptoKit provides HKDF but not PBKDF2, so stretch the password with
244
+ CommonCrypto first, then optionally split it with HKDF:
303
245
 
304
246
  ```swift
305
247
  import CommonCrypto
306
- import CryptoKit
307
-
308
- // Step 1: PBKDF2 stretches the low-entropy password
309
- let password = "MyPassword123"
310
- let salt = Data((0..<32).map { _ in UInt8.random(in: 0...255) })
311
- var derivedBytes = [UInt8](repeating: 0, count: 32)
312
-
313
- CCKeyDerivationPBKDF(
314
- CCPBKDFAlgorithm(kCCPBKDF2),
315
- password, password.utf8.count,
316
- Array(salt), salt.count,
317
- CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256),
318
- 600_000, // OWASP 2023 recommended minimum for HMAC-SHA256
319
- &derivedBytes, derivedBytes.count
320
- )
321
248
 
322
- // Step 2: HKDF derives purpose-specific subkeys (domain separation)
323
- let masterKey = SymmetricKey(data: derivedBytes)
324
- let encryptionKey = HKDF<SHA256>.deriveKey(
325
- inputKeyMaterial: masterKey,
326
- info: Data("encryption".utf8),
327
- outputByteCount: 32
328
- )
329
- let authKey = HKDF<SHA256>.deriveKey(
330
- inputKeyMaterial: masterKey,
331
- info: Data("authentication".utf8),
332
- outputByteCount: 32
333
- )
334
- ```
335
-
336
- > **Iteration count note:** The OWASP Password Storage Cheat Sheet recommends **600,000 iterations minimum** for PBKDF2-HMAC-SHA256. Use at least 600,000 for new implementations; only use lower counts when supporting legacy interoperability with documented justification.
337
-
338
- **Critical distinction:** HKDF is designed for already-high-entropy input (shared secrets, master keys). It does **not** add computational cost. Never use HKDF alone for passwords - always PBKDF2 first.
339
-
340
- ### HKDF for High-Entropy Key Derivation
341
-
342
- **✅ Correct: Deriving subkeys from a high-entropy master key**
343
-
344
- ```swift
345
- // When input is already high-entropy (e.g., ECDH shared secret)
346
- let inputKey = SymmetricKey(size: .bits256)
347
- let derivedKey = HKDF<SHA256>.deriveKey(
348
- inputKeyMaterial: inputKey,
349
- salt: Data("app-specific-salt".utf8),
350
- info: Data("aes-encryption-key-v1".utf8),
351
- outputByteCount: 32
352
- )
353
- ```
354
-
355
- HKDF follows RFC 5869 and supports one-shot `deriveKey()` and two-phase `extract()` → `expand()`. Use distinct `info` strings for domain separation when deriving multiple subkeys from a single shared secret. Available since iOS 14+.
356
-
357
- > **API note:** `HKDF.deriveKey()` does not throw - no `try` required despite some code examples showing it.
358
-
359
- ### Key Storage and Hardcoding
249
+ func stretch(passphrase: String, salt: Data, rounds: UInt32 = 600_000) -> SymmetricKey? {
250
+ let secret = Array(passphrase.utf8)
251
+ var output = [UInt8](repeating: 0, count: 32)
252
+ let status = salt.withUnsafeBytes { saltBytes in
253
+ CCKeyDerivationPBKDF(
254
+ CCPBKDFAlgorithm(kCCPBKDF2),
255
+ secret.map { Int8(bitPattern: $0) }, secret.count,
256
+ saltBytes.bindMemory(to: UInt8.self).baseAddress, salt.count,
257
+ CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256), rounds,
258
+ &output, output.count
259
+ )
260
+ }
261
+ guard status == kCCSuccess else { return nil }
262
+ return SymmetricKey(data: output)
263
+ }
360
264
 
361
- **❌ Wrong: Hardcoding keys in source code**
265
+ struct PasswordKeys {
266
+ let salt: Data
267
+ let encryption: SymmetricKey
268
+ let authentication: SymmetricKey
269
+ }
362
270
 
363
- ```swift
364
- // NEVER - extractable via `strings` command on the binary
365
- let key = SymmetricKey(data: Data(base64Encoded: "c2VjcmV0S2V5MTIzNDU2Nzg5MDEyMzQ1Ng==")!)
271
+ func passwordKeys(for passphrase: String) -> PasswordKeys? {
272
+ var saltBytes = [UInt8](repeating: 0, count: 32)
273
+ guard SecRandomCopyBytes(kSecRandomDefault, saltBytes.count, &saltBytes) == errSecSuccess else { return nil }
274
+ let salt = Data(saltBytes)
275
+ guard let master = stretch(passphrase: passphrase, salt: salt) else { return nil }
276
+ return PasswordKeys(
277
+ salt: salt,
278
+ encryption: HKDF<SHA256>.deriveKey(
279
+ inputKeyMaterial: master, info: Data("encryption".utf8), outputByteCount: 32
280
+ ),
281
+ authentication: HKDF<SHA256>.deriveKey(
282
+ inputKeyMaterial: master, info: Data("authentication".utf8), outputByteCount: 32
283
+ )
284
+ )
285
+ }
366
286
  ```
367
287
 
368
- A Zimperium 2025 study found 48% of mobile apps contain hardcoded secrets. iOS binaries can be decrypted and analyzed with tools like Hopper or IDA Pro. **Store keys in the Keychain** with `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`, derive them at runtime from user credentials, or fetch from a secure server. See [credential-storage-patterns.md] for detailed patterns.
288
+ Keep the salt with the ciphertext: it is not secret, and deriving the same keys
289
+ again later needs it.
369
290
 
370
- **SymmetricKey memory behavior:** Keys live in regular process memory (not the Secure Enclave - only asymmetric `SecureEnclave.P256` keys are hardware-backed). CryptoKit automatically overwrites key material during deallocation. For persistent storage, serialize to the Keychain - never UserDefaults or files.
291
+ The OWASP Password Storage Cheat Sheet sets 600,000 PBKDF2-HMAC-SHA256 iterations
292
+ as the minimum; go lower only for a documented legacy interop requirement. Salts
293
+ must be random and at least 16 bytes (32 above).
371
294
 
372
- ---
295
+ For random bytes, `SecRandomCopyBytes` and `SymmetricKey(size:)` are the APIs to
296
+ use. On Apple platforms `arc4random_buf` and `SystemRandomNumberGenerator` also draw
297
+ from the kernel CSPRNG and are cryptographically secure, so they are not a
298
+ vulnerability; still prefer the Security and CryptoKit calls for key material so the
299
+ intent is explicit and the status is checked. What is never acceptable is a seeded
300
+ or non-cryptographic generator (`rand()`, `random()`, `srand48`, a custom LCG) or
301
+ `arc4random() % n` where modulo bias matters (use `arc4random_uniform`).
373
302
 
374
- ## Migrating from CommonCrypto to CryptoKit
303
+ ### HKDF
375
304
 
376
- CommonCrypto's C API requires manual buffer allocation, unsafe pointer management, and provides no authenticated encryption. CryptoKit replaces all common operations with type-safe Swift that is harder to misuse.
377
-
378
- ### Hashing: CC_SHA256 → SHA256
305
+ HKDF (RFC 5869) expands high-entropy input into subkeys. It adds no work factor, so
306
+ it is never enough on its own for a password.
379
307
 
380
308
  ```swift
381
- // ❌ Legacy CommonCrypto - unsafe pointers, manual buffer sizing
382
- import CommonCrypto
383
- var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
384
- data.withUnsafeBytes { bytes in
385
- CC_SHA256(bytes.baseAddress, CC_LONG(data.count), &digest)
386
- }
387
-
388
- // ✅ CryptoKit - one line, type-safe
389
- import CryptoKit
390
- let digest = SHA256.hash(data: data)
391
- ```
392
-
393
- ### Encryption: CCCrypt (AES-CBC) → AES.GCM
394
-
395
- ```swift
396
- // ❌ Legacy CommonCrypto - AES-CBC, unauthenticated, manual IV, buffer math
397
- import CommonCrypto
398
- var outputBuffer = [UInt8](repeating: 0, count: data.count + kCCBlockSizeAES128)
399
- var numBytesEncrypted = 0
400
- let status = CCCrypt(
401
- CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES),
402
- CCOptions(kCCOptionPKCS7Padding),
403
- keyBytes, kCCKeySizeAES256, ivBytes,
404
- dataBytes, data.count,
405
- &outputBuffer, outputBuffer.count, &numBytesEncrypted
309
+ let rootKey = SymmetricKey(size: .bits256)
310
+ let sessionKey = HKDF<SHA256>.deriveKey(
311
+ inputKeyMaterial: rootKey,
312
+ salt: Data("session-salt-01".utf8),
313
+ info: Data("chat-session/aes".utf8),
314
+ outputByteCount: 32
406
315
  )
407
- // ⚠️ Still need to add HMAC separately for integrity!
408
-
409
- // ✅ CryptoKit - one line, authenticated, automatic nonce
410
- import CryptoKit
411
- let sealedBox = try AES.GCM.seal(data, using: key)
412
316
  ```
413
317
 
414
- The critical architectural shift: CommonCrypto's `CCCrypt` provides AES-CBC (unauthenticated). Without manual Encrypt-then-MAC (HMAC), CBC ciphertext is vulnerable to **padding oracle attacks** and silent tampering. CryptoKit's AES-GCM bundles authentication - `open()` throws `CryptoKitError.authenticationFailure` if any byte is modified.
318
+ Use one-shot `deriveKey` or the two-step `extract` then `expand`. Give each subkey
319
+ its own `info` string for domain separation. The standalone `HKDF` type is iOS 14+.
320
+ `deriveKey` does not throw, so there is no `try`.
415
321
 
416
- ### HMAC: CCHmac → HMAC
322
+ ### Where keys live
417
323
 
418
324
  ```swift
419
- // ❌ Legacy CommonCrypto - C-style pointers
420
- import CommonCrypto
421
- var hmac = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
422
- CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256),
423
- keyBytes, keyData.count, dataBytes, data.count, &hmac)
424
-
425
- // ✅ CryptoKit - generic, type-safe, constant-time verification built in
426
- import CryptoKit
427
- let mac = HMAC<SHA256>.authenticationCode(for: data, using: key)
428
- let valid = HMAC<SHA256>.isValidAuthenticationCode(mac, authenticating: data, using: key)
325
+ // Broken: extractable from the binary with `strings`
326
+ let embedded = SymmetricKey(data: Data(base64Encoded: "q83vEjRWeJq8...")!)
429
327
  ```
430
328
 
431
- ### What to Keep in CommonCrypto
432
-
433
- CryptoKit deliberately omits: **PBKDF2** (use `CCKeyDerivationPBKDF`), **AES-CBC** (needed for legacy system interop), **AES-ECB** (almost never appropriate). For everything else, CryptoKit is the correct choice.
434
-
435
- ---
436
-
437
- ## AI Code Generator Mistakes
438
-
439
- Large language models producing iOS cryptography code frequently introduce these errors:
440
-
441
- **1. Using CommonCrypto instead of CryptoKit.** Models trained on older code default to `CC_SHA256` and `CCCrypt`. These require manual memory management and lack authenticated encryption. Always use CryptoKit for iOS 13+ targets.
442
-
443
- **2. Reusing or hardcoding nonces.** Generators sometimes create a nonce once and reuse it, or use `Data(repeating: 0, count: 12)`. This enables complete AES-GCM key recovery (see nonce reuse section above). Omit the `nonce:` parameter to use automatic generation.
444
-
445
- **3. Using AES-CBC without authentication.** Models produce `CCCrypt`-based AES-CBC without HMAC, leaving ciphertext vulnerable to padding oracle attacks. AES-GCM and ChaChaPoly authenticate by default - no reason for unauthenticated encryption in new code.
446
-
447
- **4. Creating SymmetricKey directly from a password string.** `SymmetricKey(data: password.data(using: .utf8)!)` appears constantly. This skips key stretching entirely. Use PBKDF2 (≥600,000 iterations) for passwords, then optionally HKDF for subkey derivation.
448
-
449
- **5. Recommending MD5 or SHA-1 for checksums.** Models suggest `Insecure.MD5` for file integrity. SHA-256 is equally fast on modern hardware with actual collision resistance.
450
-
451
- **6. Manual SealedBox serialization.** Generators sometimes manually concatenate nonce + ciphertext + tag instead of using `SealedBox.combined`. This introduces serialization bugs - use the built-in `combined` property and `SealedBox(combined:)` initializer.
452
-
453
- ---
454
-
455
- ## Quantum Considerations for Symmetric Cryptography
456
-
457
- WWDC 2025 session 314 ("Get ahead with quantum-secure cryptography") introduced ML-KEM and ML-DSA for asymmetric crypto (see [cryptokit-public-key.md]). For symmetric crypto, quantum computers weaken effective key strength by roughly half via Grover's algorithm:
458
-
459
- - **AES-256:** 128-bit post-quantum security - **sufficient**
460
- - **AES-128:** 64-bit post-quantum security - **insufficient**
461
-
462
- **Recommendation:** Use `SymmetricKey(size: .bits256)` exclusively. Quantum-secure TLS 1.3 is enabled by default in iOS 26 for `URLSession` and Network.framework connections.
463
-
464
- CryptoKit is built on Apple's **corecrypto** library (FIPS 140-2/140-3 validated, hand-tuned assembly per Apple microarchitecture). Apple's hardware crypto engine sits in the DMA path between flash storage and system memory, performing inline AES-256 encryption at line speed with zero CPU overhead.
465
-
466
- ---
467
-
468
- ## OWASP Mapping
469
-
470
- CryptoKit symmetric practices address **OWASP Mobile Top 10 M10 (Insufficient Cryptography)**: weak algorithms, insufficient key lengths, improper key management, flawed implementation.
471
-
472
- **Relevant MASTG test cases:** MASTG-TEST-0061 (algorithm configuration), MASTG-TEST-0062 (key management), MASTG-TEST-0209 (insufficient key sizes), MASTG-TEST-0210 (broken symmetric algorithms), MASTG-TEST-0211 (broken hashing), MASTG-TEST-0213 (hardcoded keys), MASTG-TEST-0317 (broken encryption modes).
473
-
474
- **MASTG knowledge base:** MASTG-KNOW-0066 (CryptoKit), MASTG-KNOW-0067 (CommonCrypto).
475
-
476
- **MASWE entries:** MASWE-0010 (improper key derivation), MASWE-0013 (hardcoded cryptographic keys), MASWE-0020 (improper encryption), MASWE-0021 (improper hashing), MASWE-0022 (predictable initialization vectors).
477
-
478
- See [compliance-owasp-mapping.md] for the full compliance matrix.
479
-
480
- ---
481
-
482
- ## Testing Guidance
483
-
484
- | Test Case | What It Proves | Expected Outcome |
485
- | ------------------------------------------ | ------------------------------ | -------------------------------------- |
486
- | AES-GCM decrypt after ciphertext tampering | Authentication works | `CryptoKitError.authenticationFailure` |
487
- | AES-GCM decrypt with wrong AAD | Metadata binding | `CryptoKitError.authenticationFailure` |
488
- | HMAC verify with wrong key | Timing-safe verification | Returns `false` |
489
- | HMAC verify with tampered message | Integrity detection | Returns `false` |
490
- | SHA-3 availability fallback | Backward compatibility | Falls back to SHA-256 on <iOS 26 |
491
- | SealedBox round-trip (combined format) | Serialization correctness | Decrypted output matches plaintext |
492
- | PBKDF2 + HKDF derivation determinism | Key derivation reproducibility | Same password + salt → same key |
493
-
494
- **CI scanning rules:** Flag `Insecure.MD5`, `Insecure.SHA1`, `CCCrypt`, `SymmetricKey(data:` followed by string literal, and hardcoded base64 key patterns in code review.
495
-
496
- ---
497
-
498
- ## WWDC and Reference Citations
499
-
500
- - **WWDC 2019-709** - "Cryptography and Your Apps": CryptoKit introduction, SymmetricKey memory zeroing, automatic nonce generation rationale
501
- - **WWDC 2020** - "What's New in CryptoKit": HKDF addition (iOS 14), expanded key agreement
502
- - **WWDC 2025 Session 314** - "Get ahead with quantum-secure cryptography": AES-256 quantum guidance, SHA-3 context, ML-KEM/ML-DSA (asymmetric)
503
- - **Apple CryptoKit Documentation** - https://sosumi.ai/documentation/cryptokit/
504
- - **Apple Platform Security Guide** - corecrypto FIPS validation, hardware crypto engine, file Data Protection
505
- - **OWASP Mobile Top 10 (2024)** - M10: Insufficient Cryptography
506
- - **OWASP MASTG** - iOS cryptographic testing methodology
507
- - **RFC 5869** - HKDF specification
508
- - **Joux (2006)** - "Authentication Failures in NIST version of GCM" (nonce reuse attack)
509
-
510
- ---
511
-
512
- ## Conclusion
513
-
514
- CryptoKit's design philosophy - authenticated encryption by default, automatic nonce generation, memory zeroing, constant-time comparisons - eliminates the most common categories of cryptographic implementation errors. For new code: `AES.GCM.seal()` with automatic nonces for encryption, `SHA256` (or `SHA3_256` on iOS 26+) for hashing, `HMAC<SHA256>` for authentication, and `SymmetricKey(size: .bits256)` for key generation. Derive keys from passwords with PBKDF2 (≥600,000 iterations, CommonCrypto) followed by HKDF (CryptoKit) - never pass raw passwords to `SymmetricKey(data:)`. Store keys in the Keychain, not source code. Prefer AES-GCM over ChaChaPoly on Apple hardware for the hardware acceleration advantage, but ChaChaPoly remains sound for cross-platform consistency or software-only environments.
515
-
516
- ---
517
-
518
- ## Summary Checklist
519
-
520
- 1. **CryptoKit over CommonCrypto** - All new hashing, HMAC, and encryption uses `import CryptoKit`, not `import CommonCrypto` (except PBKDF2)
521
- 2. **SHA-256 minimum** - No `Insecure.MD5` or `Insecure.SHA1` for any security purpose; CI rules flag these
522
- 3. **AES-GCM or ChaChaPoly** - All symmetric encryption uses AEAD; no unauthenticated AES-CBC in new code
523
- 4. **Automatic nonces** - The `nonce:` parameter is omitted from `seal()` calls unless protocol-mandated; no static or zero nonces
524
- 5. **256-bit keys** - `SymmetricKey(size: .bits256)` for quantum resilience; no `.bits128` for security-sensitive data
525
- 6. **PBKDF2 before HKDF for passwords** - Password → `CCKeyDerivationPBKDF` (≥600,000 iterations, ≥16-byte random salt) → `SymmetricKey` → optional HKDF for subkeys; never raw password to `SymmetricKey(data:)`
526
- 7. **SealedBox.combined for serialization** - Use `.combined` / `SealedBox(combined:)` for storage and network; no manual nonce/ciphertext/tag concatenation
527
- 8. **Keys in Keychain** - Symmetric keys persisted via Keychain with `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`; no hardcoded keys in source, no UserDefaults, no plist
528
- 9. **Constant-time HMAC verification** - Use `HMAC.isValidAuthenticationCode()`, never manual byte comparison
529
- 10. **SHA-3 availability guarded** - `SHA3_256` wrapped in `#available(iOS 26.0, macOS 26.0, *)` with SHA-256 fallback
530
- 11. **Associated data for context binding** - AES-GCM `authenticating:` parameter used when ciphertext must be bound to metadata (user ID, resource ID, version)
329
+ A 2025 industry study found hardcoded secrets in 48% of mobile apps, and any
330
+ disassembler recovers them from the shipped binary. Keys instead come from one of:
331
+ the Keychain with `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`, derivation from
332
+ something the user knows, or a secure server.
333
+
334
+ A `SymmetricKey` lives in process memory. It is not in the Secure Enclave; only the
335
+ asymmetric `SecureEnclave.P256` (and on iOS 26 the post-quantum SE types) are
336
+ hardware-backed (see [secure-enclave.md](secure-enclave.md)). Persist symmetric keys
337
+ only in the Keychain, never in UserDefaults or plain files
338
+ (see [credential-storage-patterns.md](credential-storage-patterns.md)).
339
+
340
+ ## Moving off CommonCrypto
341
+
342
+ CommonCrypto means manual buffers, unsafe pointers and no authenticated encryption.
343
+
344
+ | CommonCrypto | CryptoKit |
345
+ | --- | --- |
346
+ | `CC_SHA256` with `withUnsafeBytes` and `CC_SHA256_DIGEST_LENGTH` | `SHA256.hash(data:)` |
347
+ | `CCCrypt` AES-CBC, `kCCOptionPKCS7Padding`, `kCCKeySizeAES256`, manual IV, output buffer sized with `kCCBlockSizeAES128` | `AES.GCM.seal(_:using:)` |
348
+ | `CCHmac(kCCHmacAlgSHA256, ...)` | `HMAC<SHA256>.authenticationCode` and `isValidAuthenticationCode` |
349
+
350
+ CBC without encrypt-then-MAC is exposed to padding-oracle attacks and accepts
351
+ tampered ciphertext silently. GCM's `open` throws
352
+ `CryptoKitError.authenticationFailure` if any byte changed.
353
+
354
+ Keep CommonCrypto only for PBKDF2 (`CCKeyDerivationPBKDF`), interop with an existing
355
+ AES-CBC format, and AES-ECB, which is almost never the right choice.
356
+
357
+ ## Frequent mistakes
358
+
359
+ 1. Writing CommonCrypto for new code when the target is iOS 13+.
360
+ 2. Fixed, zero-filled or reused nonces. Leave `nonce:` out.
361
+ 3. AES-CBC with no MAC.
362
+ 4. Turning a password straight into a `SymmetricKey`. Use PBKDF2 with at least
363
+ 600,000 iterations, then HKDF if subkeys are needed.
364
+ 5. MD5 or SHA-1 "just for a checksum". SHA-256 is just as fast in practice.
365
+ 6. Hand-concatenating nonce, ciphertext and tag. Use `SealedBox.combined` and
366
+ `SealedBox(combined:)`.
367
+
368
+ ## Post-quantum outlook
369
+
370
+ WWDC 2025 session 314 introduced ML-KEM and ML-DSA for the asymmetric side (see
371
+ [cryptokit-public-key.md](cryptokit-public-key.md)). Symmetric primitives survive a
372
+ quantum adversary with roughly half their bit strength, so use
373
+ `SymmetricKey(size: .bits256)` and nothing smaller. iOS 26 also turns on
374
+ quantum-secure TLS 1.3 key exchange by default for `URLSession` and
375
+ Network.framework.
376
+
377
+ Under the hood CryptoKit calls corecrypto, which carries FIPS 140-2 / 140-3
378
+ validation and hand-tuned assembly per CPU microarchitecture, and the hardware
379
+ engine in the storage DMA path encrypts with AES-256 inline at line speed.
380
+
381
+ ## OWASP mapping
382
+
383
+ This material addresses OWASP Mobile Top 10 (2024) M10, Insufficient Cryptography.
384
+
385
+ - MASTG tests: MASTG-TEST-0061 (algorithm configuration), 0062 (key management),
386
+ 0209 (key sizes), 0210 (broken symmetric algorithms), 0211 (broken hashing),
387
+ 0213 (hardcoded keys), 0317 (broken modes).
388
+ - MASTG knowledge: MASTG-KNOW-0066 (CryptoKit), MASTG-KNOW-0067 (CommonCrypto).
389
+ - MASWE: 0010 (key derivation), 0013 (hardcoded keys), 0020 (improper encryption),
390
+ 0021 (improper hashing), 0022 (predictable IVs).
391
+
392
+ See also [common-anti-patterns.md](common-anti-patterns.md) (#6 nonce reuse, #7 weak
393
+ hashes) and [compliance-owasp-mapping.md](compliance-owasp-mapping.md).
394
+
395
+ ## Tests worth writing
396
+
397
+ - Flip one ciphertext byte; `open` throws `CryptoKitError.authenticationFailure`.
398
+ - Open with different AAD; same error.
399
+ - HMAC checked with the wrong key returns false.
400
+ - HMAC over a modified message returns false.
401
+ - Below iOS 26 the SHA-3 path falls back to SHA-256.
402
+ - A `combined` round trip returns the original plaintext.
403
+ - PBKDF2 plus HKDF is deterministic: same password and salt give the same key.
404
+
405
+ CI should flag `Insecure.MD5`, `Insecure.SHA1`, `CCCrypt`, `SymmetricKey(data:` fed
406
+ from a string literal, and base64 blobs that look like embedded keys.
407
+
408
+ ## Sources
409
+
410
+ WWDC 2019 session 709 (Cryptography and Your Apps); WWDC 2020 "What's New in
411
+ CryptoKit" (HKDF, iOS 14); WWDC 2025 session 314; the CryptoKit documentation; Apple
412
+ Platform Security Guide; OWASP Mobile Top 10 (2024) M10; OWASP MASTG; RFC 5869;
413
+ Joux 2006 on GCM nonce reuse.
414
+
415
+ ## Checklist
416
+
417
+ - [ ] CryptoKit everywhere except PBKDF2.
418
+ - [ ] Nothing weaker than SHA-256; CI flags MD5 and SHA-1.
419
+ - [ ] Only AEAD (AES-GCM or ChaChaPoly); no unauthenticated CBC.
420
+ - [ ] `nonce:` omitted unless a protocol dictates it; never static or zero.
421
+ - [ ] 256-bit keys; no `.bits128` for sensitive data.
422
+ - [ ] Passwords: `CCKeyDerivationPBKDF` with at least 600,000 iterations and a random
423
+ salt of 16 bytes or more, then HKDF if needed.
424
+ - [ ] Serialization through `.combined` and `SealedBox(combined:)`.
425
+ - [ ] Keys in the Keychain as WhenUnlockedThisDeviceOnly; never in source,
426
+ UserDefaults or a plist.
427
+ - [ ] MACs verified with `HMAC.isValidAuthenticationCode()`.
428
+ - [ ] `SHA3_256` behind `#available(iOS 26.0, macOS 26.0, *)` with a SHA-256 fallback.
429
+ - [ ] Metadata (user id, resource id, version) bound through `authenticating:`.