2
0

Compare commits

..

No commits in common. "master" and "2.3" have entirely different histories.
master ... 2.3

1781 changed files with 42477 additions and 59097 deletions

View File

@ -1,28 +0,0 @@
# OS-Specific junk.
.DS_Store
Thumbs.db
# IntelliJ
.idea
*.iml
*.ipr
*.iws
# Xcode IDE
xcuserdata/
DerivedData/
# Generated
/platform-darwin/Resources/Media/Images.xcassets/
/platform-darwin/Podfile.lock
/platform-darwin/Pods/
# Gradle
build
.gradle
local.properties
/builds
/platform-android/.externalNativeBuild
# Git
.git

43
.gitignore vendored
View File

@ -1,29 +1,40 @@
# OS-Specific junk.
.DS_Store
Thumbs.db
*~
# IntelliJ
.idea
/MasterPassword/Java/.idea
/.idea/*
!/.idea/encodings.xml
!/.idea/inspectionProfiles
!/.idea/projectCodeStyle.xml
!/.idea/validation.xml
*.iml
*.ipr
*.iws
out
/*.ipr
/*.iws
# Xcode IDE
xcuserdata/
DerivedData/
/DerivedData/
# Generated
/platform-darwin/Resources/Media/Images.xcassets/
/platform-darwin/Podfile.lock
/platform-darwin/Pods/
MasterPassword/Resources/Media/Images.xcassets/
# Gradle
build
.gradle
local.properties
/builds
/platform-android/.externalNativeBuild
.cxx
# Media
Press/Background.png
Press/Front-Page.png
Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
# IPA
/sendipa/*
!/sendipa/sendipa.conf
# C
MasterPassword/C/VERSION
MasterPassword/C/*.o
MasterPassword/C/mpw-*.tar.gz
MasterPassword/C/mpw
MasterPassword/C/mpw-bench
MasterPassword/C/mpw-tests
MasterPassword/C/lib/*/*
!MasterPassword/C/lib/*/.source

View File

@ -1,20 +0,0 @@
variables:
GIT_DEPTH: 3
GIT_SUBMODULE_STRATEGY: recursive
build_project:
stage: build
script:
- "( brew bundle )"
- "( ./lib/bin/build_libsodium-macos clean && ./lib/bin/build_libsodium-macos )"
- "( ./lib/bin/build_libjson-c-macos clean && ./lib/bin/build_libjson-c-macos )"
- "( cd ./platform-independent/c/cli && ./clean && targets=all ./build && ./mpw-tests && ./mpw-cli-tests )"
- "( ./gradlew --stacktrace --info clean test )"
- "( cd platform-darwin && pod install )"
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Release' -scheme 'MasterPassword iOS' -sdk iphonesimulator clean build )"
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Release' -scheme 'MasterPassword macOS' clean build )"
tags:
- brew
- java
- cocoapods
- xcode

31
.gitmodules vendored
View File

@ -1,20 +1,21 @@
[submodule "External/Pearl"]
path = platform-darwin/External/Pearl
path = External/Pearl
url = git://github.com/Lyndir/Pearl.git
[submodule "External/InAppSettingsKit"]
path = External/InAppSettingsKit
url = git://github.com/lhunath/InAppSettingsKit.git
[submodule "External/KCOrderedAccessorFix"]
path = External/KCOrderedAccessorFix
url = https://github.com/lhunath/KCOrderedAccessorFix.git
[submodule "External/AttributedMarkdown"]
path = platform-darwin/External/AttributedMarkdown
path = External/AttributedMarkdown
url = https://github.com/dreamwieber/AttributedMarkdown.git
[submodule "MasterPassword/Web/js/mpw-js"]
path = platform-independent/web/js/mpw-js
[submodule "External/uicolor-utilities"]
path = External/uicolor-utilities
url = git://github.com/lhunath/uicolor-utilities.git
[submodule "External/jrswizzle"]
path = External/jrswizzle
url = git://github.com/jonmarimba/jrswizzle.git
[submodule "Site/mpw-js/js/mpw-js"]
path = Site/mpw-js/js/mpw-js
url = https://github.com/tmthrgd/mpw-js.git
[submodule "lib/libsodium"]
path = lib/libsodium
url = https://github.com/jedisct1/libsodium.git
[submodule "lib/libjson-c"]
path = lib/libjson-c
url = https://github.com/json-c/json-c.git
[submodule "public/site"]
path = public/site
url = https://gitlab.com/MasterPassword/MasterPassword.git
branch = gh-pages
shallow = true

11
.idea/encodings.xml generated Executable file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false">
<file url="file://$PROJECT_DIR$/MasterPassword/Java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-algorithm" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-cli" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-gui" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-model" charset="UTF-8" />
</component>
</project>

View File

@ -0,0 +1,19 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0" is_locked="false">
<option name="myName" value="Project Default" />
<option name="myLocal" value="false" />
<inspection_tool class="Convert to string" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="FunctionImplicitDeclarationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ImplicitIntegerAndEnumConversion" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="LossyEncoding" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MethodIsLaterInTheScope" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OCNotLocalizedStringInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OCUnusedMacroInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OCUnusedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="Replace with subshell" enabled="true" level="INFO" enabled_by_default="true" />
<inspection_tool class="SignednessMismatch" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnavailableInDeploymentTarget" enabled="true" level="INFO" enabled_by_default="true" />
<inspection_tool class="UnusedLocalVariable" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedParameter" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>

9
.idea/projectCodeStyle.xml generated Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value />
</option>
</component>
</project>

7
.travis.yml Normal file
View File

@ -0,0 +1,7 @@
language: objective-c
xcode_project: MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj
xcode_scheme: MasterPassword iOS (Development)
xcode_sdk: iphonesimulator
env: TERM=dumb
git:
submodules: true

View File

@ -1,6 +0,0 @@
brew "libsodium"
brew "json-c"
brew "libtool"
brew "automake"
brew "autoconf"

View File

@ -1,12 +0,0 @@
# Set up a container for doing gradle cross-compiling.
#
# docker build -t lhunath/mp-gradle --file Dockerfile /var/empty
FROM debian:stable-slim
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=863199
RUN mkdir -p /usr/share/man/man1
RUN apt-get update && apt-get install -y default-jdk-headless git-core bash libtool automake autoconf make g++-multilib
RUN git clone --depth=3 $(: --shallow-submodules) --recurse-submodules https://gitlab.com/MasterPassword/MasterPassword.git /mpw
RUN cd /mpw && ./gradlew -i clean
RUN cd /mpw && git pull && git log -1 && ./gradlew -i check

1
External/AttributedMarkdown vendored Submodule

@ -0,0 +1 @@
Subproject commit d598fb4f5e29f5aaa66e7e880a9857019865881b

1
External/InAppSettingsKit vendored Submodule

@ -0,0 +1 @@
Subproject commit b58b72563acecb727da1f7ca151798a911229593

1
External/KCOrderedAccessorFix vendored Submodule

@ -0,0 +1 @@
Subproject commit 1b8f8b79ad12b70976c7a417ff1a9d29e8c0ed73

View File

@ -0,0 +1 @@
Versions/Current/Crashlytics

View File

@ -0,0 +1 @@
Versions/Current/Headers

View File

@ -0,0 +1 @@
Versions/Current/Modules

View File

@ -0,0 +1 @@
Versions/Current/Resources

Binary file not shown.

View File

@ -0,0 +1,220 @@
//
// Crashlytics.h
// Crashlytics
//
// Copyright 2013 Crashlytics, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
*
* The CLS_LOG macro provides as easy way to gather more information in your log messages that are
* sent with your crash data. CLS_LOG prepends your custom log message with the function name and
* line number where the macro was used. If your app was built with the DEBUG preprocessor macro
* defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog.
* If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only.
*
* Example output:
* -[AppDelegate login:] line 134 $ login start
*
* If you would like to change this macro, create a new header file, unset our define and then define
* your own version. Make sure this new header file is imported after the Crashlytics header file.
*
* #undef CLS_LOG
* #define CLS_LOG(__FORMAT__, ...) CLSNSLog...
*
**/
#ifdef DEBUG
#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
/**
*
* Add logging that will be sent with your crash data. This logging will not show up in the system.log
* and will only be visible in your Crashlytics dashboard.
*
**/
OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
OBJC_EXTERN void CLSLogv(NSString *format, va_list args) NS_FORMAT_FUNCTION(1,0);
/**
*
* Add logging that will be sent with your crash data. This logging will show up in the system.log
* and your Crashlytics dashboard. It is not recommended for Release builds.
*
**/
OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
OBJC_EXTERN void CLSNSLogv(NSString *format, va_list args) NS_FORMAT_FUNCTION(1,0);
@protocol CrashlyticsDelegate;
@interface Crashlytics : NSObject
@property (nonatomic, readonly, copy) NSString *apiKey;
@property (nonatomic, readonly, copy) NSString *version;
@property (nonatomic, assign) BOOL debugMode;
@property (nonatomic, assign) NSObject <CrashlyticsDelegate> *delegate;
/**
*
* The recommended way to install Crashlytics into your application is to place a call
* to +startWithAPIKey: in your -application:didFinishLaunchingWithOptions: method.
*
* This delay defaults to 1 second in order to generally give the application time to
* fully finish launching.
*
**/
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey;
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey afterDelay:(NSTimeInterval)delay;
/**
*
* If you need the functionality provided by the CrashlyticsDelegate protocol, you can use
* these convenience methods to activate the framework and set the delegate in one call.
*
**/
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(NSObject <CrashlyticsDelegate> *)delegate;
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(NSObject <CrashlyticsDelegate> *)delegate afterDelay:(NSTimeInterval)delay;
/**
*
* Access the singleton Crashlytics instance.
*
**/
+ (Crashlytics *)sharedInstance;
/**
*
* The easiest way to cause a crash - great for testing!
*
**/
- (void)crash;
/**
*
* Many of our customers have requested the ability to tie crashes to specific end-users of their
* application in order to facilitate responses to support requests or permit the ability to reach
* out for more information. We allow you to specify up to three separate values for display within
* the Crashlytics UI - but please be mindful of your end-user's privacy.
*
* We recommend specifying a user identifier - an arbitrary string that ties an end-user to a record
* in your system. This could be a database id, hash, or other value that is meaningless to a
* third-party observer but can be indexed and queried by you.
*
* Optionally, you may also specify the end-user's name or username, as well as email address if you
* do not have a system that works well with obscured identifiers.
*
* Pursuant to our EULA, this data is transferred securely throughout our system and we will not
* disseminate end-user data unless required to by law. That said, if you choose to provide end-user
* contact information, we strongly recommend that you disclose this in your application's privacy
* policy. Data privacy is of our utmost concern.
*
**/
- (void)setUserIdentifier:(NSString *)identifier;
- (void)setUserName:(NSString *)name;
- (void)setUserEmail:(NSString *)email;
+ (void)setUserIdentifier:(NSString *)identifier;
+ (void)setUserName:(NSString *)name;
+ (void)setUserEmail:(NSString *)email;
/**
*
* Set a value for a key to be associated with your crash data.
*
**/
- (void)setObjectValue:(id)value forKey:(NSString *)key;
- (void)setIntValue:(int)value forKey:(NSString *)key;
- (void)setBoolValue:(BOOL)value forKey:(NSString *)key;
- (void)setFloatValue:(float)value forKey:(NSString *)key;
+ (void)setObjectValue:(id)value forKey:(NSString *)key;
+ (void)setIntValue:(int)value forKey:(NSString *)key;
+ (void)setBoolValue:(BOOL)value forKey:(NSString *)key;
+ (void)setFloatValue:(float)value forKey:(NSString *)key;
@end
/**
* The CLSCrashReport protocol exposes methods that you can call on crash report objects passed
* to delegate methods. If you want these values or the entire object to stay in memory retain
* them or copy them.
**/
@protocol CLSCrashReport <NSObject>
@required
/**
* Returns the session identifier for the crash report.
**/
@property (nonatomic, readonly) NSString *identifier;
/**
* Returns the custom key value data for the crash report.
**/
@property (nonatomic, readonly) NSDictionary *customKeys;
/**
* Returns the CFBundleVersion of the application that crashed.
**/
@property (nonatomic, readonly) NSString *bundleVersion;
/**
* Returns the CFBundleShortVersionString of the application that crashed.
**/
@property (nonatomic, readonly) NSString *bundleShortVersionString;
/**
* Returns the date that the application crashed at.
**/
@property (nonatomic, readonly) NSDate *crashedOnDate;
/**
* Returns the os version that the application crashed on.
**/
@property (nonatomic, readonly) NSString *OSVersion;
/**
* Returns the os build version that the application crashed on.
**/
@property (nonatomic, readonly) NSString *OSBuildVersion;
@end
/**
*
* The CrashlyticsDelegate protocol provides a mechanism for your application to take
* action on events that occur in the Crashlytics crash reporting system. You can make
* use of these calls by assigning an object to the Crashlytics' delegate property directly,
* or through the convenience startWithAPIKey:delegate:... methods.
*
**/
@protocol CrashlyticsDelegate <NSObject>
@optional
/**
*
* Called once a Crashlytics instance has determined that the last execution of the
* application ended in a crash. This is called some time after the crash reporting
* process has begun. If you have specified a delay in one of the
* startWithAPIKey:... calls, this will take at least that long to be invoked.
*
**/
- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics;
/**
*
* Just like crashlyticsDidDetectCrashDuringPreviousExecution this delegate method is
* called once a Crashlytics instance has determined that the last execution of the
* application ended in a crash. A CLSCrashReport is passed back that contains data about
* the last crash report that was generated. See the CLSCrashReport protocol for method details.
* This method is called after crashlyticsDidDetectCrashDuringPreviousExecution.
*
**/
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash;
@end

View File

@ -0,0 +1,6 @@
framework module Crashlytics {
umbrella header "Crashlytics.h"
export *
module * { export * }
}

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>Crashlytics</string>
<key>CFBundleIdentifier</key>
<string>com.crashlytics.sdk.mac</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Crashlytics</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.2.5</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>macosx</string>
</array>
<key>CFBundleVersion</key>
<string>39</string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>MinimumOSVersion</key>
<string>10.6</string>
</dict>
</plist>

View File

@ -0,0 +1 @@
A

BIN
External/Mac/Crashlytics.framework/run vendored Executable file

Binary file not shown.

1
External/Pearl vendored Submodule

@ -0,0 +1 @@
Subproject commit 65b4e3d9984d077f66e6ab15f2ffcc4965d07825

View File

@ -0,0 +1 @@
Versions/Current/Crashlytics

View File

@ -0,0 +1 @@
Versions/Current/Headers

View File

@ -0,0 +1,6 @@
framework module Crashlytics {
umbrella header "Crashlytics.h"
export *
module * { export * }
}

View File

@ -0,0 +1 @@
Versions/Current/Resources

Binary file not shown.

View File

