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

View File

@@ -56,6 +56,7 @@ typedef NS_OPTIONS(NSUInteger, MPPasswordCharacterFlags) {
+ (NSString *)passwordWithDefaultSettings; + (NSString *)passwordWithDefaultSettings;
+ (NSUInteger)minimumPasswordLengthWithCharacterSet:(MPPasswordCharacterFlags)characterSet customCharacters:(NSString *)customCharacter ensureOccurance:(BOOL)ensureOccurance;
/** /**
* @return returns a random character from the string * @return returns a random character from the string
*/ */
@@ -84,6 +85,6 @@ typedef NS_OPTIONS(NSUInteger, MPPasswordCharacterFlags) {
* *
* @return entropy of the receiver as bits * @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 @end

View File

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