ai_root_shield 0.4.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,263 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'uri'
6
+
7
+ module AiRootShield
8
+ module Platform
9
+ # Android-specific security module with SafetyNet and Play Integrity API integration
10
+ class AndroidSecurityModule
11
+ # SafetyNet API response codes
12
+ SAFETYNET_SUCCESS = 'SUCCESS'
13
+ SAFETYNET_NETWORK_ERROR = 'NETWORK_ERROR'
14
+ SAFETYNET_API_NOT_AVAILABLE = 'API_NOT_AVAILABLE'
15
+
16
+ # Play Integrity API verdict types
17
+ PLAY_INTEGRITY_MEETS_DEVICE_INTEGRITY = 'MEETS_DEVICE_INTEGRITY'
18
+ PLAY_INTEGRITY_MEETS_BASIC_INTEGRITY = 'MEETS_BASIC_INTEGRITY'
19
+ PLAY_INTEGRITY_MEETS_STRONG_INTEGRITY = 'MEETS_STRONG_INTEGRITY'
20
+
21
+ def initialize(api_key: nil, package_name: nil)
22
+ @api_key = api_key
23
+ @package_name = package_name
24
+ @cache = {}
25
+ end
26
+
27
+ # Analyze Android device security using SafetyNet and Play Integrity APIs
28
+ def analyze_device_security(device_logs)
29
+ results = {
30
+ safetynet: analyze_safetynet(device_logs),
31
+ play_integrity: analyze_play_integrity(device_logs),
32
+ hardware_security: analyze_hardware_security(device_logs),
33
+ system_integrity: analyze_system_integrity(device_logs),
34
+ risk_factors: [],
35
+ risk_score: 0
36
+ }
37
+
38
+ calculate_android_risk_score(results)
39
+ results
40
+ end
41
+
42
+ private
43
+
44
+ # Analyze SafetyNet Attestation API results
45
+ def analyze_safetynet(device_logs)
46
+ safetynet_data = device_logs.dig('safetynet') || {}
47
+
48
+ {
49
+ basic_integrity: safetynet_data['basicIntegrity'] || false,
50
+ cts_profile_match: safetynet_data['ctsProfileMatch'] || false,
51
+ evaluation_type: safetynet_data['evaluationType'] || 'UNKNOWN',
52
+ nonce_verified: verify_safetynet_nonce(safetynet_data['nonce']),
53
+ timestamp: safetynet_data['timestampMs'],
54
+ advice: parse_safetynet_advice(safetynet_data['advice'])
55
+ }
56
+ end
57
+
58
+ # Analyze Play Integrity API results
59
+ def analyze_play_integrity(device_logs)
60
+ integrity_data = device_logs.dig('play_integrity') || {}
61
+
62
+ {
63
+ device_verdict: integrity_data.dig('deviceIntegrity', 'deviceRecognitionVerdict') || [],
64
+ app_verdict: integrity_data.dig('appIntegrity', 'verdict') || 'UNKNOWN',
65
+ account_verdict: integrity_data.dig('accountDetails', 'verdict') || 'UNKNOWN',
66
+ environment_verdict: integrity_data.dig('environmentDetails', 'verdict') || 'UNKNOWN',
67
+ meets_device_integrity: check_device_integrity_verdict(integrity_data),
68
+ app_licensing_verdict: integrity_data.dig('appIntegrity', 'appLicensingVerdict')
69
+ }
70
+ end
71
+
72
+ # Analyze hardware-based security features
73
+ def analyze_hardware_security(device_logs)
74
+ hardware_data = device_logs.dig('hardware_security') || {}
75
+
76
+ {
77
+ tee_available: hardware_data['teeAvailable'] || false,
78
+ secure_element_available: hardware_data['seAvailable'] || false,
79
+ hardware_keystore: hardware_data['hardwareKeystore'] || false,
80
+ biometric_hardware: analyze_biometric_hardware(hardware_data),
81
+ strongbox_available: hardware_data['strongboxAvailable'] || false,
82
+ verified_boot: hardware_data['verifiedBoot'] || false,
83
+ dm_verity_enabled: hardware_data['dmVerityEnabled'] || false
84
+ }
85
+ end
86
+
87
+ # Analyze Android system integrity
88
+ def analyze_system_integrity(device_logs)
89
+ system_data = device_logs.dig('system_integrity') || {}
90
+
91
+ {
92
+ selinux_enforcing: system_data['selinuxEnforcing'] || false,
93
+ verified_boot_state: system_data['verifiedBootState'] || 'UNKNOWN',
94
+ bootloader_locked: system_data['bootloaderLocked'] || false,
95
+ system_partition_verified: system_data['systemPartitionVerified'] || false,
96
+ vendor_partition_verified: system_data['vendorPartitionVerified'] || false,
97
+ build_tags: analyze_build_tags(system_data['buildTags']),
98
+ adb_enabled: system_data['adbEnabled'] || false,
99
+ developer_options_enabled: system_data['developerOptionsEnabled'] || false
100
+ }
101
+ end
102
+
103
+ # Verify SafetyNet nonce for replay attack protection
104
+ def verify_safetynet_nonce(nonce)
105
+ return false unless nonce
106
+
107
+ # Implement nonce verification logic
108
+ # This should check against your server-side generated nonce
109
+ cached_nonce = @cache[:expected_nonce]
110
+ return false unless cached_nonce
111
+
112
+ nonce == cached_nonce
113
+ end
114
+
115
+ # Parse SafetyNet advice for specific security issues
116
+ def parse_safetynet_advice(advice)
117
+ return [] unless advice.is_a?(Array)
118
+
119
+ parsed_advice = []
120
+ advice.each do |item|
121
+ case item
122
+ when 'LOCK_SCREEN_SECURED'
123
+ parsed_advice << { type: 'SECURITY', message: 'Device has secure lock screen' }
124
+ when 'NOT_FOR_PRODUCTION'
125
+ parsed_advice << { type: 'WARNING', message: 'Device not suitable for production use' }
126
+ when 'RESTORE_TO_FACTORY_ROM'
127
+ parsed_advice << { type: 'CRITICAL', message: 'Device ROM has been modified' }
128
+ else
129
+ parsed_advice << { type: 'INFO', message: item }
130
+ end
131
+ end
132
+
133
+ parsed_advice
134
+ end
135
+
136
+ # Check Play Integrity device verdict
137
+ def check_device_integrity_verdict(integrity_data)
138
+ verdicts = integrity_data.dig('deviceIntegrity', 'deviceRecognitionVerdict') || []
139
+
140
+ {
141
+ meets_device_integrity: verdicts.include?(PLAY_INTEGRITY_MEETS_DEVICE_INTEGRITY),
142
+ meets_basic_integrity: verdicts.include?(PLAY_INTEGRITY_MEETS_BASIC_INTEGRITY),
143
+ meets_strong_integrity: verdicts.include?(PLAY_INTEGRITY_MEETS_STRONG_INTEGRITY)
144
+ }
145
+ end
146
+
147
+ # Analyze biometric hardware consistency
148
+ def analyze_biometric_hardware(hardware_data)
149
+ biometric_data = hardware_data['biometrics'] || {}
150
+
151
+ {
152
+ fingerprint_available: biometric_data['fingerprintAvailable'] || false,
153
+ face_available: biometric_data['faceAvailable'] || false,
154
+ iris_available: biometric_data['irisAvailable'] || false,
155
+ biometric_prompt_supported: biometric_data['biometricPromptSupported'] || false,
156
+ hardware_level: biometric_data['hardwareLevel'] || 'NONE',
157
+ enrolled_biometrics: biometric_data['enrolledBiometrics'] || 0
158
+ }
159
+ end
160
+
161
+ # Analyze Android build tags for security implications
162
+ def analyze_build_tags(build_tags)
163
+ return { secure: false, tags: [] } unless build_tags
164
+
165
+ tags = build_tags.split(',').map(&:strip)
166
+ secure_tags = ['release-keys']
167
+ insecure_tags = ['test-keys', 'dev-keys', 'debug']
168
+
169
+ {
170
+ secure: (tags & secure_tags).any? && (tags & insecure_tags).empty?,
171
+ tags: tags,
172
+ has_release_keys: tags.include?('release-keys'),
173
+ has_test_keys: tags.include?('test-keys'),
174
+ has_debug_keys: tags.include?('debug')
175
+ }
176
+ end
177
+
178
+ # Calculate Android-specific risk score
179
+ def calculate_android_risk_score(results)
180
+ risk_score = 0
181
+ risk_factors = []
182
+
183
+ # SafetyNet analysis
184
+ safetynet = results[:safetynet]
185
+ unless safetynet[:basic_integrity]
186
+ risk_score += 25
187
+ risk_factors << 'SAFETYNET_BASIC_INTEGRITY_FAILED'
188
+ end
189
+
190
+ unless safetynet[:cts_profile_match]
191
+ risk_score += 20
192
+ risk_factors << 'SAFETYNET_CTS_PROFILE_MISMATCH'
193
+ end
194
+
195
+ # Play Integrity analysis
196
+ play_integrity = results[:play_integrity]
197
+ unless play_integrity[:meets_device_integrity][:meets_device_integrity]
198
+ risk_score += 30
199
+ risk_factors << 'PLAY_INTEGRITY_DEVICE_INTEGRITY_FAILED'
200
+ end
201
+
202
+ if play_integrity[:app_verdict] != 'PLAY_RECOGNIZED'
203
+ risk_score += 15
204
+ risk_factors << 'PLAY_INTEGRITY_APP_NOT_RECOGNIZED'
205
+ end
206
+
207
+ # Hardware security analysis
208
+ hardware = results[:hardware_security]
209
+ unless hardware[:tee_available]
210
+ risk_score += 10
211
+ risk_factors << 'TEE_NOT_AVAILABLE'
212
+ end
213
+
214
+ unless hardware[:hardware_keystore]
215
+ risk_score += 8
216
+ risk_factors << 'HARDWARE_KEYSTORE_UNAVAILABLE'
217
+ end
218
+
219
+ # System integrity analysis
220
+ system = results[:system_integrity]
221
+ unless system[:selinux_enforcing]
222
+ risk_score += 20
223
+ risk_factors << 'SELINUX_NOT_ENFORCING'
224
+ end
225
+
226
+ unless system[:bootloader_locked]
227
+ risk_score += 25
228
+ risk_factors << 'BOOTLOADER_UNLOCKED'
229
+ end
230
+
231
+ unless system[:build_tags][:secure]
232
+ risk_score += 15
233
+ risk_factors << 'INSECURE_BUILD_TAGS'
234
+ end
235
+
236
+ if system[:adb_enabled]
237
+ risk_score += 5
238
+ risk_factors << 'ADB_ENABLED'
239
+ end
240
+
241
+ results[:risk_score] = [risk_score, 100].min
242
+ results[:risk_factors] = risk_factors
243
+ end
244
+
245
+ # Set expected nonce for SafetyNet verification
246
+ def set_expected_nonce(nonce)
247
+ @cache[:expected_nonce] = nonce
248
+ end
249
+
250
+ # Generate SafetyNet request with proper nonce
251
+ def generate_safetynet_request
252
+ nonce = SecureRandom.base64(32)
253
+ set_expected_nonce(nonce)
254
+
255
+ {
256
+ nonce: nonce,
257
+ timestamp: Time.now.to_i * 1000,
258
+ package_name: @package_name
259
+ }
260
+ end
261
+ end
262
+ end
263
+ end