@ -0,0 +1,225 @@
//
// Crashlytics.h
// Crashlytics
//
// Copyright 2013 Crashlytics, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
*
* The CLS_LOG macro provides as easy way to gather more information in your log messages that are
* sent with your crash data. CLS_LOG prepends your custom log message with the function name and
* line number where the macro was used. If your app was built with the DEBUG preprocessor macro
* defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog.
* If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only.
*
* Example output:
* -[AppDelegate login:] line 134 $ login start
*
* If you would like to change this macro, create a new header file, unset our define and then define
* your own version. Make sure this new header file is imported after the Crashlytics header file.
*
* #undef CLS_LOG
* #define CLS_LOG(__FORMAT__, ...) CLSNSLog...
*
**/
#ifdef DEBUG
#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
/**
*
* Add logging that will be sent with your crash data. This logging will not show up in the system.log
* and will only be visible in your Crashlytics dashboard.
*
**/
OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
OBJC_EXTERN void CLSLogv(NSString *format, va_list args) NS_FORMAT_FUNCTION(1,0);
/**
*
* Add logging that will be sent with your crash data. This logging will show up in the system.log
* and your Crashlytics dashboard. It is not recommended for Release builds.
*
**/
OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
OBJC_EXTERN void CLSNSLogv(NSString *format, va_list args) NS_FORMAT_FUNCTION(1,0);
@protocol CrashlyticsDelegate;
@interface Crashlytics : NSObject
@property (nonatomic, readonly, copy) NSString *apiKey;
@property (nonatomic, readonly, copy) NSString *version;
@property (nonatomic, assign) BOOL debugMode;
@property (nonatomic, assign) NSObject <CrashlyticsDelegate> *delegate;
/**
*
* The recommended way to install Crashlytics into your application is to place a call
* to +startWithAPIKey: in your -application:didFinishLaunchingWithOptions: method.
*
* This delay defaults to 1 second in order to generally give the application time to
* fully finish launching.
*
**/
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey;
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey afterDelay:(NSTimeInterval)delay;
/**
*
* If you need the functionality provided by the CrashlyticsDelegate protocol, you can use
* these convenience methods to activate the framework and set the delegate in one call.
*
**/
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(NSObject <CrashlyticsDelegate> *)delegate;
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(NSObject <CrashlyticsDelegate> *)delegate afterDelay:(NSTimeInterval)delay;
/**
*
* Access the singleton Crashlytics instance.
*
**/
+ (Crashlytics *)sharedInstance;
/**
*
* The easiest way to cause a crash - great for testing!
*
**/
- (void)crash;
/**
*
* Many of our customers have requested the ability to tie crashes to specific end-users of their
* application in order to facilitate responses to support requests or permit the ability to reach
* out for more information. We allow you to specify up to three separate values for display within
* the Crashlytics UI - but please be mindful of your end-user's privacy.
*
* We recommend specifying a user identifier - an arbitrary string that ties an end-user to a record
* in your system. This could be a database id, hash, or other value that is meaningless to a
* third-party observer but can be indexed and queried by you.
*
* Optionally, you may also specify the end-user's name or username, as well as email address if you
* do not have a system that works well with obscured identifiers.
*
* Pursuant to our EULA, this data is transferred securely throughout our system and we will not
* disseminate end-user data unless required to by law. That said, if you choose to provide end-user
* contact information, we strongly recommend that you disclose this in your application's privacy
* policy. Data privacy is of our utmost concern.
*
**/
- (void)setUserIdentifier:(NSString *)identifier;
- (void)setUserName:(NSString *)name;
- (void)setUserEmail:(NSString *)email;
+ (void)setUserIdentifier:(NSString *)identifier;
+ (void)setUserName:(NSString *)name;
+ (void)setUserEmail:(NSString *)email;
/**
*
* Set a value for a key to be associated with your crash data.
*
**/
- (void)setObjectValue:(id)value forKey:(NSString *)key;
- (void)setIntValue:(int)value forKey:(NSString *)key;
- (void)setBoolValue:(BOOL)value forKey:(NSString *)key;
- (void)setFloatValue:(float)value forKey:(NSString *)key;
+ (void)setObjectValue:(id)value forKey:(NSString *)key;
+ (void)setIntValue:(int)value forKey:(NSString *)key;
+ (void)setBoolValue:(BOOL)value forKey:(NSString *)key;
+ (void)setFloatValue:(float)value forKey:(NSString *)key;
@end
/**
* The CLSCrashReport protocol exposes methods that you can call on crash report objects passed
* to delegate methods. If you want these values or the entire object to stay in memory retain
* them or copy them.
**/
@protocol CLSCrashReport <NSObject>
@required
/**
* Returns the session identifier for the crash report.
**/
@property (nonatomic, readonly) NSString *identifier;
/**
* Returns the custom key value data for the crash report.
**/
@property (nonatomic, readonly) NSDictionary *customKeys;
/**
* Returns the CFBundleVersion of the application that crashed.
**/
@property (nonatomic, readonly) NSString *bundleVersion;
/**
* Returns the CFBundleShortVersionString of the application that crashed.
**/
@property (nonatomic, readonly) NSString *bundleShortVersionString;
/**
* Returns the date that the application crashed at.
**/
@property (nonatomic, readonly) NSDate *crashedOnDate;
/**
* Returns the os version that the application crashed on.
**/
@property (nonatomic, readonly) NSString *OSVersion;
/**
* Returns the os build version that the application crashed on.
**/
@property (nonatomic, readonly) NSString *OSBuildVersion;
@end
/**
*
* The CrashlyticsDelegate protocol provides a mechanism for your application to take
* action on events that occur in the Crashlytics crash reporting system. You can make
* use of these calls by assigning an object to the Crashlytics' delegate property directly,
* or through the convenience startWithAPIKey:delegate:... methods.
*
**/
@protocol CrashlyticsDelegate <NSObject>
@optional
/**
*
* Called once a Crashlytics instance has determined that the last execution of the
* application ended in a crash. This is called some time after the crash reporting
* process has begun. If you have specified a delay in one of the
* startWithAPIKey:... calls, this will take at least that long to be invoked.
*
**/
- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics;
/**
*
* Just like crashlyticsDidDetectCrashDuringPreviousExecution this delegate method is
* called once a Crashlytics instance has determined that the last execution of the
* application ended in a crash. A CLSCrashReport is passed back that contains data about
* the last crash report that was generated. See the CLSCrashReport protocol for method details.
* This method is called after crashlyticsDidDetectCrashDuringPreviousExecution.
*
**/
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash;
@end
/**
* `CrashlyticsKit` can be used as a parameter to `[Fabric with:@[CrashlyticsKit]];` in Objective-C. In Swift, simply use `Crashlytics()`
*/
#define CrashlyticsKit [Crashlytics sharedInstance]

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>Crashlytics</string>
<key>CFBundleIdentifier</key>
<string>com.crashlytics.ios</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Crashlytics</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.2.9</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>iPhoneOS</string>
</array>
<key>CFBundleVersion</key>
<string>44</string>
<key>DTPlatformName</key>
<string>iphoneos</string>
<key>MinimumOSVersion</key>
<string>4.0</string>
</dict>
</plist>

View File

@ -0,0 +1 @@
A

BIN
External/iOS/Crashlytics.framework/run vendored Executable file

Binary file not shown.

BIN
External/iOS/Crashlytics.framework/submit vendored Executable file

Binary file not shown.

1
External/iOS/Reveal.framework/Headers vendored Symbolic link
View File

@ -0,0 +1 @@
Versions/Current/Headers

1
External/iOS/Reveal.framework/Reveal vendored Symbolic link
View File

@ -0,0 +1 @@
Versions/Current/Reveal

View File

@ -0,0 +1,17 @@
//
// Copyright (c) 2013 Itty Bitty Apps. All rights reserved.
#import <Foundation/Foundation.h>
extern NSString * const IBARevealLoaderRequestStartNotification;
extern NSString * const IBARevealLoaderRequestStopNotification;
extern NSString * const IBARevealLoaderSetOptionsNotification;
extern NSString * const IBARevealLoaderOptionsLogLevelMaskKey;
@interface IBARevealLoader : NSObject
+ (void)startServer;
+ (void)stopServer;
@end

View File

@ -0,0 +1,57 @@
// Copyright (c) 2013 Itty Bitty Apps Pty Ltd. All rights reserved.
//
#import <Foundation/Foundation.h>
CF_EXTERN_C_BEGIN
/*!
\brief The Reveal Log level bit flags.
\discussion These flags are additive. i.e. you should bitwise OR them together.
\seealso IBARevealLoggerSetLevelMask
\seealso IBARevealLoggerGetLevelMask
Example:
// Enable Error, Warning and Info logger levels.
IBARevealLoggerSetLevelMask(IBARevealLogLevelError|IBARevealLogLevelWarn|IBARevealLogLevelInfo);
*/
typedef NS_OPTIONS(int32_t, IBARevealLogLevel)
{
IBARevealLogLevelNone = 0,
IBARevealLogLevelDebug = (1 << 0),
IBARevealLogLevelInfo = (1 << 1),
IBARevealLogLevelWarn = (1 << 2),
IBARevealLogLevelError = (1 << 3)
};
/*!
\brief Set the Reveal logger level mask.
\param mask A bit mask which is a combination of the IBARevealLogLevel enum options.
\discussion If you do not wish to see log messages from Reveal you should call this function with an appropriate level mask as early in your application's lifecycle as possible. For example in your application's main() function.
Example:
// Enable Error, Warning and Info logger levels.
IBARevealLoggerSetLevelMask(IBARevealLogLevelError|IBARevealLogLevelWarn|IBARevealLogLevelInfo);
*/
CF_EXPORT void IBARevealLoggerSetLevelMask(int32_t mask);
/*!
\brief Get the current Reveal logger level mask.
\return A bit mask representing the levels at which Reveal is currently logging.
\discussion The default Reveal Logger level mask is IBARevealLogLevelError|IBARevealLogLevelWarn|IBARevealLogLevelInfo.
Example:
// Turn off the Info log level.
IBARevealLoggerSetLevelMask(IBARevealLoggerGetLevelMask() & ~IBARevealLogLevelInfo);
*/
CF_EXPORT int32_t IBARevealLoggerGetLevelMask(void);
CF_EXTERN_C_END

View File

@ -0,0 +1,5 @@
// Copyright (c) 2013 Itty Bitty Apps Pty Ltd. All rights reserved.
//
#import "IBARevealLogger.h"
#import "IBARevealLoader.h"

Binary file not shown.

View File

@ -0,0 +1 @@
A

1
External/jrswizzle vendored Submodule

@ -0,0 +1 @@
Subproject commit 98d18aee73329321c320a2df85bacdb9f08a34a6

1
External/uicolor-utilities vendored Submodule

@ -0,0 +1 @@
Subproject commit ae96212a4903a2b9e1df1e3542c9962f0d64a74b

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj">
</FileRef>
<FileRef
location = "group:MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj">
</FileRef>
</Workspace>

333
MasterPassword/C/build Executable file
View File

