├── .classpath
├── .project
├── .settings
├── org.eclipse.jdt.core.prefs
├── org.eclipse.jdt.ui.prefs
└── org.eclipse.ltk.core.refactoring.prefs
├── AndroidManifest.xml
├── LICENSE
├── default.properties
├── res
├── drawable-hdpi
│ ├── banner.png
│ └── icon.png
├── drawable-ldpi
│ ├── banner.png
│ └── icon.png
├── drawable-mdpi
│ ├── banner.png
│ └── icon.png
├── layout
│ ├── accountdetailsview.xml
│ ├── accountsactivity.xml
│ ├── loginactivity.xml
│ ├── setlocalpasswordactivity.xml
│ ├── setservercredentialsactivity.xml
│ ├── statementactivity.xml
│ ├── summaryactivity.xml
│ ├── transferactivity.xml
│ └── viewstatementactivity.xml
├── menu
│ └── options_menu.xml
├── values
│ └── strings.xml
└── xml
│ └── userpreferences.xml
└── src
└── com
└── securitycompass
└── labs
└── falsesecuremobile
├── Account.java
├── AccountsActivity.java
├── BankingActivity.java
├── BankingApplication.java
├── BankingListActivity.java
├── CryptoTool.java
├── EditPreferencesActivity.java
├── HttpException.java
├── LoginActivity.java
├── RestClient.java
├── SetLocalPasswordActivity.java
├── SetServerCredentialsActivity.java
├── StatementActivity.java
├── SummaryActivity.java
├── TransferActivity.java
└── ViewStatementActivity.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | FalseSecureMobile
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 | net.sourceforge.metrics.builder
30 |
31 |
32 |
33 |
34 |
35 | com.android.ide.eclipse.adt.AndroidNature
36 | org.eclipse.jdt.core.javanature
37 | net.sourceforge.metrics.nature
38 |
39 |
40 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | #Mon Mar 14 12:06:34 EDT 2011
2 | eclipse.preferences.version=1
3 | org.eclipse.jdt.core.codeComplete.argumentPrefixes=
4 | org.eclipse.jdt.core.codeComplete.argumentSuffixes=
5 | org.eclipse.jdt.core.codeComplete.fieldPrefixes=
6 | org.eclipse.jdt.core.codeComplete.fieldSuffixes=
7 | org.eclipse.jdt.core.codeComplete.localPrefixes=
8 | org.eclipse.jdt.core.codeComplete.localSuffixes=
9 | org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
10 | org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
11 | org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
12 | org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
13 | org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
14 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
15 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
16 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
17 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
18 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
19 | org.eclipse.jdt.core.formatter.alignment_for_assignment=0
20 | org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
21 | org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
22 | org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
23 | org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
24 | org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
25 | org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
26 | org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
27 | org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
28 | org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
29 | org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
30 | org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
31 | org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
32 | org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
33 | org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
34 | org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
35 | org.eclipse.jdt.core.formatter.blank_lines_after_package=1
36 | org.eclipse.jdt.core.formatter.blank_lines_before_field=0
37 | org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
38 | org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
39 | org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
40 | org.eclipse.jdt.core.formatter.blank_lines_before_method=1
41 | org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
42 | org.eclipse.jdt.core.formatter.blank_lines_before_package=0
43 | org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
44 | org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
45 | org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
46 | org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
47 | org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
48 | org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
49 | org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
50 | org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
51 | org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
52 | org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
53 | org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
54 | org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
55 | org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
56 | org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
57 | org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=true
58 | org.eclipse.jdt.core.formatter.comment.format_block_comments=true
59 | org.eclipse.jdt.core.formatter.comment.format_header=false
60 | org.eclipse.jdt.core.formatter.comment.format_html=true
61 | org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
62 | org.eclipse.jdt.core.formatter.comment.format_line_comments=true
63 | org.eclipse.jdt.core.formatter.comment.format_source_code=true
64 | org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
65 | org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
66 | org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=do not insert
67 | org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
68 | org.eclipse.jdt.core.formatter.comment.line_length=100
69 | org.eclipse.jdt.core.formatter.compact_else_if=true
70 | org.eclipse.jdt.core.formatter.continuation_indentation=2
71 | org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
72 | org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
73 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
74 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
75 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
76 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
77 | org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
78 | org.eclipse.jdt.core.formatter.indent_empty_lines=false
79 | org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
80 | org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
81 | org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
82 | org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
83 | org.eclipse.jdt.core.formatter.indentation.size=8
84 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
85 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
86 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
87 | org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
88 | org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
89 | org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
90 | org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
91 | org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
92 | org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
93 | org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
94 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
95 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
96 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
97 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
98 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
99 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
100 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
101 | org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
102 | org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
103 | org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
104 | org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
105 | org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
106 | org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
107 | org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
108 | org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
109 | org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
110 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
111 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
112 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
113 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
114 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
115 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
116 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
117 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
118 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
119 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
120 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
121 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
122 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
123 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
124 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
125 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
126 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
127 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
128 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
129 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
130 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
131 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
132 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
133 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
134 | org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
135 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
136 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
137 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
138 | org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
139 | org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
140 | org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
141 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
142 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
143 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
144 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
145 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
146 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
147 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
148 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
149 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
150 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
151 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
152 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
153 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
154 | org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
155 | org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
156 | org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
157 | org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
158 | org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
159 | org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
160 | org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
161 | org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
162 | org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
163 | org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
164 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
165 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
166 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
167 | org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
168 | org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
169 | org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
170 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
171 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
172 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
173 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
174 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
175 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
176 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
177 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
178 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
179 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
180 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
181 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
182 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
183 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
184 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
185 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
186 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
187 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
188 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
189 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
190 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
191 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
192 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
193 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
194 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
195 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
196 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
197 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
198 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
199 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
200 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
201 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
202 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
203 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
204 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
205 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
206 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
207 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
208 | org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
209 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
210 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
211 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
212 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
213 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
214 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
215 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
216 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
217 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
218 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
219 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
220 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
221 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
222 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
223 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
224 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
225 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
226 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
227 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
228 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
229 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
230 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
231 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
232 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
233 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
234 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
235 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
236 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
237 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
238 | org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
239 | org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
240 | org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
241 | org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
242 | org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
243 | org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
244 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
245 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
246 | org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
247 | org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
248 | org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
249 | org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
250 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
251 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
252 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
253 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
254 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
255 | org.eclipse.jdt.core.formatter.join_lines_in_comments=true
256 | org.eclipse.jdt.core.formatter.join_wrapped_lines=true
257 | org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
258 | org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
259 | org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
260 | org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
261 | org.eclipse.jdt.core.formatter.lineSplit=100
262 | org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
263 | org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
264 | org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
265 | org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
266 | org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
267 | org.eclipse.jdt.core.formatter.tabulation.char=space
268 | org.eclipse.jdt.core.formatter.tabulation.size=4
269 | org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
270 | org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
271 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.ui.prefs:
--------------------------------------------------------------------------------
1 | #Mon Mar 07 22:32:23 EST 2011
2 | cleanup.add_default_serial_version_id=true
3 | cleanup.add_generated_serial_version_id=false
4 | cleanup.add_missing_annotations=true
5 | cleanup.add_missing_deprecated_annotations=true
6 | cleanup.add_missing_methods=false
7 | cleanup.add_missing_nls_tags=false
8 | cleanup.add_missing_override_annotations=true
9 | cleanup.add_serial_version_id=false
10 | cleanup.always_use_blocks=true
11 | cleanup.always_use_parentheses_in_expressions=false
12 | cleanup.always_use_this_for_non_static_field_access=false
13 | cleanup.always_use_this_for_non_static_method_access=false
14 | cleanup.convert_to_enhanced_for_loop=false
15 | cleanup.correct_indentation=false
16 | cleanup.format_source_code=false
17 | cleanup.format_source_code_changes_only=false
18 | cleanup.make_local_variable_final=true
19 | cleanup.make_parameters_final=false
20 | cleanup.make_private_fields_final=true
21 | cleanup.make_type_abstract_if_missing_method=false
22 | cleanup.make_variable_declarations_final=false
23 | cleanup.never_use_blocks=false
24 | cleanup.never_use_parentheses_in_expressions=true
25 | cleanup.organize_imports=false
26 | cleanup.qualify_static_field_accesses_with_declaring_class=false
27 | cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
28 | cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
29 | cleanup.qualify_static_member_accesses_with_declaring_class=true
30 | cleanup.qualify_static_method_accesses_with_declaring_class=false
31 | cleanup.remove_private_constructors=true
32 | cleanup.remove_trailing_whitespaces=false
33 | cleanup.remove_trailing_whitespaces_all=true
34 | cleanup.remove_trailing_whitespaces_ignore_empty=false
35 | cleanup.remove_unnecessary_casts=true
36 | cleanup.remove_unnecessary_nls_tags=true
37 | cleanup.remove_unused_imports=true
38 | cleanup.remove_unused_local_variables=false
39 | cleanup.remove_unused_private_fields=true
40 | cleanup.remove_unused_private_members=false
41 | cleanup.remove_unused_private_methods=true
42 | cleanup.remove_unused_private_types=true
43 | cleanup.sort_members=false
44 | cleanup.sort_members_all=false
45 | cleanup.use_blocks=false
46 | cleanup.use_blocks_only_for_return_and_throw=false
47 | cleanup.use_parentheses_in_expressions=false
48 | cleanup.use_this_for_non_static_field_access=false
49 | cleanup.use_this_for_non_static_field_access_only_if_necessary=true
50 | cleanup.use_this_for_non_static_method_access=false
51 | cleanup.use_this_for_non_static_method_access_only_if_necessary=true
52 | cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile
53 | cleanup_settings_version=2
54 | eclipse.preferences.version=1
55 | formatter_profile=_Android Stylee
56 | formatter_settings_version=11
57 | org.eclipse.jdt.ui.exception.name=e
58 | org.eclipse.jdt.ui.gettersetter.use.is=true
59 | org.eclipse.jdt.ui.javadoc=false
60 | org.eclipse.jdt.ui.keywordthis=false
61 | org.eclipse.jdt.ui.overrideannotation=true
62 | org.eclipse.jdt.ui.text.custom_code_templates=/**\n * @return the ${bare_field_name}\n *//**\n * @param ${param} the ${bare_field_name} to set\n *//**\n * ${tags}\n *//**\n * \n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * \n *//**\n * ${tags}\n *//* (non-Javadoc)\n * ${see_to_overridden}\n *//**\n * ${tags}\n * ${see_to_target}\n */${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n\n\n\n// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();// ${todo} Auto-generated method stub\n${body_statement}${body_statement}\n// ${todo} Auto-generated constructor stubreturn ${field};${field} \= ${param};
63 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.ltk.core.refactoring.prefs:
--------------------------------------------------------------------------------
1 | #Mon Mar 07 22:36:11 EST 2011
2 | eclipse.preferences.version=1
3 | org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false
4 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011, Security Compass
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 | 1. Redistributions of source code must retain the above copyright notice, this
7 | list of conditions and the following disclaimer.
8 | 2. Redistributions in binary form must reproduce the above copyright notice,
9 | this list of conditions and the following disclaimer in the documentation
10 | and/or other materials provided with the distribution.
11 | 3. All advertising materials mentioning features or use of this software
12 | must display the following acknowledgement:
13 | This product includes software developed by Security Compass.
14 | 4. Neither the name of Security Compass nor the names of its contributors may be
15 | used to endorse or promote products derived from this software without
16 | specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY Security Compass ''AS IS'' AND ANY EXPRESS OR IMPLIED
19 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
21 | EVENT SHALL Security Compass BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 | POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/default.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "build.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-9
12 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SecurityCompass/AndroidLabs/8318d0136bc29ffc428bdfe6aa89db83c1ebd608/res/drawable-hdpi/banner.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SecurityCompass/AndroidLabs/8318d0136bc29ffc428bdfe6aa89db83c1ebd608/res/drawable-hdpi/icon.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SecurityCompass/AndroidLabs/8318d0136bc29ffc428bdfe6aa89db83c1ebd608/res/drawable-ldpi/banner.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SecurityCompass/AndroidLabs/8318d0136bc29ffc428bdfe6aa89db83c1ebd608/res/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SecurityCompass/AndroidLabs/8318d0136bc29ffc428bdfe6aa89db83c1ebd608/res/drawable-mdpi/banner.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SecurityCompass/AndroidLabs/8318d0136bc29ffc428bdfe6aa89db83c1ebd608/res/drawable-mdpi/icon.png
--------------------------------------------------------------------------------
/res/layout/accountdetailsview.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/res/layout/accountsactivity.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
9 |
10 |
--------------------------------------------------------------------------------
/res/layout/loginactivity.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/res/layout/setlocalpasswordactivity.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
15 |
19 |
22 |
26 |
31 |
32 |
--------------------------------------------------------------------------------
/res/layout/setservercredentialsactivity.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
12 |
14 |
17 |
20 |
25 |
26 |
--------------------------------------------------------------------------------
/res/layout/statementactivity.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/res/layout/summaryactivity.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/layout/transferactivity.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
12 |
14 |
16 |
18 |
20 |
23 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/res/layout/viewstatementactivity.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/res/menu/options_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | FalseSecureMobile
4 |
5 | Username
6 | Password
7 | Login
8 |
9 | Accounts
10 | Transfer
11 | Statement
12 |
13 | Clear
14 |
15 | From
16 | To
17 | Amount
18 | Transfer
19 | Enter amount
20 | Transfer completed
21 |
22 | Account 1\n\n\tChecking Account\n\tBalance:
23 | $53.00\n\nAccount 2\n\n\tSavings Account\n\tBalance: $2876.28\n
24 |
25 |
26 | Please configure a local password
27 | Password
28 | Confirm
29 | Done
30 |
31 | Please enter your bank credentials
32 | Username
33 | Password
34 | Done
35 |
36 |
37 | Login failed
38 | Couldn\'t communicate with server
39 | HTTP error:
40 | Couldn\'t interpret server response
41 | Can\'t transfer to same account
42 | Invalid amount
43 | Passwords don\'t match
44 | Weak password. Needs to contain an uppercase letter, a lowercase letter, a special character and be at least 6 characters long.
45 | The password checking and setting mechanism has failed. Please see your application vendor, as this is unrecoverable.
46 | The SSL mechanism has failed due to an unsupported algorithm. Please see your application vendor, as this is unrecoverable.
47 | The SSL mechanism has failed due to a key management problem. Please see your application vendor, as this is unrecoverable.
48 | SSL Communication error
49 | Can\'t display selected statement
50 | Application is now successfully set up
51 |
52 |
56 |
57 |
--------------------------------------------------------------------------------
/res/xml/userpreferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
11 |
12 |
15 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/Account.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | /**
8 | * @Author Ewan Sinclair
9 | * Represents a bank account */
10 | public class Account {
11 |
12 | private int accountNumber;
13 | private String accountType;
14 | private double balance;
15 |
16 | /** Creates a new Account with the given parameters
17 | * @param accountNumber The account number
18 | * @param accountType The type of account
19 | * @param balance The balance held in the account*/
20 | public Account(int accountNumber, String accountType, double balance) {
21 | this.accountNumber = accountNumber;
22 | this.accountType = accountType;
23 | this.balance = balance;
24 | }
25 |
26 | /** Returns the account type
27 | * @return The type of Account*/
28 | public String getAccountType() {
29 | return accountType;
30 | }
31 |
32 | /** Sets the account type
33 | * @param accountType The type of Account to set*/
34 | public void setAccountType(String accountType) {
35 | this.accountType = accountType;
36 | }
37 |
38 | /** Returns the balance of the Account
39 | * @return The balance of the Account*/
40 | public double getBalance() {
41 | return balance;
42 | }
43 |
44 | /** Sets the balance of the Account
45 | * @param balance The balance to set for the Account*/
46 | public void setBalance(double balance) {
47 | this.balance = balance;
48 | }
49 |
50 | /** Sets the account number
51 | * @param accountNumber The account number to set*/
52 | public void setAccountNumber(int accountNumber) {
53 | this.accountNumber = accountNumber;
54 | }
55 |
56 | /** Returns the account number
57 | * @return The account number for this Account*/
58 | public int getAccountNumber() {
59 | return accountNumber;
60 | }
61 |
62 | /** Returns a String representation of the Account
63 | * @return A String representation of this Account*/
64 | public String toString(){
65 | return (accountNumber + " (" + accountType + "): " + balance);
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/AccountsActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import java.io.IOException;
8 | import java.security.KeyManagementException;
9 | import java.security.NoSuchAlgorithmException;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | import org.json.JSONException;
14 |
15 | import android.accounts.AuthenticatorException;
16 | import android.content.Context;
17 | import android.os.Bundle;
18 | import android.util.Log;
19 | import android.view.LayoutInflater;
20 | import android.view.View;
21 | import android.view.ViewGroup;
22 | import android.widget.ArrayAdapter;
23 | import android.widget.TextView;
24 | import android.widget.Toast;
25 |
26 | /**
27 | * Displays a full summary of all the accounts
28 | * @author Ewan Sinclair
29 | */
30 | public class AccountsActivity extends BankingListActivity {
31 |
32 | /** Useful for avoiding casts when a Context needs to be passed */
33 | private Context mCtx;
34 | /** Central data store, state, and operations */
35 | private BankingApplication mThisApplication;
36 | /** A list of the accounts for this user */
37 | private List mAccounts;
38 |
39 | private static final String TAG = "AccountsActivity";
40 |
41 | /** Called when the activity is first created. */
42 | @Override
43 | public void onCreate(Bundle savedInstanceState) {
44 |
45 | super.onCreate(savedInstanceState);
46 | setContentView(R.layout.accountsactivity);
47 | setAppropriateVisibility();
48 |
49 | mCtx = this;
50 | mAccounts=new ArrayList(); //To avoid null reference on network error
51 |
52 | mThisApplication = (BankingApplication) getApplication();
53 |
54 | updateAccounts();
55 |
56 | setListAdapter(new AccountDetailAdapter(mCtx, R.layout.accountdetailsview, mAccounts));
57 |
58 | }
59 |
60 | /** Updates the account information stored locally and refreshes the display */
61 | private void updateAccounts() {
62 | try {
63 | mAccounts = mThisApplication.getAccounts();
64 | } catch (JSONException e) {
65 | Toast.makeText(mCtx, R.string.error_toast_json_problem, Toast.LENGTH_SHORT).show();
66 | Log.e(TAG, e.toString());
67 | } catch (AuthenticatorException e) {
68 | Log.e(TAG, e.toString());
69 | authenticate();
70 | } catch (KeyManagementException e){
71 | Toast.makeText(mCtx, R.string.error_ssl_keymanagement, Toast.LENGTH_LONG).show();
72 | Log.e(TAG, e.toString());
73 | } catch (NoSuchAlgorithmException e){
74 | Toast.makeText(mCtx, R.string.error_ssl_algorithm, Toast.LENGTH_LONG).show();
75 | Log.e(TAG, e.toString());
76 | } catch (IOException e) {
77 | Toast.makeText(mCtx, R.string.error_toast_rest_problem, Toast.LENGTH_SHORT).show();
78 | Log.e(TAG, e.toString());
79 | }
80 |
81 | //If the account list failed on retrieval, use an empty list
82 | if (!mThisApplication.isLocked()) {
83 | //refreshDisplayInformation();
84 | } else {
85 | mAccounts=new ArrayList();
86 | }
87 | }
88 |
89 | /**
90 | * Returns a version of the given string with the first letter in uppercase.
91 | * @param input The String to capitalise.
92 | * @return The capitalised String.
93 | */
94 | private String capitalise(String input) {
95 | String result = input.substring(0, 1).toUpperCase() + input.substring(1);
96 | return result;
97 | }
98 |
99 | private class AccountDetailAdapter extends ArrayAdapter{
100 |
101 | public AccountDetailAdapter(Context context, int viewResourceId, List accounts) {
102 | super(context, viewResourceId, accounts);
103 | }
104 |
105 | @Override
106 | public View getView(int position, View convertView, ViewGroup parent){
107 | View item=convertView;
108 |
109 | if (item == null) {
110 | LayoutInflater inflater = getLayoutInflater();
111 | item = inflater.inflate(R.layout.accountdetailsview, null);
112 | }
113 |
114 | TextView accountNumber = (TextView) item.findViewById(R.id.accounts_screen_accountnumber);
115 | TextView accountType = (TextView) item.findViewById(R.id.accounts_screen_accounttype);
116 | TextView accountBalance = (TextView) item.findViewById(R.id.accounts_screen_accountbalance);
117 |
118 | Account displayAccount=mAccounts.get(position);
119 | accountNumber.setText("Account " + displayAccount.getAccountNumber());
120 | accountType.setText(capitalise(displayAccount.getAccountType()) + " Account");
121 | accountBalance.setText("Balance: $" + displayAccount.getBalance());
122 |
123 | return item;
124 | }
125 |
126 |
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/BankingActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import android.app.Activity;
8 | import android.content.Intent;
9 | import android.content.SharedPreferences.Editor;
10 | import android.os.Bundle;
11 | import android.view.Menu;
12 | import android.view.MenuInflater;
13 | import android.view.MenuItem;
14 | import android.view.View;
15 |
16 | /**
17 | * A superclass containing methods and variables that all Activities in this application will use
18 | * @author Ewan Sinclair
19 | */
20 | public class BankingActivity extends Activity {
21 |
22 | /** Central data store, state, and operations */
23 | protected BankingApplication mThisApplication;
24 |
25 | @Override
26 | public void onCreate(Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | mThisApplication = (BankingApplication) getApplication();
29 | if (mThisApplication.isLocked() && (this.getClass() != LoginActivity.class)) {
30 | launchLoginScreen();
31 | } else {
32 | View v = findViewById(R.id.root_view);
33 | if (v != null) {
34 | v.setVisibility(View.VISIBLE);
35 | }
36 | }
37 | }
38 |
39 | @Override
40 | public boolean onOptionsItemSelected(MenuItem item) {
41 | switch (item.getItemId()) {
42 | case R.id.optionsmenu_reset:
43 | resetApplication();
44 | return true;
45 | case R.id.optionsmenu_preferences:
46 | launchPreferenceScreen();
47 | return true;
48 | default:
49 | return super.onOptionsItemSelected(item);
50 | }
51 | }
52 |
53 | @Override
54 | public boolean onCreateOptionsMenu(Menu menu) {
55 | super.onCreateOptionsMenu(menu);
56 | MenuInflater inflater = getMenuInflater();
57 | inflater.inflate(R.menu.options_menu, menu);
58 | return true;
59 | }
60 |
61 | @Override
62 | protected void onResume() {
63 | super.onResume();
64 | setAppropriateVisibility();
65 | mThisApplication.registerActivityForegrounded();
66 | if (mThisApplication.isLocked() && (this.getClass() != LoginActivity.class)) {
67 | Intent i = new Intent(this, LoginActivity.class);
68 | i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
69 | startActivity(i);
70 | }
71 | }
72 |
73 | @Override
74 | protected void onPause() {
75 | super.onPause();
76 | setInvisible();
77 | mThisApplication.registerActivityBackgrounded();
78 | }
79 |
80 | /**
81 | * Checks whether the application is locked and makes this Activity invisible if so.
82 | */
83 | public void setAppropriateVisibility() {
84 | View v = findViewById(R.id.root_view);
85 | if (v != null) {
86 | if (mThisApplication.isLocked()) {
87 | v.setVisibility(View.GONE);
88 | } else {
89 | v.setVisibility(View.VISIBLE);
90 | }
91 | }
92 | }
93 |
94 | /**
95 | * Makes this Activity invisible.
96 | */
97 | public void setInvisible() {
98 | View v = findViewById(R.id.root_view);
99 | if (v != null) {
100 | v.setVisibility(View.GONE);
101 | }
102 |
103 | }
104 |
105 | private void resetApplication() {
106 | BankingApplication ba = (BankingApplication) getApplication();
107 | ba.clearStatements();
108 | Editor e = ba.getSharedPrefs().edit();
109 | e.clear();
110 | e.commit();
111 | ba.lockApplication();
112 | launchLoginScreen();
113 | }
114 |
115 | private void launchPreferenceScreen() {
116 | Intent i = new Intent(this, EditPreferencesActivity.class);
117 | startActivity(i);
118 | }
119 |
120 | /**
121 | * Called when the app needs authentication, normally due to a session timeout. The current
122 | * activity stack will be cleared, and the login Activity brought to the front.
123 | */
124 | protected void authenticate() {
125 | Intent i = new Intent(this, LoginActivity.class);
126 | i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
127 | startActivity(i);
128 | }
129 |
130 | /** Launches the accounts screen, doing any necessary processing first */
131 | private void launchLoginScreen() {
132 | Intent launchLogin = new Intent(this, LoginActivity.class);
133 | launchLogin.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
134 | startActivity(launchLogin);
135 | }
136 |
137 | }
138 |
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/BankingApplication.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import java.io.File;
8 | import java.io.FileOutputStream;
9 | import java.io.IOException;
10 | import java.io.UnsupportedEncodingException;
11 | import java.security.GeneralSecurityException;
12 | import java.security.KeyManagementException;
13 | import java.security.MessageDigest;
14 | import java.security.NoSuchAlgorithmException;
15 | import java.security.SecureRandom;
16 | import java.util.List;
17 |
18 | import org.json.JSONException;
19 |
20 | import android.accounts.AuthenticatorException;
21 | import android.app.Application;
22 | import android.content.SharedPreferences;
23 | import android.content.SharedPreferences.Editor;
24 | import android.os.Handler;
25 | import android.preference.PreferenceManager;
26 | import android.util.Base64;
27 | import android.util.Log;
28 |
29 | /**
30 | * Stores session keys and handles moving the application between locked and unlocked states.
31 | * Instantiated when the application loads.
32 | * @author Ewan Sinclair
33 | */
34 | public class BankingApplication extends Application {
35 |
36 | private String sessionKey;
37 | private String sessionCreateDate;
38 | private boolean locked;
39 | private String cleartextServerUser;
40 | private String cleartextServerPass;
41 |
42 | private int foregroundedActivities;
43 | private Handler timingHandler;
44 | private CryptoTool mCipher;
45 | private byte[] mCryptoKey;
46 |
47 | // How many hashing iterations to perform
48 | private static final int HASH_ITERATIONS = 1000;
49 |
50 | /* These variables are used for anchoring preference keys */
51 | /** The name of the shared preferences file for prefs not accessible via the preferences screen */
52 | public static final String SHARED_PREFS = "preferences";
53 | /** Whether the application is running for the first time */
54 | public static final String PREF_FIRST_RUN = "firstrun";
55 | /** A hash of the local password */
56 | public static final String PREF_LOCALPASS_HASH = "localpasshash";
57 | /** The salt used when hashing the local password */
58 | public static final String PREF_LOCALPASS_SALT = "localpasssalt";
59 | /** The username to present to the banking service */
60 | public static final String PREF_REST_USER = "serveruser";
61 | /** The password to present to the banking service */
62 | public static final String PREF_REST_PASSWORD = "serverpass";
63 | /** The initialisation vector for the encrypted banking service password */
64 | public static final String PREF_REST_PASSWORD_IV = "serverpassiv";
65 | /** The initialisation vector for the encrypted banking service username */
66 | public static final String PREF_REST_USER_IV = "serveruseriv";
67 | public static String PREF_DERIVED_KEY_SALT = "derivedkeysalt";
68 |
69 | /** A tag to identify the class if it logs anything */
70 | public static final String TAG = "BankingApplication";
71 |
72 | /** Setup for when the application initialises. */
73 | @Override
74 | public void onCreate() {
75 | super.onCreate();
76 | timingHandler = new Handler();
77 | foregroundedActivities = 0;
78 | locked = true;
79 | mCipher = new CryptoTool();
80 | }
81 |
82 | /**
83 | * Returns a string representation of the server we will be making our requests on.
84 | * @return A string representation of the server address.
85 | */
86 | public String getRestServer() {
87 | return PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString(
88 | "bankserviceaddress", "10.0.2.2");
89 | }
90 |
91 | /**
92 | * Returns a string representation of the port we will be making our HTTP requests on.
93 | * @return A string representation of the port set for HTTP communication.
94 | */
95 | public String getPort() {
96 | if (isHttpsEnabled()) {
97 | return PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
98 | .getString("httpsport", "8443");
99 | } else {
100 | return PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
101 | .getString("httpport", "8080");
102 | }
103 | }
104 |
105 | /**
106 | * Returns the directory where statements are kept, as a String.
107 | * @return The directory where statements are kept, as a String.
108 | */
109 | public String getStatementDir() {
110 | return getFilesDir().toString();
111 | }
112 |
113 | /**
114 | * Returns whether the HTTPS setting is enabled, as a boolean.
115 | * @return whether the HTTPS setting is enabled, as a boolean.
116 | */
117 | public boolean isHttpsEnabled() {
118 | return PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean(
119 | "httpsenabled", false);
120 | }
121 |
122 | /**
123 | * Checks to see if the given password hashes to the same value as the stored one.
124 | * @param enteredPassword The password to check.
125 | * @return Whether the password matched.
126 | * @throws NoSuchAlgorithmException if the hashing algorithm is unavailable
127 | * @throws UnsupportedEncodingException if Base64 encoding is not available
128 | */
129 | public boolean checkPassword(String enteredPassword) throws NoSuchAlgorithmException,
130 | UnsupportedEncodingException {
131 | String saltString = getSharedPrefs().getString(PREF_LOCALPASS_SALT, "");
132 | byte[] saltBytes = Base64.decode(saltString, Base64.DEFAULT);
133 | String hashOfEnteredPassword = hash(enteredPassword, saltBytes);
134 | String hashOfStoredpassword = getSharedPrefs().getString(PREF_LOCALPASS_HASH, "");
135 | return hashOfEnteredPassword.equals(hashOfStoredpassword);
136 | }
137 |
138 | /**
139 | * Peforms a bunch of hash iterations on the given String and salt and returns the result.
140 | * @param password String password to hash.
141 | * @param salt The salt to hash with.
142 | * @return A base64 encoded string representing the hash.
143 | * @throws NoSuchAlgorithmException
144 | * @throws UnsupportedEncodingException
145 | */
146 | private String hash(String password, byte[] salt) throws NoSuchAlgorithmException,
147 | UnsupportedEncodingException {
148 | byte[] passwordBytes = (password).getBytes("UTF-8");
149 |
150 | MessageDigest md = MessageDigest.getInstance("SHA-256");
151 | md.reset();
152 | md.update(salt);
153 | byte[] hashed = md.digest(passwordBytes);
154 |
155 | for (int count = 0; count < HASH_ITERATIONS; count++) {
156 | md.reset();
157 | md.update(salt);
158 | hashed = md.digest(hashed);
159 | }
160 |
161 | byte[] b64Hash = Base64.encode(hashed, Base64.DEFAULT);
162 | return new String(b64Hash);
163 |
164 | }
165 |
166 | /**
167 | * Returns the shared preferences for this app, with permission mode pre-set.
168 | * @return The shared preferences for this app, with permission mode pre-set.
169 | */
170 | public SharedPreferences getSharedPrefs() {
171 | return getSharedPreferences(SHARED_PREFS, MODE_WORLD_READABLE);
172 | }
173 |
174 | /**
175 | * Logs into the REST service, generating a new session key.
176 | * @param username Username to log in with.
177 | * @param password Password to log in with.
178 | * @return A status code representing any error that occurred.
179 | * @throws JSONException if the server returned invalid JSON
180 | * @throws IOException if there was a communication error with the server
181 | * @throws KeyManagementException if the server key couldn't be trusted
182 | * @throws HttpException if the HTTP/S request failed
183 | * @throws NoSuchAlgorithmException if the set SSL encryption algorithm is unavilable
184 | */
185 | public int performLogin(String username, String password) throws JSONException, IOException,
186 | KeyManagementException, HttpException, NoSuchAlgorithmException {
187 | RestClient restClient = new RestClient(this, isHttpsEnabled());
188 | int statusCode = restClient.performLogin(getRestServer(), getPort(), username, password);
189 | return statusCode;
190 | }
191 |
192 | /** Performs all operations necessary to secure the application. */
193 | public void lockApplication() {
194 | cleartextServerUser = "";
195 | cleartextServerPass = "";
196 | sessionKey = "";
197 | sessionCreateDate = "";
198 | if (mCryptoKey != null) {
199 | for (byte b : mCryptoKey) {
200 | b = 'x';
201 | }
202 | }
203 | locked = true;
204 | System.gc();
205 | }
206 |
207 | /**
208 | * Performs all operations necessary to make the application usable.
209 | * @param password The password to try unlocking with
210 | * @return Whether the operation succeeded
211 | * @throws UnsupportedEncodingException if Base64 encoding isn't available
212 | * @throws NoSuchAlgorithmException if the hashing algorithm couldn't be found
213 | * @throws JSONException if the server returned invalid JSON
214 | * @throws IOException if there was a communication error with the server
215 | * @throws KeyManagementException if the server key couldn't be trusted
216 | * @throws GeneralSecurityException if a cryptographic operation failed
217 | * @throws HttpException if the HTTP/S request failed
218 | */
219 | public int unlockApplication(String password) throws UnsupportedEncodingException,
220 | NoSuchAlgorithmException, IOException, JSONException, KeyManagementException,
221 | GeneralSecurityException, HttpException {
222 |
223 | if (checkPassword(password)) {
224 | mCryptoKey = mCipher.genKeyPwkdf2(password, getPbkSalt(), CryptoTool.NUM_ITERATIONS)
225 | .getEncoded();
226 | cleartextServerUser = mCipher.decryptB64String(getRestUsername(), mCryptoKey,
227 | getRestUserNameIv());
228 | cleartextServerPass = mCipher.decryptB64String(getRestPassword(), mCryptoKey,
229 | getRestPasswordIv());
230 | int statusCode = performLogin(cleartextServerUser, cleartextServerPass);
231 | if (statusCode == RestClient.NULL_ERROR) {
232 | locked = false;
233 | return statusCode;
234 | }
235 | }
236 | return RestClient.NO_OP;
237 | }
238 |
239 | /**
240 | * Returns the application's state of lockdown.
241 | * @return The application's state of lockdown.
242 | */
243 | public boolean isLocked() {
244 | return locked;
245 | }
246 |
247 | /**
248 | * Returns the AES key currently in use.
249 | * @return The AES key currently in use.
250 | */
251 | public byte[] getCryptoKey() {
252 | return mCryptoKey;
253 | }
254 |
255 | /**
256 | * Returns the salt to be used for the password-generated AES key.
257 | * @return The salt to be used for the password-generated AES key.
258 | */
259 | public byte[] getPbkSalt() {
260 | return Base64.decode(getSharedPrefs().getString(PREF_DERIVED_KEY_SALT, ""), Base64.DEFAULT);
261 | }
262 |
263 | /**
264 | * Returns the stored username for the REST service.
265 | * @return The stored username for the REST service.
266 | */
267 | public String getRestUsername() {
268 | return getSharedPrefs().getString(PREF_REST_USER, "");
269 | }
270 |
271 | /**
272 | * Returns the IV for the stored username for the REST service.
273 | * @return The IV for the stored username for the REST service.
274 | */
275 | public byte[] getRestUserNameIv() {
276 | return Base64.decode(getSharedPrefs().getString(PREF_REST_USER_IV, ""), Base64.DEFAULT);
277 | }
278 |
279 | /**
280 | * Returns the stored password for the REST service.
281 | * @return The stored password for the REST service.
282 | */
283 | public String getRestPassword() {
284 | return getSharedPrefs().getString(PREF_REST_PASSWORD, "");
285 | }
286 |
287 | /**
288 | * Returns the IV for the stored password for the REST service.
289 | * @return The IV for the stored password for the REST service.
290 | */
291 | public byte[] getRestPasswordIv() {
292 | return Base64.decode(getSharedPrefs().getString(PREF_REST_PASSWORD_IV, ""), Base64.DEFAULT);
293 | }
294 |
295 | /**
296 | * Sets the user's credentials for the banking service.
297 | * @param username The username to set.
298 | * @param password The password to set.
299 | * @throws GeneralSecurityException if a cryptographic operation failed
300 | */
301 | public void setServerCredentials(String username, String password)
302 | throws GeneralSecurityException {
303 |
304 | byte[] userIv = mCipher.getIv();
305 | byte[] passwordIv = mCipher.getIv();
306 |
307 | String cryptUsername = mCipher.encryptToB64String(username, mCryptoKey, userIv);
308 | String cryptPassword = mCipher.encryptToB64String(password, mCryptoKey, passwordIv);
309 |
310 | Editor e = getSharedPrefs().edit();
311 | e.putString(PREF_REST_USER, cryptUsername);
312 | e.putString(PREF_REST_PASSWORD, cryptPassword);
313 | e.putString(PREF_REST_USER_IV, new String(Base64.encode(userIv, Base64.DEFAULT)));
314 | e.putString(PREF_REST_PASSWORD_IV, new String(Base64.encode(passwordIv, Base64.DEFAULT)));
315 | e.commit();
316 | }
317 |
318 | /**
319 | * Sets the local password, accomplished by storing a hashcode.
320 | * @param password The plain String version of the password to set.
321 | * @throws NoSuchAlgorithmException if the hashing algorithm is unavailable
322 | * @throws UnsupportedEncodingException if Base64 encoding is not available
323 | */
324 | public void setLocalPassword(String password) throws NoSuchAlgorithmException,
325 | UnsupportedEncodingException {
326 |
327 | // First we generate a random salt
328 | SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
329 | byte[] saltByteArray = new byte[32];
330 | random.nextBytes(saltByteArray);
331 |
332 | // Perform the hash, getting a Base64 encoded String
333 | String hashString = hash(password, saltByteArray);
334 |
335 | // Base64 encode the salt, store our strings
336 | byte[] b64Salt = Base64.encode(saltByteArray, Base64.DEFAULT);
337 | String saltString = new String(b64Salt);
338 |
339 | Editor e = getSharedPrefs().edit();
340 | e.putString(PREF_LOCALPASS_HASH, hashString);
341 | e.putString(PREF_LOCALPASS_SALT, saltString);
342 | e.commit();
343 | }
344 |
345 | /**
346 | * Generates a key using PBKDF2 and uses it to encrypt and set the banking service credentials.
347 | * Also sets the local password (which will be checked by hash before being used for generating
348 | * a key).
349 | * @param localPass The local password to set.
350 | * @param restUser The username for the banking service.
351 | * @param restPass The password for the banking service.
352 | * @throws GeneralSecurityException if a cryptographic operation failed.
353 | * @throws UnsupportedEncodingException if Base64 encoding is unavailable.
354 | */
355 | public void setCredentials(String localPass, String restUser, String restPass)
356 | throws GeneralSecurityException, UnsupportedEncodingException {
357 |
358 | Editor e = getSharedPrefs().edit();
359 | byte[] salt = mCipher.getSalt();
360 | String b64Salt = new String(Base64.encode(salt, Base64.DEFAULT));
361 | e.putString(PREF_DERIVED_KEY_SALT, b64Salt);
362 | e.commit();
363 |
364 | mCryptoKey = mCipher.genKeyPwkdf2(localPass, salt, CryptoTool.NUM_ITERATIONS).getEncoded();
365 |
366 | setLocalPassword(localPass);
367 | setServerCredentials(restUser, restPass);
368 |
369 | }
370 |
371 | /**
372 | * Returns a list of all Accounts and their details.
373 | * @return A list of the accounts returned by the server, represented as Account objects.
374 | * @throws JSONException if the server returned invalid JSON
375 | * @throws IOException if the network connection failed
376 | * @throws AuthenticatorException if the server rejects the session key
377 | * @throws NoSuchAlgorithmException if the algorithm used to hash the password is not available
378 | * @throws KeyManagementException if the server's SSL certificate couldn't be trusted
379 | */
380 | public List getAccounts() throws JSONException, IOException, AuthenticatorException,
381 | NoSuchAlgorithmException, KeyManagementException {
382 | RestClient restClient = new RestClient(this, isHttpsEnabled());
383 | List result = null;
384 | try {
385 | result = restClient.getAccounts(getRestServer(), getPort());
386 | } catch (AuthenticatorException e) {
387 | lockApplication();
388 | throw e;
389 | }
390 |
391 | return result;
392 | }
393 |
394 | /**
395 | * Downloads a statement and displays it. within this class.
396 | * @throws IOException if network communication failed
397 | * @throws NoSuchAlgorithmException if the algorithm used to hash the password is unavilable
398 | * @throws KeyManagementException if the server's SSL certificate couldn't be trusted
399 | * @throws AuthenticatorException if the server rejected the proferred session key.
400 | * @throws GeneralSecurityException if a cryptographic operation failed
401 | */
402 | public void downloadStatement() throws IOException, NoSuchAlgorithmException,
403 | KeyManagementException, AuthenticatorException, GeneralSecurityException {
404 | RestClient restClient = new RestClient(this, isHttpsEnabled());
405 |
406 | String statementHtml = restClient.getStatement(getRestServer(), getPort());
407 |
408 | CryptoTool cipher = new CryptoTool();
409 | byte[] iv = cipher.getIv();
410 | byte[] ciphertext = cipher.encrypt(statementHtml.getBytes(), mCryptoKey, iv);
411 |
412 | String timestamp = Long.toString(System.currentTimeMillis());
413 |
414 | FileOutputStream outputFileStream = openFileOutput(timestamp + ".statement", MODE_PRIVATE);
415 |
416 | outputFileStream.write(ciphertext);
417 | outputFileStream.flush();
418 | outputFileStream.close();
419 |
420 | outputFileStream = openFileOutput(timestamp + ".iv", MODE_PRIVATE);
421 |
422 | outputFileStream.write(iv);
423 | outputFileStream.flush();
424 | outputFileStream.close();
425 | }
426 |
427 | /**
428 | * Clears all statements from the download directory
429 | */
430 | public void clearStatements() {
431 | File downloadDir = getFilesDir();
432 | File[] directoryContents = downloadDir.listFiles();
433 | if (directoryContents != null) {
434 | for (File f : directoryContents) {
435 | f.delete();
436 | }
437 | }
438 | }
439 |
440 | /**
441 | * Transfers money between accounts
442 | * @param fromAccount The account to take funds from.
443 | * @param toAccount The account in which to deposit the funds.
444 | * @param amount The amount to transfer.
445 | * @return A status code representing the server's response
446 | * @throws IOException if network communication failed
447 | * @throws NoSuchAlgorithmException if the algorithm used to hash the password is unavilable
448 | * @throws KeyManagementException if the server's SSL certificate couldn't be trusted
449 | * @throws HttpException if the HTTP/S request failed
450 | */
451 | public int transferFunds(int fromAccount, int toAccount, double amount) throws IOException,
452 | NoSuchAlgorithmException, KeyManagementException, HttpException {
453 | RestClient restClient = new RestClient(this, isHttpsEnabled());
454 | int statusCode = restClient.transfer(getRestServer(), getPort(), fromAccount, toAccount,
455 | amount, sessionKey);
456 | return statusCode;
457 | }
458 |
459 | /**
460 | * Sets the details for the current authenticated session.
461 | * @param key The session key.
462 | * @param date A string representation of the date this key was issued.
463 | */
464 | public void setSession(String key, String date) {
465 | sessionKey = key;
466 | sessionCreateDate = date;
467 | }
468 |
469 | /**
470 | * Returns the current session key.
471 | * @return The current session key.
472 | */
473 | public String getSessionKey() {
474 | return sessionKey;
475 | }
476 |
477 | /**
478 | * Returns the creation date of the current session key.
479 | * @return A String representation of the creation date of the current session key.
480 | */
481 | public String getSessionCreateDate() {
482 | return sessionCreateDate;
483 | }
484 |
485 | /** Informs the application that an Activity is in the foreground */
486 | public void registerActivityForegrounded() {
487 | foregroundedActivities++;
488 | }
489 |
490 | /**
491 | * Informs the Application that an Activity has entered the background, and sets a check in 2
492 | * seconds to see if the entire application is now in the background
493 | */
494 | public void registerActivityBackgrounded() {
495 | foregroundedActivities--;
496 | timingHandler.removeCallbacks(checkBackgroundTask);
497 | timingHandler.postDelayed(checkBackgroundTask, 2000);
498 | }
499 |
500 | /**
501 | * Checks if all Activities are in the background (e.g. the entire app), and locks the app if
502 | * they are.
503 | */
504 | public void checkIfBackgrounded() {
505 | if (foregroundedActivities == 0) {
506 | lockApplication();
507 | }
508 | }
509 |
510 | /**
511 | * A simple helper class to perform a delayed check of whether the application is backgrounded
512 | */
513 | public Runnable checkBackgroundTask = new Runnable() {
514 |
515 | @Override
516 | public void run() {
517 | checkIfBackgrounded();
518 | }
519 |
520 | };
521 |
522 | }
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/BankingListActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import android.app.ListActivity;
8 | import android.content.Intent;
9 | import android.content.SharedPreferences.Editor;
10 | import android.os.Bundle;
11 | import android.view.Menu;
12 | import android.view.MenuInflater;
13 | import android.view.MenuItem;
14 | import android.view.View;
15 |
16 | /**
17 | * A superclass containing methods and variables that all Activities in this application will use
18 | * @author Ewan Sinclair
19 | */
20 | public class BankingListActivity extends ListActivity {
21 |
22 | /** Central data store, state, and operations */
23 | protected BankingApplication mThisApplication;
24 |
25 | @Override
26 | public void onCreate(Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | mThisApplication = (BankingApplication) getApplication();
29 | if (mThisApplication.isLocked()) {
30 | launchLoginScreen();
31 | }
32 | }
33 |
34 | @Override
35 | public boolean onOptionsItemSelected(MenuItem item) {
36 | switch (item.getItemId()) {
37 | case R.id.optionsmenu_reset:
38 | resetApplication();
39 | return true;
40 | case R.id.optionsmenu_preferences:
41 | launchPreferenceScreen();
42 | return true;
43 | default:
44 | return super.onOptionsItemSelected(item);
45 | }
46 | }
47 |
48 | /**
49 | * Checks whether the application is locked and makes this Activity invisible if so.
50 | */
51 | public void setAppropriateVisibility() {
52 | View v = findViewById(R.id.root_view);
53 | if (v != null) {
54 | if (mThisApplication.isLocked()) {
55 | v.setVisibility(View.GONE);
56 | } else {
57 | v.setVisibility(View.VISIBLE);
58 | }
59 | }
60 | }
61 |
62 | /**
63 | * Makes this Activity invisible.
64 | */
65 | public void setInvisible() {
66 | View v = findViewById(R.id.root_view);
67 | if (v != null) {
68 | v.setVisibility(View.GONE);
69 | }
70 |
71 | }
72 |
73 | @Override
74 | public boolean onCreateOptionsMenu(Menu menu) {
75 | super.onCreateOptionsMenu(menu);
76 | MenuInflater inflater = getMenuInflater();
77 | inflater.inflate(R.menu.options_menu, menu);
78 | return true;
79 | }
80 |
81 | @Override
82 | protected void onResume() {
83 | super.onResume();
84 | setAppropriateVisibility();
85 | mThisApplication.registerActivityForegrounded();
86 | if (mThisApplication.isLocked()) {
87 | Intent i = new Intent(this, LoginActivity.class);
88 | i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
89 | startActivity(i);
90 | }
91 | }
92 |
93 | @Override
94 | protected void onPause() {
95 | super.onPause();
96 | setInvisible();
97 | mThisApplication.registerActivityBackgrounded();
98 | }
99 |
100 | private void resetApplication() {
101 | BankingApplication ba = (BankingApplication) getApplication();
102 | ba.clearStatements();
103 | Editor e = ba.getSharedPrefs().edit();
104 | e.clear();
105 | e.commit();
106 | ba.lockApplication();
107 | launchLoginScreen();
108 | }
109 |
110 | private void launchPreferenceScreen() {
111 | Intent i = new Intent(this, EditPreferencesActivity.class);
112 | startActivity(i);
113 | }
114 |
115 | /** Launches the accounts screen, doing any necessary processing first */
116 | protected void launchLoginScreen() {
117 | Intent launchLogin = new Intent(this, LoginActivity.class);
118 | launchLogin.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
119 | startActivity(launchLogin);
120 | }
121 |
122 | /** Launches the accounts screen, doing any necessary processing first */
123 | protected void authenticate() {
124 | Intent launchLogin = new Intent(this, LoginActivity.class);
125 | launchLogin.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
126 | startActivity(launchLogin);
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/CryptoTool.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | * */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import java.security.InvalidAlgorithmParameterException;
8 | import java.security.InvalidKeyException;
9 | import java.security.NoSuchAlgorithmException;
10 | import java.security.SecureRandom;
11 | import java.security.spec.InvalidKeySpecException;
12 |
13 | import javax.crypto.BadPaddingException;
14 | import javax.crypto.Cipher;
15 | import javax.crypto.IllegalBlockSizeException;
16 | import javax.crypto.KeyGenerator;
17 | import javax.crypto.NoSuchPaddingException;
18 | import javax.crypto.SecretKey;
19 | import javax.crypto.SecretKeyFactory;
20 | import javax.crypto.spec.IvParameterSpec;
21 | import javax.crypto.spec.PBEKeySpec;
22 | import javax.crypto.spec.SecretKeySpec;
23 |
24 | import android.util.Base64;
25 |
26 | /**
27 | * Suite of tools for encryption and decryption.
28 | * @author Ewan Sinclair
29 | */
30 | public class CryptoTool {
31 |
32 | public static final String CRYPTO_SPEC = "AES/CBC/PKCS5Padding";
33 | public final static int KEY_BITS = 256;
34 | public final static int IV_BYTES = 16;
35 | public final static int SALT_BYTES = 32;
36 | public final static int NUM_ITERATIONS = 1000;
37 |
38 | /** Arbitrary key */
39 | public static final String DEFAULT_B64_KEY_STRING = "T0xXpDs1lT9q36aPehvDnaX3EgaFlM4JKIGYvqTqld0=";
40 |
41 | /** No-argument constuctor. Doesn't do anything. */
42 | public CryptoTool() {
43 |
44 | }
45 |
46 | /**
47 | * Encrypts the provided String and returns a base64 representation.
48 | * @param cleartext The text to be encrypted.
49 | * @param key The AES key to use for encrypting.
50 | * @param iv The initialisation vector to use for encrypting.
51 | * @return A Base64 encoded String representing the encrypted text.
52 | * @throws InvalidKeyException if the given key can't be used
53 | * @throws NoSuchPaddingException if our required padding type isn't available
54 | * @throws NoSuchAlgorithmException if the encryption algorithm isn't available.
55 | * @throws BadPaddingException if the padding was bad
56 | * @throws IllegalBlockSizeException if the cipher block is of an illegal size
57 | * @throws InvalidAlgorithmParameterException if the algorithm has had parameters set
58 | */
59 | public String encryptToB64String(String cleartext, byte[] key, byte[] iv)
60 | throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException,
61 | BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
62 | String ciphertext = "";
63 |
64 | SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
65 |
66 | Cipher cipher = Cipher.getInstance(CRYPTO_SPEC);
67 | IvParameterSpec ivSpec = new IvParameterSpec(iv);
68 | cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
69 |
70 | byte[] ciphertextBytes = cipher.doFinal(cleartext.getBytes());
71 | ciphertext = new String(Base64.encode(ciphertextBytes, Base64.DEFAULT));
72 |
73 | return ciphertext;
74 | }
75 |
76 | /**
77 | * Encrypts the provided byte array.
78 | * @param cleartext The text to be encrypted.
79 | * @param key The AES key to use for encrypting.
80 | * @param iv The initialisation vector to use for encrypting.
81 | * @return A byte array representing the encrypted text.
82 | * @throws InvalidKeyException if the given key can't be used
83 | * @throws NoSuchPaddingException if our required padding type isn't available
84 | * @throws NoSuchAlgorithmException if the encryption algorithm isn't available.
85 | * @throws BadPaddingException if the padding was bad
86 | * @throws IllegalBlockSizeException if the cipher block is of an illegal size
87 | * @throws InvalidAlgorithmParameterException if the algorithm has had parameters set
88 | */
89 | public byte[] encrypt(byte[] cleartext, byte[] key, byte[] iv) throws InvalidKeyException,
90 | NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException,
91 | IllegalBlockSizeException, InvalidAlgorithmParameterException {
92 | SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
93 |
94 | Cipher cipher = Cipher.getInstance(CRYPTO_SPEC);
95 | IvParameterSpec ivSpec = new IvParameterSpec(iv);
96 | cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
97 |
98 | byte[] ciphertextBytes = cipher.doFinal(cleartext);
99 |
100 | return ciphertextBytes;
101 | }
102 |
103 | /**
104 | * Decrypts the provided base64 String and returns plaintext.
105 | * @param ciphertextB64 The text to be decrypted.
106 | * @param key The AES key to use for decrypting.
107 | * @param iv The initialisation vector to use for decrypting.
108 | * @return A String representation of the cleartext.
109 | * @throws InvalidKeyException if the given key can't be used
110 | * @throws NoSuchPaddingException if our required padding type isn't available
111 | * @throws NoSuchAlgorithmException if the encryption algorithm isn't available.
112 | * @throws BadPaddingException if the padding was bad
113 | * @throws IllegalBlockSizeException if the cipher block is of an illegal size
114 | * @throws InvalidAlgorithmParameterException if the algorithm has had parameters set
115 | */
116 | public String decryptB64String(String ciphertextB64, byte[] key, byte[] iv)
117 | throws NoSuchPaddingException, NoSuchAlgorithmException,
118 | InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException,
119 | BadPaddingException {
120 | byte[] ciphertextBytes = Base64.decode(ciphertextB64, Base64.DEFAULT);
121 |
122 | SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
123 | Cipher cipher = Cipher.getInstance(CRYPTO_SPEC);
124 | IvParameterSpec ivSpec = new IvParameterSpec(iv);
125 | cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
126 |
127 | byte[] cleartextBytes = cipher.doFinal(ciphertextBytes);
128 | String cleartext = new String(cleartextBytes);
129 |
130 | return cleartext;
131 | }
132 |
133 | /**
134 | * Decrypts the provided byte array and returns plaintext.
135 | * @param ciphertextBytes The text to be decrypted.
136 | * @param key The AES key to use for decrypting.
137 | * @param iv The initialisation vector to use for decrypting.
138 | * @return A Base64 encoded String representing the encrypted text.
139 | * @throws InvalidKeyException if the given key can't be used
140 | * @throws NoSuchPaddingException if our required padding type isn't available
141 | * @throws NoSuchAlgorithmException if the encryption algorithm isn't available.
142 | * @throws BadPaddingException if the padding was bad
143 | * @throws IllegalBlockSizeException if the cipher block is of an illegal size
144 | * @throws InvalidAlgorithmParameterException if the algorithm has had parameters set
145 | */
146 | public String decryptBytes(byte[] ciphertextBytes, byte[] key, byte[] iv)
147 | throws NoSuchPaddingException, NoSuchAlgorithmException,
148 | InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException,
149 | BadPaddingException {
150 |
151 | SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
152 | Cipher cipher = Cipher.getInstance(CRYPTO_SPEC);
153 | IvParameterSpec ivSpec = new IvParameterSpec(iv);
154 | cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
155 |
156 | byte[] cleartextBytes = cipher.doFinal(ciphertextBytes);
157 | String cleartext = new String(cleartextBytes);
158 |
159 | return cleartext;
160 | }
161 |
162 | /**
163 | * Generates a random AES key.
164 | * @return A randomly generated key.
165 | * @throws NoSuchAlgorithmException if the AES algorithm is unavailable.
166 | */
167 | public SecretKey genRandomKey() throws NoSuchAlgorithmException {
168 | KeyGenerator keyGen = KeyGenerator.getInstance("AES");
169 | keyGen.init(KEY_BITS);
170 | SecretKey key = keyGen.generateKey();
171 | return key;
172 | }
173 |
174 | /**
175 | * Generates a random AES key, encoded as a Base64 String.
176 | * @return A randomly generated key, encoded as a Base64 String.
177 | * @throws NoSuchAlgorithmException if the AES algorithm is unavailable.
178 | */
179 | public String genRandomBase64KeyString() throws NoSuchAlgorithmException {
180 | SecretKey key = genRandomKey();
181 | byte[] keyBytes = key.getEncoded();
182 | String b64String = new String(Base64.encode(keyBytes, Base64.DEFAULT));
183 | return b64String;
184 | }
185 |
186 | /**
187 | * @param password The password to generate the key from.
188 | * @param salt Some bytes to salt the password with.
189 | * @param iterations How many iterations to use when generating the key.
190 | * @return A key produced from the given password and parameters.
191 | * @throws NoSuchAlgorithmException if the PBKDF2 algorithm was unavailable.
192 | * @throws InvalidKeySpecException if the keyspec produced from the given parameters is
193 | * rejected.
194 | */
195 | public SecretKey genKeyPwkdf2(String password, byte[] salt, int iterations)
196 | throws NoSuchAlgorithmException, InvalidKeySpecException {
197 | SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
198 | PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iterations, KEY_BITS);
199 | SecretKey generatedKey = f.generateSecret(keySpec);
200 | return generatedKey;
201 | }
202 |
203 | /**
204 | * Generates a random initialisation vector.
205 | * @return A random initialisation vector.
206 | */
207 | public byte[] getIv() {
208 | SecureRandom sr = new SecureRandom();
209 | byte[] iv = new byte[IV_BYTES];
210 | sr.nextBytes(iv);
211 | return iv;
212 | }
213 |
214 | /**
215 | * Generates a random salt.
216 | * @return A random salt.
217 | */
218 | public byte[] getSalt() {
219 | SecureRandom sr = new SecureRandom();
220 | byte[] salt = new byte[SALT_BYTES];
221 | sr.nextBytes(salt);
222 | return salt;
223 | }
224 |
225 | /**
226 | * Decodes Base64 encoded Strings.
227 | * @param b64String The base64 String to be decoded.
228 | * @return The decoded String.
229 | */
230 | public byte[] decodeB64(String b64String) {
231 | return Base64.decode(b64String, Base64.DEFAULT);
232 | }
233 | }
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/EditPreferencesActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import android.os.Bundle;
8 | import android.preference.PreferenceActivity;
9 |
10 | /**
11 | * Simple interface to allow the user to edit some preferences.
12 | * @author Ewan Sinclair
13 | */
14 | public class EditPreferencesActivity extends PreferenceActivity {
15 |
16 | private BankingApplication mThisApplication;
17 |
18 | @Override
19 | public void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | mThisApplication=(BankingApplication)getApplication();
22 | addPreferencesFromResource(R.xml.userpreferences);
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/HttpException.java:
--------------------------------------------------------------------------------
1 | package com.securitycompass.labs.falsesecuremobile;
2 |
3 |
4 | /**
5 | * Exception that contains an HTTP error code
6 | * @author Ewan Sinclair
7 | */
8 | public class HttpException extends Exception{
9 |
10 | private int mStatusCode;
11 |
12 | /**
13 | * Creates a new instance with a given status code
14 | * @param statusCode the status code returned by the HTTP transaction
15 | */
16 | public HttpException(int statusCode){
17 | mStatusCode=statusCode;
18 | }
19 |
20 | /**
21 | * Returns the status code held in this Exception
22 | * @return the status code held in this Exception.
23 | */
24 | public int getStatusCode() {
25 | return mStatusCode;
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/LoginActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import java.io.IOException;
8 | import java.io.UnsupportedEncodingException;
9 | import java.security.GeneralSecurityException;
10 | import java.security.KeyManagementException;
11 | import java.security.NoSuchAlgorithmException;
12 |
13 | import javax.net.ssl.SSLException;
14 |
15 | import org.json.JSONException;
16 |
17 | import android.content.Context;
18 | import android.content.Intent;
19 | import android.content.SharedPreferences;
20 | import android.os.Bundle;
21 | import android.util.Log;
22 | import android.view.View;
23 | import android.view.View.OnClickListener;
24 | import android.widget.Button;
25 | import android.widget.EditText;
26 | import android.widget.Toast;
27 |
28 | /**
29 | * Graphical class which allows the user to enter a username and password,
30 | * then perform a login with them.
31 | * @author Ewan Sinclair
32 | */
33 | public class LoginActivity extends BankingActivity {
34 |
35 | /** Useful for avoiding casts when a Context needs to be passed */
36 | private Context mCtx;
37 | /** The button that initiates the login */
38 | private Button mLoginButton;
39 | /** The text field to collect/hold the password. */
40 | private EditText mPasswordField;
41 | /** This application's preferences */
42 | private SharedPreferences mSharedPrefs;
43 |
44 | private static final String TAG = "LoginActivity";
45 |
46 | /** Called when the activity is first created. */
47 | @Override
48 | public void onCreate(Bundle savedInstanceState) {
49 | super.onCreate(savedInstanceState);
50 | mCtx = this;
51 |
52 | mSharedPrefs = mThisApplication.getSharedPrefs();
53 | checkFirstRun();
54 |
55 | setContentView(R.layout.loginactivity);
56 |
57 | mLoginButton = (Button) findViewById(R.id.loginscreen_login_button);
58 | mPasswordField = (EditText) findViewById(R.id.loginscreen_password);
59 |
60 | mLoginButton.setOnClickListener(new OnClickListener() {
61 |
62 | @Override
63 | public void onClick(View v) {
64 | performLogin();
65 | }
66 | });
67 |
68 | }
69 |
70 | /**
71 | * Checks if the application is running for the first time, and sends the user to the
72 | * appropriate setup if it is.
73 | */
74 | private void checkFirstRun() {
75 | if (mSharedPrefs.getBoolean(BankingApplication.PREF_FIRST_RUN, true)) {
76 | Intent i = new Intent(mCtx, SetServerCredentialsActivity.class);
77 | i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
78 | startActivity(i);
79 | }
80 | }
81 |
82 | /** Grabs the username and password and attempts to log in with them */
83 | private void performLogin() {
84 |
85 | String password = mPasswordField.getText().toString();
86 |
87 | int unlockStatus = RestClient.NO_OP;
88 |
89 | try {
90 | unlockStatus = mThisApplication.unlockApplication(password);
91 | } catch (UnsupportedEncodingException e) {
92 | Toast.makeText(mCtx, R.string.error_toast_hasherror, Toast.LENGTH_LONG).show();
93 | Log.e(TAG, e.toString());
94 | } catch (NoSuchAlgorithmException e) {
95 | if (e.toString().matches(".*SSL.*")) {
96 | Toast.makeText(mCtx, R.string.error_ssl_algorithm, Toast.LENGTH_LONG).show();
97 | } else {
98 | Toast.makeText(mCtx, R.string.error_toast_hasherror, Toast.LENGTH_LONG).show();
99 | }
100 | Log.e(TAG, e.toString());
101 | } catch (JSONException e) {
102 | Toast.makeText(mCtx, R.string.error_toast_json_problem, Toast.LENGTH_SHORT).show();
103 | Log.e(TAG, e.toString());
104 | } catch (KeyManagementException e) {
105 | Toast.makeText(mCtx, R.string.error_ssl_keymanagement, Toast.LENGTH_LONG).show();
106 | Log.e(TAG, e.toString());
107 | } catch (SSLException e) {
108 | Toast.makeText(mCtx, R.string.error_ssl_general, Toast.LENGTH_SHORT).show();
109 | Log.e(TAG, e.toString());
110 | } catch (HttpException e) {
111 | Toast.makeText(mCtx, getString(R.string.error_toast_http_error) + e.getStatusCode(), Toast.LENGTH_SHORT).show();
112 | Log.e(TAG, e.toString());
113 | } catch (IOException e) {
114 | Toast.makeText(mCtx, R.string.error_toast_rest_problem, Toast.LENGTH_SHORT).show();
115 | Log.e(TAG, e.toString());
116 | } catch (GeneralSecurityException e) {
117 | Toast.makeText(mCtx, "Crypto failure", Toast.LENGTH_SHORT).show();
118 | Log.e(TAG, e.toString());
119 | }
120 |
121 | if (unlockStatus == RestClient.NULL_ERROR) {
122 | Intent launchIntent = new Intent(mCtx, SummaryActivity.class);
123 | launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
124 | startActivity(launchIntent);
125 | } else {
126 | Toast.makeText(mCtx, R.string.toast_loginfailed, Toast.LENGTH_SHORT).show();
127 | }
128 | }
129 |
130 | }
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/RestClient.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import java.io.BufferedReader;
8 | import java.io.DataOutputStream;
9 | import java.io.IOException;
10 | import java.io.InputStream;
11 | import java.io.InputStreamReader;
12 | import java.net.HttpURLConnection;
13 | import java.net.URL;
14 | import java.net.URLEncoder;
15 | import java.security.KeyManagementException;
16 | import java.security.NoSuchAlgorithmException;
17 | import java.security.cert.CertificateException;
18 | import java.security.cert.X509Certificate;
19 | import java.util.ArrayList;
20 | import java.util.HashMap;
21 | import java.util.List;
22 | import java.util.Map;
23 |
24 | import javax.net.ssl.HostnameVerifier;
25 | import javax.net.ssl.HttpsURLConnection;
26 | import javax.net.ssl.SSLContext;
27 | import javax.net.ssl.TrustManager;
28 | import javax.net.ssl.X509TrustManager;
29 |
30 | import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
31 | import org.json.JSONArray;
32 | import org.json.JSONException;
33 | import org.json.JSONObject;
34 |
35 | import android.accounts.AuthenticatorException;
36 | import android.util.Log;
37 |
38 | /**
39 | * Handles HTTP/S communication with the banking service, abstracting it as a set of methods that
40 | * can be called by the rest of the application for any given banking function.
41 | * @author Ewan Sinclair
42 | */
43 | public class RestClient {
44 |
45 | private static final String TAG = "RestClient";
46 |
47 | /*
48 | * These codes are used to indicate the status of transactions that have finished, mainly
49 | * representing error codes returned by the server
50 | */
51 | /** REST operation didn't happen. */
52 | public static final int NO_OP = -2;
53 | /** No error ocurred, transaction completed. */
54 | public static final int NULL_ERROR = -1;
55 | /** The banking service rejected the given username and password. */
56 | public static final int ERROR_CREDENTIALS = 1;
57 | /** The banking service rejected the session key. */
58 | public static final int ERROR_SESSION_KEY = 2;
59 | /** The account referenced in the request doesn't exist. */
60 | public static final int ERROR_ACCOUNT_NOT_EXIST = 3;
61 | /** The balance is too low to perform the given transaction. */
62 | public static final int ERROR_BALANCE_TOO_LOW = 4;
63 | /** Operation was forbidden. */
64 | public static final int ERROR_FORBIDDEN = 5;
65 | /** Permission for the operation was denied. */
66 | public static final int ERROR_PERMISSION_DENIED = 6;
67 |
68 | private BankingApplication mAppState;
69 | private boolean mHttpsMode;
70 | private HostnameVerifier mHostnameVerifier;
71 |
72 | /**
73 | * Creates the RestClient and connects it to the state of the application. This allows for it to
74 | * modify the state based on what happens during requests.
75 | * @param appState The BankingApplication containing the app-wide state variables.
76 | * @param enableHttps Whether to use HTTPS.
77 | * @throws NoSuchAlgorithmException if the SSL encryption algorithm chosen is not available.
78 | * @throws KeyManagementException if lax SSL initialisation fails.
79 | */
80 | public RestClient(BankingApplication appState, boolean enableHttps)
81 | throws NoSuchAlgorithmException, KeyManagementException {
82 | this.mAppState = appState;
83 | mHttpsMode = enableHttps;
84 | setLaxSSL();
85 | }
86 |
87 | /**
88 | * Performs a simple HTTP GET and returns the result.
89 | * @param urlName API Service endpoint.
90 | * @return HttpContent from the url.
91 | * @throws IOException if the network connection failed
92 | */
93 | public String getHttpContent(String urlName) throws IOException {
94 | String line;
95 | String result;
96 | StringBuilder httpContent = new StringBuilder();
97 |
98 | URL url = new URL(urlName);
99 | HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
100 | int responseCode = httpConnection.getResponseCode();
101 | if (responseCode == HttpURLConnection.HTTP_OK) {
102 | InputStream inputStream = httpConnection.getInputStream();
103 | BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
104 | while ((line = br.readLine()) != null) {
105 | httpContent.append(line);
106 | }
107 |
108 | httpConnection.disconnect();
109 | }
110 | result = httpContent.toString();
111 | return result;
112 | }
113 |
114 | /**
115 | * Performs an HTTP POST with the given data.
116 | * @param urlString The URL to POST to.
117 | * @param variables key/value pairs for all parameters to be POSTed.
118 | * @return The data passed back from the server, as a String.
119 | * @throws IOException if the network connection failed.
120 | * @throws HttpException if the HTTP/S request failed
121 | */
122 | public String postHttpContent(String urlString, Map variables)
123 | throws IOException, HttpException {
124 | String response = "";
125 | URL url = new URL(urlString);
126 | HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
127 | httpConnection.setDoInput(true);
128 | httpConnection.setDoOutput(true);
129 | httpConnection.setUseCaches(false);
130 | httpConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
131 |
132 | // Assemble a String out of the parameters we're posting
133 | String postData = "";
134 | for (String key : variables.keySet()) {
135 | postData += "&" + key + "=" + variables.get(key);
136 | }
137 | postData = postData.substring(1);
138 |
139 | // Send the POST data
140 | DataOutputStream postOut = new DataOutputStream(httpConnection.getOutputStream());
141 | postOut.writeBytes(postData);
142 | postOut.flush();
143 | postOut.close();
144 |
145 | // Now get the response the server gives us
146 | int responseCode = httpConnection.getResponseCode();
147 | if (responseCode == HttpURLConnection.HTTP_OK) {
148 | String line;
149 | BufferedReader br = new BufferedReader(new InputStreamReader(httpConnection
150 | .getInputStream()));
151 | while ((line = br.readLine()) != null) {
152 | response += line;
153 | }
154 | } else {
155 | response = "";
156 | Log.e(TAG, "HTTP request failed on: " + urlString + " With error code: "
157 | + responseCode);
158 | throw new HttpException(responseCode);
159 | }
160 | return response;
161 | }
162 |
163 | /**
164 | * Performs a simple HTTPS GET and returns the result.
165 | * @param urlName API Service endpoint.
166 | * @return HttpContent from the url.
167 | * @throws IOException if the network connection failed.
168 | */
169 | public String getHttpsContent(String urlName) throws IOException {
170 | String line;
171 | String result;
172 | StringBuilder httpsContent = new StringBuilder();
173 |
174 | URL url = new URL(urlName);
175 | HttpsURLConnection httpsConnection = (HttpsURLConnection) url.openConnection();
176 | if (mHostnameVerifier != null) {
177 | httpsConnection.setHostnameVerifier(mHostnameVerifier);
178 | }
179 | int responseCode = httpsConnection.getResponseCode();
180 | if (responseCode == HttpsURLConnection.HTTP_OK) {
181 | InputStream inputStream = httpsConnection.getInputStream();
182 | BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
183 | while ((line = br.readLine()) != null) {
184 | httpsContent.append(line);
185 | }
186 |
187 | httpsConnection.disconnect();
188 | }
189 | result = httpsContent.toString();
190 | return result;
191 | }
192 |
193 | /**
194 | * Performs an HTTPS POST with the given data.
195 | * @param urlString The URL to POST to.
196 | * @param variables key/value pairs for all parameters to be POSTed.
197 | * @return The data passed back from the server, as a String.
198 | * @throws IOException if the network connection failed.
199 | * @throws HttpException if the HTTP transaction failed
200 | */
201 | public String postHttpsContent(String urlString, Map variables)
202 | throws IOException, HttpException {
203 | String response = "";
204 | URL url = new URL(urlString);
205 | HttpsURLConnection httpsConnection = (HttpsURLConnection) url.openConnection();
206 | if (mHostnameVerifier != null) {
207 | httpsConnection.setHostnameVerifier(mHostnameVerifier);
208 | }
209 | httpsConnection.setDoInput(true);
210 | httpsConnection.setDoOutput(true);
211 | httpsConnection.setUseCaches(false);
212 | httpsConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
213 |
214 | // Assemble a String out of the parameters we're posting
215 | String postData = "";
216 | for (String key : variables.keySet()) {
217 | postData += "&" + key + "=" + variables.get(key);
218 | }
219 | postData = postData.substring(1);
220 |
221 | // Send the POST data
222 | DataOutputStream postOut = new DataOutputStream(httpsConnection.getOutputStream());
223 | postOut.writeBytes(postData);
224 | postOut.flush();
225 | postOut.close();
226 |
227 | // Now get the response the server gives us
228 | int responseCode = httpsConnection.getResponseCode();
229 | if (responseCode == HttpsURLConnection.HTTP_OK) {
230 | String line;
231 | BufferedReader br = new BufferedReader(new InputStreamReader(httpsConnection
232 | .getInputStream()));
233 | while ((line = br.readLine()) != null) {
234 | response += line;
235 | }
236 | } else {
237 | response = "";
238 | Log.e(TAG, "HTTPs request failed on: " + urlString + " With error code: "
239 | + responseCode);
240 | throw new HttpException(responseCode);
241 | }
242 | return response;
243 | }
244 |
245 | /**
246 | * Logs into the REST service, generating a new session key.
247 | * @param server The address of the server to log into.
248 | * @param port The port number to use.
249 | * @param username Username to log in with.
250 | * @param password Password to log in with.
251 | * @return Whether the login was successful.
252 | * @throws JSONException if the server returned invalid JSON.
253 | * @throws IOException if the network connection failed.
254 | * @throws HttpException if the HTTP transaction failed.
255 | */
256 | public int performLogin(String server, String port, String username, String password)
257 | throws JSONException, IOException, HttpException {
258 | // First perform the RESTful operation
259 | String protocol = mHttpsMode ? "https://" : "http://";
260 | String url = protocol + server + ":" + port + "/login";
261 | Map parameters = new HashMap();
262 | parameters.put("username", username);
263 | parameters.put("password", password);
264 | String JsonResponse;
265 | if (mHttpsMode) {
266 | JsonResponse = postHttpsContent(url, parameters);
267 | } else {
268 | JsonResponse = postHttpContent(url, parameters);
269 | }
270 |
271 | // Now parse out the JSON response and act accordingly
272 | int errorCode = parseError(JsonResponse);
273 | if (errorCode == NULL_ERROR) {
274 | JSONObject jsonObject = new JSONObject(JsonResponse);
275 | String key = jsonObject.getString("key");
276 | String created = jsonObject.getString("created");
277 | mAppState.setSession(key, created);
278 | return errorCode;
279 | }
280 | return errorCode;
281 | }
282 |
283 | /**
284 | * Queries the server for a list of accounts, and returns a list of them.
285 | * @param server The address of the server to query.
286 | * @param port The port we will make our query on.
287 | * @return A list of all accounts the server told us about, and their details.
288 | * @throws JSONException if the server returned invalid JSON.
289 | * @throws IOException if the network connection failed.
290 | * @throws AuthenticatorException if the server rejected the proferred session key.
291 | */
292 | public List getAccounts(String server, String port) throws JSONException, IOException,
293 | AuthenticatorException {
294 | List accounts = new ArrayList();
295 | String protocol = mHttpsMode ? "https://" : "http://";
296 | String url = protocol + server + ":" + port + "/accounts" + "?session_key="
297 | + URLEncoder.encode(mAppState.getSessionKey());
298 | String result;
299 | if (mHttpsMode) {
300 | result = getHttpsContent(url);
301 | } else {
302 | result = getHttpContent(url);
303 | }
304 | int errorCode = parseError(result);
305 |
306 | if (errorCode == NULL_ERROR) {
307 |
308 | JSONArray resultArray = new JSONArray(result);
309 | for (int count = 0; count < resultArray.length(); count++) {
310 | JSONObject accountJson = resultArray.getJSONObject(count);
311 | double balance = accountJson.getDouble("balance");
312 | String accountType = accountJson.getString("type");
313 | int accountNumber = accountJson.getInt("account_number");
314 | accounts.add(new Account(accountNumber, accountType, balance));
315 | }
316 |
317 | } else if (errorCode == ERROR_SESSION_KEY) {
318 | throw new AuthenticatorException("Session key invalid");
319 | }
320 |
321 | return accounts;
322 | }
323 |
324 | /**
325 | * Queries the server for a list of accounts, and returns a list of them.
326 | * @param server The address of the server to query.
327 | * @param port The port we will make our query on.
328 | * @return A list of all accounts the server told us about, and their details.
329 | * @throws IOException if the network connection failed.
330 | * @throws AuthenticatorException if the server rejected the proferred session key.
331 | */
332 | public String getStatement(String server, String port) throws IOException,
333 | AuthenticatorException {
334 | String protocol = mHttpsMode ? "https://" : "http://";
335 | String url = (protocol + server + ":" + port + "/statement" + "?session_key=" + URLEncoder
336 | .encode(mAppState.getSessionKey()));
337 | String result;
338 | if (mHttpsMode) {
339 | result = getHttpsContent(url);
340 | } else {
341 | result = getHttpContent(url);
342 | }
343 |
344 | int errorCode = parseError(result);
345 |
346 | if (errorCode == NULL_ERROR) {
347 | // No need to do anything to the data
348 | } else if (errorCode == ERROR_SESSION_KEY) {
349 | throw new AuthenticatorException("Session key invalid");
350 | }
351 |
352 | return result;
353 | }
354 |
355 | /**
356 | * Transfers funds between the given accounts.
357 | * @param server The server to use.
358 | * @param port The port to use.
359 | * @param fromAccount The number of the account we're transferring from.
360 | * @param toAccount The number of the account we're transferring to.
361 | * @param amount The amount to be transferred
362 | * @param sessionKey The session key to use
363 | * @return A code indicating if the transaction succeeded, or why it failed.
364 | * @throws IOException if the network connection failed.
365 | * @throws HttpException if the HTTP transaction failed.
366 | */
367 | public int transfer(String server, String port, int fromAccount, int toAccount, double amount,
368 | String sessionKey) throws IOException, HttpException {
369 | Map variables = new HashMap();
370 | variables.put("from_account", Integer.toString(fromAccount));
371 | variables.put("to_account", Integer.toString(toAccount));
372 | variables.put("amount", Double.toString(amount));
373 | String response;
374 | if (mHttpsMode) {
375 | response = postHttpsContent("https://" + server + ":" + port + "/transfer"
376 | + "?session_key=" + URLEncoder.encode(sessionKey), variables);
377 | } else {
378 | response = postHttpContent("http://" + server + ":" + port + "/transfer"
379 | + "?session_key=" + URLEncoder.encode(sessionKey), variables);
380 | }
381 | int statusCode = parseError(response);
382 | return statusCode;
383 | }
384 |
385 | /**
386 | * Takes a JSON string and returns an int representing the error code in it.
387 | * @param json The string to check for JSON encapsulated error codes.
388 | * @return The error code the string represented, or a code for no error.
389 | */
390 | public int parseError(String json) {
391 | int errorCode;
392 | String errorString;
393 | if (json == null || json.equals("")) {
394 | return -1;
395 | }
396 |
397 | // First see if this string is a valid error message
398 | try {
399 | JSONObject jsonObject = new JSONObject(json);
400 | errorString = jsonObject.getString("error");
401 | } catch (JSONException e) {
402 | errorCode = NULL_ERROR;
403 | return errorCode;
404 | }
405 |
406 | // Since it is, encode it as an int and return it
407 | // String is of format "E[0-9]+", e.g. "E3"
408 | return Integer.parseInt(errorString.trim().substring(1));
409 | }
410 |
411 | /**
412 | * Sets the application to accept all SSL certificates.
413 | * @throws NoSuchAlgorithmException if the SSL encryption algorithm wasn't available.
414 | * @throws KeyManagementException if initialising ther SSLContext fails.
415 | */
416 | private void setLaxSSL() throws NoSuchAlgorithmException, KeyManagementException {
417 | TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
418 |
419 | @Override
420 | public X509Certificate[] getAcceptedIssuers() {
421 | return null;
422 | }
423 |
424 | @Override
425 | public void checkServerTrusted(X509Certificate[] chain, String authType)
426 | throws CertificateException {
427 |
428 | }
429 |
430 | @Override
431 | public void checkClientTrusted(X509Certificate[] chain, String authType)
432 | throws CertificateException {
433 |
434 | }
435 | } };
436 |
437 | SSLContext sslContext = SSLContext.getInstance("TLS");
438 | sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
439 |
440 | HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
441 |
442 | mHostnameVerifier = new AllowAllHostnameVerifier();
443 |
444 | }
445 |
446 | }
447 |
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/SetLocalPasswordActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import java.io.IOException;
8 | import java.io.UnsupportedEncodingException;
9 | import java.security.GeneralSecurityException;
10 | import java.security.KeyManagementException;
11 | import java.security.NoSuchAlgorithmException;
12 |
13 | import org.json.JSONException;
14 |
15 | import android.app.Activity;
16 | import android.content.Context;
17 | import android.content.Intent;
18 | import android.content.SharedPreferences.Editor;
19 | import android.os.Bundle;
20 | import android.util.Log;
21 | import android.view.View;
22 | import android.view.View.OnClickListener;
23 | import android.widget.Button;
24 | import android.widget.EditText;
25 | import android.widget.Toast;
26 |
27 | /**
28 | * This class prompts the user to set their local unlock password
29 | * @author Ewan Sinclair
30 | */
31 | public class SetLocalPasswordActivity extends Activity {
32 |
33 | private EditText mPasswordField;
34 | private EditText mConfirmPasswordField;
35 | private Button mDoneButton;
36 | private Context mCtx;
37 | private BankingApplication mThisApplication;
38 |
39 | private String restUser;
40 | private String restPass;
41 |
42 | private final static String TAG = "SetLocalPasswordActivity";
43 |
44 | /** Called when the Activity is first created */
45 | @Override
46 | public void onCreate(Bundle savedInstanceState) {
47 | super.onCreate(savedInstanceState);
48 | this.setContentView(R.layout.setlocalpasswordactivity);
49 |
50 | mCtx = this;
51 | mThisApplication = (BankingApplication) getApplication();
52 |
53 | restUser = getIntent().getStringExtra("restUser");
54 | restPass = getIntent().getStringExtra("restPass");
55 |
56 | mPasswordField = (EditText) findViewById(R.id.setupunlockactivity_password);
57 | mConfirmPasswordField = (EditText) findViewById(R.id.setupunlockactivity_repeat_password);
58 | mDoneButton = (Button) findViewById(R.id.setupunlockactivity_donebutton);
59 |
60 | mDoneButton.setOnClickListener(new OnClickListener() {
61 |
62 | @Override
63 | public void onClick(View v) {
64 | grabAndSetPassword();
65 | }
66 | });
67 |
68 | }
69 |
70 | /** Grabs the two passwords entered, and sets them as the local unlock password if they match */
71 | private void grabAndSetPassword() {
72 | String pass1 = mPasswordField.getText().toString();
73 | String pass2 = mConfirmPasswordField.getText().toString();
74 | if (!pass1.equals(pass2)) {
75 | Toast.makeText(mCtx, R.string.error_passwords_not_matching, Toast.LENGTH_SHORT).show();
76 | } else if (!isValidPassword(pass2)) {
77 | Toast.makeText(mCtx, R.string.error_weak_password, Toast.LENGTH_LONG).show();
78 | } else {
79 | try {
80 | mThisApplication.setCredentials(pass2, restUser, restPass);
81 | mThisApplication.unlockApplication(pass2);
82 | } catch (NoSuchAlgorithmException e) {
83 | Toast.makeText(mCtx, R.string.error_toast_hasherror, Toast.LENGTH_LONG).show();
84 | Log.e(TAG, e.toString());
85 | } catch (UnsupportedEncodingException e) {
86 | Toast.makeText(mCtx, R.string.error_toast_hasherror, Toast.LENGTH_LONG).show();
87 | Log.e(TAG, e.toString());
88 | } catch (JSONException e) {
89 | Toast.makeText(mCtx, R.string.error_toast_json_problem, Toast.LENGTH_SHORT).show();
90 | Log.e(TAG, e.toString());
91 | } catch (HttpException e) {
92 | Toast.makeText(mCtx, getString(R.string.error_toast_http_error) + e.getStatusCode(), Toast.LENGTH_SHORT).show();
93 | Log.e(TAG, e.toString());
94 | } catch (KeyManagementException e){
95 | Toast.makeText(mCtx, R.string.error_ssl_keymanagement, Toast.LENGTH_LONG).show();
96 | Log.e(TAG, e.toString());
97 | } catch (IOException e) {
98 | Toast.makeText(mCtx, R.string.error_toast_rest_problem, Toast.LENGTH_SHORT).show();
99 | Log.e(TAG, e.toString());
100 | } catch (GeneralSecurityException e) {
101 | Toast.makeText(mCtx, "Crypto failure", Toast.LENGTH_SHORT).show();
102 | Log.e(TAG, e.toString());
103 | }
104 |
105 | Toast.makeText(mCtx, R.string.initialsetup_success, Toast.LENGTH_SHORT).show();
106 | Intent i = new Intent(mCtx, SummaryActivity.class);
107 | i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
108 | startActivity(i);
109 | }
110 | }
111 |
112 | /**
113 | * Checks a String to see if it is a valid password.
114 | * @param password The password to check.
115 | * @return Whether the password was strong enough.
116 | */
117 | public boolean isValidPassword(String password) {
118 | boolean valid = (password.matches(".*[A-Z].*") && password.matches(".*[a-z].*")
119 | && password.matches(".*[0-9].*") && password.matches(".*[@#$%\\^&+=].*") && password
120 | .length() >= 6);
121 | return valid;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/SetServerCredentialsActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import java.io.IOException;
8 | import java.security.GeneralSecurityException;
9 | import java.security.KeyManagementException;
10 | import java.security.NoSuchAlgorithmException;
11 |
12 | import org.json.JSONException;
13 |
14 | import android.app.Activity;
15 | import android.content.Context;
16 | import android.content.Intent;
17 | import android.content.SharedPreferences.Editor;
18 | import android.os.Bundle;
19 | import android.util.Log;
20 | import android.view.View;
21 | import android.view.View.OnClickListener;
22 | import android.widget.Button;
23 | import android.widget.EditText;
24 | import android.widget.Toast;
25 |
26 | /**
27 | * One-time setup screen for the user to enter their credentials for the banking service.
28 | * @author Ewan Sinclair
29 | */
30 | public class SetServerCredentialsActivity extends Activity {
31 |
32 | private EditText mUserField;
33 | private EditText mPasswordField;
34 | private Button mDoneButton;
35 | private Context mCtx;
36 | private BankingApplication mThisApplication;
37 |
38 | private static final String TAG = "SetServerCredentialsActivity";
39 |
40 | /** Called when the Activity is first created */
41 | @Override
42 | public void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | this.setContentView(R.layout.setservercredentialsactivity);
45 |
46 | mCtx = this;
47 | mThisApplication = (BankingApplication) getApplication();
48 |
49 | mUserField = (EditText) findViewById(R.id.setservercredentialsactivity_username);
50 | mPasswordField = (EditText) findViewById(R.id.setservercredentialsactivity_password);
51 | mDoneButton = (Button) findViewById(R.id.setservercredentialsactivity_donebutton);
52 |
53 | mDoneButton.setOnClickListener(new OnClickListener() {
54 |
55 | @Override
56 | public void onClick(View v) {
57 | setServerCredentials();
58 | }
59 | });
60 |
61 | }
62 |
63 | private void setServerCredentials() {
64 | String username = mUserField.getText().toString();
65 | String password = mPasswordField.getText().toString();
66 |
67 | int statuscode = RestClient.NO_OP;
68 | try {
69 | statuscode = mThisApplication.performLogin(username, password);
70 | } catch (JSONException e) {
71 | Toast.makeText(mCtx, R.string.error_toast_json_problem, Toast.LENGTH_SHORT).show();
72 | Log.e(TAG, e.toString());
73 | return;
74 | } catch (KeyManagementException e) {
75 | Toast.makeText(mCtx, R.string.error_ssl_keymanagement, Toast.LENGTH_LONG).show();
76 | Log.e(TAG, e.toString());
77 | } catch (NoSuchAlgorithmException e) {
78 | Toast.makeText(mCtx, R.string.error_ssl_algorithm, Toast.LENGTH_LONG).show();
79 | Log.e(TAG, e.toString());
80 | } catch (HttpException e) {
81 | Toast.makeText(mCtx, getString(R.string.error_toast_http_error) + e.getStatusCode(),
82 | Toast.LENGTH_SHORT).show();
83 | Log.e(TAG, e.toString());
84 | } catch (IOException e) {
85 | Toast.makeText(mCtx, R.string.error_toast_rest_problem, Toast.LENGTH_SHORT).show();
86 | Log.e(TAG, e.toString());
87 | return;
88 | }
89 |
90 | if (statuscode == RestClient.NULL_ERROR) {
91 | Editor e = mThisApplication.getSharedPrefs().edit();
92 | e.putBoolean(BankingApplication.PREF_FIRST_RUN, false);
93 | e.commit();
94 | mThisApplication.lockApplication();
95 | Intent i = new Intent(mCtx, SetLocalPasswordActivity.class);
96 | i.putExtra("restUser", username);
97 | i.putExtra("restPass", password);
98 | i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
99 | startActivity(i);
100 | } else {
101 | Toast.makeText(mCtx, R.string.toast_loginfailed, Toast.LENGTH_SHORT).show();
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/StatementActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import java.io.File;
8 | import java.io.FileInputStream;
9 | import java.io.FileNotFoundException;
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.security.KeyManagementException;
13 | import java.security.NoSuchAlgorithmException;
14 | import java.util.ArrayList;
15 | import java.util.Collections;
16 | import java.util.List;
17 |
18 | import android.accounts.AuthenticatorException;
19 | import android.content.Context;
20 | import android.content.Intent;
21 | import android.os.Bundle;
22 | import android.text.format.DateUtils;
23 | import android.util.Base64;
24 | import android.util.Log;
25 | import android.view.View;
26 | import android.view.ViewGroup;
27 | import android.view.View.OnClickListener;
28 | import android.widget.AdapterView;
29 | import android.widget.ArrayAdapter;
30 | import android.widget.Button;
31 | import android.widget.TextView;
32 | import android.widget.Toast;
33 | import android.widget.AdapterView.OnItemClickListener;
34 |
35 | /**
36 | * Displays a list of statements, each of which can be clicked to view it.
37 | * @author Ewan Sinclair
38 | */
39 | public class StatementActivity extends BankingListActivity {
40 |
41 | /** Useful for avoiding casts when a Context needs to be passed */
42 | private Context mCtx;
43 | private BankingApplication mThisApplication;
44 | private File mStatementsDir;
45 | private File[] mStatements;
46 | private File[] mIvFiles;
47 | private Button mClearButton;
48 | private StatementAdapter mAdapter;
49 |
50 | private static final String TAG = "StatementActivity";
51 |
52 | /** Called when the activity is first created. */
53 | @Override
54 | public void onCreate(Bundle savedInstanceState) {
55 | super.onCreate(savedInstanceState);
56 | setContentView(R.layout.statementactivity);
57 | setAppropriateVisibility();
58 |
59 | mCtx = this;
60 | mThisApplication = (BankingApplication) getApplication();
61 |
62 | mClearButton = (Button) findViewById(R.id.statementscreen_clear_button);
63 |
64 | mStatementsDir = getFilesDir();
65 | downloadStatement();
66 | readStatementFiles();
67 |
68 | mAdapter = new StatementAdapter(mCtx, android.R.layout.simple_list_item_1, mStatements);
69 | setListAdapter(mAdapter);
70 |
71 | mClearButton.setOnClickListener(new OnClickListener() {
72 |
73 | @Override
74 | public void onClick(View v) {
75 | mThisApplication.clearStatements();
76 | refreshView();
77 | }
78 | });
79 |
80 | getListView().setOnItemClickListener(new OnItemClickListener(
81 |
82 | ) {
83 |
84 | @Override
85 | public void onItemClick(AdapterView> arg0, View arg1, int position, long id) {
86 | Intent intent = new Intent(mCtx, ViewStatementActivity.class);
87 | intent.putExtra("statement_html", decryptStatement(position));
88 | startActivity(intent);
89 | }
90 | });
91 |
92 | }
93 |
94 | /**
95 | * Clears out all downloaded files, downloads the latest one, and refreshes the list.
96 | */
97 | private void refreshView() {
98 | downloadStatement();
99 | readStatementFiles();
100 | mAdapter = new StatementAdapter(mCtx, android.R.layout.simple_list_item_1, mStatements);
101 | setListAdapter(mAdapter);
102 | }
103 |
104 | /** Downloads the most recent statement */
105 | private void downloadStatement() {
106 | try {
107 | mThisApplication.downloadStatement();
108 | } catch (KeyManagementException e) {
109 | Toast.makeText(mCtx, R.string.error_ssl_keymanagement, Toast.LENGTH_LONG).show();
110 | Log.e(TAG, e.toString());
111 | } catch (NoSuchAlgorithmException e) {
112 | Toast.makeText(mCtx, R.string.error_ssl_algorithm, Toast.LENGTH_LONG).show();
113 | Log.e(TAG, e.toString());
114 | } catch (AuthenticatorException e) {
115 | Log.e(TAG, e.toString());
116 | authenticate();
117 | } catch (IOException e) {
118 | Toast.makeText(mCtx, R.string.error_toast_rest_problem, Toast.LENGTH_SHORT).show();
119 | Log.e(TAG, e.toString());
120 | } catch (Exception e) {
121 | Toast.makeText(mCtx, "General Error", Toast.LENGTH_SHORT).show();
122 | Log.e(TAG, e.toString());
123 | }
124 | }
125 |
126 | /** Looks in the statements directory and loads what look like statement filenames into the list */
127 | private void readStatementFiles() {
128 | File[] allFiles = mStatementsDir.listFiles();
129 | List filteredStatements = new ArrayList();
130 | List filteredIvFiles = new ArrayList();
131 |
132 | for (File f : allFiles) {
133 | if (f.getName().matches("^[0-9]*\\.statement")) {
134 | String ivPath = f.getPath().replaceAll("\\.statement", "\\.iv");
135 | File fIv = new File(ivPath);
136 | if (fIv.exists()) {
137 | filteredStatements.add(f);
138 | filteredIvFiles.add(fIv);
139 | }
140 |
141 | }
142 |
143 | }
144 | // The list will now display with the most recent at the top
145 | Collections.reverse(filteredStatements);
146 | Collections.reverse(filteredIvFiles);
147 | mStatements = filteredStatements.toArray(new File[0]);
148 | mIvFiles = filteredIvFiles.toArray(new File[0]);
149 | }
150 |
151 | private String decryptStatement(int position) {
152 |
153 | CryptoTool cipher = new CryptoTool();
154 | byte[] key = mThisApplication.getCryptoKey();
155 |
156 | // Get the IV and statement file contents
157 | byte[] ciphertext=null, iv=null;
158 | try {
159 | ciphertext = readFile(mStatements[position]);
160 | iv = readFile(mIvFiles[position]);
161 | } catch (Exception e) {
162 | Toast.makeText(mCtx, "Error reading statement file", Toast.LENGTH_SHORT).show();
163 | Log.e(TAG, e.toString());
164 | }
165 |
166 | String cleartext="";
167 | try {
168 | cleartext=cipher.decryptBytes(ciphertext, key, iv);
169 | } catch (Exception e) {
170 | Toast.makeText(mCtx, "Error decrypting", Toast.LENGTH_SHORT).show();
171 | Log.e(TAG, e.toString());
172 | }
173 |
174 | return cleartext;
175 | }
176 |
177 | private byte[] readFile(File f) throws FileNotFoundException, IOException {
178 |
179 | InputStream is = new FileInputStream(f);
180 | byte[] bytes = new byte[(int) f.length()];
181 |
182 | // Read in the bytes
183 | int offset = 0;
184 | int numRead = 0;
185 | while (offset < bytes.length
186 | && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
187 | offset += numRead;
188 | }
189 |
190 | // Ensure all the bytes have been read in
191 | if (offset < bytes.length) {
192 | throw new IOException("Could not completely read file " + f.getName());
193 | }
194 |
195 | // Close the input stream and return bytes
196 | is.close();
197 | return bytes;
198 | }
199 |
200 | private class StatementAdapter extends ArrayAdapter {
201 |
202 | public StatementAdapter(Context context, int textViewResourceId, File[] objects) {
203 | super(context, textViewResourceId, objects);
204 | }
205 |
206 | @Override
207 | public View getView(int position, View convertView, ViewGroup parent) {
208 | TextView view;
209 | if (convertView != null) {
210 | view = (TextView) convertView;
211 | } else {
212 | view = (TextView) getLayoutInflater().inflate(android.R.layout.simple_list_item_1,
213 | null);
214 | }
215 |
216 | // Extract the creation time of the file from its filename
217 | String timeStampString = mStatements[position].getName().replaceAll("\\.statement", "");
218 | long timeStamp = Long.parseLong(timeStampString);
219 |
220 | int formatFlags = DateUtils.LENGTH_MEDIUM | DateUtils.FORMAT_24HOUR
221 | | DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_SHOW_DATE
222 | | DateUtils.FORMAT_SHOW_TIME;
223 | String formattedDateString = DateUtils.formatDateTime(mCtx, timeStamp, formatFlags);
224 |
225 | view.setText(formattedDateString);
226 | return view;
227 | }
228 |
229 | }
230 |
231 | }
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/SummaryActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.os.Bundle;
10 | import android.view.View;
11 | import android.widget.AdapterView;
12 | import android.widget.ArrayAdapter;
13 | import android.widget.ListAdapter;
14 | import android.widget.AdapterView.OnItemClickListener;
15 |
16 | /**
17 | * Menu screen which allows the user to select whichever banking activity they'd like to perform.
18 | * @author Ewan Sinclair
19 | */
20 | public class SummaryActivity extends BankingListActivity {
21 |
22 | private Context mCtx;
23 | private final String[] optionNames = { "Accounts", "Statement", "Transfer" };
24 |
25 | private static final int LAUNCH_ACCOUNTS = 0;
26 | private static final int LAUNCH_STATEMENT = 1;
27 | private static final int LAUNCH_TRANSFER = 2;
28 |
29 | /** Called when the activity is first created. */
30 | @Override
31 | public void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.summaryactivity);
34 | setAppropriateVisibility();
35 |
36 | mCtx = this;
37 |
38 | ListAdapter la = new ArrayAdapter(mCtx, android.R.layout.simple_list_item_1,
39 | optionNames);
40 | setListAdapter(la);
41 |
42 | getListView().setOnItemClickListener(new OnItemClickListener(
43 |
44 | ) {
45 |
46 | @Override
47 | public void onItemClick(AdapterView> arg0, View arg1, int position, long id) {
48 | launchSelectedScreen(position);
49 | }
50 | });
51 |
52 | }
53 |
54 | /**
55 | * Launches the screen with the given ID. This is necessary because when a ListView item is
56 | * clicked, it returns a position. We can't associate a listener directly to each list item.
57 | */
58 | private void launchSelectedScreen(int screenId) {
59 | if (screenId == LAUNCH_ACCOUNTS) {
60 | launchAccountsScreen();
61 | } else if (screenId == LAUNCH_STATEMENT) {
62 | launchStatementScreen();
63 | } else if (screenId == LAUNCH_TRANSFER) {
64 | launchTransferScreen();
65 | }
66 | }
67 |
68 | /** Launches the accounts screen, doing any necessary processing first */
69 | private void launchAccountsScreen() {
70 | Intent launchAccounts = new Intent(mCtx, AccountsActivity.class);
71 | startActivity(launchAccounts);
72 | }
73 |
74 | /** Launches the transfer screen, doing any necessary processing first */
75 | private void launchTransferScreen() {
76 | Intent launchTransfer = new Intent(mCtx, TransferActivity.class);
77 | startActivity(launchTransfer);
78 | }
79 |
80 | /** Launches the statement screen, doing any necessary processing first */
81 | private void launchStatementScreen() {
82 | Intent launchTransfer = new Intent(mCtx, StatementActivity.class);
83 | startActivity(launchTransfer);
84 | }
85 |
86 | }
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/TransferActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import java.io.IOException;
8 | import java.security.KeyManagementException;
9 | import java.security.NoSuchAlgorithmException;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | import org.json.JSONException;
14 |
15 | import android.accounts.AuthenticatorException;
16 | import android.content.Context;
17 | import android.content.Intent;
18 | import android.os.Bundle;
19 | import android.util.Log;
20 | import android.view.View;
21 | import android.view.ViewGroup;
22 | import android.view.View.OnClickListener;
23 | import android.widget.AdapterView;
24 | import android.widget.BaseAdapter;
25 | import android.widget.Button;
26 | import android.widget.EditText;
27 | import android.widget.Spinner;
28 | import android.widget.SpinnerAdapter;
29 | import android.widget.TextView;
30 | import android.widget.Toast;
31 | import android.widget.AdapterView.OnItemSelectedListener;
32 |
33 | /**
34 | * Screen which allows the user to make a transfer between two accounts.
35 | * @author Ewan Sinclair
36 | */
37 | public class TransferActivity extends BankingActivity {
38 |
39 | /** Useful for avoiding casts when a Context needs to be passed. */
40 | private Context mCtx;
41 | /** Central data store, state, and operations. */
42 | private BankingApplication mThisApplication;
43 | /** A list of the accounts for this user. */
44 | private List mAccounts;
45 |
46 | /** The dropdown selector for the 'from' account. */
47 | private Spinner mFromAccountSpinner;
48 | /** The dropdown selector for the 'to' account. */
49 | private Spinner mToAccountSpinner;
50 | /** The button to trigger the transfer. */
51 | private Button mTransferButton;
52 | /** The field in which the amount to transfer will be entered */
53 | private EditText mAmountField;
54 |
55 | /** The adapter we'll attach the 'from' spinner to. */
56 | private AccountListAdapter mFromAccountListAdapter;
57 | /** The adapter we'll attach the 'to' spinner to. */
58 | private AccountListAdapter mToAccountListAdapter;
59 | /** Holds the currently selected account to transfer funds from. */
60 | private Account mFromAccount;
61 | /** Holds the currently selected account to transfer funds to. */
62 | private Account mToAccount;
63 |
64 | private final static int TRANSFER_FROM = 1;
65 | private final static int TRANSFER_TO = 2;
66 | private static final String TAG = "TransferActivity";
67 |
68 | /** Called when the activity is first created. */
69 | @Override
70 | public void onCreate(Bundle savedInstanceState) {
71 | super.onCreate(savedInstanceState);
72 | setContentView(R.layout.transferactivity);
73 | setAppropriateVisibility();
74 |
75 | mCtx = this;
76 | mThisApplication = (BankingApplication) getApplication();
77 |
78 | mAccounts = new ArrayList();
79 |
80 | // Set up the dropdown account selectors
81 | mFromAccountSpinner = (Spinner) findViewById(R.id.transferscreen_fromaccount_spinner);
82 | mToAccountSpinner = (Spinner) findViewById(R.id.transferscreen_toaccount_spinner);
83 |
84 | mFromAccountListAdapter = new AccountListAdapter();
85 | mToAccountListAdapter = new AccountListAdapter();
86 |
87 | updateAccounts();
88 |
89 | mFromAccountSpinner.setAdapter(mFromAccountListAdapter);
90 | mToAccountSpinner.setAdapter(mToAccountListAdapter);
91 |
92 | mFromAccountSpinner.setOnItemSelectedListener(new AccountSelectionListener(TRANSFER_FROM));
93 | mToAccountSpinner.setOnItemSelectedListener(new AccountSelectionListener(TRANSFER_TO));
94 |
95 | // Set up the button to make it all go, and the amount field
96 | mTransferButton = (Button) findViewById(R.id.transferscreen_transfer_button);
97 |
98 | mTransferButton.setOnClickListener(new OnClickListener() {
99 |
100 | @Override
101 | public void onClick(View v) {
102 | performTransfer();
103 |
104 | }
105 | });
106 |
107 | mAmountField = (EditText) findViewById(R.id.transferscreen_enteramount_field);
108 |
109 | if (mAccounts.size() >= 2) {
110 | mToAccountSpinner.setSelection(1);
111 | }
112 | refreshDisplayInformation();
113 |
114 | }
115 |
116 | /** Updates the account information stored locally and refreshes the display */
117 | private void updateAccounts() {
118 | try {
119 | // We can't just replace this with a new list, as there may be references held to it.
120 | List tempAccounts = mThisApplication.getAccounts();
121 | mAccounts.clear();
122 | for (Account a : tempAccounts) {
123 | mAccounts.add(a);
124 | }
125 | } catch (JSONException e) {
126 | Toast.makeText(mCtx, R.string.error_toast_json_problem, Toast.LENGTH_SHORT).show();
127 | Log.e(TAG, e.toString());
128 | } catch (IOException e) {
129 | Toast.makeText(mCtx, R.string.error_toast_rest_problem, Toast.LENGTH_SHORT).show();
130 | Log.e(TAG, e.toString());
131 | } catch (AuthenticatorException e) {
132 | Log.e(TAG, e.toString());
133 | authenticate();
134 | } catch (KeyManagementException e) {
135 | Toast.makeText(mCtx, R.string.error_ssl_keymanagement, Toast.LENGTH_LONG).show();
136 | Log.e(TAG, e.toString());
137 | } catch (NoSuchAlgorithmException e) {
138 | Toast.makeText(mCtx, R.string.error_ssl_algorithm, Toast.LENGTH_LONG).show();
139 | Log.e(TAG, e.toString());
140 | }
141 |
142 | // If the account list failed on retrieval, use an empty list
143 | if (!mThisApplication.isLocked()) {
144 | refreshDisplayInformation();
145 | } else {
146 | mAccounts.clear();
147 | }
148 | }
149 |
150 | /**
151 | * Pulls the currently entered information from the UI and performs a transfer using the set
152 | * values
153 | */
154 | private void performTransfer() {
155 | // TODO: Check status code and act accordingly if an error is present.
156 | if (mFromAccount == mToAccount) {
157 | Toast.makeText(mCtx, R.string.error_transfer_same_account, Toast.LENGTH_SHORT).show();
158 | } else {
159 | String amountText = mAmountField.getText().toString();
160 | double amount = 0;
161 | try {
162 | amount = Double.parseDouble(amountText);
163 | } catch (NumberFormatException e) {
164 | Toast.makeText(mCtx, R.string.error_transfer_invalid_amount, Toast.LENGTH_SHORT)
165 | .show();
166 | return;
167 | }
168 |
169 | if (amount > 0) {
170 | try {
171 | int responseCode = mThisApplication.transferFunds(mFromAccount
172 | .getAccountNumber(), mToAccount.getAccountNumber(), amount);
173 | Log.i(TAG, "Response code for transfer: " + responseCode);
174 | } catch (KeyManagementException e) {
175 | Toast.makeText(mCtx, R.string.error_ssl_keymanagement, Toast.LENGTH_LONG)
176 | .show();
177 | Log.e(TAG, e.toString());
178 | } catch (NoSuchAlgorithmException e) {
179 | Toast.makeText(mCtx, R.string.error_ssl_algorithm, Toast.LENGTH_LONG).show();
180 | Log.e(TAG, e.toString());
181 | } catch (HttpException e) {
182 | Toast.makeText(mCtx, getString(R.string.error_toast_http_error) + e.getStatusCode(),
183 | Toast.LENGTH_SHORT).show();
184 | Log.e(TAG, e.toString());
185 | } catch (IOException e) {
186 | Toast.makeText(mCtx, R.string.error_toast_rest_problem, Toast.LENGTH_SHORT)
187 | .show();
188 | Log.e(TAG, e.toString());
189 | }
190 |
191 | updateAccounts();
192 | Toast.makeText(mCtx, R.string.transferscreen_success, Toast.LENGTH_SHORT).show();
193 | launchSummaryScreen();
194 | } else {
195 | Toast.makeText(mCtx, R.string.error_transfer_invalid_amount, Toast.LENGTH_SHORT).show();
196 | }
197 |
198 | }
199 | }
200 |
201 | /** Updates the display to reflect the currently held account information. */
202 | private void refreshDisplayInformation() {
203 | /*
204 | * Simply calling notifyDataSetChanged() on the adapters here doesn't work, as the
205 | * mFromAccount reference remains set to the same as it was before the transaction, E.G. the
206 | * same type and account number, but with the earlier balance. Clearly the Spinner's
207 | * OnItemSelected() method isn't triggered when calling notifyDataSetChanged().
208 | */
209 |
210 | int fromPos = mFromAccountSpinner.getSelectedItemPosition();
211 | int toPos = mToAccountSpinner.getSelectedItemPosition();
212 |
213 | mFromAccountListAdapter = new AccountListAdapter();
214 | mToAccountListAdapter = new AccountListAdapter();
215 |
216 | mFromAccountSpinner.setAdapter(mFromAccountListAdapter);
217 | mToAccountSpinner.setAdapter(mToAccountListAdapter);
218 |
219 | mFromAccountSpinner.setSelection(fromPos);
220 | mToAccountSpinner.setSelection(toPos);
221 |
222 | }
223 |
224 | private void launchSummaryScreen(){
225 | Intent i=new Intent(this, SummaryActivity.class);
226 | i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
227 | startActivity(i);
228 | }
229 |
230 | /**
231 | * Returns a version of the given string with the first letter in uppercase.
232 | * @param input The String to capitalise.
233 | * @return The capitalised String.
234 | */
235 | private String capitalise(String input) {
236 | String result = input.substring(0, 1).toUpperCase() + input.substring(1);
237 | return result;
238 | }
239 |
240 | private class AccountSelectionListener implements OnItemSelectedListener {
241 |
242 | private int transferDirection;
243 |
244 | public AccountSelectionListener(int direction) {
245 | super();
246 | transferDirection = direction;
247 | }
248 |
249 | @Override
250 | public void onItemSelected(AdapterView> parent, View selectedView, int position, long id)
251 | throws IndexOutOfBoundsException {
252 | if (transferDirection == TRANSFER_FROM) {
253 | mFromAccount = mAccounts.get(position);
254 | } else if (transferDirection == TRANSFER_TO) {
255 | mToAccount = mAccounts.get(position);
256 | } else
257 | throw new IndexOutOfBoundsException(
258 | "From/To indicator int out of bounds in AccountSelectionListener");
259 | }
260 |
261 | @Override
262 | public void onNothingSelected(AdapterView> arg0) {
263 | // Nothing to do here
264 | }
265 |
266 | }
267 |
268 | /** Helper class to present a List to a Spinner for selecting one */
269 | private class AccountListAdapter extends BaseAdapter implements SpinnerAdapter {
270 |
271 | @Override
272 | public int getCount() {
273 | return mAccounts.size();
274 | }
275 |
276 | @Override
277 | public Object getItem(int position) {
278 | return mAccounts.get(position);
279 | }
280 |
281 | @Override
282 | public long getItemId(int position) {
283 | return position;
284 | }
285 |
286 | @Override
287 | public View getView(int position, View convertView, ViewGroup parent) {
288 | TextView view;
289 | if (convertView == null) {
290 | view = (TextView) getLayoutInflater().inflate(
291 | (android.R.layout.simple_spinner_item), null);
292 | } else {
293 | view = (TextView) convertView;
294 | }
295 | String accountNumString = Integer.toString(mAccounts.get(position).getAccountNumber());
296 | view.setText(capitalise(mAccounts.get(position).getAccountType()) + " ("
297 | + accountNumString.substring(accountNumString.length() - 4) + "): $"
298 | + mAccounts.get(position).getBalance());
299 | return view;
300 | }
301 |
302 | @Override
303 | public View getDropDownView(int position, View convertView, ViewGroup parent) {
304 | TextView view;
305 | if (convertView == null) {
306 | view = (TextView) getLayoutInflater().inflate(
307 | (android.R.layout.simple_spinner_dropdown_item), null);
308 | } else {
309 | view = (TextView) convertView;
310 | }
311 | String accountNumString = Integer.toString(mAccounts.get(position).getAccountNumber());
312 | view.setText(capitalise(mAccounts.get(position).getAccountType()) + " ("
313 | + accountNumString.substring(accountNumString.length() - 4) + "): $"
314 | + mAccounts.get(position).getBalance());
315 | return view;
316 | }
317 |
318 | }
319 |
320 | }
--------------------------------------------------------------------------------
/src/com/securitycompass/labs/falsesecuremobile/ViewStatementActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Security Compass
3 | */
4 |
5 | package com.securitycompass.labs.falsesecuremobile;
6 |
7 | import android.content.Intent;
8 | import android.os.Bundle;
9 | import android.webkit.WebView;
10 | import android.widget.Toast;
11 |
12 | /**
13 | * Screen which displays a statement file.
14 | * @author Ewan Sinclair
15 | */
16 | public class ViewStatementActivity extends BankingActivity {
17 |
18 | WebView mStatementDisplay;
19 |
20 | @Override
21 | public void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | setContentView(R.layout.viewstatementactivity);
24 | setAppropriateVisibility();
25 |
26 | mStatementDisplay = (WebView) findViewById(R.id.viewstatementscreen_webview);
27 |
28 | Intent i = getIntent();
29 | if (i.hasExtra("statement_filename")) {
30 | String filename = i.getStringExtra("statement_filename");
31 | mStatementDisplay.loadUrl("file://" + filename);
32 | } else if (i.hasExtra("statement_html")){
33 | String html = i.getStringExtra("statement_html");
34 | mStatementDisplay.loadData(html, "text/html", "utf-8");
35 | } else {
36 | Toast.makeText(this, R.string.error_invalid_statment, Toast.LENGTH_SHORT).show();
37 | }
38 |
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------