├── .editorconfig
├── .gitattributes
├── .gitignore
├── Images
├── change.png
├── charsets.png
├── entry.png
├── group.png
├── menu.png
├── profile.png
└── rule.png
├── LICENSE
├── README.md
├── RuleBuilder.sln
├── RuleBuilder
├── App.config
├── Forms
│ ├── ChangePassword.xaml
│ ├── ChangePassword.xaml.cs
│ ├── EditRule.xaml
│ ├── EditRule.xaml.cs
│ ├── EditRuleModel.cs
│ └── EntryFormMod.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Images.Designer.cs
│ ├── Images.resx
│ ├── ImagesHigh.Designer.cs
│ ├── ImagesHigh.resx
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── Resources
│ ├── IconsHigh
│ │ ├── Dice.png
│ │ ├── NewPassword.png
│ │ └── Organizer.png
│ ├── IconsLow
│ │ ├── Dice.png
│ │ ├── NewPassword.png
│ │ └── Organizer.png
│ ├── Refresh.png
│ ├── Strings.xaml
│ └── Styles.xaml
├── Rule
│ ├── CharacterClass.cs
│ ├── Component.cs
│ ├── Configuration.cs
│ ├── Expiration.cs
│ ├── IPasswordGenerator.cs
│ ├── PasswordProfile.cs
│ ├── PasswordRule.cs
│ ├── Random.cs
│ ├── RuleProperty.cs
│ └── Serialization
│ │ ├── CharacterClassContract.cs
│ │ ├── ComponentContract.cs
│ │ ├── ConfigurationContract.cs
│ │ ├── Entry.cs
│ │ ├── ExpirationContract.cs
│ │ ├── ProfileContract.cs
│ │ └── RuleContract.cs
├── RuleBuilder.csproj
├── RuleBuilderExt.cs
├── Util
│ ├── ComponentTemplateSelector.cs
│ ├── Entropy.cs
│ ├── Hotkey.cs
│ ├── IntegerRangeRule.cs
│ ├── NativeMethods.cs
│ └── ScaledResourceManager.cs
└── version.txt
└── RuleBuilderTests
├── Properties
└── AssemblyInfo.cs
├── Rule
├── CharacterClassTests.cs
├── ComponentTests.cs
├── ExpirationTests.cs
├── PasswordProfileTests.cs
├── PasswordRuleTests.cs
├── RandomTests.cs
└── Serialization
│ ├── CharacterClassContractTests.cs
│ ├── ComponentContractTests.cs
│ ├── ConfigurationContractTests.cs
│ ├── EntryTests.cs
│ ├── ProfileContractTests.cs
│ └── RuleContractTests.cs
├── RuleBuilderTests.csproj
├── Util
└── EntropyTests.cs
└── packages.config
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories
2 | root = true
3 |
4 | # C# files
5 | [*.cs]
6 |
7 | #### Core EditorConfig Options ####
8 |
9 | # Indentation and spacing
10 | indent_size = 4
11 | indent_style = tab
12 | tab_width = 4
13 |
14 | # New line preferences
15 | end_of_line = crlf
16 | insert_final_newline = false
17 |
18 | #### .NET Coding Conventions ####
19 |
20 | # Organize usings
21 | dotnet_separate_import_directive_groups = false
22 | dotnet_sort_system_directives_first = true
23 |
24 | # this. and Me. preferences
25 | dotnet_style_qualification_for_event = true:silent
26 | dotnet_style_qualification_for_field = true:silent
27 | dotnet_style_qualification_for_method = true:silent
28 | dotnet_style_qualification_for_property = true:silent
29 |
30 | # Language keywords vs BCL types preferences
31 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
32 | dotnet_style_predefined_type_for_member_access = true:suggestion
33 |
34 | # Parentheses preferences
35 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
36 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
37 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
38 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
39 |
40 | # Modifier preferences
41 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
42 |
43 | # Expression-level preferences
44 | csharp_style_deconstructed_variable_declaration = true:suggestion
45 | csharp_style_inlined_variable_declaration = true:silent
46 | csharp_style_throw_expression = true:silent
47 | dotnet_style_coalesce_expression = true:silent
48 | dotnet_style_collection_initializer = true:silent
49 | dotnet_style_explicit_tuple_names = true:suggestion
50 | dotnet_style_null_propagation = true:silent
51 | dotnet_style_object_initializer = true:silent
52 | dotnet_style_prefer_auto_properties = true:silent
53 | dotnet_style_prefer_compound_assignment = true:suggestion
54 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent
55 | dotnet_style_prefer_conditional_expression_over_return = true:silent
56 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
57 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
58 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
59 |
60 | # Field preferences
61 | dotnet_style_readonly_field = true:suggestion
62 |
63 | # Parameter preferences
64 | dotnet_code_quality_unused_parameters = all:suggestion
65 |
66 | #### C# Coding Conventions ####
67 |
68 | # var preferences
69 | csharp_style_var_elsewhere = false:warning
70 | csharp_style_var_for_built_in_types = false:silent
71 | csharp_style_var_when_type_is_apparent = false:silent
72 |
73 | # Expression-bodied members
74 | csharp_style_expression_bodied_accessors = when_on_single_line:silent
75 | csharp_style_expression_bodied_constructors = false:silent
76 | csharp_style_expression_bodied_indexers = when_on_single_line:silent
77 | csharp_style_expression_bodied_lambdas = true:silent
78 | csharp_style_expression_bodied_local_functions = false:silent
79 | csharp_style_expression_bodied_methods = when_on_single_line:silent
80 | csharp_style_expression_bodied_operators = when_on_single_line:silent
81 | csharp_style_expression_bodied_properties = when_on_single_line:silent
82 |
83 | # Pattern matching preferences
84 | csharp_style_pattern_matching_over_as_with_null_check = true:silent
85 | csharp_style_pattern_matching_over_is_with_cast_check = true:silent
86 |
87 | # Null-checking preferences
88 | csharp_style_conditional_delegate_call = true:silent
89 |
90 | # Modifier preferences
91 | csharp_prefer_static_local_function = true:suggestion
92 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
93 |
94 | # Code-block preferences
95 | csharp_prefer_braces = true:warning
96 | csharp_prefer_simple_using_statement = true:suggestion
97 |
98 | # Expression-level preferences
99 | csharp_prefer_simple_default_expression = true:suggestion
100 | csharp_style_pattern_local_over_anonymous_function = true:suggestion
101 | csharp_style_prefer_index_operator = true:suggestion
102 | csharp_style_prefer_range_operator = true:suggestion
103 | csharp_style_unused_value_assignment_preference = discard_variable:silent
104 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent
105 |
106 | # 'using' directive preferences
107 | csharp_using_directive_placement = outside_namespace:silent
108 |
109 | #### C# Formatting Rules ####
110 |
111 | # New line preferences
112 | csharp_new_line_before_catch = false
113 | csharp_new_line_before_else = false
114 | csharp_new_line_before_finally = false
115 | csharp_new_line_before_members_in_anonymous_types = true
116 | csharp_new_line_before_members_in_object_initializers = true
117 | csharp_new_line_before_open_brace = none
118 | csharp_new_line_between_query_expression_clauses = true
119 |
120 | # Indentation preferences
121 | csharp_indent_block_contents = true
122 | csharp_indent_braces = false
123 | csharp_indent_case_contents = true
124 | csharp_indent_case_contents_when_block = true
125 | csharp_indent_labels = one_less_than_current
126 | csharp_indent_switch_labels = true
127 |
128 | # Space preferences
129 | csharp_space_after_cast = false
130 | csharp_space_after_colon_in_inheritance_clause = true
131 | csharp_space_after_comma = true
132 | csharp_space_after_dot = false
133 | csharp_space_after_keywords_in_control_flow_statements = true
134 | csharp_space_after_semicolon_in_for_statement = true
135 | csharp_space_around_binary_operators = before_and_after
136 | csharp_space_around_declaration_statements = false
137 | csharp_space_before_colon_in_inheritance_clause = true
138 | csharp_space_before_comma = false
139 | csharp_space_before_dot = false
140 | csharp_space_before_open_square_brackets = false
141 | csharp_space_before_semicolon_in_for_statement = false
142 | csharp_space_between_empty_square_brackets = false
143 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
144 | csharp_space_between_method_call_name_and_opening_parenthesis = false
145 | csharp_space_between_method_call_parameter_list_parentheses = false
146 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
147 | csharp_space_between_method_declaration_name_and_open_parenthesis = false
148 | csharp_space_between_method_declaration_parameter_list_parentheses = false
149 | csharp_space_between_parentheses = false
150 | csharp_space_between_square_brackets = false
151 |
152 | # Wrapping preferences
153 | csharp_preserve_single_line_blocks = true
154 | csharp_preserve_single_line_statements = false
155 |
156 | #### Naming styles ####
157 |
158 | # Naming rules
159 |
160 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
161 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
162 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
163 |
164 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
165 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
166 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
167 |
168 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
169 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
170 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
171 |
172 | # Symbol specifications
173 |
174 | dotnet_naming_symbols.interface.applicable_kinds = interface
175 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal
176 | dotnet_naming_symbols.interface.required_modifiers =
177 |
178 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
179 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal
180 | dotnet_naming_symbols.types.required_modifiers =
181 |
182 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
183 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal
184 | dotnet_naming_symbols.non_field_members.required_modifiers =
185 |
186 | # Naming styles
187 |
188 | dotnet_naming_style.pascal_case.required_prefix =
189 | dotnet_naming_style.pascal_case.required_suffix =
190 | dotnet_naming_style.pascal_case.word_separator =
191 | dotnet_naming_style.pascal_case.capitalization = pascal_case
192 |
193 | dotnet_naming_style.begins_with_i.required_prefix = I
194 | dotnet_naming_style.begins_with_i.required_suffix =
195 | dotnet_naming_style.begins_with_i.word_separator =
196 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
197 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 |
59 | # StyleCop
60 | StyleCopReport.xml
61 |
62 | # Files built by Visual Studio
63 | *_i.c
64 | *_p.c
65 | *_h.h
66 | *.ilk
67 | *.meta
68 | *.obj
69 | *.iobj
70 | *.pch
71 | *.pdb
72 | *.ipdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *_wpftmp.csproj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !?*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 |
235 | # RIA/Silverlight projects
236 | Generated_Code/
237 |
238 | # Backup & report files from converting an old project file
239 | # to a newer Visual Studio version. Backup files are not needed,
240 | # because we have git ;-)
241 | _UpgradeReport_Files/
242 | Backup*/
243 | UpgradeLog*.XML
244 | UpgradeLog*.htm
245 | ServiceFabricBackup/
246 | *.rptproj.bak
247 |
248 | # SQL Server files
249 | *.mdf
250 | *.ldf
251 | *.ndf
252 |
253 | # Business Intelligence projects
254 | *.rdl.data
255 | *.bim.layout
256 | *.bim_*.settings
257 | *.rptproj.rsuser
258 | *- Backup*.rdl
259 |
260 | # Microsoft Fakes
261 | FakesAssemblies/
262 |
263 | # GhostDoc plugin setting file
264 | *.GhostDoc.xml
265 |
266 | # Node.js Tools for Visual Studio
267 | .ntvs_analysis.dat
268 | node_modules/
269 |
270 | # Visual Studio 6 build log
271 | *.plg
272 |
273 | # Visual Studio 6 workspace options file
274 | *.opt
275 |
276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
277 | *.vbw
278 |
279 | # Visual Studio LightSwitch build output
280 | **/*.HTMLClient/GeneratedArtifacts
281 | **/*.DesktopClient/GeneratedArtifacts
282 | **/*.DesktopClient/ModelManifest.xml
283 | **/*.Server/GeneratedArtifacts
284 | **/*.Server/ModelManifest.xml
285 | _Pvt_Extensions
286 |
287 | # Paket dependency manager
288 | .paket/paket.exe
289 | paket-files/
290 |
291 | # FAKE - F# Make
292 | .fake/
293 |
294 | # JetBrains Rider
295 | .idea/
296 | *.sln.iml
297 |
298 | # CodeRush personal settings
299 | .cr/personal
300 |
301 | # Python Tools for Visual Studio (PTVS)
302 | __pycache__/
303 | *.pyc
304 |
305 | # Cake - Uncomment if you are using it
306 | # tools/**
307 | # !tools/packages.config
308 |
309 | # Tabs Studio
310 | *.tss
311 |
312 | # Telerik's JustMock configuration file
313 | *.jmconfig
314 |
315 | # BizTalk build output
316 | *.btp.cs
317 | *.btm.cs
318 | *.odx.cs
319 | *.xsd.cs
320 |
321 | # OpenCover UI analysis results
322 | OpenCover/
323 |
324 | # Azure Stream Analytics local run output
325 | ASALocalRun/
326 |
327 | # MSBuild Binary and Structured Log
328 | *.binlog
329 |
330 | # NVidia Nsight GPU debugger configuration file
331 | *.nvuser
332 |
333 | # MFractors (Xamarin productivity tool) working folder
334 | .mfractor/
335 |
336 | # Local History for Visual Studio
337 | .localhistory/
338 |
339 | # BeatPulse healthcheck temp database
340 | healthchecksdb
--------------------------------------------------------------------------------
/Images/change.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/Images/change.png
--------------------------------------------------------------------------------
/Images/charsets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/Images/charsets.png
--------------------------------------------------------------------------------
/Images/entry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/Images/entry.png
--------------------------------------------------------------------------------
/Images/group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/Images/group.png
--------------------------------------------------------------------------------
/Images/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/Images/menu.png
--------------------------------------------------------------------------------
/Images/profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/Images/profile.png
--------------------------------------------------------------------------------
/Images/rule.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/Images/rule.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Ira Hanson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # KeePass Rule Builder
2 | This is a plugin for the [KeePass 2](https://keepass.info/) password manager. With this plugin, you can use the KeePass database to keep track of all of the different requirements that websites have for account passwords, and easily generate new passwords according to those requirements.
3 |
4 | The strongest passwords are randomly generated. However, some services unwisely place constraints on the passwords that you can use. A website may state that a password must be less than a certain length, or that it must contain certain types of characters, or that it must *not* contain certain other characters. To make matters worse, every website has different requirements, so changing passwords means reconfiguring your password generator every time.
5 |
6 | The [password generator](https://keepass.info/help/base/pwgenerator.html) in KeePass helps with these challenges, but it can still be difficult to configure it to exactly meet the requirements of each service while keeping passwords as strong as possible.
7 |
8 | The purpose of this plugin is to make it easy to tell KeePass how to generate a password for each service and to streamline the process of changing passwords.
9 |
10 | ## Installation
11 | To install this plugin, download the latest version from the [Releases](https://github.com/ihanson/KeePass-Rule-Builder/releases) page and copy it into your KeePass installation’s Plugins directory. Do not rename the DLL file. See the [KeePass documentation](https://keepass.info/help/v2/plugins.html) for more help.
12 |
13 | This plugin is also available as a [Chocolatey package](https://community.chocolatey.org/packages/keepass-plugin-rulebuilder). I am not the maintainer of this package.
14 |
15 | ## How to use
16 | ### Changing a password
17 | To change a password for an entry in KeePass, right-click the password entry and click **Generate New Password**.
18 |
19 |
20 |
21 | The Change Password window will open. This window shows the current password as well as a new randomly generated password. Copy those passwords into the appropriate fields where you are setting the password. You can also use the hotkeys **Ctrl+Shift+Z** and **Ctrl+Shift+X** to auto-type the old and new passwords, respectively. If you want to set an expiration date, you can do that here.
22 |
23 |
24 |
25 | Once you have successfully changed the password, click **Save New Password** to store the new password into the KeePass database. Every time you change the password, the old password will be backed up in the entry’s [history](https://keepass.info/help/v2/entry.html#hst).
26 |
27 | ### Password rules
28 | By default, this tool will generate a new password based on the **Automatically generated passwords for new entries** profile in KeePass. You should [configure this profile](https://keepass.info/help/base/pwgenerator.html#configauto) to produce a very strong password. However, for services for which the passwords generated by this profile are [*too* strong](https://twitter.com/PWTooStrong), the plugin can help you generate a password according to each service’s individual password policy.
29 |
30 | To specify the rules constraining your password, click the **Edit Rule** button from the Change Password window, or select **Edit Password Rule** from the entry context menu. In the Password Rule window, you can specify the rule in one of two ways.
31 |
32 | #### Profile
33 | You can select an existing KeePass profile—either one that is built into KeePass or a custom one that you have created. To choose this option, select **Profile** and choose the profile you want to use.
34 |
35 |
36 |
37 | #### Rule
38 | You can also build a password rule by providing a list of the character sets that may, must, or must not be used in the password. To enter a password rule in this way, select the **Rule** option in the Password Rule dialog.
39 |
40 | Let’s say that a website requires passwords with the following properties:
41 |
42 | - A password must be 8–20 characters long.
43 | - A password must contain at least one letter.
44 | - A password must contain at least one digit.
45 | - A password may contain special characters, from the character set `!@#$%^&*()`.
46 | - Passwords must be changed every 18 months.
47 |
48 | As shown in the screenshot below, we will first enter a **Length** of *20*, the maximum password length. (There is no point in generating a password shorter than the maximum length.) Then click the **Add Character Set** button to add the built-in **Letters** and **Digits** character sets, and specify that both of them are **Required**. Click **Add Character Set** again to select a **Custom** character set, which you can then populate with the “special characters” listed in the password requirements. Finally, indicate that a password generated from this rule expires after 18 months.
49 |
50 |
51 |
52 | Other options available in the Add Character Set menu are **All characters**, **Punctuation**, **Uppercase letters**, and **Lowercase letters**. If the service for which you are generating a password requires that a password *not* contain certain characters, you can enter those into the **Exclude** field.
53 |
54 |
55 |
56 | The **Example** field in the Edit Rule window shows a sample password that follows the rule or profile that you have selected. (This is not the same password that will be set to the password entry when you save it.)
57 |
58 | The [password strength](https://en.wikipedia.org/wiki/Password_strength) is calculated from the configured rule (not the generated password). Every additional bit of strength represents a doubling of the expected time it would take for a hacker to guess the password by brute force.
59 |
60 | Once you have finished entering the password requirements, click **Accept**. If you were editing the rule from the Change Password window, a new password will automatically be generated using your new rule. Follow the steps above to save this password.
61 |
62 | Any time you need to generate another password, just use the Generate New Password menu item. It will automatically use the rule and expiration date that you have set for that entry.
63 |
64 | ### Other features
65 | You can access this plugin’s features from the password generation menu in the standard KeePass editor window. Click **Generate From Rule** to generate a new password based on the entry’s configured rule, or click **Edit Password Rule** to edit the rule. Click **Open Built-in Password Generator** to access the normal KeePass password generator.
66 |
67 |
68 |
69 | A rule can be configured for a group of entries. This option is available in the context menu of the group. If a group has a rule configured, all entries in that group will use that rule when a password is generated, unless the rule is overridden in the entry itself.
70 |
71 |
72 |
73 | ---
74 | Icons made by [Freepik](https://www.flaticon.com/authors/freepik "Freepik") from [www.flaticon.com](https://www.flaticon.com/ "Flaticon")
75 |
--------------------------------------------------------------------------------
/RuleBuilder.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.1.32328.378
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RuleBuilder", "RuleBuilder\RuleBuilder.csproj", "{6A61D4EB-BA26-4E32-906E-DEF1280163AD}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RuleBuilderTests", "RuleBuilderTests\RuleBuilderTests.csproj", "{9A7EE14B-4E48-40A2-898E-1139329BFA03}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EB95F864-50D9-491E-A787-E87ED1694325}"
11 | ProjectSection(SolutionItems) = preProject
12 | LICENSE = LICENSE
13 | README.md = README.md
14 | EndProjectSection
15 | EndProject
16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Images", "Images", "{9B39E0D1-37E2-488E-B073-54AA16495410}"
17 | ProjectSection(SolutionItems) = preProject
18 | Images\change.png = Images\change.png
19 | Images\charsets.png = Images\charsets.png
20 | Images\entry.png = Images\entry.png
21 | Images\group.png = Images\group.png
22 | Images\menu.png = Images\menu.png
23 | Images\profile.png = Images\profile.png
24 | Images\rule.png = Images\rule.png
25 | EndProjectSection
26 | EndProject
27 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{A8F703CE-B688-45F7-B5D3-BFD2E3B105E5}"
28 | ProjectSection(SolutionItems) = preProject
29 | .editorconfig = .editorconfig
30 | .gitattributes = .gitattributes
31 | .gitignore = .gitignore
32 | .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml
33 | EndProjectSection
34 | EndProject
35 | Global
36 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
37 | Debug|Any CPU = Debug|Any CPU
38 | Release|Any CPU = Release|Any CPU
39 | EndGlobalSection
40 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
41 | {6A61D4EB-BA26-4E32-906E-DEF1280163AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 | {6A61D4EB-BA26-4E32-906E-DEF1280163AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 | {6A61D4EB-BA26-4E32-906E-DEF1280163AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
44 | {6A61D4EB-BA26-4E32-906E-DEF1280163AD}.Release|Any CPU.Build.0 = Release|Any CPU
45 | {9A7EE14B-4E48-40A2-898E-1139329BFA03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46 | {9A7EE14B-4E48-40A2-898E-1139329BFA03}.Debug|Any CPU.Build.0 = Debug|Any CPU
47 | {9A7EE14B-4E48-40A2-898E-1139329BFA03}.Release|Any CPU.ActiveCfg = Release|Any CPU
48 | {9A7EE14B-4E48-40A2-898E-1139329BFA03}.Release|Any CPU.Build.0 = Release|Any CPU
49 | EndGlobalSection
50 | GlobalSection(SolutionProperties) = preSolution
51 | HideSolutionNode = FALSE
52 | EndGlobalSection
53 | GlobalSection(NestedProjects) = preSolution
54 | {9B39E0D1-37E2-488E-B073-54AA16495410} = {EB95F864-50D9-491E-A787-E87ED1694325}
55 | {A8F703CE-B688-45F7-B5D3-BFD2E3B105E5} = {EB95F864-50D9-491E-A787-E87ED1694325}
56 | EndGlobalSection
57 | GlobalSection(ExtensibilityGlobals) = postSolution
58 | SolutionGuid = {74AB2C26-02C4-43E8-84EC-13CA0B1C150B}
59 | EndGlobalSection
60 | EndGlobal
61 |
--------------------------------------------------------------------------------
/RuleBuilder/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/RuleBuilder/Forms/ChangePassword.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/RuleBuilder/Forms/ChangePassword.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 | using System.Windows;
4 | using System.Windows.Forms;
5 | using System.Windows.Interop;
6 | using KeePass.App;
7 | using KeePassLib;
8 | using KeePassLib.Security;
9 | using RuleBuilder.Rule;
10 | using RuleBuilder.Util;
11 |
12 | namespace RuleBuilder.Forms {
13 | public partial class ChangePassword : Window {
14 | private const int HotKeyMessage = 0x312;
15 | private const short ShiftKey = 0x10;
16 |
17 | private ChangePassword(KeePass.Forms.MainForm mainForm, PwDatabase database, PwEntry entry) {
18 | this.InitializeComponent();
19 | this.MainForm = mainForm;
20 | this.Database = database;
21 | this.Entry = entry;
22 | new WindowInteropHelper(this).Owner = mainForm.Handle;
23 | this.Title = $"{Properties.Resources.ChangePassword}: {entry.Strings.Get(PwDefs.TitleField)?.ReadString() ?? string.Empty}";
24 | this.Configuration = Rule.Serialization.Entry.EntryDefaultConfiguration(entry);
25 | this.txtOldPassword.Text = entry.Strings.Get(PwDefs.PasswordField)?.ReadString() ?? string.Empty;
26 | this.txtNewPassword.Text = this.Configuration.Generator.NewPassword();
27 | this.SetExpiration();
28 | }
29 |
30 | private KeePass.Forms.MainForm MainForm { get; }
31 |
32 | private PwDatabase Database { get; }
33 |
34 | private PwEntry Entry { get; }
35 |
36 | private int OldPasswordHotKeyID { get; set; }
37 |
38 | private int NewPasswordHotKeyID { get; set; }
39 |
40 | private bool EntryChanged { get; set; }
41 |
42 | private Configuration Configuration { get; set; }
43 |
44 | private bool RuleChanged { get; set; }
45 |
46 | private HwndSource Source { get; set; }
47 |
48 | public static bool ShowChangePasswordDialog(KeePass.Forms.MainForm mainForm, PwEntry entry) {
49 | if (mainForm == null) {
50 | throw new ArgumentNullException(nameof(mainForm));
51 | }
52 | if (entry == null) {
53 | throw new ArgumentNullException(nameof(entry));
54 | }
55 | ChangePassword window = new ChangePassword(mainForm, mainForm.ActiveDatabase, entry);
56 | _ = window.ShowDialog();
57 | return window.EntryChanged;
58 | }
59 |
60 | private IntPtr HwndHook(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
61 | if (msg == HotKeyMessage) {
62 | WaitForKeyRelease();
63 | int hotKeyID = wParam.ToInt32();
64 | if (hotKeyID == this.OldPasswordHotKeyID) {
65 | _ = KeePass.Util.AutoType.PerformIntoCurrentWindow(this.Entry, this.Database, EscapeAutoType(this.txtOldPassword.Text));
66 | handled = true;
67 | } else if (hotKeyID == this.NewPasswordHotKeyID) {
68 | _ = KeePass.Util.AutoType.PerformIntoCurrentWindow(this.Entry, this.Database, EscapeAutoType(this.txtNewPassword.Text));
69 | handled = true;
70 | }
71 | }
72 | return IntPtr.Zero;
73 | }
74 |
75 | private static void WaitForKeyRelease() {
76 | DateTime start = DateTime.UtcNow;
77 | while (ShiftKeyDown()) {
78 | if (DateTime.UtcNow.Ticks - start.Ticks > 2e7) {
79 | break;
80 | }
81 | }
82 | }
83 |
84 | private static string EscapeAutoType(string text) => Regex.Replace(text, @"[+%^~()[\]{}]", (Match match) => $"{{{match.Value}}}");
85 |
86 | private static bool ShiftKeyDown() => (NativeMethods.GetKeyState(ShiftKey) & 0x80) != 0;
87 |
88 | private void SaveClicked(object sender, RoutedEventArgs e) {
89 | string oldPassword = this.Entry.Strings.Get(PwDefs.PasswordField).ReadString();
90 | string newPassword = this.txtNewPassword.Text;
91 | bool passwordChanged = oldPassword != newPassword;
92 | if (passwordChanged || this.RuleChanged) {
93 | if (passwordChanged) {
94 | this.Entry.CreateBackup(this.Database);
95 | this.Entry.Strings.Set(PwDefs.PasswordField, new ProtectedString(true, newPassword));
96 | if (this.chkExpiration.IsChecked == true && this.dateExpiration.SelectedDate != null) {
97 | this.Entry.Expires = true;
98 | this.Entry.ExpiryTime = TimeZoneInfo.ConvertTimeToUtc(this.dateExpiration.SelectedDate.Value);
99 | } else {
100 | this.Entry.Expires = false;
101 | }
102 | }
103 | if (this.RuleChanged) {
104 | Rule.Serialization.Entry.SetEntryConfiguration(this.Entry, this.Configuration);
105 | }
106 | this.Entry.Touch(true);
107 | this.EntryChanged = true;
108 | }
109 | this.DialogResult = true;
110 | }
111 |
112 | private void EditRuleClicked(object sender, RoutedEventArgs e) {
113 | Configuration config = this.Configuration;
114 | if (EditRule.ShowRuleDialog(this.MainForm, ref config)) {
115 | this.RuleChanged = true;
116 | this.Configuration = config;
117 | this.txtNewPassword.Text = this.Configuration.Generator.NewPassword();
118 | this.SetExpiration();
119 | }
120 | }
121 |
122 | private void RefreshClicked(object sender, RoutedEventArgs e) {
123 | this.txtNewPassword.Text = this.Configuration.Generator.NewPassword();
124 | }
125 |
126 | private void WindowLoaded(object sender, RoutedEventArgs e) {
127 | this.Source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
128 | this.Source.AddHook(this.HwndHook);
129 | if (this.Entry.GetAutoTypeEnabled() && AppPolicy.Try(AppPolicyId.AutoTypeWithoutContext)) {
130 | try {
131 | this.OldPasswordHotKeyID = HotKey.RegisterHotKey(this, Keys.Z | Keys.Control | Keys.Shift);
132 | this.lblAutoTypeOld.Text = $"{Properties.Resources.AutoType}: Ctrl+Shift+Z";
133 | } catch (HotKeyException ex) {
134 | _ = ex;
135 | }
136 | try {
137 | this.NewPasswordHotKeyID = HotKey.RegisterHotKey(this, Keys.X | Keys.Control | Keys.Shift);
138 | this.lblAutoTypeNew.Text = $"{Properties.Resources.AutoType}: Ctrl+Shift+X";
139 | } catch (HotKeyException ex) {
140 | _ = ex;
141 | }
142 | }
143 | this.MinHeight = this.Height;
144 | this.MaxHeight = this.Height;
145 | }
146 |
147 | private void WindowClosed(object sender, EventArgs e) {
148 | HotKey.UnregisterHotKey(this, this.OldPasswordHotKeyID);
149 | HotKey.UnregisterHotKey(this, this.NewPasswordHotKeyID);
150 | }
151 |
152 | private void SetExpiration() {
153 | if (this.Configuration.Expiration != null) {
154 | this.chkExpiration.IsChecked = true;
155 | this.dateExpiration.IsEnabled = true;
156 | this.dateExpiration.SelectedDate = this.Configuration.Expiration.DateFrom(DateTime.Today);
157 | } else if (this.Entry.Expires) {
158 | this.chkExpiration.IsChecked = true;
159 | this.dateExpiration.IsEnabled = true;
160 | this.dateExpiration.SelectedDate = this.Entry.ExpiryTime.ToLocalTime();
161 | } else {
162 | this.chkExpiration.IsChecked = false;
163 | this.dateExpiration.IsEnabled = false;
164 | this.dateExpiration.SelectedDate = DateTime.Today;
165 | }
166 | }
167 |
168 | private void ExpirationClicked(object sender, RoutedEventArgs e) {
169 | this.dateExpiration.IsEnabled = this.chkExpiration.IsChecked ?? false;
170 | }
171 |
172 | private void ExpirationDateChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) {
173 | if (this.dateExpiration.SelectedDate == null) {
174 | this.dateExpiration.SelectedDate = DateTime.Today;
175 | }
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/RuleBuilder/Forms/EditRule.xaml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------
/RuleBuilder/Forms/EditRule.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.Windows;
5 | using System.Windows.Controls;
6 | using System.Windows.Interop;
7 | using RuleBuilder.Rule;
8 | using RuleBuilder.Util;
9 |
10 | namespace RuleBuilder.Forms {
11 | public partial class EditRule : Window {
12 | private EditRule(KeePass.Forms.MainForm mainForm, Configuration config) {
13 | this.Data = new EditRuleModel(config);
14 | this.Data.PasswordRule.Components.Add(new Rule.Component(null, false));
15 | this.DataContext = this.Data;
16 | new WindowInteropHelper(this).Owner = mainForm.Handle;
17 | InitializeComponent();
18 | (this.Data.RuleType == RuleType.Rule ? this.rdoRule : this.rdoProfile).IsChecked = true;
19 | this.Data.PasswordExpires = config.Expiration != null;
20 | this.Data.ExpirationLength = config.Expiration?.Length ?? 1;
21 | this.Data.ExpirationUnit = (int)(config.Expiration?.Unit ?? ExpirationUnit.Years);
22 | this.GenerateExamplePassword();
23 | this.Data.RuleChanged += () => this.GenerateExamplePassword();
24 | this.dgComponents.SelectedIndex = 0;
25 | this.BuildMenu();
26 | }
27 |
28 | public EditRuleModel Data { get; }
29 |
30 | private ContextMenu CharacterClassMenu { get; } = new ContextMenu() {
31 | Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom
32 | };
33 |
34 | private void BuildMenu() {
35 | this.CharacterClassMenu.Items.Clear();
36 | foreach (Rule.CharacterClass characterClass in new Rule.CharacterClass[] {
37 | Rule.CharacterClass.AllCharacters,
38 | Rule.CharacterClass.Letters,
39 | Rule.CharacterClass.Digits,
40 | Rule.CharacterClass.Punctuation,
41 | Rule.CharacterClass.UppercaseLetters,
42 | Rule.CharacterClass.LowercaseLetters
43 | }) {
44 | this.CharacterClassMenu.Items.Add(this.CharacterClassItem(characterClass.Name, characterClass));
45 | }
46 | this.CharacterClassMenu.Items.Add(new Separator());
47 | this.CharacterClassMenu.Items.Add(this.CharacterClassItem(Properties.Resources.Custom, new Rule.CharacterClass()));
48 | }
49 |
50 | private MenuItem CharacterClassItem(string caption, Rule.CharacterClass characterClass) {
51 | MenuItem item = new MenuItem() {
52 | Header = caption
53 | };
54 | item.Click += (_1, _2) => {
55 | IList components = this.Data.PasswordRule.Components;
56 | int index = components.Count - 1;
57 | components.Insert(index, new Rule.Component(characterClass.Clone(), false));
58 | this.dgComponents.SelectedIndex = index;
59 | this.GenerateExamplePassword();
60 | };
61 | return item;
62 | }
63 |
64 | public static bool ShowRuleDialog(KeePass.Forms.MainForm mainForm, ref Configuration config) {
65 | if (mainForm == null) {
66 | throw new ArgumentNullException(nameof(mainForm));
67 | }
68 | if (config == null) {
69 | config = new Configuration();
70 | }
71 | EditRule window = new EditRule(mainForm, config);
72 | _ = window.ShowDialog();
73 | config = window.Data.Configuration;
74 | return window.DialogResult ?? false;
75 | }
76 |
77 | private void RuleOrProfileChecked(object sender, RoutedEventArgs e) {
78 | bool isRule = rdoRule.IsChecked ?? false;
79 | this.Data.RuleType = isRule ? RuleType.Rule : RuleType.Profile;
80 | this.gridRule.Visibility = isRule ? Visibility.Visible : Visibility.Hidden;
81 | this.lbProfiles.Visibility = isRule ? Visibility.Hidden : Visibility.Visible;
82 | }
83 |
84 | private void GeneratePasswordClicked(object sender, RoutedEventArgs e) {
85 | this.GenerateExamplePassword();
86 | }
87 |
88 | private void GenerateExamplePassword() {
89 | double? strength = this.Strength();
90 | if (strength == null) {
91 | this.panelStrength.Visibility = Visibility.Hidden;
92 | } else {
93 | this.panelStrength.Visibility = Visibility.Visible;
94 | this.txtStrength.Text = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Bits, strength);
95 | }
96 | this.txtExample.Text = this.Data.Configuration.Generator.NewPassword();
97 | }
98 |
99 | private double? Strength() {
100 | if (this.Data.RuleType == RuleType.Rule) {
101 | try {
102 | return Entropy.EntropyBits(this.Data.PasswordRule);
103 | } catch (ArgumentOutOfRangeException) {
104 | return null;
105 | }
106 | } else {
107 | return null;
108 | }
109 | }
110 |
111 | private void AcceptClicked(object sender, RoutedEventArgs e) {
112 | this.DialogResult = true;
113 | }
114 |
115 | private void DataGridSelected(object sender, RoutedEventArgs e) {
116 | _ = ((DataGrid)sender).BeginEdit(e);
117 | }
118 |
119 | private void AddRowClicked(object sender, RoutedEventArgs e) {
120 | this.CharacterClassMenu.PlacementTarget = (Button)sender;
121 | this.CharacterClassMenu.IsOpen = true;
122 | }
123 |
124 | private void DeleteRowClicked(object sender, RoutedEventArgs e) {
125 | int oldIndex = this.dgComponents.SelectedIndex;
126 | this.Data.PasswordRule.Components.Remove(dgComponents.SelectedItem as Rule.Component);
127 | this.dgComponents.SelectedIndex = oldIndex;
128 | this.GenerateExamplePassword();
129 | }
130 |
131 | private void SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e) {
132 | this.btnDeleteRow.IsEnabled = (((DataGrid)sender).SelectedItem as Rule.Component)?.CharacterClass != null;
133 | }
134 |
135 | private void TextBoxLoaded(object sender, RoutedEventArgs e) {
136 | TextBox textBox = (TextBox)sender;
137 | if (this.dgComponents.SelectedValue == textBox.DataContext) {
138 | _ = textBox.Focus();
139 | }
140 | }
141 |
142 | private void FormClosing(object sender, System.ComponentModel.CancelEventArgs e) {
143 | this.Data.PasswordRule.Components.RemoveAt(this.Data.PasswordRule.Components.Count - 1);
144 | }
145 |
146 | private void CustomCharacterSetChanged(object sender, TextChangedEventArgs e) => this.GenerateExamplePassword();
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/RuleBuilder/Forms/EditRuleModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using KeePass.Util;
5 | using KeePassLib.Cryptography.PasswordGenerator;
6 | using RuleBuilder.Rule;
7 |
8 | namespace RuleBuilder.Forms {
9 | public class EditRuleModel : RuleProperty {
10 | public EditRuleModel(Configuration config) {
11 | this.Configuration = config ?? throw new ArgumentNullException(nameof(config));
12 | this.RuleType = config.Generator is Rule.PasswordRule ? RuleType.Rule : RuleType.Profile;
13 | this.PasswordRule = (config.Generator as Rule.PasswordRule)?.Clone() ?? new Rule.PasswordRule();
14 | this.SelectedProfileIndex = this.ProfileIndex((config.Generator as Rule.PasswordProfile)?.Clone() ?? Rule.PasswordProfile.DefaultProfile);
15 | this.PasswordRule.RuleChanged += () => this.NotifyRuleChanged();
16 | }
17 |
18 | public List Profiles { get; } = new Rule.PasswordProfile[] {
19 | Rule.PasswordProfile.DefaultProfile
20 | }.Concat(
21 | PwGeneratorUtil.GetAllProfiles(false).Select((PwProfile profile) => new Rule.PasswordProfile(profile))
22 | ).ToList();
23 |
24 | private int _selectedProfileIndex;
25 | public int SelectedProfileIndex {
26 | get => this._selectedProfileIndex;
27 | set {
28 | if (this._selectedProfileIndex != value) {
29 | this._selectedProfileIndex = value;
30 | this.NotifyPropertyChanged(nameof(this.SelectedProfileIndex));
31 | }
32 | }
33 | }
34 |
35 | private RuleType _ruleType;
36 | public RuleType RuleType {
37 | get => this._ruleType;
38 | set {
39 | if (this._ruleType != value) {
40 | this._ruleType = value;
41 | this.NotifyPropertyChanged(nameof(this.RuleType));
42 | }
43 | }
44 | }
45 |
46 | public Rule.PasswordRule PasswordRule { get; }
47 |
48 | private bool _passwordExpires;
49 | public bool PasswordExpires {
50 | get => this._passwordExpires;
51 | set {
52 | if (this._passwordExpires != value) {
53 | this._passwordExpires = value;
54 | this.NotifyPropertyChanged(nameof(this.PasswordExpires), false);
55 | }
56 | }
57 | }
58 |
59 | private int _expirationLength;
60 | public int ExpirationLength {
61 | get => this._expirationLength;
62 | set {
63 | if (this._expirationLength != value) {
64 | this._expirationLength = value;
65 | this.NotifyPropertyChanged(nameof(this.ExpirationLength), false);
66 | }
67 | }
68 | }
69 |
70 | private ExpirationUnit _expirationUnit;
71 | public int ExpirationUnit {
72 | get => (int)this._expirationUnit;
73 | set {
74 | ExpirationUnit unit = (ExpirationUnit)value;
75 | if (this._expirationUnit != unit) {
76 | this._expirationUnit = unit;
77 | this.NotifyPropertyChanged(nameof(this.ExpirationUnit), false);
78 | }
79 | }
80 | }
81 |
82 | private Configuration _configuration;
83 | public Configuration Configuration {
84 | get {
85 | this._configuration.Generator = this.RuleType == RuleType.Rule
86 | ? (IPasswordGenerator)this.PasswordRule
87 | : this.Profiles[this.SelectedProfileIndex];
88 | this._configuration.Expiration = this.PasswordExpires
89 | ? new Expiration((ExpirationUnit)this.ExpirationUnit, this.ExpirationLength)
90 | : null;
91 | return this._configuration;
92 | }
93 | set {
94 | this._configuration = value;
95 | }
96 | }
97 |
98 | private int ProfileIndex(Rule.PasswordProfile profile) {
99 | if (profile.IsDefaultProfile) {
100 | return 0;
101 | } else {
102 | int index = this.Profiles.FindIndex((Rule.PasswordProfile p) => p.Name == profile.Name);
103 | return index != -1 ? index : 0;
104 | }
105 | }
106 | }
107 |
108 | public enum RuleType {
109 | Rule,
110 | Profile
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/RuleBuilder/Forms/EntryFormMod.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Windows.Forms;
4 | using KeePass;
5 | using KeePass.App.Configuration;
6 | using KeePass.Forms;
7 | using KeePass.Plugins;
8 | using KeePass.UI;
9 | using KeePass.Util;
10 | using KeePassLib;
11 | using KeePassLib.Cryptography.PasswordGenerator;
12 | using KeePassLib.Security;
13 | using RuleBuilder.Properties;
14 | using RuleBuilder.Rule;
15 | using RuleBuilder.Rule.Serialization;
16 |
17 | namespace RuleBuilder.Forms {
18 | delegate void ChangeExpiration(DateTime newDate);
19 |
20 | static class EntryFormMod {
21 | public static void RegisterEntryForm(IPluginHost pluginHost) {
22 | GlobalWindowManager.WindowAdded += new EventHandler((object _, GwmWindowEventArgs args) => {
23 | if (args.Form is PwEntryForm entryForm) {
24 | ModifyEntryForm(entryForm, pluginHost.MainWindow);
25 | }
26 | });
27 | }
28 |
29 | private static void ModifyEntryForm(PwEntryForm entryForm, MainForm mainWindow) {
30 | try {
31 | TabControl tabs = entryForm.Controls["m_tabMain"] as TabControl;
32 | TabPage page = tabs.TabPages[tabs.TabPages.IndexOfKey("m_tabEntry")];
33 | Button genPwButton = page.Controls["m_btnGenPw"] as Button;
34 | CheckBox expires = page.Controls["m_cbExpires"] as CheckBox;
35 | DateTimePicker expiresDate = page.Controls["m_dtExpireDateTime"] as DateTimePicker;
36 | if (expires == null || expiresDate == null) {
37 | throw new NullReferenceException();
38 | }
39 | Button changePassword = new Button() {
40 | Left = genPwButton.Left,
41 | Top = genPwButton.Top,
42 | Size = genPwButton.Size,
43 | TabIndex = genPwButton.TabIndex,
44 | UseVisualStyleBackColor = true,
45 | };
46 | _ = UIUtil.SetButtonImage(changePassword, UIUtil.CreateDropDownImage(Images.NewPassword), true);
47 | new ToolTip().SetToolTip(changePassword, Resources.GenerateAPassword);
48 | void changeExpiration(DateTime newDate) {
49 | expires.Checked = true;
50 | expiresDate.Value = newDate;
51 | }
52 | changePassword.Click += (object _1, EventArgs _2) => ShowMenu(entryForm, mainWindow, changePassword, changeExpiration);
53 | genPwButton.Visible = false;
54 | page.Controls.Add(changePassword);
55 |
56 | } catch (NullReferenceException) { }
57 | }
58 |
59 | private static void ShowMenu(PwEntryForm entryForm, MainForm mainWindow, Button button, ChangeExpiration changeExpiration) {
60 | CustomContextMenuStripEx menu = new CustomContextMenuStripEx();
61 | menu.Items.AddRange(MenuItems(entryForm, mainWindow, changeExpiration));
62 | menu.ShowEx(button);
63 | }
64 |
65 | private static ToolStripItem[] MenuItems(PwEntryForm entryForm, MainForm mainWindow, ChangeExpiration changeExpiration) {
66 | List items = new List();
67 | items.Add(RuleBuilderExt.MenuItem(Resources.GenerateFromRule, () => GenerateFromRule(entryForm, changeExpiration), Images.Dice));
68 | items.Add(RuleBuilderExt.MenuItem(Resources.EditPasswordRule, () => EditRule(entryForm, mainWindow), null));
69 |
70 | List profiles = PwGeneratorUtil.GetAllProfiles(true);
71 | if (profiles.Count > 0) {
72 | items.Add(new ToolStripSeparator());
73 | bool hideBuiltIn = (Program.Config.UI.UIFlags &
74 | (ulong)AceUIFlags.HideBuiltInPwGenPrfInEntryDlg) != 0;
75 | foreach (PwProfile profile in profiles) {
76 | if (!hideBuiltIn || !PwGeneratorUtil.IsBuiltInProfile(profile.Name)) {
77 | items.Add(RuleBuilderExt.MenuItem(profile.Name, () => GenerateFromProfile(entryForm, profile), Images.Organizer));
78 | }
79 | }
80 | }
81 |
82 | items.Add(new ToolStripSeparator());
83 | items.Add(RuleBuilderExt.MenuItem(Resources.OpenBuiltInPasswordGenerator, () => GenerateFromProfileEditor(entryForm), Images.NewPassword));
84 |
85 | return items.ToArray();
86 | }
87 |
88 | private static void GenerateFromRule(PwEntryForm entryForm, ChangeExpiration changeExpiration) {
89 | entryForm.UpdateEntryStrings(true, false);
90 | Configuration config = Entry.DeserializedDefaultConfiguration(
91 | entryForm.EntryStrings.Get(Entry.PasswordRuleKey)?.ReadString(), entryForm.EntryRef.ParentGroup);
92 | entryForm.EntryStrings.Set(PwDefs.PasswordField, new ProtectedString(true, config.Generator.NewPassword()));
93 | if (config.Expiration != null) {
94 | changeExpiration(config.Expiration.DateFrom(DateTime.Today));
95 | }
96 | entryForm.UpdateEntryStrings(false, true, true);
97 | }
98 |
99 | private static void EditRule(PwEntryForm entryForm, MainForm mainWindow) {
100 | entryForm.UpdateEntryStrings(true, false);
101 | Configuration config = entryForm.EntryStrings.Exists(Entry.PasswordRuleKey)
102 | ? Entry.DeserializedConfiguration(entryForm.EntryStrings.Get(Entry.PasswordRuleKey).ReadString())
103 | : null;
104 | if (Forms.EditRule.ShowRuleDialog(mainWindow, ref config)) {
105 | if (config == null || config.IsDefaultConfiguration()) {
106 | entryForm.EntryStrings.Remove(Entry.PasswordRuleKey);
107 | } else {
108 | entryForm.EntryStrings.Set(Entry.PasswordRuleKey, new ProtectedString(false, Entry.SerializedConfiguration(config)));
109 | }
110 | entryForm.UpdateEntryStrings(false, true, true);
111 | }
112 | }
113 |
114 | private static void GenerateFromProfileEditor(PwEntryForm entryForm) {
115 | entryForm.UpdateEntryStrings(true, false);
116 | PwProfile derived = PwProfile.DeriveFromPassword(entryForm.EntryStrings.GetSafe(PwDefs.PasswordField));
117 | PwGeneratorForm generator = new PwGeneratorForm();
118 | try {
119 | generator.InitEx(derived, true, false);
120 | if (generator.ShowDialog() == DialogResult.OK) {
121 | GenerateFromProfile(entryForm, generator.SelectedProfile);
122 | }
123 | } finally {
124 | UIUtil.DestroyForm(generator);
125 | }
126 | }
127 |
128 | private static void GenerateFromProfile(PwEntryForm entryForm, PwProfile profile) {
129 | entryForm.UpdateEntryStrings(true, false);
130 | byte[] entropy = EntropyForm.CollectEntropyIfEnabled(profile);
131 | _ = PwGenerator.Generate(out ProtectedString password, profile, entropy, Program.PwGeneratorPool);
132 | entryForm.EntryStrings.Set(PwDefs.PasswordField, password);
133 | entryForm.UpdateEntryStrings(false, true, true);
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/RuleBuilder/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.InteropServices;
4 | using System.Windows;
5 |
6 | // General Information about an assembly is controlled through the following
7 | // set of attributes. Change these attribute values to modify the information
8 | // associated with an assembly.
9 | [assembly: AssemblyTitle("Rule Builder")]
10 | [assembly: AssemblyDescription("Simplify password changes with site-specific password rules")]
11 | [assembly: AssemblyConfiguration("")]
12 | [assembly: AssemblyCompany("Ira Hanson")]
13 | [assembly: AssemblyProduct("KeePass Plugin")]
14 | [assembly: AssemblyCopyright("2022")]
15 | [assembly: AssemblyTrademark("")]
16 | [assembly: AssemblyCulture("")]
17 |
18 | // Setting ComVisible to false makes the types in this assembly not visible
19 | // to COM components. If you need to access a type in this assembly from
20 | // COM, set the ComVisible attribute to true on that type.
21 | [assembly: ComVisible(false)]
22 |
23 | //In order to begin building localizable applications, set
24 | //CultureYouAreCodingWith in your .csproj file
25 | //inside a . For example, if you are using US english
26 | //in your source files, set the to en-US. Then uncomment
27 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
28 | //the line below to match the UICulture setting in the project file.
29 |
30 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
31 |
32 |
33 | [assembly: ThemeInfo(
34 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
35 | //(used if a resource is not found in the page,
36 | // or application resource dictionaries)
37 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
38 | //(used if a resource is not found in the page,
39 | // app, or any theme specific resource dictionaries)
40 | )]
41 |
42 |
43 | // Version information for an assembly consists of the following four values:
44 | //
45 | // Major Version
46 | // Minor Version
47 | // Build Number
48 | // Revision
49 | //
50 | // You can specify all the values or you can default the Build and Revision Numbers
51 | // by using the '*' as shown below:
52 | // [assembly: AssemblyVersion("1.0.*")]
53 | [assembly: AssemblyVersion("3.4.0.0")]
54 | [assembly: AssemblyFileVersion("3.4.0.0")]
55 | [assembly: NeutralResourcesLanguage("en-US")]
56 |
--------------------------------------------------------------------------------
/RuleBuilder/Properties/Images.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace RuleBuilder.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Images {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Images() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RuleBuilder.Properties.Images", typeof(Images).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized resource of type System.Drawing.Bitmap.
65 | ///
66 | internal static System.Drawing.Bitmap Dice {
67 | get {
68 | object obj = ResourceManager.GetObject("Dice", resourceCulture);
69 | return ((System.Drawing.Bitmap)(obj));
70 | }
71 | }
72 |
73 | ///
74 | /// Looks up a localized resource of type System.Drawing.Bitmap.
75 | ///
76 | internal static System.Drawing.Bitmap NewPassword {
77 | get {
78 | object obj = ResourceManager.GetObject("NewPassword", resourceCulture);
79 | return ((System.Drawing.Bitmap)(obj));
80 | }
81 | }
82 |
83 | ///
84 | /// Looks up a localized resource of type System.Drawing.Bitmap.
85 | ///
86 | internal static System.Drawing.Bitmap Organizer {
87 | get {
88 | object obj = ResourceManager.GetObject("Organizer", resourceCulture);
89 | return ((System.Drawing.Bitmap)(obj));
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/RuleBuilder/Properties/Images.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\IconsLow\Dice.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
123 |
124 |
125 | ..\Resources\IconsLow\NewPassword.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
126 |
127 |
128 | ..\Resources\IconsLow\Organizer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
129 |
130 |
--------------------------------------------------------------------------------
/RuleBuilder/Properties/ImagesHigh.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace RuleBuilder.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class ImagesHigh {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal ImagesHigh() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RuleBuilder.Properties.ImagesHigh", typeof(ImagesHigh).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized resource of type System.Drawing.Bitmap.
65 | ///
66 | internal static System.Drawing.Bitmap Dice {
67 | get {
68 | object obj = ResourceManager.GetObject("Dice", resourceCulture);
69 | return ((System.Drawing.Bitmap)(obj));
70 | }
71 | }
72 |
73 | ///
74 | /// Looks up a localized resource of type System.Drawing.Bitmap.
75 | ///
76 | internal static System.Drawing.Bitmap NewPassword {
77 | get {
78 | object obj = ResourceManager.GetObject("NewPassword", resourceCulture);
79 | return ((System.Drawing.Bitmap)(obj));
80 | }
81 | }
82 |
83 | ///
84 | /// Looks up a localized resource of type System.Drawing.Bitmap.
85 | ///
86 | internal static System.Drawing.Bitmap Organizer {
87 | get {
88 | object obj = ResourceManager.GetObject("Organizer", resourceCulture);
89 | return ((System.Drawing.Bitmap)(obj));
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/RuleBuilder/Properties/ImagesHigh.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\IconsHigh\Dice.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
123 |
124 |
125 | ..\Resources\IconsHigh\NewPassword.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
126 |
127 |
128 | ..\Resources\IconsHigh\Organizer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
129 |
130 |
--------------------------------------------------------------------------------
/RuleBuilder/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace RuleBuilder.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RuleBuilder.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to All characters.
65 | ///
66 | internal static string AllCharacters {
67 | get {
68 | return ResourceManager.GetString("AllCharacters", resourceCulture);
69 | }
70 | }
71 |
72 | ///
73 | /// Looks up a localized string similar to Auto-type.
74 | ///
75 | internal static string AutoType {
76 | get {
77 | return ResourceManager.GetString("AutoType", resourceCulture);
78 | }
79 | }
80 |
81 | ///
82 | /// Looks up a localized string similar to {0:0} bits.
83 | ///
84 | internal static string Bits {
85 | get {
86 | return ResourceManager.GetString("Bits", resourceCulture);
87 | }
88 | }
89 |
90 | ///
91 | /// Looks up a localized string similar to Change Password.
92 | ///
93 | internal static string ChangePassword {
94 | get {
95 | return ResourceManager.GetString("ChangePassword", resourceCulture);
96 | }
97 | }
98 |
99 | ///
100 | /// Looks up a localized string similar to Custom.
101 | ///
102 | internal static string Custom {
103 | get {
104 | return ResourceManager.GetString("Custom", resourceCulture);
105 | }
106 | }
107 |
108 | ///
109 | /// Looks up a localized string similar to Digits (0–9).
110 | ///
111 | internal static string Digits {
112 | get {
113 | return ResourceManager.GetString("Digits", resourceCulture);
114 | }
115 | }
116 |
117 | ///
118 | /// Looks up a localized string similar to Edit Password Rule....
119 | ///
120 | internal static string EditPasswordRule {
121 | get {
122 | return ResourceManager.GetString("EditPasswordRule", resourceCulture);
123 | }
124 | }
125 |
126 | ///
127 | /// Looks up a localized string similar to Generate a password.
128 | ///
129 | internal static string GenerateAPassword {
130 | get {
131 | return ResourceManager.GetString("GenerateAPassword", resourceCulture);
132 | }
133 | }
134 |
135 | ///
136 | /// Looks up a localized string similar to Generate From Rule....
137 | ///
138 | internal static string GenerateFromRule {
139 | get {
140 | return ResourceManager.GetString("GenerateFromRule", resourceCulture);
141 | }
142 | }
143 |
144 | ///
145 | /// Looks up a localized string similar to Generate New Password....
146 | ///
147 | internal static string GenerateNewPassword {
148 | get {
149 | return ResourceManager.GetString("GenerateNewPassword", resourceCulture);
150 | }
151 | }
152 |
153 | ///
154 | /// Looks up a localized string similar to Invalid character set enumeration.
155 | ///
156 | internal static string InvalidCharacterSet {
157 | get {
158 | return ResourceManager.GetString("InvalidCharacterSet", resourceCulture);
159 | }
160 | }
161 |
162 | ///
163 | /// Looks up a localized string similar to Letters (A–Z, a–z).
164 | ///
165 | internal static string Letters {
166 | get {
167 | return ResourceManager.GetString("Letters", resourceCulture);
168 | }
169 | }
170 |
171 | ///
172 | /// Looks up a localized string similar to Lowercase letters (a–z).
173 | ///
174 | internal static string LowercaseLetters {
175 | get {
176 | return ResourceManager.GetString("LowercaseLetters", resourceCulture);
177 | }
178 | }
179 |
180 | ///
181 | /// Looks up a localized string similar to Open Built-in Password Generator....
182 | ///
183 | internal static string OpenBuiltInPasswordGenerator {
184 | get {
185 | return ResourceManager.GetString("OpenBuiltInPasswordGenerator", resourceCulture);
186 | }
187 | }
188 |
189 | ///
190 | /// Looks up a localized string similar to Password length must not be negative..
191 | ///
192 | internal static string PasswordLengthMustNotBeNegative {
193 | get {
194 | return ResourceManager.GetString("PasswordLengthMustNotBeNegative", resourceCulture);
195 | }
196 | }
197 |
198 | ///
199 | /// Looks up a localized string similar to Punctuation.
200 | ///
201 | internal static string Punctuation {
202 | get {
203 | return ResourceManager.GetString("Punctuation", resourceCulture);
204 | }
205 | }
206 |
207 | ///
208 | /// Looks up a localized string similar to Unable to register hotkey..
209 | ///
210 | internal static string UnableToRegisterHotkey {
211 | get {
212 | return ResourceManager.GetString("UnableToRegisterHotkey", resourceCulture);
213 | }
214 | }
215 |
216 | ///
217 | /// Looks up a localized string similar to Uppercase letters (A–Z).
218 | ///
219 | internal static string UppercaseLetters {
220 | get {
221 | return ResourceManager.GetString("UppercaseLetters", resourceCulture);
222 | }
223 | }
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/RuleBuilder/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | All characters
122 |
123 |
124 | Auto-type
125 |
126 |
127 | {0:0} bits
128 |
129 |
130 | Change Password
131 |
132 |
133 | Custom
134 |
135 |
136 | Digits (0–9)
137 |
138 |
139 | Edit Password Rule...
140 |
141 |
142 | Generate a password
143 |
144 |
145 | Generate From Rule...
146 |
147 |
148 | Generate New Password...
149 |
150 |
151 | Invalid character set enumeration
152 |
153 |
154 | Letters (A–Z, a–z)
155 |
156 |
157 | Lowercase letters (a–z)
158 |
159 |
160 | Open Built-in Password Generator...
161 |
162 |
163 | Password length must not be negative.
164 |
165 |
166 | Punctuation
167 |
168 |
169 | Unable to register hotkey.
170 |
171 |
172 | Uppercase letters (A–Z)
173 |
174 |
--------------------------------------------------------------------------------
/RuleBuilder/Resources/IconsHigh/Dice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/RuleBuilder/Resources/IconsHigh/Dice.png
--------------------------------------------------------------------------------
/RuleBuilder/Resources/IconsHigh/NewPassword.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/RuleBuilder/Resources/IconsHigh/NewPassword.png
--------------------------------------------------------------------------------
/RuleBuilder/Resources/IconsHigh/Organizer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/RuleBuilder/Resources/IconsHigh/Organizer.png
--------------------------------------------------------------------------------
/RuleBuilder/Resources/IconsLow/Dice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/RuleBuilder/Resources/IconsLow/Dice.png
--------------------------------------------------------------------------------
/RuleBuilder/Resources/IconsLow/NewPassword.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/RuleBuilder/Resources/IconsLow/NewPassword.png
--------------------------------------------------------------------------------
/RuleBuilder/Resources/IconsLow/Organizer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/RuleBuilder/Resources/IconsLow/Organizer.png
--------------------------------------------------------------------------------
/RuleBuilder/Resources/Refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/a225049bca357d323c651c331c65e5f7975fedf5/RuleBuilder/Resources/Refresh.png
--------------------------------------------------------------------------------
/RuleBuilder/Resources/Strings.xaml:
--------------------------------------------------------------------------------
1 |
6 | Edit Rule
7 | _Rule
8 | _Profile
9 | Length
10 | _Delete Row
11 | Characters
12 | Required
13 | Exclude
14 | Example
15 | Passwords expire after
16 | Days
17 | Weeks
18 | Months
19 | Years
20 | Strength
21 | Generate another password
22 | _Save New Password
23 | _Discard
24 | _Accept
25 | _Cancel
26 | Add Character _Set
27 | Change Password
28 | Old password
29 | New password
30 | _Edit Rule
31 | Ctrl+Shift+Z
32 | Ctrl+Shift+X
33 | Password expires
34 |
--------------------------------------------------------------------------------
/RuleBuilder/Resources/Styles.xaml:
--------------------------------------------------------------------------------
1 |
5 |
8 |
11 |
14 |
18 |
24 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/CharacterClass.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using RuleBuilder.Properties;
5 |
6 | namespace RuleBuilder.Rule {
7 | public enum CharacterClassEnum {
8 | Custom = 0,
9 | AllCharacters = 1,
10 | Letters = 2,
11 | Digits = 3,
12 | Punctuation = 4,
13 | UppercaseLetters = 5,
14 | LowercaseLetters = 6
15 | }
16 | public class CharacterClass : RuleProperty {
17 | public static readonly CharacterClass AllCharacters = new CharacterClass(
18 | Resources.AllCharacters,
19 | UppercaseLettersChars + LowercaseLettersChars + DigitsChars + PunctuationChars,
20 | CharacterClassEnum.AllCharacters
21 | );
22 |
23 | public static readonly CharacterClass Letters = new CharacterClass(
24 | Resources.Letters,
25 | UppercaseLettersChars + LowercaseLettersChars,
26 | CharacterClassEnum.Letters
27 | );
28 |
29 | public static readonly CharacterClass Digits = new CharacterClass(
30 | Resources.Digits,
31 | DigitsChars,
32 | CharacterClassEnum.Digits
33 | );
34 |
35 | public static readonly CharacterClass Punctuation = new CharacterClass(
36 | Resources.Punctuation,
37 | PunctuationChars,
38 | CharacterClassEnum.Punctuation
39 | );
40 |
41 | public static readonly CharacterClass UppercaseLetters = new CharacterClass(
42 | Resources.UppercaseLetters,
43 | UppercaseLettersChars,
44 | CharacterClassEnum.UppercaseLetters
45 | );
46 |
47 | public static readonly CharacterClass LowercaseLetters = new CharacterClass(
48 | Resources.LowercaseLetters,
49 | LowercaseLettersChars,
50 | CharacterClassEnum.LowercaseLetters
51 | );
52 |
53 | private const string UppercaseLettersChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
54 | private const string LowercaseLettersChars = "abcdefghijklmnopqrstuvwxyz";
55 | private const string DigitsChars = "0123456789";
56 | private const string PunctuationChars = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ";
57 |
58 | public CharacterClass() : this(string.Empty) { }
59 |
60 | public CharacterClass(string characters) : this(Resources.Custom, characters, CharacterClassEnum.Custom) { }
61 |
62 | private CharacterClass(string name, string characters, CharacterClassEnum enumeration) {
63 | this.Name = name;
64 | this.Characters = characters;
65 | this.Enumeration = enumeration;
66 | }
67 |
68 | public string Name { get; }
69 |
70 | private string _characters = string.Empty;
71 | public string Characters {
72 | get => this._characters;
73 | set {
74 | if (this._characters != value) {
75 | this._characters = value;
76 | this.NotifyPropertyChanged(nameof(this.Characters));
77 | }
78 | }
79 | }
80 |
81 | public CharacterClassEnum Enumeration { get; }
82 |
83 | public static CharacterClass EnumeratedCharacterClass(CharacterClassEnum enumeration) {
84 | switch (enumeration) {
85 | case CharacterClassEnum.AllCharacters:
86 | return AllCharacters;
87 | case CharacterClassEnum.Letters:
88 | return Letters;
89 | case CharacterClassEnum.Digits:
90 | return Digits;
91 | case CharacterClassEnum.Punctuation:
92 | return Punctuation;
93 | case CharacterClassEnum.UppercaseLetters:
94 | return UppercaseLetters;
95 | case CharacterClassEnum.LowercaseLetters:
96 | return LowercaseLetters;
97 | default:
98 | throw new ArgumentException(Resources.InvalidCharacterSet, nameof(enumeration));
99 | }
100 | }
101 |
102 | public static HashSet SplitString(string str) {
103 | HashSet result = new HashSet();
104 | TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(str);
105 | while (enumerator.MoveNext()) {
106 | result.Add((string)enumerator.Current);
107 | }
108 | return result;
109 | }
110 |
111 | public CharacterClass Clone() => new CharacterClass(this.Name, this.Characters, this.Enumeration);
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/Component.cs:
--------------------------------------------------------------------------------
1 | namespace RuleBuilder.Rule {
2 | public class Component : RuleProperty {
3 | public Component(CharacterClass characterClass, bool required) {
4 | this.ChangeDelegate = () => this.NotifyRuleChanged();
5 | this.CharacterClass = characterClass;
6 | this.Required = required;
7 | }
8 |
9 | private RuleChangedDelegate ChangeDelegate { get; }
10 |
11 | private CharacterClass _characterClass;
12 | public CharacterClass CharacterClass {
13 | get => this._characterClass;
14 | set {
15 | if (value != this._characterClass) {
16 | if (this._characterClass != null) {
17 | this._characterClass.RuleChanged -= this.ChangeDelegate;
18 | }
19 | if (value != null) {
20 | value.RuleChanged += this.ChangeDelegate;
21 | }
22 | this._characterClass = value;
23 | this.NotifyPropertyChanged(nameof(this.CharacterClass));
24 | }
25 | }
26 | }
27 |
28 | private bool _required;
29 | public bool Required {
30 | get => this._required;
31 | set {
32 | if (value != this._required) {
33 | this._required = value;
34 | this.NotifyPropertyChanged(nameof(this.Required));
35 | }
36 | }
37 | }
38 |
39 | public Component Clone() => new Component(this.CharacterClass, this.Required);
40 | }
41 | }
--------------------------------------------------------------------------------
/RuleBuilder/Rule/Configuration.cs:
--------------------------------------------------------------------------------
1 | namespace RuleBuilder.Rule {
2 | public class Configuration {
3 | public Configuration() {
4 | this.Generator = PasswordProfile.DefaultProfile;
5 | }
6 |
7 | public Configuration(IPasswordGenerator generator, Expiration expiration = null) {
8 | this.Expiration = expiration;
9 | this.Generator = generator;
10 | }
11 |
12 | public IPasswordGenerator Generator { get; set; }
13 |
14 | public Expiration Expiration { get; set; }
15 |
16 | public bool IsDefaultConfiguration() => this.Expiration == null && this.Generator is PasswordProfile profile && profile.IsDefaultProfile;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/Expiration.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RuleBuilder.Rule {
4 | public class Expiration {
5 | public Expiration(ExpirationUnit unit, int length) {
6 | if (!Enum.IsDefined(typeof(ExpirationUnit), unit)) {
7 | throw new ArgumentOutOfRangeException(nameof(unit));
8 | }
9 | if (length < 1 || length > 999) {
10 | throw new ArgumentOutOfRangeException(nameof(length));
11 | }
12 | this.Unit = unit;
13 | this.Length = length;
14 | }
15 |
16 | public ExpirationUnit Unit { get; }
17 |
18 | public int Length { get; }
19 |
20 | public DateTime DateFrom(DateTime origin) {
21 | try {
22 | switch (this.Unit) {
23 | case ExpirationUnit.Days:
24 | return origin.AddDays(this.Length);
25 | case ExpirationUnit.Weeks:
26 | return origin.AddDays(this.Length * 7);
27 | case ExpirationUnit.Months:
28 | return origin.AddMonths(this.Length);
29 | case ExpirationUnit.Years:
30 | return origin.AddYears(this.Length);
31 | default:
32 | throw new ArgumentException("Invalid unit");
33 | }
34 | } catch (ArgumentOutOfRangeException) {
35 | return DateTime.MaxValue;
36 | }
37 | }
38 | }
39 |
40 | public enum ExpirationUnit {
41 | Days = 0,
42 | Weeks = 1,
43 | Months = 2,
44 | Years = 3
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/IPasswordGenerator.cs:
--------------------------------------------------------------------------------
1 | namespace RuleBuilder.Rule {
2 | public interface IPasswordGenerator {
3 | string NewPassword();
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/PasswordProfile.cs:
--------------------------------------------------------------------------------
1 | using KeePass.Resources;
2 | using KeePass.Util;
3 | using KeePassLib.Cryptography.PasswordGenerator;
4 | using KeePassLib.Security;
5 |
6 | namespace RuleBuilder.Rule {
7 | public class PasswordProfile : IPasswordGenerator {
8 | public PasswordProfile(PwProfile profile) {
9 | this.Profile = profile;
10 | }
11 | private PasswordProfile(PasswordProfile other) : this(other.Profile) {
12 | this.IsDefaultProfile = other.IsDefaultProfile;
13 | }
14 |
15 | public static PasswordProfile DefaultProfile => new PasswordProfile(KeePass.Program.Config.PasswordGenerator.AutoGeneratedPasswordsProfile) {
16 | IsDefaultProfile = true
17 | };
18 |
19 | public string Name => this.IsDefaultProfile ? KPRes.AutoGeneratedPasswordSettings : this.Profile.Name;
20 |
21 | public bool IsDefaultProfile { get; private set; }
22 |
23 | public PwProfile Profile { get; }
24 |
25 | public static PasswordProfile NamedProfile(string key) {
26 | foreach (PwProfile profile in PwGeneratorUtil.GetAllProfiles(false)) {
27 | if (profile.Name == key) {
28 | return new PasswordProfile(profile);
29 | }
30 | }
31 | return DefaultProfile;
32 | }
33 |
34 | public string NewPassword() {
35 | _ = PwGenerator.Generate(out ProtectedString result, this.Profile, null, KeePass.Program.PwGeneratorPool);
36 | return result.ReadString();
37 | }
38 |
39 | public PasswordProfile Clone() => new PasswordProfile(this);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/PasswordRule.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Collections.Specialized;
5 | using System.Linq;
6 |
7 |
8 | namespace RuleBuilder.Rule {
9 | public class PasswordRule : RuleProperty, IPasswordGenerator {
10 | public PasswordRule() {
11 | void changeDelegate() {
12 | this.NotifyRuleChanged();
13 | }
14 | this.Components.CollectionChanged += (object _, NotifyCollectionChangedEventArgs args) => {
15 | changeDelegate();
16 | if (args.OldItems != null) {
17 | foreach (object item in args.OldItems) {
18 | ((Component)item).RuleChanged -= changeDelegate;
19 | }
20 | }
21 | if (args.NewItems != null) {
22 | foreach (object item in args.NewItems) {
23 | ((Component)item).RuleChanged += changeDelegate;
24 | }
25 | }
26 | };
27 | }
28 |
29 | public PasswordRule(int length, IEnumerable components, string excludeCharacters) : this() {
30 | this.Length = length;
31 | this.SetComponents(components);
32 | this.ExcludeCharacters = excludeCharacters;
33 | }
34 |
35 | private PasswordRule(PasswordRule other) : this() {
36 | this.Length = other.Length;
37 | this.ExcludeCharacters = other.ExcludeCharacters;
38 | this.Components = new ObservableCollection(other.Components.Select((Component component) => component.Clone()));
39 | }
40 |
41 | private int _length = (int)KeePass.Program.Config.PasswordGenerator.AutoGeneratedPasswordsProfile.Length;
42 | public int Length {
43 | get => this._length;
44 | set {
45 | if (this._length != value) {
46 | this._length = value;
47 | this.NotifyPropertyChanged(nameof(this.Length));
48 | }
49 | }
50 | }
51 |
52 | private string _excludeCharacters = string.Empty;
53 | public string ExcludeCharacters {
54 | get => this._excludeCharacters;
55 | set {
56 | if (this._excludeCharacters != value) {
57 | this._excludeCharacters = value;
58 | this.NotifyPropertyChanged(nameof(this.ExcludeCharacters));
59 | }
60 | }
61 | }
62 |
63 | public ObservableCollection Components { get; } = new ObservableCollection();
64 |
65 | public void SetComponents(IEnumerable newComponents) {
66 | if (newComponents == null) {
67 | throw new ArgumentNullException(nameof(newComponents));
68 | }
69 | this.Components.Clear();
70 | foreach (Component component in newComponents) {
71 | this.Components.Add(component);
72 | }
73 | }
74 |
75 | public string NewPassword() {
76 | List password = new List();
77 | foreach (Component component in this.Components.Where((Component component) => component.Required)) {
78 | HashSet chars = CharacterClass.SplitString(component.CharacterClass?.Characters ?? string.Empty);
79 | foreach (string c in CharacterClass.SplitString(this.ExcludeCharacters)) {
80 | _ = chars.Remove(c);
81 | }
82 | if (chars.Count > 0) {
83 | password.Add(Random.RandomItem(chars));
84 | }
85 | }
86 | this.FillToLength(password);
87 | Random.Shuffle(password);
88 | return string.Join(string.Empty, password);
89 | }
90 |
91 | private void FillToLength(List password) {
92 | if (password.Count >= this.Length) {
93 | return;
94 | }
95 | HashSet allCharacters = this.AllCharacters();
96 | if (allCharacters.Count > 0) {
97 | while (password.Count < this.Length) {
98 | password.Add(Random.RandomItem(allCharacters));
99 | }
100 | }
101 | return;
102 | }
103 |
104 | public HashSet AllCharacters() {
105 | HashSet chars = new HashSet();
106 | List components = this.Components.Where((Component component) => component.CharacterClass != null).ToList();
107 | foreach (string charSet in
108 | components.Count > 0
109 | ? components.Select((Component component) => component.CharacterClass?.Characters ?? string.Empty)
110 | : new List() { CharacterClass.AllCharacters.Characters }
111 | ) {
112 | chars.UnionWith(CharacterClass.SplitString(charSet));
113 | }
114 | foreach (string c in CharacterClass.SplitString(this.ExcludeCharacters)) {
115 | _ = chars.Remove(c);
116 | }
117 | return chars;
118 | }
119 |
120 | public PasswordRule Clone() => new PasswordRule(this);
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/Random.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using KeePassLib.Cryptography;
4 |
5 | namespace RuleBuilder.Rule {
6 | public static class Random {
7 | public static T RandomItem(IEnumerable set) {
8 | List list = new List(set);
9 | return list[RandomNumber(list.Count)];
10 | }
11 | public static void Shuffle(List list) {
12 | if (list == null) {
13 | throw new ArgumentNullException(nameof(list));
14 | }
15 | for (int i = 0; i < list.Count; i++) {
16 | int swapIndex = RandomNumber(list.Count - i) + i;
17 | T temp = list[i];
18 | list[i] = list[swapIndex];
19 | list[swapIndex] = temp;
20 | }
21 | }
22 | private static int RandomNumber(int range) {
23 | if (range <= 0) {
24 | throw new ArgumentOutOfRangeException(nameof(range));
25 | }
26 | uint uiRange = (uint)range;
27 | uint max = uint.MaxValue - ((uint.MaxValue - uiRange + 1) % uiRange);
28 | uint number;
29 | do {
30 | number = BitConverter.ToUInt32(CryptoRandom.Instance.GetRandomBytes(sizeof(uint)), 0);
31 | } while (number > max);
32 | return (int)(number % uiRange);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/RuleProperty.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace RuleBuilder.Rule {
4 | public delegate void RuleChangedDelegate();
5 | public abstract class RuleProperty : INotifyPropertyChanged {
6 | public event PropertyChangedEventHandler PropertyChanged = (_1, _2) => { };
7 | public event RuleChangedDelegate RuleChanged = () => { };
8 | public void NotifyPropertyChanged(string property, bool affectsRule = true) {
9 | this.PropertyChanged(this, new PropertyChangedEventArgs(property));
10 | if (affectsRule) {
11 | this.RuleChanged();
12 | }
13 | }
14 | public void NotifyRuleChanged() {
15 | this.RuleChanged();
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/Serialization/CharacterClassContract.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 |
4 | namespace RuleBuilder.Rule.Serialization {
5 | [DataContract]
6 | public class CharacterClassContract {
7 | public CharacterClassContract(CharacterClass namedSet) {
8 | if (namedSet == null) {
9 | throw new ArgumentNullException(nameof(namedSet));
10 | }
11 | this.CharacterClass = namedSet.Enumeration;
12 | if (this.CharacterClass == CharacterClassEnum.Custom) {
13 | this.Characters = namedSet.Characters;
14 | }
15 | }
16 |
17 | [DataMember(EmitDefaultValue = false)]
18 | public string Characters { get; private set; }
19 |
20 | [DataMember]
21 | public CharacterClassEnum CharacterClass { get; private set; }
22 |
23 | public CharacterClass DeserializedObject() => this.CharacterClass == CharacterClassEnum.Custom ?
24 | new CharacterClass(this.Characters)
25 | : Rule.CharacterClass.EnumeratedCharacterClass(this.CharacterClass);
26 | }
27 | }
--------------------------------------------------------------------------------
/RuleBuilder/Rule/Serialization/ComponentContract.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 |
4 | namespace RuleBuilder.Rule.Serialization {
5 | [DataContract]
6 | public class ComponentContract {
7 | public ComponentContract(Component component) {
8 | if (component == null) {
9 | throw new ArgumentNullException(nameof(component));
10 | }
11 | this.CharacterSet = new CharacterClassContract(component.CharacterClass);
12 | this.Required = component.Required;
13 | }
14 | [DataMember]
15 | public CharacterClassContract CharacterSet { get; private set; }
16 | [DataMember(EmitDefaultValue = false)]
17 | public bool Required { get; private set; }
18 | public Component DeserializedObject() => new Component(this.CharacterSet.DeserializedObject(), this.Required);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/Serialization/ConfigurationContract.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 |
4 | namespace RuleBuilder.Rule.Serialization {
5 | [DataContract]
6 | public class ConfigurationContract {
7 | public ConfigurationContract(Configuration config) {
8 | if (config == null) {
9 | throw new ArgumentNullException(nameof(config));
10 | }
11 | if (config.Generator is PasswordRule) {
12 | this.Rule = new RuleContract((PasswordRule)config.Generator);
13 | } else if (config.Generator is PasswordProfile) {
14 | this.Profile = new ProfileContract((PasswordProfile)config.Generator);
15 | }
16 | this.Expiration = config.Expiration != null ? new ExpirationContract(config.Expiration) : null;
17 | }
18 |
19 | [DataMember(EmitDefaultValue = false)]
20 | public RuleContract Rule { get; private set; }
21 |
22 | [DataMember(EmitDefaultValue = false)]
23 | public ProfileContract Profile { get; private set; }
24 |
25 | [DataMember(EmitDefaultValue = false)]
26 | public ExpirationContract Expiration { get; private set; }
27 |
28 | public Configuration DeserializedObject() => new Configuration(
29 | (IPasswordGenerator)this.Rule?.DeserializedObject() ?? this.Profile.DeserializedObject(),
30 | this.Expiration?.DeserializedObject()
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/Serialization/Entry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Runtime.Serialization.Json;
4 | using System.Text;
5 | using KeePassLib;
6 | using KeePassLib.Security;
7 |
8 | namespace RuleBuilder.Rule.Serialization {
9 | public static class Entry {
10 | public const string PasswordRuleKey = "Password Rule";
11 |
12 | public static Configuration EntryDefaultConfiguration(PwEntry entry) {
13 | if (entry == null) {
14 | throw new ArgumentNullException(nameof(entry));
15 | }
16 | Configuration config = EntryConfiguration(entry);
17 | if (config != null) {
18 | return config;
19 | }
20 | PwGroup group = entry.ParentGroup;
21 | while (group != null) {
22 | config = GroupConfiguration(group);
23 | if (config != null) {
24 | return config;
25 | }
26 | group = group.ParentGroup;
27 | }
28 | return new Configuration();
29 | }
30 |
31 | public static Configuration EntryConfiguration(PwEntry entry) {
32 | if (entry == null) {
33 | throw new ArgumentNullException(nameof(entry));
34 | }
35 | try {
36 | ProtectedString configStr = entry.Strings.Get(PasswordRuleKey);
37 | return configStr != null ? DeserializedConfiguration(configStr.ReadString()) : null;
38 | } catch {
39 | return null;
40 | }
41 | }
42 |
43 | public static void SetEntryConfiguration(PwEntry entry, Configuration config) {
44 | if (entry == null) {
45 | throw new ArgumentNullException(nameof(entry));
46 | }
47 | if (config == null) {
48 | throw new ArgumentNullException(nameof(config));
49 | }
50 | if (config.IsDefaultConfiguration()) {
51 | entry.Strings.Remove(PasswordRuleKey);
52 | } else {
53 | entry.Strings.Set(PasswordRuleKey, new ProtectedString(false, SerializedConfiguration(config)));
54 | }
55 | }
56 |
57 | public static Configuration GroupConfiguration(PwGroup group) {
58 | if (group == null) {
59 | throw new ArgumentNullException(nameof(group));
60 | }
61 | try {
62 | string configStr = group.CustomData.Get(PasswordRuleKey);
63 | return configStr != null ? DeserializedConfiguration(configStr) : null;
64 | } catch {
65 | return null;
66 | }
67 | }
68 |
69 | public static void SetGroupConfiguration(PwGroup group, Configuration config) {
70 | if (group == null) {
71 | throw new ArgumentNullException(nameof(group));
72 | }
73 | if (config == null) {
74 | throw new ArgumentNullException(nameof(config));
75 | }
76 | if (config.Expiration == null && config.Generator is PasswordProfile && ((PasswordProfile)config.Generator).IsDefaultProfile) {
77 | group.CustomData.Remove(PasswordRuleKey);
78 | } else {
79 | group.CustomData.Set(PasswordRuleKey, SerializedConfiguration(config));
80 | }
81 | }
82 |
83 | public static string SerializedConfiguration(Configuration config) {
84 | using (StreamReader reader = new StreamReader(new MemoryStream(), Encoding.UTF8)) {
85 | new DataContractJsonSerializer(typeof(ConfigurationContract)).WriteObject(reader.BaseStream, new ConfigurationContract(config));
86 | reader.BaseStream.Position = 0;
87 | return reader.ReadToEnd();
88 | }
89 | }
90 |
91 | public static Configuration DeserializedDefaultConfiguration(string generatorStr, PwGroup parent) {
92 | if (generatorStr != null) {
93 | return DeserializedConfiguration(generatorStr);
94 | }
95 | PwGroup group = parent;
96 | while (group != null) {
97 | Configuration config = GroupConfiguration(group);
98 | if (config != null) {
99 | return config;
100 | }
101 | group = group.ParentGroup;
102 | }
103 | return new Configuration();
104 | }
105 |
106 | public static Configuration DeserializedConfiguration(string generatorStr) {
107 | using (StreamWriter writer = new StreamWriter(new MemoryStream(), new UTF8Encoding(false)) {
108 | AutoFlush = true
109 | }) {
110 | writer.Write(generatorStr);
111 | writer.BaseStream.Position = 0;
112 | return ((ConfigurationContract)new DataContractJsonSerializer(typeof(ConfigurationContract)).ReadObject(writer.BaseStream)).DeserializedObject();
113 | }
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/Serialization/ExpirationContract.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 |
4 | namespace RuleBuilder.Rule.Serialization {
5 | [DataContract]
6 | public class ExpirationContract {
7 | public ExpirationContract(Expiration expiration) {
8 | if (expiration == null) {
9 | throw new ArgumentNullException(nameof(expiration));
10 | }
11 | this.Unit = expiration.Unit;
12 | this.Length = expiration.Length;
13 | }
14 |
15 | [DataMember]
16 | public ExpirationUnit Unit { get; private set; }
17 |
18 | [DataMember]
19 | public int Length { get; private set; }
20 |
21 | public Expiration DeserializedObject() => new Expiration(this.Unit, this.Length);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/Serialization/ProfileContract.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 |
4 | namespace RuleBuilder.Rule.Serialization {
5 | [DataContract]
6 | public class ProfileContract {
7 | public ProfileContract(PasswordProfile profile) {
8 | if (profile == null) {
9 | throw new ArgumentNullException(nameof(profile));
10 | }
11 | this.IsDefault = profile.IsDefaultProfile;
12 | if (!this.IsDefault) {
13 | this.Name = profile.Name;
14 | }
15 | }
16 | [DataMember(EmitDefaultValue = false)]
17 | public bool IsDefault { get; private set; }
18 | [DataMember(EmitDefaultValue = false)]
19 | public string Name { get; private set; }
20 | public PasswordProfile DeserializedObject() => this.IsDefault ? PasswordProfile.DefaultProfile : PasswordProfile.NamedProfile(this.Name);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/RuleBuilder/Rule/Serialization/RuleContract.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 | using System.Runtime.Serialization;
6 | using RuleBuilder.Properties;
7 |
8 | namespace RuleBuilder.Rule.Serialization {
9 | [DataContract]
10 | public class RuleContract {
11 | public RuleContract(PasswordRule rule) {
12 | if (rule == null) {
13 | throw new ArgumentNullException(nameof(rule));
14 | }
15 | this.Length = rule.Length;
16 | this.Components = rule.Components
17 | .Where((Component component) => component.CharacterClass != null)
18 | .Select((Component component) => new ComponentContract(component))
19 | .ToList();
20 | this.Exclude = rule.ExcludeCharacters;
21 | }
22 | [DataMember]
23 | public int Length { get; private set; }
24 | [DataMember]
25 | public List Components { get; private set; }
26 | [DataMember]
27 | public string Exclude { get; private set; }
28 | public PasswordRule DeserializedObject() {
29 | if (this.Length < 0) {
30 | throw new SerializationException(Resources.PasswordLengthMustNotBeNegative);
31 | }
32 | return new PasswordRule(
33 | this.Length,
34 | new ObservableCollection(
35 | this.Components.ConvertAll((ComponentContract component) => component.DeserializedObject())
36 | ),
37 | this.Exclude
38 | );
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/RuleBuilder/RuleBuilder.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {6A61D4EB-BA26-4E32-906E-DEF1280163AD}
8 | Library
9 | RuleBuilder
10 | RuleBuilder
11 | v4.7.2
12 | 512
13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 4
15 | true
16 | true
17 |
18 |
19 |
20 |
21 |
22 | AnyCPU
23 | true
24 | full
25 | false
26 | bin\Debug\
27 | DEBUG;TRACE
28 | prompt
29 | 4
30 |
31 |
32 | AnyCPU
33 | pdbonly
34 | true
35 | bin\Release\
36 | TRACE
37 | prompt
38 | 4
39 |
40 |
41 |
42 | true
43 | AllEnabledByDefault
44 |
45 |
46 |
47 | False
48 | ..\..\KeePass-2.40\KeePass.exe
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | 4.0
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | ChangePassword.xaml
72 |
73 |
74 | True
75 | True
76 | Images.resx
77 |
78 |
79 | True
80 | True
81 | ImagesHigh.resx
82 |
83 |
84 | True
85 | True
86 | Resources.resx
87 |
88 |
89 |
90 |
91 |
92 |
93 | EditRule.xaml
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | Code
119 |
120 |
121 | ResXFileCodeGenerator
122 | Images.Designer.cs
123 |
124 |
125 | ResXFileCodeGenerator
126 | ImagesHigh.Designer.cs
127 |
128 |
129 | ResXFileCodeGenerator
130 | Resources.Designer.cs
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | Designer
139 | MSBuild:Compile
140 |
141 |
142 | Designer
143 | MSBuild:Compile
144 |
145 |
146 | Designer
147 | MSBuild:Compile
148 |
149 |
150 | Designer
151 | MSBuild:Compile
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 | copy $(TargetPath) "$(SolutionDir)\..\KeePass-2.50\Plugins"
181 |
182 |
--------------------------------------------------------------------------------
/RuleBuilder/RuleBuilderExt.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Drawing;
4 | using System.Windows.Forms;
5 | using KeePass.Plugins;
6 | using KeePassLib;
7 | using RuleBuilder.Properties;
8 | using RuleBuilder.Rule;
9 | using RuleBuilder.Rule.Serialization;
10 |
11 | namespace RuleBuilder {
12 | public class RuleBuilderExt : Plugin {
13 | private IPluginHost host;
14 | public override bool Initialize(IPluginHost host) {
15 | this.host = host;
16 | Util.ScaledResourceManager.Initialize();
17 | foreach (ToolStripItem item in new ToolStripItem[] {
18 | new ToolStripSeparator(),
19 | MenuItem(Resources.GenerateNewPassword, this.ShowChangePassword, Images.Dice),
20 | MenuItem(Resources.EditPasswordRule, this.ShowChangeRule, null)
21 | }) {
22 | _ = this.host.MainWindow.EntryContextMenu.Items.Add(item);
23 | this.host.MainWindow.EntryContextMenu.Opening += (object sender, CancelEventArgs args) => {
24 | item.Visible = this.host.MainWindow.GetSelectedEntriesCount() == 1;
25 | };
26 | }
27 | _ = this.host.MainWindow.GroupContextMenu.Items.Add(
28 | MenuItem(Resources.EditPasswordRule, this.ShowGroupChangeRule, null)
29 | );
30 |
31 | Forms.EntryFormMod.RegisterEntryForm(host);
32 |
33 | return base.Initialize(host);
34 | }
35 |
36 | public override string UpdateUrl => "https://raw.githubusercontent.com/ihanson/KeePass-Rule-Builder/main/RuleBuilder/version.txt";
37 |
38 | internal static ToolStripMenuItem MenuItem(string text, Action action, Image image) => new ToolStripMenuItem(text, image, (object _1, EventArgs _2) => action());
39 |
40 | private void ShowChangePassword() {
41 | PwEntry entry = this.host.MainWindow.GetSelectedEntry(true);
42 | if (entry != null) {
43 | if (Forms.ChangePassword.ShowChangePasswordDialog(this.host.MainWindow, entry)) {
44 | this.RefreshEntries();
45 | }
46 | }
47 | }
48 |
49 | private void ShowChangeRule() {
50 | PwEntry entry = this.host.MainWindow.GetSelectedEntry(true);
51 | if (entry != null) {
52 | Configuration config = Entry.EntryConfiguration(entry);
53 | if (Forms.EditRule.ShowRuleDialog(this.host.MainWindow, ref config)) {
54 | Entry.SetEntryConfiguration(entry, config);
55 | entry.Touch(true);
56 | this.RefreshEntries();
57 | }
58 | }
59 | }
60 |
61 | private void ShowGroupChangeRule() {
62 | PwGroup group = this.host.MainWindow.GetSelectedGroup();
63 | if (group != null) {
64 | Configuration config = Entry.GroupConfiguration(group);
65 | if (Forms.EditRule.ShowRuleDialog(this.host.MainWindow, ref config)) {
66 | Entry.SetGroupConfiguration(group, config);
67 | group.Touch(true);
68 | this.RefreshEntries();
69 | }
70 | }
71 | }
72 |
73 | private void RefreshEntries() {
74 | this.host.MainWindow.RefreshEntriesList();
75 | this.host.MainWindow.UpdateUI(false, null, false, null, false, null, true);
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/RuleBuilder/Util/ComponentTemplateSelector.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls;
3 |
4 | namespace RuleBuilder.Util {
5 | public class ComponentTemplateSelector : DataTemplateSelector {
6 | public override DataTemplate SelectTemplate(object item, DependencyObject container) {
7 | Rule.Component component = item as Rule.Component;
8 | if (component == null) {
9 | return null;
10 | }
11 | if (component.CharacterClass == null) {
12 | return this.BlankCell;
13 | }
14 | return component.CharacterClass?.Enumeration == Rule.CharacterClassEnum.Custom && this.CustomCharacterCell != null
15 | ? this.CustomCharacterCell
16 | : this.CharacterClassCell;
17 | }
18 | public DataTemplate CharacterClassCell { get; set; }
19 | public DataTemplate CustomCharacterCell { get; set; }
20 | public DataTemplate BlankCell { get; set; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/RuleBuilder/Util/Entropy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using RuleBuilder.Rule;
5 |
6 | namespace RuleBuilder.Util {
7 | public static class Entropy {
8 | private const int MaxRequired = 5;
9 |
10 | public static double EntropyBits(PasswordRule rule) {
11 | if (rule == null) {
12 | throw new ArgumentNullException(nameof(rule));
13 | }
14 | HashSet allCharacters = rule.AllCharacters();
15 | double numTotalPasswords = Math.Pow(allCharacters.Count, rule.Length);
16 | List requiredSets = RequiredCharacterSets(rule);
17 | if (requiredSets.Count > MaxRequired) {
18 | throw new ArgumentOutOfRangeException(nameof(rule), "Too many required character sets to calculate the entropy");
19 | }
20 | double numInvalidPasswords = 0.0;
21 | for (int n = 1; n <= requiredSets.Count; n++) {
22 | foreach (List required in SubsetsOfLength(requiredSets, n)) {
23 | numInvalidPasswords += CountPasswordsNotContaining(
24 | allCharacters,
25 | rule.Length,
26 | required
27 | ) * (n % 2 == 1 ? 1.0 : -1.0);
28 | }
29 | }
30 | double result = Math.Log(numTotalPasswords - numInvalidPasswords, 2.0);
31 | if (double.IsInfinity(result) || double.IsNaN(result)) {
32 | throw new ArgumentOutOfRangeException(nameof(rule), "Entropy is too large to be calculated");
33 | }
34 | return result;
35 | }
36 |
37 | private static List RequiredCharacterSets(PasswordRule rule) {
38 | HashSet allCharacters = rule.AllCharacters();
39 | var sets = new List();
40 | foreach (Component component in rule.Components.Where((Component component) => component.Required)) {
41 | HashSet chars = CharacterClass.SplitString(component.CharacterClass?.Characters ?? string.Empty);
42 | chars.IntersectWith(allCharacters);
43 | if (chars.Count > 0) {
44 | AppendOrUpdate(sets, chars);
45 | }
46 | }
47 | return sets;
48 | }
49 |
50 | private static void AppendOrUpdate(List list, HashSet chars) {
51 | foreach (CharacterSetCount count in list) {
52 | if (count.Characters.SetEquals(chars)) {
53 | count.Increment();
54 | return;
55 | }
56 | }
57 | list.Add(new CharacterSetCount(chars));
58 | }
59 |
60 | private static IEnumerable> SubsetsOfLength(List list, int length, int start = 0) {
61 | if (length == 0) {
62 | yield return new List();
63 | } else {
64 | for (int i = start; i + length - 1 < list.Count; i++) {
65 | foreach (List set in SubsetsOfLength(list, length - 1, i + 1)) {
66 | yield return set.Prepend(list[i]).ToList();
67 | }
68 | }
69 | }
70 | }
71 |
72 | private static double CountPasswordsNotContaining(HashSet allCharacters, int passwordLength, List charSets) {
73 | int otherCharCount = allCharacters.Where((string character) => !charSets.Any((CharacterSetCount charSet) => charSet.Characters.Contains(character))).Count();
74 | if (otherCharCount == 0) {
75 | return 0.0;
76 | }
77 | double total = 0.0;
78 | foreach (List shortCount in ShortCounts(charSets)) {
79 | int remainingLength = passwordLength;
80 | double passwordCount = 1.0;
81 | foreach (CharacterSetCount charSet in shortCount) {
82 | passwordCount *= Combinations(remainingLength, charSet.Count) * Math.Pow(charSet.Characters.Count, charSet.Count);
83 | remainingLength -= charSet.Count;
84 | }
85 | passwordCount *= Math.Pow(otherCharCount, remainingLength);
86 | total += passwordCount;
87 | }
88 | return total;
89 | }
90 |
91 | private static IEnumerable> ShortCounts(List charSets, int start = 0) {
92 | if (start >= charSets.Count) {
93 | yield return new List();
94 | } else {
95 | for (int n = 0; n < charSets[start].Count; n++) {
96 | foreach (List list in ShortCounts(charSets, start + 1)) {
97 | yield return list.Prepend(charSets[0].WithCount(n)).ToList();
98 | }
99 | }
100 | }
101 | }
102 |
103 | private static double Combinations(int supersetSize, int subsetSize) {
104 | double numerator = 1.0;
105 | for (int n = supersetSize; n > (supersetSize - subsetSize); n--) {
106 | numerator *= n;
107 | }
108 | double denominator = 1.0;
109 | for (int n = 2; n <= subsetSize; n++) {
110 | denominator *= n;
111 | }
112 | return numerator / denominator;
113 | }
114 | }
115 |
116 | class CharacterSetCount {
117 | public CharacterSetCount(HashSet characters) {
118 | this.Characters = characters;
119 | }
120 |
121 | public HashSet Characters { get; }
122 |
123 | public int Count { get; private set; } = 1;
124 |
125 | public CharacterSetCount WithCount(int count) => new CharacterSetCount(this.Characters) {
126 | Count = count
127 | };
128 |
129 | public void Increment() {
130 | this.Count++;
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/RuleBuilder/Util/Hotkey.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 | using System.Windows;
4 | using System.Windows.Forms;
5 | using System.Windows.Interop;
6 | using RuleBuilder.Properties;
7 |
8 | namespace RuleBuilder.Util {
9 | internal static class HotKey {
10 | private enum Modifier {
11 | Alt = 0x0001,
12 | Ctrl = 0x0002,
13 | Shift = 0x0004,
14 | Windows = 0x0005,
15 | NoRepeat = 0x4000
16 | }
17 | private static int ID { get; set; }
18 | public static int RegisterHotKey(Window window, Keys keys) {
19 | if (!NativeMethods.RegisterHotKey(new WindowInteropHelper(window).Handle, ++ID, Modifiers(keys), (uint)(keys & Keys.KeyCode))) {
20 | throw new HotKeyException(Resources.UnableToRegisterHotkey);
21 | }
22 | return ID;
23 | }
24 | public static void UnregisterHotKey(Window window, int id) {
25 | NativeMethods.UnregisterHotKey(new WindowInteropHelper(window).Handle, id);
26 | }
27 | private static uint Modifiers(Keys keys) {
28 | uint result = 0;
29 | if ((keys & Keys.Alt) != Keys.None) {
30 | result |= (uint)Modifier.Alt;
31 | }
32 | if ((keys & Keys.Control) != Keys.None) {
33 | result |= (uint)Modifier.Ctrl;
34 | }
35 | if ((keys & Keys.Shift) != Keys.None) {
36 | result |= (uint)Modifier.Shift;
37 | }
38 | return result;
39 | }
40 | }
41 | [Serializable]
42 | public class HotKeyException : Exception {
43 | public HotKeyException() : base() { }
44 |
45 | public HotKeyException(string message) : base(message) { }
46 |
47 | public HotKeyException(string message, Exception innerException) : base(message, innerException) { }
48 |
49 | protected HotKeyException(SerializationInfo info, StreamingContext context) : base(info, context) { }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/RuleBuilder/Util/IntegerRangeRule.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Controls;
4 |
5 | namespace RuleBuilder.Util {
6 | public class IntegerRangeRule : ValidationRule {
7 | public int? Min { get; set; }
8 | public int? Max { get; set; }
9 | public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
10 | try {
11 | int valInt = int.Parse((string)value, NumberFormatInfo.GetInstance(CultureInfo.CurrentCulture));
12 | if (!this.ValueInRange(valInt)) {
13 | return new ValidationResult(false, null);
14 | }
15 | } catch (FormatException) {
16 | return new ValidationResult(false, null);
17 | }
18 | return ValidationResult.ValidResult;
19 | }
20 | private bool ValueInRange(int value) {
21 | if (this.Min != null && value < this.Min) {
22 | return false;
23 | }
24 | if (this.Max != null && value > this.Max) {
25 | return false;
26 | }
27 | return true;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/RuleBuilder/Util/NativeMethods.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace RuleBuilder.Util {
5 | internal class NativeMethods {
6 | [DllImport("user32")]
7 | public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
8 | [DllImport("user32")]
9 | public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
10 | [DllImport("user32")]
11 | public static extern short GetKeyState(int nVertKey);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/RuleBuilder/Util/ScaledResourceManager.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Drawing;
3 | using System.Globalization;
4 | using System.Reflection;
5 | using System.Resources;
6 | using KeePass.UI;
7 | using KeePassLib;
8 | using KeePassLib.Utility;
9 | using RuleBuilder.Properties;
10 |
11 | namespace RuleBuilder.Util {
12 | class ScaledResourceManager : ResourceManager {
13 | private Dictionary Icons { get; } = new Dictionary();
14 |
15 | private ResourceManager LowRM { get; }
16 |
17 | private ScaledResourceManager(ResourceManager lowRM) {
18 | this.LowRM = lowRM;
19 | }
20 |
21 | public static void Initialize() {
22 | ResourceManager currentRM = Images.ResourceManager;
23 | if (DpiUtil.ScalingRequired && !(currentRM is ScaledResourceManager)) {
24 | PropertyInfo prop = typeof(Images).GetProperty(nameof(Images.ResourceManager), BindingFlags.Static | BindingFlags.NonPublic);
25 | prop.GetValue(null, null);
26 |
27 | FieldInfo field = typeof(Images).GetField("resourceMan", BindingFlags.Static | BindingFlags.NonPublic);
28 | field.SetValue(null, new ScaledResourceManager(currentRM));
29 | }
30 | }
31 |
32 | public override object GetObject(string name) => this.GetObject(name, null);
33 |
34 | public override object GetObject(string name, CultureInfo _) {
35 | if (!this.Icons.ContainsKey(name)) {
36 | this.Icons[name] = ScaledImage(name);
37 | }
38 | return this.Icons[name];
39 | }
40 |
41 | private Image ScaledImage(string name) {
42 | Image low = (Image)this.LowRM.GetObject(name);
43 | Image high = (Image)ImagesHigh.ResourceManager.GetObject(name);
44 | return GfxUtil.ScaleImage(high, DpiUtil.ScaleIntX(low.Width), DpiUtil.ScaleIntY(low.Height), ScaleTransformFlags.UIIcon);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/RuleBuilder/version.txt:
--------------------------------------------------------------------------------
1 | :
2 | Rule Builder:3.4
3 | :
--------------------------------------------------------------------------------
/RuleBuilderTests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("RuleBuilderTests")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("RuleBuilderTests")]
12 | [assembly: AssemblyCopyright("Copyright © 2019")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("9a7ee14b-4e48-40a2-898e-1139329bfa03")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/RuleBuilderTests/Rule/CharacterClassTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 |
5 | namespace RuleBuilder.Rule.Tests {
6 | [TestClass]
7 | public class CharacterClassTests {
8 | [TestMethod]
9 | public void DefaultCharacterClassTest() {
10 | CharacterClass charClass = new CharacterClass();
11 | Assert.AreEqual(string.Empty, charClass.Characters);
12 | Assert.AreEqual(CharacterClassEnum.Custom, charClass.Enumeration);
13 | }
14 |
15 | [TestMethod]
16 | public void CustomCharacterClassTest() {
17 | CharacterClass charClass = new CharacterClass("abc");
18 | Assert.AreEqual("abc", charClass.Characters);
19 | Assert.AreEqual(CharacterClassEnum.Custom, charClass.Enumeration);
20 | }
21 |
22 | [TestMethod]
23 | public void EnumeratedCharacterClassTest() {
24 | Assert.AreSame(CharacterClass.AllCharacters, CharacterClass.EnumeratedCharacterClass(CharacterClassEnum.AllCharacters));
25 | Assert.AreSame(CharacterClass.Letters, CharacterClass.EnumeratedCharacterClass(CharacterClassEnum.Letters));
26 | Assert.AreSame(CharacterClass.Digits, CharacterClass.EnumeratedCharacterClass(CharacterClassEnum.Digits));
27 | Assert.AreSame(CharacterClass.Punctuation, CharacterClass.EnumeratedCharacterClass(CharacterClassEnum.Punctuation));
28 | Assert.AreSame(CharacterClass.UppercaseLetters, CharacterClass.EnumeratedCharacterClass(CharacterClassEnum.UppercaseLetters));
29 | Assert.AreSame(CharacterClass.LowercaseLetters, CharacterClass.EnumeratedCharacterClass(CharacterClassEnum.LowercaseLetters));
30 | _ = Assert.ThrowsException(() => {
31 | _ = CharacterClass.EnumeratedCharacterClass((CharacterClassEnum)99);
32 | });
33 | }
34 |
35 | [TestMethod]
36 | public void SplitStringTest() => Assert.IsTrue(new HashSet { "a", "b", "c", "🧸", "🐱", "\x200d", "👤" }.SetEquals(CharacterClass.SplitString("abc🧸🐱👤")));
37 | }
38 | }
--------------------------------------------------------------------------------
/RuleBuilderTests/Rule/ComponentTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 |
3 | namespace RuleBuilder.Rule.Tests {
4 | [TestClass]
5 | public class ComponentTests {
6 | [TestMethod]
7 | public void ComponentTest() {
8 | Component component = new Component(CharacterClass.Letters, true);
9 | Assert.AreEqual(CharacterClassEnum.Letters, component.CharacterClass.Enumeration);
10 | Assert.IsTrue(component.Required);
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/RuleBuilderTests/Rule/ExpirationTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using RuleBuilder.Rule;
4 |
5 | namespace RuleBuilderTests.Rule {
6 | [TestClass]
7 | public class ExpirationTests {
8 | [TestMethod]
9 | public void ExpirationTest() {
10 | Expiration expiration = new Expiration(ExpirationUnit.Days, 20);
11 | Assert.AreEqual(ExpirationUnit.Days, expiration.Unit);
12 | Assert.AreEqual(20, expiration.Length);
13 | _ = Assert.ThrowsException(() => new Expiration((ExpirationUnit)300, 300));
14 | _ = Assert.ThrowsException(() => new Expiration(ExpirationUnit.Months, 0));
15 | _ = Assert.ThrowsException(() => new Expiration(ExpirationUnit.Months, 1000));
16 | }
17 |
18 | [TestMethod]
19 | public void DateFromTest() {
20 | DateTime origin = new DateTime(2021, 5, 31);
21 | Assert.AreEqual(new DateTime(2021, 6, 12), new Expiration(ExpirationUnit.Days, 12).DateFrom(origin));
22 | Assert.AreEqual(new DateTime(2021, 10, 18), new Expiration(ExpirationUnit.Weeks, 20).DateFrom(origin));
23 | Assert.AreEqual(new DateTime(2021, 6, 30), new Expiration(ExpirationUnit.Months, 1).DateFrom(origin));
24 | Assert.AreEqual(new DateTime(2021, 7, 31), new Expiration(ExpirationUnit.Months, 2).DateFrom(origin));
25 | Assert.AreEqual(new DateTime(2022, 2, 28), new Expiration(ExpirationUnit.Months, 9).DateFrom(origin));
26 | Assert.AreEqual(new DateTime(2022, 5, 31), new Expiration(ExpirationUnit.Years, 1).DateFrom(origin));
27 | Assert.AreEqual(DateTime.MaxValue, new Expiration(ExpirationUnit.Years, 100).DateFrom(new DateTime(9990, 1, 1)));
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/RuleBuilderTests/Rule/PasswordProfileTests.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 | using KeePass.Resources;
3 | using KeePassLib.Cryptography.PasswordGenerator;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 |
6 | namespace RuleBuilder.Rule.Tests {
7 | [TestClass]
8 | public class PasswordProfileTests {
9 | private static readonly string ProfileName = $"{KPRes.MacAddress} ({KPRes.BuiltIn})";
10 | [TestMethod]
11 | public void PasswordProfileTest() {
12 | PwProfile profile = new PwProfile();
13 | Assert.AreSame(profile, new PasswordProfile(profile).Profile);
14 | }
15 | [TestMethod]
16 | public void NamedProfileTest() => Assert.AreEqual(ProfileName, PasswordProfile.NamedProfile(ProfileName).Name);
17 | [TestMethod]
18 | public void NamedProfileNotFoundTest() => Assert.IsTrue(PasswordProfile.NamedProfile("xxxxxxxxxxxxx not a real profile xxxxxxxxxxxxx").IsDefaultProfile);
19 |
20 | [TestMethod]
21 | public void NewPasswordTest() => Assert.IsTrue(Regex.IsMatch(PasswordProfile.NamedProfile(ProfileName).NewPassword(), @"(?:[\dA-F]{2}-){5}[\dA-F]{2}"));
22 | }
23 | }
--------------------------------------------------------------------------------
/RuleBuilderTests/Rule/PasswordRuleTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 |
4 | namespace RuleBuilder.Rule.Tests {
5 | [TestClass]
6 | public class PasswordRuleTests {
7 | [TestMethod]
8 | public void NoComponentsTest() {
9 | Assert.AreEqual(16, new PasswordRule() {
10 | Length = 16
11 | }.NewPassword().Length);
12 | }
13 |
14 | [TestMethod]
15 | public void ComponentsTest() {
16 | string password = new PasswordRule(
17 | 32,
18 | new ObservableCollection() {
19 | new Component(new CharacterClass("a"), false),
20 | new Component(new CharacterClass("b"), true),
21 | new Component(new CharacterClass("cd"), true)
22 | },
23 | ""
24 | ).NewPassword();
25 | Assert.IsTrue(password.Contains("b"));
26 | Assert.IsTrue(password.Contains("c") || password.Contains("d"));
27 | Assert.AreEqual(32, password.Length);
28 | }
29 |
30 | [TestMethod]
31 | public void ExtraCharactersTest() {
32 | string password = new PasswordRule(
33 | 5,
34 | new ObservableCollection() {
35 | new Component(new CharacterClass("a"), true),
36 | new Component(new CharacterClass("bc"), true),
37 | new Component(new CharacterClass("d"), true),
38 | new Component(new CharacterClass("e"), true),
39 | new Component(new CharacterClass("f"), true),
40 | new Component(new CharacterClass("g"), true),
41 | new Component(new CharacterClass("h"), true),
42 | new Component(new CharacterClass("i"), false)
43 | },
44 | ""
45 | ).NewPassword();
46 | Assert.IsTrue(password.Contains("a"));
47 | Assert.IsTrue(password.Contains("b") || password.Contains("c"));
48 | Assert.IsTrue(password.Contains("d"));
49 | Assert.IsTrue(password.Contains("e"));
50 | Assert.IsTrue(password.Contains("f"));
51 | Assert.IsTrue(password.Contains("g"));
52 | Assert.IsTrue(password.Contains("h"));
53 | Assert.IsFalse(password.Contains("i"));
54 | Assert.AreEqual(7, password.Length);
55 | }
56 |
57 | [TestMethod]
58 | public void ExcludeTest() {
59 | string password = new PasswordRule(
60 | 32,
61 | new ObservableCollection() {
62 | new Component(new CharacterClass("abc"), false)
63 | },
64 | "ab"
65 | ).NewPassword();
66 | Assert.AreEqual(0, CharCount(password, 'a'));
67 | Assert.AreEqual(0, CharCount(password, 'b'));
68 | Assert.AreEqual(32, CharCount(password, 'c'));
69 | }
70 |
71 | private static int CharCount(string str, char character) {
72 | int count = 0;
73 | foreach (char strChar in str) {
74 | if (strChar == character) {
75 | ++count;
76 | }
77 | }
78 | return count;
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/RuleBuilderTests/Rule/RandomTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 |
5 | namespace RuleBuilder.Rule.Tests {
6 | [TestClass]
7 | public class RandomTests {
8 | [TestMethod]
9 | public void RandomItemTest() {
10 | HashSet numbers = new HashSet { 1, 2, 3, 4, 5 };
11 | Assert.IsTrue(numbers.Contains(Random.RandomItem(numbers)));
12 | }
13 | [TestMethod]
14 | public void RandomItemFailTest() {
15 | _ = Assert.ThrowsException(() => {
16 | _ = Random.RandomItem(new HashSet { });
17 | });
18 | }
19 |
20 | [TestMethod]
21 | public void ShuffleTest() {
22 | List numbers = new List(100);
23 | for (int i = 0; i < 100; i++) {
24 | numbers.Add(i);
25 | }
26 | List shuffled = new List(numbers);
27 | Random.Shuffle(shuffled);
28 | Assert.AreEqual(numbers.Count, shuffled.Count);
29 | for (int i = 0; i < numbers.Count; i++) {
30 | Assert.IsTrue(shuffled.Contains(numbers[i]));
31 | }
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/RuleBuilderTests/Rule/Serialization/CharacterClassContractTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 |
3 | namespace RuleBuilder.Rule.Serialization.Tests {
4 | [TestClass]
5 | public class CharacterClassContractTests {
6 | [TestMethod]
7 | public void BuiltInCharacterSetTest() {
8 | CharacterClassContract charClass = new CharacterClassContract(CharacterClass.LowercaseLetters);
9 | Assert.AreEqual(CharacterClassEnum.LowercaseLetters, charClass.CharacterClass);
10 | Assert.IsNull(charClass.Characters);
11 | }
12 | [TestMethod]
13 | public void BuiltInCharacterSetObjectTest() {
14 | CharacterClass charClass = new CharacterClassContract(CharacterClass.LowercaseLetters).DeserializedObject();
15 | Assert.AreEqual(CharacterClassEnum.LowercaseLetters, charClass.Enumeration);
16 | Assert.AreEqual(CharacterClass.LowercaseLetters.Characters, charClass.Characters);
17 | }
18 | [TestMethod]
19 | public void CustomCharacterSetTest() {
20 | CharacterClassContract charClass = new CharacterClassContract(new CharacterClass("abc"));
21 | Assert.AreEqual(CharacterClassEnum.Custom, charClass.CharacterClass);
22 | Assert.AreEqual("abc", charClass.Characters);
23 | }
24 | [TestMethod]
25 | public void CustomCharacterSetObjectTest() {
26 | CharacterClass charClass = new CharacterClassContract(new CharacterClass("abc")).DeserializedObject();
27 | Assert.AreEqual(CharacterClassEnum.Custom, charClass.Enumeration);
28 | Assert.AreEqual("abc", charClass.Characters);
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/RuleBuilderTests/Rule/Serialization/ComponentContractTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 |
3 | namespace RuleBuilder.Rule.Serialization.Tests {
4 | [TestClass]
5 | public class ComponentContractTests {
6 | [TestMethod]
7 | public void ComponentTest() {
8 | ComponentContract component = new ComponentContract(new Component(CharacterClass.Letters, true));
9 | Assert.AreEqual(CharacterClassEnum.Letters, component.CharacterSet.CharacterClass);
10 | Assert.IsTrue(component.Required);
11 | }
12 | [TestMethod]
13 | public void ComponentObjectTest() {
14 | Component component = new ComponentContract(new Component(CharacterClass.Letters, true)).DeserializedObject();
15 | Assert.AreEqual(CharacterClassEnum.Letters, component.CharacterClass.Enumeration);
16 | Assert.IsTrue(component.Required);
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/RuleBuilderTests/Rule/Serialization/ConfigurationContractTests.cs:
--------------------------------------------------------------------------------
1 | using KeePassLib.Cryptography.PasswordGenerator;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 |
4 | namespace RuleBuilder.Rule.Serialization.Tests {
5 | [TestClass]
6 | public class ConfigurationContractTests {
7 | [TestMethod]
8 | public void RuleTest() {
9 | ConfigurationContract configuration = new ConfigurationContract(new Configuration(new PasswordRule()));
10 | Assert.IsNotNull(configuration.Rule);
11 | Assert.IsNull(configuration.Profile);
12 | }
13 |
14 | [TestMethod]
15 | public void RuleObjectTest() {
16 | Configuration config = new ConfigurationContract(new Configuration(new PasswordRule())).DeserializedObject();
17 | Assert.IsInstanceOfType(config.Generator, typeof(PasswordRule));
18 | }
19 | [TestMethod]
20 | public void ProfileTest() {
21 | ConfigurationContract configuration = new ConfigurationContract(new Configuration(new PasswordProfile(new PwProfile())));
22 | Assert.IsNull(configuration.Rule);
23 | Assert.IsNotNull(configuration.Profile);
24 | }
25 |
26 | [TestMethod]
27 | public void ProfileObjectTest() {
28 | Configuration config = new ConfigurationContract(new Configuration(new PasswordProfile(new PwProfile()))).DeserializedObject();
29 | Assert.IsInstanceOfType(config.Generator, typeof(PasswordProfile));
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/RuleBuilderTests/Rule/Serialization/EntryTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using KeePass.Resources;
3 | using KeePassLib;
4 | using KeePassLib.Security;
5 | using Microsoft.VisualStudio.TestTools.UnitTesting;
6 |
7 | namespace RuleBuilder.Rule.Serialization.Tests {
8 | [TestClass]
9 | public class EntryTests {
10 | private const string PasswordRuleKey = "Password Rule";
11 | private static readonly string ProfileName = $"{KPRes.MacAddress} ({KPRes.BuiltIn})";
12 |
13 | [TestMethod]
14 | public void DecodeDefaultGeneratorTest() {
15 | PasswordProfile profile = (PasswordProfile)ConfigurationFromJSON(null).Generator;
16 | Assert.IsTrue(profile.IsDefaultProfile);
17 | }
18 |
19 | [TestMethod]
20 | public void DecodeInvalidJSONTest() {
21 | PasswordProfile profile = (PasswordProfile)ConfigurationFromJSON("{").Generator;
22 | Assert.IsTrue(profile.IsDefaultProfile);
23 | }
24 |
25 | [TestMethod]
26 | public void DecodeDefaultProfileTest() {
27 | PasswordProfile profile = (PasswordProfile)ConfigurationFromJSON("{Profile:{IsDefault:true}").Generator;
28 | Assert.IsTrue(profile.IsDefaultProfile);
29 | }
30 |
31 | [TestMethod]
32 | public void EncodeDefaultProfileTest() {
33 | PasswordProfile profile = (PasswordProfile)EncodeDecodeConfiguration(new Configuration()).Generator;
34 | Assert.IsTrue(profile.IsDefaultProfile);
35 | }
36 |
37 | [TestMethod]
38 | public void DecodeNamedProfileTest() {
39 | PasswordProfile profile = (PasswordProfile)ConfigurationFromJSON($"{{\"Profile\":{{\"Name\":{EncodeJSONString(ProfileName)}}}}}").Generator;
40 | Assert.IsFalse(profile.IsDefaultProfile);
41 | Assert.AreEqual(ProfileName, profile.Name);
42 | Assert.AreEqual(ProfileName, profile.Profile.Name);
43 | }
44 |
45 | [TestMethod]
46 | public void EncodeNamedProfileTest() {
47 | PasswordProfile profile = (PasswordProfile)EncodeDecodeConfiguration(new Configuration(
48 | new PasswordProfile(PasswordProfile.NamedProfile(ProfileName).Profile),
49 | new Expiration(ExpirationUnit.Months, 3)
50 | )).Generator;
51 | Assert.IsFalse(profile.IsDefaultProfile);
52 | Assert.AreEqual(ProfileName, profile.Name);
53 | Assert.AreEqual(ProfileName, profile.Profile.Name);
54 | }
55 |
56 | [TestMethod]
57 | public void DecodeRuleTest() {
58 | Configuration config = ConfigurationFromJSON(
59 | "{\"Rule\":{\"Length\":32,\"Components\":[{\"CharacterSet\":{\"CharacterClass\":2},\"Required\":true},{\"CharacterSet\":{\"Characters\":\"xyz\"}}],\"Exclude\":\"abc\"},\"Expiration\":{\"Unit\":2,\"Length\":3}"
60 | );
61 | PasswordRule rule = (PasswordRule)config.Generator;
62 | Assert.AreEqual(32, rule.Length);
63 | Assert.AreEqual(2, rule.Components.Count);
64 | Assert.AreEqual(CharacterClassEnum.Letters, rule.Components[0].CharacterClass.Enumeration);
65 | Assert.IsTrue(rule.Components[0].Required);
66 | Assert.AreEqual(CharacterClassEnum.Custom, rule.Components[1].CharacterClass.Enumeration);
67 | Assert.AreEqual("xyz", rule.Components[1].CharacterClass.Characters);
68 | Assert.IsFalse(rule.Components[1].Required);
69 | Assert.AreEqual("abc", rule.ExcludeCharacters);
70 | Assert.AreEqual(ExpirationUnit.Months, config.Expiration.Unit);
71 | Assert.AreEqual(3, config.Expiration.Length);
72 | }
73 |
74 | [TestMethod]
75 | public void EncodeRuleTest() {
76 | Configuration config = EncodeDecodeConfiguration(new Configuration(
77 | new PasswordRule(
78 | 32,
79 | new ObservableCollection() {
80 | new Component(CharacterClass.Letters, true),
81 | new Component(new CharacterClass("xyz"), false)
82 | },
83 | "abc"
84 | ),
85 | new Expiration(ExpirationUnit.Months, 3)
86 | ));
87 | PasswordRule rule = (PasswordRule)config.Generator;
88 | Assert.AreEqual(32, rule.Length);
89 | Assert.AreEqual(2, rule.Components.Count);
90 | Assert.AreEqual(CharacterClassEnum.Letters, rule.Components[0].CharacterClass.Enumeration);
91 | Assert.IsTrue(rule.Components[0].Required);
92 | Assert.AreEqual(CharacterClassEnum.Custom, rule.Components[1].CharacterClass.Enumeration);
93 | Assert.AreEqual("xyz", rule.Components[1].CharacterClass.Characters);
94 | Assert.IsFalse(rule.Components[1].Required);
95 | Assert.AreEqual("abc", rule.ExcludeCharacters);
96 | Assert.AreEqual(ExpirationUnit.Months, config.Expiration.Unit);
97 | Assert.AreEqual(3, config.Expiration.Length);
98 | }
99 |
100 | [TestMethod]
101 | public void GroupRuleTest() {
102 | PwGroup group1 = new PwGroup();
103 | PwGroup group2 = new PwGroup();
104 | PwGroup group3 = new PwGroup();
105 | PwEntry entry = new PwEntry(true, true);
106 |
107 | group1.AddGroup(group2, true);
108 | group2.AddGroup(group3, true);
109 | group3.AddEntry(entry, true);
110 | Entry.SetGroupConfiguration(
111 | group1,
112 | new Configuration(
113 | new PasswordRule(
114 | 4,
115 | new Component[] { new Component(new CharacterClass("a"), false) },
116 | string.Empty
117 | )
118 | )
119 | );
120 |
121 | PasswordRule rule = (PasswordRule)Entry.EntryDefaultConfiguration(entry).Generator;
122 | Assert.AreEqual("a", rule.Components[0].CharacterClass.Characters);
123 | }
124 |
125 | [TestMethod]
126 | public void GroupRuleOverrideTest() {
127 | PwGroup group1 = new PwGroup();
128 | PwGroup group2 = new PwGroup();
129 | PwGroup group3 = new PwGroup();
130 | PwEntry entry = new PwEntry(true, true);
131 |
132 | group1.AddGroup(group2, true);
133 | group2.AddGroup(group3, true);
134 | group3.AddEntry(entry, true);
135 | Entry.SetGroupConfiguration(
136 | group1,
137 | new Configuration(
138 | new PasswordRule(
139 | 4,
140 | new Component[] { new Component(new CharacterClass("a"), false) },
141 | string.Empty
142 | )
143 | )
144 | );
145 | Entry.SetEntryConfiguration(
146 | entry,
147 | new Configuration(
148 | new PasswordRule(
149 | 4,
150 | new Component[] { new Component(new CharacterClass("b"), false) },
151 | string.Empty
152 | )
153 | )
154 | );
155 |
156 | PasswordRule rule = (PasswordRule)Entry.EntryDefaultConfiguration(entry).Generator;
157 | Assert.AreEqual("b", rule.Components[0].CharacterClass.Characters);
158 | }
159 |
160 | [TestMethod]
161 | public void GroupRuleDefaultTest() {
162 | PwGroup group1 = new PwGroup();
163 | PwGroup group2 = new PwGroup();
164 | PwGroup group3 = new PwGroup();
165 | PwEntry entry = new PwEntry(true, true);
166 |
167 | group1.AddGroup(group2, true);
168 | group2.AddGroup(group3, true);
169 | group3.AddEntry(entry, true);
170 |
171 | PasswordProfile profile = (PasswordProfile)Entry.EntryDefaultConfiguration(entry).Generator;
172 | Assert.IsTrue(profile.IsDefaultProfile);
173 | }
174 |
175 | private static string EncodeJSONString(string str) => $"\"{str.Replace("\\", "\\\\").Replace("\"", "\\\"")}\"";
176 |
177 | private static Configuration ConfigurationFromJSON(string json) {
178 | PwEntry entry = new PwEntry(true, true);
179 | if (json != null) {
180 | entry.Strings.Set(PasswordRuleKey, new ProtectedString(false, json));
181 | }
182 | return Entry.EntryDefaultConfiguration(entry);
183 | }
184 |
185 | private static Configuration EncodeDecodeConfiguration(Configuration config) {
186 | PwEntry entry = new PwEntry(true, true);
187 | Entry.SetEntryConfiguration(entry, config);
188 | return Entry.EntryDefaultConfiguration(entry);
189 | }
190 | }
191 | }
--------------------------------------------------------------------------------
/RuleBuilderTests/Rule/Serialization/ProfileContractTests.cs:
--------------------------------------------------------------------------------
1 | using KeePass.Resources;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 |
4 | namespace RuleBuilder.Rule.Serialization.Tests {
5 | [TestClass]
6 | public class ProfileContractTests {
7 | private static readonly string ProfileName = $"{KPRes.MacAddress} ({KPRes.BuiltIn})";
8 | [TestMethod]
9 | public void DefaultProfileTest() {
10 | ProfileContract profile = new ProfileContract(PasswordProfile.DefaultProfile);
11 | Assert.IsTrue(profile.IsDefault);
12 | Assert.IsNull(profile.Name);
13 | }
14 | [TestMethod]
15 | public void DefaultProfileObjectTest() {
16 | PasswordProfile profile = new ProfileContract(PasswordProfile.DefaultProfile).DeserializedObject();
17 | Assert.IsTrue(profile.IsDefaultProfile);
18 | }
19 | [TestMethod]
20 | public void NamedProfileTest() {
21 | ProfileContract profile = new ProfileContract(new PasswordProfile(PasswordProfile.NamedProfile(ProfileName).Profile));
22 | Assert.IsFalse(profile.IsDefault);
23 | Assert.AreEqual(ProfileName, profile.Name);
24 | }
25 | [TestMethod]
26 | public void NamedProfileObjectTest() {
27 | PasswordProfile profile = new ProfileContract(new PasswordProfile(PasswordProfile.NamedProfile(ProfileName).Profile)).DeserializedObject();
28 | Assert.IsFalse(profile.IsDefaultProfile);
29 | Assert.AreEqual(ProfileName, profile.Name);
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/RuleBuilderTests/Rule/Serialization/RuleContractTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using System.Runtime.Serialization;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 |
5 | namespace RuleBuilder.Rule.Serialization.Tests {
6 | [TestClass]
7 | public class RuleContractTests {
8 | [TestMethod]
9 | public void RuleContractTest() {
10 | RuleContract rule = new RuleContract(new PasswordRule(
11 | 32,
12 | new ObservableCollection() {
13 | new Component(CharacterClass.Letters, false)
14 | },
15 | "x"
16 | ));
17 | Assert.AreEqual(32, rule.Length);
18 | Assert.AreEqual(1, rule.Components.Count);
19 | Assert.AreEqual("x", rule.Exclude);
20 | }
21 | [TestMethod]
22 | public void RuleContractObjectTest() {
23 | PasswordRule rule = new RuleContract(new PasswordRule(
24 | 32,
25 | new ObservableCollection() {
26 | new Component(CharacterClass.Letters, false)
27 | },
28 | "x"
29 | )).DeserializedObject();
30 | Assert.AreEqual(32, rule.Length);
31 | Assert.AreEqual(1, rule.Components.Count);
32 | Assert.AreEqual("x", rule.ExcludeCharacters);
33 | }
34 | [TestMethod]
35 | public void NegativeLengthTest() {
36 | _ = Assert.ThrowsException(() => {
37 | _ = new RuleContract(new PasswordRule() {
38 | Length = -1
39 | }).DeserializedObject();
40 | });
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/RuleBuilderTests/RuleBuilderTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {9A7EE14B-4E48-40A2-898E-1139329BFA03}
8 | Library
9 | Properties
10 | RuleBuilderTests
11 | RuleBuilderTests
12 | v4.7.2
13 | 512
14 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 10.0
16 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
17 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
18 | False
19 | UnitTest
20 |
21 |
22 |
23 |
24 |
25 | true
26 | full
27 | false
28 | bin\Debug\
29 | DEBUG;TRACE
30 | prompt
31 | 4
32 |
33 |
34 | pdbonly
35 | true
36 | bin\Release\
37 | TRACE
38 | prompt
39 | 4
40 | true
41 | AllEnabledByDefault
42 |
43 |
44 |
45 | ..\..\KeePass-2.50\KeePass.exe
46 |
47 |
48 | ..\packages\MSTest.TestFramework.2.2.3\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
49 |
50 |
51 | ..\packages\MSTest.TestFramework.2.2.3\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | {6991459B-221F-4CB1-AD70-9EB1B3088A08}
82 | RuleBuilder
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | False
93 |
94 |
95 | False
96 |
97 |
98 | False
99 |
100 |
101 | False
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
111 |
112 |
113 |
114 |
115 |
116 |
123 |
--------------------------------------------------------------------------------
/RuleBuilderTests/Util/EntropyTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using RuleBuilder.Rule;
5 | using RuleBuilder.Util;
6 |
7 | namespace RuleBuilderTests.Util {
8 | [TestClass]
9 | public class EntropyTests {
10 | [TestMethod]
11 | public void SinglePasswordTest() {
12 | Assert.AreEqual(0.0, Entropy.EntropyBits(
13 | new PasswordRule(32, new Component[] {
14 | new Component(new CharacterClass("0"), false)
15 | }, string.Empty)
16 | ));
17 | }
18 |
19 | [TestMethod]
20 | public void PowerOf2Test() {
21 | Assert.AreEqual(32.0, Entropy.EntropyBits(
22 | new PasswordRule(32, new Component[] {
23 | new Component(new CharacterClass("01"), false)
24 | }, string.Empty)
25 | ));
26 |
27 | Assert.AreEqual(64.0, Entropy.EntropyBits(
28 | new PasswordRule(32, new Component[] {
29 | new Component(new CharacterClass("0123"), false)
30 | }, string.Empty)
31 | ));
32 | }
33 |
34 | [TestMethod]
35 | public void TooManyRequiredTest() {
36 | _ = Assert.ThrowsException(() => Entropy.EntropyBits(
37 | new PasswordRule(32, new Component[] {
38 | new Component(new CharacterClass("a"), true),
39 | new Component(new CharacterClass("b"), true),
40 | new Component(new CharacterClass("c"), true),
41 | new Component(new CharacterClass("d"), true),
42 | new Component(new CharacterClass("e"), true),
43 | new Component(new CharacterClass("f"), true),
44 | new Component(new CharacterClass("g"), true),
45 | }, string.Empty)
46 | ));
47 | }
48 |
49 | [TestMethod]
50 | public void NoneRequiredTest() {
51 | Assert.AreEqual(8.0, Entropy.EntropyBits(
52 | new PasswordRule(4, new Component[] {
53 | new Component(new CharacterClass("0"), false),
54 | new Component(new CharacterClass("1"), false),
55 | new Component(new CharacterClass("2"), false),
56 | new Component(new CharacterClass("3"), false)
57 | }, string.Empty)
58 | ));
59 | }
60 |
61 | [TestMethod]
62 | public void OneRequiredTest() {
63 | Assert.AreEqual(3.907, Entropy.EntropyBits(
64 | new PasswordRule(4, new Component[] {
65 | new Component(new CharacterClass("0"), false),
66 | new Component(new CharacterClass("1"), true)
67 | }, string.Empty)
68 | ), 0.001);
69 | }
70 |
71 | [TestMethod]
72 | public void AllRequiredTest() {
73 | Assert.AreEqual(4.585, Entropy.EntropyBits(
74 | new PasswordRule(4, new Component[] {
75 | new Component(new CharacterClass("0"), true),
76 | new Component(new CharacterClass("1"), true),
77 | new Component(new CharacterClass("2"), true),
78 | new Component(new CharacterClass("3"), true)
79 | }, string.Empty)
80 | ), 0.001);
81 | }
82 |
83 | [TestMethod]
84 | public void RandomPositionTest() {
85 | Assert.AreEqual(Entropy.EntropyBits(
86 | new PasswordRule(4, new Component[] {
87 | new Component(new CharacterClass("0"), true),
88 | new Component(new CharacterClass("1"), true),
89 | new Component(new CharacterClass("1"), true),
90 | new Component(new CharacterClass("1"), true)
91 | }, string.Empty)
92 | ), 2.0);
93 | }
94 |
95 | [TestMethod]
96 | public void TooManyCharactersTest() {
97 | double power = Math.Floor(Math.Log10(double.MaxValue));
98 |
99 | Assert.AreEqual(power / Math.Log10(2.0), Entropy.EntropyBits(
100 | new PasswordRule((int)power, new Component[] {
101 | new Component(new CharacterClass("0123456789"), false)
102 | }, string.Empty)
103 | ), 0.001);
104 |
105 | _ = Assert.ThrowsException(() => Entropy.EntropyBits(
106 | new PasswordRule((int)power + 1, new Component[] {
107 | new Component(new CharacterClass("0123456789"), false)
108 | }, string.Empty)
109 | ));
110 | }
111 |
112 | [TestMethod]
113 | public void DuplicateSetTest() {
114 | _ = Entropy.EntropyBits(new PasswordRule(
115 | 16,
116 | Enumerable.Repeat(new Component(new CharacterClass("0123"), true), 8)
117 | .Concat(Enumerable.Repeat(new Component(new CharacterClass("4567"), true), 8)),
118 | string.Empty
119 | ));
120 | }
121 |
122 | [TestMethod]
123 | public void ExcludedRequirementTest() {
124 | Assert.AreEqual(0.0, Entropy.EntropyBits(
125 | new PasswordRule(4, new Component[] {
126 | new Component(new CharacterClass("0"), true),
127 | new Component(new CharacterClass("1"), true)
128 | }, "1")
129 | ));
130 | }
131 |
132 | [TestMethod]
133 | public void DefaultComponentTest() {
134 | Assert.IsTrue(Entropy.EntropyBits(
135 | new PasswordRule(4, Array.Empty(), string.Empty)
136 | ) > 0.0);
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/RuleBuilderTests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------