@ -0,0 +1,333 @@
#!/usr/bin/env bash
#
# TROUBLESHOOTING
# - To enable verbose algorithm/implementation debugging, use ./build -DDEBUG
# - If you see 'undefined reference to `AES_encrypt'',
# make sure you have openssl installed.
# If libcrypto.a is in a non-standard directory, try ./build -L[your-lib-dir]
# - If you see 'undefined reference to `clock_gettime'',
# try ./build -lrt instead.
# - If you see 'x86.S:202: Error: junk at end of line, first unrecognized character is `,'',
# try commenting the line in lib/bcrypt/x86.S.
# - Take a look at the "Optional features" section. Some features have dependencies,
# either make sure you have them or disable those features.
#
# BUGS
# masterpassword@lyndir.com
#
# AUTHOR
# Maarten Billemont
#
cd "${BASH_SOURCE%/*}"
shopt -s extglob
set -e
### CONFIGURATION
# Targets to build.
if [[ $targets ]]; then
read -ra targets <<< "$targets"
else
# Default targets.
# Modify here or override using targets='mpw mpw-bench' ./build
targets=(
mpw # C CLI version of Master Password.
mpw-bench # C CLI Master Password benchmark utility.
mpw-tests # C Master Password algorithm tester.
)
fi
# Optional features.
mpw_color=1 # Colorized Identicon, requires libncurses-dev
### DEPENDENCIES
digest() {
openssl sha -sha256 -binary < "$1" | od -t x1 -An -v | tr -d '[:space:]'
}
fetch() {
if hash wget 2>/dev/null; then
wget -O "${1##*/}" "$1"
elif hash curl 2>/dev/null; then
curl "$1" > "${1##*/}"
fi
}
unpack() {
if [[ $1 = *.tar.gz || $1 = *.tgz ]]; then
tar -xvzf "$1"
elif [[ $1 = *.tar.bz2 || $1 = *.tbz2 ]]; then
tar -xvjf "$1"
elif [[ $1 = *.tar ]]; then
tar -xvf "$1"
else
echo 2>&1 "Don't know how to unpack: $1"
fi
printf 'Verifying package: %s, against digest: %s...' "$1" "$2"
[[ $(digest "$1") = $2 ]] || {
printf ' mismatch!\n'
echo 2>&1 "Downloaded package doesn't match digest."
exit 1
}
printf ' OK!\n'
files=( !("$1") )
if [[ -d $files ]] && (( ${#files[@]} == 1 )); then
mv "$files"/* .
rmdir "$files"
fi
touch .unpacked
}
fetchSource() (
source .source
if [[ -e .unpacked ]]; then
true
elif [[ $pkg && -e "${pkg##*/}" ]]; then
files=( !("${pkg##*/}") )
[[ -e $files ]] || {
echo
echo "Unpacking: ${PWD##*/}, using package..."
unpack "${pkg##*/}" "$pkg_sha256"
}
elif [[ $git ]] && hash git 2>/dev/null; then
[[ -e .git ]] || {
echo
echo "Fetching: ${PWD##*/}, using git..."
git clone "$svn" .
printf '%s' "$(git describe --always)" > "${PWD##*/}-version"
}
elif [[ $svn ]] && hash git 2>/dev/null && [[ -x "$(git --exec-path)/git-svn" ]]; then
[[ -e .git ]] || {
echo
echo "Fetching: ${PWD##*/}, using git-svn..."
git svn clone --prefix=origin/ --stdlayout "$svn" .
printf '%s' "$(git describe --always)" > "${PWD##*/}-version"
}
elif [[ $svn ]] && hash svn 2>/dev/null; then
[[ -e .svn ]] || {
echo
echo "Fetching: ${PWD##*/}, using svn..."
svn checkout "$svn/trunk" .
printf 'r%s' "$(svn info | awk '/^Revision:/{ print $2 }')" > "${PWD##*/}-version"
}
elif [[ $pkg ]]; then
files=( !("${pkg##*/}") )
[[ -e $files ]] || {
echo
echo "Fetching: ${PWD##*/}, using package..."
fetch "$pkg"
unpack "${pkg##*/}" "$pkg_sha256"
}
else
echo >&2 "error: Missing git-svn or svn."
echo >&2 "error: Please install either or manually check out the sources"
echo >&2 "error: from: $home"
echo >&2 "error: into: $PWD"
exit 1
fi
if [[ ! -e .patched ]] && (( ${#patches[@]} )); then
for patch in "${patches[@]}"; do
echo
echo "Patching: ${PWD##*/}, for $patch..."
patch -p0 < ../"${PWD##*/}-$patch.patch"
done
touch .patched
fi
)
depend() {
echo
echo "Checking dependency: $1..."
[[ -e "lib/include/$1" ]] && return
pushd "lib/$1"
fetchSource
echo
echo "Configuring dependency: $1..."
if [[ -e configure.ac ]]; then
if [[ ! -e configure ]]; then
# create configure using autotools.
if ! hash aclocal || ! hash automake; then
echo >&2 "Need autotools to build $1. Please install automake and autoconf."
exit 1
fi
aclocal
autoheader
autoconf
mkdir -p config.aux
automake --add-missing
fi
fi
if [[ -e configure ]]; then
./configure
fi
echo
echo "Building dependency: $1..."
if [[ -e Makefile ]]; then
if ! hash make; then
echo >&2 "Need make to build $1. Please install GNU make."
exit 1
fi
make
install -d "../include/$1/"
find . -name '*.h' -exec install -m 444 {} "../include/$1/" \;
else
echo >&2 "error: Don't know how to build: $1"
exit 1
fi
popd
}
### MPW
mpw() {
depend scrypt
echo
echo "Building target: $target..."
local CFLAGS=(
# include paths
-I"lib/include"
)
local LDFLAGS=(
# scrypt
"lib/scrypt/scrypt-crypto_aesctr.o"
"lib/scrypt/scrypt-sha256.o"
"lib/scrypt/scrypt-crypto_scrypt-nosse.o"
"lib/scrypt/scrypt-memlimit.o"
"lib/scrypt/scrypt-scryptenc_cpuperf.o"
"lib/scrypt/scrypt-scryptenc.o"
# library paths
-L"." -L"lib/scrypt"
# link libraries
-l"crypto"
)
# optional features
(( mpw_color )) && CFLAGS+=( -DCOLOR ) LDFLAGS+=( -l"curses" )
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
"${LDFLAGS[@]}" "mpw-cli.c" -o "mpw"
echo "done! Now run ./install or use ./mpw"
}
### MPW-BENCH
mpw-bench() {
depend scrypt
depend bcrypt
echo
echo "Building target: $target..."
local CFLAGS=(
# include paths
-I"lib/include"
)
local LDFLAGS=(
# scrypt
"lib/scrypt/scrypt-crypto_aesctr.o"
"lib/scrypt/scrypt-sha256.o"
"lib/scrypt/scrypt-crypto_scrypt-nosse.o"
"lib/scrypt/scrypt-memlimit.o"
"lib/scrypt/scrypt-scryptenc_cpuperf.o"
"lib/scrypt/scrypt-scryptenc.o"
# bcrypt
"lib/bcrypt/crypt_blowfish.o"
"lib/bcrypt/crypt_gensalt.o"
"lib/bcrypt/wrapper.o"
"lib/bcrypt/x86.o"
# library paths
-L"." -L"lib/scrypt"
-L"lib/bcrypt"
# link libraries
-l"crypto"
)
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
"${LDFLAGS[@]}" "mpw-bench.c" -o "mpw-bench"
echo "done! Now use ./mpw-bench"
}
### MPW-TESTS
mpw-tests() {
depend scrypt
echo
echo "Building target: $target..."
local CFLAGS=(
# include paths
-I"lib/include"
-I"/usr/include/libxml2"
-I"/usr/local/include/libxml2"
)
local LDFLAGS=(
# scrypt
"lib/scrypt/scrypt-crypto_aesctr.o"
"lib/scrypt/scrypt-sha256.o"
"lib/scrypt/scrypt-crypto_scrypt-nosse.o"
"lib/scrypt/scrypt-memlimit.o"
"lib/scrypt/scrypt-scryptenc_cpuperf.o"
"lib/scrypt/scrypt-scryptenc.o"
# library paths
-L"." -L"lib/scrypt"
# link libraries
-l"crypto" -l"xml2"
)
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
cc "${CFLAGS[@]}" "$@" -c mpw-tests-util.c -o mpw-tests-util.o
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" "mpw-tests-util.o" \
"${LDFLAGS[@]}" "mpw-tests.c" -o "mpw-tests"
echo "done! Now use ./mpw-tests"
}
### TARGETS
haslib() {
! LC_ALL=C cc -l"$1" 2>&1 | grep -q 'library not found'
}
cc() {
if hash llvm-gcc 2>/dev/null; then
llvm-gcc "$@"
elif hash gcc 2>/dev/null; then
gcc -std=gnu99 "$@"
elif hash clang 2>/dev/null; then
clang "$@"
else
echo >&2 "Need a compiler. Please install GCC or LLVM."
exit 1
fi
}
echo "Will build targets: ${targets[*]}..."
for target in "${targets[@]}"; do
"$target" "$@"
done

20
MasterPassword/C/distribute Executable file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -e
cd "${BASH_SOURCE%/*}"
tag=$(git describe)
commit=$(git describe --long --dirty)
[[ $tag && $commit = $tag* ]] || exit 1
git show --show-signature --pretty=format:%H --quiet "$tag" > VERSION
mpwArchive=mpw-$commit.tar.gz
[[ -e $mpwArchive ]] && echo "WARNING: $mpwArchive already exists. Will overwrite."
read -n1 -p "Will prepare and release $mpwArchive. Press a key to continue or ^C to abort."
git ls-files -z . | xargs -0 tar -Lcvzf "$mpwArchive"
echo "$mpwArchive ready, SHA256: $(openssl sha -sha256 < "$mpwArchive")"
cd ../../Site/current
ln -sf "../../MasterPassword/C/$mpwArchive"
[[ -e $_ ]]
echo "Linked from site, please update your hyperlinks to point to http://masterpasswordapp.com/$mpwArchive"

View File

@ -23,7 +23,7 @@ bindir=$(ask -d "$bindir" "What bin directory should I install to?")
[[ -w "$bindir" ]] || ftl 'Cannot write to bin directory: %s' "$bindir" || exit
# Install Master Password.
install -m555 mpw "$bindir"
install mpw "$bindir"
[[ ! -e "$bindir/bashlib" ]] && install bashlib "$bindir" ||:
# Convenience bash function.
@ -45,20 +45,9 @@ fi
echo
inf "You can also save your user name in ~/.bashrc. Leave blank to skip this step."
if MPW_FULLNAME=$(ask "Your full name:") && [[ $MPW_FULLNAME ]] ; then
printf 'export MPW_FULLNAME=%q\n' "$MPW_FULLNAME" >> ~/.bashrc
fi
inf "If you have an askpass program you'd like to use, you can specify it here."
inf "An askpass program provides a graphical interface for entering things like your master password."
inf "Leave blank to skip this step and enter passwords using the terminal."
if [[ ! $MPW_ASKPASS ]] && hash ssh-askpass 2>/dev/null; then
MPW_ASKPASS=ssh-askpass
fi
if MPW_ASKPASS=$(ask +"$MPW_ASKPASS" "askpass program:") && [[ $MPW_ASKPASS ]] ; then
printf 'export MPW_ASKPASS=%q\n' "$MPW_ASKPASS" >> ~/.bashrc
if MP_FULLNAME=$(ask "Your full name:") && [[ $MP_FULLNAME ]] ; then
printf 'export MP_FULLNAME=%q\n' "$MP_FULLNAME" >> ~/.bashrc
fi
echo
inf "Shell features installed."
inf "To load these convenience features into your already running shell, type: source ~/.bashrc"
inf "To begin using Master Password, type: mpw -h or mpw my-site-name"
inf "To begin using Master Password, type: mpw [site name]"

View File

@ -0,0 +1,12 @@
--- x86.S 2014-11-21 09:09:58.000000000 -0500
+++ x86.S 2014-11-21 09:11:01.000000000 -0500
@@ -199,5 +199,9 @@
#endif
#if defined(__ELF__) && defined(__linux__)
+#if defined(__arm__)
+.section .note.GNU-stack,"",%progbits
+#else
.section .note.GNU-stack,"",@progbits
#endif
+#endif

View File

@ -0,0 +1,4 @@
home=http://www.openwall.com/crypt/
pkg=http://www.openwall.com/crypt/crypt_blowfish-1.3.tar.gz
pkg_sha256=83fa01fca6996fe8d882b7f8e9ba0305a5664936100b01481ea3c6a8ce8d72fd
patches=(arm)

View File

@ -0,0 +1,38 @@
diff -ruN /Users/lhunath/.src/scrypt/Makefile ./Makefile
--- /Users/lhunath/.src/scrypt/Makefile 2014-05-02 11:28:58.000000000 -0400
+++ ./Makefile 2014-05-02 12:07:27.000000000 -0400
@@ -2,11 +2,11 @@
VER?= nosse
SRCS= main.c
LDADD+= -lcrypto
-WARNS?= 6
+WARNS?= 0
# We have a config file for FreeBSD
CFLAGS += -I .
-CFLAGS += -DCONFIG_H_FILE=\"config_freebsd.h\"
+CFLAGS += -DCONFIG_H_FILE=\"config_osx.h\"
# Include all possible object files containing built scrypt code.
CLEANFILES += crypto_scrypt-ref.o
diff -ruN /Users/lhunath/.src/scrypt/lib/util/memlimit.c ./lib/util/memlimit.c
--- /Users/lhunath/.src/scrypt/lib/util/memlimit.c 2014-05-02 11:28:58.000000000 -0400
+++ ./lib/util/memlimit.c 2014-05-02 11:52:42.000000000 -0400
@@ -75,7 +75,7 @@
* have returned to us.
*/
if (usermemlen == sizeof(uint64_t))
- usermem = *(uint64_t *)usermembuf;
+ usermem = *(uint64_t *)(void *)usermembuf;
else if (usermemlen == sizeof(uint32_t))
usermem = SIZE_MAX;
else
diff -ruN /Users/lhunath/.src/scrypt/lib/util/memlimit.c ./lib/util/memlimit.c
--- /Users/lhunath/.src/scrypt/config_osx.h 1969-12-31 19:00:00.000000000 -0500
+++ config_osx.h 2014-05-02 12:06:55.000000000 -0400
@@ -0,0 +1,5 @@
+/* A default configuration for FreeBSD, used if there is no config.h. */
+
+#define HAVE_POSIX_MEMALIGN 1
+#define HAVE_SYSCTL_HW_USERMEM 1
+#define HAVE_SYS_PARAM_H 1

View File

@ -0,0 +1,4 @@
home=https://code.google.com/p/scrypt/
svn=http://scrypt.googlecode.com/svn
pkg=http://masterpasswordapp.com/libscrypt-b12b554.tar.gz
pkg_sha256=c726daec68a345e420896f005394a948dc5a6924713ed94b684c856d4c247f0b

View File

@ -0,0 +1,53 @@
//
// mpw-algorithm.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include "mpw-algorithm.h"
#include "mpw-algorithm_v0.c"
#include "mpw-algorithm_v1.c"
#include "mpw-algorithm_v2.c"
#include "mpw-algorithm_v3.c"
#define MP_N 32768
#define MP_r 8
#define MP_p 2
#define MP_hash PearlHashSHA256
const uint8_t *mpw_masterKeyForUser(const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) {
switch (algorithmVersion) {
case MPAlgorithmVersion0:
return mpw_masterKeyForUser_v0( fullName, masterPassword );
case MPAlgorithmVersion1:
return mpw_masterKeyForUser_v1( fullName, masterPassword );
case MPAlgorithmVersion2:
return mpw_masterKeyForUser_v2( fullName, masterPassword );
case MPAlgorithmVersion3:
return mpw_masterKeyForUser_v3( fullName, masterPassword );
default:
ftl( "Unsupported version: %d", algorithmVersion );
return NULL;
}
}
const char *mpw_passwordForSite(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext, const MPAlgorithmVersion algorithmVersion) {
switch (algorithmVersion) {
case MPAlgorithmVersion0:
return mpw_passwordForSite_v0( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
case MPAlgorithmVersion1:
return mpw_passwordForSite_v1( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
case MPAlgorithmVersion2:
return mpw_passwordForSite_v2( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
case MPAlgorithmVersion3:
return mpw_passwordForSite_v3( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
default:
ftl( "Unsupported version: %d", algorithmVersion );
return NULL;
}
}

View File

@ -0,0 +1,33 @@
//
// mpw-algorithm.h
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
// NOTE: mpw is currently NOT thread-safe.
#include "mpw-types.h"
typedef enum(unsigned int, MPAlgorithmVersion) {
/** V0 did math with chars whose signedness was platform-dependent. */
MPAlgorithmVersion0,
/** V1 miscounted the byte-length of multi-byte site names. */
MPAlgorithmVersion1,
/** V2 miscounted the byte-length of multi-byte user names. */
MPAlgorithmVersion2,
/** V3 is the current version. */
MPAlgorithmVersion3,
};
#define MPAlgorithmVersionCurrent MPAlgorithmVersion3
/** Derive the master key for a user based on their name and master password.
* @return A new MP_dkLen-byte allocated buffer or NULL if an allocation error occurred. */
const uint8_t *mpw_masterKeyForUser(
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
/** Encode a password for the site from the given master key and site parameters.
* @return A newly allocated string or NULL if an allocation error occurred. */
const char *mpw_passwordForSite(
const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext, const MPAlgorithmVersion algorithmVersion);

View File

@ -0,0 +1,132 @@
//
// mpw-algorithm.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include "mpw-types.h"
#include "mpw-util.h"
#define MP_N 32768
#define MP_r 8
#define MP_p 2
#define MP_hash PearlHashSHA256
static const char *mpw_templateForType_v0(MPSiteType type, uint16_t seedByte) {
size_t count = 0;
const char **templates = mpw_templatesForType( type, &count );
if (!count)
return NULL;
return templates[seedByte % count];
}
static const char mpw_characterFromClass_v0(char characterClass, uint16_t seedByte) {
const char *classCharacters = mpw_charactersInClass( characterClass );
return classCharacters[seedByte % strlen( classCharacters )];
}
static const uint8_t *mpw_masterKeyForUser_v0(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
trc( "algorithm: v%d\n", 0 );
trc( "fullName: %s (%zu)\n", fullName, mpw_charlen( fullName ) );
trc( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
// Calculate the master key salt.
// masterKeySalt = mpKeyScope . #fullName . fullName
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( mpw_charlen( fullName ) ) );
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
ftl( "Could not allocate master key salt: %d\n", errno );
return NULL;
}
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
// masterKey = scrypt( masterPassword, masterKeySalt )
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( masterKeySalt, masterKeySaltSize );
if (!masterKey) {
ftl( "Could not allocate master key: %d\n", errno );
return NULL;
}
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
return masterKey;
}
static const char *mpw_passwordForSite_v0(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
trc( "algorithm: v%d\n", 0 );
trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant );
trc( "siteType: %d\n", siteType );
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
mpw_hex_l( htonl( siteCounter ) ),
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
size_t sitePasswordInfoSize = 0;
uint8_t *sitePasswordInfo = NULL;
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteName ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
if (siteContext) {
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteContext ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
}
if (!sitePasswordInfo) {
ftl( "Could not allocate site seed info: %d\n", errno );
return NULL;
}
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
const char *sitePasswordSeed = (const char *)mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
if (!sitePasswordSeed) {
ftl( "Could not allocate site seed: %d\n", errno );
return NULL;
}
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
// Determine the template.
const char *template = mpw_templateForType_v0( siteType, htons( sitePasswordSeed[0] ) );
trc( "type %d, template: %s\n", siteType, template );
if (strlen( template ) > 32) {
ftl( "Template too long for password seed: %lu", strlen( template ) );
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return NULL;
}
// Encode the password from the seed using the template.
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
for (size_t c = 0; c < strlen( template ); ++c) {
sitePassword[c] = mpw_characterFromClass_v0( template[c], htons( sitePasswordSeed[c + 1] ) );
trc( "class %c, index %u (0x%02X) -> character: %c\n",
template[c], htons( sitePasswordSeed[c + 1] ), htons( sitePasswordSeed[c + 1] ), sitePassword[c] );
}
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return sitePassword;
}

View File

@ -0,0 +1,116 @@
//
// mpw-algorithm.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include "mpw-types.h"
#include "mpw-util.h"
#define MP_N 32768
#define MP_r 8
#define MP_p 2
#define MP_hash PearlHashSHA256
static const uint8_t *mpw_masterKeyForUser_v1(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
trc( "algorithm: v%d\n", 1 );
trc( "fullName: %s (%zu)\n", fullName, mpw_charlen( fullName ) );
trc( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
// Calculate the master key salt.
// masterKeySalt = mpKeyScope . #fullName . fullName
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( mpw_charlen( fullName ) ) );
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
ftl( "Could not allocate master key salt: %d\n", errno );
return NULL;
}
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
// masterKey = scrypt( masterPassword, masterKeySalt )
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( masterKeySalt, masterKeySaltSize );
if (!masterKey) {
ftl( "Could not allocate master key: %d\n", errno );
return NULL;
}
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
return masterKey;
}
static const char *mpw_passwordForSite_v1(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
trc( "algorithm: v%d\n", 1 );
trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant );
trc( "siteType: %d\n", siteType );
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
mpw_hex_l( htonl( siteCounter ) ),
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
size_t sitePasswordInfoSize = 0;
uint8_t *sitePasswordInfo = NULL;
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteName ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
if (siteContext) {
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteContext ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
}
if (!sitePasswordInfo) {
ftl( "Could not allocate site seed info: %d\n", errno );
return NULL;
}
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
if (!sitePasswordSeed) {
ftl( "Could not allocate site seed: %d\n", errno );
return NULL;
}
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
// Determine the template.
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
trc( "type %d, template: %s\n", siteType, template );
if (strlen( template ) > 32) {
ftl( "Template too long for password seed: %lu", strlen( template ) );
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return NULL;
}
// Encode the password from the seed using the template.
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
for (size_t c = 0; c < strlen( template ); ++c) {
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
sitePassword[c] );
}
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return sitePassword;
}

View File

@ -0,0 +1,116 @@
//
// mpw-algorithm.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include "mpw-types.h"
#include "mpw-util.h"
#define MP_N 32768
#define MP_r 8
#define MP_p 2
#define MP_hash PearlHashSHA256
static const uint8_t *mpw_masterKeyForUser_v2(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
trc( "algorithm: v%d\n", 2 );
trc( "fullName: %s (%zu)\n", fullName, mpw_charlen( fullName ) );
trc( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
// Calculate the master key salt.
// masterKeySalt = mpKeyScope . #fullName . fullName
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( mpw_charlen( fullName ) ) );
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
ftl( "Could not allocate master key salt: %d\n", errno );
return NULL;
}
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
// masterKey = scrypt( masterPassword, masterKeySalt )
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( masterKeySalt, masterKeySaltSize );
if (!masterKey) {
ftl( "Could not allocate master key: %d\n", errno );
return NULL;
}
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
return masterKey;
}
static const char *mpw_passwordForSite_v2(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
trc( "algorithm: v%d\n", 2 );
trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant );
trc( "siteType: %d\n", siteType );
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
mpw_hex_l( htonl( siteCounter ) ),
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
size_t sitePasswordInfoSize = 0;
uint8_t *sitePasswordInfo = NULL;
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
if (siteContext) {
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
}
if (!sitePasswordInfo) {
ftl( "Could not allocate site seed info: %d\n", errno );
return NULL;
}
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
if (!sitePasswordSeed) {
ftl( "Could not allocate site seed: %d\n", errno );
return NULL;
}
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
// Determine the template.
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
trc( "type %d, template: %s\n", siteType, template );
if (strlen( template ) > 32) {
ftl( "Template too long for password seed: %lu", strlen( template ) );
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return NULL;
}
// Encode the password from the seed using the template.
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
for (size_t c = 0; c < strlen( template ); ++c) {
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
sitePassword[c] );
}
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return sitePassword;
}

View File

@ -0,0 +1,116 @@
//
// mpw-algorithm.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include "mpw-types.h"
#include "mpw-util.h"
#define MP_N 32768
#define MP_r 8
#define MP_p 2
#define MP_hash PearlHashSHA256
static const uint8_t *mpw_masterKeyForUser_v3(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
trc( "algorithm: v%d\n", 3 );
trc( "fullName: %s (%zu)\n", fullName, strlen( fullName ) );
trc( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
// Calculate the master key salt.
// masterKeySalt = mpKeyScope . #fullName . fullName
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( strlen( fullName ) ) );
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
ftl( "Could not allocate master key salt: %d\n", errno );
return NULL;
}
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
// masterKey = scrypt( masterPassword, masterKeySalt )
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( masterKeySalt, masterKeySaltSize );
if (!masterKey) {
ftl( "Could not allocate master key: %d\n", errno );
return NULL;
}
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
return masterKey;
}
static const char *mpw_passwordForSite_v3(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
trc( "algorithm: v%d\n", 3 );
trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant );
trc( "siteType: %d\n", siteType );
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
mpw_hex_l( htonl( siteCounter ) ),
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
size_t sitePasswordInfoSize = 0;
uint8_t *sitePasswordInfo = NULL;
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
if (siteContext) {
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
}
if (!sitePasswordInfo) {
ftl( "Could not allocate site seed info: %d\n", errno );
return NULL;
}
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
if (!sitePasswordSeed) {
ftl( "Could not allocate site seed: %d\n", errno );
return NULL;
}
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
// Determine the template.
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
trc( "type %d, template: %s\n", siteType, template );
if (strlen( template ) > 32) {
ftl( "Template too long for password seed: %lu", strlen( template ) );
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return NULL;
}
// Encode the password from the seed using the template.
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
for (size_t c = 0; c < strlen( template ); ++c) {
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
sitePassword[c] );
}
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return sitePassword;
}

