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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -4
- data/README.md +191 -14
- data/bindings/python/README.md +304 -0
- data/bindings/python/ai_root_shield.py +438 -0
- data/bindings/python/setup.py +65 -0
- data/examples/device_logs/android_safetynet_device.json +148 -0
- data/examples/device_logs/ios_jailbroken_device.json +172 -0
- data/exe/ai_root_shield +220 -7
- data/lib/ai_root_shield/ci_cd/security_test_module.rb +743 -0
- data/lib/ai_root_shield/dashboard/web_dashboard.rb +441 -0
- data/lib/ai_root_shield/enterprise/alert_system.rb +601 -0
- data/lib/ai_root_shield/enterprise/hybrid_detection_engine.rb +650 -0
- data/lib/ai_root_shield/enterprise/performance_optimizer.rb +613 -0
- data/lib/ai_root_shield/enterprise/policy_manager.rb +637 -0
- data/lib/ai_root_shield/integrations/siem_connector.rb +695 -0
- data/lib/ai_root_shield/platform/android_security_module.rb +263 -0
- data/lib/ai_root_shield/platform/hardware_security_analyzer.rb +452 -0
- data/lib/ai_root_shield/platform/ios_security_module.rb +513 -0
- data/lib/ai_root_shield/platform/unified_report_generator.rb +613 -0
- data/lib/ai_root_shield/version.rb +1 -1
- data/lib/ai_root_shield.rb +152 -1
- data/security_test_artifacts/security_report.json +124 -0
- data/security_test_artifacts/security_results.sarif +16 -0
- data/security_test_artifacts/security_tests.xml +3 -0
- metadata +20 -1
@@ -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
|