@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.
- package/CHANGELOG.md +9 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +266 -267
- package/package.json +2 -2
- package/pipeline/scripts/_notices.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +68 -68
- package/pipeline/skills/shared/README.md +70 -70
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md
CHANGED
|
@@ -1,573 +1,366 @@
|
|
|
1
|
-
# Compliance
|
|
1
|
+
# OWASP Mobile Compliance for Apple Platforms
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This file connects everyday iOS security code with three OWASP documents as they stand for 2024 to 2026: the 2024 Mobile Top 10, the MASVS at version 2.1.0, and the MASTG. It concentrates on the four categories this skill owns: M1, M3, M9 and M10.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Generated code and older audit templates still quote the 2016 list, for example calling data storage "M2" or cryptography "M5". The 2024 list replaced that numbering completely.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The stakes are well documented. A March 2025 scan of 156,080 iOS apps found that 71% leaked at least one hardcoded secret, and the January 2025 joint CISA and FBI guidance on bad product-security practices calls hardcoded credentials (CWE-798) dangerous.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- [What Changed: 2016 → 2024 OWASP Mobile Top 10](#what-changed-2016-2024-owasp-mobile-top-10)
|
|
12
|
-
- [Master Traceability Matrix](#master-traceability-matrix)
|
|
13
|
-
- [M1 - Improper Credential Usage](#m1-improper-credential-usage)
|
|
14
|
-
- [MASTG Test Cases](#mastg-test-cases)
|
|
15
|
-
- [Compliant: Keychain credential storage](#compliant-keychain-credential-storage)
|
|
16
|
-
- [Anti-pattern: common AI-generated credential storage](#anti-pattern-common-ai-generated-credential-storage)
|
|
17
|
-
- [M3 - Insecure Authentication/Authorization](#m3-insecure-authenticationauthorization)
|
|
18
|
-
- [MASTG Test Cases](#mastg-test-cases)
|
|
19
|
-
- [The LAContext Vulnerability](#the-lacontext-vulnerability)
|
|
20
|
-
- [`.biometryCurrentSet` vs `.biometryAny`](#biometrycurrentset-vs-biometryany)
|
|
21
|
-
- [Compliant: hardware-bound biometric authentication](#compliant-hardware-bound-biometric-authentication)
|
|
22
|
-
- [Anti-pattern: LAContext-only authentication](#anti-pattern-lacontext-only-authentication)
|
|
23
|
-
- [M9 - Insecure Data Storage](#m9-insecure-data-storage)
|
|
24
|
-
- [iOS Storage Security Properties](#ios-storage-security-properties)
|
|
25
|
-
- [MASTG Test Cases](#mastg-test-cases)
|
|
26
|
-
- [NSFileProtection Classes](#nsfileprotection-classes)
|
|
27
|
-
- [Compliant: file storage with Data Protection](#compliant-file-storage-with-data-protection)
|
|
28
|
-
- [Anti-pattern: insecure data storage](#anti-pattern-insecure-data-storage)
|
|
29
|
-
- [M10 - Insufficient Cryptography](#m10-insufficient-cryptography)
|
|
30
|
-
- [Deprecated vs. Approved Algorithms](#deprecated-vs-approved-algorithms)
|
|
31
|
-
- [MASTG Test Cases](#mastg-test-cases)
|
|
32
|
-
- [Compliant: CryptoKit encryption](#compliant-cryptokit-encryption)
|
|
33
|
-
- [Anti-pattern: insecure cryptography](#anti-pattern-insecure-cryptography)
|
|
34
|
-
- [kSecAttrAccessible Selection Guide](#ksecattraccessible-selection-guide)
|
|
35
|
-
- [Enterprise Audit Workflow](#enterprise-audit-workflow)
|
|
36
|
-
- [How Security Teams Evaluate iOS Apps](#how-security-teams-evaluate-ios-apps)
|
|
37
|
-
- [Top 10 Audit Findings](#top-10-audit-findings)
|
|
38
|
-
- [Evidence Kit (5 Artifacts)](#evidence-kit-5-artifacts)
|
|
39
|
-
- [Jailbreak-Era Testing (2025-2026)](#jailbreak-era-testing-20252026)
|
|
40
|
-
- [Post-Quantum Cryptography Roadmap](#post-quantum-cryptography-roadmap)
|
|
41
|
-
- [Cross-Reference Index](#cross-reference-index)
|
|
42
|
-
- [Conclusion](#conclusion)
|
|
43
|
-
- [Summary Checklist](#summary-checklist)
|
|
44
|
-
|
|
45
|
-
## What Changed: 2016 → 2024 OWASP Mobile Top 10
|
|
46
|
-
|
|
47
|
-
The 2024 edition is a complete overhaul. Four categories are entirely new, two pairs were merged, and everything was renumbered. Any code comment or documentation citing the 2016 numbering is outdated.
|
|
48
|
-
|
|
49
|
-
| 2024 Category | Status | 2016 Predecessor |
|
|
50
|
-
| --------------------------------------------- | ---------- | ------------------ |
|
|
51
|
-
| **M1: Improper Credential Usage** | New | None |
|
|
52
|
-
| **M2: Inadequate Supply Chain Security** | New | None |
|
|
53
|
-
| **M3: Insecure Authentication/Authorization** | Merged | 2016 M4 + M6 |
|
|
54
|
-
| **M4: Insufficient Input/Output Validation** | New | None |
|
|
55
|
-
| **M5: Insecure Communication** | Renumbered | 2016 M3 |
|
|
56
|
-
| **M6: Inadequate Privacy Controls** | New | None |
|
|
57
|
-
| **M7: Insufficient Binary Protections** | Merged | 2016 M8 + M9 |
|
|
58
|
-
| **M8: Security Misconfiguration** | Expanded | 2016 M10 (partial) |
|
|
59
|
-
| **M9: Insecure Data Storage** | Renumbered | 2016 M2 |
|
|
60
|
-
| **M10: Insufficient Cryptography** | Renumbered | 2016 M5 |
|
|
61
|
-
|
|
62
|
-
**MASVS v2.1.0** (January 18, 2024) reorganized into 8 control groups with concise, testable controls. The old L1/L2/R verification levels became **MAS Testing Profiles** within the MASTG, aligned with NIST OSCAL. Legacy MSTG-\* test IDs (e.g., MSTG-STORAGE-1) were deprecated in favor of new MASTG-TEST-02xx/03xx identifiers with granular, tool-specific test procedures.
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
## Master Traceability Matrix
|
|
67
|
-
|
|
68
|
-
This matrix links each OWASP 2024 category to its MASVS controls, MASTG test cases, iOS APIs, and required audit evidence.
|
|
69
|
-
|
|
70
|
-
| OWASP 2024 | MASVS v2 Controls | Key MASTG Tests (New IDs) | iOS APIs / Flags | Required Evidence |
|
|
71
|
-
| --------------------------------- | --------------------------------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
72
|
-
| **M1** Improper Credential Usage | MASVS-STORAGE-1, MASVS-AUTH-1, MASVS-CRYPTO-2 | 0213, 0214, 0299, 0300, 0302 | Keychain + `SecAccessControl`; App Attest | Static scan (no literals); keychain dump with ACL; attestation logs |
|
|
73
|
-
| **M3** Insecure Auth/AuthZ | MASVS-AUTH-1, MASVS-AUTH-2, MASVS-AUTH-3 | 0266, 0267, 0268, 0269, 0270, 0271 | `SecAccessControlCreateWithFlags` + `.biometryCurrentSet`; `ASWebAuthenticationSession` | Auth flow diagrams; biometric bypass test results; token TTL policy |
|
|
74
|
-
| **M9** Insecure Data Storage | MASVS-STORAGE-1, MASVS-STORAGE-2 | 0296, 0297, 0299, 0300, 0301, 0302, 0303, 0215, 0298, 0313, 0314 | Keychain accessibility flags; `NSFileProtectionComplete`; `isExcludedFromBackup` | `xattr` listings; backup extraction; keychain dump |
|
|
75
|
-
| **M10** Insufficient Cryptography | MASVS-CRYPTO-1, MASVS-CRYPTO-2 | 0209, 0210, 0211, 0213, 0214, 0311, 0317 | CryptoKit `AES.GCM`/`ChaChaPoly`; `SecRandomCopyBytes`; Secure Enclave keys | Crypto inventory; algorithm audit; unit tests |
|
|
76
|
-
|
|
77
|
-
> **Cross-reference note:** MASVS-STORAGE-1 and MASTG-TEST-0299/0302 appear under both M1 and M9. This is intentional - keychain configuration simultaneously addresses credential storage and data-at-rest protection. See `keychain-access-control.md` for detailed accessibility flag guidance.
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
## M1 - Improper Credential Usage
|
|
82
|
-
|
|
83
|
-
**Scope:** Hardcoded credentials in source/config, insecure credential transmission, insecure on-device storage, weak auth protocols. Attack vectors: EASY. Impact: SEVERE. Entirely new in 2024 - no 2016 predecessor.
|
|
84
|
-
|
|
85
|
-
**Cybernews 2025 data:** 815,000+ hardcoded secrets across 156,080 iOS apps (average 5.2 per app), including 19 Stripe secret keys, 836 unprotected cloud endpoints exposing 406TB, and 2,218 misconfigured Firebase endpoints leaking 19.8M records. Secrets found in plaintext IPA files without decompilation.
|
|
86
|
-
|
|
87
|
-
### MASTG Test Cases
|
|
88
|
-
|
|
89
|
-
| Test ID | Legacy ID | Verifies | Profile |
|
|
90
|
-
| --------------- | -------------- | ----------------------------------------------------- | ------- |
|
|
91
|
-
| MASTG-TEST-0213 | MSTG-CRYPTO-1 | No hardcoded cryptographic keys in source/binary | L1, L2 |
|
|
92
|
-
| MASTG-TEST-0214 | MSTG-CRYPTO-5 | No cryptographic keys in bundle files (plist, config) | L1, L2 |
|
|
93
|
-
| MASTG-TEST-0299 | MSTG-STORAGE-1 | Files use appropriate Data Protection classes | L1 |
|
|
94
|
-
| MASTG-TEST-0300 | MSTG-STORAGE-1 | Static: references to APIs storing unencrypted data | L2 |
|
|
95
|
-
| MASTG-TEST-0302 | MSTG-STORAGE-2 | Sensitive data unencrypted in private storage | L2 |
|
|
96
|
-
|
|
97
|
-
**Testing procedure:** Use radare2 for static analysis - search for `SecKeyCreateWithData` with hardcoded key data or CryptoKit key initialization with inline bytes. Use objection (`ios keychain dump`, `ios nsuserdefaults get`) and filesystem grep at runtime. Check `.xcconfig`, `Info.plist`, and embedded resources for API keys.
|
|
98
|
-
|
|
99
|
-
**App Attest (iOS 14+):** Closes the secret provisioning gap by verifying device integrity before the server issues credentials. This avoids hardcoded secrets entirely - the server provisions secrets only to attested, genuine app instances. See `credential-storage-patterns.md` for implementation details.
|
|
100
|
-
|
|
101
|
-
### Compliant: Keychain credential storage
|
|
102
|
-
|
|
103
|
-
```swift
|
|
104
|
-
import Security
|
|
105
|
-
|
|
106
|
-
/// Stores a credential securely in the iOS Keychain.
|
|
107
|
-
/// Compliance: OWASP M1 (Improper Credential Usage), MASVS-STORAGE-1
|
|
108
|
-
/// Test cases: MASTG-TEST-0213, MASTG-TEST-0299
|
|
109
|
-
/// iOS 8.0+ (SecAccessControlCreateWithFlags), iOS 11.3+ (.biometryCurrentSet)
|
|
110
|
-
func storeCredential(account: String, secret: Data, service: String) throws {
|
|
111
|
-
// ✅ CORRECT - secrets are persisted in Keychain with explicit access control
|
|
112
|
-
// Delete existing item first to avoid errSecDuplicateItem
|
|
113
|
-
let deleteQuery: [String: Any] = [
|
|
114
|
-
kSecClass as String: kSecClassGenericPassword,
|
|
115
|
-
kSecAttrAccount as String: account,
|
|
116
|
-
kSecAttrService as String: service
|
|
117
|
-
]
|
|
118
|
-
SecItemDelete(deleteQuery as CFDictionary)
|
|
119
|
-
|
|
120
|
-
var error: Unmanaged<CFError>?
|
|
121
|
-
guard let accessControl = SecAccessControlCreateWithFlags(
|
|
122
|
-
kCFAllocatorDefault,
|
|
123
|
-
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
|
|
124
|
-
.biometryCurrentSet,
|
|
125
|
-
&error
|
|
126
|
-
) else {
|
|
127
|
-
throw error!.takeRetainedValue() as Error
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
let query: [String: Any] = [
|
|
131
|
-
kSecClass as String: kSecClassGenericPassword,
|
|
132
|
-
kSecAttrAccount as String: account,
|
|
133
|
-
kSecAttrService as String: service,
|
|
134
|
-
kSecAttrAccessControl as String: accessControl,
|
|
135
|
-
kSecValueData as String: secret
|
|
136
|
-
]
|
|
137
|
-
|
|
138
|
-
let status = SecItemAdd(query as CFDictionary, nil)
|
|
139
|
-
guard status == errSecSuccess else {
|
|
140
|
-
throw NSError(domain: NSOSStatusErrorDomain, code: Int(status))
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Anti-pattern: common AI-generated credential storage
|
|
146
|
-
|
|
147
|
-
```swift
|
|
148
|
-
// ❌ WRONG - UserDefaults writes to UNENCRYPTED plist at:
|
|
149
|
-
// <AppSandbox>/Library/Preferences/<BundleID>.plist
|
|
150
|
-
// Extractable via iTunes backup, iMazing, or objection
|
|
151
|
-
UserDefaults.standard.set(apiToken, forKey: "auth_token")
|
|
9
|
+
Related: [keychain-access-control.md](keychain-access-control.md), [credential-storage-patterns.md](credential-storage-patterns.md), [biometric-authentication.md](biometric-authentication.md), [cryptokit-symmetric.md](cryptokit-symmetric.md), [cryptokit-public-key.md](cryptokit-public-key.md), [common-anti-patterns.md](common-anti-patterns.md).
|
|
152
10
|
|
|
153
|
-
|
|
154
|
-
let stripeKey = "sk_live_EXAMPLE_KEY_DO_NOT_USE"
|
|
155
|
-
|
|
156
|
-
// ❌ WRONG - Secret in Info.plist (plaintext in IPA archive)
|
|
157
|
-
// <key>API_SECRET</key><string>my-secret-key-12345</string>
|
|
158
|
-
|
|
159
|
-
// ❌ WRONG - NSKeyedArchiver to Documents directory (no encryption)
|
|
160
|
-
let data = try NSKeyedArchiver.archivedData(
|
|
161
|
-
withRootObject: credentials, requiringSecureCoding: true)
|
|
162
|
-
try data.write(to: documentsURL.appendingPathComponent("creds.dat"))
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
**Why these fail audits:** objection `ios nsuserdefaults get` reveals UserDefaults instantly. MobSF flags hardcoded key patterns. Backup extraction exposes Documents directory. All fail MASTG-TEST-0213 and MASTG-TEST-0302.
|
|
166
|
-
|
|
167
|
-
---
|
|
11
|
+
## Contents
|
|
168
12
|
|
|
169
|
-
|
|
13
|
+
- [2016 to 2024: what moved](#2016-to-2024-what-moved)
|
|
14
|
+
- [Traceability matrix](#traceability-matrix)
|
|
15
|
+
- [M1 Improper Credential Usage](#m1-improper-credential-usage)
|
|
16
|
+
- [M3 Insecure Authentication/Authorization](#m3-insecure-authenticationauthorization)
|
|
17
|
+
- [M9 Insecure Data Storage](#m9-insecure-data-storage)
|
|
18
|
+
- [M10 Insufficient Cryptography](#m10-insufficient-cryptography)
|
|
19
|
+
- [Choosing kSecAttrAccessible](#choosing-ksecattraccessible)
|
|
20
|
+
- [Running an enterprise audit](#running-an-enterprise-audit)
|
|
21
|
+
- [Post-quantum roadmap](#post-quantum-roadmap)
|
|
22
|
+
- [Cross-reference index](#cross-reference-index)
|
|
23
|
+
- [Takeaways](#takeaways)
|
|
24
|
+
- [Checklist](#checklist)
|
|
170
25
|
|
|
171
|
-
|
|
26
|
+
## 2016 to 2024: what moved
|
|
172
27
|
|
|
173
|
-
|
|
28
|
+
| 2024 category | Origin |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| M1 Improper Credential Usage | Added in 2024 |
|
|
31
|
+
| M2 Inadequate Supply Chain Security | Added in 2024 |
|
|
32
|
+
| M3 Insecure Authentication/Authorization | Merges 2016 M4 and M6 |
|
|
33
|
+
| M4 Insufficient Input/Output Validation | Added in 2024 |
|
|
34
|
+
| M5 Insecure Communication | Was 2016 M3 |
|
|
35
|
+
| M6 Inadequate Privacy Controls | Added in 2024 |
|
|
36
|
+
| M7 Insufficient Binary Protections | Merges 2016 M8 and M9 |
|
|
37
|
+
| M8 Security Misconfiguration | Broadens part of 2016 M10 |
|
|
38
|
+
| M9 Insecure Data Storage | Was 2016 M2 |
|
|
39
|
+
| M10 Insufficient Cryptography | Was 2016 M5 |
|
|
174
40
|
|
|
175
|
-
|
|
176
|
-
| --------------- | ----------- | ------------------------------------------------------------- | ------- |
|
|
177
|
-
| MASTG-TEST-0266 | MSTG-AUTH-8 | Static: references to `LAContext.evaluatePolicy` | L2 |
|
|
178
|
-
| MASTG-TEST-0267 | MSTG-AUTH-8 | Dynamic: runtime event-based biometric auth (bypassable) | L2 |
|
|
179
|
-
| MASTG-TEST-0268 | MSTG-AUTH-8 | Static: APIs allowing fallback to non-biometric auth | L2 |
|
|
180
|
-
| MASTG-TEST-0269 | MSTG-AUTH-8 | Dynamic: runtime fallback to non-biometric auth | L2 |
|
|
181
|
-
| MASTG-TEST-0270 | MSTG-AUTH-8 | Static: `.biometryCurrentSet` for enrollment change detection | L2 |
|
|
182
|
-
| MASTG-TEST-0271 | MSTG-AUTH-8 | Dynamic: enrollment change detection enforced at runtime | L2 |
|
|
41
|
+
MASVS v2.1.0, published 18 January 2024, has eight control groups. The old L1, L2 and R verification levels moved into the MASTG as MAS Testing Profiles aligned with NIST OSCAL, and the legacy `MSTG-*` test identifiers are deprecated in favour of `MASTG-TEST-02xx` and `MASTG-TEST-03xx`.
|
|
183
42
|
|
|
184
|
-
|
|
43
|
+
## Traceability matrix
|
|
185
44
|
|
|
186
|
-
|
|
45
|
+
| Category | Apple APIs | Evidence | MASTG tests | MASVS controls |
|
|
46
|
+
| --- | --- | --- | --- | --- |
|
|
47
|
+
| M1 | Keychain with `SecAccessControl`, App Attest | Static scan with no secret literals, keychain dump showing the ACL, attestation logs | 0213, 0214, 0299, 0300, 0302 | STORAGE-1, AUTH-1, CRYPTO-2 |
|
|
48
|
+
| M3 | `SecAccessControlCreateWithFlags` with `.biometryCurrentSet`, `ASWebAuthenticationSession` | Auth flow diagrams, biometric bypass results, token lifetime policy | 0266 to 0271 | AUTH-1, AUTH-2, AUTH-3 |
|
|
49
|
+
| M9 | Keychain accessibility classes, `NSFileProtectionComplete`, `isExcludedFromBackup` | `xattr` listings, backup extraction, keychain dump | 0215, 0296, 0297, 0298, 0299, 0300, 0301, 0302, 0303, 0313, 0314 | STORAGE-1, STORAGE-2 |
|
|
50
|
+
| M10 | `AES.GCM`, `ChaChaPoly`, `SecRandomCopyBytes`, Secure Enclave keys | Crypto inventory, algorithm audit, unit tests | 0209, 0210, 0211, 0213, 0214, 0311, 0317 | CRYPTO-1, CRYPTO-2 |
|
|
187
51
|
|
|
188
|
-
|
|
52
|
+
Some identifiers are listed twice deliberately. MASVS-STORAGE-1 plus tests 0299 and 0302 belong to M1 as well as M9, because a credential sitting in plaintext is a credential problem and a storage problem at once.
|
|
189
53
|
|
|
190
|
-
|
|
191
|
-
// Forces LAContext.evaluatePolicy to always succeed
|
|
192
|
-
if (ObjC.available) {
|
|
193
|
-
var hook = ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"];
|
|
194
|
-
Interceptor.attach(hook.implementation, {
|
|
195
|
-
onEnter: function (args) {
|
|
196
|
-
var block = new ObjC.Block(args[4]);
|
|
197
|
-
const appCallback = block.implementation;
|
|
198
|
-
block.implementation = function (error, value) {
|
|
199
|
-
return appCallback(1, null); // Force success=true
|
|
200
|
-
};
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
```
|
|
54
|
+
## M1 Improper Credential Usage
|
|
205
55
|
|
|
206
|
-
|
|
56
|
+
Covers credentials hardcoded in source or configuration, sent insecurely, stored insecurely on the device, or used with weak authentication protocols. Exploitability: easy. Impact: severe. There is no 2016 predecessor.
|
|
207
57
|
|
|
208
|
-
The
|
|
58
|
+
The same 2025 scan counted more than 815,000 secrets (an average above five per app). Among them were 19 live payment-provider secret keys, 836 unprotected cloud storage endpoints exposing about 406 TB, and 2,218 misconfigured backend-as-a-service database endpoints leaking 19.8 million records. All of it was read straight out of IPA files.
|
|
209
59
|
|
|
210
|
-
|
|
60
|
+
| Profile | MASTG test | What it checks | Legacy ID |
|
|
61
|
+
| --- | --- | --- | --- |
|
|
62
|
+
| L1, L2 | 0213 | Keys embedded in source or the binary | MSTG-CRYPTO-1 |
|
|
63
|
+
| L1, L2 | 0214 | Keys in bundled resource files | MSTG-CRYPTO-5 |
|
|
64
|
+
| L1 | 0299 | Which protection class each item uses | MSTG-STORAGE-1 |
|
|
65
|
+
| L2 | 0300 | Code that calls plaintext storage APIs | MSTG-STORAGE-1 |
|
|
66
|
+
| L2 | 0302 | Secrets found in clear in the private container | MSTG-STORAGE-2 |
|
|
211
67
|
|
|
212
|
-
|
|
213
|
-
| --------------------- | ----------------------------------------------------- | ---------------------------------------------------- | ----- |
|
|
214
|
-
| `.biometryCurrentSet` | Item invalidated if new biometric enrolled | **Recommended** - prevents enrollment-change attacks | 11.3+ |
|
|
215
|
-
| `.biometryAny` | Accessible with any enrolled biometric, even new ones | Lower - attacker can add their own fingerprint | 11.3+ |
|
|
216
|
-
| `.userPresence` | Biometry OR passcode (system chooses) | Allows passcode fallback | 8.0+ |
|
|
217
|
-
| `.devicePasscode` | Passcode only | No biometric option | 9.0+ |
|
|
68
|
+
How testers look: radare2 over the binary for `SecKeyCreateWithData` fed with constant bytes or CryptoKit keys built from inline data; `objection` with `ios keychain dump` and `ios nsuserdefaults get`; a grep of the extracted file system; and a read through `.xcconfig` files, `Info.plist` and every embedded resource.
|
|
218
69
|
|
|
219
|
-
|
|
70
|
+
App Attest (iOS 14 and later) closes the provisioning gap: the server hands secrets only to instances it has attested as genuine (see the `device-integrity` skill).
|
|
220
71
|
|
|
221
|
-
|
|
72
|
+
Compliant storage of a credential:
|
|
222
73
|
|
|
223
74
|
```swift
|
|
224
|
-
import LocalAuthentication
|
|
225
75
|
import Security
|
|
226
76
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
// STEP 1: Store secret with biometric protection
|
|
234
|
-
func storeBiometricProtectedSecret(account: String, secret: Data) throws {
|
|
235
|
-
// ✅ CORRECT - Secure Enclave gates secret release through keychain ACLs
|
|
236
|
-
var error: Unmanaged<CFError>?
|
|
237
|
-
guard let accessControl = SecAccessControlCreateWithFlags(
|
|
238
|
-
kCFAllocatorDefault,
|
|
239
|
-
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
|
|
240
|
-
.biometryCurrentSet,
|
|
241
|
-
&error
|
|
242
|
-
) else {
|
|
243
|
-
throw error!.takeRetainedValue() as Error
|
|
77
|
+
// MASTG-TEST-0299, 0300 | MASVS-AUTH-1, MASVS-STORAGE-1 | OWASP M1, M9
|
|
78
|
+
// SecAccessControlCreateWithFlags: iOS 8.0+, .biometryCurrentSet: iOS 11.3+
|
|
79
|
+
func storeSigningSecret(_ secret: Data, account: String) throws {
|
|
80
|
+
guard let acl = SecAccessControlCreateWithFlags(nil,
|
|
81
|
+
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, .biometryCurrentSet, nil) else {
|
|
82
|
+
throw KeychainFailure(errSecParam)
|
|
244
83
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
let status = SecItemAdd(query as CFDictionary, nil)
|
|
255
|
-
guard status == errSecSuccess else {
|
|
256
|
-
throw NSError(domain: NSOSStatusErrorDomain, code: Int(status))
|
|
84
|
+
let identity: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
|
|
85
|
+
kSecAttrService: "com.example.treasury",
|
|
86
|
+
kSecAttrAccount: account]
|
|
87
|
+
var insert = identity
|
|
88
|
+
insert[kSecValueData] = secret
|
|
89
|
+
insert[kSecAttrAccessControl] = acl
|
|
90
|
+
var status = SecItemAdd(insert as CFDictionary, nil)
|
|
91
|
+
if status == errSecDuplicateItem {
|
|
92
|
+
status = SecItemUpdate(identity as CFDictionary, [kSecValueData: secret] as CFDictionary)
|
|
257
93
|
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// STEP 2: Retrieve - Secure Enclave enforces biometric check
|
|
261
|
-
func retrieveBiometricProtectedSecret(account: String) throws -> Data? {
|
|
262
|
-
let context = LAContext()
|
|
263
|
-
context.localizedReason = "Authenticate to access your account"
|
|
264
|
-
|
|
265
|
-
let query: [String: Any] = [
|
|
266
|
-
kSecClass as String: kSecClassGenericPassword,
|
|
267
|
-
kSecAttrAccount as String: account,
|
|
268
|
-
kSecAttrService as String: "com.app.biometric-auth",
|
|
269
|
-
kSecUseAuthenticationContext as String: context,
|
|
270
|
-
kSecReturnData as String: true
|
|
271
|
-
]
|
|
272
|
-
|
|
273
|
-
var result: AnyObject?
|
|
274
|
-
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
275
|
-
guard status == errSecSuccess else { return nil }
|
|
276
|
-
return result as? Data
|
|
94
|
+
guard status == errSecSuccess else { throw KeychainFailure(status) }
|
|
277
95
|
}
|
|
278
96
|
```
|
|
279
97
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
```swift
|
|
283
|
-
// ❌ WRONG - #2 most common iOS audit finding
|
|
284
|
-
// Bypassable: objection -g com.app explore -> ios ui biometrics_bypass
|
|
285
|
-
let context = LAContext()
|
|
286
|
-
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
|
|
287
|
-
localizedReason: "Log in") { success, error in
|
|
288
|
-
if success {
|
|
289
|
-
// ❌ This Boolean is hookable - no cryptographic proof
|
|
290
|
-
self.showMainScreen() // Attacker gains full access
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
```
|
|
98
|
+
`KeychainFailure` is a small error type wrapping the `OSStatus`. Add first and update on `errSecDuplicateItem`. Deleting and re-adding is reserved for changing the access control of an existing item.
|
|
294
99
|
|
|
295
|
-
|
|
100
|
+
Findings that fail MASTG-TEST-0213 and 0302:
|
|
296
101
|
|
|
297
|
-
|
|
102
|
+
- A token in `UserDefaults`, which lands in `<AppSandbox>/Library/Preferences/<BundleID>.plist`.
|
|
103
|
+
- A live payment secret (`sk_live_...`) as a Swift literal.
|
|
104
|
+
- A secret stored as an `Info.plist` value.
|
|
105
|
+
- Credentials written with `NSKeyedArchiver` into `Documents`, which backups include.
|
|
298
106
|
|
|
299
|
-
|
|
107
|
+
`objection` and MobSF find all four within minutes, and a plain backup extraction exposes everything in `Documents`.
|
|
300
108
|
|
|
301
|
-
|
|
109
|
+
## M3 Insecure Authentication/Authorization
|
|
302
110
|
|
|
303
|
-
|
|
304
|
-
| ------------------------------------------ | ------------------ | -------------- | ------------------------ | ------------------------------- |
|
|
305
|
-
| Keychain (`WhenPasscodeSetThisDeviceOnly`) | ✅ AES-256-GCM | ❌ | ❌ | ✅ Use for secrets |
|
|
306
|
-
| Keychain (`AfterFirstUnlock`) | ✅ | ✅ (encrypted) | ❌ | ⚠️ Acceptable for L1 |
|
|
307
|
-
| `NSFileProtectionComplete` files | ✅ (when locked) | ✅ | ❌ | ✅ Use for sensitive files |
|
|
308
|
-
| UserDefaults | ❌ Plaintext plist | ✅ | ✅ (via backup) | ❌ Never for secrets |
|
|
309
|
-
| Documents/ (default protection) | ✅ (Class C) | ✅ | ✅ (via backup) | ❌ Not without extra encryption |
|
|
310
|
-
| SQLite/CoreData (no SQLCipher) | ❌ | ✅ | ✅ (via backup) | ❌ Not for secrets |
|
|
311
|
-
| NSLog output | ❌ | N/A | ✅ (Console.app) | ❌ Never log secrets |
|
|
111
|
+
Merges 2016 M4 and M6: remote authentication, local biometric authentication, and authorization decided only on the client. Exploitability: easy. Impact: severe. A biometric check done with `LAContext` alone falls to Frida in under ten seconds.
|
|
312
112
|
|
|
313
|
-
|
|
113
|
+
| MASTG test | Type | Checks |
|
|
114
|
+
| --- | --- | --- |
|
|
115
|
+
| 0266 | Static | References to `evaluatePolicy` used as the gate |
|
|
116
|
+
| 0267 | Dynamic | Event-based authentication that can be bypassed |
|
|
117
|
+
| 0268 | Static | APIs that fall back to non-biometric authentication |
|
|
118
|
+
| 0269 | Dynamic | Fallback behaviour at runtime |
|
|
119
|
+
| 0270 | Static | Use of `.biometryCurrentSet` |
|
|
120
|
+
| 0271 | Dynamic | Enforcement after an enrolment change |
|
|
314
121
|
|
|
315
|
-
|
|
122
|
+
All six replace legacy MSTG-AUTH-8 and belong to the L2 profile.
|
|
316
123
|
|
|
317
|
-
|
|
318
|
-
| --------------- | -------------- | ------------------------------------------------- | ------- |
|
|
319
|
-
| MASTG-TEST-0299 | MSTG-STORAGE-1 | Data Protection classes for private storage files | L1 |
|
|
320
|
-
| MASTG-TEST-0300 | MSTG-STORAGE-1 | Static: references to unencrypted storage APIs | L2 |
|
|
321
|
-
| MASTG-TEST-0301 | MSTG-STORAGE-1 | Dynamic: runtime use of unencrypted storage | L2 |
|
|
322
|
-
| MASTG-TEST-0302 | MSTG-STORAGE-2 | Sensitive data unencrypted in private storage | L2 |
|
|
323
|
-
| MASTG-TEST-0296 | MSTG-STORAGE-3 | Sensitive data in logs | L1, L2 |
|
|
324
|
-
| MASTG-TEST-0297 | MSTG-STORAGE-3 | Insertion of sensitive data into log statements | L1, L2 |
|
|
325
|
-
| MASTG-TEST-0215 | MSTG-STORAGE-8 | Sensitive data not excluded from backup | L1, L2 |
|
|
326
|
-
| MASTG-TEST-0313 | MSTG-STORAGE-5 | APIs preventing keyboard caching | L1, L2 |
|
|
124
|
+
The match itself happens in the Secure Enclave, but `evaluatePolicy` returns a plain `Bool` to user space with no cryptographic proof attached, so a Frida script of fewer than ten lines, or `objection`'s `ios ui biometrics_bypass`, changes the answer.
|
|
327
125
|
|
|
328
|
-
|
|
126
|
+
| Flag | Behaviour | Recommendation | Available from |
|
|
127
|
+
| --- | --- | --- | --- |
|
|
128
|
+
| `.biometryCurrentSet` | Invalidated when enrolment changes | Recommended | iOS 11.3 |
|
|
129
|
+
| `.biometryAny` | Any enrolled biometric, including ones added later | Weaker | iOS 11.3 |
|
|
130
|
+
| `.userPresence` | Biometrics or device passcode | General use | iOS 8.0 |
|
|
131
|
+
| `.devicePasscode` | Passcode only | Passcode-only flows | iOS 9.0 |
|
|
329
132
|
|
|
330
|
-
|
|
331
|
-
| ------------------- | ------------------------------------------------------ | ------------------------------------------------- | -------- |
|
|
332
|
-
| A: Complete | `NSFileProtectionComplete` | Only when unlocked; key discarded ~10s after lock | No |
|
|
333
|
-
| B: Unless Open | `NSFileProtectionCompleteUnlessOpen` | Already-open files remain accessible when locked | No |
|
|
334
|
-
| C: Until First Auth | `NSFileProtectionCompleteUntilFirstUserAuthentication` | After first unlock, even when locked | **Yes** |
|
|
335
|
-
| D: None | `NSFileProtectionNone` | Always; protected only by device UID | No |
|
|
133
|
+
Use `.biometryCurrentSet` for anything valuable. With `.biometryAny`, a thief who knows the passcode can enrol their own finger or face and then unlock the item.
|
|
336
134
|
|
|
337
|
-
|
|
135
|
+
Compliant pattern: store as in the M1 example, then read with an `LAContext` passed through `kSecUseAuthenticationContext`. A short compliance snippet may simply return nil on any failure; the full read with status handling is in [biometric-authentication.md](biometric-authentication.md).
|
|
338
136
|
|
|
339
137
|
```swift
|
|
340
|
-
import
|
|
341
|
-
|
|
342
|
-
/// Writes sensitive data with Complete file protection.
|
|
343
|
-
/// Compliance: OWASP M9 (Insecure Data Storage), MASVS-STORAGE-1
|
|
344
|
-
/// Test cases: MASTG-TEST-0299
|
|
345
|
-
/// iOS 9.0+ (.completeFileProtection option)
|
|
346
|
-
func writeProtectedFile(data: Data, to url: URL) throws {
|
|
347
|
-
try data.write(to: url, options: [.atomic, .completeFileProtection])
|
|
348
|
-
}
|
|
138
|
+
import LocalAuthentication
|
|
349
139
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
140
|
+
// OWASP M3 | MASVS-AUTH-2 | MASTG-TEST-0266, 0270
|
|
141
|
+
func readSigningSecret(account: String, reason: String) -> Data? {
|
|
142
|
+
let context = LAContext()
|
|
143
|
+
context.localizedReason = reason
|
|
144
|
+
let lookup: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
|
|
145
|
+
kSecAttrService: "com.example.treasury",
|
|
146
|
+
kSecAttrAccount: account,
|
|
147
|
+
kSecReturnData: true,
|
|
148
|
+
kSecUseAuthenticationContext: context]
|
|
149
|
+
var out: CFTypeRef?
|
|
150
|
+
return SecItemCopyMatching(lookup as CFDictionary, &out) == errSecSuccess ? out as? Data : nil
|
|
358
151
|
}
|
|
359
152
|
```
|
|
360
153
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
```swift
|
|
364
|
-
// ❌ WRONG - Unencrypted plist
|
|
365
|
-
UserDefaults.standard.set("Bearer eyJhbGciOiJSUzI1NiIs...", forKey: "authToken")
|
|
366
|
-
|
|
367
|
-
// ❌ WRONG - Default file protection (Class C) for sensitive file
|
|
368
|
-
try sensitiveData.write(to: documentsURL.appendingPathComponent("profile.dat"))
|
|
369
|
-
|
|
370
|
-
// ❌ WRONG - Logging sensitive data (Console.app / idevicesyslog)
|
|
371
|
-
NSLog("User token: %@", authToken)
|
|
372
|
-
print("Password entered: \(password)")
|
|
373
|
-
|
|
374
|
-
// ❌ WRONG - Not excluding sensitive files from backup
|
|
375
|
-
// Files in Documents/ are in iTunes/Finder backups by default
|
|
376
|
-
// Extractable with iMazing on non-jailbroken devices
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
---
|
|
154
|
+
The anti-pattern, showing the main screen when `evaluatePolicy` succeeds, is the second most common finding in iOS audits.
|
|
380
155
|
|
|
381
|
-
##
|
|
156
|
+
## M9 Insecure Data Storage
|
|
382
157
|
|
|
383
|
-
|
|
158
|
+
Covers missing or weak encryption, data in reachable locations, weak access control, and leaks through logs, caches and backups. It was M2 in 2016.
|
|
384
159
|
|
|
385
|
-
|
|
160
|
+
| Storage | Protection | Verdict |
|
|
161
|
+
| --- | --- | --- |
|
|
162
|
+
| Keychain, `WhenPasscodeSetThisDeviceOnly` | Encrypted, never in backups | Use for secrets |
|
|
163
|
+
| Keychain, `AfterFirstUnlock` | Encrypted, included in encrypted backups | Acceptable at L1 |
|
|
164
|
+
| File with `NSFileProtectionComplete` | Encrypted while locked | Use for sensitive files |
|
|
165
|
+
| `UserDefaults` | Plaintext | Never for secrets |
|
|
166
|
+
| `Documents` with the default class | Class C | Not without extra encryption |
|
|
167
|
+
| SQLite or Core Data without SQLCipher | Plaintext database | Not for secrets |
|
|
168
|
+
| `NSLog` output | Visible in Console.app | Never log secrets |
|
|
386
169
|
|
|
387
|
-
|
|
388
|
-
| -------------- | --------------------------------- | -------------------------------------------------------------- |
|
|
389
|
-
| Hashing | MD5, SHA-1 (for security) | SHA256, SHA384, SHA512; SHA3 (iOS 26+) |
|
|
390
|
-
| Symmetric | DES, 3DES, RC4, Blowfish, AES-ECB | AES.GCM (AES-256-GCM), ChaChaPoly |
|
|
391
|
-
| Asymmetric | RSA < 2048 bits | P256, P384, P521, Curve25519, Ed25519 |
|
|
392
|
-
| Key derivation | Simple SHA hash of password | HKDF; Argon2/bcrypt/scrypt server-side |
|
|
393
|
-
| RNG | `rand()`, `random()`, `srand()` | `SecRandomCopyBytes` (iOS 2+), CryptoKit auto-nonces (iOS 13+) |
|
|
394
|
-
| Post-quantum | All classical PKC (by 2030) | ML-KEM, ML-DSA, X-Wing (iOS 26+) |
|
|
170
|
+
Keychain items outlive the app: that has been the confirmed behaviour since iOS 10.3, and only erasing the device clears them. One exception: items with `WhenPasscodeSetThisDeviceOnly` are deleted when the user removes the device passcode.
|
|
395
171
|
|
|
396
|
-
|
|
172
|
+
| Profile | MASTG test | What it checks | Legacy ID |
|
|
173
|
+
| --- | --- | --- | --- |
|
|
174
|
+
| L1 | 0299 | Protection class per item | MSTG-STORAGE-1 |
|
|
175
|
+
| L2 | 0300, 0301 | Plaintext storage APIs, statically and at runtime | MSTG-STORAGE-1 |
|
|
176
|
+
| L2 | 0302 | Secrets left readable in the private container | MSTG-STORAGE-2 |
|
|
177
|
+
| L1, L2 | 0296, 0297 | Secrets written to logs | MSTG-STORAGE-3 |
|
|
178
|
+
| L1, L2 | 0215 | Sensitive files that backups still include | MSTG-STORAGE-8 |
|
|
179
|
+
| L1, L2 | 0313 | Keyboard cache | MSTG-STORAGE-5 |
|
|
397
180
|
|
|
398
|
-
|
|
181
|
+
File protection classes:
|
|
399
182
|
|
|
400
|
-
|
|
183
|
+
| Class | Constant | Behaviour |
|
|
184
|
+
| --- | --- | --- |
|
|
185
|
+
| A | `NSFileProtectionComplete` | Readable only while unlocked; the key is discarded about 10 seconds after locking |
|
|
186
|
+
| B | `NSFileProtectionCompleteUnlessOpen` | Files already open stay readable after locking |
|
|
187
|
+
| C | `NSFileProtectionCompleteUntilFirstUserAuthentication` | Default; readable from first unlock until reboot |
|
|
188
|
+
| D | `NSFileProtectionNone` | Protected only by the device UID key |
|
|
401
189
|
|
|
402
|
-
|
|
403
|
-
| --------------- | ------------- | ----------------------------------------------- | ------- |
|
|
404
|
-
| MASTG-TEST-0209 | MSTG-CRYPTO-2 | Key size meets minimum requirements | L1, L2 |
|
|
405
|
-
| MASTG-TEST-0210 | MSTG-CRYPTO-2 | No broken symmetric algorithms (DES, 3DES, RC4) | L1, L2 |
|
|
406
|
-
| MASTG-TEST-0211 | MSTG-CRYPTO-3 | No broken hashes (MD5, SHA-1 for security) | L1, L2 |
|
|
407
|
-
| MASTG-TEST-0317 | MSTG-CRYPTO-3 | No broken encryption modes (ECB) | L1, L2 |
|
|
408
|
-
| MASTG-TEST-0311 | MSTG-CRYPTO-6 | CSPRNG used (not `rand`/`random`) | L1, L2 |
|
|
409
|
-
| MASTG-TEST-0213 | MSTG-CRYPTO-1 | No hardcoded cryptographic keys in code | L1, L2 |
|
|
410
|
-
| MASTG-TEST-0214 | MSTG-CRYPTO-5 | No hardcoded cryptographic keys in files | L1, L2 |
|
|
411
|
-
|
|
412
|
-
**iOS-specific testing:** Use radare2 to find references to `kCCAlgorithmDES`, `kCCAlgorithm3DES`, `kCCAlgorithmRC4`, `kCCOptionECBMode` in CommonCrypto calls. Search for `CC_MD5`, `CC_SHA1` or CryptoKit `Insecure.MD5`/`Insecure.SHA1`. MASTG demos: MASTG-DEMO-0015 (CommonCrypto broken hash), MASTG-DEMO-0016 (CryptoKit broken hash), MASTG-DEMO-0018 (broken encryption).
|
|
413
|
-
|
|
414
|
-
### Compliant: CryptoKit encryption
|
|
415
|
-
|
|
416
|
-
Canonical full round-trip patterns are in `cryptokit-symmetric.md` and anti-pattern #6 in `common-anti-patterns.md`. This compliance snippet stays minimal to avoid duplicating canonical crypto guidance.
|
|
190
|
+
Compliant file handling:
|
|
417
191
|
|
|
418
192
|
```swift
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
let sealedBox = try AES.GCM.seal(plaintext, using: key)
|
|
427
|
-
guard let combined = sealedBox.combined else { throw CryptoError.invalidCiphertext }
|
|
428
|
-
return combined
|
|
193
|
+
// OWASP M9 | MASVS-STORAGE-1 | MASTG-TEST-0299, 0215
|
|
194
|
+
func persistStatement(_ pdf: Data, to url: URL) throws {
|
|
195
|
+
try pdf.write(to: url, options: [.completeFileProtection, .atomic])
|
|
196
|
+
var flags = URLResourceValues()
|
|
197
|
+
flags.isExcludedFromBackup = true
|
|
198
|
+
var target = url
|
|
199
|
+
try target.setResourceValues(flags)
|
|
429
200
|
}
|
|
430
|
-
|
|
431
|
-
// Compliance: MASVS-CRYPTO-2, MASTG-TEST-0213
|
|
432
|
-
let encryptionKey = SymmetricKey(size: .bits256) // 256-bit from CSPRNG
|
|
433
201
|
```
|
|
434
202
|
|
|
435
|
-
|
|
203
|
+
`Data.WritingOptions.completeFileProtection` is available from iOS 9.0 and `isExcludedFromBackup` from iOS 5.1.
|
|
436
204
|
|
|
437
|
-
|
|
438
|
-
// ❌ WRONG - MD5 (collisions trivially constructable) - fails MASTG-TEST-0211
|
|
439
|
-
var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
|
|
440
|
-
CC_MD5(data.bytes, CC_LONG(data.count), &digest)
|
|
441
|
-
|
|
442
|
-
// ❌ WRONG - ECB mode - fails MASTG-TEST-0317
|
|
443
|
-
CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES),
|
|
444
|
-
CCOptions(kCCOptionECBMode), key, keyLength, nil, plaintext, ...)
|
|
205
|
+
Anti-patterns: a bearer token in `UserDefaults`; a sensitive file left at the default class C; `NSLog` or `print` of a token or password; sensitive files in `Documents` that end up in backups, where desktop backup tools extract them.
|
|
445
206
|
|
|
446
|
-
|
|
447
|
-
let seed = srand(UInt32(time(nil))) // Predictable seed
|
|
207
|
+
## M10 Insufficient Cryptography
|
|
448
208
|
|
|
449
|
-
|
|
450
|
-
let key = SymmetricKey(data: "my-secret-key-1234567890123456".data(using: .utf8)!)
|
|
209
|
+
Covers weak algorithms, short keys, poor key management, weak randomness and deprecated hashes. Exploitability: average. Impact: severe. It was M5 in 2016.
|
|
451
210
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
211
|
+
| Area | Deprecated | Use instead |
|
|
212
|
+
| --- | --- | --- |
|
|
213
|
+
| Hashing | MD5, SHA-1 | SHA-256, SHA-384, SHA-512; SHA-3 from iOS 26 |
|
|
214
|
+
| Symmetric | Blowfish, RC4, DES and 3DES, or AES in ECB mode | AES-256-GCM, ChaChaPoly |
|
|
215
|
+
| Asymmetric | RSA below 2048 bits | NIST curves (P-256 up to P-521), Curve25519, Ed25519 |
|
|
216
|
+
| Key derivation | A plain SHA hash of a password | HKDF for keys; Argon2, bcrypt or scrypt on the server for passwords |
|
|
217
|
+
| Randomness | `srand()` seeding, `rand()`, `random()` | `SecRandomCopyBytes` (iOS 2 and later), CryptoKit's automatic nonces (iOS 13 and later) |
|
|
218
|
+
| Post-quantum | Classical public-key crypto alone, deprecated by NIST from 2030 | From iOS 26: ML-KEM, ML-DSA and the X-Wing hybrid |
|
|
456
219
|
|
|
457
|
-
|
|
220
|
+
On `arc4random`: on current Apple platforms it is backed by the system cryptographic generator, not RC4, so it is not a finding by itself. What auditors flag is too little entropy (a 32-bit result, a six-digit code) or modulo bias from `% n`. For keys, nonces, salts and tokens, `SecRandomCopyBytes` remains the recommendation because it is documented for cryptographic use, reports failure and makes the intent obvious; [common-anti-patterns.md](common-anti-patterns.md) entry 10 applies the same rule.
|
|
458
221
|
|
|
459
|
-
|
|
222
|
+
Reusing an AES-GCM nonce destroys confidentiality for the affected messages and leaks the GHASH key H, which enables forgeries. CryptoKit's automatic nonce removes the risk as long as code does not supply its own.
|
|
460
223
|
|
|
461
|
-
|
|
224
|
+
| What it checks | MASTG test | Legacy ID |
|
|
225
|
+
| --- | --- | --- |
|
|
226
|
+
| Key lengths | 0209 | MSTG-CRYPTO-2 |
|
|
227
|
+
| DES, 3DES and RC4 absent | 0210 | MSTG-CRYPTO-2 |
|
|
228
|
+
| Broken hash functions absent | 0211 | MSTG-CRYPTO-3 |
|
|
229
|
+
| ECB mode absent | 0317 | MSTG-CRYPTO-3 |
|
|
230
|
+
| Randomness from a CSPRNG | 0311 | MSTG-CRYPTO-6 |
|
|
231
|
+
| Keys absent from code | 0213 | MSTG-CRYPTO-1 |
|
|
232
|
+
| Keys absent from files | 0214 | MSTG-CRYPTO-5 |
|
|
462
233
|
|
|
463
|
-
|
|
234
|
+
All belong to both L1 and L2.
|
|
464
235
|
|
|
465
|
-
|
|
466
|
-
| -------------------------------------------------- | ------ | ------ | ----------------------- | ----------------------------------------------------- |
|
|
467
|
-
| `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` | ❌ | ❌ | ✅ (deleted if removed) | **Highest-sensitivity: auth tokens, encryption keys** |
|
|
468
|
-
| `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` | ❌ | ❌ | ❌ | Sensitive data, device-specific |
|
|
469
|
-
| `kSecAttrAccessibleWhenUnlocked` (default) | ✅ | ✅ | ❌ | General credentials needing sync |
|
|
470
|
-
| `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` | ❌ | ❌ | ❌ | Background-accessible, device-specific |
|
|
471
|
-
| `kSecAttrAccessibleAfterFirstUnlock` | ✅ | ✅ | ❌ | Background tasks (e.g., push notification keys) |
|
|
472
|
-
| `kSecAttrAccessibleAlways` | ✅ | ✅ | ❌ | **❌ DEPRECATED (iOS 12) - never use** |
|
|
236
|
+
Testing on iOS: radare2 searches for `kCCAlgorithmDES`, `kCCAlgorithm3DES`, `kCCAlgorithmRC4` and `kCCOptionECBMode`, plus `CC_MD5`, `CC_SHA1`, `Insecure.MD5` and `Insecure.SHA1`. MASTG-DEMO-0015 (CommonCrypto hashing), MASTG-DEMO-0016 (CryptoKit hashing) and MASTG-DEMO-0018 (broken encryption) show the expected findings.
|
|
473
237
|
|
|
474
|
-
|
|
238
|
+
```swift
|
|
239
|
+
import CryptoKit
|
|
475
240
|
|
|
476
|
-
|
|
241
|
+
// MASTG-TEST-0210, 0213, 0317 | MASVS-CRYPTO-1 and -2 | OWASP M10
|
|
242
|
+
let statementKey = SymmetricKey(size: .bits256)
|
|
477
243
|
|
|
478
|
-
|
|
244
|
+
func sealStatement(_ plaintext: Data) throws -> Data {
|
|
245
|
+
let sealed = try AES.GCM.seal(plaintext, using: statementKey)
|
|
246
|
+
guard let packed = sealed.combined else { throw CryptoKitError.incorrectParameterSize }
|
|
247
|
+
return packed
|
|
248
|
+
}
|
|
249
|
+
```
|
|
479
250
|
|
|
480
|
-
|
|
251
|
+
The key itself is stored in the keychain, never compiled in.
|
|
481
252
|
|
|
482
|
-
|
|
253
|
+
Anti-patterns: `CC_MD5`; `CCCrypt` with `kCCOptionECBMode`; a generator seeded with `srand(time(nil))`; `SymmetricKey(data:)` built from a string literal; a static all-zero nonce.
|
|
483
254
|
|
|
484
|
-
|
|
255
|
+
## Choosing kSecAttrAccessible
|
|
485
256
|
|
|
486
|
-
|
|
257
|
+
One accessibility decision touches M1, M3, M9 and M10 at once.
|
|
487
258
|
|
|
488
|
-
|
|
259
|
+
| Class | Backup | iCloud Keychain | Condition | Typical use |
|
|
260
|
+
| --- | --- | --- | --- | --- |
|
|
261
|
+
| `WhenPasscodeSetThisDeviceOnly` | No | No | Passcode must be set; item deleted if it is removed | The most sensitive values, such as session tokens and data keys |
|
|
262
|
+
| `WhenUnlockedThisDeviceOnly` | No | No | Device unlocked | Sensitive, device-specific data |
|
|
263
|
+
| `WhenUnlocked` (default) | Yes | Eligible | Device unlocked | General credentials that should sync |
|
|
264
|
+
| `AfterFirstUnlockThisDeviceOnly` | No | No | After first unlock | Background access, device-specific |
|
|
265
|
+
| `AfterFirstUnlock` | Yes | Eligible | After first unlock | Background work such as push-related keys |
|
|
266
|
+
| `Always` | | | Deprecated in iOS 12 | Never |
|
|
489
267
|
|
|
490
|
-
|
|
268
|
+
`ThisDeviceOnly` and synchronization exclude each other. Such an item never migrates to another device and never syncs; adding one with `kSecAttrSynchronizable: true` fails with `errSecParam` rather than quietly staying local.
|
|
491
269
|
|
|
492
|
-
|
|
493
|
-
| ---- | ------------------------------- | ------ | ------------ | ----------- | -------- |
|
|
494
|
-
| 1 | Secrets in UserDefaults/plists | M1, M9 | STORAGE-1 | 0299, 0302 | Critical |
|
|
495
|
-
| 2 | LAContext-only biometric auth | M3 | AUTH-2 | 0266, 0267 | High |
|
|
496
|
-
| 3 | Missing certificate pinning | M5 | NETWORK-2 | 0244 | High |
|
|
497
|
-
| 4 | Hardcoded API keys in binary | M1 | CRYPTO-2 | 0213, 0214 | Critical |
|
|
498
|
-
| 5 | Deprecated crypto (MD5, DES) | M10 | CRYPTO-1 | 0210, 0211 | High |
|
|
499
|
-
| 6 | Insecure keychain accessibility | M9 | STORAGE-1 | 0299 | Medium |
|
|
500
|
-
| 7 | Sensitive data in logs | M9 | STORAGE-2 | 0296, 0297 | Medium |
|
|
501
|
-
| 8 | Missing jailbreak detection | M7 | RESILIENCE-1 | - | Low |
|
|
502
|
-
| 9 | Unencrypted SQLite/Realm | M9 | STORAGE-1 | 0302 | High |
|
|
503
|
-
| 10 | ATS exceptions allowing HTTP | M5 | NETWORK-1 | - | Medium |
|
|
270
|
+
`kSecAttrAccessible` and `kSecAttrAccessControl` are also mutually exclusive in one dictionary. The accessibility value goes into `SecAccessControlCreateWithFlags` as its protection argument, and supplying both attributes returns `errSecParam` (-50).
|
|
504
271
|
|
|
505
|
-
|
|
272
|
+
Between the two strictest foreground classes, decide by threat model and recovery plan. `WhenPasscodeSetThisDeviceOnly` is stricter; `WhenUnlockedThisDeviceOnly` is the safer default when losing the item on passcode removal would surprise users and there is no way to restore it.
|
|
506
273
|
|
|
507
|
-
|
|
508
|
-
| ---------------------- | --------------------------------------------- | ----------- |
|
|
509
|
-
| Static analysis report | No hardcoded secrets or weak crypto | M1, M10 |
|
|
510
|
-
| Filesystem/xattr log | `NSFileProtectionComplete` applied | M9 |
|
|
511
|
-
| Keychain dump | `ThisDeviceOnly` + `SecAccessControl` present | M1, M9 |
|
|
512
|
-
| Backup extraction | No sensitive data migrated | M9 |
|
|
513
|
-
| Code snippets | Correct APIs and flags used | All |
|
|
274
|
+
## Running an enterprise audit
|
|
514
275
|
|
|
515
|
-
|
|
276
|
+
MAS Testing Profiles: L1 is the baseline for low-risk apps. L2 adds defence in depth for financial, health and other highly sensitive apps, and there auditors expect encryption keys held in the keychain, biometrics tied to hardware, and pinned certificates.
|
|
516
277
|
|
|
517
|
-
|
|
278
|
+
Tooling:
|
|
518
279
|
|
|
519
|
-
|
|
280
|
+
| Phase | Tools |
|
|
281
|
+
| --- | --- |
|
|
282
|
+
| Static | MobSF, radare2 |
|
|
283
|
+
| Dynamic | `objection`: `ios keychain dump`, file protection checks, `ios nsuserdefaults get`, `ios ui biometrics_bypass` |
|
|
284
|
+
| Network | An intercepting HTTPS proxy, with `objection`'s SSL-pinning bypass to test pinning |
|
|
285
|
+
| Binary | class-dump, dsdump |
|
|
520
286
|
|
|
521
|
-
|
|
287
|
+
Paths auditors always open:
|
|
522
288
|
|
|
523
|
-
|
|
289
|
+
- `<Sandbox>/Library/Preferences/<BundleID>.plist`
|
|
290
|
+
- `<Sandbox>/Documents/`
|
|
291
|
+
- `<Sandbox>/Library/Caches/`
|
|
292
|
+
- `<Sandbox>/Library/SplashBoard/Snapshots/`
|
|
293
|
+
- `<Sandbox>/tmp/`
|
|
524
294
|
|
|
525
|
-
|
|
526
|
-
| ------------------------ | ------------------------------------------------------------------------------------- |
|
|
527
|
-
| February 2024 (iOS 17.4) | iMessage PQ3 - first quantum-secure messaging at scale |
|
|
528
|
-
| August 2024 | NIST finalizes FIPS 203/204/205 |
|
|
529
|
-
| January 2025 | CISA adds insecure crypto algorithms to bad practices list |
|
|
530
|
-
| June 2025 (WWDC) | CryptoKit PQC APIs announced for iOS 26 |
|
|
531
|
-
| September 2025 (iOS 26) | ML-KEM-768/1024, ML-DSA-65/87, X-Wing KEM in CryptoKit; quantum-secure TLS by default |
|
|
532
|
-
| 2030 (NIST target) | Classical public-key crypto deprecated |
|
|
533
|
-
| 2035 (CNSA 2.0) | Classical algorithms disallowed for National Security Systems |
|
|
295
|
+
Most frequent findings:
|
|
534
296
|
|
|
535
|
-
|
|
297
|
+
| # | Severity | Finding | MASTG | OWASP | MASVS |
|
|
298
|
+
| --- | --- | --- | --- | --- | --- |
|
|
299
|
+
| 1 | Critical | Secrets in `UserDefaults` or plists | 0299, 0302 | M1, M9 | STORAGE-1 |
|
|
300
|
+
| 2 | High | Biometrics via `LAContext` only | 0266, 0267 | M3 | AUTH-2 |
|
|
301
|
+
| 3 | High | No certificate pinning | 0244 | M5 | NETWORK-2 |
|
|
302
|
+
| 4 | Critical | Hardcoded keys | 0213, 0214 | M1 | CRYPTO-2 |
|
|
303
|
+
| 5 | High | Deprecated crypto | 0210, 0211 | M10 | CRYPTO-1 |
|
|
304
|
+
| 6 | Medium | Weak keychain accessibility | 0299 | M9 | STORAGE-1 |
|
|
305
|
+
| 7 | Medium | Sensitive data in logs | 0296, 0297 | M9 | STORAGE-2 |
|
|
306
|
+
| 8 | Low | No jailbreak detection | none listed | M7 | RESILIENCE-1 |
|
|
307
|
+
| 9 | High | Unencrypted SQLite or other embedded database | 0302 | M9 | STORAGE-1 |
|
|
308
|
+
| 10 | Medium | ATS exceptions allowing HTTP | none listed | M5 | NETWORK-1 |
|
|
536
309
|
|
|
537
|
-
|
|
310
|
+
Evidence to hand over:
|
|
538
311
|
|
|
539
|
-
|
|
312
|
+
| Artefact | Covers |
|
|
313
|
+
| --- | --- |
|
|
314
|
+
| Static analysis report | M1, M10 |
|
|
315
|
+
| File system or `xattr` listing proving `NSFileProtectionComplete` | M9 |
|
|
316
|
+
| Keychain dump showing `ThisDeviceOnly` classes and `SecAccessControl` | M1, M9 |
|
|
317
|
+
| Backup extraction containing no sensitive data | M9 |
|
|
318
|
+
| Code excerpts showing the APIs and flags in use | All |
|
|
540
319
|
|
|
541
|
-
|
|
542
|
-
| --------------------------------------------------------- | --- | --- | --- | ---------------- | -------------------------------- |
|
|
543
|
-
| Keychain + `WhenPasscodeSetThisDeviceOnly` | ✅ | - | ✅ | ✅ (key storage) | `keychain-access-control.md` |
|
|
544
|
-
| `SecAccessControlCreateWithFlags` + `.biometryCurrentSet` | ✅ | ✅ | ✅ | - | `biometric-authentication.md` |
|
|
545
|
-
| CryptoKit AES.GCM with auto-nonce | - | - | ✅ | ✅ | `cryptokit-symmetric.md` |
|
|
546
|
-
| `NSFileProtectionComplete` | - | - | ✅ | - | `keychain-access-control.md` |
|
|
547
|
-
| `SecRandomCopyBytes` for key/token generation | ✅ | ✅ | - | ✅ | `cryptokit-symmetric.md` |
|
|
548
|
-
| App Attest for credential provisioning | ✅ | ✅ | - | - | `credential-storage-patterns.md` |
|
|
549
|
-
| ML-KEM/ML-DSA (iOS 26+) | - | - | - | ✅ | `cryptokit-public-key.md` |
|
|
320
|
+
Current iOS 26 releases have no public jailbreak. Dynamic testing therefore relies on Frida Gadget injected into a re-signed IPA together with `objection`, on virtualized iOS devices, or on backup extraction. Static analysis with MobSF or semgrep plus Gadget-based dynamic testing is now the primary approach.
|
|
550
321
|
|
|
551
|
-
|
|
322
|
+
## Post-quantum roadmap
|
|
552
323
|
|
|
553
|
-
|
|
324
|
+
WWDC 2025 session 314 introduced CryptoKit's post-quantum APIs. The driving threat is "harvest now, decrypt later": traffic recorded today is decrypted once a large quantum computer exists.
|
|
554
325
|
|
|
555
|
-
|
|
326
|
+
| Date | Event |
|
|
327
|
+
| --- | --- |
|
|
328
|
+
| February 2024 | iOS 17.4 ships PQ3 for iMessage |
|
|
329
|
+
| August 2024 | NIST finalises FIPS 203, 204 and 205 |
|
|
330
|
+
| January 2025 | CISA lists insecure cryptography as a bad practice |
|
|
331
|
+
| June 2025 | CryptoKit post-quantum APIs announced |
|
|
332
|
+
| September 2025 | iOS 26 ships ML-KEM-768 and ML-KEM-1024, ML-DSA-65 and ML-DSA-87, X-Wing, and post-quantum TLS by default |
|
|
333
|
+
| 2030 | NIST deprecates classical public-key algorithms |
|
|
334
|
+
| 2035 | CNSA 2.0 disallows classical algorithms for National Security Systems |
|
|
556
335
|
|
|
557
|
-
|
|
336
|
+
Apple deploys hybrid schemes, so security never falls below today's classical level even if a new algorithm is weakened. Apps should build crypto agility the same way: keep algorithm choices behind a protocol so ML-KEM or ML-DSA can replace a classical primitive without touching callers.
|
|
558
337
|
|
|
559
|
-
|
|
338
|
+
## Cross-reference index
|
|
560
339
|
|
|
561
|
-
|
|
340
|
+
| Control | Categories | Details in |
|
|
341
|
+
| --- | --- | --- |
|
|
342
|
+
| Keychain with `WhenPasscodeSetThisDeviceOnly` | M1, M9, and M10 for stored keys | [keychain-access-control.md](keychain-access-control.md) |
|
|
343
|
+
| `SecAccessControlCreateWithFlags` with `.biometryCurrentSet` | M1, M3, M9 | [biometric-authentication.md](biometric-authentication.md) |
|
|
344
|
+
| `AES.GCM` with automatic nonces | M9, M10 | [cryptokit-symmetric.md](cryptokit-symmetric.md) |
|
|
345
|
+
| `NSFileProtectionComplete` | M9 | This file |
|
|
346
|
+
| `SecRandomCopyBytes` | M1, M3, M10 | [common-anti-patterns.md](common-anti-patterns.md) |
|
|
347
|
+
| App Attest before provisioning secrets | M1, M3 | [credential-storage-patterns.md](credential-storage-patterns.md) |
|
|
348
|
+
| ML-KEM, ML-DSA | M10 | [cryptokit-public-key.md](cryptokit-public-key.md) |
|
|
562
349
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
350
|
+
## Takeaways
|
|
351
|
+
|
|
352
|
+
A single well-formed keychain write, with `WhenPasscodeSetThisDeviceOnly` and `.biometryCurrentSet`, satisfies M1, M3 and M9 together. Guidance that files storage under M2 or cryptography under M5 was written for the 2016 list, and code comments citing `MSTG-*` identifiers should move to `MASTG-TEST` identifiers. With NIST's 2030 date and post-quantum TLS on by default in iOS 26, compliance programmes should assess hybrid post-quantum strategies now.
|
|
353
|
+
|
|
354
|
+
## Checklist
|
|
355
|
+
|
|
356
|
+
- [ ] Findings and comments use 2024 category numbers only.
|
|
357
|
+
- [ ] Test references use `MASTG-TEST-02xx` and `03xx` identifiers.
|
|
358
|
+
- [ ] Credentials live only in the keychain, with a `ThisDeviceOnly` class unless they are meant to sync.
|
|
359
|
+
- [ ] Biometrics unlock keychain items protected with `.biometryCurrentSet`.
|
|
360
|
+
- [ ] No dictionary sets both `kSecAttrAccessible` and `kSecAttrAccessControl`.
|
|
361
|
+
- [ ] Cryptography uses CryptoKit or `SecKey`; no MD5, DES, 3DES, RC4 or ECB.
|
|
362
|
+
- [ ] Nonces come from CryptoKit; any manual nonce has a documented uniqueness scheme.
|
|
363
|
+
- [ ] Sensitive files use `NSFileProtectionComplete` and `isExcludedFromBackup`.
|
|
364
|
+
- [ ] No sensitive values in logs; secure fields set `.autocorrectionType = .no` and `.isSecureTextEntry = true` to keep them out of the keyboard cache.
|
|
365
|
+
- [ ] Comments on security-relevant code name the MASTG test, the MASVS control and the OWASP category they satisfy.
|
|
366
|
+
- [ ] Crypto sits behind protocols so ML-KEM and ML-DSA can be adopted without rewriting callers.
|