View File

@ -0,0 +1,107 @@
//
// mpw-bench.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <scrypt/sha256.h>
#include <bcrypt/ow-crypt.h>
#include "mpw-algorithm.h"
#include "mpw-util.h"
#define MP_N 32768
#define MP_r 8
#define MP_p 2
#define MP_dkLen 64
#define MP_hash PearlHashSHA256
static void mpw_getTime(struct timeval *time) {
if (gettimeofday( time, NULL ) != 0)
ftl( "Could not get time: %d\n", errno );
}
static const double mpw_showSpeed(struct timeval startTime, const unsigned int iterations, const char *operation) {
struct timeval endTime;
mpw_getTime( &endTime );
const time_t dsec = (endTime.tv_sec - startTime.tv_sec);
const suseconds_t dusec = (endTime.tv_usec - startTime.tv_usec);
const double elapsed = dsec + dusec / 1000000.;
const double speed = iterations / elapsed;
fprintf( stderr, " done. " );
fprintf( stdout, "%d %s iterations in %llds %lldµs -> %.2f/s\n", iterations, operation, (long long)dsec, (long long)dusec, speed );
return speed;
}
int main(int argc, char *const argv[]) {
const char *fullName = "Robert Lee Mitchel";
const char *masterPassword = "banana colored duckling";
const char *siteName = "masterpasswordapp.com";
const uint32_t siteCounter = 1;
const MPSiteType siteType = MPSiteTypeGeneratedLong;
const MPSiteVariant siteVariant = MPSiteVariantPassword;
const char *siteContext = NULL;
struct timeval startTime;
// Start MPW
unsigned int iterations = 100;
mpw_getTime( &startTime );
for (int i = 0; i < iterations; ++i) {
const uint8_t *masterKey = mpw_masterKeyForUser(
fullName, masterPassword, MPAlgorithmVersionCurrent );
if (!masterKey)
ftl( "Could not allocate master key: %d\n", errno );
free( (void *)mpw_passwordForSite(
masterKey, siteName, siteType, siteCounter, siteVariant, siteContext, MPAlgorithmVersionCurrent ) );
free( (void *)masterKey );
if (i % 1 == 0)
fprintf( stderr, "\rmpw: iteration %d / %d..", i, iterations );
}
const double mpwSpeed = mpw_showSpeed( startTime, iterations, "mpw" );
// Start SHA-256
iterations = 50000000;
uint8_t hash[32];
mpw_getTime( &startTime );
for (int i = 0; i < iterations; ++i) {
SHA256_Buf( masterPassword, strlen( masterPassword ), hash );
if (i % 1000 == 0)
fprintf( stderr, "\rsha256: iteration %d / %d..", i, iterations );
}
const double sha256Speed = mpw_showSpeed( startTime, iterations, "sha256" );
// Start BCrypt
int bcrypt_cost = 9;
iterations = 600;
mpw_getTime( &startTime );
for (int i = 0; i < iterations; ++i) {
crypt( masterPassword, crypt_gensalt( "$2b$", bcrypt_cost, fullName, strlen( fullName ) ) );
if (i % 10 == 0)
fprintf( stderr, "\rbcrypt (cost %d): iteration %d / %d..", bcrypt_cost, i, iterations );
}
const double bcrypt9Speed = mpw_showSpeed( startTime, iterations, "bcrypt9" );
// Summarize.
fprintf( stdout, "\n== SUMMARY ==\nOn this machine,\n" );
fprintf( stdout, " - mpw is %f times slower than sha256.\n", sha256Speed / mpwSpeed );
fprintf( stdout, " - mpw is %f times slower than bcrypt (cost 9).\n", bcrypt9Speed / mpwSpeed );
return 0;
}

222
MasterPassword/C/mpw-cli.c Normal file
View File

@ -0,0 +1,222 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#if defined(READLINE)
#include <readline/readline.h>
#elif defined(EDITLINE)
#include <histedit.h>
#endif
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); exit(2); } while (0)
#include "mpw-algorithm.h"
#include "mpw-util.h"
#define MP_env_fullname "MP_FULLNAME"
#define MP_env_sitetype "MP_SITETYPE"
#define MP_env_sitecounter "MP_SITECOUNTER"
#define MP_env_algorithm "MP_ALGORITHM"
static void usage() {
fprintf( stderr, "Usage: mpw [-u name] [-t type] [-c counter] [-V version] [-v variant] [-C context] [-h] site\n\n" );
fprintf( stderr, " -u name Specify the full name of the user.\n"
" Defaults to %s in env.\n\n", MP_env_fullname );
fprintf( stderr, " -t type Specify the password's template.\n"
" Defaults to %s in env or 'long' for password, 'name' for login.\n"
" x, max, maximum | 20 characters, contains symbols.\n"
" l, long | Copy-friendly, 14 characters, contains symbols.\n"
" m, med, medium | Copy-friendly, 8 characters, contains symbols.\n"
" b, basic | 8 characters, no symbols.\n"
" s, short | Copy-friendly, 4 characters, no symbols.\n"
" i, pin | 4 numbers.\n"
" n, name | 9 letter name.\n"
" p, phrase | 20 character sentence.\n\n", MP_env_sitetype );
fprintf( stderr, " -c counter The value of the counter.\n"
" Defaults to %s in env or 1.\n\n", MP_env_sitecounter );
fprintf( stderr, " -V version The algorithm version to use.\n"
" Defaults to %s in env or %d.\n\n", MP_env_algorithm, MPAlgorithmVersionCurrent );
fprintf( stderr, " -v variant The kind of content to generate.\n"
" Defaults to 'password'.\n"
" p, password | The password to log in with.\n"
" l, login | The username to log in as.\n"
" a, answer | The answer to a security question.\n\n" );
fprintf( stderr, " -C context A variant-specific context.\n"
" Defaults to empty.\n"
" -v p, password | Doesn't currently use a context.\n"
" -v l, login | Doesn't currently use a context.\n"
" -v a, answer | Empty for a universal site answer or\n"
" | the most significant word(s) of the question.\n\n" );
fprintf( stderr, " ENVIRONMENT\n\n"
" MP_FULLNAME | The full name of the user.\n"
" MP_SITETYPE | The default password template.\n"
" MP_SITECOUNTER | The default counter value.\n\n" );
exit( 0 );
}
static char *homedir(const char *filename) {
char *homedir = NULL;
struct passwd *passwd = getpwuid( getuid() );
if (passwd)
homedir = passwd->pw_dir;
if (!homedir)
homedir = getenv( "HOME" );
if (!homedir)
homedir = getcwd( NULL, 0 );
char *homefile = NULL;
asprintf( &homefile, "%s/%s", homedir, filename );
return homefile;
}
static char *getlinep(const char *prompt) {
char *buf = NULL;
size_t bufSize = 0;
ssize_t lineSize;
fprintf( stderr, "%s", prompt );
fprintf( stderr, " " );
if ((lineSize = getline( &buf, &bufSize, stdin )) < 0) {
free( buf );
return NULL;
}
buf[lineSize - 1] = 0;
return buf;
}
int main(int argc, char *const argv[]) {
// Read the environment.
char *fullName = getenv( MP_env_fullname );
const char *masterPassword = NULL;
const char *siteName = NULL;
MPSiteType siteType = MPSiteTypeGeneratedLong;
const char *siteTypeString = getenv( MP_env_sitetype );
MPSiteVariant siteVariant = MPSiteVariantPassword;
const char *siteVariantString = NULL;
const char *siteContextString = NULL;
uint32_t siteCounter = 1;
const char *siteCounterString = getenv( MP_env_sitecounter );
MPAlgorithmVersion algorithmVersion = MPAlgorithmVersionCurrent;
const char *algorithmVersionString = getenv( MP_env_algorithm );
if (algorithmVersionString && strlen( algorithmVersionString ))
if (sscanf( algorithmVersionString, "%u", &algorithmVersion ) != 1)
ftl( "Invalid %s: %s\n", MP_env_algorithm, algorithmVersionString );
// Read the options.
for (int opt; (opt = getopt( argc, argv, "u:P:t:c:v:V:C:h" )) != -1;)
switch (opt) {
case 'u':
fullName = optarg;
break;
case 'P':
// Do not use this. Passing your master password via the command-line
// is insecure. This is here for non-interactive testing purposes only.
masterPassword = strcpy( malloc( strlen( optarg ) + 1 ), optarg );
break;
case 't':
siteTypeString = optarg;
break;
case 'c':
siteCounterString = optarg;
break;
case 'v':
siteVariantString = optarg;
break;
case 'V':
if (sscanf( optarg, "%u", &algorithmVersion ) != 1)
ftl( "Not a version: %s\n", optarg );
break;
case 'C':
siteContextString = optarg;
break;
case 'h':
usage();
break;
case '?':
switch (optopt) {
case 'u':
ftl( "Missing full name to option: -%c\n", optopt );
break;
case 't':
ftl( "Missing type name to option: -%c\n", optopt );
break;
case 'c':
ftl( "Missing counter value to option: -%c\n", optopt );
break;
default:
ftl( "Unknown option: -%c\n", optopt );
}
default:
ftl("Unexpected option: %c", opt);
}
if (optind < argc)
siteName = argv[optind];
// Convert and validate input.
if (!fullName && !(fullName = getlinep( "Your full name:" )))
ftl( "Missing full name.\n" );
if (!siteName && !(siteName = getlinep( "Site name:" )))
ftl( "Missing site name.\n" );
if (siteCounterString)
siteCounter = (uint32_t)atol( siteCounterString );
if (siteCounter < 1)
ftl( "Invalid site counter: %d\n", siteCounter );
if (siteVariantString)
siteVariant = mpw_variantWithName( siteVariantString );
if (siteVariant == MPSiteVariantLogin)
siteType = MPSiteTypeGeneratedName;
if (siteVariant == MPSiteVariantAnswer)
siteType = MPSiteTypeGeneratedPhrase;
if (siteTypeString)
siteType = mpw_typeWithName( siteTypeString );
trc( "algorithmVersion: %u\n", algorithmVersion );
// Read the master password.
char *mpwConfigPath = homedir( ".mpw" );
if (!mpwConfigPath)
ftl( "Couldn't resolve path for configuration file: %d\n", errno );
trc( "mpwConfigPath: %s\n", mpwConfigPath );
FILE *mpwConfig = fopen( mpwConfigPath, "r" );
free( mpwConfigPath );
if (mpwConfig) {
char *line = NULL;
size_t linecap = 0;
while (getline( &line, &linecap, mpwConfig ) > 0) {
char *lineData = line;
if (strcmp( strsep( &lineData, ":" ), fullName ) == 0) {
masterPassword = strcpy( malloc( strlen( lineData ) ), strsep( &lineData, "\n" ) );
break;
}
}
mpw_free( line, linecap );
}
while (!masterPassword || !strlen(masterPassword))
masterPassword = getpass( "Your master password: " );
// Summarize operation.
fprintf( stderr, "%s's password for %s:\n[ %s ]: ", fullName, siteName, mpw_identicon( fullName, masterPassword ) );
// Output the password.
const uint8_t *masterKey = mpw_masterKeyForUser(
fullName, masterPassword, algorithmVersion );
mpw_freeString( masterPassword );
if (!masterKey)
ftl( "Couldn't derive master key." );
const char *sitePassword = mpw_passwordForSite(
masterKey, siteName, siteType, siteCounter, siteVariant, siteContextString, algorithmVersion );
mpw_free( masterKey, MP_dkLen );
if (!sitePassword)
ftl( "Couldn't derive site password." );
fprintf( stdout, "%s\n", sitePassword );
return 0;
}

View File

