@mmerterden/multi-agent-pipeline 20.6.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 (326) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +328 -329
  5. package/package.json +2 -2
  6. package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
  7. package/pipeline/scripts/_notices.mjs +12 -1
  8. package/pipeline/scripts/gen-skills-index.mjs +1 -1
  9. package/pipeline/skills/.skill-manifest.json +106 -106
  10. package/pipeline/skills/shared/README.md +71 -71
  11. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
  12. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  13. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  14. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  15. package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
  16. package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
  17. package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
  18. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  19. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  20. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  21. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  22. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  23. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  24. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  25. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  26. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  27. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  28. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  29. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  30. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  31. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  32. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  33. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  34. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  35. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  36. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  37. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  38. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  39. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  40. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  41. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  42. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  43. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  44. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  45. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  46. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  47. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  48. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  49. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  50. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  51. package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
  52. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  53. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  54. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  55. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
  56. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  57. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  58. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  59. package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
  60. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
  61. package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
  62. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  63. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  64. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  65. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  66. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  67. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  68. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  69. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  70. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  71. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  72. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  73. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  74. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  75. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  76. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  77. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  78. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  79. package/pipeline/skills/shared/external/council/SKILL.md +1 -0
  80. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  81. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  82. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  83. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
  84. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
  85. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  86. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  87. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  88. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  89. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  90. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  91. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  92. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  93. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  94. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  95. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  96. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  97. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  98. package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
  99. package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
  100. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
  101. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  102. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  103. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  104. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  105. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  106. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  107. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
  108. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
  109. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  110. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  111. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  112. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  113. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  114. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
  115. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  116. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  117. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  118. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  119. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
  120. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  121. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  122. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  123. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  124. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  125. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  126. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  127. package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
  128. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  129. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  130. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  131. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  132. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  133. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  134. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
  135. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
  136. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
  137. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
  138. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
  139. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
  140. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
  141. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
  142. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
  143. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
  144. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
  145. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
  146. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
  147. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
  148. package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
  149. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
  150. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  151. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  152. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  153. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  154. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  155. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  156. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  157. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  158. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  159. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  160. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  161. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  162. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  163. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
  164. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  165. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  166. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  167. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  168. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  169. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  170. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  171. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  172. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  173. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  174. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  175. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  176. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  177. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  178. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  179. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  180. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  181. package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
  182. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  183. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  184. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  185. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  186. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
  187. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
  188. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  189. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  190. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  191. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
  192. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
  193. package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
  194. package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
  195. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  196. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  197. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  198. package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
  199. package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
  200. package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
  201. package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
  202. package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
  203. package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
  204. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
  205. package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
  206. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  207. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  208. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  209. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  210. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  211. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  212. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  213. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  214. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  215. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  216. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  217. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  218. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  219. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  220. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  221. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  222. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  223. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  224. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  225. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  226. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  227. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  228. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  229. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  230. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  231. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  232. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  233. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  234. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  235. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  236. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  237. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  238. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  239. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  240. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  241. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  242. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  243. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  244. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  245. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  246. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  247. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  248. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  249. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  250. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  251. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  252. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  253. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  254. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  255. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  256. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  257. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  258. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  259. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  260. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  261. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  262. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  263. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  264. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  265. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  266. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  267. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  268. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  269. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  270. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  271. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  272. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  273. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  274. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  275. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  276. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  277. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  278. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  279. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  280. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  281. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  282. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  283. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  284. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  285. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  286. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  287. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  288. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  289. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  290. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  291. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  292. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  293. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  294. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  295. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  296. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  297. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  298. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  299. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  300. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  301. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  302. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  303. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  304. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  305. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  306. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  307. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  308. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  309. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  310. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
  311. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
  312. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  313. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  314. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
  315. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  316. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  317. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  318. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
  319. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  320. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  321. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
  322. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
  323. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
  324. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  325. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  326. 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:`.