Password generator now allows to require a character form each character group

This commit is contained in:
Michael Starke
2019-01-31 13:39:56 +01:00
parent 6a1f8e0472
commit ed0a57a607
4 changed files with 80 additions and 40 deletions

View File

@@ -10,7 +10,7 @@
<connections>
<outlet property="customButton" destination="468" id="598"/>
<outlet property="customCharactersTextField" destination="411" id="479"/>
<outlet property="ensureCharacterFromEachGroupButton" destination="RDM-JY-oF9" id="btK-Gf-vpP"/>
<outlet property="ensureOccuranceButton" destination="RDM-JY-oF9" id="btK-Gf-vpP"/>
<outlet property="entropyIndicator" destination="635" id="676"/>
<outlet property="entropyTextField" destination="652" id="675"/>
<outlet property="lowerCaseButton" destination="456" id="593"/>

View File

@@ -68,7 +68,7 @@ typedef NS_ENUM(NSUInteger, MPPasswordRating) {
@property (strong) IBOutlet NSButton *numbersButton;
@property (strong) IBOutlet NSButton *symbolsButton;
@property (strong) IBOutlet NSButton *customButton;
@property (strong) IBOutlet NSButton *ensureCharacterFromEachGroupButton;
@property (strong) IBOutlet NSButton *ensureOccuranceButton;
@property (strong) IBOutlet NSButton *setDefaultButton;
@property (strong) IBOutlet NSTextField *entropyTextField;
@property (strong) IBOutlet NSLevelIndicator *entropyIndicator;
@@ -76,7 +76,7 @@ typedef NS_ENUM(NSUInteger, MPPasswordRating) {
@property (nonatomic, copy) NSString *customString;
@property (nonatomic, assign) BOOL useCustomString;
@property (nonatomic, assign) BOOL useCharacterFromEachGroup;
@property (nonatomic, assign) BOOL ensureOccurance;
@property (nonatomic, assign) NSUInteger passwordLength;
@property (nonatomic, assign) CGFloat entropy;
@@ -98,7 +98,7 @@ typedef NS_ENUM(NSUInteger, MPPasswordRating) {
_entropy = 0.0;
_useEntryDefaults = NO;
_allowsEntryDefaults = NO;
_useCharacterFromEachGroup = NO;
_ensureOccurance = NO;
[self _setupDefaults];
}
return self;
@@ -128,7 +128,7 @@ typedef NS_ENUM(NSUInteger, MPPasswordRating) {
self.customCharactersTextField.delegate = self;
[self.customButton bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(useCustomString)) options:nil];
[self.ensureCharacterFromEachGroupButton bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(useCharacterFromEachGroup)) options:nil];
[self.ensureOccuranceButton bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(ensureOccurance)) options:nil];
NSString *copyToPasteBoardKeyPath = [MPSettingsHelper defaultControllerPathForKey:kMPSettingsKeyCopyGeneratedPasswordToClipboard];
NSUserDefaultsController *defaultsController = NSUserDefaultsController.sharedUserDefaultsController;
@@ -171,7 +171,7 @@ typedef NS_ENUM(NSUInteger, MPPasswordRating) {
- (IBAction)_generatePassword:(id)sender {
self.password = [NSString passwordWithCharactersets:self.characterFlags
withCustomCharacters:self._customCharacters
ensureOccurence:self.useCharacterFromEachGroup
ensureOccurence:self.ensureOccurance
length:self.passwordLength];
}
@@ -228,7 +228,7 @@ typedef NS_ENUM(NSUInteger, MPPasswordRating) {
entryDefaults[kMPSettingsKeyPasswordCharacterFlags] = @(self.characterFlags);
entryDefaults[kMPSettingsKeyPasswordUseCustomString] = @(self.useCustomString);
entryDefaults[kMPSettingsKeyPasswordCustomString] = self.customCharactersTextField.stringValue;
entryDefaults[kMPSettingsKeyPasswordEnsureOccurance] = @(self.useCharacterFromEachGroup);
entryDefaults[kMPSettingsKeyPasswordEnsureOccurance] = @(self.ensureOccurance);
NSMutableDictionary *availableDefaults = [[self _availableEntryDefaults] mutableCopy];
if(!availableDefaults) {
availableDefaults = [[NSMutableDictionary alloc] initWithCapacity:1];
@@ -284,7 +284,7 @@ typedef NS_ENUM(NSUInteger, MPPasswordRating) {
if(![_password isEqualToString:password]) {
_password = [password copy];
NSString *customString = self.useCustomString ? self.customCharactersTextField.stringValue : nil;
self.entropy = [password entropyWhithPossibleCharacterSet:self.characterFlags andCustomCharacters:customString];
self.entropy = [password entropyWhithCharacterSet:self.characterFlags customCharacters:customString ensureOccurance:self.ensureOccurance];
}
}
@@ -305,9 +305,9 @@ typedef NS_ENUM(NSUInteger, MPPasswordRating) {
}
}
- (void)setUseCharacterFromEachGroup:(BOOL)useCharacterFromEachGroup {
if(self.useCharacterFromEachGroup != useCharacterFromEachGroup) {
_useCharacterFromEachGroup = useCharacterFromEachGroup;
- (void)setEnsureOccurance:(BOOL)useCharacterFromEachGroup {
if(self.ensureOccurance != useCharacterFromEachGroup) {
_ensureOccurance = useCharacterFromEachGroup;
[self _resetCharacters];
[self _generatePassword:nil];
}
@@ -356,14 +356,14 @@ typedef NS_ENUM(NSUInteger, MPPasswordRating) {
self.characterFlags = [entryDefaults[kMPSettingsKeyPasswordCharacterFlags] integerValue];
self.useCustomString = [entryDefaults[kMPSettingsKeyPasswordUseCustomString] boolValue];
self.customString = entryDefaults[kMPSettingsKeyPasswordCustomString];
self.useCharacterFromEachGroup = [entryDefaults[kMPSettingsKeyPasswordEnsureOccurance] boolValue];
self.ensureOccurance = [entryDefaults[kMPSettingsKeyPasswordEnsureOccurance] boolValue];
}
else {
self.passwordLength = [NSUserDefaults.standardUserDefaults integerForKey:kMPSettingsKeyDefaultPasswordLength];
self.characterFlags = [NSUserDefaults.standardUserDefaults integerForKey:kMPSettingsKeyPasswordCharacterFlags];
self.useCustomString = [NSUserDefaults.standardUserDefaults boolForKey:kMPSettingsKeyPasswordUseCustomString];
self.customString = [NSUserDefaults.standardUserDefaults stringForKey:kMPSettingsKeyPasswordCustomString];
self.useCharacterFromEachGroup = [NSUserDefaults.standardUserDefaults boolForKey:kMPSettingsKeyPasswordEnsureOccurance];
self.ensureOccurance = [NSUserDefaults.standardUserDefaults boolForKey:kMPSettingsKeyPasswordEnsureOccurance];
}
}
@@ -393,19 +393,8 @@ typedef NS_ENUM(NSUInteger, MPPasswordRating) {
self.symbolsButton.state = (useSymbols ? NSOnState : NSOffState);
// ensure minimum character lenght
if(self.useCharacterFromEachGroup) {
NSUInteger minimumPasswordLength = 0;
NSUInteger activeFlags = self.characterFlags;
while(activeFlags > 0) {
if(activeFlags & 1) {
minimumPasswordLength++;
}
activeFlags >>= 1;
}
if(self.ensureOccurance) {
if(self.passwordLength < minimumPasswordLength) {
self.passwordLength = minimumPasswordLength;
}
}
}

View File

@@ -56,6 +56,7 @@ typedef NS_OPTIONS(NSUInteger, MPPasswordCharacterFlags) {
+ (NSString *)passwordWithDefaultSettings;
+ (NSUInteger)minimumPasswordLengthWithCharacterSet:(MPPasswordCharacterFlags)characterSet customCharacters:(NSString *)customCharacter ensureOccurance:(BOOL)ensureOccurance;
/**
* @return returns a random character from the string
*/
@@ -84,6 +85,6 @@ typedef NS_OPTIONS(NSUInteger, MPPasswordCharacterFlags) {
*
* @return entropy of the receiver as bits
*/
- (CGFloat)entropyWhithPossibleCharacterSet:(MPPasswordCharacterFlags)allowedCharacters andCustomCharacters:(NSString *)customCharacters;
- (CGFloat)entropyWhithCharacterSet:(MPPasswordCharacterFlags)characterSet customCharacters:(NSString *)customCharacters ensureOccurance:(BOOL)ensureOccurance;
@end

View File

@@ -26,23 +26,32 @@
#import "NSString+MPComposedCharacterAdditions.h"
#import "MPSettingsHelper.h"
NSString *const kMPLowercaseLetterCharacters = @"abcdefghijklmnopqrstuvwxyz";
NSString *const kMPNumberCharacters = @"1234567890";
NSString *const kMPSymbolCharacters = @"!$%&\\|/<>(){}[]=?*'+#-_.:,;";
static NSDictionary<NSNumber *, NSString *> *characterClassMap () {
static dispatch_once_t onceToken;
static NSDictionary *characterClassMap;
dispatch_once(&onceToken, ^{
characterClassMap = @{ @(MPPasswordCharactersLowerCase) : @"abcdefghijklmnopqrstuvwxyz",
@(MPPasswordCharactersUpperCase) : @"abcdefghijklmnopqrstuvwxyz".uppercaseString,
@(MPPasswordCharactersNumbers) : @"1234567890",
@(MPPasswordCharactersSymbols) : @"!$%&\\|/<>(){}[]=?*'+#-_.:,;"
};
});
return characterClassMap;
}
static NSString *allowedCharactersString(MPPasswordCharacterFlags flags) {
NSMutableString *characterString = [NSMutableString stringWithCapacity:30];
if(flags & MPPasswordCharactersLowerCase) {
[characterString appendString:kMPLowercaseLetterCharacters];
[characterString appendString:characterClassMap()[@(MPPasswordCharactersLowerCase)]];
}
if(flags & MPPasswordCharactersUpperCase) {
[characterString appendString:kMPLowercaseLetterCharacters.uppercaseString];
[characterString appendString:characterClassMap()[@(MPPasswordCharactersUpperCase)]];
}
if(flags & MPPasswordCharactersNumbers) {
[characterString appendString:kMPNumberCharacters];
[characterString appendString:characterClassMap()[@(MPPasswordCharactersNumbers)]];
}
if(flags & MPPasswordCharactersSymbols){
[characterString appendString:kMPSymbolCharacters];
[characterString appendString:characterClassMap()[@(MPPasswordCharactersSymbols)]];
}
return characterString;
}
@@ -74,10 +83,30 @@ static NSString *mergeWithoutDuplicates(NSString* baseCharacters, NSString* cust
withCustomCharacters:(NSString *)customCharacters
ensureOccurence:(BOOL)ensureOccurence
length:(NSUInteger)length {
if(ensureOccurence) {
length = MAX(length, [NSString minimumPasswordLengthWithCharacterSet:allowedCharacters customCharacters:customCharacters ensureOccurance:ensureOccurence]);
}
NSMutableString *password = [NSMutableString stringWithCapacity:length];
NSString *characters = mergeWithoutDuplicates(
allowedCharactersString(allowedCharacters),
customCharacters);
if(ensureOccurence) {
if(allowedCharacters & MPPasswordCharactersLowerCase) {
[password appendString:characterClassMap()[@(MPPasswordCharactersLowerCase)].randomCharacter];
}
if(allowedCharacters & MPPasswordCharactersUpperCase) {
[password appendString:characterClassMap()[@(MPPasswordCharactersUpperCase)].randomCharacter];
}
if(allowedCharacters & MPPasswordCharactersNumbers) {
[password appendString:characterClassMap()[@(MPPasswordCharactersNumbers)].randomCharacter];
}
if(allowedCharacters & MPPasswordCharactersSymbols){
[password appendString:characterClassMap()[@(MPPasswordCharactersSymbols)].randomCharacter];
}
if(customCharacters.length > 0) {
[password appendString:customCharacters.randomCharacter];
}
}
while(password.composedCharacterLength < length) {
NSString *randomCharacter = characters.randomCharacter;
if(randomCharacter.length > 0) {
@@ -87,7 +116,7 @@ static NSString *mergeWithoutDuplicates(NSString* baseCharacters, NSString* cust
break;
}
}
return password;
return ensureOccurence ? password.shuffledString : password;
}
+ (NSString *)passwordWithDefaultSettings {
@@ -103,6 +132,21 @@ static NSString *mergeWithoutDuplicates(NSString* baseCharacters, NSString* cust
return [NSString passwordWithCharactersets:characterFlags withCustomCharacters:@"" ensureOccurence:NO length:passwordLength];
}
+ (NSUInteger)minimumPasswordLengthWithCharacterSet:(MPPasswordCharacterFlags)characterSet customCharacters:(NSString *)customCharacter ensureOccurance:(BOOL)ensureOccurance {
NSUInteger minimumPasswordLength = 0;
NSUInteger activeFlags = characterSet;
while(activeFlags > 0) {
if(activeFlags & 1) {
minimumPasswordLength++;
}
activeFlags >>= 1;
}
if(customCharacter.length > 0) {
minimumPasswordLength++;
}
return minimumPasswordLength;
}
- (NSString *)passwordWithLength:(NSUInteger)length {
return [NSString passwordFromString:self length:length];
}
@@ -114,12 +158,18 @@ static NSString *mergeWithoutDuplicates(NSString* baseCharacters, NSString* cust
return [self composedCharacterAtIndex:arc4random_uniform((int)self.composedCharacterLength)];
}
- (CGFloat)entropyWhithPossibleCharacterSet:(MPPasswordCharacterFlags)allowedCharacters andCustomCharacters:(NSString *)customCharacters {
- (CGFloat)entropyWhithCharacterSet:(MPPasswordCharacterFlags)characterSet customCharacters:(NSString *)customCharacters ensureOccurance:(BOOL)ensureOccurance {
if(ensureOccurance) {
return 0;
}
else {
NSString *characters = nil;
characters = mergeWithoutDuplicates(allowedCharactersString(allowedCharacters), customCharacters);
characters = mergeWithoutDuplicates(allowedCharactersString(characterSet), customCharacters);
CGFloat alphabetCount = characters.composedCharacterLength;
CGFloat passwordLength = self.composedCharacterLength;
return passwordLength * ( log10(alphabetCount) / log10(2) );
}
}
- (NSString *)shuffledString {