@ -1,21 +1,3 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
//
// mpw-tests-util.c
// MasterPassword
@ -32,23 +14,31 @@
#include "mpw-tests-util.h"
xmlNodePtr mpw_xmlTestCaseNode(xmlNodePtr testCaseNode, const char *nodeName) {
static xmlChar const *mpw_xmlPath(xmlNodePtr context) {
if (!nodeName)
return NULL;
if (context->parent) {
char *string = calloc( 256, 1 );
snprintf( string, 256, "%s/%s", mpw_xmlPath( context->parent ), context->name );
return BAD_CAST string;
}
return context->name? context->name: (xmlChar const *)"";
}
xmlNodePtr mpw_xmlTestCaseNode(xmlNodePtr testCaseNode, const char *nodeName) {
// Try to find an attribute node.
for (xmlAttrPtr child = testCaseNode->properties; child; child = child->next)
if (xmlStrcmp( child->name, BAD_CAST nodeName ) == OK)
if (xmlStrcmp( child->name, BAD_CAST nodeName ) == 0)
return (xmlNodePtr)child;
// Try to find an element node.
for (xmlNodePtr child = testCaseNode->children; child; child = child->next)
if (xmlStrcmp( child->name, BAD_CAST nodeName ) == OK)
if (xmlStrcmp( child->name, BAD_CAST nodeName ) == 0)
return child;
// Missing content, try to find parent case.
if (strcmp( nodeName, "parent" ) == OK)
if (strcmp(nodeName, "parent") == 0)
// Was just searching for testCaseNode's parent, none found.
return NULL;
xmlChar *parentId = mpw_xmlTestCaseString( testCaseNode, "parent" );
@ -58,7 +48,7 @@ xmlNodePtr mpw_xmlTestCaseNode(xmlNodePtr testCaseNode, const char *nodeName) {
for (xmlNodePtr otherTestCaseNode = testCaseNode->parent->children; otherTestCaseNode; otherTestCaseNode = otherTestCaseNode->next) {
xmlChar *id = mpw_xmlTestCaseString( otherTestCaseNode, "id" );
int foundParent = id && xmlStrcmp( id, parentId ) == OK;
int foundParent = xmlStrcmp( id, parentId ) == 0;
xmlFree( id );
if (foundParent) {
@ -67,26 +57,19 @@ xmlNodePtr mpw_xmlTestCaseNode(xmlNodePtr testCaseNode, const char *nodeName) {
}
}
err( "Missing parent: %s, for case: %s", parentId, mpw_xmlTestCaseString( testCaseNode, "id" ) );
return NULL;
ftl( "Missing parent: %s, for case: %s\n", parentId, mpw_xmlTestCaseString( testCaseNode, "id" ) );
}
xmlChar *mpw_xmlTestCaseString(xmlNodePtr context, const char *nodeName) {
if (!nodeName)
return NULL;
xmlNodePtr child = mpw_xmlTestCaseNode( context, nodeName );
return child? xmlNodeGetContent( child ): NULL;
return xmlNodeGetContent( child );
}
uint32_t mpw_xmlTestCaseInteger(xmlNodePtr context, const char *nodeName) {
if (!nodeName)
return 0;
xmlChar *string = mpw_xmlTestCaseString( context, nodeName );
uint32_t integer = string? (uint32_t)strtoul( (char *)string, NULL, 10 ): 0;
uint32_t integer = atol( (char *)string );
xmlFree( string );
return integer;

View File

@ -0,0 +1,16 @@
//
// mpw-tests-util.h
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-21.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <libxml/parser.h>
xmlNodePtr mpw_xmlTestCaseNode(
xmlNodePtr testCaseNode, const char *nodeName);
xmlChar *mpw_xmlTestCaseString(
xmlNodePtr context, const char *nodeName);
uint32_t mpw_xmlTestCaseInteger(
xmlNodePtr context, const char *nodeName);

View File

@ -0,0 +1,81 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); exit(2); } while (0)
#include "mpw-algorithm.h"
#include "mpw-util.h"
#include "mpw-tests-util.h"
int main(int argc, char *const argv[]) {
int failedTests = 0;
xmlNodePtr tests = xmlDocGetRootElement( xmlParseFile( "mpw_tests.xml" ) );
for (xmlNodePtr testCase = tests->children; testCase; testCase = testCase->next) {
if (testCase->type != XML_ELEMENT_NODE || xmlStrcmp( testCase->name, BAD_CAST "case" ) != 0)
continue;
// Read in the test case.
xmlChar *id = mpw_xmlTestCaseString( testCase, "id" );
uint32_t algorithm = mpw_xmlTestCaseInteger( testCase, "algorithm" );
xmlChar *fullName = mpw_xmlTestCaseString( testCase, "fullName" );
xmlChar *masterPassword = mpw_xmlTestCaseString( testCase, "masterPassword" );
xmlChar *keyID = mpw_xmlTestCaseString( testCase, "keyID" );
xmlChar *siteName = mpw_xmlTestCaseString( testCase, "siteName" );
uint32_t siteCounter = mpw_xmlTestCaseInteger( testCase, "siteCounter" );
xmlChar *siteTypeString = mpw_xmlTestCaseString( testCase, "siteType" );
xmlChar *siteVariantString = mpw_xmlTestCaseString( testCase, "siteVariant" );
xmlChar *siteContext = mpw_xmlTestCaseString( testCase, "siteContext" );
xmlChar *result = mpw_xmlTestCaseString( testCase, "result" );
MPSiteType siteType = mpw_typeWithName( (char *)siteTypeString );
MPSiteVariant siteVariant = mpw_variantWithName( (char *)siteVariantString );
// Run the test case.
fprintf( stdout, "test case %s... ", id );
if (!xmlStrlen( result )) {
fprintf( stdout, "abstract.\n" );
continue;
}
// 1. calculate the master key.
const uint8_t *masterKey = mpw_masterKeyForUser(
(char *)fullName, (char *)masterPassword, algorithm );
if (!masterKey)
ftl( "Couldn't derive master key." );
// 2. calculate the site password.
const char *sitePassword = mpw_passwordForSite(
masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext, algorithm );
mpw_free( masterKey, MP_dkLen );
if (!sitePassword)
ftl( "Couldn't derive site password." );
// Check the result.
if (xmlStrcmp( result, BAD_CAST sitePassword ) == 0)
fprintf( stdout, "pass.\n" );
else {
++failedTests;
fprintf( stdout, "FAILED! (got %s != expected %s)\n", sitePassword, result );
}
// Free test case.
mpw_freeString( sitePassword );
xmlFree( id );
xmlFree( fullName );
xmlFree( masterPassword );
xmlFree( keyID );
xmlFree( siteName );
xmlFree( siteTypeString );
xmlFree( siteVariantString );
xmlFree( siteContext );
xmlFree( result );
}
return failedTests;
}

View File

@ -0,0 +1,195 @@
//
// mpw-types.c
// MasterPassword
//
// Created by Maarten Billemont on 2012-02-01.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#ifdef COLOR
#include <curses.h>
#include <term.h>
#endif
#include "mpw-types.h"
#include "mpw-util.h"
const MPSiteType mpw_typeWithName(const char *typeName) {
// Lower-case and trim optionally leading "Generated" string from typeName to standardize it.
size_t stdTypeNameOffset = 0;
size_t stdTypeNameSize = strlen( typeName );
if (strstr(typeName, "Generated" ) == typeName)
stdTypeNameSize -= (stdTypeNameOffset = strlen( "Generated" ));
char stdTypeName[stdTypeNameSize + 1];
for (size_t c = 0; c < stdTypeNameSize; ++c)
stdTypeName[c] = (char)tolower( typeName[c + stdTypeNameOffset] );
stdTypeName[stdTypeNameSize] = '\0';
// Find what site type is represented by the type name.
if (0 == strcmp( stdTypeName, "x" ) || 0 == strcmp( stdTypeName, "max" ) || 0 == strcmp( stdTypeName, "maximum" ))
return MPSiteTypeGeneratedMaximum;
if (0 == strcmp( stdTypeName, "l" ) || 0 == strcmp( stdTypeName, "long" ))
return MPSiteTypeGeneratedLong;
if (0 == strcmp( stdTypeName, "m" ) || 0 == strcmp( stdTypeName, "med" ) || 0 == strcmp( stdTypeName, "medium" ))
return MPSiteTypeGeneratedMedium;
if (0 == strcmp( stdTypeName, "b" ) || 0 == strcmp( stdTypeName, "basic" ))
return MPSiteTypeGeneratedBasic;
if (0 == strcmp( stdTypeName, "s" ) || 0 == strcmp( stdTypeName, "short" ))
return MPSiteTypeGeneratedShort;
if (0 == strcmp( stdTypeName, "i" ) || 0 == strcmp( stdTypeName, "pin" ))
return MPSiteTypeGeneratedPIN;
if (0 == strcmp( stdTypeName, "n" ) || 0 == strcmp( stdTypeName, "name" ))
return MPSiteTypeGeneratedName;
if (0 == strcmp( stdTypeName, "p" ) || 0 == strcmp( stdTypeName, "phrase" ))
return MPSiteTypeGeneratedPhrase;
ftl( "Not a generated type name: %s", stdTypeName );
}
const char **mpw_templatesForType(MPSiteType type, size_t *count) {
if (!(type & MPSiteTypeClassGenerated)) {
ftl( "Not a generated type: %d", type );
*count = 0;
return NULL;
}
switch (type) {
case MPSiteTypeGeneratedMaximum: {
return alloc_array( *count, const char *,
"anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" );
}
case MPSiteTypeGeneratedLong: {
return alloc_array( *count, const char *,
"CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" );
}
case MPSiteTypeGeneratedMedium: {
return alloc_array( *count, const char *,
"CvcnoCvc", "CvcCvcno" );
}
case MPSiteTypeGeneratedBasic: {
return alloc_array( *count, const char *,
"aaanaaan", "aannaaan", "aaannaaa" );
}
case MPSiteTypeGeneratedShort: {
return alloc_array( *count, const char *,
"Cvcn" );
}
case MPSiteTypeGeneratedPIN: {
return alloc_array( *count, const char *,
"nnnn" );
}
case MPSiteTypeGeneratedName: {
return alloc_array( *count, const char *,
"cvccvcvcv" );
}
case MPSiteTypeGeneratedPhrase: {
return alloc_array( *count, const char *,
"cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" );
}
default: {
ftl( "Unknown generated type: %d", type );
*count = 0;
return NULL;
}
}
}
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) {
size_t count = 0;
const char **templates = mpw_templatesForType( type, &count );
if (!count)
return NULL;
char const *template = templates[seedByte % count];
free( templates );
return template;
}
const MPSiteVariant mpw_variantWithName(const char *variantName) {
// Lower-case and trim optionally leading "generated" string from typeName to standardize it.
size_t stdVariantNameSize = strlen( variantName );
char stdVariantName[stdVariantNameSize + 1];
for (size_t c = 0; c < stdVariantNameSize; ++c)
stdVariantName[c] = (char)tolower( variantName[c] );
stdVariantName[stdVariantNameSize] = '\0';
if (0 == strcmp( stdVariantName, "p" ) || 0 == strcmp( stdVariantName, "password" ))
return MPSiteVariantPassword;
if (0 == strcmp( stdVariantName, "l" ) || 0 == strcmp( stdVariantName, "login" ))
return MPSiteVariantLogin;
if (0 == strcmp( stdVariantName, "a" ) || 0 == strcmp( stdVariantName, "answer" ))
return MPSiteVariantAnswer;
fprintf( stderr, "Not a variant name: %s", stdVariantName );
abort();
}
const char *mpw_scopeForVariant(MPSiteVariant variant) {
switch (variant) {
case MPSiteVariantPassword: {
return "com.lyndir.masterpassword";
}
case MPSiteVariantLogin: {
return "com.lyndir.masterpassword.login";
}
case MPSiteVariantAnswer: {
return "com.lyndir.masterpassword.answer";
}
default: {
fprintf( stderr, "Unknown variant: %d", variant );
abort();
}
}
}
const char *mpw_charactersInClass(char characterClass) {
switch (characterClass) {
case 'V':
return "AEIOU";
case 'C':
return "BCDFGHJKLMNPQRSTVWXYZ";
case 'v':
return "aeiou";
case 'c':
return "bcdfghjklmnpqrstvwxyz";
case 'A':
return "AEIOUBCDFGHJKLMNPQRSTVWXYZ";
case 'a':
return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
case 'n':
return "0123456789";
case 'o':
return "@&%?,=[]_:-+*$#!'^~;()/.";
case 'x':
return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
case ' ':
return " ";
default: {
fprintf( stderr, "Unknown character class: %c", characterClass );
abort();
}
}
}
const char mpw_characterFromClass(char characterClass, uint8_t seedByte) {
const char *classCharacters = mpw_charactersInClass( characterClass );
return classCharacters[seedByte % strlen( classCharacters )];
}

View File

@ -0,0 +1,99 @@
//
// mpw-types.h
// MasterPassword
//
// Created by Maarten Billemont on 2012-02-01.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#ifndef _MPW_TYPES_H
#define _MPW_TYPES_H
#include <stdlib.h>
#include <stdint.h>
#ifdef NS_ENUM
#define enum(_type, _name) NS_ENUM(_type, _name)
#else
#define enum(_type, _name) _type _name; enum
#endif
#define MP_dkLen 64
//// Types.
typedef enum( unsigned int, MPSiteVariant ) {
/** Generate the password to log in with. */
MPSiteVariantPassword,
/** Generate the login name to log in as. */
MPSiteVariantLogin,
/** Generate the answer to a security question. */
MPSiteVariantAnswer,
};
typedef enum( unsigned int, MPSiteTypeClass ) {
/** Generate the password. */
MPSiteTypeClassGenerated = 1 << 4,
/** Store the password. */
MPSiteTypeClassStored = 1 << 5,
};
typedef enum( unsigned int, MPSiteFeature ) {
/** Export the key-protected content data. */
MPSiteFeatureExportContent = 1 << 10,
/** Never export content. */
MPSiteFeatureDevicePrivate = 1 << 11,
};
typedef enum( unsigned int, MPSiteType) {
MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0,
MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0,
MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0,
MPSiteTypeGeneratedBasic = 0x4 | MPSiteTypeClassGenerated | 0x0,
MPSiteTypeGeneratedShort = 0x3 | MPSiteTypeClassGenerated | 0x0,
MPSiteTypeGeneratedPIN = 0x5 | MPSiteTypeClassGenerated | 0x0,
MPSiteTypeGeneratedName = 0xE | MPSiteTypeClassGenerated | 0x0,
MPSiteTypeGeneratedPhrase = 0xF | MPSiteTypeClassGenerated | 0x0,
MPSiteTypeStoredPersonal = 0x0 | MPSiteTypeClassStored | MPSiteFeatureExportContent,
MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate,
};
//// Type utilities.
/**
* @return The variant represented by the given name.
*/
const MPSiteVariant mpw_variantWithName(const char *variantName);
/**
* @return An internal string containing the scope identifier to apply when encoding for the given variant.
*/
const char *mpw_scopeForVariant(MPSiteVariant variant);
/**
* @return The type represented by the given name.
*/
const MPSiteType mpw_typeWithName(const char *typeName);
/**
* @return A newly allocated array of internal strings that express the templates to use for the given type.
* The amount of elements in the array is stored in count.
* If an unsupported type is given, count will be 0 and will return NULL.
* The array needs to be free'ed, the strings themselves must not be free'ed or modified.
*/
const char **mpw_templatesForType(MPSiteType type, size_t *count);
/**
* @return An internal string that contains the password encoding template of the given type
* for a seed that starts with the given byte.
*/
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte);
/**
* @return An internal string that contains all the characters that occur in the given character class.
*/
const char *mpw_charactersInClass(char characterClass);
/**
* @return A character from given character class that encodes the given byte.
*/
const char mpw_characterFromClass(char characterClass, uint8_t seedByte);
#endif // _MPW_TYPES_H

218
MasterPassword/C/mpw-util.c Normal file
View File

