Compare commits
224 Commits
2.4.1-andr
...
2.6-cli-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30fdb54e94 | ||
|
|
4f552be5a9 | ||
|
|
1439df9f9a | ||
|
|
e676a0e258 | ||
|
|
895df6377d | ||
|
|
3d46f60ff4 | ||
|
|
44d8ab6e53 | ||
|
|
cd70009c2c | ||
|
|
4261160902 | ||
|
|
ced7aef5d7 | ||
|
|
63100913c5 | ||
|
|
6904d4c427 | ||
|
|
4271d77225 | ||
|
|
6811773e54 | ||
|
|
060ce61030 | ||
|
|
9a5e9ced31 | ||
|
|
568401a612 | ||
|
|
92a3a0ccbd | ||
|
|
ba24c2be34 | ||
|
|
019cefd3fb | ||
|
|
eef82f7ed4 | ||
|
|
2dfe0f78b0 | ||
|
|
627144b583 | ||
|
|
fad0f5e5dd | ||
|
|
8562338b62 | ||
|
|
17de69834e | ||
|
|
aeeab7dbf6 | ||
|
|
ce60ba6c9f | ||
|
|
d22f93e564 | ||
|
|
6f4f6b8d1e | ||
|
|
6fa8ee53cd | ||
|
|
23af56c150 | ||
|
|
91828cbad7 | ||
|
|
40d2788ae0 | ||
|
|
21a3a28980 | ||
|
|
f5c7bee58f | ||
|
|
e364f5159b | ||
|
|
74f9f1ca00 | ||
|
|
328d38ac19 | ||
|
|
7735d82c7b | ||
|
|
1e7c200865 | ||
|
|
724b357dd8 | ||
|
|
a85efc5736 | ||
|
|
9eb58119ea | ||
|
|
77b4ed2cfd | ||
|
|
011416690a | ||
|
|
53eb5c8a73 | ||
|
|
2f99855cd4 | ||
|
|
18eaeec1de | ||
|
|
5ee700c9b9 | ||
|
|
a8949ca07e | ||
|
|
0a42579d9e | ||
|
|
f2f8747126 | ||
|
|
f83cdacab8 | ||
|
|
98aeb02d32 | ||
|
|
2bbaeccd05 | ||
|
|
91e0a04e66 | ||
|
|
661fc523ad | ||
|
|
b9cbaf7343 | ||
|
|
e451308fdc | ||
|
|
1b51c5efa4 | ||
|
|
a8776eec58 | ||
|
|
d9cdb7ef83 | ||
|
|
28c7a64bd2 | ||
|
|
d7193f7753 | ||
|
|
f5c7d11f0e | ||
|
|
c0ba96daa2 | ||
|
|
b374d9e04a | ||
|
|
2033ebdc72 | ||
|
|
c3bb896f40 | ||
|
|
4f7c28563d | ||
|
|
b1985a2bf2 | ||
|
|
ee50a4d025 | ||
|
|
b26f5a82d7 | ||
|
|
c044ae79cd | ||
|
|
a261538602 | ||
|
|
18daef7808 | ||
|
|
68d1ab58b7 | ||
|
|
2b660adf00 | ||
|
|
e15d01882f | ||
|
|
23491faccc | ||
|
|
5f2e1611f1 | ||
|
|
9abacaf905 | ||
|
|
322e056661 | ||
|
|
228f8e4ed1 | ||
|
|
d6415277d0 | ||
|
|
db41a6635f | ||
|
|
096919637f | ||
|
|
434d70ebff | ||
|
|
bb8829b66f | ||
|
|
10f2c107c6 | ||
|
|
03080b9ccd | ||
|
|
b00ad53e42 | ||
|
|
99e286456e | ||
|
|
46cdf56944 | ||
|
|
9d5105a9e5 | ||
|
|
3c5cb1673a | ||
|
|
13107063df | ||
|
|
8a73baa6bc | ||
|
|
b65fedf40d | ||
|
|
04ab276d93 | ||
|
|
6d88d6bde0 | ||
|
|
4103c6e659 | ||
|
|
16004f2ffe | ||
|
|
37c0d323d9 | ||
|
|
560cb1a266 | ||
|
|
738ad197b2 | ||
|
|
cfcc5287db | ||
|
|
0b5502b673 | ||
|
|
d3e3c9d720 | ||
|
|
3c3f88d820 | ||
|
|
2e2c654ec9 | ||
|
|
d361ae2381 | ||
|
|
fcbb93762a | ||
|
|
f86210f5da | ||
|
|
e96f678236 | ||
|
|
8b9067ab4b | ||
|
|
5af383235a | ||
|
|
25b13dfb22 | ||
|
|
635692ef09 | ||
|
|
e6bab4e504 | ||
|
|
cd6b7e6051 | ||
|
|
b180202e07 | ||
|
|
f83f2af529 | ||
|
|
cf2c30cfe6 | ||
|
|
834e94ebd5 | ||
|
|
6d9be3fdfe | ||
|
|
07e55140ac | ||
|
|
fbbd08790d | ||
|
|
fcaa5d1d8c | ||
|
|
ea5be8efcb | ||
|
|
c8b4933c3d | ||
|
|
981ee171ae | ||
|
|
3ed6b93736 | ||
|
|
56a515c5ea | ||
|
|
15ac7a2dbf | ||
|
|
c5c7999753 | ||
|
|
bb58ed0169 | ||
|
|
4545a5c745 | ||
|
|
da8c7064fe | ||
|
|
d9bd604436 | ||
|
|
c99252809d | ||
|
|
d704f451a3 | ||
|
|
2c9ab5d153 | ||
|
|
d5d33da12f | ||
|
|
cbef1a611b | ||
|
|
0a1f215a1a | ||
|
|
907d2a8ca6 | ||
|
|
89f6e77f67 | ||
|
|
f2fb16a0b9 | ||
|
|
e3edd42b88 | ||
|
|
cc5d246d7d | ||
|
|
ca320de6d9 | ||
|
|
ae979d7240 | ||
|
|
eb1c443940 | ||
|
|
dadcefc9bf | ||
|
|
cdbaec9751 | ||
|
|
f48d480c77 | ||
|
|
23aae490df | ||
|
|
3fcf1131ac | ||
|
|
2bdec415e9 | ||
|
|
4c12f4af56 | ||
|
|
d91140439a | ||
|
|
a7912dd1b7 | ||
|
|
05391d893e | ||
|
|
2047422b08 | ||
|
|
e3fffc1923 | ||
|
|
9bf50569cc | ||
|
|
9ef265d9de | ||
|
|
385f347b33 | ||
|
|
4058d33202 | ||
|
|
98c5ee3425 | ||
|
|
b01e370fc0 | ||
|
|
21d5ef823f | ||
|
|
da71ed6f7f | ||
|
|
5a6acb2e44 | ||
|
|
29ecbaeb19 | ||
|
|
3ebdfe1180 | ||
|
|
60ac096f99 | ||
|
|
b13bdb171e | ||
|
|
2cfa1439e6 | ||
|
|
a363a8a715 | ||
|
|
e39062a32d | ||
|
|
4c5c7b8aaf | ||
|
|
ee4e8f4229 | ||
|
|
e4d3c0b47c | ||
|
|
6e685f0036 | ||
|
|
b248f2d440 | ||
|
|
ec0712350f | ||
|
|
d440f2bcac | ||
|
|
bef55cca03 | ||
|
|
ebe5206f92 | ||
|
|
b478691980 | ||
|
|
86f956571d | ||
|
|
559934607b | ||
|
|
de4300c3d3 | ||
|
|
05f91d53db | ||
|
|
01eddf9f42 | ||
|
|
9b2c0b33ef | ||
|
|
77b095e25e | ||
|
|
93cdcc1743 | ||
|
|
2fe1ed84e6 | ||
|
|
4903df21f8 | ||
|
|
31fb9bc4e1 | ||
|
|
23dcb11a10 | ||
|
|
ee0ca569f5 | ||
|
|
8ca8ef7aab | ||
|
|
018ab83af3 | ||
|
|
58a6f17641 | ||
|
|
77bee803b8 | ||
|
|
c3f4d148a4 | ||
|
|
f94ae03bde | ||
|
|
88199db988 | ||
|
|
33ff573295 | ||
|
|
e0822b270e | ||
|
|
acc4c34b85 | ||
|
|
63b757f51a | ||
|
|
eb527d985b | ||
|
|
94159ed11a | ||
|
|
18fce4eaf8 | ||
|
|
8347c72882 | ||
|
|
f03abb1c8d | ||
|
|
10cfe95158 | ||
|
|
c6b285a9c0 |
43
.gitignore
vendored
43
.gitignore
vendored
@@ -3,40 +3,29 @@
|
|||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
/MasterPassword/Java/.idea
|
.idea
|
||||||
/.idea/*
|
|
||||||
!/.idea/encodings.xml
|
|
||||||
!/.idea/inspectionProfiles
|
|
||||||
!/.idea/projectCodeStyle.xml
|
|
||||||
!/.idea/validation.xml
|
|
||||||
*.iml
|
*.iml
|
||||||
/*.ipr
|
*.ipr
|
||||||
/*.iws
|
*.iws
|
||||||
|
|
||||||
# Xcode IDE
|
# Xcode IDE
|
||||||
xcuserdata/
|
xcuserdata/
|
||||||
/DerivedData/
|
DerivedData/
|
||||||
|
|
||||||
# Generated
|
# Generated
|
||||||
MasterPassword/Resources/Media/Images.xcassets/
|
/platform-darwin/Resources/Media/Images.xcassets/
|
||||||
|
|
||||||
# Media
|
# Media
|
||||||
Press/Background.png
|
public/Press/Background.png
|
||||||
Press/Front-Page.png
|
public/Press/Front-Page.png
|
||||||
Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
|
public/Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
|
||||||
|
|
||||||
# IPA
|
# Gradle
|
||||||
/sendipa/*
|
build
|
||||||
!/sendipa/sendipa.conf
|
!/build
|
||||||
|
.gradle
|
||||||
|
local.properties
|
||||||
|
|
||||||
# C
|
# Maven
|
||||||
MasterPassword/C/VERSION
|
target
|
||||||
MasterPassword/C/*.o
|
dependency-reduced-pom.xml
|
||||||
MasterPassword/C/mpw-*.tar.gz
|
|
||||||
MasterPassword/C/mpw
|
|
||||||
MasterPassword/C/mpw-bench
|
|
||||||
MasterPassword/C/mpw-tests
|
|
||||||
MasterPassword/C/lib/*/.unpacked
|
|
||||||
MasterPassword/C/lib/*/.patched
|
|
||||||
MasterPassword/C/lib/*/src
|
|
||||||
MasterPassword/C/lib/include
|
|
||||||
|
|||||||
20
.gitmodules
vendored
20
.gitmodules
vendored
@@ -1,21 +1,27 @@
|
|||||||
[submodule "External/Pearl"]
|
[submodule "External/Pearl"]
|
||||||
path = External/Pearl
|
path = platform-darwin/External/Pearl
|
||||||
url = git://github.com/Lyndir/Pearl.git
|
url = git://github.com/Lyndir/Pearl.git
|
||||||
[submodule "External/InAppSettingsKit"]
|
[submodule "External/InAppSettingsKit"]
|
||||||
path = External/InAppSettingsKit
|
path = platform-darwin/External/InAppSettingsKit
|
||||||
url = git://github.com/lhunath/InAppSettingsKit.git
|
url = git://github.com/lhunath/InAppSettingsKit.git
|
||||||
[submodule "External/KCOrderedAccessorFix"]
|
[submodule "External/KCOrderedAccessorFix"]
|
||||||
path = External/KCOrderedAccessorFix
|
path = platform-darwin/External/KCOrderedAccessorFix
|
||||||
url = https://github.com/lhunath/KCOrderedAccessorFix.git
|
url = https://github.com/lhunath/KCOrderedAccessorFix.git
|
||||||
[submodule "External/AttributedMarkdown"]
|
[submodule "External/AttributedMarkdown"]
|
||||||
path = External/AttributedMarkdown
|
path = platform-darwin/External/AttributedMarkdown
|
||||||
url = https://github.com/dreamwieber/AttributedMarkdown.git
|
url = https://github.com/dreamwieber/AttributedMarkdown.git
|
||||||
[submodule "External/uicolor-utilities"]
|
[submodule "External/uicolor-utilities"]
|
||||||
path = External/uicolor-utilities
|
path = platform-darwin/External/uicolor-utilities
|
||||||
url = git://github.com/lhunath/uicolor-utilities.git
|
url = git://github.com/lhunath/uicolor-utilities.git
|
||||||
[submodule "External/jrswizzle"]
|
[submodule "External/jrswizzle"]
|
||||||
path = External/jrswizzle
|
path = platform-darwin/External/jrswizzle
|
||||||
url = git://github.com/jonmarimba/jrswizzle.git
|
url = git://github.com/jonmarimba/jrswizzle.git
|
||||||
[submodule "MasterPassword/Web/js/mpw-js"]
|
[submodule "MasterPassword/Web/js/mpw-js"]
|
||||||
path = MasterPassword/Web/js/mpw-js
|
path = platform-independent/web-js/js/mpw-js
|
||||||
url = https://github.com/tmthrgd/mpw-js.git
|
url = https://github.com/tmthrgd/mpw-js.git
|
||||||
|
[submodule "platform-darwin/External/libsodium"]
|
||||||
|
path = platform-darwin/External/libsodium
|
||||||
|
url = https://github.com/jedisct1/libsodium.git
|
||||||
|
[submodule "platform-darwin/External/libjson-c"]
|
||||||
|
path = platform-darwin/External/libjson-c
|
||||||
|
url = https://github.com/lhunath/json-c.git
|
||||||
|
|||||||
11
.idea/encodings.xml
generated
11
.idea/encodings.xml
generated
@@ -1,11 +0,0 @@
|
|||||||
<?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" />
|
|
||||||
<file url="PROJECT" charset="UTF-8" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
18
.idea/inspectionProfiles/Project_Default.xml
generated
18
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,18 +0,0 @@
|
|||||||
<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="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>
|
|
||||||
7
.idea/inspectionProfiles/profiles_settings.xml
generated
7
.idea/inspectionProfiles/profiles_settings.xml
generated
@@ -1,7 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="PROJECT_PROFILE" value="Project Default" />
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="true" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
language: objective-c
|
language: objective-c
|
||||||
osx_image: xcode8
|
osx_image: xcode8.3
|
||||||
env: TERM=dumb SHLVL=0
|
env: TERM=dumb SHLVL=0
|
||||||
git:
|
git:
|
||||||
submodules: true
|
submodules: true
|
||||||
script: xcodebuild -workspace MasterPassword.xcworkspace -scheme 'MasterPassword iOS (App Store)' -configuration 'AdHoc-iOS' -sdk iphonesimulator
|
script:
|
||||||
|
- "( brew install libsodium json-c )"
|
||||||
|
- "( cd ./platform-independent/cli-c && ./clean && targets='mpw mpw-bench mpw-tests' ./build && ./mpw-tests && ./mpw-cli-tests )"
|
||||||
|
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword iOS' -sdk iphonesimulator )"
|
||||||
|
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword macOS' )"
|
||||||
|
|||||||
1
External/InAppSettingsKit
vendored
1
External/InAppSettingsKit
vendored
Submodule External/InAppSettingsKit deleted from b58b72563a
1
External/Pearl
vendored
1
External/Pearl
vendored
Submodule External/Pearl deleted from 4ac4add457
1
External/iOS/Reveal.framework
vendored
1
External/iOS/Reveal.framework
vendored
@@ -1 +0,0 @@
|
|||||||
/Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/Reveal.framework
|
|
||||||
10
MasterPassword.xcworkspace/contents.xcworkspacedata
generated
10
MasterPassword.xcworkspace/contents.xcworkspacedata
generated
@@ -1,10 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,332 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# TROUBLESHOOTING
|
|
||||||
# - 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
|
|
||||||
|
|
||||||
# Distribution specific configuration.
|
|
||||||
# Homebrew
|
|
||||||
if hash brew 2>/dev/null; then
|
|
||||||
opensslPath=$(brew --prefix openssl)
|
|
||||||
export CFLAGS="$CFLAGS -I$opensslPath/include"
|
|
||||||
export LDFLAGS="$LDFLAGS -L$opensslPath/lib"
|
|
||||||
fi
|
|
||||||
|
|
||||||
### 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() {
|
|
||||||
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'
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
files=( * )
|
|
||||||
if [[ -d $files ]] && (( ${#files[@]} == 1 )); then
|
|
||||||
mv "$files"/* .
|
|
||||||
rmdir "$files"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
fetchSource() (
|
|
||||||
local name=${PWD##*/}
|
|
||||||
source .source
|
|
||||||
|
|
||||||
if [[ -e .unpacked ]]; then
|
|
||||||
true
|
|
||||||
|
|
||||||
elif [[ $pkg && -e "${pkg##*/}" ]]; then
|
|
||||||
[[ -e src ]] || {
|
|
||||||
echo
|
|
||||||
echo "Unpacking: $name, using package..."
|
|
||||||
( mkdir src && cd src && unpack "../${pkg##*/}" "$pkg_sha256" )
|
|
||||||
touch .unpacked
|
|
||||||
}
|
|
||||||
|
|
||||||
elif [[ $git ]] && hash git 2>/dev/null; then
|
|
||||||
[[ -e .git ]] || {
|
|
||||||
echo
|
|
||||||
echo "Fetching: $name, using git..."
|
|
||||||
git clone "$git" src
|
|
||||||
touch .unpacked
|
|
||||||
}
|
|
||||||
|
|
||||||
elif [[ $svn ]] && hash git 2>/dev/null && [[ -x "$(git --exec-path)/git-svn" ]]; then
|
|
||||||
[[ -e .git ]] || {
|
|
||||||
echo
|
|
||||||
echo "Fetching: $name, using git-svn..."
|
|
||||||
git svn clone --prefix=origin/ --stdlayout "$svn" src
|
|
||||||
touch .unpacked
|
|
||||||
}
|
|
||||||
|
|
||||||
elif [[ $svn ]] && hash svn 2>/dev/null; then
|
|
||||||
[[ -e .svn ]] || {
|
|
||||||
echo
|
|
||||||
echo "Fetching: $name, using svn..."
|
|
||||||
svn checkout "$svn/trunk" src
|
|
||||||
touch .unpacked
|
|
||||||
}
|
|
||||||
|
|
||||||
elif [[ $pkg ]]; then
|
|
||||||
[[ -e src ]] || {
|
|
||||||
echo
|
|
||||||
echo "Fetching: $name, using package..."
|
|
||||||
fetch "$pkg"
|
|
||||||
( mkdir src && cd src && unpack "../${pkg##*/}" "$pkg_sha256" )
|
|
||||||
touch .unpacked
|
|
||||||
}
|
|
||||||
|
|
||||||
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/src"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -e .patched ]] && (( ${#patches[@]} )); then
|
|
||||||
pushd src
|
|
||||||
for patch in "${patches[@]}"; do
|
|
||||||
echo
|
|
||||||
echo "Patching: $name, for $patch..."
|
|
||||||
patch -p0 < "../$patch.patch"
|
|
||||||
done
|
|
||||||
popd
|
|
||||||
touch .patched
|
|
||||||
fi
|
|
||||||
)
|
|
||||||
depend() {
|
|
||||||
local name=$1
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Checking dependency: $name..."
|
|
||||||
[[ -e "lib/include/$name" ]] && return
|
|
||||||
|
|
||||||
pushd "lib/$name"
|
|
||||||
fetchSource
|
|
||||||
pushd "src"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Configuring dependency: $name..."
|
|
||||||
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 $name. 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: $name..."
|
|
||||||
if [[ -e Makefile ]]; then
|
|
||||||
if ! hash make; then
|
|
||||||
echo >&2 "Need make to build $name. Please install GNU make."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
make
|
|
||||||
install -d "../../include/$name/"
|
|
||||||
find . -name '*.h' -exec install -m 444 {} "../../include/$name/" \;
|
|
||||||
else
|
|
||||||
echo >&2 "error: Don't know how to build: $name"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
popd
|
|
||||||
popd
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### MPW
|
|
||||||
mpw() {
|
|
||||||
depend scrypt
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Building target: $target..."
|
|
||||||
local CFLAGS=(
|
|
||||||
# include paths
|
|
||||||
-I"lib/include"
|
|
||||||
)
|
|
||||||
local LDFLAGS=(
|
|
||||||
# scrypt
|
|
||||||
"lib/scrypt/src/libcperciva/"*/*.o
|
|
||||||
"lib/scrypt/src/lib/crypto/"*.o
|
|
||||||
# library paths
|
|
||||||
-L"." -L"lib/scrypt/src"
|
|
||||||
# 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/src/libcperciva/"*/*.o
|
|
||||||
"lib/scrypt/src/lib/crypto/"*.o
|
|
||||||
# bcrypt
|
|
||||||
"lib/bcrypt/src/crypt_blowfish.o"
|
|
||||||
"lib/bcrypt/src/crypt_gensalt.o"
|
|
||||||
"lib/bcrypt/src/wrapper.o"
|
|
||||||
"lib/bcrypt/src/x86.o"
|
|
||||||
# library paths
|
|
||||||
-L"." -L"lib/scrypt/src"
|
|
||||||
-L"lib/bcrypt/src"
|
|
||||||
# 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/src/libcperciva/"*/*.o
|
|
||||||
"lib/scrypt/src/lib/crypto/"*.o
|
|
||||||
# library paths
|
|
||||||
-L"." -L"lib/scrypt/src"
|
|
||||||
# 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
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#!/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"
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
home=http://www.openwall.com/crypt/
|
|
||||||
pkg=http://www.openwall.com/crypt/crypt_blowfish-1.3.tar.gz
|
|
||||||
pkg_sha256=83fa01fca6996fe8d882b7f8e9ba0305a5664936100b01481ea3c6a8ce8d72fd
|
|
||||||
patches=(arm)
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
--- 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
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
home=http://www.tarsnap.com/scrypt.html
|
|
||||||
git=https://github.com/Tarsnap/scrypt.git
|
|
||||||
pkg=http://www.tarsnap.com/scrypt/scrypt-1.2.0.tgz
|
|
||||||
pkg_sha256=1754bc89405277c8ac14220377a4c240ddc34b1ce70882aa92cd01bfdc8569d4
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
//
|
|
||||||
// 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) {
|
|
||||||
|
|
||||||
if (!fullName || !masterPassword)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
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) {
|
|
||||||
|
|
||||||
if (!masterKey || !siteName)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
//
|
|
||||||
// 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);
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-algorithm.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#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 );
|
|
||||||
char const *template = count? templates[seedByte % count]: NULL;
|
|
||||||
free( templates );
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_utf8_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_push_string( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
|
||||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( mpw_utf8_strlen( fullName ) ) );
|
|
||||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
|
|
||||||
if (!masterKeySalt) {
|
|
||||||
ftl( "Could not allocate master key salt: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "masterKeySalt ID: %s\n", mpw_id_buf( 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_id_buf( 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_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_utf8_strlen( siteName ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
|
||||||
if (siteContext) {
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_utf8_strlen( siteContext ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
|
||||||
}
|
|
||||||
if (!sitePasswordInfo) {
|
|
||||||
ftl( "Could not allocate site seed info: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( 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_id_buf( 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;
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-algorithm.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#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_utf8_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_push_string( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
|
||||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( mpw_utf8_strlen( fullName ) ) );
|
|
||||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
|
|
||||||
if (!masterKeySalt) {
|
|
||||||
ftl( "Could not allocate master key salt: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "masterKeySalt ID: %s\n", mpw_id_buf( 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_id_buf( 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_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_utf8_strlen( siteName ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
|
||||||
if (siteContext) {
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_utf8_strlen( siteContext ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
|
||||||
}
|
|
||||||
if (!sitePasswordInfo) {
|
|
||||||
ftl( "Could not allocate site seed info: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( 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_id_buf( 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;
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-algorithm.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#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_utf8_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_push_string( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
|
||||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( mpw_utf8_strlen( fullName ) ) );
|
|
||||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
|
|
||||||
if (!masterKeySalt) {
|
|
||||||
ftl( "Could not allocate master key salt: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "masterKeySalt ID: %s\n", mpw_id_buf( 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_id_buf( 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_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
|
||||||
if (siteContext) {
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
|
||||||
}
|
|
||||||
if (!sitePasswordInfo) {
|
|
||||||
ftl( "Could not allocate site seed info: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( 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_id_buf( 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;
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-algorithm.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#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_push_string( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
|
||||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( strlen( fullName ) ) );
|
|
||||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
|
|
||||||
if (!masterKeySalt) {
|
|
||||||
ftl( "Could not allocate master key salt: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "masterKeySalt ID: %s\n", mpw_id_buf( 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_id_buf( 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_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
|
||||||
if (siteContext) {
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
|
||||||
}
|
|
||||||
if (!sitePasswordInfo) {
|
|
||||||
ftl( "Could not allocate site seed info: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( 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_id_buf( 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;
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
//
|
|
||||||
// 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 <math.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;
|
|
||||||
unsigned int iterations;
|
|
||||||
float percent;
|
|
||||||
const uint8_t *masterKey;
|
|
||||||
|
|
||||||
// Start HMAC-SHA-256
|
|
||||||
// Similar to phase-two of mpw
|
|
||||||
uint8_t *sitePasswordInfo = malloc( 128 );
|
|
||||||
iterations = 3000000;
|
|
||||||
masterKey = mpw_masterKeyForUser( fullName, masterPassword, MPAlgorithmVersionCurrent );
|
|
||||||
if (!masterKey) {
|
|
||||||
ftl( "Could not allocate master key: %d\n", errno );
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
mpw_getTime( &startTime );
|
|
||||||
for (int i = 1; i <= iterations; ++i) {
|
|
||||||
free( (void *)mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, 128 ) );
|
|
||||||
|
|
||||||
if (modff(100.f * i / iterations, &percent) == 0)
|
|
||||||
fprintf( stderr, "\rhmac-sha-256: iteration %d / %d (%.0f%%)..", i, iterations, percent );
|
|
||||||
}
|
|
||||||
const double hmacSha256Speed = mpw_showSpeed( startTime, iterations, "hmac-sha-256" );
|
|
||||||
free( (void *)masterKey );
|
|
||||||
|
|
||||||
// Start BCrypt
|
|
||||||
// Similar to phase-one of mpw
|
|
||||||
int bcrypt_cost = 9;
|
|
||||||
iterations = 1000;
|
|
||||||
mpw_getTime( &startTime );
|
|
||||||
for (int i = 1; i <= iterations; ++i) {
|
|
||||||
crypt( masterPassword, crypt_gensalt( "$2b$", bcrypt_cost, fullName, strlen( fullName ) ) );
|
|
||||||
|
|
||||||
if (modff(100.f * i / iterations, &percent) == 0)
|
|
||||||
fprintf( stderr, "\rbcrypt (cost %d): iteration %d / %d (%.0f%%)..", bcrypt_cost, i, iterations, percent );
|
|
||||||
}
|
|
||||||
const double bcrypt9Speed = mpw_showSpeed( startTime, iterations, "bcrypt9" );
|
|
||||||
|
|
||||||
// Start SCrypt
|
|
||||||
// Phase one of mpw
|
|
||||||
iterations = 50;
|
|
||||||
mpw_getTime( &startTime );
|
|
||||||
for (int i = 1; i <= iterations; ++i) {
|
|
||||||
free( (void *)mpw_masterKeyForUser( fullName, masterPassword, MPAlgorithmVersionCurrent ) );
|
|
||||||
|
|
||||||
if (modff(100.f * i / iterations, &percent) == 0)
|
|
||||||
fprintf( stderr, "\rscrypt_mpw: iteration %d / %d (%.0f%%)..", i, iterations, percent );
|
|
||||||
}
|
|
||||||
const double scryptSpeed = mpw_showSpeed( startTime, iterations, "scrypt_mpw" );
|
|
||||||
|
|
||||||
// Start MPW
|
|
||||||
// Both phases of mpw
|
|
||||||
iterations = 50;
|
|
||||||
mpw_getTime( &startTime );
|
|
||||||
for (int i = 1; i <= iterations; ++i) {
|
|
||||||
masterKey = mpw_masterKeyForUser( fullName, masterPassword, MPAlgorithmVersionCurrent );
|
|
||||||
if (!masterKey) {
|
|
||||||
ftl( "Could not allocate master key: %d\n", errno );
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
free( (void *)mpw_passwordForSite(
|
|
||||||
masterKey, siteName, siteType, siteCounter, siteVariant, siteContext, MPAlgorithmVersionCurrent ) );
|
|
||||||
free( (void *)masterKey );
|
|
||||||
|
|
||||||
if (modff(100.f * i / iterations, &percent) == 0)
|
|
||||||
fprintf( stderr, "\rmpw: iteration %d / %d (%.0f%%)..", i, iterations, percent );
|
|
||||||
}
|
|
||||||
const double mpwSpeed = mpw_showSpeed( startTime, iterations, "mpw" );
|
|
||||||
|
|
||||||
// Summarize.
|
|
||||||
fprintf( stdout, "\n== SUMMARY ==\nOn this machine,\n" );
|
|
||||||
fprintf( stdout, " - mpw is %f times slower than hmac-sha-256 (reference: 320000 on an MBP Late 2013).\n", hmacSha256Speed / mpwSpeed );
|
|
||||||
fprintf( stdout, " - mpw is %f times slower than bcrypt (cost 9) (reference: 22 on an MBP Late 2013).\n", bcrypt9Speed / mpwSpeed );
|
|
||||||
fprintf( stdout, " - scrypt is %f times slower than bcrypt (cost 9) (reference: 22 on an MBP Late 2013).\n", bcrypt9Speed / scryptSpeed );
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
#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
|
|
||||||
|
|
||||||
#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] [-a algorithm] [-V variant] [-C context] [-v|-q] [-h] site\n\n" );
|
|
||||||
fprintf( stderr, " -u name Specify the full name of the user.\n"
|
|
||||||
" Defaults to %s in env or prompts.\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, " -a 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, " -v Increase output verbosity (can be repeated).\n\n" );
|
|
||||||
fprintf( stderr, " -q Decrease output verbosity (can be repeated).\n\n" );
|
|
||||||
fprintf( stderr, " ENVIRONMENT\n\n"
|
|
||||||
" %-14s | The full name of the user (see -u).\n"
|
|
||||||
" %-14s | The default password template (see -t).\n"
|
|
||||||
" %-14s | The default counter value (see -c).\n"
|
|
||||||
" %-14s | The default algorithm version (see -a).\n\n",
|
|
||||||
MP_env_fullname, MP_env_sitetype, MP_env_sitecounter, MP_env_algorithm );
|
|
||||||
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 *getline_prompt(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 )) <= 1) {
|
|
||||||
free( buf );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
buf[lineSize - 1] = 0;
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *const argv[]) {
|
|
||||||
|
|
||||||
// Read the environment.
|
|
||||||
const char *fullName = NULL;
|
|
||||||
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:a:C:vqh" )) != -1;)
|
|
||||||
switch (opt) {
|
|
||||||
case 'u':
|
|
||||||
fullName = strdup( 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 'a':
|
|
||||||
if (sscanf( optarg, "%u", &algorithmVersion ) != 1)
|
|
||||||
ftl( "Not a version: %s\n", optarg );
|
|
||||||
break;
|
|
||||||
case 'C':
|
|
||||||
siteContextString = optarg;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
++mpw_verbosity;
|
|
||||||
break;
|
|
||||||
case 'q':
|
|
||||||
--mpw_verbosity;
|
|
||||||
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 = strdup( argv[optind] );
|
|
||||||
|
|
||||||
// Convert and validate input.
|
|
||||||
if (!fullName && (fullName = getenv( MP_env_fullname )))
|
|
||||||
fullName = strdup( fullName );
|
|
||||||
if (!fullName && !(fullName = getline_prompt( "Your full name:" )))
|
|
||||||
ftl( "Missing full name.\n" );
|
|
||||||
if (!siteName && !(siteName = getline_prompt( "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.
|
|
||||||
const char *identicon = mpw_identicon( fullName, masterPassword );
|
|
||||||
fprintf( stderr, "%s's password for %s:\n[ %s ]: ", fullName, siteName, identicon );
|
|
||||||
mpw_free_string( identicon );
|
|
||||||
|
|
||||||
// Output the password.
|
|
||||||
const uint8_t *masterKey = mpw_masterKeyForUser(
|
|
||||||
fullName, masterPassword, algorithmVersion );
|
|
||||||
mpw_free_string( masterPassword );
|
|
||||||
mpw_free_string( fullName );
|
|
||||||
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 );
|
|
||||||
mpw_free_string( siteName );
|
|
||||||
if (!sitePassword)
|
|
||||||
ftl( "Couldn't derive site password." );
|
|
||||||
|
|
||||||
fprintf( stdout, "%s\n", sitePassword );
|
|
||||||
mpw_free_string( sitePassword );
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//
|
|
||||||
// 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);
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#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_free_string( sitePassword );
|
|
||||||
xmlFree( id );
|
|
||||||
xmlFree( fullName );
|
|
||||||
xmlFree( masterPassword );
|
|
||||||
xmlFree( keyID );
|
|
||||||
xmlFree( siteName );
|
|
||||||
xmlFree( siteTypeString );
|
|
||||||
xmlFree( siteVariantString );
|
|
||||||
xmlFree( siteContext );
|
|
||||||
xmlFree( result );
|
|
||||||
}
|
|
||||||
|
|
||||||
return failedTests;
|
|
||||||
}
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
//
|
|
||||||
// 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 );
|
|
||||||
return MPSiteTypeGeneratedLong;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 mpw_alloc_array( *count, const char *,
|
|
||||||
"anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedLong: {
|
|
||||||
return mpw_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 mpw_alloc_array( *count, const char *,
|
|
||||||
"CvcnoCvc", "CvcCvcno" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedBasic: {
|
|
||||||
return mpw_alloc_array( *count, const char *,
|
|
||||||
"aaanaaan", "aannaaan", "aaannaaa" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedShort: {
|
|
||||||
return mpw_alloc_array( *count, const char *,
|
|
||||||
"Cvcn" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedPIN: {
|
|
||||||
return mpw_alloc_array( *count, const char *,
|
|
||||||
"nnnn" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedName: {
|
|
||||||
return mpw_alloc_array( *count, const char *,
|
|
||||||
"cvccvcvcv" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedPhrase: {
|
|
||||||
return mpw_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 );
|
|
||||||
char const *template = count? templates[seedByte % count]: NULL;
|
|
||||||
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 )];
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
//
|
|
||||||
// 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_push_buf(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_push_string(uint8_t **buffer, size_t *const bufferSize, const char *pushString) {
|
|
||||||
|
|
||||||
mpw_push_buf( buffer, bufferSize, pushString, strlen( pushString ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void mpw_push_int(uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt) {
|
|
||||||
|
|
||||||
mpw_push_buf( buffer, bufferSize, &pushInt, sizeof( pushInt ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void mpw_free(const void *buffer, const size_t bufferSize) {
|
|
||||||
|
|
||||||
if (buffer) {
|
|
||||||
memset( (void *)buffer, 0, bufferSize );
|
|
||||||
free( (void *)buffer );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void mpw_free_string(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) {
|
|
||||||
|
|
||||||
if (!secret || !salt)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
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_id_buf(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 termsetup;
|
|
||||||
static int initputvar() {
|
|
||||||
if (!isatty(STDERR_FILENO))
|
|
||||||
return 0;
|
|
||||||
if (putvarc)
|
|
||||||
free(putvarc);
|
|
||||||
if (!termsetup) {
|
|
||||||
int status;
|
|
||||||
if (! (termsetup = (setupterm(NULL, STDERR_FILENO, &status) == OK && status == 1))) {
|
|
||||||
wrn( "Terminal doesn't support color (setupterm errno %d).\n", status );
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
putvarc=(char *)calloc(256, sizeof(char));
|
|
||||||
putvari=0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
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 (initputvar()) {
|
|
||||||
uint8_t colorIdentifier = (uint8_t)(identiconSeed[4] % 7 + 1);
|
|
||||||
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_utf8_sizeof(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_utf8_strlen(const char *utf8String) {
|
|
||||||
|
|
||||||
size_t charlen = 0;
|
|
||||||
char *remainingString = (char *)utf8String;
|
|
||||||
for (int charByteSize; (charByteSize = mpw_utf8_sizeof( (unsigned char)*remainingString )); remainingString += charByteSize)
|
|
||||||
++charlen;
|
|
||||||
|
|
||||||
return charlen;
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-util.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
//// Logging.
|
|
||||||
|
|
||||||
#ifndef trc
|
|
||||||
int mpw_verbosity;
|
|
||||||
#define trc_level 3
|
|
||||||
#define trc(...) \
|
|
||||||
if (mpw_verbosity >= 3) \
|
|
||||||
fprintf( stderr, __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
#ifndef dbg
|
|
||||||
#define dbg_level 2
|
|
||||||
#define dbg(...) \
|
|
||||||
if (mpw_verbosity >= 2) \
|
|
||||||
fprintf( stderr, __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
#ifndef inf
|
|
||||||
#define inf_level 1
|
|
||||||
#define inf(...) \
|
|
||||||
if (mpw_verbosity >= 1) \
|
|
||||||
fprintf( stderr, __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
#ifndef wrn
|
|
||||||
#define wrn_level 0
|
|
||||||
#define wrn(...) \
|
|
||||||
if (mpw_verbosity >= 0) \
|
|
||||||
fprintf( stderr, __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
#ifndef err
|
|
||||||
#define err_level -1
|
|
||||||
#define err(...) \
|
|
||||||
if (mpw_verbosity >= -1) \
|
|
||||||
fprintf( stderr, __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
#ifndef ftl
|
|
||||||
#define ftl_level -2
|
|
||||||
#define ftl(...) \
|
|
||||||
do { \
|
|
||||||
if (mpw_verbosity >= -2) \
|
|
||||||
fprintf( stderr, __VA_ARGS__ ); \
|
|
||||||
exit( 2 ); \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//// Buffers and memory.
|
|
||||||
|
|
||||||
#define mpw_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_push_buf(
|
|
||||||
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_push_string(
|
|
||||||
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_push_int(
|
|
||||||
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_free_string(
|
|
||||||
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_id_buf(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_utf8_strlen(const char *utf8String);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../Java/masterpassword-tests/src/main/resources/mpw_tests.xml
|
|
||||||
8
MasterPassword/Java/.gitignore
vendored
8
MasterPassword/Java/.gitignore
vendored
@@ -1,8 +0,0 @@
|
|||||||
# Gradle
|
|
||||||
build
|
|
||||||
.gradle
|
|
||||||
local.properties
|
|
||||||
|
|
||||||
# Maven
|
|
||||||
target
|
|
||||||
dependency-reduced-pom.xml
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
apply plugin: 'java'
|
|
||||||
|
|
||||||
description = 'Master Password Algorithm Implementation'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compile( 'com.lyndir.lhunath.opal:opal-system:1.6-p10' ) {
|
|
||||||
exclude( module: 'joda-time' )
|
|
||||||
}
|
|
||||||
compile 'com.lambdaworks:scrypt:1.4.0'
|
|
||||||
compile 'org.jetbrains:annotations:13.0'
|
|
||||||
compile 'com.google.code.findbugs:jsr305:3.0.1'
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* @author lhunath, 15-02-04
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
@ParametersAreNonnullByDefault package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
apply plugin: 'com.android.application'
|
|
||||||
|
|
||||||
android {
|
|
||||||
compileSdkVersion 25
|
|
||||||
buildToolsVersion "25.0.0"
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_1_7
|
|
||||||
targetCompatibility JavaVersion.VERSION_1_7
|
|
||||||
}
|
|
||||||
defaultConfig {
|
|
||||||
applicationId "com.lyndir.masterpassword"
|
|
||||||
minSdkVersion 19
|
|
||||||
targetSdkVersion 25
|
|
||||||
versionCode 20401
|
|
||||||
versionName "2.4.1"
|
|
||||||
}
|
|
||||||
// release with: STORE_PW=$(mpw masterpassword.keystore) KEY_PW=$(mpw masterpassword-android) gradle assembleRelease
|
|
||||||
signingConfigs {
|
|
||||||
release {
|
|
||||||
storeFile file('masterpassword.keystore')
|
|
||||||
storePassword System.getenv('STORE_PW')
|
|
||||||
|
|
||||||
keyAlias 'masterpassword-android'
|
|
||||||
keyPassword System.getenv('KEY_PW')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
signingConfig signingConfigs.release
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compile project( ':masterpassword-algorithm' )
|
|
||||||
compile project( ':masterpassword-tests' )
|
|
||||||
|
|
||||||
// Android dependencies
|
|
||||||
compile 'org.slf4j:slf4j-android:1.7.13-underscore'
|
|
||||||
compile 'com.jakewharton:butterknife:8.5.1'
|
|
||||||
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
|
|
||||||
compile files('libs/scrypt-1.4.0-native.jar')
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
/Users/lhunath/annex/secret/release-com.lyndir.masterpassword.jks
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-08-25
|
|
||||||
*/
|
|
||||||
public class Res {
|
|
||||||
|
|
||||||
public static Typeface sourceCodePro_Black;
|
|
||||||
public static Typeface sourceCodePro_ExtraLight;
|
|
||||||
public static Typeface exo_Bold;
|
|
||||||
public static Typeface exo_ExtraBold;
|
|
||||||
public static Typeface exo_Regular;
|
|
||||||
public static Typeface exo_Thin;
|
|
||||||
|
|
||||||
private static boolean initialized;
|
|
||||||
|
|
||||||
public static void init(Resources resources) {
|
|
||||||
|
|
||||||
if (initialized)
|
|
||||||
return;
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
sourceCodePro_Black = Typeface.createFromAsset( resources.getAssets(), "SourceCodePro-Black.otf" );
|
|
||||||
sourceCodePro_ExtraLight = Typeface.createFromAsset( resources.getAssets(), "SourceCodePro-ExtraLight.otf" );
|
|
||||||
exo_Bold = Typeface.createFromAsset( resources.getAssets(), "Exo2.0-Bold.otf" );
|
|
||||||
exo_ExtraBold = Typeface.createFromAsset( resources.getAssets(), "Exo2.0-ExtraBold.otf" );
|
|
||||||
exo_Regular = Typeface.createFromAsset( resources.getAssets(), "Exo2.0-Regular.otf" );
|
|
||||||
exo_Thin = Typeface.createFromAsset( resources.getAssets(), "Exo2.0-Thin.otf" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
/Users/lhunath/SpiderOak Hive/secret/release-com.lyndir.masterpassword.jks
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
rootProject.name = 'masterpassword'
|
|
||||||
include ':masterpassword-tests'
|
|
||||||
include ':masterpassword-algorithm'
|
|
||||||
include ':masterpassword-model'
|
|
||||||
include ':masterpassword-cli'
|
|
||||||
include ':masterpassword-gui'
|
|
||||||
include ':masterpassword-android'
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPAlgorithm
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 16/07/12.
|
|
||||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPKey.h"
|
|
||||||
#import "MPStoredSiteEntity.h"
|
|
||||||
#import "MPGeneratedSiteEntity.h"
|
|
||||||
#import "MPSiteQuestionEntity.h"
|
|
||||||
#import "mpw-algorithm.h"
|
|
||||||
|
|
||||||
#define MPAlgorithmDefaultVersion MPAlgorithmVersionCurrent
|
|
||||||
#define MPAlgorithmDefault MPAlgorithmForVersion(MPAlgorithmDefaultVersion)
|
|
||||||
|
|
||||||
id<MPAlgorithm> MPAlgorithmForVersion(MPAlgorithmVersion version);
|
|
||||||
id<MPAlgorithm> MPAlgorithmDefaultForBundleVersion(NSString *bundleVersion);
|
|
||||||
|
|
||||||
PearlEnum( MPAttacker,
|
|
||||||
MPAttacker1, MPAttacker5K, MPAttacker20M, MPAttacker5B );
|
|
||||||
|
|
||||||
typedef struct TimeToCrack {
|
|
||||||
unsigned long long hours;
|
|
||||||
unsigned long long days;
|
|
||||||
unsigned long long weeks;
|
|
||||||
unsigned long long months;
|
|
||||||
unsigned long long years;
|
|
||||||
unsigned long long universes;
|
|
||||||
} TimeToCrack;
|
|
||||||
|
|
||||||
NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
|
|
||||||
|
|
||||||
@protocol MPAlgorithm<NSObject>
|
|
||||||
|
|
||||||
@required
|
|
||||||
- (MPAlgorithmVersion)version;
|
|
||||||
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc;
|
|
||||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit;
|
|
||||||
|
|
||||||
- (NSData *)keyIDForKeyData:(NSData *)keyData;
|
|
||||||
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
|
||||||
|
|
||||||
- (NSString *)nameOfType:(MPSiteType)type;
|
|
||||||
- (NSString *)shortNameOfType:(MPSiteType)type;
|
|
||||||
- (NSString *)classNameOfType:(MPSiteType)type;
|
|
||||||
- (Class)classOfType:(MPSiteType)type;
|
|
||||||
- (NSArray *)allTypes;
|
|
||||||
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType;
|
|
||||||
- (MPSiteType)nextType:(MPSiteType)type;
|
|
||||||
- (MPSiteType)previousType:(MPSiteType)type;
|
|
||||||
|
|
||||||
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key;
|
|
||||||
- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
|
||||||
usingKey:(MPKey *)key;
|
|
||||||
- (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key;
|
|
||||||
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
|
||||||
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key;
|
|
||||||
|
|
||||||
- (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
|
|
||||||
- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
|
|
||||||
|
|
||||||
- (BOOL)savePassword:(NSString *)clearPassword toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
|
||||||
|
|
||||||
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
|
||||||
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
|
||||||
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
|
||||||
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey;
|
|
||||||
|
|
||||||
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
|
|
||||||
result:(void ( ^ )(NSString *result))resultBlock;
|
|
||||||
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
|
|
||||||
result:(void ( ^ )(NSString *result))resultBlock;
|
|
||||||
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
|
|
||||||
result:(void ( ^ )(NSString *result))resultBlock;
|
|
||||||
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey
|
|
||||||
result:(void ( ^ )(NSString *result))resultBlock;
|
|
||||||
|
|
||||||
- (void)importProtectedPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey
|
|
||||||
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
|
||||||
- (void)importClearTextPassword:(NSString *)clearPassword intoSite:(MPSiteEntity *)site
|
|
||||||
usingKey:(MPKey *)siteKey;
|
|
||||||
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
|
||||||
|
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker;
|
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPAlgorithmV0
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 16/07/12.
|
|
||||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAlgorithm.h"
|
|
||||||
|
|
||||||
@interface MPAlgorithmV0 : NSObject<MPAlgorithm>
|
|
||||||
@end
|
|
||||||
@@ -1,894 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPAlgorithmV0
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 16/07/12.
|
|
||||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef trc
|
|
||||||
#error error
|
|
||||||
#endif
|
|
||||||
#import "MPAlgorithmV0.h"
|
|
||||||
#import "MPEntities.h"
|
|
||||||
#import "MPAppDelegate_Shared.h"
|
|
||||||
#import "MPAppDelegate_InApp.h"
|
|
||||||
#import "mpw-util.h"
|
|
||||||
#include <openssl/bn.h>
|
|
||||||
#include <openssl/err.h>
|
|
||||||
|
|
||||||
/* An AMD HD 7970 calculates 2495M SHA-1 hashes per second at a cost of ~350$ per GPU */
|
|
||||||
#define CRACKING_PER_SECOND 2495000000UL
|
|
||||||
#define CRACKING_PRICE 350
|
|
||||||
|
|
||||||
NSOperationQueue *_mpwQueue = nil;
|
|
||||||
|
|
||||||
@implementation MPAlgorithmV0 {
|
|
||||||
BN_CTX *_ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init {
|
|
||||||
|
|
||||||
if (!(self = [super init]))
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
_ctx = BN_CTX_new();
|
|
||||||
|
|
||||||
static dispatch_once_t once = 0;
|
|
||||||
dispatch_once( &once, ^{
|
|
||||||
_mpwQueue = [NSOperationQueue new];
|
|
||||||
_mpwQueue.maxConcurrentOperationCount = 1;
|
|
||||||
_mpwQueue.name = @"mpw queue";
|
|
||||||
} );
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc {
|
|
||||||
|
|
||||||
BN_CTX_free( _ctx );
|
|
||||||
_ctx = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPAlgorithmVersion)version {
|
|
||||||
|
|
||||||
return MPAlgorithmVersion0;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)description {
|
|
||||||
|
|
||||||
return strf( @"V%lu", (unsigned long)self.version );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)debugDescription {
|
|
||||||
|
|
||||||
return strf( @"<%@: version=%lu>", NSStringFromClass( [self class] ), (unsigned long)self.version );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)other {
|
|
||||||
|
|
||||||
if (other == self)
|
|
||||||
return YES;
|
|
||||||
if (!other || ![other conformsToProtocol:@protocol(MPAlgorithm)])
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
return [(id<MPAlgorithm>)other version] == [self version];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)mpw_perform:(void ( ^ )(void))operationBlock {
|
|
||||||
|
|
||||||
if ([NSOperationQueue currentQueue] == _mpwQueue) {
|
|
||||||
operationBlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:operationBlock];
|
|
||||||
if ([operation respondsToSelector:@selector( qualityOfService )])
|
|
||||||
operation.qualityOfService = NSQualityOfServiceUserInitiated;
|
|
||||||
[_mpwQueue addOperations:@[ operation ] waitUntilFinished:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc {
|
|
||||||
|
|
||||||
NSError *error = nil;
|
|
||||||
NSFetchRequest *migrationRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
|
||||||
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user];
|
|
||||||
NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error];
|
|
||||||
if (!migrationSites) {
|
|
||||||
err( @"While looking for sites to migrate: %@", [error fullDescription] );
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL success = YES;
|
|
||||||
for (MPSiteEntity *migrationSite in migrationSites)
|
|
||||||
if (![migrationSite tryMigrateExplicitly:NO])
|
|
||||||
success = NO;
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
|
||||||
|
|
||||||
if ([site.algorithm version] != [self version] - 1)
|
|
||||||
// Only migrate from previous version.
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
if (!explicit) {
|
|
||||||
// This migration requires explicit permission.
|
|
||||||
site.requiresExplicitMigration = YES;
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply migration.
|
|
||||||
site.requiresExplicitMigration = NO;
|
|
||||||
site.algorithm = self;
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
|
|
||||||
|
|
||||||
__block NSData *keyData;
|
|
||||||
[self mpw_perform:^{
|
|
||||||
NSDate *start = [NSDate date];
|
|
||||||
uint8_t const *masterKeyBytes = mpw_masterKeyForUser( fullName.UTF8String, masterPassword.UTF8String, [self version] );
|
|
||||||
if (masterKeyBytes) {
|
|
||||||
keyData = [NSData dataWithBytes:masterKeyBytes length:MP_dkLen];
|
|
||||||
trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", //
|
|
||||||
fullName, masterPassword, [self keyIDForKeyData:keyData], -[start timeIntervalSinceNow] );
|
|
||||||
mpw_free( masterKeyBytes, MP_dkLen );
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
return keyData;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSData *)keyIDForKeyData:(NSData *)keyData {
|
|
||||||
|
|
||||||
return [keyData hashWith:PearlHashSHA256];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)nameOfType:(MPSiteType)type {
|
|
||||||
|
|
||||||
if (!type)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case MPSiteTypeGeneratedMaximum:
|
|
||||||
return @"Maximum Security Password";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedLong:
|
|
||||||
return @"Long Password";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
|
||||||
return @"Medium Password";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
|
||||||
return @"Basic Password";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedShort:
|
|
||||||
return @"Short Password";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
return @"PIN";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
return @"Name";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedPhrase:
|
|
||||||
return @"Phrase";
|
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal:
|
|
||||||
return @"Personal Password";
|
|
||||||
|
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
|
||||||
return @"Device Private Password";
|
|
||||||
}
|
|
||||||
|
|
||||||
Throw( @"Type not supported: %lu", (long)type );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)shortNameOfType:(MPSiteType)type {
|
|
||||||
|
|
||||||
if (!type)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case MPSiteTypeGeneratedMaximum:
|
|
||||||
return @"Maximum";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedLong:
|
|
||||||
return @"Long";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
|
||||||
return @"Medium";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
|
||||||
return @"Basic";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedShort:
|
|
||||||
return @"Short";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
return @"PIN";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
return @"Name";
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedPhrase:
|
|
||||||
return @"Phrase";
|
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal:
|
|
||||||
return @"Personal";
|
|
||||||
|
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
|
||||||
return @"Device";
|
|
||||||
}
|
|
||||||
|
|
||||||
Throw( @"Type not supported: %lu", (long)type );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)classNameOfType:(MPSiteType)type {
|
|
||||||
|
|
||||||
return NSStringFromClass( [self classOfType:type] );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (Class)classOfType:(MPSiteType)type {
|
|
||||||
|
|
||||||
if (!type)
|
|
||||||
Throw( @"No type given." );
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case MPSiteTypeGeneratedMaximum:
|
|
||||||
return [MPGeneratedSiteEntity class];
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedLong:
|
|
||||||
return [MPGeneratedSiteEntity class];
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
|
||||||
return [MPGeneratedSiteEntity class];
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
|
||||||
return [MPGeneratedSiteEntity class];
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedShort:
|
|
||||||
return [MPGeneratedSiteEntity class];
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
return [MPGeneratedSiteEntity class];
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
return [MPGeneratedSiteEntity class];
|
|
||||||
|
|
||||||
case MPSiteTypeGeneratedPhrase:
|
|
||||||
return [MPGeneratedSiteEntity class];
|
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal:
|
|
||||||
return [MPStoredSiteEntity class];
|
|
||||||
|
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
|
||||||
return [MPStoredSiteEntity class];
|
|
||||||
}
|
|
||||||
|
|
||||||
Throw( @"Type not supported: %lu", (long)type );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSArray *)allTypes {
|
|
||||||
|
|
||||||
return [self allTypesStartingWith:MPSiteTypeGeneratedPhrase];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType {
|
|
||||||
|
|
||||||
NSMutableArray *allTypes = [[NSMutableArray alloc] initWithCapacity:8];
|
|
||||||
MPSiteType currentType = startingType;
|
|
||||||
do {
|
|
||||||
[allTypes addObject:@(currentType)];
|
|
||||||
} while ((currentType = [self nextType:currentType]) != startingType);
|
|
||||||
|
|
||||||
return allTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPSiteType)nextType:(MPSiteType)type {
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case MPSiteTypeGeneratedPhrase:
|
|
||||||
return MPSiteTypeGeneratedName;
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
return MPSiteTypeGeneratedMaximum;
|
|
||||||
case MPSiteTypeGeneratedMaximum:
|
|
||||||
return MPSiteTypeGeneratedLong;
|
|
||||||
case MPSiteTypeGeneratedLong:
|
|
||||||
return MPSiteTypeGeneratedMedium;
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
|
||||||
return MPSiteTypeGeneratedBasic;
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
|
||||||
return MPSiteTypeGeneratedShort;
|
|
||||||
case MPSiteTypeGeneratedShort:
|
|
||||||
return MPSiteTypeGeneratedPIN;
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
return MPSiteTypeStoredPersonal;
|
|
||||||
case MPSiteTypeStoredPersonal:
|
|
||||||
return MPSiteTypeStoredDevicePrivate;
|
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
|
||||||
return MPSiteTypeGeneratedPhrase;
|
|
||||||
default:
|
|
||||||
return MPSiteTypeGeneratedLong;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPSiteType)previousType:(MPSiteType)type {
|
|
||||||
|
|
||||||
MPSiteType previousType = type, nextType = type;
|
|
||||||
while ((nextType = [self nextType:nextType]) != type)
|
|
||||||
previousType = nextType;
|
|
||||||
|
|
||||||
return previousType;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key {
|
|
||||||
|
|
||||||
return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedName withCounter:1
|
|
||||||
variant:MPSiteVariantLogin context:nil usingKey:key];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
|
||||||
usingKey:(MPKey *)key {
|
|
||||||
|
|
||||||
return [self generateContentForSiteNamed:name ofType:type withCounter:counter
|
|
||||||
variant:MPSiteVariantPassword context:nil usingKey:key];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key {
|
|
||||||
|
|
||||||
return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedPhrase withCounter:1
|
|
||||||
variant:MPSiteVariantAnswer context:question usingKey:key];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
|
||||||
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key {
|
|
||||||
|
|
||||||
__block NSString *content;
|
|
||||||
[self mpw_perform:^{
|
|
||||||
char const *contentBytes = mpw_passwordForSite( [key keyDataForAlgorithm:self].bytes,
|
|
||||||
name.UTF8String, type, (uint32_t)counter, variant, context.UTF8String, [self version] );
|
|
||||||
if (contentBytes) {
|
|
||||||
content = [NSString stringWithCString:contentBytes encoding:NSUTF8StringEncoding];
|
|
||||||
mpw_free_string( contentBytes );
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key {
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key {
|
|
||||||
|
|
||||||
return [self decryptContent:site.contentObject usingKey:key];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)savePassword:(NSString *)clearContent toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
|
||||||
switch (site.type) {
|
|
||||||
case MPSiteTypeGeneratedMaximum:
|
|
||||||
case MPSiteTypeGeneratedLong:
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
|
||||||
case MPSiteTypeGeneratedShort:
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
case MPSiteTypeGeneratedPhrase: {
|
|
||||||
wrn( @"Cannot save content to site with generated type %lu.", (long)site.type );
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal: {
|
|
||||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
|
||||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
|
||||||
(long)site.type, [site class] );
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSData *encryptionKey = [siteKey keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
|
||||||
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
|
||||||
encryptWithSymmetricKey:encryptionKey padding:YES];
|
|
||||||
if ([((MPStoredSiteEntity *)site).contentObject isEqualToData:encryptedContent])
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
((MPStoredSiteEntity *)site).contentObject = encryptedContent;
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
case MPSiteTypeStoredDevicePrivate: {
|
|
||||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
|
||||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
|
||||||
(long)site.type, [site class] );
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSData *encryptionKey = [siteKey keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
|
||||||
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
|
||||||
encryptWithSymmetricKey:encryptionKey padding:YES];
|
|
||||||
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
|
||||||
if (!encryptedContent)
|
|
||||||
[PearlKeyChain deleteItemForQuery:siteQuery];
|
|
||||||
else
|
|
||||||
[PearlKeyChain addOrUpdateItemForQuery:siteQuery withAttributes:@{
|
|
||||||
(__bridge id)kSecValueData : encryptedContent,
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
|
||||||
#endif
|
|
||||||
}];
|
|
||||||
((MPStoredSiteEntity *)site).contentObject = nil;
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Throw( @"Unsupported type: %ld", (long)site.type );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
|
||||||
|
|
||||||
dispatch_group_t group = dispatch_group_create();
|
|
||||||
dispatch_group_enter( group );
|
|
||||||
__block NSString *result = nil;
|
|
||||||
[self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
|
|
||||||
result = result_;
|
|
||||||
dispatch_group_leave( group );
|
|
||||||
}];
|
|
||||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
|
||||||
|
|
||||||
dispatch_group_t group = dispatch_group_create();
|
|
||||||
dispatch_group_enter( group );
|
|
||||||
__block NSString *result = nil;
|
|
||||||
[self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
|
|
||||||
result = result_;
|
|
||||||
dispatch_group_leave( group );
|
|
||||||
}];
|
|
||||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
|
||||||
|
|
||||||
dispatch_group_t group = dispatch_group_create();
|
|
||||||
dispatch_group_enter( group );
|
|
||||||
__block NSString *result = nil;
|
|
||||||
[self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) {
|
|
||||||
result = result_;
|
|
||||||
dispatch_group_leave( group );
|
|
||||||
}];
|
|
||||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey {
|
|
||||||
|
|
||||||
dispatch_group_t group = dispatch_group_create();
|
|
||||||
dispatch_group_enter( group );
|
|
||||||
__block NSString *result = nil;
|
|
||||||
[self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) {
|
|
||||||
result = result_;
|
|
||||||
dispatch_group_leave( group );
|
|
||||||
}];
|
|
||||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
|
||||||
NSString *name = site.name;
|
|
||||||
BOOL loginGenerated = site.loginGenerated && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateLogins];
|
|
||||||
NSString *loginName = site.loginName;
|
|
||||||
id<MPAlgorithm> algorithm = nil;
|
|
||||||
if (!name.length)
|
|
||||||
err( @"Missing name." );
|
|
||||||
else if (!siteKey)
|
|
||||||
err( @"Missing key." );
|
|
||||||
else
|
|
||||||
algorithm = site.algorithm;
|
|
||||||
|
|
||||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
|
||||||
resultBlock( loginName || !loginGenerated? loginName:
|
|
||||||
[algorithm generateLoginForSiteNamed:name usingKey:siteKey] );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
|
||||||
switch (site.type) {
|
|
||||||
case MPSiteTypeGeneratedMaximum:
|
|
||||||
case MPSiteTypeGeneratedLong:
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
|
||||||
case MPSiteTypeGeneratedShort:
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
case MPSiteTypeGeneratedPhrase: {
|
|
||||||
if (![site isKindOfClass:[MPGeneratedSiteEntity class]]) {
|
|
||||||
wrn( @"Site with generated type %lu is not an MPGeneratedSiteEntity, but a %@.",
|
|
||||||
(long)site.type, [site class] );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *name = site.name;
|
|
||||||
MPSiteType type = site.type;
|
|
||||||
NSUInteger counter = ((MPGeneratedSiteEntity *)site).counter;
|
|
||||||
id<MPAlgorithm> algorithm = nil;
|
|
||||||
if (!site.name.length)
|
|
||||||
err( @"Missing name." );
|
|
||||||
else if (!siteKey)
|
|
||||||
err( @"Missing key." );
|
|
||||||
else
|
|
||||||
algorithm = site.algorithm;
|
|
||||||
|
|
||||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
|
||||||
NSString *result = [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey];
|
|
||||||
resultBlock( result );
|
|
||||||
} );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal: {
|
|
||||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
|
||||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
|
||||||
(long)site.type, [site class] );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSData *encryptedContent = ((MPStoredSiteEntity *)site).contentObject;
|
|
||||||
|
|
||||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
|
||||||
NSString *result = [self decryptContent:encryptedContent usingKey:siteKey];
|
|
||||||
resultBlock( result );
|
|
||||||
} );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MPSiteTypeStoredDevicePrivate: {
|
|
||||||
NSAssert( [site isKindOfClass:[MPStoredSiteEntity class]],
|
|
||||||
@"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.", (long)site.type,
|
|
||||||
[site class] );
|
|
||||||
|
|
||||||
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
|
||||||
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:siteQuery];
|
|
||||||
|
|
||||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
|
||||||
NSString *result = [self decryptContent:encryptedContent usingKey:siteKey];
|
|
||||||
resultBlock( result );
|
|
||||||
} );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
|
||||||
NSString *name = site.name;
|
|
||||||
id<MPAlgorithm> algorithm = nil;
|
|
||||||
if (!site.name.length)
|
|
||||||
err( @"Missing name." );
|
|
||||||
else if (!siteKey)
|
|
||||||
err( @"Missing key." );
|
|
||||||
else
|
|
||||||
algorithm = site.algorithm;
|
|
||||||
|
|
||||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
|
||||||
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion:nil usingKey:siteKey];
|
|
||||||
resultBlock( result );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey
|
|
||||||
result:(void ( ^ )(NSString *result))resultBlock {
|
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:question.site.user.algorithm] isEqualToData:question.site.user.keyID],
|
|
||||||
@"Site does not belong to current user." );
|
|
||||||
NSString *name = question.site.name;
|
|
||||||
NSString *keyword = question.keyword;
|
|
||||||
id<MPAlgorithm> algorithm = nil;
|
|
||||||
if (!name.length)
|
|
||||||
err( @"Missing name." );
|
|
||||||
else if (!siteKey)
|
|
||||||
err( @"Missing key." );
|
|
||||||
else if ([[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateAnswers])
|
|
||||||
algorithm = question.site.algorithm;
|
|
||||||
|
|
||||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
|
||||||
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion:keyword usingKey:siteKey];
|
|
||||||
resultBlock( result );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)importProtectedPassword:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
|
|
||||||
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
|
||||||
switch (site.type) {
|
|
||||||
case MPSiteTypeGeneratedMaximum:
|
|
||||||
case MPSiteTypeGeneratedLong:
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
|
||||||
case MPSiteTypeGeneratedShort:
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
case MPSiteTypeGeneratedPhrase:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal: {
|
|
||||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
|
||||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
|
||||||
(long)site.type, [site class] );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ([[importKey keyIDForAlgorithm:self] isEqualToData:[siteKey keyIDForAlgorithm:self]])
|
|
||||||
((MPStoredSiteEntity *)site).contentObject = [protectedContent decodeBase64];
|
|
||||||
|
|
||||||
else {
|
|
||||||
NSString *clearContent = [self decryptContent:[protectedContent decodeBase64] usingKey:importKey];
|
|
||||||
[self importClearTextPassword:clearContent intoSite:site usingKey:siteKey];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)importClearTextPassword:(NSString *)clearContent intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
|
||||||
switch (site.type) {
|
|
||||||
case MPSiteTypeGeneratedMaximum:
|
|
||||||
case MPSiteTypeGeneratedLong:
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
|
||||||
case MPSiteTypeGeneratedShort:
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
case MPSiteTypeGeneratedPhrase:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal: {
|
|
||||||
[self savePassword:clearContent toSite:site usingKey:siteKey];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MPSiteTypeStoredDevicePrivate:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
|
||||||
|
|
||||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
|
||||||
if (!(site.type & MPSiteFeatureExportContent))
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
NSString *result = nil;
|
|
||||||
switch (site.type) {
|
|
||||||
case MPSiteTypeGeneratedMaximum:
|
|
||||||
case MPSiteTypeGeneratedLong:
|
|
||||||
case MPSiteTypeGeneratedMedium:
|
|
||||||
case MPSiteTypeGeneratedBasic:
|
|
||||||
case MPSiteTypeGeneratedShort:
|
|
||||||
case MPSiteTypeGeneratedPIN:
|
|
||||||
case MPSiteTypeGeneratedName:
|
|
||||||
case MPSiteTypeGeneratedPhrase: {
|
|
||||||
result = nil;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MPSiteTypeStoredPersonal: {
|
|
||||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
|
||||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
|
||||||
(long)site.type, [site class] );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
result = [((MPStoredSiteEntity *)site).contentObject encodeBase64];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MPSiteTypeStoredDevicePrivate: {
|
|
||||||
result = nil;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)migrateExplicitly:(BOOL)explicit {
|
|
||||||
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDictionary *)queryForDevicePrivateSiteNamed:(NSString *)name {
|
|
||||||
|
|
||||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
|
||||||
attributes:@{
|
|
||||||
(__bridge id)kSecAttrService : @"DevicePrivate",
|
|
||||||
(__bridge id)kSecAttrAccount : name
|
|
||||||
}
|
|
||||||
matches:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)decryptContent:(NSData *)encryptedContent usingKey:(MPKey *)key {
|
|
||||||
|
|
||||||
if (!key)
|
|
||||||
return nil;
|
|
||||||
NSData *decryptedContent = nil;
|
|
||||||
if ([encryptedContent length]) {
|
|
||||||
NSData *encryptionKey = [key keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
|
||||||
decryptedContent = [encryptedContent decryptWithSymmetricKey:encryptionKey padding:YES];
|
|
||||||
}
|
|
||||||
if (!decryptedContent)
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker {
|
|
||||||
|
|
||||||
if (!(type & MPSiteTypeClassGenerated))
|
|
||||||
return NO;
|
|
||||||
size_t count = 0;
|
|
||||||
const char **templates = mpw_templatesForType( type, &count );
|
|
||||||
if (!templates)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
BIGNUM *permutations = BN_new(), *templatePermutations = BN_new();
|
|
||||||
for (size_t t = 0; t < count; ++t) {
|
|
||||||
const char *template = templates[t];
|
|
||||||
BN_one( templatePermutations );
|
|
||||||
|
|
||||||
for (NSUInteger c = 0; c < strlen( template ); ++c)
|
|
||||||
BN_mul_word( templatePermutations,
|
|
||||||
(BN_ULONG)strlen( mpw_charactersInClass( template[c] ) ) );
|
|
||||||
|
|
||||||
BN_add( permutations, permutations, templatePermutations );
|
|
||||||
}
|
|
||||||
BN_free( templatePermutations );
|
|
||||||
free( templates );
|
|
||||||
|
|
||||||
return [self timeToCrack:timeToCrack permutations:permutations forAttacker:attacker];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker {
|
|
||||||
|
|
||||||
BIGNUM *permutations = BN_new();
|
|
||||||
BN_one( permutations );
|
|
||||||
|
|
||||||
for (NSUInteger c = 0; c < [password length]; ++c) {
|
|
||||||
const char passwordCharacter = [password substringWithRange:NSMakeRange( c, 1 )].UTF8String[0];
|
|
||||||
|
|
||||||
unsigned int characterEntropy = 0;
|
|
||||||
for (NSString *characterClass in @[ @"v", @"c", @"a", @"x" ]) {
|
|
||||||
char const *charactersForClass = mpw_charactersInClass( characterClass.UTF8String[0] );
|
|
||||||
|
|
||||||
if (strchr( charactersForClass, passwordCharacter )) {
|
|
||||||
// Found class for password character.
|
|
||||||
characterEntropy = (BN_ULONG)strlen( charactersForClass );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!characterEntropy)
|
|
||||||
characterEntropy = 256 /* a byte */;
|
|
||||||
|
|
||||||
BN_mul_word( permutations, characterEntropy );
|
|
||||||
}
|
|
||||||
|
|
||||||
return [self timeToCrack:timeToCrack permutations:permutations forAttacker:attacker];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack permutations:(BIGNUM *)permutations forAttacker:(MPAttacker)attacker {
|
|
||||||
|
|
||||||
// Determine base seconds needed to calculate the permutations.
|
|
||||||
BIGNUM *secondsToCrack = BN_dup( permutations );
|
|
||||||
BN_div_word( secondsToCrack, CRACKING_PER_SECOND );
|
|
||||||
|
|
||||||
// Modify seconds needed by applying our hardware budget.
|
|
||||||
switch (attacker) {
|
|
||||||
case MPAttacker1:
|
|
||||||
break;
|
|
||||||
case MPAttacker5K:
|
|
||||||
BN_mul_word( secondsToCrack, CRACKING_PRICE );
|
|
||||||
BN_div_word( secondsToCrack, 5000 );
|
|
||||||
break;
|
|
||||||
case MPAttacker20M:
|
|
||||||
BN_mul_word( secondsToCrack, CRACKING_PRICE );
|
|
||||||
BN_div_word( secondsToCrack, 20000000 );
|
|
||||||
break;
|
|
||||||
case MPAttacker5B:
|
|
||||||
BN_mul_word( secondsToCrack, CRACKING_PRICE );
|
|
||||||
BN_div_word( secondsToCrack, 5000 );
|
|
||||||
BN_div_word( secondsToCrack, 1000000 );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
BIGNUM *max = BN_new();
|
|
||||||
BN_set_word( max, (BN_ULONG)-1 );
|
|
||||||
|
|
||||||
BIGNUM *hoursToCrack = BN_dup( secondsToCrack );
|
|
||||||
BN_div_word( hoursToCrack, 3600 );
|
|
||||||
if (BN_cmp( hoursToCrack, max ) < 0)
|
|
||||||
timeToCrack->hours = BN_get_word( hoursToCrack );
|
|
||||||
else
|
|
||||||
timeToCrack->hours = (BN_ULONG)-1;
|
|
||||||
|
|
||||||
BIGNUM *daysToCrack = BN_dup( hoursToCrack );
|
|
||||||
BN_div_word( daysToCrack, 24 );
|
|
||||||
if (BN_cmp( daysToCrack, max ) < 0)
|
|
||||||
timeToCrack->days = BN_get_word( daysToCrack );
|
|
||||||
else
|
|
||||||
timeToCrack->days = (BN_ULONG)-1;
|
|
||||||
|
|
||||||
BIGNUM *weeksToCrack = BN_dup( daysToCrack );
|
|
||||||
BN_div_word( weeksToCrack, 7 );
|
|
||||||
if (BN_cmp( weeksToCrack, max ) < 0)
|
|
||||||
timeToCrack->weeks = BN_get_word( weeksToCrack );
|
|
||||||
else
|
|
||||||
timeToCrack->weeks = (BN_ULONG)-1;
|
|
||||||
|
|
||||||
BIGNUM *monthsToCrack = BN_dup( daysToCrack );
|
|
||||||
BN_div_word( monthsToCrack, 31 );
|
|
||||||
if (BN_cmp( monthsToCrack, max ) < 0)
|
|
||||||
timeToCrack->months = BN_get_word( monthsToCrack );
|
|
||||||
else
|
|
||||||
timeToCrack->months = (BN_ULONG)-1;
|
|
||||||
|
|
||||||
BIGNUM *yearsToCrack = BN_dup( daysToCrack );
|
|
||||||
BN_div_word( yearsToCrack, 356 );
|
|
||||||
if (BN_cmp( yearsToCrack, max ) < 0)
|
|
||||||
timeToCrack->years = BN_get_word( yearsToCrack );
|
|
||||||
else
|
|
||||||
timeToCrack->years = (BN_ULONG)-1;
|
|
||||||
|
|
||||||
BIGNUM *universesToCrack = BN_dup( yearsToCrack );
|
|
||||||
BN_div_word( universesToCrack, 14000 );
|
|
||||||
BN_div_word( universesToCrack, 1000000 );
|
|
||||||
if (BN_cmp( universesToCrack, max ) < 0)
|
|
||||||
timeToCrack->universes = BN_get_word( universesToCrack );
|
|
||||||
else
|
|
||||||
timeToCrack->universes = (BN_ULONG)-1;
|
|
||||||
|
|
||||||
for (unsigned long error = ERR_get_error(); error; error = ERR_get_error())
|
|
||||||
err( @"bignum error: %lu", error );
|
|
||||||
|
|
||||||
BN_free( max );
|
|
||||||
BN_free( permutations );
|
|
||||||
BN_free( secondsToCrack );
|
|
||||||
BN_free( hoursToCrack );
|
|
||||||
BN_free( daysToCrack );
|
|
||||||
BN_free( weeksToCrack );
|
|
||||||
BN_free( monthsToCrack );
|
|
||||||
BN_free( yearsToCrack );
|
|
||||||
BN_free( universesToCrack );
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPAlgorithmV1
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 17/07/12.
|
|
||||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAlgorithmV0.h"
|
|
||||||
|
|
||||||
@interface MPAlgorithmV1 : MPAlgorithmV0
|
|
||||||
@end
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPAlgorithmV1
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 17/07/12.
|
|
||||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAlgorithmV1.h"
|
|
||||||
#import "MPEntities.h"
|
|
||||||
|
|
||||||
@implementation MPAlgorithmV1
|
|
||||||
|
|
||||||
- (MPAlgorithmVersion)version {
|
|
||||||
|
|
||||||
return MPAlgorithmVersion1;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
|
||||||
|
|
||||||
if ([site.algorithm version] != [self version] - 1)
|
|
||||||
// Only migrate from previous version.
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
if (!explicit) {
|
|
||||||
if (site.type & MPSiteTypeClassGenerated) {
|
|
||||||
// This migration requires explicit permission for types of the generated class.
|
|
||||||
site.requiresExplicitMigration = YES;
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply migration.
|
|
||||||
site.requiresExplicitMigration = NO;
|
|
||||||
site.algorithm = self;
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPAlgorithmV2
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 17/07/12.
|
|
||||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAlgorithmV1.h"
|
|
||||||
|
|
||||||
@interface MPAlgorithmV2 : MPAlgorithmV1
|
|
||||||
@end
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPAlgorithmV2
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 17/07/12.
|
|
||||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAlgorithmV2.h"
|
|
||||||
#import "MPEntities.h"
|
|
||||||
|
|
||||||
@implementation MPAlgorithmV2
|
|
||||||
|
|
||||||
- (MPAlgorithmVersion)version {
|
|
||||||
|
|
||||||
return MPAlgorithmVersion2;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
|
||||||
|
|
||||||
if ([site.algorithm version] != [self version] - 1)
|
|
||||||
// Only migrate from previous version.
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
if (!explicit) {
|
|
||||||
if (site.type & MPSiteTypeClassGenerated && site.name.length != [site.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
|
||||||
// This migration requires explicit permission for types of the generated class.
|
|
||||||
site.requiresExplicitMigration = YES;
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply migration.
|
|
||||||
site.requiresExplicitMigration = NO;
|
|
||||||
site.algorithm = self;
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPAlgorithmV3
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 13/01/15.
|
|
||||||
// Copyright 2015 lhunath (Maarten Billemont). All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAlgorithmV2.h"
|
|
||||||
|
|
||||||
@interface MPAlgorithmV3 : MPAlgorithmV2
|
|
||||||
@end
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPAlgorithmV3
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 13/01/15.
|
|
||||||
// Copyright 2015 lhunath (Maarten Billemont). All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAlgorithmV3.h"
|
|
||||||
#import "MPEntities.h"
|
|
||||||
|
|
||||||
@implementation MPAlgorithmV3
|
|
||||||
|
|
||||||
- (MPAlgorithmVersion)version {
|
|
||||||
|
|
||||||
return MPAlgorithmVersion3;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
|
|
||||||
|
|
||||||
if ([site.algorithm version] != [self version] - 1)
|
|
||||||
// Only migrate from previous version.
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
if (!explicit) {
|
|
||||||
if (site.type & MPSiteTypeClassGenerated &&
|
|
||||||
site.user.name.length != [site.user.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
|
||||||
// This migration requires explicit permission for types of the generated class.
|
|
||||||
site.requiresExplicitMigration = YES;
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply migration.
|
|
||||||
site.requiresExplicitMigration = NO;
|
|
||||||
site.algorithm = self;
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
//
|
|
||||||
// MPAppDelegate_Key.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 24/11/11.
|
|
||||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <StoreKit/StoreKit.h>
|
|
||||||
#import "MPAppDelegate_Shared.h"
|
|
||||||
|
|
||||||
#define MPProductGenerateLogins @"com.lyndir.masterpassword.products.generatelogins"
|
|
||||||
#define MPProductGenerateAnswers @"com.lyndir.masterpassword.products.generateanswers"
|
|
||||||
#define MPProductOSIntegration @"com.lyndir.masterpassword.products.osintegration"
|
|
||||||
#define MPProductTouchID @"com.lyndir.masterpassword.products.touchid"
|
|
||||||
#define MPProductFuel @"com.lyndir.masterpassword.products.fuel"
|
|
||||||
|
|
||||||
#define MP_FUEL_HOURLY_RATE 30.f /* Tier 1 purchases/h ~> USD/h */
|
|
||||||
|
|
||||||
@protocol MPInAppDelegate
|
|
||||||
|
|
||||||
- (void)updateWithProducts:(NSArray /* SKProduct */ *)products;
|
|
||||||
- (void)updateWithTransaction:(SKPaymentTransaction *)transaction;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface MPAppDelegate_Shared(InApp)
|
|
||||||
|
|
||||||
- (void)registerProductsObserver:(id<MPInAppDelegate>)delegate;
|
|
||||||
- (void)removeProductsObserver:(id<MPInAppDelegate>)delegate;
|
|
||||||
|
|
||||||
- (void)reloadProducts;
|
|
||||||
- (BOOL)canMakePayments;
|
|
||||||
- (BOOL)isFeatureUnlocked:(NSString *)productIdentifier;
|
|
||||||
|
|
||||||
- (void)restoreCompletedTransactions;
|
|
||||||
- (void)purchaseProductWithIdentifier:(NSString *)productIdentifier quantity:(NSInteger)quantity;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
//
|
|
||||||
// MPAppDelegate.m
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 24/11/11.
|
|
||||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAppDelegate_InApp.h"
|
|
||||||
|
|
||||||
@interface MPAppDelegate_Shared(InApp_Private)<SKProductsRequestDelegate, SKPaymentTransactionObserver>
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation MPAppDelegate_Shared(InApp)
|
|
||||||
|
|
||||||
PearlAssociatedObjectProperty( NSArray*, Products, products );
|
|
||||||
PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObservers );
|
|
||||||
|
|
||||||
- (void)registerProductsObserver:(id<MPInAppDelegate>)delegate {
|
|
||||||
|
|
||||||
if (!self.productObservers)
|
|
||||||
self.productObservers = [NSMutableArray array];
|
|
||||||
[self.productObservers addObject:delegate];
|
|
||||||
|
|
||||||
if (self.products)
|
|
||||||
[delegate updateWithProducts:self.products];
|
|
||||||
else
|
|
||||||
[self reloadProducts];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeProductsObserver:(id<MPInAppDelegate>)delegate {
|
|
||||||
|
|
||||||
[self.productObservers removeObject:delegate];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reloadProducts {
|
|
||||||
|
|
||||||
SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:
|
|
||||||
[[NSSet alloc] initWithObjects:MPProductGenerateLogins, MPProductGenerateAnswers, MPProductTouchID, MPProductFuel, nil]];
|
|
||||||
productsRequest.delegate = self;
|
|
||||||
[productsRequest start];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (SKPaymentQueue *)paymentQueue {
|
|
||||||
|
|
||||||
static dispatch_once_t once = 0;
|
|
||||||
dispatch_once( &once, ^{
|
|
||||||
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
|
|
||||||
} );
|
|
||||||
|
|
||||||
return [SKPaymentQueue defaultQueue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)canMakePayments {
|
|
||||||
|
|
||||||
return [SKPaymentQueue canMakePayments];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isFeatureUnlocked:(NSString *)productIdentifier {
|
|
||||||
|
|
||||||
if (![productIdentifier length])
|
|
||||||
// Missing a product.
|
|
||||||
return NO;
|
|
||||||
if ([productIdentifier isEqualToString:MPProductFuel])
|
|
||||||
// Consumable product.
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
#if ADHOC || DEBUG
|
|
||||||
// All features are unlocked for beta / debug / mac versions.
|
|
||||||
return YES;
|
|
||||||
#else
|
|
||||||
// Check if product is purchased.
|
|
||||||
return [[NSUserDefaults standardUserDefaults] objectForKey:productIdentifier] != nil;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)restoreCompletedTransactions {
|
|
||||||
|
|
||||||
[[self paymentQueue] restoreCompletedTransactions];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)purchaseProductWithIdentifier:(NSString *)productIdentifier quantity:(NSInteger)quantity {
|
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
if (![[MPAppDelegate_Shared get] canMakePayments]) {
|
|
||||||
[PearlAlert showAlertWithTitle:@"Store Not Set Up" message:
|
|
||||||
@"Try logging using the App Store or from Settings."
|
|
||||||
viewStyle:UIAlertViewStyleDefault initAlert:nil
|
|
||||||
tappedButtonBlock:nil cancelTitle:@"Thanks" otherTitles:nil];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (SKProduct *product in self.products)
|
|
||||||
if ([product.productIdentifier isEqualToString:productIdentifier]) {
|
|
||||||
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
|
|
||||||
payment.quantity = quantity;
|
|
||||||
[[self paymentQueue] addPayment:payment];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - SKProductsRequestDelegate
|
|
||||||
|
|
||||||
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
|
|
||||||
|
|
||||||
inf( @"products: %@, invalid: %@", response.products, response.invalidProductIdentifiers );
|
|
||||||
self.products = response.products;
|
|
||||||
|
|
||||||
for (id<MPInAppDelegate> productObserver in self.productObservers)
|
|
||||||
[productObserver updateWithProducts:self.products];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
|
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
[PearlAlert showAlertWithTitle:@"Purchase Failed" message:
|
|
||||||
strf( @"%@\n\n%@", error.localizedDescription,
|
|
||||||
@"Ensure you are online and try logging out and back into iTunes from your device's Settings." )
|
|
||||||
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:nil
|
|
||||||
cancelTitle:@"OK" otherTitles:nil];
|
|
||||||
#else
|
|
||||||
#endif
|
|
||||||
err( @"StoreKit request (%@) failed: %@", request, [error fullDescription] );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)requestDidFinish:(SKRequest *)request {
|
|
||||||
|
|
||||||
dbg( @"StoreKit request (%@) finished.", request );
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - SKPaymentTransactionObserver
|
|
||||||
|
|
||||||
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
|
|
||||||
|
|
||||||
for (SKPaymentTransaction *transaction in transactions) {
|
|
||||||
dbg( @"transaction updated: %@ -> %d", transaction.payment.productIdentifier, (int)(transaction.transactionState) );
|
|
||||||
switch (transaction.transactionState) {
|
|
||||||
case SKPaymentTransactionStatePurchased: {
|
|
||||||
inf( @"purchased: %@", transaction.payment.productIdentifier );
|
|
||||||
if ([transaction.payment.productIdentifier isEqualToString:MPProductFuel]) {
|
|
||||||
float currentFuel = [[MPiOSConfig get].developmentFuelRemaining floatValue];
|
|
||||||
float purchasedFuel = transaction.payment.quantity / MP_FUEL_HOURLY_RATE;
|
|
||||||
[MPiOSConfig get].developmentFuelRemaining = @(currentFuel + purchasedFuel);
|
|
||||||
if (![MPiOSConfig get].developmentFuelChecked || currentFuel < DBL_EPSILON)
|
|
||||||
[MPiOSConfig get].developmentFuelChecked = [NSDate date];
|
|
||||||
}
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
|
|
||||||
forKey:transaction.payment.productIdentifier];
|
|
||||||
[queue finishTransaction:transaction];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SKPaymentTransactionStateRestored: {
|
|
||||||
inf( @"restored: %@", transaction.payment.productIdentifier );
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
|
|
||||||
forKey:transaction.payment.productIdentifier];
|
|
||||||
[queue finishTransaction:transaction];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SKPaymentTransactionStatePurchasing:
|
|
||||||
case SKPaymentTransactionStateDeferred:
|
|
||||||
break;
|
|
||||||
case SKPaymentTransactionStateFailed:
|
|
||||||
err( @"Transaction failed: %@, reason: %@", transaction.payment.productIdentifier, [transaction.error fullDescription] );
|
|
||||||
[queue finishTransaction:transaction];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (id<MPInAppDelegate> productObserver in self.productObservers)
|
|
||||||
[productObserver updateWithTransaction:transaction];
|
|
||||||
}
|
|
||||||
if (![[NSUserDefaults standardUserDefaults] synchronize])
|
|
||||||
wrn( @"Couldn't synchronize after transaction updates." );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
|
|
||||||
|
|
||||||
err( @"StoreKit restore failed: %@", [error fullDescription] );
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// MPAppDelegate_Key.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 24/11/11.
|
|
||||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAppDelegate_Shared.h"
|
|
||||||
|
|
||||||
@interface MPAppDelegate_Shared(Key)
|
|
||||||
|
|
||||||
- (BOOL)signInAsUser:(MPUserEntity *)user saveInContext:(NSManagedObjectContext *)moc usingMasterPassword:(NSString *)password;
|
|
||||||
- (void)signOutAnimated:(BOOL)animated;
|
|
||||||
|
|
||||||
- (void)storeSavedKeyFor:(MPUserEntity *)user;
|
|
||||||
- (void)forgetSavedKeyFor:(MPUserEntity *)user;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
//
|
|
||||||
// MPAppDelegate_Shared.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 24/11/11.
|
|
||||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPEntities.h"
|
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
@interface MPAppDelegate_Shared : PearlAppDelegate
|
|
||||||
#else
|
|
||||||
@interface MPAppDelegate_Shared : NSObject <PearlConfigDelegate>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@property(strong, nonatomic, readonly) MPKey *key;
|
|
||||||
@property(strong, nonatomic, readonly) NSManagedObjectID *activeUserOID;
|
|
||||||
@property(strong, nonatomic, readonly) NSPersistentStoreCoordinator *storeCoordinator;
|
|
||||||
|
|
||||||
+ (instancetype)get;
|
|
||||||
|
|
||||||
- (MPUserEntity *)activeUserForMainThread;
|
|
||||||
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)context;
|
|
||||||
- (void)setActiveUser:(MPUserEntity *)activeUser;
|
|
||||||
- (void)handleCoordinatorError:(NSError *)error;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
//
|
|
||||||
// MPAppDelegate_Key.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 24/11/11.
|
|
||||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAppDelegate_Shared.h"
|
|
||||||
|
|
||||||
#import "MPFixable.h"
|
|
||||||
|
|
||||||
typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
|
||||||
MPImportResultSuccess,
|
|
||||||
MPImportResultCancelled,
|
|
||||||
MPImportResultInvalidPassword,
|
|
||||||
MPImportResultMalformedInput,
|
|
||||||
MPImportResultInternalError,
|
|
||||||
};
|
|
||||||
|
|
||||||
@interface MPAppDelegate_Shared(Store)
|
|
||||||
|
|
||||||
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady;
|
|
||||||
+ (BOOL)managedObjectContextForMainThreadPerformBlock:(void (^)(NSManagedObjectContext *mainContext))mocBlock;
|
|
||||||
+ (BOOL)managedObjectContextForMainThreadPerformBlockAndWait:(void (^)(NSManagedObjectContext *mainContext))mocBlock;
|
|
||||||
+ (BOOL)managedObjectContextPerformBlock:(void (^)(NSManagedObjectContext *context))mocBlock;
|
|
||||||
+ (BOOL)managedObjectContextPerformBlockAndWait:(void (^)(NSManagedObjectContext *context))mocBlock;
|
|
||||||
|
|
||||||
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context;
|
|
||||||
- (void)deleteAndResetStore;
|
|
||||||
|
|
||||||
/** @param completion The block to execute after adding the site, executed from the main thread with the new site in the main MOC. */
|
|
||||||
- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion;
|
|
||||||
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type;
|
|
||||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
|
||||||
askImportPassword:(NSString *(^)(NSString *userName))importPassword
|
|
||||||
askUserPassword:(NSString *(^)(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword;
|
|
||||||
- (NSString *)exportSitesRevealPasswords:(BOOL)revealPasswords;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,833 +0,0 @@
|
|||||||
//
|
|
||||||
// MPAppDelegate.m
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 24/11/11.
|
|
||||||
// Copyright (c) 2011 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAppDelegate_Store.h"
|
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
|
|
||||||
#else
|
|
||||||
#define STORE_OPTIONS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MPMigrationLevelLocalStoreKey @"MPMigrationLevelLocalStoreKey"
|
|
||||||
|
|
||||||
typedef NS_ENUM( NSInteger, MPStoreMigrationLevel ) {
|
|
||||||
MPStoreMigrationLevelV1,
|
|
||||||
MPStoreMigrationLevelV2,
|
|
||||||
MPStoreMigrationLevelV3,
|
|
||||||
MPStoreMigrationLevelCurrent = MPStoreMigrationLevelV3,
|
|
||||||
};
|
|
||||||
|
|
||||||
@implementation MPAppDelegate_Shared(Store)
|
|
||||||
|
|
||||||
PearlAssociatedObjectProperty( NSOperationQueue *, StoreQueue, storeQueue );
|
|
||||||
|
|
||||||
PearlAssociatedObjectProperty( NSManagedObjectContext*, PrivateManagedObjectContext, privateManagedObjectContext );
|
|
||||||
|
|
||||||
PearlAssociatedObjectProperty( NSManagedObjectContext*, MainManagedObjectContext, mainManagedObjectContext );
|
|
||||||
|
|
||||||
PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
|
||||||
|
|
||||||
#pragma mark - Core Data setup
|
|
||||||
|
|
||||||
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady {
|
|
||||||
|
|
||||||
NSAssert( [[NSThread currentThread] isMainThread], @"Can only access main MOC from the main thread." );
|
|
||||||
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
|
|
||||||
if (!mainManagedObjectContext || ![[NSThread currentThread] isMainThread])
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
return mainManagedObjectContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (BOOL)managedObjectContextForMainThreadPerformBlock:(void ( ^ )(NSManagedObjectContext *mainContext))mocBlock {
|
|
||||||
|
|
||||||
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
|
|
||||||
if (!mainManagedObjectContext)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
[mainManagedObjectContext performBlock:^{
|
|
||||||
@try {
|
|
||||||
mocBlock( mainManagedObjectContext );
|
|
||||||
}
|
|
||||||
@catch (id exception) {
|
|
||||||
err( @"While performing managed block:\n%@", [exception fullDescription] );
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (BOOL)managedObjectContextForMainThreadPerformBlockAndWait:(void ( ^ )(NSManagedObjectContext *mainContext))mocBlock {
|
|
||||||
|
|
||||||
NSManagedObjectContext *mainManagedObjectContext = [[self get] mainManagedObjectContextIfReady];
|
|
||||||
if (!mainManagedObjectContext)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
[mainManagedObjectContext performBlockAndWait:^{
|
|
||||||
@try {
|
|
||||||
mocBlock( mainManagedObjectContext );
|
|
||||||
}
|
|
||||||
@catch (NSException *exception) {
|
|
||||||
err( @"While performing managed block:\n%@", [exception fullDescription] );
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (BOOL)managedObjectContextPerformBlock:(void ( ^ )(NSManagedObjectContext *context))mocBlock {
|
|
||||||
|
|
||||||
NSManagedObjectContext *privateManagedObjectContextIfReady = [[self get] privateManagedObjectContextIfReady];
|
|
||||||
if (!privateManagedObjectContextIfReady)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
|
||||||
moc.parentContext = privateManagedObjectContextIfReady;
|
|
||||||
[moc performBlock:^{
|
|
||||||
@try {
|
|
||||||
mocBlock( moc );
|
|
||||||
}
|
|
||||||
@catch (NSException *exception) {
|
|
||||||
err( @"While performing managed block:\n%@", [exception fullDescription] );
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (BOOL)managedObjectContextPerformBlockAndWait:(void ( ^ )(NSManagedObjectContext *context))mocBlock {
|
|
||||||
|
|
||||||
NSManagedObjectContext *privateManagedObjectContextIfReady = [[self get] privateManagedObjectContextIfReady];
|
|
||||||
if (!privateManagedObjectContextIfReady)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
|
||||||
moc.parentContext = privateManagedObjectContextIfReady;
|
|
||||||
[moc performBlockAndWait:^{
|
|
||||||
@try {
|
|
||||||
mocBlock( moc );
|
|
||||||
}
|
|
||||||
@catch (NSException *exception) {
|
|
||||||
err( @"While performing managed block:\n%@", [exception fullDescription] );
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSManagedObjectContext *)mainManagedObjectContextIfReady {
|
|
||||||
|
|
||||||
[self loadStore];
|
|
||||||
return self.mainManagedObjectContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSManagedObjectContext *)privateManagedObjectContextIfReady {
|
|
||||||
|
|
||||||
[self loadStore];
|
|
||||||
return self.privateManagedObjectContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSURL *)localStoreURL {
|
|
||||||
|
|
||||||
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
|
|
||||||
inDomains:NSUserDomainMask] lastObject];
|
|
||||||
return [[[applicationSupportURL
|
|
||||||
URLByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier isDirectory:YES]
|
|
||||||
URLByAppendingPathComponent:@"MasterPassword" isDirectory:NO]
|
|
||||||
URLByAppendingPathExtension:@"sqlite"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)loadStore {
|
|
||||||
|
|
||||||
static dispatch_once_t once = 0;
|
|
||||||
dispatch_once( &once, ^{
|
|
||||||
(self.storeQueue = [NSOperationQueue new]).maxConcurrentOperationCount = 1;
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Do nothing if already fully set up, otherwise (re-)load the store.
|
|
||||||
if (self.storeCoordinator && self.mainManagedObjectContext && self.privateManagedObjectContext)
|
|
||||||
return;
|
|
||||||
|
|
||||||
[self.storeQueue addOperationWithBlock:^{
|
|
||||||
// Do nothing if already fully set up, otherwise (re-)load the store.
|
|
||||||
if (self.storeCoordinator && self.mainManagedObjectContext && self.privateManagedObjectContext)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Unregister any existing observers and contexts.
|
|
||||||
PearlRemoveNotificationObserversFrom( self.mainManagedObjectContext );
|
|
||||||
[self.mainManagedObjectContext performBlockAndWait:^{
|
|
||||||
[self.mainManagedObjectContext reset];
|
|
||||||
self.mainManagedObjectContext = nil;
|
|
||||||
}];
|
|
||||||
[self.privateManagedObjectContext performBlockAndWait:^{
|
|
||||||
[self.privateManagedObjectContext reset];
|
|
||||||
self.privateManagedObjectContext = nil;
|
|
||||||
}];
|
|
||||||
|
|
||||||
// Don't load when the store is corrupted.
|
|
||||||
if ([self.storeCorrupted boolValue])
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Check if migration is necessary.
|
|
||||||
[self migrateStore];
|
|
||||||
|
|
||||||
// Install managed object contexts and observers.
|
|
||||||
self.privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
|
||||||
[self.privateManagedObjectContext performBlockAndWait:^{
|
|
||||||
self.privateManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
|
|
||||||
self.privateManagedObjectContext.persistentStoreCoordinator = self.storeCoordinator;
|
|
||||||
}];
|
|
||||||
|
|
||||||
self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
|
|
||||||
self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext;
|
|
||||||
|
|
||||||
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
|
|
||||||
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
|
|
||||||
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainManagedObjectContext, NSNotification *note) {
|
|
||||||
[mainManagedObjectContext performBlock:^{
|
|
||||||
@try {
|
|
||||||
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:note];
|
|
||||||
}
|
|
||||||
@catch (NSException *exception) {
|
|
||||||
err( @"While merging changes:\n%@", [exception fullDescription] );
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
} );
|
|
||||||
|
|
||||||
|
|
||||||
// Create a new store coordinator.
|
|
||||||
NSError *error = nil;
|
|
||||||
NSURL *localStoreURL = [self localStoreURL];
|
|
||||||
if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
|
|
||||||
withIntermediateDirectories:YES attributes:nil error:&error]) {
|
|
||||||
err( @"Couldn't create our application support directory: %@", [error fullDescription] );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL]
|
|
||||||
options:@{
|
|
||||||
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
|
||||||
NSInferMappingModelAutomaticallyOption : @YES,
|
|
||||||
STORE_OPTIONS
|
|
||||||
} error:&error]) {
|
|
||||||
err( @"Failed to open store: %@", [error fullDescription] );
|
|
||||||
self.storeCorrupted = @YES;
|
|
||||||
[self handleCoordinatorError:error];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.storeCorrupted = @NO;
|
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, UIApp, [NSOperationQueue mainQueue],
|
|
||||||
#else
|
|
||||||
PearlAddNotificationObserver( NSApplicationWillResignActiveNotification, NSApp, [NSOperationQueue mainQueue],
|
|
||||||
#endif
|
|
||||||
^(MPAppDelegate_Shared *self, NSNotification *note) {
|
|
||||||
[self.mainManagedObjectContext saveToStore];
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Perform a data sanity check on the newly loaded store to find and fix any issues.
|
|
||||||
if ([[MPConfig get].checkInconsistency boolValue])
|
|
||||||
[MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
|
||||||
[self findAndFixInconsistenciesSaveInContext:context];
|
|
||||||
}];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)deleteAndResetStore {
|
|
||||||
|
|
||||||
@synchronized (self) {
|
|
||||||
// Unregister any existing observers and contexts.
|
|
||||||
PearlRemoveNotificationObserversFrom( self.mainManagedObjectContext );
|
|
||||||
[self.mainManagedObjectContext performBlockAndWait:^{
|
|
||||||
[self.mainManagedObjectContext reset];
|
|
||||||
self.mainManagedObjectContext = nil;
|
|
||||||
}];
|
|
||||||
[self.privateManagedObjectContext performBlockAndWait:^{
|
|
||||||
[self.privateManagedObjectContext reset];
|
|
||||||
self.privateManagedObjectContext = nil;
|
|
||||||
}];
|
|
||||||
NSError *error = nil;
|
|
||||||
for (NSPersistentStore *store in self.storeCoordinator.persistentStores) {
|
|
||||||
if (![self.storeCoordinator removePersistentStore:store error:&error])
|
|
||||||
err( @"Couldn't remove persistence store from coordinator: %@", [error fullDescription] );
|
|
||||||
}
|
|
||||||
if (![[NSFileManager defaultManager] removeItemAtURL:self.localStoreURL error:&error])
|
|
||||||
err( @"Couldn't remove persistence store at URL %@: %@", self.localStoreURL, [error fullDescription] );
|
|
||||||
|
|
||||||
[self loadStore];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context {
|
|
||||||
|
|
||||||
NSError *error = nil;
|
|
||||||
NSFetchRequest *fetchRequest = [NSFetchRequest new];
|
|
||||||
fetchRequest.fetchBatchSize = 50;
|
|
||||||
|
|
||||||
MPFixableResult result = MPFixableResultNoProblems;
|
|
||||||
for (NSEntityDescription *entity in [context.persistentStoreCoordinator.managedObjectModel entities])
|
|
||||||
if (class_conformsToProtocol( NSClassFromString( entity.managedObjectClassName ), @protocol(MPFixable) )) {
|
|
||||||
fetchRequest.entity = entity;
|
|
||||||
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
|
|
||||||
if (!objects) {
|
|
||||||
err( @"Failed to fetch %@ objects: %@", entity, [error fullDescription] );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (NSManagedObject<MPFixable> *object in objects)
|
|
||||||
result = MPApplyFix( result, ^MPFixableResult {
|
|
||||||
return [object findAndFixInconsistenciesInContext:context];
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == MPFixableResultNoProblems)
|
|
||||||
inf( @"Sanity check found no problems in store." );
|
|
||||||
|
|
||||||
else {
|
|
||||||
[context saveToStore];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPFoundInconsistenciesNotification object:nil userInfo:@{
|
|
||||||
MPInconsistenciesFixResultUserKey : @(result)
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)migrateStore {
|
|
||||||
|
|
||||||
MPStoreMigrationLevel migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey];
|
|
||||||
if (migrationLevel >= MPStoreMigrationLevelCurrent)
|
|
||||||
// Local store up-to-date.
|
|
||||||
return;
|
|
||||||
|
|
||||||
inf( @"Local store migration level: %d (current %d)", (signed)migrationLevel, (signed)MPStoreMigrationLevelCurrent );
|
|
||||||
if (migrationLevel <= MPStoreMigrationLevelV1 && ![self migrateV1LocalStore]) {
|
|
||||||
inf( @"Failed to migrate old V1 to new local store." );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (migrationLevel <= MPStoreMigrationLevelV2 && ![self migrateV2LocalStore]) {
|
|
||||||
inf( @"Failed to migrate old V2 to new local store." );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[NSUserDefaults standardUserDefaults] setInteger:MPStoreMigrationLevelCurrent forKey:MPMigrationLevelLocalStoreKey];
|
|
||||||
inf( @"Successfully migrated old to new local store." );
|
|
||||||
if (![[NSUserDefaults standardUserDefaults] synchronize])
|
|
||||||
wrn( @"Couldn't synchronize after store migration." );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)migrateV1LocalStore {
|
|
||||||
|
|
||||||
NSURL *applicationFilesDirectory = [[[NSFileManager defaultManager]
|
|
||||||
URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
|
|
||||||
NSURL *oldLocalStoreURL = [[applicationFilesDirectory
|
|
||||||
URLByAppendingPathComponent:@"MasterPassword" isDirectory:NO] URLByAppendingPathExtension:@"sqlite"];
|
|
||||||
if (![[NSFileManager defaultManager] fileExistsAtPath:oldLocalStoreURL.path isDirectory:NULL]) {
|
|
||||||
inf( @"No V1 local store to migrate." );
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
inf( @"Migrating V1 local store" );
|
|
||||||
NSURL *newLocalStoreURL = [self localStoreURL];
|
|
||||||
if (![[NSFileManager defaultManager] fileExistsAtPath:newLocalStoreURL.path isDirectory:NULL]) {
|
|
||||||
inf( @"New local store already exists." );
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSError *error = nil;
|
|
||||||
if (![NSPersistentStore migrateStore:oldLocalStoreURL withOptions:@{ STORE_OPTIONS }
|
|
||||||
toStore:newLocalStoreURL withOptions:@{ STORE_OPTIONS }
|
|
||||||
model:nil error:&error]) {
|
|
||||||
err( @"Couldn't migrate the old store to the new location: %@", [error fullDescription] );
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)migrateV2LocalStore {
|
|
||||||
|
|
||||||
NSURL *applicationSupportURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
|
|
||||||
inDomains:NSUserDomainMask] lastObject];
|
|
||||||
NSURL *oldLocalStoreURL;
|
|
||||||
// On iOS, each app is in a sandbox so we don't need to app-scope this directory.
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
oldLocalStoreURL = [[applicationSupportURL
|
|
||||||
URLByAppendingPathComponent:@"UbiquityStore" isDirectory:NO]
|
|
||||||
URLByAppendingPathExtension:@"sqlite"];
|
|
||||||
#else
|
|
||||||
// The directory is shared between all apps on the system so we need to scope it for the running app.
|
|
||||||
oldLocalStoreURL = [[[applicationSupportURL
|
|
||||||
URLByAppendingPathComponent:[NSRunningApplication currentApplication].bundleIdentifier isDirectory:YES]
|
|
||||||
URLByAppendingPathComponent:@"UbiquityStore" isDirectory:NO]
|
|
||||||
URLByAppendingPathExtension:@"sqlite"];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (![[NSFileManager defaultManager] fileExistsAtPath:oldLocalStoreURL.path isDirectory:NULL]) {
|
|
||||||
inf( @"No V2 local store to migrate." );
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
inf( @"Migrating V2 local store" );
|
|
||||||
NSURL *newLocalStoreURL = [self localStoreURL];
|
|
||||||
if ([[NSFileManager defaultManager] fileExistsAtPath:newLocalStoreURL.path isDirectory:NULL]) {
|
|
||||||
inf( @"New local store already exists." );
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSError *error = nil;
|
|
||||||
if (![NSPersistentStore migrateStore:oldLocalStoreURL withOptions:@{
|
|
||||||
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
|
||||||
NSInferMappingModelAutomaticallyOption : @YES,
|
|
||||||
STORE_OPTIONS
|
|
||||||
} toStore:newLocalStoreURL withOptions:@{
|
|
||||||
NSMigratePersistentStoresAutomaticallyOption : @YES,
|
|
||||||
NSInferMappingModelAutomaticallyOption : @YES,
|
|
||||||
STORE_OPTIONS
|
|
||||||
} model:nil error:&error]) {
|
|
||||||
err( @"Couldn't migrate the old store to the new location: %@", [error fullDescription] );
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Utilities
|
|
||||||
|
|
||||||
- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion {
|
|
||||||
|
|
||||||
if (![siteName length]) {
|
|
||||||
completion( nil, nil );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
|
||||||
MPUserEntity *activeUser = [self activeUserInContext:context];
|
|
||||||
NSAssert( activeUser, @"Missing user." );
|
|
||||||
if (!activeUser) {
|
|
||||||
completion( nil, nil );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MPSiteType type = activeUser.defaultType;
|
|
||||||
id<MPAlgorithm> algorithm = MPAlgorithmDefault;
|
|
||||||
Class entityType = [algorithm classOfType:type];
|
|
||||||
|
|
||||||
MPSiteEntity *site = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
|
|
||||||
site.name = siteName;
|
|
||||||
site.user = activeUser;
|
|
||||||
site.type = type;
|
|
||||||
site.lastUsed = [NSDate date];
|
|
||||||
site.algorithm = algorithm;
|
|
||||||
|
|
||||||
NSError *error = nil;
|
|
||||||
if (site.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ site ] error:&error])
|
|
||||||
err( @"Failed to obtain a permanent object ID after creating new site: %@", [error fullDescription] );
|
|
||||||
|
|
||||||
[context saveToStore];
|
|
||||||
|
|
||||||
completion( site, context );
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type {
|
|
||||||
|
|
||||||
if (site.type == type)
|
|
||||||
return site;
|
|
||||||
|
|
||||||
if ([site.algorithm classOfType:type] == site.typeClass) {
|
|
||||||
site.type = type;
|
|
||||||
[context saveToStore];
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
// Type requires a different class of site. Recreate the site.
|
|
||||||
Class entityType = [site.algorithm classOfType:type];
|
|
||||||
MPSiteEntity *newSite = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
|
|
||||||
newSite.type = type;
|
|
||||||
newSite.name = site.name;
|
|
||||||
newSite.user = site.user;
|
|
||||||
newSite.uses = site.uses;
|
|
||||||
newSite.lastUsed = site.lastUsed;
|
|
||||||
newSite.algorithm = site.algorithm;
|
|
||||||
newSite.loginName = site.loginName;
|
|
||||||
|
|
||||||
NSError *error = nil;
|
|
||||||
if (![context obtainPermanentIDsForObjects:@[ newSite ] error:&error])
|
|
||||||
err( @"Failed to obtain a permanent object ID after changing object type: %@", [error fullDescription] );
|
|
||||||
|
|
||||||
[context deleteObject:site];
|
|
||||||
[context saveToStore];
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
|
|
||||||
site = newSite;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
|
|
||||||
return site;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
|
||||||
askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
|
|
||||||
askUserPassword:(NSString *( ^ )(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword {
|
|
||||||
|
|
||||||
NSAssert( ![[NSThread currentThread] isMainThread], @"This method should not be invoked from the main thread." );
|
|
||||||
|
|
||||||
__block MPImportResult result = MPImportResultCancelled;
|
|
||||||
do {
|
|
||||||
if ([MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
|
||||||
result = [self importSites:importedSitesString askImportPassword:importPassword askUserPassword:userPassword
|
|
||||||
saveInContext:context];
|
|
||||||
}])
|
|
||||||
break;
|
|
||||||
usleep( (useconds_t)(USEC_PER_SEC * 0.2) );
|
|
||||||
} while (YES);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
|
||||||
askImportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
|
||||||
askUserPassword:(NSString *( ^ )(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))askUserPassword
|
|
||||||
saveInContext:(NSManagedObjectContext *)context {
|
|
||||||
|
|
||||||
// Compile patterns.
|
|
||||||
static NSRegularExpression *headerPattern;
|
|
||||||
static NSArray *sitePatterns;
|
|
||||||
NSError *error = nil;
|
|
||||||
if (!headerPattern) {
|
|
||||||
headerPattern = [[NSRegularExpression alloc]
|
|
||||||
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
|
|
||||||
options:(NSRegularExpressionOptions)0 error:&error];
|
|
||||||
if (error) {
|
|
||||||
err( @"Error loading the header pattern: %@", [error fullDescription] );
|
|
||||||
return MPImportResultInternalError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!sitePatterns) {
|
|
||||||
sitePatterns = @[
|
|
||||||
[[NSRegularExpression alloc] // Format 0
|
|
||||||
initWithPattern:@"^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)? +([^\t]+)\t(.*)"
|
|
||||||
options:(NSRegularExpressionOptions)0 error:&error],
|
|
||||||
[[NSRegularExpression alloc] // Format 1
|
|
||||||
initWithPattern:@"^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)?(:[[:digit:]]+)? +([^\t]*)\t *([^\t]+)\t(.*)"
|
|
||||||
options:(NSRegularExpressionOptions)0 error:&error]
|
|
||||||
];
|
|
||||||
if (error) {
|
|
||||||
err( @"Error loading the site patterns: %@", [error fullDescription] );
|
|
||||||
return MPImportResultInternalError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse import data.
|
|
||||||
inf( @"Importing sites." );
|
|
||||||
__block MPUserEntity *user = nil;
|
|
||||||
id<MPAlgorithm> importAlgorithm = nil;
|
|
||||||
NSUInteger importFormat = 0;
|
|
||||||
NSUInteger importAvatar = NSNotFound;
|
|
||||||
NSString *importBundleVersion = nil, *importUserName = nil;
|
|
||||||
NSData *importKeyID = nil;
|
|
||||||
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
|
|
||||||
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
|
||||||
NSMutableSet *sitesToDelete = [NSMutableSet set];
|
|
||||||
NSMutableArray *importedSiteSites = [NSMutableArray arrayWithCapacity:[importedSiteLines count]];
|
|
||||||
NSFetchRequest *siteFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
|
||||||
for (NSString *importedSiteLine in importedSiteLines) {
|
|
||||||
if ([importedSiteLine hasPrefix:@"#"]) {
|
|
||||||
// Comment or header
|
|
||||||
if (!headerStarted) {
|
|
||||||
if ([importedSiteLine isEqualToString:@"##"])
|
|
||||||
headerStarted = YES;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (headerEnded)
|
|
||||||
continue;
|
|
||||||
if ([importedSiteLine isEqualToString:@"##"]) {
|
|
||||||
headerEnded = YES;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header
|
|
||||||
if ([headerPattern numberOfMatchesInString:importedSiteLine options:(NSMatchingOptions)0
|
|
||||||
range:NSMakeRange( 0, [importedSiteLine length] )] != 1) {
|
|
||||||
err( @"Invalid header format in line: %@", importedSiteLine );
|
|
||||||
return MPImportResultMalformedInput;
|
|
||||||
}
|
|
||||||
NSTextCheckingResult *headerSites = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
|
|
||||||
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
|
|
||||||
NSString *headerName = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:1]];
|
|
||||||
NSString *headerValue = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:2]];
|
|
||||||
if ([headerName isEqualToString:@"User Name"]) {
|
|
||||||
importUserName = headerValue;
|
|
||||||
|
|
||||||
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
|
||||||
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
|
|
||||||
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
|
|
||||||
if (!users) {
|
|
||||||
err( @"While looking for user: %@, error: %@", importUserName, [error fullDescription] );
|
|
||||||
return MPImportResultInternalError;
|
|
||||||
}
|
|
||||||
if ([users count] > 1) {
|
|
||||||
err( @"While looking for user: %@, found more than one: %lu", importUserName, (unsigned long)[users count] );
|
|
||||||
return MPImportResultInternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
user = [users lastObject];
|
|
||||||
dbg( @"Existing user? %@", [user debugDescription] );
|
|
||||||
}
|
|
||||||
if ([headerName isEqualToString:@"Key ID"])
|
|
||||||
importKeyID = [headerValue decodeHex];
|
|
||||||
if ([headerName isEqualToString:@"Version"]) {
|
|
||||||
importBundleVersion = headerValue;
|
|
||||||
importAlgorithm = MPAlgorithmDefaultForBundleVersion( importBundleVersion );
|
|
||||||
}
|
|
||||||
if ([headerName isEqualToString:@"Format"]) {
|
|
||||||
importFormat = (NSUInteger)[headerValue integerValue];
|
|
||||||
if (importFormat >= [sitePatterns count]) {
|
|
||||||
err( @"Unsupported import format: %lu", (unsigned long)importFormat );
|
|
||||||
return MPImportResultInternalError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ([headerName isEqualToString:@"Avatar"])
|
|
||||||
importAvatar = (NSUInteger)[headerValue integerValue];
|
|
||||||
if ([headerName isEqualToString:@"Passwords"]) {
|
|
||||||
if ([headerValue isEqualToString:@"VISIBLE"])
|
|
||||||
clearText = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!headerEnded)
|
|
||||||
continue;
|
|
||||||
if (![importUserName length])
|
|
||||||
return MPImportResultMalformedInput;
|
|
||||||
if (![importedSiteLine length])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Site
|
|
||||||
NSRegularExpression *sitePattern = sitePatterns[importFormat];
|
|
||||||
if ([sitePattern numberOfMatchesInString:importedSiteLine options:(NSMatchingOptions)0
|
|
||||||
range:NSMakeRange( 0, [importedSiteLine length] )] != 1) {
|
|
||||||
err( @"Invalid site format in line: %@", importedSiteLine );
|
|
||||||
return MPImportResultMalformedInput;
|
|
||||||
}
|
|
||||||
NSTextCheckingResult *siteElements = [[sitePattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
|
|
||||||
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
|
|
||||||
NSString *lastUsed, *uses, *type, *version, *counter, *siteName, *loginName, *exportContent;
|
|
||||||
switch (importFormat) {
|
|
||||||
case 0:
|
|
||||||
lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]];
|
|
||||||
uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
|
|
||||||
type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]];
|
|
||||||
version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]];
|
|
||||||
if ([version length])
|
|
||||||
version = [version substringFromIndex:1]; // Strip the leading colon.
|
|
||||||
counter = @"";
|
|
||||||
loginName = @"";
|
|
||||||
siteName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]];
|
|
||||||
exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]];
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]];
|
|
||||||
uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
|
|
||||||
type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]];
|
|
||||||
version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]];
|
|
||||||
if ([version length])
|
|
||||||
version = [version substringFromIndex:1]; // Strip the leading colon.
|
|
||||||
counter = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]];
|
|
||||||
if ([counter length])
|
|
||||||
counter = [counter substringFromIndex:1]; // Strip the leading colon.
|
|
||||||
loginName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]];
|
|
||||||
siteName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:7]];
|
|
||||||
exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:8]];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
err( @"Unexpected import format: %lu", (unsigned long)importFormat );
|
|
||||||
return MPImportResultInternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find existing site.
|
|
||||||
if (user) {
|
|
||||||
siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
|
|
||||||
NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
|
|
||||||
if (!existingSites) {
|
|
||||||
err( @"Lookup of existing sites failed for site: %@, user: %@, error: %@", siteName, user.userID, [error fullDescription] );
|
|
||||||
return MPImportResultInternalError;
|
|
||||||
}
|
|
||||||
if ([existingSites count]) {
|
|
||||||
dbg( @"Existing sites: %@", existingSites );
|
|
||||||
[sitesToDelete addObjectsFromArray:existingSites];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[importedSiteSites addObject:@[ lastUsed, uses, type, version, counter, loginName, siteName, exportContent ]];
|
|
||||||
dbg( @"Will import site: lastUsed=%@, uses=%@, type=%@, version=%@, counter=%@, loginName=%@, siteName=%@, exportContent=%@",
|
|
||||||
lastUsed, uses, type, version, counter, loginName, siteName, exportContent );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ask for confirmation to import these sites and the master password of the user.
|
|
||||||
inf( @"Importing %lu sites, deleting %lu sites, for user: %@", (unsigned long)[importedSiteSites count],
|
|
||||||
(unsigned long)[sitesToDelete count], [MPUserEntity idFor:importUserName] );
|
|
||||||
NSString *userMasterPassword = askUserPassword( user? user.name: importUserName, [importedSiteSites count],
|
|
||||||
[sitesToDelete count] );
|
|
||||||
if (!userMasterPassword) {
|
|
||||||
inf( @"Import cancelled." );
|
|
||||||
return MPImportResultCancelled;
|
|
||||||
}
|
|
||||||
MPKey *userKey = [[MPKey alloc] initForFullName:user? user.name: importUserName withMasterPassword:userMasterPassword];
|
|
||||||
if (user && ![[userKey keyIDForAlgorithm:user.algorithm] isEqualToData:user.keyID])
|
|
||||||
return MPImportResultInvalidPassword;
|
|
||||||
__block MPKey *importKey = userKey;
|
|
||||||
if (importKeyID && ![[importKey keyIDForAlgorithm:importAlgorithm] isEqualToData:importKeyID])
|
|
||||||
importKey = [[MPKey alloc] initForFullName:importUserName withMasterPassword:askImportPassword( importUserName )];
|
|
||||||
if (importKeyID && ![[importKey keyIDForAlgorithm:importAlgorithm] isEqualToData:importKeyID])
|
|
||||||
return MPImportResultInvalidPassword;
|
|
||||||
|
|
||||||
// Delete existing sites.
|
|
||||||
if (sitesToDelete.count)
|
|
||||||
[sitesToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
|
|
||||||
inf( @"Deleting site: %@, it will be replaced by an imported site.", [obj name] );
|
|
||||||
[context deleteObject:obj];
|
|
||||||
}];
|
|
||||||
|
|
||||||
// Make sure there is a user.
|
|
||||||
if (user) {
|
|
||||||
if (importAvatar != NSNotFound)
|
|
||||||
user.avatar = importAvatar;
|
|
||||||
dbg( @"Updating User: %@", [user debugDescription] );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
user = [MPUserEntity insertNewObjectInContext:context];
|
|
||||||
user.name = importUserName;
|
|
||||||
user.algorithm = MPAlgorithmDefault;
|
|
||||||
user.keyID = [userKey keyIDForAlgorithm:user.algorithm];
|
|
||||||
if (importAvatar != NSNotFound)
|
|
||||||
user.avatar = importAvatar;
|
|
||||||
dbg( @"Created User: %@", [user debugDescription] );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import new sites.
|
|
||||||
for (NSArray *siteElements in importedSiteSites) {
|
|
||||||
NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:siteElements[0]];
|
|
||||||
NSUInteger uses = (unsigned)[siteElements[1] integerValue];
|
|
||||||
MPSiteType type = (MPSiteType)[siteElements[2] integerValue];
|
|
||||||
MPAlgorithmVersion version = (MPAlgorithmVersion)[siteElements[3] integerValue];
|
|
||||||
NSUInteger counter = [siteElements[4] length]? (unsigned)[siteElements[4] integerValue]: NSNotFound;
|
|
||||||
NSString *loginName = [siteElements[5] length]? siteElements[5]: nil;
|
|
||||||
NSString *siteName = siteElements[6];
|
|
||||||
NSString *exportContent = siteElements[7];
|
|
||||||
|
|
||||||
// Create new site.
|
|
||||||
id<MPAlgorithm> algorithm = MPAlgorithmForVersion( version );
|
|
||||||
Class entityType = [algorithm classOfType:type];
|
|
||||||
if (!entityType) {
|
|
||||||
err( @"Invalid site type in import file: %@ has type %lu", siteName, (long)type );
|
|
||||||
return MPImportResultInternalError;
|
|
||||||
}
|
|
||||||
MPSiteEntity *site = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
|
|
||||||
site.name = siteName;
|
|
||||||
site.loginName = loginName;
|
|
||||||
site.user = user;
|
|
||||||
site.type = type;
|
|
||||||
site.uses = uses;
|
|
||||||
site.lastUsed = lastUsed;
|
|
||||||
site.algorithm = algorithm;
|
|
||||||
if ([exportContent length]) {
|
|
||||||
if (clearText)
|
|
||||||
[site.algorithm importClearTextPassword:exportContent intoSite:site usingKey:userKey];
|
|
||||||
else
|
|
||||||
[site.algorithm importProtectedPassword:exportContent protectedByKey:importKey intoSite:site usingKey:userKey];
|
|
||||||
}
|
|
||||||
if ([site isKindOfClass:[MPGeneratedSiteEntity class]] && counter != NSNotFound)
|
|
||||||
((MPGeneratedSiteEntity *)site).counter = counter;
|
|
||||||
|
|
||||||
dbg( @"Created Site: %@", [site debugDescription] );
|
|
||||||
}
|
|
||||||
|
|
||||||
if (![context saveToStore])
|
|
||||||
return MPImportResultInternalError;
|
|
||||||
|
|
||||||
inf( @"Import completed successfully." );
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSitesImportedNotification object:nil userInfo:@{
|
|
||||||
MPSitesImportedNotificationUserKey : user
|
|
||||||
}];
|
|
||||||
|
|
||||||
return MPImportResultSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)exportSitesRevealPasswords:(BOOL)revealPasswords {
|
|
||||||
|
|
||||||
MPUserEntity *activeUser = [self activeUserForMainThread];
|
|
||||||
inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", activeUser.userID );
|
|
||||||
|
|
||||||
// Header.
|
|
||||||
NSMutableString *export = [NSMutableString new];
|
|
||||||
[export appendFormat:@"# Master Password site export\n"];
|
|
||||||
if (revealPasswords)
|
|
||||||
[export appendFormat:@"# Export of site names and passwords in clear-text.\n"];
|
|
||||||
else
|
|
||||||
[export appendFormat:@"# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n"];
|
|
||||||
[export appendFormat:@"# \n"];
|
|
||||||
[export appendFormat:@"##\n"];
|
|
||||||
[export appendFormat:@"# User Name: %@\n", activeUser.name];
|
|
||||||
[export appendFormat:@"# Avatar: %lu\n", (unsigned long)activeUser.avatar];
|
|
||||||
[export appendFormat:@"# Key ID: %@\n", [activeUser.keyID encodeHex]];
|
|
||||||
[export appendFormat:@"# Date: %@\n", [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]]];
|
|
||||||
[export appendFormat:@"# Version: %@\n", [PearlInfoPlist get].CFBundleVersion];
|
|
||||||
[export appendFormat:@"# Format: 1\n"];
|
|
||||||
if (revealPasswords)
|
|
||||||
[export appendFormat:@"# Passwords: VISIBLE\n"];
|
|
||||||
else
|
|
||||||
[export appendFormat:@"# Passwords: PROTECTED\n"];
|
|
||||||
[export appendFormat:@"##\n"];
|
|
||||||
[export appendFormat:@"#\n"];
|
|
||||||
[export appendFormat:@"# Last Times Password Login\t Site\tSite\n"];
|
|
||||||
[export appendFormat:@"# used used type name\t name\tpassword\n"];
|
|
||||||
|
|
||||||
// Sites.
|
|
||||||
for (MPSiteEntity *site in activeUser.sites) {
|
|
||||||
NSDate *lastUsed = site.lastUsed;
|
|
||||||
NSUInteger uses = site.uses;
|
|
||||||
MPSiteType type = site.type;
|
|
||||||
id<MPAlgorithm> algorithm = site.algorithm;
|
|
||||||
NSUInteger counter = 0;
|
|
||||||
NSString *loginName = site.loginName;
|
|
||||||
NSString *siteName = site.name;
|
|
||||||
NSString *content = nil;
|
|
||||||
|
|
||||||
// Generated-specific
|
|
||||||
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
|
||||||
counter = ((MPGeneratedSiteEntity *)site).counter;
|
|
||||||
|
|
||||||
|
|
||||||
// Determine the content to export.
|
|
||||||
if (!(type & MPSiteFeatureDevicePrivate)) {
|
|
||||||
if (revealPasswords)
|
|
||||||
content = [site.algorithm resolvePasswordForSite:site usingKey:self.key];
|
|
||||||
else if (type & MPSiteFeatureExportContent)
|
|
||||||
content = [site.algorithm exportPasswordForSite:site usingKey:self.key];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *lastUsedExport = [[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed];
|
|
||||||
long usesExport = (long)uses;
|
|
||||||
NSString *typeExport = strf( @"%lu:%lu:%lu", (long)type, (long)[algorithm version], (long)counter );
|
|
||||||
NSString *loginNameExport = loginName?: @"";
|
|
||||||
NSString *contentExport = content?: @"";
|
|
||||||
[export appendFormat:@"%@ %8ld %8S %25S\t%25S\t%@\n",
|
|
||||||
lastUsedExport, usesExport,
|
|
||||||
(const unsigned short *)[typeExport cStringUsingEncoding:NSUTF16StringEncoding],
|
|
||||||
(const unsigned short *)[loginNameExport cStringUsingEncoding:NSUTF16StringEncoding],
|
|
||||||
(const unsigned short *)[siteName cStringUsingEncoding:NSUTF16StringEncoding],
|
|
||||||
contentExport];
|
|
||||||
}
|
|
||||||
|
|
||||||
return export;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
//
|
|
||||||
// MPConfig.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 02/01/12.
|
|
||||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "Pearl.h"
|
|
||||||
|
|
||||||
@interface MPConfig : PearlConfig
|
|
||||||
|
|
||||||
@property(nonatomic, retain) NSNumber *sendInfo;
|
|
||||||
@property(nonatomic, retain) NSNumber *rememberLogin;
|
|
||||||
@property(nonatomic, retain) NSNumber *hidePasswords;
|
|
||||||
|
|
||||||
@property(nonatomic, retain) NSNumber *checkInconsistency;
|
|
||||||
|
|
||||||
@property(nonatomic, strong) NSNumber *siteAttacker;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// MPConfig.m
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 02/01/12.
|
|
||||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAppDelegate_Shared.h"
|
|
||||||
|
|
||||||
@implementation MPConfig
|
|
||||||
|
|
||||||
@dynamic sendInfo, rememberLogin, checkInconsistency, hidePasswords, siteAttacker;
|
|
||||||
|
|
||||||
- (id)init {
|
|
||||||
|
|
||||||
if (!(self = [super init]))
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
[self.defaults registerDefaults:@{
|
|
||||||
NSStringFromSelector( @selector( askForReviews ) ) : @YES,
|
|
||||||
|
|
||||||
NSStringFromSelector( @selector( sendInfo ) ) : @NO,
|
|
||||||
NSStringFromSelector( @selector( rememberLogin ) ) : @NO,
|
|
||||||
NSStringFromSelector( @selector( hidePasswords ) ) : @NO,
|
|
||||||
NSStringFromSelector( @selector( checkInconsistency ) ) : @NO,
|
|
||||||
NSStringFromSelector( @selector( siteAttacker ) ) : @(MPAttacker1),
|
|
||||||
}];
|
|
||||||
|
|
||||||
self.delegate = [MPAppDelegate_Shared get];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPFixable.h
|
|
||||||
// MPFixable
|
|
||||||
//
|
|
||||||
// Created by lhunath on 2014-04-26.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
typedef NS_ENUM( NSUInteger, MPFixableResult ) {
|
|
||||||
MPFixableResultNoProblems,
|
|
||||||
MPFixableResultProblemsFixed,
|
|
||||||
MPFixableResultProblemsNotFixed,
|
|
||||||
};
|
|
||||||
|
|
||||||
MPFixableResult MPApplyFix(MPFixableResult previousResult, MPFixableResult(^fixBlock)(void));
|
|
||||||
|
|
||||||
@protocol MPFixable<NSObject>
|
|
||||||
|
|
||||||
- (MPFixableResult)findAndFixInconsistenciesInContext:(NSManagedObjectContext *)context;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPFixable.m
|
|
||||||
// MPFixable
|
|
||||||
//
|
|
||||||
// Created by lhunath on 2014-04-26.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPFixable.h"
|
|
||||||
|
|
||||||
MPFixableResult MPApplyFix(MPFixableResult previousResult, MPFixableResult(^fixBlock)(void)) {
|
|
||||||
|
|
||||||
MPFixableResult additionalResult = fixBlock();
|
|
||||||
switch (previousResult) {
|
|
||||||
case MPFixableResultNoProblems:
|
|
||||||
return additionalResult;
|
|
||||||
case MPFixableResultProblemsFixed:
|
|
||||||
switch (additionalResult) {
|
|
||||||
case MPFixableResultNoProblems:
|
|
||||||
case MPFixableResultProblemsFixed:
|
|
||||||
return previousResult;
|
|
||||||
case MPFixableResultProblemsNotFixed:
|
|
||||||
return additionalResult;
|
|
||||||
}
|
|
||||||
case MPFixableResultProblemsNotFixed:
|
|
||||||
return additionalResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
Throw( @"Unexpected previous=%ld or additional=%ld result.", (long)previousResult, (long)additionalResult );
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// MPGeneratedSiteEntity.h
|
|
||||||
// MasterPassword-Mac
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-09-21.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreData/CoreData.h>
|
|
||||||
#import "MPSiteEntity.h"
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPGeneratedSiteEntity : MPSiteEntity
|
|
||||||
|
|
||||||
@property (nonatomic, retain) NSNumber * counter_;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//
|
|
||||||
// MPGeneratedSiteEntity.m
|
|
||||||
// MasterPassword-Mac
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-09-21.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPGeneratedSiteEntity.h"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPGeneratedSiteEntity
|
|
||||||
|
|
||||||
@dynamic counter_;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPKey
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 16/07/12.
|
|
||||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import "MPAlgorithm.h"
|
|
||||||
|
|
||||||
@protocol MPAlgorithm;
|
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, MPKeyOrigin) {
|
|
||||||
MPKeyOriginMasterPassword,
|
|
||||||
MPKeyOriginKeyChain,
|
|
||||||
MPKeyOriginKeyChainBiometric,
|
|
||||||
};
|
|
||||||
|
|
||||||
@interface MPKey : NSObject
|
|
||||||
|
|
||||||
@property(nonatomic, readonly) NSString *fullName;
|
|
||||||
@property(nonatomic, readonly) MPKeyOrigin origin;
|
|
||||||
|
|
||||||
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
|
||||||
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
|
|
||||||
forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin;
|
|
||||||
|
|
||||||
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm;
|
|
||||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm;
|
|
||||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength;
|
|
||||||
|
|
||||||
- (BOOL)isEqualToKey:(MPKey *)key;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPKey
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 16/07/12.
|
|
||||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAlgorithm.h"
|
|
||||||
|
|
||||||
@interface MPKey()
|
|
||||||
|
|
||||||
@property(nonatomic) NSString *fullName;
|
|
||||||
@property(nonatomic) MPKeyOrigin origin;
|
|
||||||
@property(nonatomic) NSString *masterPassword;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation MPKey {
|
|
||||||
NSCache *_keyCache;
|
|
||||||
};
|
|
||||||
|
|
||||||
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
|
|
||||||
|
|
||||||
if (!(self = [super init]))
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
_keyCache = [NSCache new];
|
|
||||||
self.fullName = fullName;
|
|
||||||
self.origin = MPKeyOriginMasterPassword;
|
|
||||||
self.masterPassword = masterPassword;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
|
|
||||||
forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin {
|
|
||||||
|
|
||||||
if (!(self = [self initForFullName:fullName withMasterPassword:nil]))
|
|
||||||
return nil;
|
|
||||||
|
|
||||||
self.origin = origin;
|
|
||||||
[_keyCache setObject:keyData forKey:algorithm];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm {
|
|
||||||
|
|
||||||
return [algorithm keyIDForKeyData:[self keyDataForAlgorithm:algorithm]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm {
|
|
||||||
|
|
||||||
NSData *keyData = [_keyCache objectForKey:algorithm];
|
|
||||||
if (keyData)
|
|
||||||
return keyData;
|
|
||||||
|
|
||||||
keyData = [algorithm keyDataForFullName:self.fullName withMasterPassword:self.masterPassword];
|
|
||||||
if (keyData)
|
|
||||||
[_keyCache setObject:keyData forKey:algorithm];
|
|
||||||
|
|
||||||
return keyData;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength {
|
|
||||||
|
|
||||||
NSData *keyData = [self keyDataForAlgorithm:algorithm];
|
|
||||||
return [keyData subdataWithRange:NSMakeRange( 0, MIN( subKeyLength, keyData.length ) )];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isEqualToKey:(MPKey *)key {
|
|
||||||
|
|
||||||
return [self.fullName isEqualToString:key.fullName] && [self.masterPassword isEqualToString:self.masterPassword];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isEqual:(id)object {
|
|
||||||
|
|
||||||
if (![object isKindOfClass:[MPKey class]])
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
return [self isEqualToKey:object];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
//
|
|
||||||
// MPSiteEntity.h
|
|
||||||
// MasterPassword-Mac
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-09-21.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreData/CoreData.h>
|
|
||||||
|
|
||||||
@class MPSiteQuestionEntity, MPUserEntity;
|
|
||||||
|
|
||||||
@interface MPSiteEntity : NSManagedObject
|
|
||||||
|
|
||||||
//@property (nonatomic, retain) id content; // Hide here, reveal in MPStoredSiteEntity
|
|
||||||
@property (nonatomic, retain) NSDate * lastUsed;
|
|
||||||
@property (nonatomic, retain) NSNumber * loginGenerated_;
|
|
||||||
@property (nonatomic, retain) NSString * loginName;
|
|
||||||
@property (nonatomic, retain) NSString * name;
|
|
||||||
@property (nonatomic, retain) NSNumber * requiresExplicitMigration_;
|
|
||||||
@property (nonatomic, retain) NSNumber * type_;
|
|
||||||
@property (nonatomic, retain) NSNumber * uses_;
|
|
||||||
@property (nonatomic, retain) NSNumber * version_;
|
|
||||||
@property (nonatomic, retain) NSOrderedSet *questions;
|
|
||||||
@property (nonatomic, retain) MPUserEntity *user;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface MPSiteEntity (CoreDataGeneratedAccessors)
|
|
||||||
|
|
||||||
- (void)insertObject:(MPSiteQuestionEntity *)value inQuestionsAtIndex:(NSUInteger)idx;
|
|
||||||
- (void)removeObjectFromQuestionsAtIndex:(NSUInteger)idx;
|
|
||||||
- (void)insertQuestions:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
|
|
||||||
- (void)removeQuestionsAtIndexes:(NSIndexSet *)indexes;
|
|
||||||
- (void)replaceObjectInQuestionsAtIndex:(NSUInteger)idx withObject:(MPSiteQuestionEntity *)value;
|
|
||||||
- (void)replaceQuestionsAtIndexes:(NSIndexSet *)indexes withQuestions:(NSArray *)values;
|
|
||||||
- (void)addQuestionsObject:(MPSiteQuestionEntity *)value;
|
|
||||||
- (void)removeQuestionsObject:(MPSiteQuestionEntity *)value;
|
|
||||||
- (void)addQuestions:(NSOrderedSet *)values;
|
|
||||||
- (void)removeQuestions:(NSOrderedSet *)values;
|
|
||||||
@end
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
//
|
|
||||||
// MPSiteEntity.m
|
|
||||||
// MasterPassword-Mac
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-09-21.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPSiteEntity.h"
|
|
||||||
#import "MPSiteQuestionEntity.h"
|
|
||||||
#import "MPUserEntity.h"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPSiteEntity
|
|
||||||
|
|
||||||
//@dynamic content;
|
|
||||||
@dynamic lastUsed;
|
|
||||||
@dynamic loginGenerated_;
|
|
||||||
@dynamic loginName;
|
|
||||||
@dynamic name;
|
|
||||||
@dynamic requiresExplicitMigration_;
|
|
||||||
@dynamic type_;
|
|
||||||
@dynamic uses_;
|
|
||||||
@dynamic version_;
|
|
||||||
@dynamic questions;
|
|
||||||
@dynamic user;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// MPSiteQuestionEntity.h
|
|
||||||
// MasterPassword-iOS
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-09-27.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreData/CoreData.h>
|
|
||||||
|
|
||||||
@class MPSiteEntity;
|
|
||||||
|
|
||||||
@interface MPSiteQuestionEntity : NSManagedObject
|
|
||||||
|
|
||||||
@property (nonatomic, retain) NSString * keyword;
|
|
||||||
@property (nonatomic, retain) MPSiteEntity *site;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// MPSiteQuestionEntity.m
|
|
||||||
// MasterPassword-iOS
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-09-27.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPSiteQuestionEntity.h"
|
|
||||||
#import "MPSiteEntity.h"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPSiteQuestionEntity
|
|
||||||
|
|
||||||
@dynamic keyword;
|
|
||||||
@dynamic site;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// MPStoredSiteEntity.h
|
|
||||||
// MasterPassword-Mac
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-09-21.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreData/CoreData.h>
|
|
||||||
#import "MPSiteEntity.h"
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPStoredSiteEntity : MPSiteEntity
|
|
||||||
|
|
||||||
@property (nonatomic, retain) NSData *contentObject;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//
|
|
||||||
// MPStoredSiteEntity.m
|
|
||||||
// MasterPassword-Mac
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-09-21.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPStoredSiteEntity.h"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPStoredSiteEntity
|
|
||||||
|
|
||||||
@dynamic contentObject;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
//
|
|
||||||
// MPTypes.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 02/01/12.
|
|
||||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
__BEGIN_DECLS
|
|
||||||
extern NSString *const MPErrorDomain;
|
|
||||||
|
|
||||||
extern NSString *const MPSignedInNotification;
|
|
||||||
extern NSString *const MPSignedOutNotification;
|
|
||||||
extern NSString *const MPKeyForgottenNotification;
|
|
||||||
extern NSString *const MPSiteUpdatedNotification;
|
|
||||||
extern NSString *const MPCheckConfigNotification;
|
|
||||||
extern NSString *const MPSitesImportedNotification;
|
|
||||||
extern NSString *const MPFoundInconsistenciesNotification;
|
|
||||||
|
|
||||||
extern NSString *const MPSitesImportedNotificationUserKey;
|
|
||||||
extern NSString *const MPInconsistenciesFixResultUserKey;
|
|
||||||
__END_DECLS
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
//
|
|
||||||
// MPTypes.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 02/01/12.
|
|
||||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPTypes.h"
|
|
||||||
|
|
||||||
NSString *const MPErrorDomain = @"MPErrorDomain";
|
|
||||||
|
|
||||||
NSString *const MPSignedInNotification = @"MPSignedInNotification";
|
|
||||||
NSString *const MPSignedOutNotification = @"MPSignedOutNotification";
|
|
||||||
NSString *const MPKeyForgottenNotification = @"MPKeyForgottenNotification";
|
|
||||||
NSString *const MPSiteUpdatedNotification = @"MPSiteUpdatedNotification";
|
|
||||||
NSString *const MPCheckConfigNotification = @"MPCheckConfigNotification";
|
|
||||||
NSString *const MPSitesImportedNotification = @"MPSitesImportedNotification";
|
|
||||||
NSString *const MPFoundInconsistenciesNotification = @"MPFoundInconsistenciesNotification";
|
|
||||||
|
|
||||||
NSString *const MPSitesImportedNotificationUserKey = @"MPSitesImportedNotificationUserKey";
|
|
||||||
NSString *const MPInconsistenciesFixResultUserKey = @"MPInconsistenciesFixResultUserKey";
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
//
|
|
||||||
// MPUserEntity.h
|
|
||||||
// MasterPassword-Mac
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-09-21.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreData/CoreData.h>
|
|
||||||
|
|
||||||
@class MPSiteEntity;
|
|
||||||
|
|
||||||
@interface MPUserEntity : NSManagedObject
|
|
||||||
|
|
||||||
@property (nonatomic, retain) NSNumber * avatar_;
|
|
||||||
@property (nonatomic, retain) NSNumber * defaultType_;
|
|
||||||
@property (nonatomic, retain) NSData * keyID;
|
|
||||||
@property (nonatomic, retain) NSDate * lastUsed;
|
|
||||||
@property (nonatomic, retain) NSString * name;
|
|
||||||
@property (nonatomic, retain) NSNumber * saveKey_;
|
|
||||||
@property (nonatomic, retain) NSNumber * touchID_;
|
|
||||||
@property (nonatomic, retain) NSNumber * version_;
|
|
||||||
@property (nonatomic, retain) NSSet *sites;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface MPUserEntity (CoreDataGeneratedAccessors)
|
|
||||||
|
|
||||||
- (void)addSitesObject:(MPSiteEntity *)value;
|
|
||||||
- (void)removeSitesObject:(MPSiteEntity *)value;
|
|
||||||
- (void)addSites:(NSSet *)values;
|
|
||||||
- (void)removeSites:(NSSet *)values;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// MPUserEntity.m
|
|
||||||
// MasterPassword-Mac
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-09-21.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPUserEntity.h"
|
|
||||||
#import "MPSiteEntity.h"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPUserEntity
|
|
||||||
|
|
||||||
@dynamic avatar_;
|
|
||||||
@dynamic defaultType_;
|
|
||||||
@dynamic keyID;
|
|
||||||
@dynamic lastUsed;
|
|
||||||
@dynamic name;
|
|
||||||
@dynamic saveKey_;
|
|
||||||
@dynamic touchID_;
|
|
||||||
@dynamic version_;
|
|
||||||
@dynamic sites;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Maarten Billemont on 2016-05-15.
|
|
||||||
// Copyright (c) 2016 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
IB_DESIGNABLE
|
|
||||||
@interface MPGradientView : NSView
|
|
||||||
|
|
||||||
@property(nonatomic, retain) IBInspectable NSColor *startingColor;
|
|
||||||
@property(nonatomic, retain) IBInspectable NSColor *endingColor;
|
|
||||||
@property(nonatomic, assign) IBInspectable NSInteger angle;
|
|
||||||
@property(nonatomic, assign) IBInspectable CGFloat ratio;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPInitialWindowController.h
|
|
||||||
// MPInitialWindowController
|
|
||||||
//
|
|
||||||
// Created by lhunath on 2014-06-29.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <AppKit/AppKit.h>
|
|
||||||
|
|
||||||
@interface MPInitialWindowController : NSWindowController
|
|
||||||
|
|
||||||
@property(nonatomic, weak) IBOutlet NSButton *openAtLoginButton;
|
|
||||||
|
|
||||||
- (IBAction)iphoneAppStore:(id)sender;
|
|
||||||
- (IBAction)androidPlayStore:(id)sender;
|
|
||||||
- (IBAction)togglePreference:(id)sender;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPInitialWindowController.h
|
|
||||||
// MPInitialWindowController
|
|
||||||
//
|
|
||||||
// Created by lhunath on 2014-06-29.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPInitialWindowController.h"
|
|
||||||
#import "MPMacAppDelegate.h"
|
|
||||||
#import "MPAppDelegate_Store.h"
|
|
||||||
|
|
||||||
@implementation MPInitialWindowController
|
|
||||||
|
|
||||||
#pragma mark - Life
|
|
||||||
|
|
||||||
- (void)windowDidLoad {
|
|
||||||
|
|
||||||
[super windowDidLoad];
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window
|
|
||||||
queue:nil usingBlock:^(NSNotification *note) {
|
|
||||||
[MPMacAppDelegate get].initialWindowController = nil;
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Actions
|
|
||||||
|
|
||||||
- (IBAction)iphoneAppStore:(id)sender {
|
|
||||||
|
|
||||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://itunes.apple.com/app/id510296984"]];
|
|
||||||
[self close];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)androidPlayStore:(id)sender {
|
|
||||||
|
|
||||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://masterpasswordapp.com"]];
|
|
||||||
[self close];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)togglePreference:(id)sender {
|
|
||||||
|
|
||||||
if (sender == self.openAtLoginButton)
|
|
||||||
[[MPMacAppDelegate get] setLoginItemEnabled:self.openAtLoginButton.state == NSOnState];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Maarten Billemont on 2016-04-30.
|
|
||||||
// Copyright (c) 2016 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
@interface MPMacApplication : NSApplication
|
|
||||||
@end
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//
|
|
||||||
// MPMacConfig.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 02/01/12.
|
|
||||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPConfig.h"
|
|
||||||
|
|
||||||
@interface MPMacConfig : MPConfig
|
|
||||||
|
|
||||||
@property(nonatomic, retain) NSString *usedUserName;
|
|
||||||
@property(nonatomic, retain) NSNumber *fullScreen;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//
|
|
||||||
// MPMacConfig.m
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 02/01/12.
|
|
||||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
@implementation MPMacConfig
|
|
||||||
|
|
||||||
@dynamic usedUserName;
|
|
||||||
@dynamic fullScreen;
|
|
||||||
|
|
||||||
- (id)init {
|
|
||||||
|
|
||||||
if (!(self = [super init]))
|
|
||||||
return self;
|
|
||||||
|
|
||||||
[self.defaults registerDefaults:@{
|
|
||||||
NSStringFromSelector( @selector( appleID ) ) : @"510296984",
|
|
||||||
NSStringFromSelector( @selector( fullScreen ) ) : @YES,
|
|
||||||
}];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPNoStateButton.h
|
|
||||||
// MPNoStateButton
|
|
||||||
//
|
|
||||||
// Created by lhunath on 14-10-27.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPNoStateButtonCell : NSButtonCell
|
|
||||||
@end
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPNoStateButton.h
|
|
||||||
// MPNoStateButton
|
|
||||||
//
|
|
||||||
// Created by lhunath on 14-10-27.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPNoStateButton.h"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation MPNoStateButtonCell {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setState:(NSInteger)state {
|
|
||||||
|
|
||||||
[super setState:NSOnState];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPPasswordWindow.h
|
|
||||||
// MPPasswordWindow
|
|
||||||
//
|
|
||||||
// Created by lhunath on 2014-06-19.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
|
|
||||||
@interface MPPasswordWindow : NSWindow
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPPasswordWindow.h
|
|
||||||
// MPPasswordWindow
|
|
||||||
//
|
|
||||||
// Created by lhunath on 2014-06-19.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPPasswordWindow.h"
|
|
||||||
|
|
||||||
@implementation MPPasswordWindow
|
|
||||||
|
|
||||||
#pragma mark - Life
|
|
||||||
|
|
||||||
- (BOOL)canBecomeKeyWindow {
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - State
|
|
||||||
|
|
||||||
- (void)update {
|
|
||||||
|
|
||||||
if ([[MPMacConfig get].fullScreen boolValue]) {
|
|
||||||
[self setLevel:NSScreenSaverWindowLevel];
|
|
||||||
[self setFrame:self.screen.frame display:YES];
|
|
||||||
}
|
|
||||||
else if (self.level != NSNormalWindowLevel) {
|
|
||||||
[self setLevel:NSNormalWindowLevel];
|
|
||||||
[self setFrame:NSMakeRect( 0, 0, 640, 600) display:NO];
|
|
||||||
[self center];
|
|
||||||
}
|
|
||||||
|
|
||||||
[super update];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Private
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPSiteModel.h
|
|
||||||
// MPSiteModel
|
|
||||||
//
|
|
||||||
// Created by lhunath on 2/11/2014.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import "MPSiteEntity.h"
|
|
||||||
#import "MPAlgorithm.h"
|
|
||||||
#import "MPUserEntity.h"
|
|
||||||
|
|
||||||
@class MPSiteEntity;
|
|
||||||
|
|
||||||
@interface MPSiteModel : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic) NSString *name;
|
|
||||||
@property (nonatomic) NSAttributedString *displayedName;
|
|
||||||
@property (nonatomic) MPSiteType type;
|
|
||||||
@property (nonatomic) NSString *typeName;
|
|
||||||
@property (nonatomic) NSString *content;
|
|
||||||
@property (nonatomic) NSString *displayedContent;
|
|
||||||
@property (nonatomic) NSString *question;
|
|
||||||
@property (nonatomic) NSString *answer;
|
|
||||||
@property (nonatomic) NSString *loginName;
|
|
||||||
@property (nonatomic) BOOL loginGenerated;
|
|
||||||
@property (nonatomic) NSNumber *uses;
|
|
||||||
@property (nonatomic) NSUInteger counter;
|
|
||||||
@property (nonatomic) NSDate *lastUsed;
|
|
||||||
@property (nonatomic) id<MPAlgorithm> algorithm;
|
|
||||||
@property (nonatomic) MPAlgorithmVersion algorithmVersion;
|
|
||||||
@property (nonatomic, readonly) BOOL outdated;
|
|
||||||
@property (nonatomic, readonly) BOOL generated;
|
|
||||||
@property (nonatomic, readonly) BOOL stored;
|
|
||||||
@property (nonatomic, readonly) BOOL transient;
|
|
||||||
|
|
||||||
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups;
|
|
||||||
- (instancetype)initWithName:(NSString *)siteName forUser:(MPUserEntity *)user;
|
|
||||||
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc;
|
|
||||||
|
|
||||||
- (void)updateContent;
|
|
||||||
@end
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPSitesTableView.h
|
|
||||||
// MPSitesTableView
|
|
||||||
//
|
|
||||||
// Created by lhunath on 2014-06-30.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <AppKit/AppKit.h>
|
|
||||||
|
|
||||||
@class MPPasswordWindowController;
|
|
||||||
|
|
||||||
@interface MPSitesTableView : NSTableView
|
|
||||||
|
|
||||||
@property(nonatomic, weak) MPPasswordWindowController *controller;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPSitesTableView.h
|
|
||||||
// MPSitesTableView
|
|
||||||
//
|
|
||||||
// Created by lhunath on 2014-06-30.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPSitesTableView.h"
|
|
||||||
#import "MPPasswordWindowController.h"
|
|
||||||
|
|
||||||
@implementation MPSitesTableView
|
|
||||||
|
|
||||||
- (void)doCommandBySelector:(SEL)aSelector {
|
|
||||||
|
|
||||||
[self.controller handleCommand:aSelector];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)keyDown:(NSEvent *)theEvent {
|
|
||||||
|
|
||||||
[self interpretKeyEvents:@[ theEvent ]];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
<?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>ATSApplicationFontsPath</key>
|
|
||||||
<string>.</string>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>Master Password</string>
|
|
||||||
<key>CFBundleDocumentTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>mpsites</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeIconFile</key>
|
|
||||||
<string></string>
|
|
||||||
<key>CFBundleTypeIconFiles</key>
|
|
||||||
<array/>
|
|
||||||
<key>CFBundleTypeMIMETypes</key>
|
|
||||||
<array>
|
|
||||||
<string>text/plain</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Master Password sites</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Viewer</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Owner</string>
|
|
||||||
<key>LSItemContentTypes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.lyndir.masterpassword.sites</string>
|
|
||||||
</array>
|
|
||||||
<key>LSTypeIsPackage</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>Master Password</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>[auto]</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>[auto]</string>
|
|
||||||
<key>Fabric</key>
|
|
||||||
<dict>
|
|
||||||
<key>APIKey</key>
|
|
||||||
<string>0d10c90776f5ef5acd01ddbeaca9a6cba4814560</string>
|
|
||||||
<key>Kits</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>KitInfo</key>
|
|
||||||
<dict/>
|
|
||||||
<key>KitName</key>
|
|
||||||
<string>Crashlytics</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<key>LSApplicationCategoryType</key>
|
|
||||||
<string>public.app-category.productivity</string>
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
|
||||||
<key>LSUIElement</key>
|
|
||||||
<true/>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
|
||||||
<string>Copyright © 2011-2014 Lyndir</string>
|
|
||||||
<key>NSMainNibFile</key>
|
|
||||||
<string>MainMenu</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>MPMacApplication</string>
|
|
||||||
<key>UTExportedTypeDeclarations</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>UTTypeConformsTo</key>
|
|
||||||
<array>
|
|
||||||
<string>public.utf8-plain-text</string>
|
|
||||||
</array>
|
|
||||||
<key>UTTypeDescription</key>
|
|
||||||
<string>Master Password sites</string>
|
|
||||||
<key>UTTypeIconFile</key>
|
|
||||||
<string></string>
|
|
||||||
<key>UTTypeIdentifier</key>
|
|
||||||
<string>com.lyndir.masterpassword.sites</string>
|
|
||||||
<key>UTTypeSize320IconFile</key>
|
|
||||||
<string></string>
|
|
||||||
<key>UTTypeSize64IconFile</key>
|
|
||||||
<string></string>
|
|
||||||
<key>UTTypeTagSpecification</key>
|
|
||||||
<dict>
|
|
||||||
<key>public.filename-extension</key>
|
|
||||||
<array>
|
|
||||||
<string>mpsites</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<?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>en</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string></string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>${PRODUCT_NAME}</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1</string>
|
|
||||||
<key>LSBackgroundOnly</key>
|
|
||||||
<true/>
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
|
||||||
<string>Copyright © 2013 Maarten Billemont. All rights reserved.</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>NSApplication</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
//
|
|
||||||
// main.m
|
|
||||||
// MasterPassword-Mac-LoginHelper
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2013-06-07.
|
|
||||||
// Copyright (c) 2013 Maarten Billemont. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
|
|
||||||
NSURL *bundleURL = [[[[[[NSBundle mainBundle] bundleURL]
|
|
||||||
URLByDeletingLastPathComponent] URLByDeletingLastPathComponent]
|
|
||||||
URLByDeletingLastPathComponent] URLByDeletingLastPathComponent];
|
|
||||||
|
|
||||||
NSError *error = nil;
|
|
||||||
NSRunningApplication *application = [[NSWorkspace sharedWorkspace]
|
|
||||||
launchApplicationAtURL:bundleURL options:NSWorkspaceLaunchWithoutActivation
|
|
||||||
configuration:@{} error:&error];
|
|
||||||
|
|
||||||
if (!application || error) {
|
|
||||||
NSLog( @"Error launching main app: %@", [error debugDescription] );
|
|
||||||
return (int)error.code?: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
//
|
|
||||||
// Prefix header for all source files of the 'MasterPassword' target in the 'MasterPassword' project
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Availability.h>
|
|
||||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
|
|
||||||
#warning "This project uses features only available in Mac OS X 10.8 and later."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#import "Pearl-Prefix.pch"
|
|
||||||
|
|
||||||
#ifdef __OBJC__
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreData/CoreData.h>
|
|
||||||
|
|
||||||
#ifdef CRASHLYTICS
|
|
||||||
#import <Crashlytics/Crashlytics.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#import "MPTypes.h"
|
|
||||||
#import "MPMacConfig.h"
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#import <libgen.h>
|
|
||||||
#import <CoreFoundation/CFString.h>
|
|
||||||
#import <objc/runtime.h>
|
|
||||||
#import <objc/message.h>
|
|
||||||
#import <objc/NSObjCRuntime.h>
|
|
||||||
#import <stdlib.h>
|
|
||||||
|
|
||||||
#define trc(format, ...) \
|
|
||||||
do { \
|
|
||||||
char *_msg = NULL; \
|
|
||||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
|
||||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
|
||||||
_sendMsg( objc_msgSend( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
|
||||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), \
|
|
||||||
CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ), __LINE__, \
|
|
||||||
CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ), 0, \
|
|
||||||
CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ) ); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define dbg(format, ...) \
|
|
||||||
do { \
|
|
||||||
char *_msg = NULL; \
|
|
||||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
|
||||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
|
||||||
_sendMsg( objc_msgSend( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
|
||||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), \
|
|
||||||
CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ), __LINE__, \
|
|
||||||
CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ), 1, \
|
|
||||||
CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ) ); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define inf(format, ...) \
|
|
||||||
do { \
|
|
||||||
char *_msg = NULL; \
|
|
||||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
|
||||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
|
||||||
_sendMsg( objc_msgSend( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
|
||||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), \
|
|
||||||
CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ), __LINE__, \
|
|
||||||
CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ), 2, \
|
|
||||||
CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ) ); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define wrn(format, ...) \
|
|
||||||
do { \
|
|
||||||
char *_msg = NULL; \
|
|
||||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
|
||||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
|
||||||
_sendMsg( objc_msgSend( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
|
||||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), \
|
|
||||||
CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ), __LINE__, \
|
|
||||||
CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ), 3, \
|
|
||||||
CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ) ); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define err(format, ...) \
|
|
||||||
do { \
|
|
||||||
char *_msg = NULL; \
|
|
||||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
|
||||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
|
||||||
_sendMsg( objc_msgSend( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
|
||||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), \
|
|
||||||
CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ), __LINE__, \
|
|
||||||
CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ), 4, \
|
|
||||||
CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ) ); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define ftl(format, ...) \
|
|
||||||
do { \
|
|
||||||
char *_msg = NULL; \
|
|
||||||
asprintf( &_msg, format, ##__VA_ARGS__ ); \
|
|
||||||
void (*_sendMsg)(id, SEL, CFStringRef, NSInteger, CFStringRef, NSUInteger, CFStringRef) = (void *)objc_msgSend; \
|
|
||||||
_sendMsg( objc_msgSend( (id)objc_getClass( "PearlLogger" ), sel_getUid( "get" ) ), \
|
|
||||||
sel_getUid( "inFile:atLine:fromFunction:withLevel:text:" ), \
|
|
||||||
CFStringCreateWithCString( NULL, basename( (char *)__FILE__ ), kCFStringEncodingUTF8 ), __LINE__, \
|
|
||||||
CFStringCreateWithCString( NULL, __FUNCTION__, kCFStringEncodingUTF8 ), 5, \
|
|
||||||
CFStringCreateWithCString( NULL, _msg, kCFStringEncodingUTF8 ) ); \
|
|
||||||
abort(); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
//
|
|
||||||
// main.m
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 04/03/12.
|
|
||||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
|
|
||||||
return NSApplicationMain( argc, (const char **)argv );
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
//
|
|
||||||
// AttributedMarkdown.h
|
|
||||||
// AttributedMarkdown
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-09-28.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
@interface AttributedMarkdown : NSObject
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
//
|
|
||||||
// AttributedMarkdown.m
|
|
||||||
// AttributedMarkdown
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-09-28.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "AttributedMarkdown.h"
|
|
||||||
|
|
||||||
@implementation AttributedMarkdown
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<?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>en</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>com.lyndir.$(PRODUCT_NAME:rfc1034identifier)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>$(PRODUCT_NAME)</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>BNDL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
//
|
|
||||||
// MPPreferencesViewController.h
|
|
||||||
// MasterPassword-iOS
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 04/06/12.
|
|
||||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import "MPTypeViewController.h"
|
|
||||||
#import "MPSiteQuestionEntity.h"
|
|
||||||
|
|
||||||
@interface MPAnswersViewController : UIViewController
|
|
||||||
|
|
||||||
@property (nonatomic) IBOutlet UITableView *tableView;
|
|
||||||
|
|
||||||
- (void)setSite:(MPSiteEntity *)site;
|
|
||||||
- (MPSiteEntity *)siteInContext:(NSManagedObjectContext *)context;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface MPGlobalAnswersCell : UITableViewCell
|
|
||||||
|
|
||||||
@property (nonatomic) IBOutlet UILabel *titleLabel;
|
|
||||||
@property (nonatomic) IBOutlet UITextField *answerField;
|
|
||||||
|
|
||||||
- (void)setSite:(MPSiteEntity *)site;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface MPSendAnswersCell : UITableViewCell
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface MPMultipleAnswersCell : UITableViewCell <UITextFieldDelegate>
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface MPAnswersQuestionCell : UITableViewCell
|
|
||||||
|
|
||||||
@property(nonatomic) IBOutlet UITextField *questionField;
|
|
||||||
@property(nonatomic) IBOutlet UITextField *answerField;
|
|
||||||
|
|
||||||
- (void)setQuestion:(MPSiteQuestionEntity *)question forSite:(MPSiteEntity *)site inVC:(MPAnswersViewController *)VC;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPAppSettingsViewController.h
|
|
||||||
// MPAppSettingsViewController
|
|
||||||
//
|
|
||||||
// Created by lhunath on 2014-04-18.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import "IASKAppSettingsViewController.h"
|
|
||||||
|
|
||||||
@interface MPAppSettingsViewController : IASKAppSettingsViewController
|
|
||||||
@end
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
|
||||||
*
|
|
||||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
|
||||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*
|
|
||||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
//
|
|
||||||
// MPAppSettingsViewController.h
|
|
||||||
// MPAppSettingsViewController
|
|
||||||
//
|
|
||||||
// Created by lhunath on 2014-04-18.
|
|
||||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MPAppSettingsViewController.h"
|
|
||||||
#import "UIColor+Expanded.h"
|
|
||||||
|
|
||||||
@implementation MPAppSettingsViewController {
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated {
|
|
||||||
|
|
||||||
[super viewWillAppear:animated];
|
|
||||||
|
|
||||||
self.tableView.contentInset = UIEdgeInsetsMake( 64, 0, 49, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
||||||
|
|
||||||
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
|
|
||||||
cell.backgroundColor = [UIColor clearColor];
|
|
||||||
cell.textLabel.textColor = [UIColor whiteColor];
|
|
||||||
|
|
||||||
if (cell.selectionStyle != UITableViewCellSelectionStyleNone) {
|
|
||||||
cell.selectedBackgroundView = [[UIView alloc] initWithFrame:cell.bounds];
|
|
||||||
cell.selectedBackgroundView.backgroundColor = [UIColor colorWithRGBAHex:0x78DDFB33];
|
|
||||||
}
|
|
||||||
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user