@ -0,0 +1,218 @@
//
// mpw-util.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef COLOR
#include <unistd.h>
#include <curses.h>
#include <term.h>
#endif
#include <scrypt/sha256.h>
#include <scrypt/crypto_scrypt.h>
#include "mpw-util.h"
void mpw_pushBuf(uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize) {
if (*bufferSize == (size_t)-1)
// The buffer was marked as broken, it is missing a previous push. Abort to avoid corrupt content.
return;
*bufferSize += pushSize;
uint8_t *resizedBuffer = realloc( *buffer, *bufferSize );
if (!resizedBuffer) {
// realloc failed, we can't push. Mark the buffer as broken.
mpw_free( *buffer, *bufferSize - pushSize );
*bufferSize = (size_t)-1;
*buffer = NULL;
return;
}
*buffer = resizedBuffer;
uint8_t *pushDst = *buffer + *bufferSize - pushSize;
memcpy( pushDst, pushBuffer, pushSize );
}
void mpw_pushString(uint8_t **buffer, size_t *const bufferSize, const char *pushString) {
mpw_pushBuf( buffer, bufferSize, pushString, strlen( pushString ) );
}
void mpw_pushInt(uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt) {
mpw_pushBuf( buffer, bufferSize, &pushInt, sizeof( pushInt ) );
}
void mpw_free(const void *buffer, const size_t bufferSize) {
memset( (void *)buffer, 0, bufferSize );
free( (void *)buffer );
}
void mpw_freeString(const char *string) {
mpw_free( string, strlen( string ) );
}
uint8_t const *mpw_scrypt(const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize,
uint64_t N, uint32_t r, uint32_t p) {
uint8_t *key = malloc( keySize );
if (!key)
return NULL;
if (crypto_scrypt( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) < 0) {
mpw_free( key, keySize );
return NULL;
}
return key;
}
uint8_t const *mpw_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize) {
uint8_t *const buffer = malloc( 32 );
if (!buffer)
return NULL;
HMAC_SHA256_Buf( key, keySize, salt, saltSize, buffer );
return buffer;
}
const char *mpw_idForBuf(const void *buf, size_t length) {
uint8_t hash[32];
SHA256_Buf( buf, length, hash );
return mpw_hex( hash, 32 );
}
static char **mpw_hex_buf = NULL;
static unsigned int mpw_hex_buf_i = 0;
const char *mpw_hex(const void *buf, size_t length) {
// FIXME
if (!mpw_hex_buf) {
mpw_hex_buf = malloc( 10 * sizeof( char * ) );
for (uint8_t i = 0; i < 10; ++i)
mpw_hex_buf[i] = NULL;
}
mpw_hex_buf_i = (mpw_hex_buf_i + 1) % 10;
mpw_hex_buf[mpw_hex_buf_i] = realloc( mpw_hex_buf[mpw_hex_buf_i], length * 2 + 1 );
for (size_t kH = 0; kH < length; kH++)
sprintf( &(mpw_hex_buf[mpw_hex_buf_i][kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
return mpw_hex_buf[mpw_hex_buf_i];
}
const char *mpw_hex_l(uint32_t number) {
return mpw_hex( &number, sizeof( number ) );
}
#ifdef COLOR
static int putvari;
static char *putvarc = NULL;
static int istermsetup = 0;
static void initputvar() {
if (putvarc)
free(putvarc);
putvarc=(char *)calloc(256, sizeof(char));
putvari=0;
if (!istermsetup)
istermsetup = (OK == setupterm(NULL, STDERR_FILENO, NULL));
}
static int putvar(int c) {
putvarc[putvari++]=c;
return 0;
}
#endif
const char *mpw_identicon(const char *fullName, const char *masterPassword) {
const char *leftArm[] = { "", "", "", "" };
const char *rightArm[] = { "", "", "", "" };
const char *body[] = { "", "", "", "", "", "" };
const char *accessory[] = {
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
};
uint8_t identiconSeed[32];
HMAC_SHA256_Buf( masterPassword, strlen( masterPassword ), fullName, strlen( fullName ), identiconSeed );
char *colorString, *resetString;
#ifdef COLOR
if (isatty( STDERR_FILENO )) {
uint8_t colorIdentifier = (uint8_t)(identiconSeed[4] % 7 + 1);
initputvar();
tputs(tparm(tgetstr("AF", NULL), colorIdentifier), 1, putvar);
colorString = calloc(strlen(putvarc) + 1, sizeof(char));
strcpy(colorString, putvarc);
tputs(tgetstr("me", NULL), 1, putvar);
resetString = calloc(strlen(putvarc) + 1, sizeof(char));
strcpy(resetString, putvarc);
} else
#endif
{
colorString = calloc( 1, sizeof( char ) );
resetString = calloc( 1, sizeof( char ) );
}
char *identicon = (char *)calloc( 256, sizeof( char ) );
snprintf( identicon, 256, "%s%s%s%s%s%s",
colorString,
leftArm[identiconSeed[0] % (sizeof( leftArm ) / sizeof( leftArm[0] ))],
body[identiconSeed[1] % (sizeof( body ) / sizeof( body[0] ))],
rightArm[identiconSeed[2] % (sizeof( rightArm ) / sizeof( rightArm[0] ))],
accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( accessory[0] ))],
resetString );
free( colorString );
free( resetString );
return identicon;
}
/**
* @return the amount of bytes used by UTF-8 to encode a single character that starts with the given byte.
*/
static int mpw_charByteSize(unsigned char utf8Byte) {
if (!utf8Byte)
return 0;
if ((utf8Byte & 0x80) == 0)
return 1;
if ((utf8Byte & 0xC0) != 0xC0)
return 0;
if ((utf8Byte & 0xE0) == 0xC0)
return 2;
if ((utf8Byte & 0xF0) == 0xE0)
return 3;
if ((utf8Byte & 0xF8) == 0xF0)
return 4;
return 0;
}
const size_t mpw_charlen(const char *utf8String) {
size_t charlen = 0;
char *remainingString = (char *)utf8String;
for (int charByteSize; (charByteSize = mpw_charByteSize( (unsigned char)*remainingString )); remainingString += charByteSize)
++charlen;
return charlen;
}

View File

@ -0,0 +1,80 @@
//
// mpw-util.h
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdint.h>
//// Logging.
#ifdef DEBUG
#ifndef trc
#define trc(...) fprintf( stderr, __VA_ARGS__ )
#endif
#else
#ifndef trc
#define trc(...) do {} while (0)
#endif
#endif
#ifndef ftl
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); abort(); } while (0)
#endif
//// Buffers and memory.
#define alloc_array(_count, _type, ...) ({ \
_type stackElements[] = { __VA_ARGS__ }; \
_count = sizeof( stackElements ) / sizeof( _type ); \
_type *allocElements = malloc( sizeof( stackElements ) ); \
memcpy( allocElements, stackElements, sizeof( stackElements ) ); \
allocElements; \
})
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
void mpw_pushBuf(
uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize);
/** Push a string onto a buffer. reallocs the given buffer and appends the given string. */
void mpw_pushString(
uint8_t **buffer, size_t *const bufferSize, const char *pushString);
/** Push an integer onto a buffer. reallocs the given buffer and appends the given integer. */
void mpw_pushInt(
uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt);
/** Free a buffer after zero'ing its contents. */
void mpw_free(
const void *buffer, const size_t bufferSize);
/** Free a string after zero'ing its contents. */
void mpw_freeString(
const char *string);
//// Cryptographic functions.
/** Perform a scrypt-based key derivation on the given key using the given salt and scrypt parameters.
* @return A new keySize-size allocated buffer. */
uint8_t const *mpw_scrypt(
const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize,
uint64_t N, uint32_t r, uint32_t p);
/** Calculate a SHA256-based HMAC by encrypting the given salt with the given key.
* @return A new 32-byte allocated buffer. */
uint8_t const *mpw_hmac_sha256(
const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize);
//// Visualizers.
/** Encode a buffer as a string of hexadecimal characters.
* @return A C-string in a reused buffer, do not free or store it. */
const char *mpw_hex(const void *buf, size_t length);
const char *mpw_hex_l(uint32_t number);
/** Encode a fingerprint for a buffer.
* @return A C-string in a reused buffer, do not free or store it. */
const char *mpw_idForBuf(const void *buf, size_t length);
/** Encode a visual fingerprint for a user.
* @return A newly allocated string. */
const char *mpw_identicon(const char *fullName, const char *masterPassword);
//// String utilities.
/** @return The amount of display characters in the given UTF-8 string. */
const size_t mpw_charlen(const char *utf8String);

View File

@ -5,7 +5,7 @@ mpw() {
if hash pbcopy 2>/dev/null; then
pbcopy
elif hash xclip 2>/dev/null; then
xclip -selection clip
xclip
else
cat; echo 2>/dev/null
return
@ -17,8 +17,8 @@ mpw() {
:| _copy 2>/dev/null
# Ask for the user's name and password if not yet known.
MPW_FULLNAME=${MPW_FULLNAME:-$(ask 'Your Full Name:')}
MP_FULLNAME=${MP_FULLNAME:-$(ask 'Your Full Name:')}
# Start Master Password and copy the output.
printf %s "$(MPW_FULLNAME=$MPW_FULLNAME command mpw "$@")" | _copy
printf %s "$(MP_FULLNAME=$MP_FULLNAME command mpw "$@")" | _copy
}

View File

@ -0,0 +1 @@
../Java/masterpassword-algorithm/src/test/resources/mpw_tests.xml

2
MasterPassword/Java/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
target
dependency-reduced-pom.xml

View File

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- PROJECT METADATA -->
<parent>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword</artifactId>
<version>2.3</version>
</parent>
<name>Master Password Algorithm Implementation</name>
<description>The implementation of the Master Password algorithm</description>
<artifactId>masterpassword-algorithm</artifactId>
<packaging>jar</packaging>
<!-- DEPENDENCY MANAGEMENT -->
<dependencies>
<!-- PROJECT REFERENCES -->
<dependency>
<groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-system</artifactId>
<version>1.6-p8</version>
</dependency>
<dependency>
<groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-crypto</artifactId>
<version>1.6-p8</version>
</dependency>
<!-- EXTERNAL DEPENDENCIES -->
<dependency>
<groupId>com.lambdaworks</groupId>
<artifactId>scrypt</artifactId>
<version>1.4.0</version>
</dependency>
<!-- TESTING -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,106 @@
package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.google.common.collect.ImmutableMap;
import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.awt.*;
import java.nio.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
/**
* @author lhunath, 15-03-29
*/
public class MPIdenticon {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPIdenticon.class );
private static final Charset charset = StandardCharsets.UTF_8;
private static final Color[] colors = new Color[]{
Color.RED, Color.GREEN, Color.YELLOW, Color.BLUE, Color.MAGENTA, Color.CYAN, Color.MONO };
private static final char[] leftArm = new char[]{ '╔', '╚', '╰', '═' };
private static final char[] rightArm = new char[]{ '╗', '╝', '╯', '═' };
private static final char[] body = new char[]{ '█', '░', '▒', '▓', '☺', '☻' };
private static final char[] accessory = new char[]{
'◈', '◎', '◐', '◑', '◒', '◓', '☀', '☁', '☂', '☃', '☄', '★', '☆', '☎', '☏', '⎈', '⌂', '☘', '☢', '☣', '☕', '⌚', '⌛', '⏰', '⚡',
'⛄', '⛅', '☔', '♔', '♕', '♖', '♗', '♘', '♙', '♚', '♛', '♜', '♝', '♞', '♟', '♨', '♩', '♪', '♫', '⚐', '⚑', '⚔', '⚖', '⚙', '⚠',
'⌘', '⏎', '✄', '✆', '✈', '✉', '✌' };
private final String fullName;
private final Color color;
private final String text;
public MPIdenticon(String fullName, String masterPassword) {
this( fullName, masterPassword.toCharArray() );
}
public MPIdenticon(String fullName, char[] masterPassword) {
this.fullName = fullName;
byte[] masterPasswordBytes = charset.encode( CharBuffer.wrap( masterPassword ) ).array();
ByteBuffer identiconSeedBytes = ByteBuffer.wrap(
MessageAuthenticationDigests.HmacSHA256.of( masterPasswordBytes, fullName.getBytes( charset ) ) );
Arrays.fill( masterPasswordBytes, (byte) 0 );
IntBuffer identiconSeedBuffer = IntBuffer.allocate( identiconSeedBytes.capacity() );
while (identiconSeedBytes.hasRemaining())
identiconSeedBuffer.put( identiconSeedBytes.get() & 0xFF );
int[] identiconSeed = identiconSeedBuffer.array();
color = colors[identiconSeed[4] % colors.length];
text = strf( "%c%c%c%c", leftArm[identiconSeed[0] % leftArm.length], body[identiconSeed[1] % body.length],
rightArm[identiconSeed[2] % rightArm.length], accessory[identiconSeed[3] % accessory.length] );
}
public String getFullName() {
return fullName;
}
public String getText() {
return text;
}
public Color getColor() {
return color;
}
public enum BackgroundMode {
DARK, LIGHT
}
public enum Color {
RED( "#dc322f", "#dc322f" ),
GREEN( "#859900", "#859900" ),
YELLOW( "#b58900", "#b58900" ),
BLUE( "#268bd2", "#268bd2" ),
MAGENTA( "#d33682", "#d33682" ),
CYAN( "#2aa198", "#2aa198" ),
MONO( "#93a1a1", "#586e75" );
private final String rgbDark;
private final String rgbLight;
Color(final String rgbDark, final String rgbLight) {
this.rgbDark = rgbDark;
this.rgbLight = rgbLight;
}
public java.awt.Color getAWTColor(BackgroundMode backgroundMode) {
switch (backgroundMode) {
case DARK:
return new java.awt.Color( Integer.decode( rgbDark ) );
case LIGHT:
return new java.awt.Color( Integer.decode( rgbLight ) );
}
throw new UnsupportedOperationException( "Unsupported background mode: " + backgroundMode );
}
}
}

View File

@ -0,0 +1,29 @@
package com.lyndir.masterpassword;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public enum MPSiteFeature {
/**
* Export the key-protected content data.
*/
ExportContent( 1 << 10 ),
/**
* Never export content.
*/
DevicePrivate( 1 << 11 );
MPSiteFeature(final int mask) {
this.mask = mask;
}
private final int mask;
public int getMask() {
return mask;
}
}

View File

@ -0,0 +1,212 @@
package com.lyndir.masterpassword;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Contract;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public enum MPSiteType {
GeneratedMaximum( "20 characters, contains symbols.", //
ImmutableList.of( "x", "max", "maximum" ), //
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
MPSiteTypeClass.Generated, 0x0 ),
GeneratedLong( "Copy-friendly, 14 characters, contains symbols.", //
ImmutableList.of( "l", "long" ), //
ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ),
new MPTemplate( "CvcvnoCvccCvcv" ), new MPTemplate( "CvcvCvccnoCvcv" ),
new MPTemplate( "CvcvCvccCvcvno" ), new MPTemplate( "CvcvnoCvcvCvcc" ),
new MPTemplate( "CvcvCvcvnoCvcc" ), new MPTemplate( "CvcvCvcvCvccno" ),
new MPTemplate( "CvccnoCvccCvcv" ), new MPTemplate( "CvccCvccnoCvcv" ),
new MPTemplate( "CvccCvccCvcvno" ), new MPTemplate( "CvcvnoCvccCvcc" ),
new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ),
new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ),
new MPTemplate( "CvccCvcvCvccno" ) ), //
MPSiteTypeClass.Generated, 0x1 ),
GeneratedMedium( "Copy-friendly, 8 characters, contains symbols.", //
ImmutableList.of( "m", "med", "medium" ), //
ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), //
MPSiteTypeClass.Generated, 0x2 ),
GeneratedBasic( "8 characters, no symbols.", //
ImmutableList.of( "b", "basic" ), //
ImmutableList.of( new MPTemplate( "aaanaaan" ), new MPTemplate( "aannaaan" ), new MPTemplate( "aaannaaa" ) ), //
MPSiteTypeClass.Generated, 0x3 ),
GeneratedShort( "Copy-friendly, 4 characters, no symbols.", //
ImmutableList.of( "s", "short" ), //
ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
MPSiteTypeClass.Generated, 0x4 ),
GeneratedPIN( "4 numbers.", //
ImmutableList.of( "i", "pin" ), //
ImmutableList.of( new MPTemplate( "nnnn" ) ), //
MPSiteTypeClass.Generated, 0x5 ),
GeneratedName( "9 letter name.", //
ImmutableList.of( "n", "name" ), //
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
MPSiteTypeClass.Generated, 0xE ),
GeneratedPhrase( "20 character sentence.", //
ImmutableList.of( "p", "phrase" ), //
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ),
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
MPSiteTypeClass.Generated, 0xF ),
StoredPersonal( "AES-encrypted, exportable.", //
ImmutableList.of( "personal" ), //
ImmutableList.<MPTemplate>of(), //
MPSiteTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ),
StoredDevicePrivate( "AES-encrypted, not exported.", //
ImmutableList.of( "device" ), //
ImmutableList.<MPTemplate>of(), //
MPSiteTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate );
static final Logger logger = Logger.get( MPSiteType.class );
private final String description;
private final List<String> options;
private final List<MPTemplate> templates;
private final MPSiteTypeClass typeClass;
private final int typeIndex;
private final Set<MPSiteFeature> typeFeatures;
MPSiteType(final String description, final List<String> options, final List<MPTemplate> templates, final MPSiteTypeClass typeClass,
final int typeIndex, final MPSiteFeature... typeFeatures) {
this.description = description;
this.options = options;
this.templates = templates;
this.typeClass = typeClass;
this.typeIndex = typeIndex;
ImmutableSet.Builder<MPSiteFeature> typeFeaturesBuilder = ImmutableSet.builder();
for (final MPSiteFeature typeFeature : typeFeatures) {
typeFeaturesBuilder.add( typeFeature );
}
this.typeFeatures = typeFeaturesBuilder.build();
}
public String getDescription() {
return description;
}
public List<String> getOptions() {
return options;
}
public MPSiteTypeClass getTypeClass() {
return typeClass;
}
public Set<MPSiteFeature> getTypeFeatures() {
return typeFeatures;
}
public int getType() {
int mask = typeIndex | typeClass.getMask();
for (MPSiteFeature typeFeature : typeFeatures)
mask |= typeFeature.getMask();
return mask;
}
/**
* @param option The option to select a type with. It is matched case insensitively.
*
* @return The type registered for the given option.
*/
public static MPSiteType forOption(final String option) {
for (final MPSiteType type : values())
if (type.getOptions().contains( option.toLowerCase() ))
return type;
throw logger.bug( "No type for option: %s", option );
}
/**
* @param name The name fromInt the type to look up. It is matched case insensitively.
*
* @return The type registered with the given name.
*/
@Contract("!null -> !null, null -> null")
public static MPSiteType forName(@Nullable final String name) {
if (name == null)
return null;
for (final MPSiteType type : values())
if (type.name().equalsIgnoreCase( name ))
return type;
throw logger.bug( "No type for name: %s", name );
}
/**
* @param typeClass The class for which we look up types.
*
* @return All types that support the given class.
*/
public static ImmutableList<MPSiteType> forClass(final MPSiteTypeClass typeClass) {
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
for (final MPSiteType type : values())
if (type.getTypeClass() == typeClass)
types.add( type );
return types.build();
}
/**
* @param type The type for which we look up types.
*
* @return The type registered with the given type.
*/
public static MPSiteType forType(final int type) {
for (MPSiteType siteType : values())
if (siteType.getType() == type)
return siteType;
throw logger.bug( "No type: %s", type );
}
/**
* @param mask The mask for which we look up types.
*
* @return All types that support the given mask.
*/
public static ImmutableList<MPSiteType> forMask(final int mask) {
int typeMask = mask & ~0xF;
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
for (MPSiteType siteType : values())
if (((siteType.getType() & ~0xF) & typeMask) != 0)
types.add( siteType );
return types.build();
}
public MPTemplate getTemplateAtRollingIndex(final int templateIndex) {
return templates.get( templateIndex % templates.size() );
}
}

View File

@ -0,0 +1,21 @@
package com.lyndir.masterpassword;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public enum MPSiteTypeClass {
Generated( 1 << 4 ),
Stored( 1 << 5 );
private final int mask;
MPSiteTypeClass(final int mask) {
this.mask = mask;
}
public int getMask() {
return mask;
}
}

View File

@ -0,0 +1,83 @@
package com.lyndir.masterpassword;
import com.google.common.collect.ImmutableList;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.List;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Contract;
/**
* @author lhunath, 14-12-02
*/
public enum MPSiteVariant {
Password( "The password to log in with.", "Doesn't currently use a context.", //
ImmutableList.of( "p", "password" ), "com.lyndir.masterpassword" ),
Login( "The username to log in as.", "Doesn't currently use a context.", //
ImmutableList.of( "l", "login" ), "com.lyndir.masterpassword.login" ),
Answer( "The answer to a security question.", "Empty for a universal site answer or\nthe most significant word(s) of the question.", //
ImmutableList.of( "a", "answer" ), "com.lyndir.masterpassword.answer" );
static final Logger logger = Logger.get( MPSiteType.class );
private final String description;
private final String contextDescription;
private final List<String> options;
private final String scope;
MPSiteVariant(final String description, final String contextDescription, final List<String> options, final String scope) {
this.contextDescription = contextDescription;
this.options = options;
this.description = description;
this.scope = scope;
}
public String getDescription() {
return description;
}
public String getContextDescription() {
return contextDescription;
}
public List<String> getOptions() {
return options;
}
public String getScope() {
return scope;
}
/**
* @param option The option to select a variant with. It is matched case insensitively.
*
* @return The variant registered for the given option.
*/
public static MPSiteVariant forOption(final String option) {
for (final MPSiteVariant variant : values())
if (variant.getOptions().contains( option.toLowerCase() ))
return variant;
throw logger.bug( "No variant for option: %s", option );
}
/**
* @param name The name fromInt the variant to look up. It is matched case insensitively.
*
* @return The variant registered with the given name.
*/
@Contract("!null -> !null, null -> null")
public static MPSiteVariant forName(@Nullable final String name) {
if (name == null)
return null;
for (final MPSiteVariant type : values())
if (type.name().equalsIgnoreCase( name ))
return type;
throw logger.bug( "No variant for name: %s", name );
}
}

View File

@ -0,0 +1,48 @@
package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.google.common.collect.ImmutableList;
import com.lyndir.lhunath.opal.system.util.MetaObject;
import java.util.List;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public class MPTemplate extends MetaObject {
private final String templateString;
private final List<MPTemplateCharacterClass> template;
MPTemplate(final String templateString) {
ImmutableList.Builder<MPTemplateCharacterClass> builder = ImmutableList.builder();
for (int i = 0; i < templateString.length(); ++i)
builder.add( MPTemplateCharacterClass.forIdentifier( templateString.charAt( i ) ) );
this.templateString = templateString;
template = builder.build();
}
public String getTemplateString() {
return templateString;
}
public MPTemplateCharacterClass getCharacterClassAtIndex(final int index) {
return template.get( index );
}
public int length() {
return template.size();
}
@Override
public String toString() {
return strf( "{MPTemplate: %s}", templateString );
}
}

View File

@ -1,25 +1,6 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import com.lyndir.lhunath.opal.system.logging.Logger;
import org.jetbrains.annotations.NonNls;
/**
@ -27,7 +8,6 @@ import org.jetbrains.annotations.NonNls;
*
* @author lhunath
*/
@SuppressWarnings({ "HardcodedFileSeparator", "SpellCheckingInspection" })
public enum MPTemplateCharacterClass {
UpperVowel( 'V', "AEIOU" ),
@ -47,7 +27,7 @@ public enum MPTemplateCharacterClass {
private final char identifier;
private final char[] characters;
MPTemplateCharacterClass(final char identifier, @NonNls final String characters) {
MPTemplateCharacterClass(final char identifier, final String characters) {
this.identifier = identifier;
this.characters = characters.toCharArray();
@ -64,7 +44,7 @@ public enum MPTemplateCharacterClass {
}
public static MPTemplateCharacterClass forIdentifier(final char identifier) {
for (final MPTemplateCharacterClass characterClass : values())
for (MPTemplateCharacterClass characterClass : values())
if (characterClass.getIdentifier() == identifier)
return characterClass;

View File

@ -0,0 +1,165 @@
package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.Arrays;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
/**
* @author lhunath, 2014-08-30
*/
public abstract class MasterKey {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKey.class );
@Nonnull
private final String fullName;
@Nullable
private byte[] masterKey;
public static MasterKey create(final String fullName, final char[] masterPassword) {
return create( Version.CURRENT, fullName, masterPassword );
}
@Nonnull
public static MasterKey create(Version version, final String fullName, final char[] masterPassword) {
switch (version) {
case V0:
return new MasterKeyV0( fullName ).revalidate( masterPassword );
case V1:
return new MasterKeyV1( fullName ).revalidate( masterPassword );
case V2:
return new MasterKeyV2( fullName ).revalidate( masterPassword );
case V3:
return new MasterKeyV3( fullName ).revalidate( masterPassword );
}
throw new UnsupportedOperationException( "Unsupported version: " + version );
}
protected MasterKey(@NotNull final String fullName) {
this.fullName = fullName;
logger.trc( "fullName: %s", fullName );
}
@Nullable
protected abstract byte[] deriveKey(final char[] masterPassword);
public abstract Version getAlgorithmVersion();
@NotNull
public String getFullName() {
return fullName;
}
@Nonnull
protected byte[] getKey() {
Preconditions.checkState( isValid() );
return Preconditions.checkNotNull( masterKey );
}
public byte[] getKeyID() {
return idForBytes( getKey() );
}
public abstract String encode(@Nonnull final String siteName, final MPSiteType siteType, int siteCounter,
final MPSiteVariant siteVariant, @Nullable final String siteContext);
public boolean isValid() {
return masterKey != null;
}
public void invalidate() {
if (masterKey != null) {
Arrays.fill( masterKey, (byte) 0 );
masterKey = null;
}
}
public MasterKey revalidate(final char[] masterPassword) {
invalidate();
logger.trc( "masterPassword: %s", new String( masterPassword ) );
long start = System.currentTimeMillis();
masterKey = deriveKey( masterPassword );
if (masterKey == null)
logger.dbg( "masterKey calculation failed after %.2fs.", (System.currentTimeMillis() - start) / 1000D );
else
logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
(System.currentTimeMillis() - start) / 1000D );
return this;
}
protected abstract byte[] bytesForInt(final int integer);
protected abstract byte[] idForBytes(final byte[] bytes);
public enum Version {
/**
* bugs:
* - does math with chars whose signedness was platform-dependent.
* - miscounted the byte-length fromInt multi-byte site names.
* - miscounted the byte-length fromInt multi-byte full names.
*/
V0,
/**
* bugs:
* - miscounted the byte-length fromInt multi-byte site names.
* - miscounted the byte-length fromInt multi-byte full names.
*/
V1,
/**
* bugs:
* - miscounted the byte-length fromInt multi-byte full names.
*/
V2,
/**
* bugs:
* - no known issues.
*/
V3;
public static final Version CURRENT = V3;
public static Version fromInt(final int algorithmVersion) {
return values()[algorithmVersion];
}
public int toInt() {
return ordinal();
}
public String toBundleVersion() {
switch (this) {
case V0:
return "1.0";
case V1:
return "2.0";
case V2:
return "2.1";
case V3:
return "2.2";
}
throw new UnsupportedOperationException( "Unsupported version: " + this );
}
}
}

View File

@ -0,0 +1,146 @@
package com.lyndir.masterpassword;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.nio.*;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.annotation.Nullable;
/**
* bugs:
* - does math with chars whose signedness was platform-dependent.
* - miscounted the byte-length fromInt multi-byte site names.
* - miscounted the byte-length fromInt multi-byte full names.
*
* @author lhunath, 2014-08-30
*/
public class MasterKeyV0 extends MasterKey {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV0.class );
protected final int MP_N = 32768;
protected final int MP_r = 8;
protected final int MP_p = 2;
protected final int MP_dkLen = 64;
protected final int MP_intLen = 32;
protected final Charset MP_charset = Charsets.UTF_8;
protected final ByteOrder MP_byteOrder = ByteOrder.BIG_ENDIAN;
protected final MessageDigests MP_hash = MessageDigests.SHA256;
protected final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
public MasterKeyV0(final String fullName) {
super( fullName );
}
@Override
public Version getAlgorithmVersion() {
return Version.V0;
}
@Nullable
@Override
protected byte[] deriveKey(final char[] masterPassword) {
String fullName = getFullName();
byte[] fullNameBytes = fullName.getBytes( MP_charset );
byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
String mpKeyScope = MPSiteVariant.Password.getScope();
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( "key scope: %s", mpKeyScope );
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
ByteBuffer mpBytesBuf = MP_charset.encode( CharBuffer.wrap( masterPassword ) );
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
Arrays.fill( mpBytesBuf.array(), (byte) 0 );
try {
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
}
catch (GeneralSecurityException e) {
logger.bug( e );
return null;
}
finally {
Arrays.fill( mpBytes, (byte) 0 );
}
}
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
@Nullable final String siteContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %d", siteCounter );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
if (siteCounter == 0)
siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MP_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
siteContextBytes == null? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeedBytes = MP_mac.of( getKey(), sitePasswordInfo );
int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length];
for (int i = 0; i < sitePasswordSeedBytes.length; ++i) {
ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN );
Arrays.fill( buf.array(), sitePasswordSeedBytes[i] > 0? (byte)0x00: (byte) 0xFF );
buf.position( 2 );
buf.put( sitePasswordSeedBytes[i] ).rewind();
sitePasswordSeed[i] = buf.getInt() & 0xFFFF;
}
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0];
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1];
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
return password.toString();
}
@Override
protected byte[] bytesForInt(final int integer) {
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( integer ).array();
}
@Override
protected byte[] idForBytes(final byte[] bytes) {
return MP_hash.of( bytes );
}
}

View File

@ -0,0 +1,82 @@
package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import javax.annotation.Nullable;
/**
* bugs:
* - miscounted the byte-length fromInt multi-byte site names.
* - miscounted the byte-length fromInt multi-byte full names.
*
* @author lhunath, 2014-08-30
*/
public class MasterKeyV1 extends MasterKeyV0 {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV1.class );
public MasterKeyV1(final String fullName) {
super( fullName );
}
@Override
public Version getAlgorithmVersion() {
return Version.V1;
}
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
@Nullable final String siteContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %d", siteCounter );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
if (siteCounter == 0)
siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MP_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
siteContextBytes == null? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeed = MP_mac.of( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
return password.toString();
}
}

View File

@ -0,0 +1,81 @@
package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import javax.annotation.Nullable;
/**
* bugs:
* - miscounted the byte-length fromInt multi-byte full names.
*
* @author lhunath, 2014-08-30
*/
public class MasterKeyV2 extends MasterKeyV1 {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV2.class );
public MasterKeyV2(final String fullName) {
super( fullName );
}
@Override
public Version getAlgorithmVersion() {
return Version.V2;
}
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
@Nullable final String siteContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %d", siteCounter );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
if (siteCounter == 0)
siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MP_charset );
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
siteContextBytes == null? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeed = MP_mac.of( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
return password.toString();
}
}

View File

@ -0,0 +1,62 @@
package com.lyndir.masterpassword;
import com.google.common.primitives.Bytes;
import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.annotation.Nullable;
/**
* bugs:
* - no known issues.
*
* @author lhunath, 2014-08-30
*/
public class MasterKeyV3 extends MasterKeyV2 {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV3.class );
public MasterKeyV3(final String fullName) {
super( fullName );
}
@Override
public Version getAlgorithmVersion() {
return Version.V3;
}
@Nullable
@Override
protected byte[] deriveKey(final char[] masterPassword) {
byte[] fullNameBytes = getFullName().getBytes( MP_charset );
byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length );
String mpKeyScope = MPSiteVariant.Password.getScope();
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( "key scope: %s", mpKeyScope );
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
ByteBuffer mpBytesBuf = MP_charset.encode( CharBuffer.wrap( masterPassword ) );
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
Arrays.fill( mpBytesBuf.array(), (byte)0 );
try {
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
}
catch (GeneralSecurityException e) {
logger.bug( e );
return null;
}
finally {
Arrays.fill( mpBytes, (byte) 0 );
}
}
}

View File

@ -0,0 +1,9 @@
/**
*
* @author lhunath, 15-02-04
*/
@ParametersAreNonnullByDefault package com.lyndir.masterpassword;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@ -0,0 +1,214 @@
package com.lyndir.masterpassword;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.*;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.xml.bind.annotation.*;
/**
* @author lhunath, 14-12-05
*/
@XmlRootElement(name = "tests")
public class MPWTests {
public static final String ID_DEFAULT = "default";
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPWTests.class );
@XmlElement(name = "case")
private List<Case> cases;
@Nonnull
public List<Case> getCases() {
return checkNotNull( cases );
}
public Case getCase(String identifier) {
for (Case testCase : getCases())
if (identifier.equals( testCase.getIdentifier() ))
return testCase;
throw new IllegalArgumentException( "No case for identifier: " + identifier );
}
@XmlRootElement(name = "case")
public static class Case {
@XmlAttribute(name = "id")
private String identifier;
@XmlAttribute
private String parent;
@XmlElement
private String algorithm;
@XmlElement
private String fullName;
@XmlElement
private String masterPassword;
@XmlElement
private String keyID;
@XmlElement
private String siteName;
@XmlElement
private Integer siteCounter;
@XmlElement
private String siteType;
@XmlElement
private String siteVariant;
@XmlElement
private String siteContext;
@XmlElement
private String result;
private transient Case parentCase;
public void initializeParentHierarchy(MPWTests tests) {
if (parent != null) {
parentCase = tests.getCase( parent );
parentCase.initializeParentHierarchy( tests );
}
algorithm = ifNotNullElse( algorithm, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.algorithm );
}
} );
fullName = ifNotNullElse( fullName, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.fullName );
}
} );
masterPassword = ifNotNullElse( masterPassword, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.masterPassword );
}
} );
keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.keyID );
}
} );
siteName = ifNotNullElse( siteName, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.siteName );
}
} );
siteCounter = ifNotNullElse( siteCounter, new NNSupplier<Integer>() {
@Nonnull
@Override
public Integer get() {
return checkNotNull( parentCase.siteCounter );
}
} );
siteType = ifNotNullElse( siteType, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.siteType );
}
} );
siteVariant = ifNotNullElse( siteVariant, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.siteVariant );
}
} );
siteContext = ifNotNullElse( siteContext, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return parentCase == null? "": checkNotNull( parentCase.siteContext );
}
} );
result = ifNotNullElse( result, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return parentCase == null? "": checkNotNull( parentCase.result );
}
} );
}
@Nonnull
public String getIdentifier() {
return identifier;
}
@Nullable
public Case getParentCase() {
return parentCase;
}
@Nonnull
public MasterKey.Version getAlgorithm() {
return MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( algorithm ) );
}
@Nonnull
public String getFullName() {
return checkNotNull( fullName );
}
@Nonnull
public char[] getMasterPassword() {
return checkNotNull( masterPassword ).toCharArray();
}
@Nonnull
public String getKeyID() {
return checkNotNull( keyID );
}
@Nonnull
public String getSiteName() {
return checkNotNull( siteName );
}
public int getSiteCounter() {
return ifNotNullElse( siteCounter, 1 );
}
@Nonnull
public MPSiteType getSiteType() {
return MPSiteType.forName( checkNotNull( siteType ) );
}
@Nonnull
public MPSiteVariant getSiteVariant() {
return MPSiteVariant.forName( checkNotNull( siteVariant ) );
}
@Nonnull
public String getSiteContext() {
return checkNotNull( siteContext );
}
@Nonnull
public String getResult() {
return checkNotNull( result );
}
@Override
public String toString() {
return identifier;
}
}
}

View File

@ -0,0 +1,88 @@
package com.lyndir.masterpassword;
import static org.testng.Assert.*;
import com.google.common.io.Resources;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.StringUtils;
import java.net.URL;
import javax.xml.bind.JAXBContext;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class MasterKeyTest {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyTest.class );
private MPWTests tests;
private MPWTests.Case defaultCase;
@BeforeMethod
public void setUp()
throws Exception {
URL testCasesResource = Resources.getResource( "mpw_tests.xml" );
tests = (MPWTests) JAXBContext.newInstance( MPWTests.class ).createUnmarshaller().unmarshal( testCasesResource );
for (MPWTests.Case testCase : tests.getCases())
testCase.initializeParentHierarchy( tests );
defaultCase = tests.getCase( MPWTests.ID_DEFAULT );
}
@Test
public void testEncode()
throws Exception {
for (MPWTests.Case testCase : tests.getCases()) {
if (testCase.getResult().isEmpty())
continue;
logger.inf( "Running test case: %s [testEncode]", testCase.getIdentifier() );
MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
assertEquals(
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
logger.inf( "passed!" );
}
}
@Test
public void testGetUserName()
throws Exception {
assertEquals( MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
defaultCase.getFullName() );
}
@Test
public void testGetKeyID()
throws Exception {
for (MPWTests.Case testCase : tests.getCases()) {
if (testCase.getResult().isEmpty())
continue;
logger.inf( "Running test case: %s [testGetKeyID]", testCase.getIdentifier() );
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase );
logger.inf( "passed!" );
}
}
@Test
public void testInvalidate()
throws Exception {
try {
MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() );
masterKey.invalidate();
masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(),
defaultCase.getSiteVariant(), defaultCase.getSiteContext() );
assertTrue( false, "Master key should have been invalidated, but was still usable." );
}
catch (IllegalStateException ignored) {
}
}
}

View File

@ -0,0 +1,15 @@
<configuration scan="false">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%-8relative %22c{0} [%-5level] %msg%n</Pattern>
</layout>
</appender>
<logger name="com.lyndir.masterpassword" level="${mp.log.level:-TRACE}" />
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -1,14 +1,14 @@
<tests>
<!-- Default values for all parameters. -->
<case id="default">
<algorithm>-1</algorithm>
<algorithm><!-- current --></algorithm>
<fullName>Robert Lee Mitchell</fullName>
<masterPassword>banana colored duckling</masterPassword>
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
<siteName>masterpasswordapp.com</siteName>
<siteCounter>1</siteCounter>
<resultType>Long</resultType>
<keyPurpose>Authentication</keyPurpose>
<siteType>GeneratedLong</siteType>
<siteVariant>Password</siteVariant>
<result><!-- abstract --></result>
</case>
@ -32,45 +32,45 @@
<result>LiheCuwhSerz6)</result>
</case>
<case id="v3_loginName" parent="v3">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<siteVariant>Login</siteVariant>
<siteType>GeneratedName</siteType>
<result>wohzaqage</result>
</case>
<case id="v3_securityAnswer" parent="v3">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<siteVariant>Answer</siteVariant>
<siteType>GeneratedPhrase</siteType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v3_securityAnswer_context" parent="v3_securityAnswer">
<keyContext>question</keyContext>
<siteContext>question</siteContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v3_type_maximum" parent="v3">
<resultType>Maximum</resultType>
<siteType>GeneratedMaximum</siteType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v3_type_medium" parent="v3">
<resultType>Medium</resultType>
<siteType>GeneratedMedium</siteType>
<result>Jej2$Quv</result>
</case>
<case id="v3_type_basic" parent="v3">
<resultType>Basic</resultType>
<siteType>GeneratedBasic</siteType>
<result>WAo2xIg6</result>
</case>
<case id="v3_type_short" parent="v3">
<resultType>Short</resultType>
<siteType>GeneratedShort</siteType>
<result>Jej2</result>
</case>
<case id="v3_type_pin" parent="v3">
<resultType>PIN</resultType>
<siteType>GeneratedPIN</siteType>
<result>7662</result>
</case>
<case id="v3_type_name" parent="v3">
<resultType>Name</resultType>
<siteType>GeneratedName</siteType>
<result>jejraquvo</result>
</case>
<case id="v3_type_phrase" parent="v3">
<resultType>Phrase</resultType>
<siteType>GeneratedPhrase</siteType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v3_counter_ceiling" parent="v3">
@ -85,7 +85,7 @@
</case>
<case id="v2_mb_fullName" parent="v2">
<fullName></fullName>
<keyID>4D5851D0B093D65DE0CF13D94877270468C0B65A6E42CA50D393AC9B99C457B5</keyID>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>WaqoGuho2[Xaxw</result>
</case>
<case id="v2_mb_masterPassword" parent="v2">
@ -98,45 +98,45 @@
<result>LiheCuwhSerz6)</result>
</case>
<case id="v2_loginName" parent="v2">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<siteVariant>Login</siteVariant>
<siteType>GeneratedName</siteType>
<result>wohzaqage</result>
</case>
<case id="v2_securityAnswer" parent="v2">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<siteVariant>Answer</siteVariant>
<siteType>GeneratedPhrase</siteType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v2_securityAnswer_context" parent="v2_securityAnswer">
<keyContext>question</keyContext>
<siteContext>question</siteContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v2_type_maximum" parent="v2">
<resultType>Maximum</resultType>
<siteType>GeneratedMaximum</siteType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v2_type_medium" parent="v2">
<resultType>Medium</resultType>
<siteType>GeneratedMedium</siteType>
<result>Jej2$Quv</result>
</case>
<case id="v2_type_basic" parent="v2">
<resultType>Basic</resultType>
<siteType>GeneratedBasic</siteType>
<result>WAo2xIg6</result>
</case>
<case id="v2_type_short" parent="v2">
<resultType>Short</resultType>
<siteType>GeneratedShort</siteType>
<result>Jej2</result>
</case>
<case id="v2_type_pin" parent="v2">
<resultType>PIN</resultType>
<siteType>GeneratedPIN</siteType>
<result>7662</result>
</case>
<case id="v2_type_name" parent="v2">
<resultType>Name</resultType>
<siteType>GeneratedName</siteType>
<result>jejraquvo</result>
</case>
<case id="v2_type_phrase" parent="v2">
<resultType>Phrase</resultType>
<siteType>GeneratedPhrase</siteType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v2_counter_ceiling" parent="v2">
@ -151,7 +151,7 @@
</case>
<case id="v1_mb_fullName" parent="v1">
<fullName></fullName>
<keyID>4D5851D0B093D65DE0CF13D94877270468C0B65A6E42CA50D393AC9B99C457B5</keyID>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>WaqoGuho2[Xaxw</result>
</case>
<case id="v1_mb_masterPassword" parent="v1">
@ -164,45 +164,45 @@
<result>WawiYarp2@Kodh</result>
</case>
<case id="v1_loginName" parent="v1">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<siteVariant>Login</siteVariant>
<siteType>GeneratedName</siteType>
<result>wohzaqage</result>
</case>
<case id="v1_securityAnswer" parent="v1">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<siteVariant>Answer</siteVariant>
<siteType>GeneratedPhrase</siteType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v1_securityAnswer_context" parent="v1_securityAnswer">
<keyContext>question</keyContext>
<siteContext>question</siteContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v1_type_maximum" parent="v1">
<resultType>Maximum</resultType>
<siteType>GeneratedMaximum</siteType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v1_type_medium" parent="v1">
<resultType>Medium</resultType>
<siteType>GeneratedMedium</siteType>
<result>Jej2$Quv</result>
</case>
<case id="v1_type_basic" parent="v1">
<resultType>Basic</resultType>
<siteType>GeneratedBasic</siteType>
<result>WAo2xIg6</result>
</case>
<case id="v1_type_short" parent="v1">
<resultType>Short</resultType>
<siteType>GeneratedShort</siteType>
<result>Jej2</result>
</case>
<case id="v1_type_pin" parent="v1">
<resultType>PIN</resultType>
<siteType>GeneratedPIN</siteType>
<result>7662</result>
</case>
<case id="v1_type_name" parent="v1">
<resultType>Name</resultType>
<siteType>GeneratedName</siteType>
<result>jejraquvo</result>
</case>
<case id="v1_type_phrase" parent="v1">
<resultType>Phrase</resultType>
<siteType>GeneratedPhrase</siteType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v1_counter_ceiling" parent="v1">
@ -217,7 +217,7 @@
</case>
<case id="v0_mb_fullName" parent="v0">
<fullName></fullName>
<keyID>4D5851D0B093D65DE0CF13D94877270468C0B65A6E42CA50D393AC9B99C457B5</keyID>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>HajrYudo7@Mamh</result>
</case>
<case id="v0_mb_masterPassword" parent="v0">
@ -230,45 +230,45 @@
<result>HahiVana2@Nole</result>
</case>
<case id="v0_loginName" parent="v0">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<siteVariant>Login</siteVariant>
<siteType>GeneratedName</siteType>
<result>lozwajave</result>
</case>
<case id="v0_securityAnswer" parent="v0">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<siteVariant>Answer</siteVariant>
<siteType>GeneratedPhrase</siteType>
<result>miy lirfijoja dubu</result>
</case>
<case id="v0_securityAnswer_context" parent="v0_securityAnswer">
<keyContext>question</keyContext>
<siteContext>question</siteContext>
<result>movm bex gevrica jaf</result>
</case>
<case id="v0_type_maximum" parent="v0">
<resultType>Maximum</resultType>
<siteType>GeneratedMaximum</siteType>
<result>w1!3bA3icmRAc)SS@lwl</result>
</case>
<case id="v0_type_medium" parent="v0">
<resultType>Medium</resultType>
<siteType>GeneratedMedium</siteType>
<result>Fej7]Jug</result>
</case>
<case id="v0_type_basic" parent="v0">
<resultType>Basic</resultType>
<siteType>GeneratedBasic</siteType>
<result>wvH7irC1</result>
</case>
<case id="v0_type_short" parent="v0">
<resultType>Short</resultType>
<siteType>GeneratedShort</siteType>
<result>Fej7</result>
</case>
<case id="v0_type_pin" parent="v0">
<resultType>PIN</resultType>
<siteType>GeneratedPIN</siteType>
<result>2117</result>
</case>
<case id="v0_type_name" parent="v0">
<resultType>Name</resultType>
<siteType>GeneratedName</siteType>
<result>fejrajugo</result>
</case>
<case id="v0_type_phrase" parent="v0">
<resultType>Phrase</resultType>
<siteType>GeneratedPhrase</siteType>
<result>fejr jug gabsibu bax</result>
</case>
<case id="v0_counter_ceiling" parent="v0">

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lyndir.masterpassword"
android:versionCode="2003"
android:versionName="2.3">
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="21" />
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:allowBackup="true">
<activity android:name=".EmergencyActivity" android:theme="@style/MPTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".UsersActivity" />
</application>
</manifest>

View File

@ -0,0 +1,13 @@
To build this module, please ensure you've done the following setup:
1. Installed the Android SDK and fully downloaded the Android SDK platform 21 in it.
2. Set the environment variable ANDROID_HOME in your shell or in ~/.mavenrc to point to the root of your Android SDK install.
3. Installed the Android SDK into your Maven's local repository.
3a. Clone the maven-android-sdk-deployer available from here: https://github.com/mosabua/maven-android-sdk-deployer.git
3b. In the root of this project, run: mvn install -P 5.0
To build this module:
1. Build the parent, by going into 'MasterPassword/Java' and running: mvn clean install
2. Build this module, by going into 'MasterPassword/Java/masterpassword-android' and running: mvn clean install
3. You can then find the APK in: 'MasterPassword/Java/masterpassword-android/target'

Some files were not shown because too many files have changed in this diff Show More