Compare commits
406 Commits
2.5-mac-2
...
2.7-java-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77c4a2af46 | ||
|
|
3da82d30b1 | ||
|
|
97532fdce6 | ||
|
|
fe63a2756a | ||
|
|
928b617ed0 | ||
|
|
18ecc41b39 | ||
|
|
a6e9e89ace | ||
|
|
0b7494ecbf | ||
|
|
8377c9c615 | ||
|
|
37a7cfa530 | ||
|
|
978b758079 | ||
|
|
38f09021b3 | ||
|
|
7455fba55e | ||
|
|
8cd9755616 | ||
|
|
46d301df94 | ||
|
|
e639137304 | ||
|
|
7c83a62f91 | ||
|
|
513840e2c4 | ||
|
|
8f7faa9e4e | ||
|
|
16cdcda94b | ||
|
|
400ebe59db | ||
|
|
476a4046e7 | ||
|
|
3403449ca2 | ||
|
|
596ace51ea | ||
|
|
80b5fcd785 | ||
|
|
a16bc9a318 | ||
|
|
462dd4e89b | ||
|
|
e5ff374a9c | ||
|
|
954c4f8d63 | ||
|
|
529f1feace | ||
|
|
5cdff6f155 | ||
|
|
81358c16f9 | ||
|
|
4a555748cd | ||
|
|
698566a914 | ||
|
|
64a69856ac | ||
|
|
f76de9520c | ||
|
|
4b3662bbe9 | ||
|
|
b25130f4d2 | ||
|
|
d6617563fc | ||
|
|
8d24ec3250 | ||
|
|
bbcc250a5c | ||
|
|
4abb50ad9b | ||
|
|
30dac64d5d | ||
|
|
e4e2aaad95 | ||
|
|
835acf45eb | ||
|
|
c3f6796833 | ||
|
|
86f4e8ec06 | ||
|
|
0dddcef28e | ||
|
|
cc583c789d | ||
|
|
42d78da74e | ||
|
|
b5040a7786 | ||
|
|
b1698ee339 | ||
|
|
8ffc0ae350 | ||
|
|
8276d2f4e5 | ||
|
|
11cf86bc73 | ||
|
|
5084511404 | ||
|
|
fffec56d4e | ||
|
|
9a0828c1eb | ||
|
|
db967a1a16 | ||
|
|
f83896d89d | ||
|
|
683c0165e6 | ||
|
|
3853b6f180 | ||
|
|
bff3577ada | ||
|
|
4c48bfb1af | ||
|
|
a8263b276c | ||
|
|
11e32abb90 | ||
|
|
b6e6dce9f0 | ||
|
|
ada1c7f6ae | ||
|
|
1cbb584011 | ||
|
|
f9289a3e9e | ||
|
|
9a37253461 | ||
|
|
5a4456bf46 | ||
|
|
1d06dd65ed | ||
|
|
ed6c32811c | ||
|
|
9a564ff35e | ||
|
|
4909479b0f | ||
|
|
434a7cc280 | ||
|
|
50a48ae092 | ||
|
|
c3017069b1 | ||
|
|
c7425be681 | ||
|
|
249a1975cd | ||
|
|
190a241a25 | ||
|
|
aef8422102 | ||
|
|
c2aafd8602 | ||
|
|
8e41cba7ac | ||
|
|
9d29775b14 | ||
|
|
55bd9382bc | ||
|
|
687923da32 | ||
|
|
66e893fd83 | ||
|
|
cc5c45e3aa | ||
|
|
d472d975ce | ||
|
|
7a97a0b0c8 | ||
|
|
02aed778bc | ||
|
|
b748e607ad | ||
|
|
c801ff546a | ||
|
|
17185391ce | ||
|
|
4579095afc | ||
|
|
788d85178d | ||
|
|
af4d7c4bc9 | ||
|
|
a4bbfdf850 | ||
|
|
cef3d470bd | ||
|
|
0d37c45dbe | ||
|
|
e568b5a9da | ||
|
|
e7ac8661f9 | ||
|
|
882de547d0 | ||
|
|
6957d46ef9 | ||
|
|
3a9a518cb1 | ||
|
|
0900aff93a | ||
|
|
3974b70a83 | ||
|
|
498b7caecb | ||
|
|
0b26260124 | ||
|
|
bc0ffbd552 | ||
|
|
c08d3a0e8b | ||
|
|
5501f1f97d | ||
|
|
073ef4f439 | ||
|
|
a7f82d3148 | ||
|
|
831b475b28 | ||
|
|
728a4486d3 | ||
|
|
5035c52846 | ||
|
|
a0447298d3 | ||
|
|
0b044ab9a4 | ||
|
|
3b24e1d1b8 | ||
|
|
cc82e52c33 | ||
|
|
faf59875bf | ||
|
|
e12e14ef03 | ||
|
|
f41f07f0ae | ||
|
|
1cfc199541 | ||
|
|
c43cc73ad5 | ||
|
|
1bd61759bf | ||
|
|
cbf277c493 | ||
|
|
87b7afd587 | ||
|
|
2db0bb35d5 | ||
|
|
c51262ccc2 | ||
|
|
1b703515dd | ||
|
|
0aa7baf59e | ||
|
|
8bdf1755b7 | ||
|
|
bda1ac3bd4 | ||
|
|
8d7c351912 | ||
|
|
38a357cb28 | ||
|
|
f0d523fb35 | ||
|
|
1cb720da32 | ||
|
|
1031414ba2 | ||
|
|
cb74b1f3fc | ||
|
|
82e2d0b5ac | ||
|
|
33f2e0edda | ||
|
|
9c8566b537 | ||
|
|
10698284d2 | ||
|
|
11185725d1 | ||
|
|
71f1b3c130 | ||
|
|
dc19806e02 | ||
|
|
94ac8b1460 | ||
|
|
40a807c6af | ||
|
|
c115e9149c | ||
|
|
7123e97ef9 | ||
|
|
37fb672133 | ||
|
|
2771125eb5 | ||
|
|
a16cb19311 | ||
|
|
8f8920b91f | ||
|
|
1d913b7f78 | ||
|
|
5fe9106f6d | ||
|
|
8d9a3e0ab0 | ||
|
|
344771dbdf | ||
|
|
d38dba7272 | ||
|
|
409f005eec | ||
|
|
df7903e146 | ||
|
|
49edaef79d | ||
|
|
fcbed9ef01 | ||
|
|
3edb414d23 | ||
|
|
d779c21cc1 | ||
|
|
8d32bc56ae | ||
|
|
9d03ed06c3 | ||
|
|
ee290e5c14 | ||
|
|
789761b177 | ||
|
|
cd0876d58a | ||
|
|
bfae4da56c | ||
|
|
f342ed5940 | ||
|
|
c7373fee28 | ||
|
|
44b2955652 | ||
|
|
8a4af69008 | ||
|
|
6650382e19 | ||
|
|
a1f5e0ba1c | ||
|
|
035bb6b285 | ||
|
|
c0107fb90e | ||
|
|
138be9d14c | ||
|
|
61f474217b | ||
|
|
d31c5eed0a | ||
|
|
5060de689b | ||
|
|
b95424ddf3 | ||
|
|
e40a442a30 | ||
|
|
b5134a9faf | ||
|
|
a791d449ce | ||
|
|
43e1a9d539 | ||
|
|
e91f80d10e | ||
|
|
9db855c7fb | ||
|
|
2dc3636b26 | ||
|
|
4d9df012f6 | ||
|
|
ff9d0d75ef | ||
|
|
4e160b3b33 | ||
|
|
5048acc9f9 | ||
|
|
1841541bc4 | ||
|
|
11d9af3844 | ||
|
|
e30b618241 | ||
|
|
966327571d | ||
|
|
303d50c197 | ||
|
|
bcdfdec211 | ||
|
|
fb769d2ac5 | ||
|
|
f8043ae16d | ||
|
|
7150f2f5c5 | ||
|
|
81bd2e3065 | ||
|
|
78c9618807 | ||
|
|
bed8939b8a | ||
|
|
9443d93500 | ||
|
|
877eba66be | ||
|
|
3af8aba40c | ||
|
|
7ece02c73d | ||
|
|
ebbd2b3ac4 | ||
|
|
a85eff4277 | ||
|
|
98f1c776be | ||
|
|
6b554c67ed | ||
|
|
f2ae35080d | ||
|
|
0ff6c93a95 | ||
|
|
9147600b97 | ||
|
|
fafe56166e | ||
|
|
0a024b2594 | ||
|
|
b4c2a393f1 | ||
|
|
39dcef46d2 | ||
|
|
d6a88583f5 | ||
|
|
1c17b84dcf | ||
|
|
cecaf1b5cc | ||
|
|
888338e107 | ||
|
|
32055abf29 | ||
|
|
0f72dffaf1 | ||
|
|
5d1be43b65 | ||
|
|
dc7089c38c | ||
|
|
34540f0844 | ||
|
|
e818713484 | ||
|
|
6e2289994c | ||
|
|
05a9ba46d0 | ||
|
|
70bb30ba0c | ||
|
|
444d7e9b35 | ||
|
|
47164c7a92 | ||
|
|
ad00ceb4ce | ||
|
|
473e3ca11f | ||
|
|
35c0431cec | ||
|
|
70c784db83 | ||
|
|
d448099a2d | ||
|
|
e3a7ea57e0 | ||
|
|
fa6133200e | ||
|
|
dfa67bdca9 | ||
|
|
8c9c4ef7b2 | ||
|
|
1adb18a7e7 | ||
|
|
f50fdb7777 | ||
|
|
33bf2c93d0 | ||
|
|
f2abcc9e43 | ||
|
|
5ef69aa045 | ||
|
|
1c0f274868 | ||
|
|
1f592f50a9 | ||
|
|
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 | ||
|
|
4f708809e5 | ||
|
|
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 |
28
.dockerignore
Normal file
28
.dockerignore
Normal file
@@ -0,0 +1,28 @@
|
||||
# OS-Specific junk.
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# Xcode IDE
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
|
||||
# Generated
|
||||
/platform-darwin/Resources/Media/Images.xcassets/
|
||||
/platform-darwin/Podfile.lock
|
||||
/platform-darwin/Pods/
|
||||
|
||||
# Gradle
|
||||
build
|
||||
.gradle
|
||||
local.properties
|
||||
/gradle/builds
|
||||
/platform-android/.externalNativeBuild
|
||||
|
||||
# Git
|
||||
.git
|
||||
30
.gitignore
vendored
30
.gitignore
vendored
@@ -13,35 +13,13 @@ xcuserdata/
|
||||
DerivedData/
|
||||
|
||||
# Generated
|
||||
/platform-independent/cli-c/VERSION
|
||||
/platform-independent/cli-c/mpw-*.tar.gz
|
||||
/platform-darwin/Resources/Media/Images.xcassets/
|
||||
|
||||
# Media
|
||||
public/Press/Background.png
|
||||
public/Press/Front-Page.png
|
||||
public/Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
|
||||
/platform-darwin/Podfile.lock
|
||||
/platform-darwin/Pods/
|
||||
|
||||
# Gradle
|
||||
build
|
||||
!/build
|
||||
.gradle
|
||||
local.properties
|
||||
|
||||
# Maven
|
||||
target
|
||||
dependency-reduced-pom.xml
|
||||
|
||||
# C
|
||||
core/c/*.o
|
||||
core/c/lib/*/.unpacked
|
||||
core/c/lib/*/.patched
|
||||
core/c/lib/*/src
|
||||
core/c/lib/include
|
||||
platform-independent/cli-c/cli/*.o
|
||||
platform-independent/cli-c/mpw-*.tar.gz
|
||||
platform-independent/cli-c/mpw-*.tar.gz.sig
|
||||
platform-independent/cli-c/mpw
|
||||
platform-independent/cli-c/mpw-bench
|
||||
platform-independent/cli-c/mpw-tests
|
||||
platform-independent/cli-c/VERSION
|
||||
/gradle/builds
|
||||
/platform-android/.externalNativeBuild
|
||||
|
||||
18
.gitlab-ci.yml
Normal file
18
.gitlab-ci.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
variables:
|
||||
GIT_DEPTH: 3
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
|
||||
build_project:
|
||||
stage: build
|
||||
script:
|
||||
- "( brew bundle )"
|
||||
- "( ./lib/bin/build_libsodium-macos clean && ./lib/bin/build_libsodium-macos )"
|
||||
- "( ./lib/bin/build_libjson-c-macos clean && ./lib/bin/build_libjson-c-macos )"
|
||||
- "( cd ./platform-independent/c/cli && ./clean && targets=all ./build && ./mpw-tests && ./mpw-cli-tests )"
|
||||
- "( cd ./gradle && ./gradlew --stacktrace clean test )"
|
||||
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword iOS' -sdk iphonesimulator clean build )"
|
||||
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword macOS' clean build )"
|
||||
tags:
|
||||
- brew
|
||||
- java_9
|
||||
- xcode_9
|
||||
15
.gitmodules
vendored
15
.gitmodules
vendored
@@ -17,8 +17,17 @@
|
||||
path = platform-darwin/External/jrswizzle
|
||||
url = git://github.com/jonmarimba/jrswizzle.git
|
||||
[submodule "MasterPassword/Web/js/mpw-js"]
|
||||
path = platform-independent/web-js/js/mpw-js
|
||||
path = platform-independent/web/js/mpw-js
|
||||
url = https://github.com/tmthrgd/mpw-js.git
|
||||
[submodule "platform-darwin/External/libsodium"]
|
||||
path = platform-darwin/External/libsodium
|
||||
[submodule "lib/libsodium"]
|
||||
path = lib/libsodium
|
||||
url = https://github.com/jedisct1/libsodium.git
|
||||
[submodule "lib/libjson-c"]
|
||||
path = lib/libjson-c
|
||||
url = https://github.com/json-c/json-c.git
|
||||
[submodule "public/site"]
|
||||
path = public/site
|
||||
url = https://github.com/Lyndir/MasterPassword.git
|
||||
branch = gh-pages
|
||||
shallow = true
|
||||
update = none
|
||||
|
||||
10
.travis.yml
10
.travis.yml
@@ -1,10 +0,0 @@
|
||||
language: objective-c
|
||||
osx_image: xcode8.3
|
||||
env: TERM=dumb SHLVL=0
|
||||
git:
|
||||
submodules: true
|
||||
script:
|
||||
- "( brew install libsodium )"
|
||||
- "( cd ./platform-independent/cli-c && ./clean && targets='mpw mpw-bench mpw-tests' ./build && ./mpw-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' )"
|
||||
6
Brewfile
Normal file
6
Brewfile
Normal file
@@ -0,0 +1,6 @@
|
||||
brew "libsodium"
|
||||
brew "json-c"
|
||||
|
||||
brew "libtool"
|
||||
brew "automake"
|
||||
brew "autoconf"
|
||||
9
Dockerfile
Normal file
9
Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
||||
FROM alpine
|
||||
|
||||
# For i386
|
||||
#FROM i386/alpine
|
||||
#ENTRYPOINT ["linux32", "--"]
|
||||
|
||||
RUN apk add --no-cache git libtool automake autoconf make g++ bash openjdk8
|
||||
RUN git clone --depth=3 $(: --shallow-submodules) --recurse-submodules https://gitlab.com/MasterPassword/MasterPassword.git /mpw
|
||||
RUN cd /mpw/gradle && ./gradlew -i clean build
|
||||
63
README.md
63
README.md
@@ -1,11 +1,4 @@
|
||||
[](https://travis-ci.org/Lyndir/MasterPassword)
|
||||
[](https://gitter.im/lyndir/MasterPassword?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||
|
||||
|
||||
|
||||
|
||||
# [Master Password •••|](http://masterpasswordapp.com)
|
||||
# [Master Password •••|](http://masterpassword.app)
|
||||
|
||||
Master Password is a completely new way of thinking about passwords.
|
||||
|
||||
@@ -13,9 +6,9 @@ It consists of an algorithm that implements the core idea and applications for v
|
||||
|
||||
To skip the intro and go straight to the information on how to use the code, [click here](#source-code).
|
||||
|
||||
Master Password is available for [📲 iOS](https://itunes.apple.com/app/id510296984), [🖥 macOS](https://ssl.masterpasswordapp.com/masterpassword-mac.zip), [📲 Android](https://ssl.masterpasswordapp.com/masterpassword-android.apk), [🖥 Desktop](https://ssl.masterpasswordapp.com/masterpassword-gui.jar), and [⌨ Console](https://ssl.masterpasswordapp.com/masterpassword-cli.tar.gz).
|
||||
Master Password is available for [📲 iOS](https://itunes.apple.com/app/id510296984), [🖥 macOS](https://masterpassword.app/masterpassword-mac.zip), [📲 Android](https://masterpassword.app/masterpassword-android.apk), [🖥 Desktop](https://masterpassword.app/masterpassword-gui.jar), and [⌨ Console](https://masterpassword.app/masterpassword-cli.tar.gz).
|
||||
|
||||
Master Password is also available from the following package managers: [macOS: Homebrew](https://brew.sh/) (`brew install mpw`).
|
||||
Master Password is also available from: [macOS: Homebrew](https://brew.sh/) (`brew install mpw`) and [Docker](https://www.docker.com/) (`docker run -ti registry.gitlab.com/masterpassword/masterpassword`).
|
||||
Get in touch if you are interested in adding Master Password to any other package managers.
|
||||
|
||||
There are many reasons for using Master Password instead of an ordinary password manager, read below for the details, but if you want my personal favourites, they would be:
|
||||
@@ -90,7 +83,7 @@ Master Password is *not* a password manager. It does not store your website pas
|
||||
|
||||
## How does it work?
|
||||
|
||||
The details of how Master Password works [are available here](http://masterpasswordapp.com/algorithm.html).
|
||||
The details of how Master Password works [are available here](https://masterpassword.app/masterpassword-algorithm.pdf).
|
||||
|
||||
In short:
|
||||
|
||||
@@ -129,14 +122,14 @@ The `master-password` on the other hand, is only a simple phrase, which means it
|
||||
|
||||
5. I have another question.
|
||||
|
||||
Please don't hesitate to [get in touch](#support), we're more than happy to answer all your Master Password questions. Any problems or suggestions can be reported [as GitHub issues](https://github.com/Lyndir/MasterPassword/issues).
|
||||
Please don't hesitate to [get in touch](#support), we're more than happy to answer all your Master Password questions. Any problems or suggestions can be reported [as GitLab issues](https://gitlab.com/MasterPassword/MasterPassword/issues/).
|
||||
|
||||
|
||||
|
||||
|
||||
# Source Code
|
||||
|
||||
Master Password's algorithm is [documented](http://masterpasswordapp.com/algorithm.html) and its implementation is Free Software (GPLv3).
|
||||
Master Password's algorithm is [documented](https://masterpassword.app/masterpassword-algorithm.pdf) and its implementation is [Free Software (GPLv3)](LICENSE).
|
||||
|
||||
|
||||
|
||||
@@ -194,49 +187,7 @@ Note that in order to build the Android application, you will need to have the A
|
||||
|
||||
Go into the `platform-independent/cli-c` directory and run `./build`. The native command-line client will then be built.
|
||||
|
||||
When the build completes, you will have an `mpw` binary you can use. You can copy it into your `PATH` or use the `./install` script to help you do so.
|
||||
|
||||
For example:
|
||||
|
||||
./build && sudo ./install
|
||||
mpw -h
|
||||
|
||||
Normally, this is all you need to do, however note that there are a few dependencies that need to be met, depending on which targets you are building (by default, only the `mpw` target is built):
|
||||
|
||||
- `mpw`
|
||||
|
||||
The C implementation depends either on `libsodium` or Tarsnap's `scrypt` and `openssl-dev`.
|
||||
|
||||
We recommend you install `libsodium`. If `libsodium` is not installed when `./build` is executed, the script will try to download and statically link Tarsnap's `scrypt` instead. Tarsnap's `scrypt` depends on you having `openssl-dev` installed.
|
||||
|
||||
If you have `mpw_color` enabled (it is enabled by default), the build also depends on `ncurses-dev` to communicate with the terminal.
|
||||
|
||||
- `mpw-bench`
|
||||
|
||||
This tool compares the performance of a few cryptographic algorithms, including bcrypt. The `./build` script will try to automatically download and statically link `bcrypt`.
|
||||
|
||||
- `mpw-tests`
|
||||
|
||||
This tool runs a suite of tests to ensure the correct passwords are being generated by the algorithm under various circumstances. The test suite is declared in `mpw-tests.xml` which needs to exist in the current working directory when running the tool. In addition, `libxml2` is used to parse the file, so this target depends on you having it installed when running `./build`.
|
||||
|
||||
|
||||
Finally, there are a few different ways you can modify the build process.
|
||||
|
||||
To build additional targets, set the `targets` environment variable:
|
||||
|
||||
targets='mpw mpw-tests' ./build
|
||||
|
||||
To pass additional compiler arguments, eg. add a library search path, pass them as arguments to the script:
|
||||
|
||||
./build -L/usr/local/lib
|
||||
|
||||
There are a few toggleable features, to change them, pass them as environment variables:
|
||||
|
||||
mpw_color=0 ./build
|
||||
|
||||
Currently, there is only one toggleable feature:
|
||||
|
||||
- `mpw_color`: [default: 1] Colorized Identicon, depends on `ncurses-dev`.
|
||||
For detailed instructions, see [the native CLI instructions](platform-independent/c/README.md).
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
@@ -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=https://www.tarsnap.com/scrypt/scrypt-1.2.1.tgz
|
||||
pkg_sha256=4621f5e7da2f802e20850436219370092e9fcda93bd598f6d4236cce33f4c577
|
||||
@@ -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,69 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#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,43 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
// 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,140 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#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,125 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#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,125 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#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,125 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#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,203 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#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,109 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#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 a key for authentication. */
|
||||
MPSiteVariantPassword,
|
||||
/** Generate a name for identification. */
|
||||
MPSiteVariantLogin,
|
||||
/** Generate an 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,282 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if COLOR
|
||||
#include <unistd.h>
|
||||
#include <curses.h>
|
||||
#include <term.h>
|
||||
#endif
|
||||
|
||||
#if HAS_CPERCIVA
|
||||
#include <scrypt/crypto_scrypt.h>
|
||||
#include <scrypt/sha256.h>
|
||||
#elif HAS_SODIUM
|
||||
#include "sodium.h"
|
||||
#endif
|
||||
|
||||
#ifndef trc
|
||||
int mpw_verbosity;
|
||||
#endif
|
||||
|
||||
#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 HAS_CPERCIVA
|
||||
if (crypto_scrypt( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) < 0) {
|
||||
mpw_free( key, keySize );
|
||||
return NULL;
|
||||
}
|
||||
#elif HAS_SODIUM
|
||||
if (crypto_pwhash_scryptsalsa208sha256_ll( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize) != 0 ) {
|
||||
mpw_free( key, keySize );
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
uint8_t const *mpw_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize) {
|
||||
|
||||
#if HAS_CPERCIVA
|
||||
uint8_t *const buffer = malloc( 32 );
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
HMAC_SHA256_Buf( key, keySize, salt, saltSize, buffer );
|
||||
return buffer;
|
||||
#elif HAS_SODIUM
|
||||
uint8_t *const buffer = malloc( crypto_auth_hmacsha256_BYTES );
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
crypto_auth_hmacsha256_state state;
|
||||
if (crypto_auth_hmacsha256_init( &state, key, keySize ) != 0 ||
|
||||
crypto_auth_hmacsha256_update( &state, salt, saltSize ) != 0 ||
|
||||
crypto_auth_hmacsha256_final( &state, buffer ) != 0) {
|
||||
mpw_free( buffer, crypto_auth_hmacsha256_BYTES );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *mpw_id_buf(const void *buf, size_t length) {
|
||||
|
||||
#if HAS_CPERCIVA
|
||||
uint8_t hash[32];
|
||||
SHA256_Buf( buf, length, hash );
|
||||
|
||||
return mpw_hex( hash, 32 );
|
||||
#elif HAS_SODIUM
|
||||
uint8_t hash[crypto_hash_sha256_BYTES];
|
||||
crypto_hash_sha256( hash, buf, length );
|
||||
|
||||
return mpw_hex( hash, crypto_hash_sha256_BYTES );
|
||||
#endif
|
||||
}
|
||||
|
||||
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 ) == 0 && 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[] = {
|
||||
"◈", "◎", "◐", "◑", "◒", "◓", "☀", "☁", "☂", "☃", "☄", "★", "☆", "☎", "☏", "⎈", "⌂", "☘", "☢", "☣",
|
||||
"☕", "⌚", "⌛", "⏰", "⚡", "⛄", "⛅", "☔", "♔", "♕", "♖", "♗", "♘", "♙", "♚", "♛", "♜", "♝", "♞", "♟",
|
||||
"♨", "♩", "♪", "♫", "⚐", "⚑", "⚔", "⚖", "⚙", "⚠", "⌘", "⏎", "✄", "✆", "✈", "✉", "✌"
|
||||
};
|
||||
|
||||
const uint8_t *identiconSeed = mpw_hmac_sha256( (const uint8_t *)masterPassword, strlen( masterPassword ), (const uint8_t *)fullName, strlen( fullName ) );
|
||||
if (!identiconSeed)
|
||||
return NULL;
|
||||
|
||||
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 );
|
||||
|
||||
mpw_free( identiconSeed, 32 );
|
||||
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,119 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//// Logging.
|
||||
|
||||
#ifndef trc
|
||||
extern 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,15 +0,0 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
description = 'Master Password Algorithm Implementation'
|
||||
|
||||
dependencies {
|
||||
compile (group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.6-p10') {
|
||||
exclude( module: 'joda-time' )
|
||||
}
|
||||
|
||||
compile group: 'com.lambdaworks', name: 'scrypt', version: '1.4.0'
|
||||
compile group: 'org.jetbrains', name: 'annotations', version: '13.0'
|
||||
compile group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.1'
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!-- PROJECT METADATA -->
|
||||
<parent>
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Master Password Algorithm Implementation</name>
|
||||
<description>The implementation of the Master Password algorithm</description>
|
||||
|
||||
<artifactId>masterpassword-algorithm</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<!-- DEPENDENCY MANAGEMENT -->
|
||||
<dependencies>
|
||||
|
||||
<!-- PROJECT REFERENCES -->
|
||||
<dependency>
|
||||
<groupId>com.lyndir.lhunath.opal</groupId>
|
||||
<artifactId>opal-system</artifactId>
|
||||
<version>1.6-p9</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- EXTERNAL DEPENDENCIES -->
|
||||
<dependency>
|
||||
<groupId>com.lambdaworks</groupId>
|
||||
<artifactId>scrypt</artifactId>
|
||||
<version>1.4.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,99 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests;
|
||||
import com.lyndir.lhunath.opal.system.MessageDigests;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 2016-10-29
|
||||
*/
|
||||
public final class MPConstant {
|
||||
|
||||
/* Environment */
|
||||
|
||||
/**
|
||||
* mpw: default user name if one is not provided.
|
||||
*/
|
||||
public static final String env_userName = "MP_USERNAME";
|
||||
/**
|
||||
* mpw: default site type if one is not provided.
|
||||
*
|
||||
* @see MPSiteType#forOption(String)
|
||||
*/
|
||||
public static final String env_siteType = "MP_SITETYPE";
|
||||
/**
|
||||
* mpw: default site counter value if one is not provided.
|
||||
*/
|
||||
public static final String env_siteCounter = "MP_SITECOUNTER";
|
||||
/**
|
||||
* mpw: default path to look for run configuration files if the platform default is not desired.
|
||||
*/
|
||||
public static final String env_rcDir = "MP_RCDIR";
|
||||
/**
|
||||
* mpw: permit automatic update checks.
|
||||
*/
|
||||
public static final String env_checkUpdates = "MP_CHECKUPDATES";
|
||||
|
||||
/* Algorithm */
|
||||
|
||||
/**
|
||||
* scrypt: CPU cost parameter.
|
||||
*/
|
||||
public static final int scrypt_N = 32768;
|
||||
/**
|
||||
* scrypt: Memory cost parameter.
|
||||
*/
|
||||
public static final int scrypt_r = 8;
|
||||
/**
|
||||
* scrypt: Parallelization parameter.
|
||||
*/
|
||||
public static final int scrypt_p = 2;
|
||||
/**
|
||||
* mpw: Master key size (byte).
|
||||
*/
|
||||
public static final int mpw_dkLen = 64;
|
||||
/**
|
||||
* mpw: Input character encoding.
|
||||
*/
|
||||
public static final Charset mpw_charset = Charsets.UTF_8;
|
||||
/**
|
||||
* mpw: Platform-agnostic byte order.
|
||||
*/
|
||||
public static final ByteOrder mpw_byteOrder = ByteOrder.BIG_ENDIAN;
|
||||
/**
|
||||
* mpw: Site digest.
|
||||
*/
|
||||
public static final MessageAuthenticationDigests mpw_digest = MessageAuthenticationDigests.HmacSHA256;
|
||||
/**
|
||||
* mpw: Key ID hash.
|
||||
*/
|
||||
public static final MessageDigests mpw_hash = MessageDigests.SHA256;
|
||||
/**
|
||||
* mpw: validity for the time-based rolling counter.
|
||||
*/
|
||||
public static final int mpw_counter_timeout = 5 * 60 /* s */;
|
||||
|
||||
|
||||
public static final int MS_PER_S = 1000;
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.util.*;
|
||||
import javax.annotation.Nullable;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
|
||||
/**
|
||||
* <i>07 04, 2012</i>
|
||||
*
|
||||
* @author lhunath
|
||||
*/
|
||||
public enum MPSiteType {
|
||||
|
||||
GeneratedMaximum( "Max", "20 characters, contains symbols.", //
|
||||
ImmutableList.of( "x", "max", "maximum" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
|
||||
MPSiteTypeClass.Generated, 0x0 ),
|
||||
|
||||
GeneratedLong( "Long", "Copy-friendly, 14 characters, contains symbols.", //
|
||||
ImmutableList.of( "l", "long" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
|
||||
new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
|
||||
new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ),
|
||||
new MPTemplate( "CvcvnoCvccCvcv" ), new MPTemplate( "CvcvCvccnoCvcv" ),
|
||||
new MPTemplate( "CvcvCvccCvcvno" ), new MPTemplate( "CvcvnoCvcvCvcc" ),
|
||||
new MPTemplate( "CvcvCvcvnoCvcc" ), new MPTemplate( "CvcvCvcvCvccno" ),
|
||||
new MPTemplate( "CvccnoCvccCvcv" ), new MPTemplate( "CvccCvccnoCvcv" ),
|
||||
new MPTemplate( "CvccCvccCvcvno" ), new MPTemplate( "CvcvnoCvccCvcc" ),
|
||||
new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ),
|
||||
new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ),
|
||||
new MPTemplate( "CvccCvcvCvccno" ) ), //
|
||||
MPSiteTypeClass.Generated, 0x1 ),
|
||||
|
||||
GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", //
|
||||
ImmutableList.of( "m", "med", "medium" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), //
|
||||
MPSiteTypeClass.Generated, 0x2 ),
|
||||
|
||||
GeneratedBasic( "Basic", "8 characters, no symbols.", //
|
||||
ImmutableList.of( "b", "basic" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "aaanaaan" ), new MPTemplate( "aannaaan" ), new MPTemplate( "aaannaaa" ) ), //
|
||||
MPSiteTypeClass.Generated, 0x3 ),
|
||||
|
||||
GeneratedShort( "Short", "Copy-friendly, 4 characters, no symbols.", //
|
||||
ImmutableList.of( "s", "short" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
|
||||
MPSiteTypeClass.Generated, 0x4 ),
|
||||
|
||||
GeneratedPIN( "PIN", "4 numbers.", //
|
||||
ImmutableList.of( "i", "pin" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "nnnn" ) ), //
|
||||
MPSiteTypeClass.Generated, 0x5 ),
|
||||
|
||||
GeneratedName( "Name", "9 letter name.", //
|
||||
ImmutableList.of( "n", "name" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
|
||||
MPSiteTypeClass.Generated, 0xE ),
|
||||
|
||||
GeneratedPhrase( "Phrase", "20 character sentence.", //
|
||||
ImmutableList.of( "p", "phrase" ), // NON-NLS
|
||||
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ),
|
||||
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
|
||||
MPSiteTypeClass.Generated, 0xF ),
|
||||
|
||||
StoredPersonal( "Personal", "AES-encrypted, exportable.", //
|
||||
ImmutableList.of( "personal" ), // NON-NLS
|
||||
ImmutableList.<MPTemplate>of(), //
|
||||
MPSiteTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ),
|
||||
|
||||
StoredDevicePrivate( "Device", "AES-encrypted, not exported.", //
|
||||
ImmutableList.of( "device" ), // NON-NLS
|
||||
ImmutableList.<MPTemplate>of(), //
|
||||
MPSiteTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate );
|
||||
|
||||
static final Logger logger = Logger.get( MPSiteType.class );
|
||||
|
||||
private final String shortName;
|
||||
private final String description;
|
||||
private final List<String> options;
|
||||
private final List<MPTemplate> templates;
|
||||
private final MPSiteTypeClass typeClass;
|
||||
private final int typeIndex;
|
||||
private final Set<MPSiteFeature> typeFeatures;
|
||||
|
||||
MPSiteType(final String shortName, final String description, final List<String> options, final List<MPTemplate> templates,
|
||||
final MPSiteTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) {
|
||||
|
||||
this.shortName = shortName;
|
||||
this.description = description;
|
||||
this.options = options;
|
||||
this.templates = templates;
|
||||
this.typeClass = typeClass;
|
||||
this.typeIndex = typeIndex;
|
||||
|
||||
ImmutableSet.Builder<MPSiteFeature> typeFeaturesBuilder = ImmutableSet.builder();
|
||||
for (final MPSiteFeature typeFeature : typeFeatures) {
|
||||
typeFeaturesBuilder.add( typeFeature );
|
||||
}
|
||||
this.typeFeatures = typeFeaturesBuilder.build();
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
public List<String> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public MPSiteTypeClass getTypeClass() {
|
||||
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
public Set<MPSiteFeature> getTypeFeatures() {
|
||||
|
||||
return typeFeatures;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
int mask = typeIndex | typeClass.getMask();
|
||||
for (final MPSiteFeature typeFeature : typeFeatures)
|
||||
mask |= typeFeature.getMask();
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param option The option to select a type with. It is matched case insensitively.
|
||||
*
|
||||
* @return The type registered for the given option.
|
||||
*/
|
||||
public static MPSiteType forOption(final String option) {
|
||||
|
||||
for (final MPSiteType type : values())
|
||||
if (type.getOptions().contains( option.toLowerCase( Locale.ROOT ) ))
|
||||
return type;
|
||||
|
||||
throw logger.bug( "No type for option: %s", option );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name The name fromInt the type to look up. It is matched case insensitively.
|
||||
*
|
||||
* @return The type registered with the given name.
|
||||
*/
|
||||
@Contract("!null -> !null")
|
||||
public static MPSiteType forName(@Nullable final String name) {
|
||||
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
for (final MPSiteType type : values())
|
||||
if (type.name().equalsIgnoreCase( name ))
|
||||
return type;
|
||||
|
||||
throw logger.bug( "No type for name: %s", name );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param typeClass The class for which we look up types.
|
||||
*
|
||||
* @return All types that support the given class.
|
||||
*/
|
||||
public static ImmutableList<MPSiteType> forClass(final MPSiteTypeClass typeClass) {
|
||||
|
||||
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
|
||||
for (final MPSiteType type : values())
|
||||
if (type.getTypeClass() == typeClass)
|
||||
types.add( type );
|
||||
|
||||
return types.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type The type for which we look up types.
|
||||
*
|
||||
* @return The type registered with the given type.
|
||||
*/
|
||||
public static MPSiteType forType(final int type) {
|
||||
|
||||
for (final MPSiteType siteType : values())
|
||||
if (siteType.getType() == type)
|
||||
return siteType;
|
||||
|
||||
throw logger.bug( "No type: %s", type );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mask The mask for which we look up types.
|
||||
*
|
||||
* @return All types that support the given mask.
|
||||
*/
|
||||
public static ImmutableList<MPSiteType> forMask(final int mask) {
|
||||
|
||||
int typeMask = mask & ~0xF;
|
||||
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
|
||||
for (final MPSiteType siteType : values())
|
||||
if (((siteType.getType() & ~0xF) & typeMask) != 0)
|
||||
types.add( siteType );
|
||||
|
||||
return types.build();
|
||||
}
|
||||
|
||||
public MPTemplate getTemplateAtRollingIndex(final int templateIndex) {
|
||||
return templates.get( templateIndex % templates.size() );
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.annotation.Nullable;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 14-12-02
|
||||
*/
|
||||
public enum MPSiteVariant {
|
||||
Password( "Generate a key for authentication.", "Doesn't currently use a context.", //
|
||||
ImmutableList.of( "p", "password" ), "com.lyndir.masterpassword" ), // NON-NLS
|
||||
Login( "Generate a name for identification.", "Doesn't currently use a context.", //
|
||||
ImmutableList.of( "l", "login" ), "com.lyndir.masterpassword.login" ), // NON-NLS
|
||||
Answer( "Generate an answer to a security question.", "Empty for a universal site answer or\nthe most significant word(s) of the question.", //
|
||||
ImmutableList.of( "a", "answer" ), "com.lyndir.masterpassword.answer" ); // NON-NLS
|
||||
|
||||
static final Logger logger = Logger.get( MPSiteType.class );
|
||||
|
||||
private final String description;
|
||||
private final String contextDescription;
|
||||
private final List<String> options;
|
||||
private final String scope;
|
||||
|
||||
MPSiteVariant(final String description, final String contextDescription, final List<String> options, @NonNls final String scope) {
|
||||
this.contextDescription = contextDescription;
|
||||
|
||||
this.options = options;
|
||||
this.description = description;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getContextDescription() {
|
||||
return contextDescription;
|
||||
}
|
||||
|
||||
public List<String> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param option The option to select a variant with. It is matched case insensitively.
|
||||
*
|
||||
* @return The variant registered for the given option.
|
||||
*/
|
||||
public static MPSiteVariant forOption(final String option) {
|
||||
|
||||
for (final MPSiteVariant variant : values())
|
||||
if (variant.getOptions().contains( option.toLowerCase( Locale.ROOT ) ))
|
||||
return variant;
|
||||
|
||||
throw logger.bug( "No variant for option: %s", option );
|
||||
}
|
||||
/**
|
||||
* @param name The name fromInt the variant to look up. It is matched case insensitively.
|
||||
*
|
||||
* @return The variant registered with the given name.
|
||||
*/
|
||||
@Contract("!null -> !null")
|
||||
public static MPSiteVariant forName(@Nullable final String name) {
|
||||
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
for (final MPSiteVariant type : values())
|
||||
if (type.name().equalsIgnoreCase( name ))
|
||||
return type;
|
||||
|
||||
throw logger.bug( "No variant for name: %s", name );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.lhunath.opal.system.*;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 2014-08-30
|
||||
*/
|
||||
public abstract class MasterKey {
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final Logger logger = Logger.get( MasterKey.class );
|
||||
private static boolean allowNativeByDefault = true;
|
||||
|
||||
@Nonnull
|
||||
private final String fullName;
|
||||
private boolean allowNative = allowNativeByDefault;
|
||||
|
||||
@Nullable
|
||||
private byte[] masterKey;
|
||||
|
||||
@SuppressWarnings("MethodCanBeVariableArityMethod")
|
||||
public static MasterKey create(final String fullName, final char[] masterPassword) {
|
||||
|
||||
return create( Version.CURRENT, fullName, masterPassword );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@SuppressWarnings("MethodCanBeVariableArityMethod")
|
||||
public static MasterKey create(final Version version, final String fullName, final char[] masterPassword) {
|
||||
|
||||
switch (version) {
|
||||
case V0:
|
||||
return new MasterKeyV0( fullName ).revalidate( masterPassword );
|
||||
case V1:
|
||||
return new MasterKeyV1( fullName ).revalidate( masterPassword );
|
||||
case V2:
|
||||
return new MasterKeyV2( fullName ).revalidate( masterPassword );
|
||||
case V3:
|
||||
return new MasterKeyV3( fullName ).revalidate( masterPassword );
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException( strf( "Unsupported version: %s", version ) );
|
||||
}
|
||||
|
||||
public static boolean isAllowNativeByDefault() {
|
||||
return allowNativeByDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Native libraries are useful for speeding up the performance of cryptographical functions.
|
||||
* Sometimes, however, we may prefer to use Java-only code.
|
||||
* For instance, for auditability / trust or because the native code doesn't work on our CPU/platform.
|
||||
* <p/>
|
||||
* This setter affects the default setting for any newly created {@link MasterKey}s.
|
||||
*
|
||||
* @param allowNative false to disallow the use of native libraries.
|
||||
*/
|
||||
public static void setAllowNativeByDefault(final boolean allowNative) {
|
||||
allowNativeByDefault = allowNative;
|
||||
}
|
||||
|
||||
protected MasterKey(@Nonnull final String fullName) {
|
||||
|
||||
this.fullName = fullName;
|
||||
logger.trc( "fullName: %s", fullName );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("MethodCanBeVariableArityMethod")
|
||||
protected abstract byte[] deriveKey(char[] masterPassword);
|
||||
|
||||
public abstract Version getAlgorithmVersion();
|
||||
|
||||
@Nonnull
|
||||
public String getFullName() {
|
||||
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public boolean isAllowNative() {
|
||||
return allowNative;
|
||||
}
|
||||
|
||||
public MasterKey setAllowNative(final boolean allowNative) {
|
||||
this.allowNative = allowNative;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected byte[] getKey() {
|
||||
|
||||
Preconditions.checkState( isValid() );
|
||||
return Preconditions.checkNotNull( masterKey );
|
||||
}
|
||||
|
||||
public byte[] getKeyID() {
|
||||
|
||||
return idForBytes( getKey() );
|
||||
}
|
||||
|
||||
public abstract String encode(@Nonnull String siteName, MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
|
||||
MPSiteVariant siteVariant, @Nullable String siteContext);
|
||||
|
||||
public boolean isValid() {
|
||||
return masterKey != null;
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
|
||||
if (masterKey != null) {
|
||||
Arrays.fill( masterKey, (byte) 0 );
|
||||
masterKey = null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("MethodCanBeVariableArityMethod")
|
||||
public MasterKey revalidate(final char[] masterPassword) {
|
||||
invalidate();
|
||||
|
||||
logger.trc( "masterPassword: %s", new String( masterPassword ) );
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
masterKey = deriveKey( masterPassword );
|
||||
|
||||
if (masterKey == null)
|
||||
logger.dbg( "masterKey calculation failed after %.2fs.", (double)(System.currentTimeMillis() - start) / MPConstant.MS_PER_S );
|
||||
else
|
||||
logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
|
||||
(double)(System.currentTimeMillis() - start) / MPConstant.MS_PER_S );
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
protected abstract byte[] bytesForInt(int number);
|
||||
|
||||
protected abstract byte[] bytesForInt(@Nonnull UnsignedInteger number);
|
||||
|
||||
protected abstract byte[] idForBytes(byte[] bytes);
|
||||
|
||||
public enum Version {
|
||||
/**
|
||||
* bugs:
|
||||
* - does math with chars whose signedness was platform-dependent.
|
||||
* - miscounted the byte-length fromInt multi-byte site names.
|
||||
* - miscounted the byte-length fromInt multi-byte full names.
|
||||
*/
|
||||
V0,
|
||||
/**
|
||||
* bugs:
|
||||
* - miscounted the byte-length fromInt multi-byte site names.
|
||||
* - miscounted the byte-length fromInt multi-byte full names.
|
||||
*/
|
||||
V1,
|
||||
/**
|
||||
* bugs:
|
||||
* - miscounted the byte-length fromInt multi-byte full names.
|
||||
*/
|
||||
V2,
|
||||
/**
|
||||
* bugs:
|
||||
* - no known issues.
|
||||
*/
|
||||
V3;
|
||||
|
||||
public static final Version CURRENT = V3;
|
||||
|
||||
public static Version fromInt(final int algorithmVersion) {
|
||||
|
||||
return values()[algorithmVersion];
|
||||
}
|
||||
|
||||
public int toInt() {
|
||||
|
||||
return ordinal();
|
||||
}
|
||||
|
||||
public String toBundleVersion() {
|
||||
switch (this) {
|
||||
case V0:
|
||||
return "1.0";
|
||||
case V1:
|
||||
return "2.0";
|
||||
case V2:
|
||||
return "2.1";
|
||||
case V3:
|
||||
return "2.2";
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException( strf( "Unsupported version: %s", this ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lambdaworks.crypto.SCrypt;
|
||||
import com.lyndir.lhunath.opal.system.*;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.nio.*;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
* bugs:
|
||||
* - V2: miscounted the byte-length fromInt multi-byte full names.
|
||||
* - V1: miscounted the byte-length fromInt multi-byte site names.
|
||||
* - V0: does math with chars whose signedness was platform-dependent.
|
||||
*
|
||||
* @author lhunath, 2014-08-30
|
||||
*/
|
||||
public class MasterKeyV0 extends MasterKey {
|
||||
|
||||
private static final int MP_intLen = 32;
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final Logger logger = Logger.get( MasterKeyV0.class );
|
||||
|
||||
public MasterKeyV0(final String fullName) {
|
||||
super( fullName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version getAlgorithmVersion() {
|
||||
|
||||
return Version.V0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected byte[] deriveKey(final char[] masterPassword) {
|
||||
String fullName = getFullName();
|
||||
byte[] fullNameBytes = fullName.getBytes( MPConstant.mpw_charset );
|
||||
byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
|
||||
|
||||
String mpKeyScope = MPSiteVariant.Password.getScope();
|
||||
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes );
|
||||
logger.trc( "key scope: %s", mpKeyScope );
|
||||
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||
|
||||
ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
|
||||
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
|
||||
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
|
||||
Arrays.fill( mpBytesBuf.array(), (byte) 0 );
|
||||
|
||||
return scrypt( masterKeySalt, mpBytes );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) {
|
||||
try {
|
||||
if (isAllowNative())
|
||||
return SCrypt.scrypt( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen );
|
||||
else
|
||||
return SCrypt.scryptJ( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen );
|
||||
}
|
||||
catch (final GeneralSecurityException e) {
|
||||
logger.bug( e );
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
Arrays.fill( mpBytes, (byte) 0 );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
|
||||
final MPSiteVariant siteVariant, @Nullable final String siteContext) {
|
||||
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
|
||||
Preconditions.checkArgument( !siteName.isEmpty() );
|
||||
|
||||
logger.trc( "siteName: %s", siteName );
|
||||
logger.trc( "siteCounter: %d", siteCounter.longValue() );
|
||||
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
|
||||
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
|
||||
|
||||
if (siteCounter.longValue() == 0)
|
||||
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout );
|
||||
|
||||
String siteScope = siteVariant.getScope();
|
||||
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
|
||||
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
|
||||
byte[] siteCounterBytes = bytesForInt( siteCounter );
|
||||
byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset );
|
||||
byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length );
|
||||
logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "<empty>": siteContext );
|
||||
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
||||
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
||||
(siteContextBytes == null)? "(null)": siteContext );
|
||||
|
||||
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||
if (siteContextBytes != null)
|
||||
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
|
||||
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||
|
||||
byte[] sitePasswordSeedBytes = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
|
||||
int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length];
|
||||
for (int i = 0; i < sitePasswordSeedBytes.length; ++i) {
|
||||
ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN );
|
||||
Arrays.fill( buf.array(), (byte) ((sitePasswordSeedBytes[i] > 0)? 0x00: 0xFF) );
|
||||
buf.position( 2 );
|
||||
buf.put( sitePasswordSeedBytes[i] ).rewind();
|
||||
sitePasswordSeed[i] = buf.getInt() & 0xFFFF;
|
||||
}
|
||||
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
|
||||
|
||||
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
||||
int templateIndex = sitePasswordSeed[0];
|
||||
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
|
||||
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
|
||||
|
||||
StringBuilder password = new StringBuilder( template.length() );
|
||||
for (int i = 0; i < template.length(); ++i) {
|
||||
int characterIndex = sitePasswordSeed[i + 1];
|
||||
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
||||
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
||||
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
|
||||
sitePasswordSeed[i + 1], passwordCharacter );
|
||||
|
||||
password.append( passwordCharacter );
|
||||
}
|
||||
|
||||
return password.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] bytesForInt(final int number) {
|
||||
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number ).array();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] bytesForInt(@Nonnull final UnsignedInteger number) {
|
||||
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number.intValue() ).array();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] idForBytes(final byte[] bytes) {
|
||||
return MPConstant.mpw_hash.of( bytes );
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.lhunath.opal.system.*;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
* bugs:
|
||||
* - V2: miscounted the byte-length fromInt multi-byte full names.
|
||||
* - V1: miscounted the byte-length fromInt multi-byte site names.
|
||||
*
|
||||
* @author lhunath, 2014-08-30
|
||||
*/
|
||||
public class MasterKeyV1 extends MasterKeyV0 {
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final Logger logger = Logger.get( MasterKeyV1.class );
|
||||
|
||||
public MasterKeyV1(final String fullName) {
|
||||
super( fullName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version getAlgorithmVersion() {
|
||||
|
||||
return Version.V1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
|
||||
final MPSiteVariant siteVariant, @Nullable final String siteContext) {
|
||||
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
|
||||
Preconditions.checkArgument( !siteName.isEmpty() );
|
||||
|
||||
logger.trc( "siteName: %s", siteName );
|
||||
logger.trc( "siteCounter: %d", siteCounter.longValue() );
|
||||
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
|
||||
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
|
||||
|
||||
if (siteCounter.longValue() == 0)
|
||||
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout );
|
||||
|
||||
String siteScope = siteVariant.getScope();
|
||||
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
|
||||
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
|
||||
byte[] siteCounterBytes = bytesForInt( siteCounter );
|
||||
byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset );
|
||||
byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length );
|
||||
logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "<empty>": siteContext );
|
||||
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
||||
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
||||
(siteContextBytes == null)? "(null)": siteContext );
|
||||
|
||||
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||
if (siteContextBytes != null)
|
||||
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
|
||||
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||
|
||||
byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
|
||||
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
|
||||
|
||||
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
||||
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
|
||||
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
|
||||
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
|
||||
|
||||
StringBuilder password = new StringBuilder( template.length() );
|
||||
for (int i = 0; i < template.length(); ++i) {
|
||||
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
|
||||
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
||||
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
||||
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
|
||||
sitePasswordSeed[i + 1], passwordCharacter );
|
||||
|
||||
password.append( passwordCharacter );
|
||||
}
|
||||
|
||||
return password.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
* bugs:
|
||||
* - V2: miscounted the byte-length fromInt multi-byte full names.
|
||||
*
|
||||
* @author lhunath, 2014-08-30
|
||||
*/
|
||||
public class MasterKeyV2 extends MasterKeyV1 {
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final Logger logger = Logger.get( MasterKeyV2.class );
|
||||
|
||||
public MasterKeyV2(final String fullName) {
|
||||
super( fullName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version getAlgorithmVersion() {
|
||||
|
||||
return Version.V2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
|
||||
final MPSiteVariant siteVariant, @Nullable final String siteContext) {
|
||||
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
|
||||
Preconditions.checkArgument( !siteName.isEmpty() );
|
||||
|
||||
logger.trc( "siteName: %s", siteName );
|
||||
logger.trc( "siteCounter: %d", siteCounter.longValue() );
|
||||
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
|
||||
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
|
||||
|
||||
if (siteCounter.longValue() == 0)
|
||||
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout );
|
||||
|
||||
String siteScope = siteVariant.getScope();
|
||||
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
|
||||
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
|
||||
byte[] siteCounterBytes = bytesForInt( siteCounter );
|
||||
byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset );
|
||||
byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length );
|
||||
logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "<empty>": siteContext );
|
||||
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
||||
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
||||
(siteContextBytes == null)? "(null)": siteContext );
|
||||
|
||||
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||
if (siteContextBytes != null)
|
||||
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
|
||||
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||
|
||||
byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
|
||||
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
|
||||
|
||||
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
||||
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
|
||||
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
|
||||
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
|
||||
|
||||
StringBuilder password = new StringBuilder( template.length() );
|
||||
for (int i = 0; i < template.length(); ++i) {
|
||||
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
|
||||
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
||||
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
||||
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
|
||||
sitePasswordSeed[i + 1], passwordCharacter );
|
||||
|
||||
password.append( passwordCharacter );
|
||||
}
|
||||
|
||||
return password.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
* bugs:
|
||||
* - no known issues.
|
||||
*
|
||||
* @author lhunath, 2014-08-30
|
||||
*/
|
||||
public class MasterKeyV3 extends MasterKeyV2 {
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final Logger logger = Logger.get( MasterKeyV3.class );
|
||||
|
||||
public MasterKeyV3(final String fullName) {
|
||||
super( fullName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version getAlgorithmVersion() {
|
||||
|
||||
return Version.V3;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected byte[] deriveKey(final char[] masterPassword) {
|
||||
byte[] fullNameBytes = getFullName().getBytes( MPConstant.mpw_charset );
|
||||
byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length );
|
||||
|
||||
String mpKeyScope = MPSiteVariant.Password.getScope();
|
||||
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes );
|
||||
logger.trc( "key scope: %s", mpKeyScope );
|
||||
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||
|
||||
ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
|
||||
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
|
||||
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
|
||||
Arrays.fill( mpBytesBuf.array(), (byte) 0 );
|
||||
|
||||
return scrypt( masterKeySalt, mpBytes );
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'net.ltgt.apt' version '0.9'
|
||||
}
|
||||
|
||||
description = 'Master Password Site Model'
|
||||
|
||||
dependencies {
|
||||
compile project(':masterpassword-algorithm')
|
||||
|
||||
compile group: 'joda-time', name: 'joda-time', version:'2.4'
|
||||
compileOnly group: 'com.google.auto.value', name: 'auto-value', version: '1.2'
|
||||
apt group: 'com.google.auto.value', name: 'auto-value', version: '1.2'
|
||||
|
||||
testCompile group: 'org.testng', name: 'testng', version:'6.8.5'
|
||||
testCompile group: 'ch.qos.logback', name: 'logback-classic', version:'1.1.2'
|
||||
}
|
||||
test.useTestNG()
|
||||
@@ -1,55 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!-- PROJECT METADATA -->
|
||||
<parent>
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Master Password Site Model</name>
|
||||
<description>A persistence model for Master Password sites.</description>
|
||||
|
||||
<artifactId>masterpassword-model</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<!-- DEPENDENCY MANAGEMENT -->
|
||||
<dependencies>
|
||||
|
||||
<!-- PROJECT REFERENCES -->
|
||||
<dependency>
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword-algorithm</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- EXTERNAL DEPENDENCIES -->
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.value</groupId>
|
||||
<artifactId>auto-value</artifactId>
|
||||
<version>1.0-rc1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- TESTING -->
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
/**
|
||||
* @author lhunath, 14-12-17
|
||||
*/
|
||||
public class IncorrectMasterPasswordException extends Exception {
|
||||
|
||||
private final MPUser user;
|
||||
|
||||
public IncorrectMasterPasswordException(final MPUser user) {
|
||||
super( "Incorrect master password for user: " + user.getFullName() );
|
||||
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public MPUser getUser() {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.masterpassword.*;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.Instant;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 14-12-05
|
||||
*/
|
||||
public class MPSite {
|
||||
|
||||
public static final MPSiteType DEFAULT_TYPE = MPSiteType.GeneratedLong;
|
||||
public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.valueOf( 1 );
|
||||
|
||||
private final MPUser user;
|
||||
private MasterKey.Version algorithmVersion;
|
||||
private Instant lastUsed;
|
||||
private String siteName;
|
||||
private MPSiteType siteType;
|
||||
private UnsignedInteger siteCounter;
|
||||
private int uses;
|
||||
private String loginName;
|
||||
|
||||
public MPSite(final MPUser user, final String siteName) {
|
||||
this( user, siteName, DEFAULT_TYPE, DEFAULT_COUNTER );
|
||||
}
|
||||
|
||||
public MPSite(final MPUser user, final String siteName, final MPSiteType siteType, final UnsignedInteger siteCounter) {
|
||||
this.user = user;
|
||||
this.algorithmVersion = MasterKey.Version.CURRENT;
|
||||
this.lastUsed = new Instant();
|
||||
this.siteName = siteName;
|
||||
this.siteType = siteType;
|
||||
this.siteCounter = siteCounter;
|
||||
}
|
||||
|
||||
protected MPSite(final MPUser user, final MasterKey.Version algorithmVersion, final Instant lastUsed, final String siteName,
|
||||
final MPSiteType siteType, final UnsignedInteger siteCounter, final int uses, @Nullable final String loginName,
|
||||
@Nullable final String importContent) {
|
||||
this.user = user;
|
||||
this.algorithmVersion = algorithmVersion;
|
||||
this.lastUsed = lastUsed;
|
||||
this.siteName = siteName;
|
||||
this.siteType = siteType;
|
||||
this.siteCounter = siteCounter;
|
||||
this.uses = uses;
|
||||
this.loginName = loginName;
|
||||
}
|
||||
|
||||
public String resultFor(final MasterKey masterKey) {
|
||||
return resultFor( masterKey, MPSiteVariant.Password, null );
|
||||
}
|
||||
|
||||
public String resultFor(final MasterKey masterKey, final MPSiteVariant variant, @Nullable final String context) {
|
||||
return masterKey.encode( siteName, siteType, siteCounter, variant, context );
|
||||
}
|
||||
|
||||
public MPUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected String exportContent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public MasterKey.Version getAlgorithmVersion() {
|
||||
return algorithmVersion;
|
||||
}
|
||||
|
||||
public void setAlgorithmVersion(final MasterKey.Version mpVersion) {
|
||||
this.algorithmVersion = mpVersion;
|
||||
}
|
||||
|
||||
public Instant getLastUsed() {
|
||||
return lastUsed;
|
||||
}
|
||||
|
||||
public void updateLastUsed() {
|
||||
lastUsed = new Instant();
|
||||
user.updateLastUsed();
|
||||
}
|
||||
|
||||
public String getSiteName() {
|
||||
return siteName;
|
||||
}
|
||||
|
||||
public void setSiteName(final String siteName) {
|
||||
this.siteName = siteName;
|
||||
}
|
||||
|
||||
public MPSiteType getSiteType() {
|
||||
return siteType;
|
||||
}
|
||||
|
||||
public void setSiteType(final MPSiteType siteType) {
|
||||
this.siteType = siteType;
|
||||
}
|
||||
|
||||
public UnsignedInteger getSiteCounter() {
|
||||
return siteCounter;
|
||||
}
|
||||
|
||||
public void setSiteCounter(final UnsignedInteger siteCounter) {
|
||||
this.siteCounter = siteCounter;
|
||||
}
|
||||
|
||||
public int getUses() {
|
||||
return uses;
|
||||
}
|
||||
|
||||
public void setUses(final int uses) {
|
||||
this.uses = uses;
|
||||
}
|
||||
|
||||
public String getLoginName() {
|
||||
return loginName;
|
||||
}
|
||||
|
||||
public void setLoginName(final String loginName) {
|
||||
this.loginName = loginName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
return (this == obj) || ((obj instanceof MPSite) && Objects.equals( siteName, ((MPSite) obj).siteName ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode( siteName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return strf( "{MPSite: %s}", siteName );
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.Instant;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 14-12-07
|
||||
*/
|
||||
public class MPSiteMarshaller {
|
||||
|
||||
private static final DateTimeFormatter rfc3339 = ISODateTimeFormat.dateTimeNoMillis();
|
||||
|
||||
private final StringBuilder export = new StringBuilder();
|
||||
private ContentMode contentMode = ContentMode.PROTECTED;
|
||||
private MasterKey masterKey;
|
||||
|
||||
public static MPSiteMarshaller marshallSafe(final MPUser user) {
|
||||
MPSiteMarshaller marshaller = new MPSiteMarshaller();
|
||||
marshaller.marshallHeaderForSafeContent( user );
|
||||
for (final MPSite site : user.getSites())
|
||||
marshaller.marshallSite( site );
|
||||
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
public static MPSiteMarshaller marshallVisible(final MPUser user, final MasterKey masterKey) {
|
||||
MPSiteMarshaller marshaller = new MPSiteMarshaller();
|
||||
marshaller.marshallHeaderForVisibleContentWithKey( user, masterKey );
|
||||
for (final MPSite site : user.getSites())
|
||||
marshaller.marshallSite( site );
|
||||
|
||||
return marshaller;
|
||||
}
|
||||
|
||||
private String marshallHeaderForSafeContent(final MPUser user) {
|
||||
return marshallHeader( ContentMode.PROTECTED, user, null );
|
||||
}
|
||||
|
||||
private String marshallHeaderForVisibleContentWithKey(final MPUser user, final MasterKey masterKey) {
|
||||
return marshallHeader( ContentMode.VISIBLE, user, masterKey );
|
||||
}
|
||||
|
||||
private String marshallHeader(final ContentMode contentMode, final MPUser user, @Nullable final MasterKey masterKey) {
|
||||
this.contentMode = contentMode;
|
||||
this.masterKey = masterKey;
|
||||
|
||||
StringBuilder header = new StringBuilder();
|
||||
header.append( "# Master Password site export\n" );
|
||||
header.append( "# " ).append( this.contentMode.description() ).append( '\n' );
|
||||
header.append( "# \n" );
|
||||
header.append( "##\n" );
|
||||
header.append( "# Format: 1\n" );
|
||||
header.append( "# Date: " ).append( rfc3339.print( new Instant() ) ).append( '\n' );
|
||||
header.append( "# User Name: " ).append( user.getFullName() ).append( '\n' );
|
||||
header.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
|
||||
header.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
|
||||
header.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
|
||||
header.append( "# Version: " ).append( MasterKey.Version.CURRENT.toBundleVersion() ).append( '\n' );
|
||||
header.append( "# Algorithm: " ).append( MasterKey.Version.CURRENT.toInt() ).append( '\n' );
|
||||
header.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
|
||||
header.append( "# Passwords: " ).append( this.contentMode.name() ).append( '\n' );
|
||||
header.append( "##\n" );
|
||||
header.append( "#\n" );
|
||||
header.append( "# Last Times Password Login\t Site\tSite\n" );
|
||||
header.append( "# used used type name\t name\tpassword\n" );
|
||||
|
||||
export.append( header );
|
||||
return header.toString();
|
||||
}
|
||||
|
||||
public String marshallSite(final MPSite site) {
|
||||
String exportLine = strf( "%s %8d %8s %25s\t%25s\t%s", //
|
||||
rfc3339.print( site.getLastUsed() ), // lastUsed
|
||||
site.getUses(), // uses
|
||||
strf( "%d:%d:%d", //
|
||||
site.getSiteType().getType(), // type
|
||||
site.getAlgorithmVersion().toInt(), // algorithm
|
||||
site.getSiteCounter().intValue() ), // counter
|
||||
ifNotNullElse( site.getLoginName(), "" ), // loginName
|
||||
site.getSiteName(), // siteName
|
||||
ifNotNullElse( contentMode.contentForSite( site, masterKey ), "" ) // password
|
||||
);
|
||||
export.append( exportLine ).append( '\n' );
|
||||
|
||||
return exportLine;
|
||||
}
|
||||
|
||||
public String getExport() {
|
||||
return export.toString();
|
||||
}
|
||||
|
||||
public ContentMode getContentMode() {
|
||||
return contentMode;
|
||||
}
|
||||
|
||||
public enum ContentMode {
|
||||
PROTECTED( "Export of site names and stored passwords (unless device-private) encrypted with the master key." ) {
|
||||
@Override
|
||||
public String contentForSite(final MPSite site, @Nullable final MasterKey masterKey) {
|
||||
return site.exportContent();
|
||||
}
|
||||
},
|
||||
VISIBLE( "Export of site names and passwords in clear-text." ) {
|
||||
@Override
|
||||
public String contentForSite(final MPSite site, @Nonnull final MasterKey masterKey) {
|
||||
return site.resultFor( Preconditions.checkNotNull( masterKey, "Master key is required when content mode is VISIBLE." ) );
|
||||
}
|
||||
};
|
||||
|
||||
private final String description;
|
||||
|
||||
ContentMode(final String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public abstract String contentForSite(MPSite site, MasterKey masterKey);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 14-12-07
|
||||
*/
|
||||
public class MPSiteResult {
|
||||
|
||||
private final MPSite site;
|
||||
|
||||
public MPSiteResult(final MPSite site) {
|
||||
this.site = site;
|
||||
}
|
||||
|
||||
public MPSite getSite() {
|
||||
return site;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
return (this == obj) || ((obj instanceof MPSiteResult) && Objects.equals( site, ((MPSiteResult) obj).site ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode( site );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return strf( "{MPSiteResult: %s}", site );
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
|
||||
import com.lyndir.lhunath.opal.system.util.NNOperation;
|
||||
import com.lyndir.masterpassword.MPSiteType;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 14-12-07
|
||||
*/
|
||||
public class MPSiteUnmarshaller {
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final Logger logger = Logger.get( MPSite.class );
|
||||
private static final DateTimeFormatter rfc3339 = ISODateTimeFormat.dateTimeNoMillis();
|
||||
private static final Pattern[] unmarshallFormats = {
|
||||
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)? +([^\t]+)\t(.*)" ),
|
||||
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)?(:\\d+)? +([^\t]*)\t *([^\t]+)\t(.*)" ) };
|
||||
private static final Pattern headerFormat = Pattern.compile( "^#\\s*([^:]+): (.*)" );
|
||||
|
||||
private final int importFormat;
|
||||
@SuppressWarnings({ "FieldCanBeLocal", "unused" })
|
||||
private final int mpVersion;
|
||||
@SuppressWarnings({ "FieldCanBeLocal", "unused" })
|
||||
private final boolean clearContent;
|
||||
private final MPUser user;
|
||||
|
||||
@Nonnull
|
||||
public static MPSiteUnmarshaller unmarshall(@Nonnull final File file)
|
||||
throws IOException {
|
||||
try (Reader reader = new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 )) {
|
||||
return unmarshall( CharStreams.readLines( reader ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static MPSiteUnmarshaller unmarshall(@Nonnull final List<String> lines) {
|
||||
byte[] keyID = null;
|
||||
String fullName = null;
|
||||
int mpVersion = 0, importFormat = 0, avatar = 0;
|
||||
boolean clearContent = false, headerStarted = false;
|
||||
MPSiteType defaultType = MPSiteType.GeneratedLong;
|
||||
MPSiteUnmarshaller marshaller = null;
|
||||
final ImmutableList.Builder<MPSite> sites = ImmutableList.builder();
|
||||
|
||||
for (final String line : lines)
|
||||
// Header delimitor.
|
||||
if (line.startsWith( "##" ))
|
||||
if (!headerStarted)
|
||||
// Starts the header.
|
||||
headerStarted = true;
|
||||
else
|
||||
// Ends the header.
|
||||
marshaller = new MPSiteUnmarshaller( importFormat, mpVersion, fullName, keyID, avatar, defaultType, clearContent );
|
||||
|
||||
// Comment.
|
||||
else if (line.startsWith( "#" )) {
|
||||
if (headerStarted && (marshaller == null)) {
|
||||
// In header.
|
||||
Matcher headerMatcher = headerFormat.matcher( line );
|
||||
if (headerMatcher.matches()) {
|
||||
String name = headerMatcher.group( 1 ), value = headerMatcher.group( 2 );
|
||||
if ("Full Name".equalsIgnoreCase( name ) || "User Name".equalsIgnoreCase( name ))
|
||||
fullName = value;
|
||||
else if ("Key ID".equalsIgnoreCase( name ))
|
||||
keyID = CodeUtils.decodeHex( value );
|
||||
else if ("Algorithm".equalsIgnoreCase( name ))
|
||||
mpVersion = ConversionUtils.toIntegerNN( value );
|
||||
else if ("Format".equalsIgnoreCase( name ))
|
||||
importFormat = ConversionUtils.toIntegerNN( value );
|
||||
else if ("Avatar".equalsIgnoreCase( name ))
|
||||
avatar = ConversionUtils.toIntegerNN( value );
|
||||
else if ("Passwords".equalsIgnoreCase( name ))
|
||||
clearContent = "visible".equalsIgnoreCase( value );
|
||||
else if ("Default Type".equalsIgnoreCase( name ))
|
||||
defaultType = MPSiteType.forType( ConversionUtils.toIntegerNN( value ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No comment.
|
||||
else if (marshaller != null)
|
||||
ifNotNull( marshaller.unmarshallSite( line ), new NNOperation<MPSite>() {
|
||||
@Override
|
||||
public void apply(@Nonnull final MPSite site) {
|
||||
sites.add( site );
|
||||
}
|
||||
} );
|
||||
|
||||
return Preconditions.checkNotNull( marshaller, "No full header found in import file." );
|
||||
}
|
||||
|
||||
protected MPSiteUnmarshaller(final int importFormat, final int mpVersion, final String fullName, final byte[] keyID, final int avatar,
|
||||
final MPSiteType defaultType, final boolean clearContent) {
|
||||
this.importFormat = importFormat;
|
||||
this.mpVersion = mpVersion;
|
||||
this.clearContent = clearContent;
|
||||
|
||||
user = new MPUser( fullName, keyID, MasterKey.Version.fromInt( mpVersion ), avatar, defaultType, new DateTime( 0 ) );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MPSite unmarshallSite(@Nonnull final String siteLine) {
|
||||
Matcher siteMatcher = unmarshallFormats[importFormat].matcher( siteLine );
|
||||
if (!siteMatcher.matches())
|
||||
return null;
|
||||
|
||||
MPSite site;
|
||||
switch (importFormat) {
|
||||
case 0:
|
||||
site = new MPSite( user, //
|
||||
MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
|
||||
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
|
||||
siteMatcher.group( 5 ), //
|
||||
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPSite.DEFAULT_COUNTER, //
|
||||
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
|
||||
null, //
|
||||
siteMatcher.group( 6 ) );
|
||||
break;
|
||||
|
||||
case 1:
|
||||
site = new MPSite( user, //
|
||||
MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
|
||||
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
|
||||
siteMatcher.group( 7 ), //
|
||||
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
|
||||
UnsignedInteger.valueOf( siteMatcher.group( 5 ).replace( ":", "" ) ), //
|
||||
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
|
||||
siteMatcher.group( 6 ), //
|
||||
siteMatcher.group( 8 ) );
|
||||
break;
|
||||
|
||||
default:
|
||||
throw logger.bug( "Unexpected format: %d", importFormat );
|
||||
}
|
||||
|
||||
user.addSite( site );
|
||||
return site;
|
||||
}
|
||||
|
||||
public MPUser getUser() {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.masterpassword.MPSiteType;
|
||||
import com.lyndir.masterpassword.MasterKey;
|
||||
import java.util.*;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.*;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 14-12-07
|
||||
*/
|
||||
public class MPUser implements Comparable<MPUser> {
|
||||
|
||||
private final String fullName;
|
||||
private final Collection<MPSite> sites = Sets.newHashSet();
|
||||
|
||||
@Nullable
|
||||
private byte[] keyID;
|
||||
private final MasterKey.Version algorithmVersion;
|
||||
private int avatar;
|
||||
private MPSiteType defaultType;
|
||||
private ReadableInstant lastUsed;
|
||||
|
||||
public MPUser(final String fullName) {
|
||||
this( fullName, null );
|
||||
}
|
||||
|
||||
public MPUser(final String fullName, @Nullable final byte[] keyID) {
|
||||
this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPSiteType.GeneratedLong, new DateTime() );
|
||||
}
|
||||
|
||||
public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar,
|
||||
final MPSiteType defaultType, final ReadableInstant lastUsed) {
|
||||
this.fullName = fullName;
|
||||
this.keyID = (keyID == null)? null: keyID.clone();
|
||||
this.algorithmVersion = algorithmVersion;
|
||||
this.avatar = avatar;
|
||||
this.defaultType = defaultType;
|
||||
this.lastUsed = lastUsed;
|
||||
}
|
||||
|
||||
public Collection<MPSiteResult> findSitesByName(final String query) {
|
||||
ImmutableList.Builder<MPSiteResult> results = ImmutableList.builder();
|
||||
for (final MPSite site : getSites())
|
||||
if (site.getSiteName().startsWith( query ))
|
||||
results.add( new MPSiteResult( site ) );
|
||||
|
||||
return results.build();
|
||||
}
|
||||
|
||||
public void addSite(final MPSite site) {
|
||||
sites.add( site );
|
||||
}
|
||||
|
||||
public void deleteSite(final MPSite site) {
|
||||
sites.remove( site );
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public boolean hasKeyID() {
|
||||
return keyID != null;
|
||||
}
|
||||
|
||||
public String exportKeyID() {
|
||||
return CodeUtils.encodeHex( keyID );
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an authentication attempt against the keyID for this user.
|
||||
*
|
||||
* Note: If this user doesn't have a keyID set yet, authentication will always succeed and the key ID will be set as a result.
|
||||
*
|
||||
* @param masterPassword The password to authenticate with.
|
||||
*
|
||||
* @return The master key for the user if authentication was successful.
|
||||
*
|
||||
* @throws IncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID.
|
||||
*/
|
||||
@Nonnull
|
||||
@SuppressWarnings("MethodCanBeVariableArityMethod")
|
||||
public MasterKey authenticate(final char[] masterPassword)
|
||||
throws IncorrectMasterPasswordException {
|
||||
MasterKey masterKey = MasterKey.create( algorithmVersion, getFullName(), masterPassword );
|
||||
if ((keyID == null) || (keyID.length == 0))
|
||||
keyID = masterKey.getKeyID();
|
||||
else if (!Arrays.equals( masterKey.getKeyID(), keyID ))
|
||||
throw new IncorrectMasterPasswordException( this );
|
||||
|
||||
return masterKey;
|
||||
}
|
||||
|
||||
public int getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(final int avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
public MPSiteType getDefaultType() {
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
public void setDefaultType(final MPSiteType defaultType) {
|
||||
this.defaultType = defaultType;
|
||||
}
|
||||
|
||||
public ReadableInstant getLastUsed() {
|
||||
return lastUsed;
|
||||
}
|
||||
|
||||
public void updateLastUsed() {
|
||||
lastUsed = new Instant();
|
||||
}
|
||||
|
||||
public Iterable<MPSite> getSites() {
|
||||
return sites;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
return (this == obj) || ((obj instanceof MPUser) && Objects.equals( fullName, ((MPUser) obj).fullName ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode( fullName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return strf( "{MPUser: %s}", fullName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final MPUser o) {
|
||||
int comparison = lastUsed.compareTo( o.lastUsed );
|
||||
if (comparison == 0)
|
||||
comparison = fullName.compareTo( o.fullName );
|
||||
|
||||
return comparison;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||
|
||||
import com.google.common.base.*;
|
||||
import com.google.common.collect.*;
|
||||
import com.google.common.io.CharSink;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.masterpassword.MPConstant;
|
||||
import java.io.*;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
* Manages user data stored in user-specific {@code .mpsites} files under {@code .mpw.d}.
|
||||
* @author lhunath, 14-12-07
|
||||
*/
|
||||
public class MPUserFileManager extends MPUserManager {
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final Logger logger = Logger.get( MPUserFileManager.class );
|
||||
private static final MPUserFileManager instance;
|
||||
|
||||
static {
|
||||
String rcDir = System.getenv( MPConstant.env_rcDir );
|
||||
if (rcDir != null)
|
||||
instance = create( new File( rcDir ) );
|
||||
else
|
||||
instance = create( new File( ifNotNullElseNullable( System.getProperty( "user.home" ), System.getenv( "HOME" ) ), ".mpw.d" ) );
|
||||
}
|
||||
|
||||
private final File userFilesDirectory;
|
||||
|
||||
public static MPUserFileManager get() {
|
||||
MPUserManager.instance = instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static MPUserFileManager create(final File userFilesDirectory) {
|
||||
return new MPUserFileManager( userFilesDirectory );
|
||||
}
|
||||
|
||||
protected MPUserFileManager(final File userFilesDirectory) {
|
||||
|
||||
super( unmarshallUsers( userFilesDirectory ) );
|
||||
this.userFilesDirectory = userFilesDirectory;
|
||||
}
|
||||
|
||||
private static Iterable<MPUser> unmarshallUsers(final File userFilesDirectory) {
|
||||
if (!userFilesDirectory.mkdirs() && !userFilesDirectory.isDirectory()) {
|
||||
logger.err( "Couldn't create directory for user files: %s", userFilesDirectory );
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
return FluentIterable.from( listUserFiles( userFilesDirectory ) ).transform( new Function<File, MPUser>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public MPUser apply(@Nullable final File file) {
|
||||
try {
|
||||
return MPSiteUnmarshaller.unmarshall( Preconditions.checkNotNull( file ) ).getUser();
|
||||
}
|
||||
catch (final IOException e) {
|
||||
logger.err( e, "Couldn't read user from: %s", file );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} ).filter( Predicates.notNull() );
|
||||
}
|
||||
|
||||
private static ImmutableList<File> listUserFiles(final File userFilesDirectory) {
|
||||
return ImmutableList.copyOf( ifNotNullElse( userFilesDirectory.listFiles( new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(final File dir, final String name) {
|
||||
return name.endsWith( ".mpsites" );
|
||||
}
|
||||
} ), new File[0] ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUser(final MPUser user) {
|
||||
super.addUser( user );
|
||||
save();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUser(final MPUser user) {
|
||||
super.deleteUser( user );
|
||||
save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the current user state to disk.
|
||||
*/
|
||||
public void save() {
|
||||
// Save existing users.
|
||||
for (final MPUser user : getUsers())
|
||||
try {
|
||||
new CharSink() {
|
||||
@Override
|
||||
public Writer openStream()
|
||||
throws IOException {
|
||||
File mpsitesFile = new File( userFilesDirectory, user.getFullName() + ".mpsites" );
|
||||
return new OutputStreamWriter( new FileOutputStream( mpsitesFile ), Charsets.UTF_8 );
|
||||
}
|
||||
}.write( MPSiteMarshaller.marshallSafe( user ).getExport() );
|
||||
}
|
||||
catch (final IOException e) {
|
||||
logger.err( e, "Unable to save sites for user: %s", user );
|
||||
}
|
||||
|
||||
// Remove deleted users.
|
||||
for (final File userFile : listUserFiles( userFilesDirectory ))
|
||||
if (getUserNamed( userFile.getName().replaceFirst( "\\.mpsites$", "" ) ) == null)
|
||||
if (!userFile.delete())
|
||||
logger.err( "Couldn't delete file: %s", userFile );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The location on the file system where the user models are stored.
|
||||
*/
|
||||
public File getPath() {
|
||||
return userFilesDirectory;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 14-12-05
|
||||
*/
|
||||
public abstract class MPUserManager {
|
||||
|
||||
private final Map<String, MPUser> usersByName = Maps.newHashMap();
|
||||
static MPUserManager instance;
|
||||
|
||||
public static MPUserManager get() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected MPUserManager(final Iterable<MPUser> users) {
|
||||
for (final MPUser user : users)
|
||||
usersByName.put( user.getFullName(), user );
|
||||
}
|
||||
|
||||
public SortedSet<MPUser> getUsers() {
|
||||
return FluentIterable.from( usersByName.values() ).toSortedSet( Ordering.natural() );
|
||||
}
|
||||
|
||||
public MPUser getUserNamed(final String fullName) {
|
||||
return usersByName.get( fullName );
|
||||
}
|
||||
|
||||
public void addUser(final MPUser user) {
|
||||
usersByName.put( user.getFullName(), user );
|
||||
}
|
||||
|
||||
public void deleteUser(final MPUser user) {
|
||||
usersByName.remove( user.getFullName() );
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @author lhunath, 15-02-04
|
||||
*/
|
||||
|
||||
@ParametersAreNonnullByDefault
|
||||
package com.lyndir.masterpassword.model;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -1,13 +0,0 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
description = 'Master Password Test Suite'
|
||||
|
||||
dependencies {
|
||||
compile project(':masterpassword-algorithm')
|
||||
|
||||
testCompile group: 'org.testng', name: 'testng', version:'6.8.5'
|
||||
testCompile group: 'ch.qos.logback', name: 'logback-classic', version:'1.1.2'
|
||||
}
|
||||
test.useTestNG()
|
||||
@@ -1,43 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!-- PROJECT METADATA -->
|
||||
<parent>
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Master Password Test Suite</name>
|
||||
<description>The standard test suite to ensure the Master Password algorithm is operating as it should</description>
|
||||
|
||||
<artifactId>masterpassword-tests</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<!-- DEPENDENCY MANAGEMENT -->
|
||||
<dependencies>
|
||||
|
||||
<!-- PROJECT REFERENCES -->
|
||||
<dependency>
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword-algorithm</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- TESTING -->
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,112 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.lhunath.opal.system.util.NNFunctionNN;
|
||||
import javax.annotation.Nonnull;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
|
||||
public class MasterKeyTest {
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final Logger logger = Logger.get( MasterKeyTest.class );
|
||||
|
||||
@NonNls
|
||||
private MPTestSuite testSuite;
|
||||
|
||||
@BeforeMethod
|
||||
public void setUp()
|
||||
throws Exception {
|
||||
|
||||
testSuite = new MPTestSuite();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncode()
|
||||
throws Exception {
|
||||
|
||||
testSuite.forEach( "testEncode", new NNFunctionNN<MPTests.Case, Boolean>() {
|
||||
@Nonnull
|
||||
@Override
|
||||
public Boolean apply(@Nonnull final MPTests.Case testCase) {
|
||||
MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
|
||||
|
||||
assertEquals(
|
||||
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(),
|
||||
testCase.getSiteVariant(), testCase.getSiteContext() ),
|
||||
testCase.getResult(), "[testEncode] Failed test case: " + testCase );
|
||||
|
||||
return true;
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserName()
|
||||
throws Exception {
|
||||
|
||||
MPTests.Case defaultCase = testSuite.getTests().getDefaultCase();
|
||||
|
||||
assertEquals( MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
|
||||
defaultCase.getFullName(), "[testGetUserName] Failed test case: " + defaultCase );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetKeyID()
|
||||
throws Exception {
|
||||
|
||||
testSuite.forEach( "testGetKeyID", new NNFunctionNN<MPTests.Case, Boolean>() {
|
||||
@Nonnull
|
||||
@Override
|
||||
public Boolean apply(@Nonnull final MPTests.Case testCase) {
|
||||
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
|
||||
|
||||
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ),
|
||||
testCase.getKeyID(), "[testGetKeyID] Failed test case: " + testCase );
|
||||
|
||||
return true;
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidate()
|
||||
throws Exception {
|
||||
|
||||
try {
|
||||
MPTests.Case defaultCase = testSuite.getTests().getDefaultCase();
|
||||
|
||||
MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() );
|
||||
masterKey.invalidate();
|
||||
masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(),
|
||||
defaultCase.getSiteVariant(), defaultCase.getSiteContext() );
|
||||
|
||||
fail( "[testInvalidate] Master key should have been invalidated, but was still usable." );
|
||||
}
|
||||
catch (final IllegalStateException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
9
gradle/.idea/codeStyleSettings.xml
generated
9
gradle/.idea/codeStyleSettings.xml
generated
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectCodeStyleSettingsManager">
|
||||
<option name="PER_PROJECT_SETTINGS">
|
||||
<value />
|
||||
</option>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Lhunath" />
|
||||
</component>
|
||||
</project>
|
||||
7
gradle/.idea/copyright/GPLv3.xml
generated
7
gradle/.idea/copyright/GPLv3.xml
generated
@@ -1,7 +0,0 @@
|
||||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="keyword" value="Copyright|License|WARRANTY" />
|
||||
<option name="myName" value="GPLv3" />
|
||||
<option name="notice" value="This file is part of &#36;project.name. Copyright (c) &#36;today.year. &#36;project.name is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. &#36;project.name is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You can find a copy of the GNU General Public License in the LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>." />
|
||||
</copyright>
|
||||
</component>
|
||||
13
gradle/.idea/copyright/profiles_settings.xml
generated
13
gradle/.idea/copyright/profiles_settings.xml
generated
@@ -1,13 +0,0 @@
|
||||
<component name="CopyrightManager">
|
||||
<settings>
|
||||
<module2copyright>
|
||||
<element module="masterpassword" copyright="Master Password" />
|
||||
</module2copyright>
|
||||
<LanguageOptions name="__TEMPLATE__">
|
||||
<option name="block" value="false" />
|
||||
<option name="separateBefore" value="true" />
|
||||
<option name="separateAfter" value="true" />
|
||||
<option name="filler" value="=" />
|
||||
</LanguageOptions>
|
||||
</settings>
|
||||
</component>
|
||||
@@ -1,9 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="projectProfile" value="Lhunath" />
|
||||
<option name="useProjectProfile" value="false" />
|
||||
<option name="PROJECT_PROFILE" value="Lhunath" />
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
15
gradle/.idea/misc.xml
generated
15
gradle/.idea/misc.xml
generated
@@ -1,12 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/../../opal/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="javax.annotation.Nullable" />
|
||||
<option name="myDefaultNotNull" value="javax.annotation.Nonnull" />
|
||||
@@ -31,10 +24,10 @@
|
||||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/classes" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="10" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ThriftCompiler">
|
||||
<compilers />
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
30
gradle/.idea/runConfigurations/Android.xml
generated
30
gradle/.idea/runConfigurations/Android.xml
generated
@@ -1,30 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Android" type="AndroidRunConfigurationType" factoryName="Android App">
|
||||
<module name="android" />
|
||||
<option name="DEPLOY" value="true" />
|
||||
<option name="ARTIFACT_NAME" value="" />
|
||||
<option name="PM_INSTALL_OPTIONS" value="" />
|
||||
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
||||
<option name="MODE" value="default_activity" />
|
||||
<option name="TARGET_SELECTION_MODE" value="SHOW_DIALOG" />
|
||||
<option name="PREFERRED_AVD" value="" />
|
||||
<option name="CLEAR_LOGCAT" value="false" />
|
||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
|
||||
<option name="FORCE_STOP_RUNNING_APP" value="true" />
|
||||
<option name="DEBUGGER_TYPE" value="Java" />
|
||||
<option name="USE_LAST_SELECTED_DEVICE" value="false" />
|
||||
<option name="PREFERRED_AVD" value="" />
|
||||
<Java />
|
||||
<Profilers>
|
||||
<option name="ENABLE_ADVANCED_PROFILING" value="false" />
|
||||
<option name="GAPID_ENABLED" value="false" />
|
||||
<option name="GAPID_DISABLE_PCS" value="false" />
|
||||
<option name="SUPPORT_LIB_ENABLED" value="true" />
|
||||
<option name="INSTRUMENTATION_ENABLED" value="true" />
|
||||
</Profilers>
|
||||
<option name="DEEP_LINK" value="" />
|
||||
<option name="ACTIVITY_CLASS" value="" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
16
gradle/.idea/runConfigurations/GUI.xml
generated
16
gradle/.idea/runConfigurations/GUI.xml
generated
@@ -1,16 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="GUI" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="com.lyndir.masterpassword.gui.GUI" />
|
||||
<option name="VM_PARAMETERS" value="" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="gui" />
|
||||
<envs />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
29
gradle/.idea/runConfigurations/Tests.xml
generated
29
gradle/.idea/runConfigurations/Tests.xml
generated
@@ -1,29 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Tests" type="TestNG" factoryName="TestNG">
|
||||
<module name="tests" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
<option name="SUITE_NAME" value="" />
|
||||
<option name="PACKAGE_NAME" value="com.lyndir.masterpassword" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="GROUP_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="PACKAGE" />
|
||||
<option name="VM_PARAMETERS" value="-ea" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/../core/java/tests" />
|
||||
<option name="OUTPUT_DIRECTORY" value="" />
|
||||
<option name="ANNOTATION_TYPE" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<option name="USE_DEFAULT_REPORTERS" value="false" />
|
||||
<option name="PROPERTIES_FILE" value="" />
|
||||
<envs />
|
||||
<properties />
|
||||
<listeners />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
3
gradle/.idea/scopes/masterpassword.xml
generated
3
gradle/.idea/scopes/masterpassword.xml
generated
@@ -1,3 +0,0 @@
|
||||
<component name="DependencyValidationManager">
|
||||
<scope name="masterpassword" pattern="com.lyndir.masterpassword.*" />
|
||||
</component>
|
||||
15
gradle/README.md
Normal file
15
gradle/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
To build a release distribution:
|
||||
|
||||
Desktop:
|
||||
|
||||
STORE_PW=$(mpw masterpassword.keystore) KEY_PW_DESKTOP=$(mpw masterpassword-desktop) gradle clean masterpassword-gui:shadowJar
|
||||
|
||||
Android:
|
||||
|
||||
STORE_PW=$(mpw masterpassword.keystore) KEY_PW_ANDROID=$(mpw masterpassword-android) gradle clean masterpassword-android:assembleRelease
|
||||
|
||||
|
||||
Note:
|
||||
|
||||
- At the time of writing, Android does not build with JDK 9+. As such, the above command must be ran with JAVA_HOME pointing to JDK 7-8.
|
||||
- The release keystores are not included in the repository. They are maintained by Maarten Billemont (lhunath@lyndir.com).
|
||||
@@ -1,34 +1,38 @@
|
||||
allprojects {
|
||||
//apply plugin: 'findbugs'
|
||||
apply plugin: 'findbugs'
|
||||
|
||||
group = 'com.lyndir.masterpassword'
|
||||
version = 'GIT-SNAPSHOT'
|
||||
version = '2.7.1'
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
sourceCompatibility = '1.7'
|
||||
targetCompatibility = '1.7'
|
||||
tasks.withType( JavaCompile ) {
|
||||
options.encoding = 'UTF-8'
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
}
|
||||
tasks.withType(FindBugs) {
|
||||
tasks.withType( FindBugs ) {
|
||||
reports {
|
||||
xml.enabled false
|
||||
html.enabled true
|
||||
xml.enabled = false
|
||||
html.enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath group: 'com.android.tools.build', name: 'gradle', version: '2.2.3'
|
||||
classpath group: 'com.android.tools.build', name: 'gradle', version: '3.1.0'
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven { url 'http://maven.lyndir.com' }
|
||||
maven { url 'https://maven.lyndir.com' }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
org.gradle.daemon=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.enableD8.desugaring=true
|
||||
|
||||
BIN
gradle/gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
@@ -1,6 +1,5 @@
|
||||
#Sun Mar 26 09:11:08 EDT 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
|
||||
|
||||
6
gradle/gradlew
vendored
6
gradle/gradlew
vendored
@@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS=""
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
@@ -155,7 +155,7 @@ if $cygwin ; then
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save ( ) {
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
|
||||
@@ -1,31 +1,40 @@
|
||||
class Mpw < Formula
|
||||
homepage "http://masterpasswordapp.com"
|
||||
url "https://ssl.masterpasswordapp.com/mpw-2.1-cli4-0-gf6b2287.tar.gz"
|
||||
sha1 "036b3d8f4bd6f0676ae16e7e9c3de65f6030874f"
|
||||
version "2.1-cli4"
|
||||
desc "Stateless/deterministic password and identity manager"
|
||||
homepage "https://masterpassword.app/"
|
||||
url "https://masterpassword.app/mpw-2.6-cli-5-0-g344771db.tar.gz"
|
||||
version "2.6-cli-5"
|
||||
sha256 "954c07b1713ecc2b30a07bead9c11e6204dd774ca67b5bdf7d2d6ad1c4eec170"
|
||||
revision 1
|
||||
head "https://gitlab.com/MasterPassword/MasterPassword.git"
|
||||
|
||||
depends_on "automake" => :build
|
||||
depends_on "autoconf" => :build
|
||||
depends_on "openssl"
|
||||
|
||||
resource "libscrypt" do
|
||||
url "http://masterpasswordapp.com/libscrypt-b12b554.tar.gz"
|
||||
sha1 "ee871e0f93a786c4e3622561f34565337cfdb815"
|
||||
bottle do
|
||||
cellar :any
|
||||
sha256 "46677cf8649983d5b77103d2ca56d9ad3697808ecc406f626a3462a089f932da" => :high_sierra
|
||||
sha256 "19bf22915b3c534ad3ee6f1dfc20f142d53ae6c0c88757ae2632b7b1daa6667f" => :sierra
|
||||
sha256 "7090c3d31289d2ac5529bd0a6bae2632a36ba7fcd4bb7974248bb36a15f67c7e" => :el_capitan
|
||||
end
|
||||
|
||||
option "without-json-c", "Disable JSON configuration support"
|
||||
option "without-ncurses", "Disable colorized identicon support"
|
||||
|
||||
depends_on "libsodium"
|
||||
depends_on "json-c" => :recommended
|
||||
depends_on "ncurses" => :recommended
|
||||
|
||||
def install
|
||||
resource("libscrypt").stage buildpath/"lib/scrypt"
|
||||
touch "lib/scrypt/.unpacked"
|
||||
cd "platform-independent/cli-c" if build.head?
|
||||
|
||||
ENV["targets"] = "mpw"
|
||||
ENV["mpw_json"] = build.with?("json-c") ? "1" : "0"
|
||||
ENV["mpw_color"] = build.with?("ncurses") ? "1" : "0"
|
||||
|
||||
ENV["targets"] = "mpw mpw-tests"
|
||||
system "./build"
|
||||
system "./mpw-tests"
|
||||
|
||||
system "./mpw-cli-tests"
|
||||
bin.install "mpw"
|
||||
end
|
||||
|
||||
test do
|
||||
assert_equal "Jejr5[RepuSosp",
|
||||
shell_output("mpw -u 'Robert Lee Mitchell' -P 'banana colored duckling' masterpasswordapp.com").strip
|
||||
shell_output("#{bin}/mpw -q -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c1 -a3 'masterpasswordapp.com'").strip
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!-- PROJECT METADATA -->
|
||||
<parent>
|
||||
<groupId>com.lyndir.lhunath</groupId>
|
||||
<artifactId>lyndir</artifactId>
|
||||
<version>1.22</version>
|
||||
</parent>
|
||||
|
||||
<name>Master Password</name>
|
||||
<description>A Java implementation of the Master Password algorithm.</description>
|
||||
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>masterpassword-tests</module>
|
||||
<module>masterpassword-algorithm</module>
|
||||
<module>masterpassword-model</module>
|
||||
<module>masterpassword-cli</module>
|
||||
<module>masterpassword-gui</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<modules>
|
||||
<module>masterpassword-android</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>mod:android</id>
|
||||
<modules>
|
||||
<module>masterpassword-android</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<!-- REMOTE ARTIFACT REPOSITORIES -->
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>lyndir</id>
|
||||
<name>Lyndir Repository</name>
|
||||
<url>http://maven.lyndir.com</url>
|
||||
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
<updatePolicy>never</updatePolicy>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
<updatePolicy>never</updatePolicy>
|
||||
</releases>
|
||||
</repository>
|
||||
</repositories>
|
||||
</project>
|
||||
@@ -1,19 +1,27 @@
|
||||
rootProject.name = 'masterpassword'
|
||||
|
||||
def local = new Properties()
|
||||
def localFile = file( 'local.properties' )
|
||||
localFile.exists() && local.load( localFile.newDataInputStream() )
|
||||
|
||||
include 'masterpassword-core'
|
||||
project( ':masterpassword-core' ).projectDir = new File( '../platform-independent/c/core' )
|
||||
|
||||
include 'masterpassword-algorithm'
|
||||
project(':masterpassword-algorithm').projectDir = new File( '../core/java/algorithm' )
|
||||
project( ':masterpassword-algorithm' ).projectDir = new File( '../platform-independent/java/algorithm' )
|
||||
|
||||
include 'masterpassword-model'
|
||||
project(':masterpassword-model').projectDir = new File( '../core/java/model' )
|
||||
project( ':masterpassword-model' ).projectDir = new File( '../platform-independent/java/model' )
|
||||
|
||||
include 'masterpassword-tests'
|
||||
project(':masterpassword-tests').projectDir = new File( '../core/java/tests' )
|
||||
|
||||
include 'masterpassword-cli'
|
||||
project(':masterpassword-cli').projectDir = new File( '../platform-independent/cli-java' )
|
||||
project( ':masterpassword-tests' ).projectDir = new File( '../platform-independent/java/tests' )
|
||||
|
||||
include 'masterpassword-gui'
|
||||
project(':masterpassword-gui').projectDir = new File( '../platform-independent/gui-java' )
|
||||
project( ':masterpassword-gui' ).projectDir = new File( '../platform-independent/java/gui' )
|
||||
|
||||
include 'masterpassword-android'
|
||||
project(':masterpassword-android').projectDir = new File( '../platform-android' )
|
||||
if (local.containsKey( 'sdk.dir' ) && file( local.getProperty( 'sdk.dir' ) ).exists()) {
|
||||
include 'masterpassword-android'
|
||||
project( ':masterpassword-android' ).projectDir = new File( '../platform-android' )
|
||||
} else {
|
||||
logger.warn( "Skipping masterpassword-android since sdk.dir is not defined in local.properties." )
|
||||
}
|
||||
|
||||
372
lib/bin/build_lib
Executable file
372
lib/bin/build_lib
Executable file
@@ -0,0 +1,372 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Your build script should simply source this script, optionally override any build hooks and then invoke `build`.
|
||||
# The build product should be available under `build-<platform>~/out`, under the library path.
|
||||
#
|
||||
# Hook lifecycle:
|
||||
# - build
|
||||
# - initialize
|
||||
# - needs
|
||||
# - clean & exit (only if script was ran with "clean" argument)
|
||||
# - prepare
|
||||
# - clean
|
||||
# - config
|
||||
# - target
|
||||
# - prepare
|
||||
# - configure
|
||||
# - build
|
||||
# - finalize
|
||||
# - merge
|
||||
# - clean
|
||||
#
|
||||
# You can override any of these hooks to provide a custom implementation or call their underscore variant to delegate to the default implementation.
|
||||
# For example:
|
||||
# target_prepare() { make -s distclean; }
|
||||
# target_configure() { _target_configure "$@" --enable-minimal; }
|
||||
set -e
|
||||
PATH+=:/usr/local/bin
|
||||
|
||||
# needs <binary> ...
|
||||
#
|
||||
# Utility for ensuring all tools needed by the script are installed prior to starting.
|
||||
needs() { _needs "$@"; }
|
||||
_needs() {
|
||||
local failed=0
|
||||
for tool; do
|
||||
hash "$tool" || { echo >&2 "Missing: $tool. Please install this tool."; (( failed++ )); }
|
||||
done
|
||||
|
||||
return $failed
|
||||
}
|
||||
|
||||
# initialize <prefix> <platform>
|
||||
#
|
||||
# The build script invokes this once prior to all other actions if the user wants a clean slate.
|
||||
initialize() { _initialize "$@"; }
|
||||
_initialize() {
|
||||
initialize_needs "$@"
|
||||
}
|
||||
|
||||
# initialize_needs <prefix> <platform>
|
||||
#
|
||||
# Check if all tools needed for the default implementations are available.
|
||||
#
|
||||
# By default, this will check for `libtool` (for libtoolize), `automake` (for aclocal) and `autoconf` (for autoreconf).
|
||||
initialize_needs() { _initialize_needs "$@"; }
|
||||
_initialize_needs() {
|
||||
if [[ $platform = windows ]]; then
|
||||
needs cmd
|
||||
export VSINSTALLDIR="${VSINSTALLDIR:-$(cd "$(cygpath -F 0x002a)/Microsoft Visual Studio"/*/*/Common7/.. && pwd)}"
|
||||
[[ -e "$VSINSTALLDIR/Common7/Tools/VsMSBuildCmd.bat" ]] || { echo >&2 "Missing: msbuild. Please install 'Build Tools for Visual Studio'."; return 1; }
|
||||
else
|
||||
needs libtool automake autoconf
|
||||
fi
|
||||
}
|
||||
|
||||
# clean <prefix> <platform>
|
||||
#
|
||||
# Fully clean up the library code, restoring it to a pristine state.
|
||||
#
|
||||
# By default, this will wipe the prefix, run `make distclean` and `git clean -fdx`.
|
||||
clean() { _clean "$@"; }
|
||||
_clean() {
|
||||
if [[ $platform = windows ]]; then
|
||||
printf '"%%VSINSTALLDIR%%\Common7\Tools\VsMSBuildCmd.bat" && msbuild /t:Clean' > .clean.bat
|
||||
cmd //c .clean.bat
|
||||
rm -f .clean.bat
|
||||
elif [[ -e Makefile ]] && make -s distclean; then :
|
||||
elif [[ -e .git ]] && git clean -fdx; then :
|
||||
fi
|
||||
|
||||
rm -rf "$prefix"
|
||||
}
|
||||
|
||||
# prepare <prefix> <platform> [ <arch> ... ]
|
||||
#
|
||||
# Configure the library for building the <arch>s on this machine.
|
||||
# The build script invokes this once prior to building each of its targets.
|
||||
# The <prefix> has been newly created.
|
||||
#
|
||||
# By default, this will run `autoreconf`.
|
||||
prepare() { _prepare "$@"; }
|
||||
_prepare() {
|
||||
prepare_clean "$@"
|
||||
prepare_config "$@"
|
||||
}
|
||||
|
||||
# prepare_clean <prefix> <platform> [ <arch> ... ]
|
||||
#
|
||||
# Perform any necessary clean-up of the library code prior to building.
|
||||
#
|
||||
# By default, this will wipe and re-create the prefix.
|
||||
prepare_clean() { _prepare_clean "$@"; }
|
||||
_prepare_clean() {
|
||||
local prefix=$1 platform=$2; shift 2
|
||||
|
||||
rm -rf "$prefix"
|
||||
install -d "$prefix/out"
|
||||
}
|
||||
|
||||
# prepare_config <prefix> <platform> [ <arch> ... ]
|
||||
#
|
||||
# Configure the library for building the <arch>s on this machine.
|
||||
#
|
||||
# By default, this will run `autoreconf`.
|
||||
prepare_config() { _prepare_config "$@"; }
|
||||
_prepare_config() {
|
||||
local prefix=$1 platform=$2; shift 2
|
||||
|
||||
[[ -e "$prefix/out/.prepared" ]] && return
|
||||
|
||||
if [[ $platform = windows ]]; then :
|
||||
else
|
||||
autoreconf --verbose --install --force 2> >(sed 's/^\([^:]*\):[0-9]\{1,\}: /\1: /')
|
||||
fi
|
||||
|
||||
touch "$prefix/out/.prepared"
|
||||
}
|
||||
|
||||
# target <prefix> <platform> <arch>
|
||||
#
|
||||
# Build the library for the given <arch> and <platform> into the given <prefix>.
|
||||
# The build script invokes this function when it's ready to build the library's code.
|
||||
# Generic platform-specific environment setup has been done.
|
||||
target() { _target "$@"; }
|
||||
_target() {
|
||||
target_prepare "$@"
|
||||
target_configure "$@"
|
||||
target_build "$@"
|
||||
}
|
||||
|
||||
# target_prepare <prefix> <platform> <arch>
|
||||
#
|
||||
# Prepare the library configuration for building the target.
|
||||
#
|
||||
# By default, this will run `make clean` if a Makefile is found.
|
||||
target_prepare() { _target_prepare "$@"; }
|
||||
_target_prepare() {
|
||||
local prefix=$1 platform=$2 arch=$3; shift 3
|
||||
|
||||
if [[ $platform = windows ]]; then :
|
||||
else
|
||||
[[ ! -e Makefile ]] || make -s clean
|
||||
fi
|
||||
}
|
||||
|
||||
# target_configure <prefix> <platform> <arch> [ <args> ... ]
|
||||
#
|
||||
# Configure the library for building the target.
|
||||
#
|
||||
# By default, this will run `./configure --host=<host> --prefix=<prefix>/<arch> <args>`.
|
||||
# By default, some platform-specific arguments will be passed in as well as
|
||||
# --enable-pic --disable-pie to ensure the resulting library can be linked again.
|
||||
target_configure() { _target_configure "$@"; }
|
||||
_target_configure() {
|
||||
local prefix=$1 platform=$2 arch=$3; shift 3
|
||||
|
||||
case "$platform" in
|
||||
'windows')
|
||||
return
|
||||
;;
|
||||
'android')
|
||||
host=( "$SDKROOT"/*-android* ) host=${host##*/}
|
||||
|
||||
set -- --with-sysroot="$SDKROOT/sysroot" "$@"
|
||||
;;
|
||||
'ios')
|
||||
[[ $arch = *arm* ]] && host=arm || host=$arch
|
||||
host+=-apple
|
||||
|
||||
set -- --disable-shared "$@"
|
||||
;;
|
||||
esac
|
||||
|
||||
./configure ${host:+--host="$host"} --enable-pic --disable-pie --prefix="$prefix/$arch" "$@"
|
||||
}
|
||||
|
||||
# target_build <prefix> <platform> <arch>
|
||||
#
|
||||
# Build the library code for the target.
|
||||
#
|
||||
# By default, this will run `make check install`.
|
||||
target_build() { _target_build "$@"; }
|
||||
_target_build() {
|
||||
local prefix=$1 platform=$2 arch=$3; shift 3
|
||||
|
||||
if [[ $platform = windows ]]; then
|
||||
# I cannot for the life of me figure out how to pass this command directly into cmd.
|
||||
printf '"%%VSINSTALLDIR%%\Common7\Tools\VsMSBuildCmd.bat" && msbuild /t:Rebuild /p:Configuration=ReleaseDLL;Platform=%s;OutDir=%s' "$arch" "$(cygpath -w "${prefix##$PWD/}/$arch/")" > .build.bat
|
||||
cmd //c .build.bat
|
||||
rm -f .build.bat
|
||||
else
|
||||
local cores=$(getconf NPROCESSORS_ONLN 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null ||:)
|
||||
make -j"${cores:-3}" install
|
||||
fi
|
||||
}
|
||||
|
||||
# finalize <prefix> <platform> [ <arch> ... ]
|
||||
#
|
||||
# Prepare the final build product.
|
||||
# The build script invokes this once after a successful build of all targets.
|
||||
finalize() { _finalize "$@"; }
|
||||
_finalize() {
|
||||
finalize_merge "$@"
|
||||
finalize_clean "$@"
|
||||
}
|
||||
|
||||
# finalize_merge <prefix> <platform> [ <arch> ... ]
|
||||
#
|
||||
# Merge all targets into a product the application can use, available at `<prefix>/out`.
|
||||
#
|
||||
# By default, this will copy the headers to `<prefix>/out/include`, install libraries into `<prefix>/out/lib` and mark the output product as successful.
|
||||
finalize_merge() { _finalize_merge "$@"; }
|
||||
_finalize_merge() {
|
||||
local prefix=$1 platform=$2; shift 2
|
||||
local archs=( "$@" )
|
||||
|
||||
[[ -e "$prefix/$archs/include" ]] && mv -f -- "$prefix/$archs/include" "$prefix/out/"
|
||||
|
||||
install -d "$prefix/out/lib"
|
||||
case "$platform" in
|
||||
'linux')
|
||||
for arch in "${archs[@]}"; do
|
||||
install -d "$prefix/out/lib/$arch"
|
||||
install -p "$prefix/$arch/lib/"*.a "$prefix/out/lib/$arch/"
|
||||
done
|
||||
;;
|
||||
'windows')
|
||||
for arch in "${archs[@]}"; do
|
||||
install -d "$prefix/out/lib/$arch"
|
||||
install -p "$prefix/$arch/"*.lib "$prefix/out/lib/$arch/"
|
||||
done
|
||||
;;
|
||||
'macos'|'ios')
|
||||
for lib in "$prefix/$archs/lib/"*; do
|
||||
if lipo -info "$lib" >/dev/null 2>&1; then
|
||||
local lib=("${lib##*/}") libs=("${archs[@]/#/$prefix/}") libs=("${libs[@]/%//lib/$lib}")
|
||||
lipo -create "${libs[@]}" -output "$prefix/out/lib/$lib"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
'android')
|
||||
for arch in "${archs[@]}"; do
|
||||
local abi=$arch
|
||||
case "$arch" in
|
||||
'arm') abi='armeabi-v7a' ;;
|
||||
'arm64') abi='arm64-v8a' ;;
|
||||
esac
|
||||
install -d "$prefix/out/lib/$abi"
|
||||
install -p "$prefix/$arch/lib/"*.so "$prefix/out/lib/$abi"
|
||||
done
|
||||
;;
|
||||
esac
|
||||
|
||||
touch "$prefix/out/.success"
|
||||
}
|
||||
|
||||
# finalize_clean <prefix> [ <arch> ... ]
|
||||
#
|
||||
# Clean up the library after a successful build (eg. housekeeping of temporary files).
|
||||
#
|
||||
# By default, this will run `make clean`.
|
||||
finalize_clean() { _finalize_clean "$@"; }
|
||||
_finalize_clean() {
|
||||
[[ ! -e Makefile ]] || make -s clean
|
||||
}
|
||||
|
||||
# build <name> [<platform>]
|
||||
#
|
||||
# Build the library <name> (found at ../<name>) for platform <platform> (or "host" if unspecified).
|
||||
build() { _build "$@"; }
|
||||
_build() {
|
||||
local name=$1 platform=${2:-host}
|
||||
local path="../$name"
|
||||
[[ $path = /* ]] || path="${BASH_SOURCE%/*}/$path"
|
||||
cd "$path"
|
||||
|
||||
if [[ $platform = host ]]; then
|
||||
case "$(uname -s)" in
|
||||
'Darwin') platform='macos' archs=( "$(uname -m)" ) ;;
|
||||
esac
|
||||
fi
|
||||
if (( ! ${#archs[@]} )); then
|
||||
case "$platform" in
|
||||
'macos') archs=( 'x86_64' ) ;;
|
||||
'ios') archs=( 'i386' 'x86_64' 'armv7' 'armv7s' 'arm64' ) ;;
|
||||
'android') archs=( 'arm' 'arm64' 'x86' 'x86_64' ) ;;
|
||||
'windows') archs=( 'Win32' 'x64' ) ;;
|
||||
*) archs=( 'i386' 'x86_64' ) ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
local prefix="$PWD/build-$platform~"
|
||||
initialize "$prefix" "$platform"
|
||||
|
||||
# "clean" argument wipes the lib clean and exits. If .success exists in prefix output, skip build.
|
||||
if [[ ${BASH_ARGV[@]:(-1)} = clean ]]; then
|
||||
clean "$prefix" "$platform"
|
||||
exit
|
||||
elif [[ -e "$prefix"/out/.success ]]; then
|
||||
echo >&2 "Skipping build for $platform: output product already built successfully."
|
||||
exit
|
||||
fi
|
||||
|
||||
# Prepare the output location and build configuration.
|
||||
prepare "$prefix" "$platform" "${archs[@]}"
|
||||
|
||||
# Repeat the build for each individual architecture.
|
||||
for arch in "${archs[@]}"; do (
|
||||
|
||||
# Set up a base environment for the platform.
|
||||
case "$platform" in
|
||||
'windows')
|
||||
;;
|
||||
'macos')
|
||||
SDKROOT="$(xcrun --show-sdk-path --sdk macosx)"
|
||||
export PATH="$(xcrun --show-sdk-platform-path --sdk macosx)/usr/bin:$PATH"
|
||||
export CFLAGS="-arch $arch -flto -O2 -g -isysroot $SDKROOT -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET:-10.8} $CFLAGS"
|
||||
export LDFLAGS="-arch $arch -flto -isysroot $SDKROOT -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET:-10.8} $LDFLAGS"
|
||||
export CPPFLAGS="$CFLAGS $CPPFLAGS"
|
||||
;;
|
||||
'ios')
|
||||
case "$arch" in
|
||||
*'arm'*)
|
||||
SDKROOT="$(xcrun --show-sdk-path --sdk iphoneos)"
|
||||
export PATH="$(xcrun --show-sdk-platform-path --sdk iphoneos)/usr/bin:$PATH"
|
||||
export CFLAGS="-arch $arch -mthumb -fembed-bitcode -flto -O2 -g -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $CFLAGS"
|
||||
export LDFLAGS="-arch $arch -mthumb -fembed-bitcode -flto -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $LDFLAGS"
|
||||
export CPPFLAGS="$CFLAGS $CPPFLAGS"
|
||||
;;
|
||||
*)
|
||||
SDKROOT="$(xcrun --show-sdk-path --sdk iphonesimulator)"
|
||||
export PATH="$(xcrun --show-sdk-platform-path --sdk iphonesimulator)/usr/bin:$PATH"
|
||||
export CFLAGS="-arch $arch -flto -O2 -g -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $CFLAGS"
|
||||
export LDFLAGS="-arch $arch -flto -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $LDFLAGS"
|
||||
export CPPFLAGS="$CFLAGS $CPPFLAGS"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
'android')
|
||||
[[ -x $ANDROID_NDK_HOME/build/ndk-build ]] || { echo >&2 "Android NDK not found. Please set ANDROID_NDK_HOME."; return 1; }
|
||||
|
||||
SDKROOT="$prefix/$arch/ndk"
|
||||
# Platform 21 is lowest that supports x86_64
|
||||
"$ANDROID_NDK_HOME/build/tools/make-standalone-toolchain.sh" --force --install-dir="$SDKROOT" --platform='android-21' --arch="$arch"
|
||||
export PATH="$SDKROOT/bin:$PATH"
|
||||
export CFLAGS="-O2 -g $CFLAGS"
|
||||
export LDFLAGS="-avoid-version $LDFLAGS"
|
||||
export CC='clang'
|
||||
|
||||
# For GCC:
|
||||
# arm CFLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb" LDFLAGS="-Wl,--fix-cortex-a8"
|
||||
# arm64 CFLAGS="-march=armv8-a"
|
||||
# x86 CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32"
|
||||
# x86_64 CFLAGS="-march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel"
|
||||
;;
|
||||
esac
|
||||
|
||||
target "$prefix" "$platform" "$arch"
|
||||
); done
|
||||
|
||||
finalize "$prefix" "$platform" "${archs[@]}"
|
||||
}
|
||||
8
lib/bin/build_libjson-c-android
Executable file
8
lib/bin/build_libjson-c-android
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
autoreconf() {
|
||||
command autoreconf -Iautoconf-archive/m4 "$@"
|
||||
}
|
||||
|
||||
build libjson-c android
|
||||
8
lib/bin/build_libjson-c-ios
Executable file
8
lib/bin/build_libjson-c-ios
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
autoreconf() {
|
||||
command autoreconf -Iautoconf-archive/m4 "$@"
|
||||
}
|
||||
|
||||
build libjson-c ios
|
||||
8
lib/bin/build_libjson-c-linux
Executable file
8
lib/bin/build_libjson-c-linux
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
autoreconf() {
|
||||
command autoreconf -Iautoconf-archive/m4 "$@"
|
||||
}
|
||||
|
||||
build libjson-c linux
|
||||
8
lib/bin/build_libjson-c-macos
Executable file
8
lib/bin/build_libjson-c-macos
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
autoreconf() {
|
||||
command autoreconf -Iautoconf-archive/m4 "$@"
|
||||
}
|
||||
|
||||
build libjson-c macos
|
||||
8
lib/bin/build_libjson-c-windows
Normal file
8
lib/bin/build_libjson-c-windows
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
autoreconf() {
|
||||
command autoreconf -Iautoconf-archive/m4 "$@"
|
||||
}
|
||||
|
||||
build libjson-c windows
|
||||
4
lib/bin/build_libsodium-android
Executable file
4
lib/bin/build_libsodium-android
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
build libsodium android
|
||||
4
lib/bin/build_libsodium-ios
Executable file
4
lib/bin/build_libsodium-ios
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
build libsodium ios
|
||||
4
lib/bin/build_libsodium-linux
Executable file
4
lib/bin/build_libsodium-linux
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
build libsodium linux
|
||||
4
lib/bin/build_libsodium-macos
Executable file
4
lib/bin/build_libsodium-macos
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
build libsodium macos
|
||||
4
lib/bin/build_libsodium-windows
Normal file
4
lib/bin/build_libsodium-windows
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
source "${BASH_SOURCE%/*}/build_lib"
|
||||
|
||||
build libsodium windows
|
||||
1
lib/libjson-c
Submodule
1
lib/libjson-c
Submodule
Submodule lib/libjson-c added at 3df1f98b4a
1
lib/libsodium
Submodule
1
lib/libsodium
Submodule
Submodule lib/libsodium added at 850edc1175
24
platform-android/CMakeLists.txt
Normal file
24
platform-android/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
project( mpw-core C )
|
||||
cmake_minimum_required( VERSION 3.0.0 )
|
||||
|
||||
add_library( mpw SHARED
|
||||
"${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/base64.c"
|
||||
"${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/aes.c"
|
||||
"${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/mpw-algorithm.c"
|
||||
"${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/mpw-types.c"
|
||||
"${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/mpw-util.c"
|
||||
"${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/mpw-marshal-util.c"
|
||||
"${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/mpw-marshal.c"
|
||||
"${PROJECT_SOURCE_DIR}/../platform-independent/c/core/src/mpw-jni.c" )
|
||||
|
||||
add_library( sodium SHARED IMPORTED )
|
||||
set_target_properties( sodium PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../lib/libsodium/build-android~/out/lib/${ANDROID_ABI}/libsodium.so" )
|
||||
target_include_directories( mpw PRIVATE "${PROJECT_SOURCE_DIR}/../lib/libsodium/build-android~/out/include" )
|
||||
target_compile_definitions( mpw PRIVATE -DMPW_SODIUM=1 )
|
||||
target_link_libraries( mpw PRIVATE sodium )
|
||||
|
||||
add_library( json-c SHARED IMPORTED )
|
||||
set_target_properties( json-c PROPERTIES IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/../lib/libjson-c/build-android~/out/lib/${ANDROID_ABI}/libjson-c.so" )
|
||||
target_include_directories( mpw PRIVATE "${PROJECT_SOURCE_DIR}/../lib/libjson-c/build-android~/out/include" )
|
||||
target_compile_definitions( mpw PRIVATE -DMPW_JSON=1 )
|
||||
target_link_libraries( mpw PRIVATE json-c )
|
||||
@@ -1,13 +0,0 @@
|
||||
To build this module, please ensure you've done the following setup:
|
||||
|
||||
1. Installed the Android SDK and fully downloaded the Android SDK platform 21 in it.
|
||||
2. Set the environment variable ANDROID_HOME in your shell or in ~/.mavenrc to point to the root of your Android SDK install.
|
||||
3. Installed the Android SDK into your Maven's local repository.
|
||||
3a. Clone the maven-android-sdk-deployer available from here: https://github.com/mosabua/maven-android-sdk-deployer.git
|
||||
3b. In the root of this project, run: mvn install -P 5.0
|
||||
|
||||
To build this module:
|
||||
|
||||
1. Build the parent, by going into 'MasterPassword/Java' and running: mvn clean install
|
||||
2. Build this module, by going into 'MasterPassword/Java/masterpassword-android' and running: mvn clean install
|
||||
3. You can then find the APK in: 'MasterPassword/Java/masterpassword-android/target'
|
||||
@@ -2,45 +2,76 @@ apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion '25.0.0'
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
buildToolsVersion '27.0.3'
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'com.lyndir.masterpassword'
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 25
|
||||
versionCode 20401
|
||||
versionName '2.4.1'
|
||||
versionCode 20701
|
||||
versionName '2.7.1'
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path 'CMakeLists.txt'
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs.srcDirs "$rootDir/../lib/libsodium/build-android~/out/lib",
|
||||
"$rootDir/../lib/libjson-c/build-android~/out/lib"
|
||||
}
|
||||
}
|
||||
|
||||
// release with: STORE_PW=$(mpw masterpassword.keystore) KEY_PW=$(mpw masterpassword-android) gradle assembleRelease
|
||||
// release with: STORE_PW=$(mpw masterpassword.keystore) KEY_PW_ANDROID=$(mpw masterpassword-android) gradle masterpassword-android:assembleRelease
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile file( 'masterpassword.keystore' )
|
||||
storePassword System.getenv( 'STORE_PW' )
|
||||
|
||||
keyAlias 'masterpassword-android'
|
||||
keyPassword System.getenv( 'KEY_PW' )
|
||||
keyPassword System.getenv( 'KEY_PW_ANDROID' )
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
if (System.getenv( 'STORE_PW' ) != null)
|
||||
if (System.getenv( 'KEY_PW_ANDROID' ) != null)
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project( ':masterpassword-algorithm' )
|
||||
compile project( ':masterpassword-tests' )
|
||||
api project( ':masterpassword-algorithm' )
|
||||
implementation group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.7-p2'
|
||||
|
||||
compile group: 'org.slf4j', name: 'slf4j-android', version:'1.7.13-underscore'
|
||||
compile group: 'com.jakewharton', name: 'butterknife', version:'8.5.1'
|
||||
annotationProcessor group: 'com.jakewharton', name: 'butterknife-compiler', version:'8.5.1'
|
||||
compile files( 'libs/scrypt-1.4.0-native.jar' )
|
||||
implementation group: 'org.slf4j', name: 'slf4j-android', version: '1.7.13-underscore'
|
||||
implementation group: 'com.jakewharton', name: 'butterknife', version: '8.5.1'
|
||||
annotationProcessor group: 'com.jakewharton', name: 'butterknife-compiler', version: '8.5.1'
|
||||
}
|
||||
|
||||
preBuild {
|
||||
dependsOn task( type: Exec, 'build_libsodium-android', {
|
||||
commandLine 'bash', "$rootDir/../lib/bin/build_libsodium-android"
|
||||
environment 'ANDROID_NDK_HOME', android.ndkDirectory
|
||||
} )
|
||||
dependsOn task( type: Exec, 'build_libjson-c-android', {
|
||||
commandLine 'bash', "$rootDir/../lib/bin/build_libjson-c-android"
|
||||
environment 'ANDROID_NDK_HOME', android.ndkDirectory
|
||||
} )
|
||||
}
|
||||
|
||||
clean {
|
||||
dependsOn task( type: Exec, 'clean_libsodium-android', {
|
||||
commandLine 'bash', "$rootDir/../lib/bin/build_libsodium-android", 'clean'
|
||||
environment 'ANDROID_NDK_HOME', android.ndkDirectory
|
||||
} )
|
||||
dependsOn task( type: Exec, 'clean_libjson-c-android', {
|
||||
commandLine 'bash', "$rootDir/../lib/bin/build_libjson-c-android", 'clean'
|
||||
environment 'ANDROID_NDK_HOME', android.ndkDirectory
|
||||
} )
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,145 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!-- PROJECT METADATA -->
|
||||
<parent>
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Master Password Android</name>
|
||||
<description>An Android application to the Master Password algorithm</description>
|
||||
|
||||
<artifactId>masterpassword-android</artifactId>
|
||||
<packaging>apk</packaging>
|
||||
|
||||
<!-- BUILD CONFIGURATION -->
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
|
||||
<artifactId>android-maven-plugin</artifactId>
|
||||
|
||||
<configuration>
|
||||
<zipalign>
|
||||
<verbose>true</verbose>
|
||||
<skip>false</skip>
|
||||
</zipalign>
|
||||
<sdk>
|
||||
<platform>21</platform>
|
||||
</sdk>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
|
||||
<artifactId>android-maven-plugin</artifactId>
|
||||
|
||||
<configuration>
|
||||
<sign>
|
||||
<debug>false</debug>
|
||||
</sign>
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<id>manifest-update</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>manifest-update</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<manifestVersionCodeUpdateFromVersion>true</manifestVersionCodeUpdateFromVersion>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jarsigner-plugin</artifactId>
|
||||
<version>1.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>signing</id>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
<inherited>true</inherited>
|
||||
<configuration>
|
||||
<archiveDirectory />
|
||||
<includes>
|
||||
<include>target/*.apk</include>
|
||||
</includes>
|
||||
<keystore>release.jks</keystore>
|
||||
<storepass>${env.PASSWORD}</storepass>
|
||||
<keypass>${env.PASSWORD}</keypass>
|
||||
<alias>masterpassword-android</alias>
|
||||
<arguments>
|
||||
<argument>-sigalg</argument><argument>MD5withRSA</argument>
|
||||
<argument>-digestalg</argument><argument>SHA1</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<!-- DEPENDENCY MANAGEMENT -->
|
||||
<dependencies>
|
||||
|
||||
<!-- PROJECT REFERENCES -->
|
||||
<dependency>
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword-algorithm</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword-tests</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- EXTERNAL DEPENDENCIES -->
|
||||
<dependency>
|
||||
<groupId>com.jakewharton</groupId>
|
||||
<artifactId>butterknife</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-android</artifactId>
|
||||
<version>1.7.13-underscore</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>android</groupId>
|
||||
<artifactId>android</artifactId>
|
||||
<version>5.0.1_r2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.lambdaworks</groupId>
|
||||
<artifactId>scrypt</artifactId>
|
||||
<version>1.4.0-android</version>
|
||||
<type>jar</type>
|
||||
<classifier>native</classifier>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
@@ -33,14 +33,14 @@ import android.view.WindowManager;
|
||||
import android.widget.*;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.Executors;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
@@ -50,14 +50,17 @@ public class EmergencyActivity extends Activity {
|
||||
private static final Logger logger = Logger.get( EmergencyActivity.class );
|
||||
private static final ClipData EMPTY_CLIP = new ClipData( new ClipDescription( "", new String[0] ), new ClipData.Item( "" ) );
|
||||
private static final int PASSWORD_NOTIFICATION = 0;
|
||||
public static final int CLIPBOARD_CLEAR_DELAY = 20 /* s */ * MPConstant.MS_PER_S;
|
||||
private static final int CLIPBOARD_CLEAR_DELAY = 20 /* s */ * MPConstants.MS_PER_S;
|
||||
|
||||
private final Preferences preferences = Preferences.get( this );
|
||||
private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
|
||||
private final ImmutableList<MPSiteType> allSiteTypes = ImmutableList.copyOf( MPSiteType.forClass( MPSiteTypeClass.Generated ) );
|
||||
private final ImmutableList<MasterKey.Version> allVersions = ImmutableList.copyOf( MasterKey.Version.values() );
|
||||
private final Preferences preferences = Preferences.get( this );
|
||||
private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(
|
||||
Executors.newSingleThreadExecutor() );
|
||||
private final ImmutableList<MPResultType> allResultTypes = ImmutableList.copyOf(
|
||||
MPResultType.forClass( MPResultTypeClass.Template ) );
|
||||
private final ImmutableList<MPAlgorithm.Version> allVersions = ImmutableList.copyOf( MPAlgorithm.Version.values() );
|
||||
|
||||
private ListenableFuture<MasterKey> masterKeyFuture;
|
||||
@Nullable
|
||||
private MPMasterKey masterKey;
|
||||
|
||||
@BindView(R.id.progressView)
|
||||
ProgressBar progressView;
|
||||
@@ -71,8 +74,8 @@ public class EmergencyActivity extends Activity {
|
||||
@BindView(R.id.siteNameField)
|
||||
EditText siteNameField;
|
||||
|
||||
@BindView(R.id.siteTypeButton)
|
||||
Button siteTypeButton;
|
||||
@BindView(R.id.resultTypeButton)
|
||||
Button resultTypeButton;
|
||||
|
||||
@BindView(R.id.counterField)
|
||||
Button siteCounterButton;
|
||||
@@ -97,7 +100,7 @@ public class EmergencyActivity extends Activity {
|
||||
|
||||
private int id_userName;
|
||||
private int id_masterPassword;
|
||||
private int id_version;
|
||||
@Nullable
|
||||
private String sitePassword;
|
||||
|
||||
public static void start(final Context context) {
|
||||
@@ -127,19 +130,19 @@ public class EmergencyActivity extends Activity {
|
||||
siteNameField.addTextChangedListener( new ValueChangedListener() {
|
||||
@Override
|
||||
void update() {
|
||||
siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) );
|
||||
siteCounterButton.setText( MessageFormat.format( "{0}", UnsignedInteger.ONE ) );
|
||||
updateSitePassword();
|
||||
}
|
||||
} );
|
||||
siteTypeButton.setOnClickListener( new View.OnClickListener() {
|
||||
resultTypeButton.setOnClickListener( new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
@SuppressWarnings("SuspiciousMethodCalls")
|
||||
MPSiteType siteType =
|
||||
allSiteTypes.get( (allSiteTypes.indexOf( siteTypeButton.getTag() ) + 1) % allSiteTypes.size() );
|
||||
preferences.setDefaultSiteType( siteType );
|
||||
siteTypeButton.setTag( siteType );
|
||||
siteTypeButton.setText( siteType.getShortName() );
|
||||
MPResultType resultType =
|
||||
allResultTypes.get( (allResultTypes.indexOf( resultTypeButton.getTag() ) + 1) % allResultTypes.size() );
|
||||
preferences.setDefaultResultType( resultType );
|
||||
resultTypeButton.setTag( resultType );
|
||||
resultTypeButton.setText( resultType.getShortName() );
|
||||
updateSitePassword();
|
||||
}
|
||||
} );
|
||||
@@ -152,11 +155,22 @@ public class EmergencyActivity extends Activity {
|
||||
updateSitePassword();
|
||||
}
|
||||
} );
|
||||
siteCounterButton.setOnLongClickListener( new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(final View v) {
|
||||
if (UnsignedInteger.valueOf( siteCounterButton.getText().toString() ).equals( UnsignedInteger.ONE ))
|
||||
return false;
|
||||
|
||||
siteCounterButton.setText( MessageFormat.format( "{0}", UnsignedInteger.ONE ) );
|
||||
updateSitePassword();
|
||||
return true;
|
||||
}
|
||||
} );
|
||||
siteVersionButton.setOnClickListener( new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
@SuppressWarnings("SuspiciousMethodCalls")
|
||||
MasterKey.Version siteVersion =
|
||||
MPAlgorithm.Version siteVersion =
|
||||
allVersions.get( (allVersions.indexOf( siteVersionButton.getTag() ) + 1) % allVersions.size() );
|
||||
preferences.setDefaultVersion( siteVersion );
|
||||
siteVersionButton.setTag( siteVersion );
|
||||
@@ -175,13 +189,13 @@ public class EmergencyActivity extends Activity {
|
||||
}
|
||||
} );
|
||||
|
||||
fullNameField.setTypeface( Res.get( this ).exo_Thin );
|
||||
fullNameField.setTypeface( Res.get( this ).exo_Thin() );
|
||||
fullNameField.setPaintFlags( fullNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
|
||||
masterPasswordField.setTypeface( Res.get( this ).sourceCodePro_ExtraLight );
|
||||
masterPasswordField.setTypeface( Res.get( this ).sourceCodePro_ExtraLight() );
|
||||
masterPasswordField.setPaintFlags( masterPasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
|
||||
siteNameField.setTypeface( Res.get( this ).exo_Regular );
|
||||
siteNameField.setTypeface( Res.get( this ).exo_Regular() );
|
||||
siteNameField.setPaintFlags( siteNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
|
||||
sitePasswordField.setTypeface( Res.get( this ).sourceCodePro_Black );
|
||||
sitePasswordField.setTypeface( Res.get( this ).sourceCodePro_Black() );
|
||||
sitePasswordField.setPaintFlags( sitePasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
|
||||
|
||||
rememberFullNameField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
|
||||
@@ -213,20 +227,20 @@ public class EmergencyActivity extends Activity {
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
|
||||
// FIXME: MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
|
||||
|
||||
fullNameField.setText( preferences.getFullName() );
|
||||
rememberFullNameField.setChecked( preferences.isRememberFullName() );
|
||||
forgetPasswordField.setChecked( preferences.isForgetPassword() );
|
||||
maskPasswordField.setChecked( preferences.isMaskPassword() );
|
||||
sitePasswordField.setTransformationMethod( preferences.isMaskPassword()? new PasswordTransformationMethod(): null );
|
||||
MPSiteType defaultSiteType = preferences.getDefaultSiteType();
|
||||
siteTypeButton.setTag( defaultSiteType );
|
||||
siteTypeButton.setText( defaultSiteType.getShortName() );
|
||||
MasterKey.Version defaultVersion = preferences.getDefaultVersion();
|
||||
MPResultType defaultResultType = preferences.getDefaultResultType();
|
||||
resultTypeButton.setTag( defaultResultType );
|
||||
resultTypeButton.setText( defaultResultType.getShortName() );
|
||||
MPAlgorithm.Version defaultVersion = preferences.getDefaultVersion();
|
||||
siteVersionButton.setTag( defaultVersion );
|
||||
siteVersionButton.setText( defaultVersion.name() );
|
||||
siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) );
|
||||
siteCounterButton.setText( MessageFormat.format( "{0}", UnsignedInteger.ONE ) );
|
||||
|
||||
if (TextUtils.isEmpty( fullNameField.getText() ))
|
||||
fullNameField.requestFocus();
|
||||
@@ -241,10 +255,8 @@ public class EmergencyActivity extends Activity {
|
||||
if (preferences.isForgetPassword()) {
|
||||
synchronized (this) {
|
||||
id_userName = id_masterPassword = 0;
|
||||
if (masterKeyFuture != null) {
|
||||
masterKeyFuture.cancel( true );
|
||||
masterKeyFuture = null;
|
||||
}
|
||||
if (masterKey != null)
|
||||
masterKey = null;
|
||||
|
||||
masterPasswordField.setText( "" );
|
||||
}
|
||||
@@ -258,25 +270,19 @@ public class EmergencyActivity extends Activity {
|
||||
}
|
||||
|
||||
private synchronized void updateMasterKey() {
|
||||
final String fullName = fullNameField.getText().toString();
|
||||
final char[] masterPassword = masterPasswordField.getText().toString().toCharArray();
|
||||
final MasterKey.Version version = (MasterKey.Version) siteVersionButton.getTag();
|
||||
String fullName = fullNameField.getText().toString();
|
||||
char[] masterPassword = masterPasswordField.getText().toString().toCharArray();
|
||||
if ((id_userName == fullName.hashCode())
|
||||
&& (id_masterPassword == Arrays.hashCode( masterPassword ))
|
||||
&& (id_version == version.ordinal()))
|
||||
if ((masterKeyFuture != null) && !masterKeyFuture.isCancelled())
|
||||
&& (id_masterPassword == Arrays.hashCode( masterPassword )))
|
||||
if (masterKey != null)
|
||||
return;
|
||||
|
||||
id_userName = fullName.hashCode();
|
||||
id_masterPassword = Arrays.hashCode( masterPassword );
|
||||
id_version = version.ordinal();
|
||||
|
||||
if (preferences.isRememberFullName())
|
||||
preferences.setFullName( fullName );
|
||||
|
||||
if (masterKeyFuture != null)
|
||||
masterKeyFuture.cancel( true );
|
||||
|
||||
if (fullName.isEmpty() || (masterPassword.length == 0)) {
|
||||
sitePasswordField.setText( "" );
|
||||
progressView.setVisibility( View.INVISIBLE );
|
||||
@@ -285,43 +291,21 @@ public class EmergencyActivity extends Activity {
|
||||
|
||||
sitePasswordField.setText( "" );
|
||||
progressView.setVisibility( View.VISIBLE );
|
||||
(masterKeyFuture = executor.submit( new Callable<MasterKey>() {
|
||||
@Override
|
||||
public MasterKey call()
|
||||
throws Exception {
|
||||
try {
|
||||
return MasterKey.create( version, fullName, masterPassword );
|
||||
}
|
||||
catch (final Exception e) {
|
||||
sitePasswordField.setText( "" );
|
||||
progressView.setVisibility( View.INVISIBLE );
|
||||
logger.err( e, "While generating master key." );
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} )).addListener( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
runOnUiThread( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateSitePassword();
|
||||
}
|
||||
} );
|
||||
}
|
||||
}, executor );
|
||||
masterKey = new MPMasterKey( fullName, masterPassword );
|
||||
updateSitePassword();
|
||||
}
|
||||
|
||||
private void updateSitePassword() {
|
||||
final String siteName = siteNameField.getText().toString();
|
||||
final MPSiteType type = (MPSiteType) siteTypeButton.getTag();
|
||||
final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
|
||||
final String siteName = siteNameField.getText().toString();
|
||||
final MPResultType type = (MPResultType) resultTypeButton.getTag();
|
||||
final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
|
||||
final MPAlgorithm.Version version = (MPAlgorithm.Version) siteVersionButton.getTag();
|
||||
|
||||
if ((masterKeyFuture == null) || siteName.isEmpty() || (type == null)) {
|
||||
if ((masterKey == null) || siteName.isEmpty() || (type == null)) {
|
||||
sitePasswordField.setText( "" );
|
||||
progressView.setVisibility( View.INVISIBLE );
|
||||
|
||||
if (masterKeyFuture == null)
|
||||
if (masterKey == null)
|
||||
updateMasterKey();
|
||||
return;
|
||||
}
|
||||
@@ -332,7 +316,8 @@ public class EmergencyActivity extends Activity {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
sitePassword = masterKeyFuture.get().encode( siteName, type, counter, MPSiteVariant.Password, null );
|
||||
sitePassword = masterKey.siteResult( siteName, version.getAlgorithm(), counter,
|
||||
MPKeyPurpose.Authentication, null, type, null );
|
||||
|
||||
runOnUiThread( new Runnable() {
|
||||
@Override
|
||||
@@ -342,66 +327,59 @@ public class EmergencyActivity extends Activity {
|
||||
}
|
||||
} );
|
||||
}
|
||||
catch (final InterruptedException ignored) {
|
||||
catch (final MPKeyUnavailableException ignored) {
|
||||
sitePasswordField.setText( "" );
|
||||
progressView.setVisibility( View.INVISIBLE );
|
||||
}
|
||||
catch (final ExecutionException e) {
|
||||
catch (final MPAlgorithmException e) {
|
||||
sitePasswordField.setText( "" );
|
||||
progressView.setVisibility( View.INVISIBLE );
|
||||
logger.err( e, "While generating site password." );
|
||||
throw Throwables.propagate( e );
|
||||
}
|
||||
catch (final RuntimeException e) {
|
||||
sitePasswordField.setText( "" );
|
||||
progressView.setVisibility( View.INVISIBLE );
|
||||
logger.err( e, "While generating site password." );
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
public void integrityTests(final View view) {
|
||||
if (masterKeyFuture != null) {
|
||||
masterKeyFuture.cancel( true );
|
||||
masterKeyFuture = null;
|
||||
}
|
||||
TestActivity.startNoSkip( this );
|
||||
}
|
||||
|
||||
public void copySitePassword(final View view) {
|
||||
final String currentSitePassword = sitePassword;
|
||||
if (TextUtils.isEmpty( currentSitePassword ))
|
||||
return;
|
||||
|
||||
final ClipboardManager clipboardManager = (ClipboardManager) getSystemService( CLIPBOARD_SERVICE );
|
||||
final ClipboardManager clipboardManager = (ClipboardManager) getSystemService( CLIPBOARD_SERVICE );
|
||||
final NotificationManager notificationManager = (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
|
||||
if (clipboardManager == null)
|
||||
return;
|
||||
|
||||
String title = strf( "Password for %s", siteNameField.getText() );
|
||||
String title = strf( "Password for %s", siteNameField.getText() );
|
||||
ClipDescription description = new ClipDescription( title, new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN } );
|
||||
clipboardManager.setPrimaryClip( new ClipData( description, new ClipData.Item( currentSitePassword ) ) );
|
||||
|
||||
Notification.Builder notificationBuilder = new Notification.Builder( this ).setContentTitle( title )
|
||||
.setContentText( "Paste the password into your app." )
|
||||
.setSmallIcon( R.drawable.icon )
|
||||
.setAutoCancel( true );
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
notificationBuilder.setVisibility( Notification.VISIBILITY_SECRET )
|
||||
.setCategory( Notification.CATEGORY_RECOMMENDATION )
|
||||
.setLocalOnly( true );
|
||||
notificationManager.notify( PASSWORD_NOTIFICATION, notificationBuilder.build() );
|
||||
if (notificationManager != null) {
|
||||
Notification.Builder notificationBuilder = new Notification.Builder( this ).setContentTitle( title )
|
||||
.setContentText(
|
||||
"Paste the password into your app." )
|
||||
.setSmallIcon( R.drawable.icon )
|
||||
.setAutoCancel( true );
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
notificationBuilder.setVisibility( Notification.VISIBILITY_SECRET )
|
||||
.setCategory( Notification.CATEGORY_RECOMMENDATION )
|
||||
.setLocalOnly( true );
|
||||
notificationManager.notify( PASSWORD_NOTIFICATION, notificationBuilder.build() );
|
||||
}
|
||||
|
||||
final Timer timer = new Timer();
|
||||
timer.schedule( new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
ClipData clip = clipboardManager.getPrimaryClip();
|
||||
for (int i = 0; i < clip.getItemCount(); ++i)
|
||||
if (currentSitePassword.equals( clip.getItemAt( i ).coerceToText( EmergencyActivity.this ) )) {
|
||||
if (currentSitePassword.contentEquals( clip.getItemAt( i ).coerceToText( EmergencyActivity.this ) )) {
|
||||
clipboardManager.setPrimaryClip( EMPTY_CLIP );
|
||||
break;
|
||||
}
|
||||
notificationManager.cancel( PASSWORD_NOTIFICATION );
|
||||
|
||||
if (notificationManager != null)
|
||||
notificationManager.cancel( PASSWORD_NOTIFICATION );
|
||||
timer.cancel();
|
||||
}
|
||||
}, CLIPBOARD_CLEAR_DELAY );
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
/**
|
||||
* @author lhunath, 2018-06-10
|
||||
*/
|
||||
public class MPConstants {
|
||||
|
||||
public static final int MS_PER_S = 1000;
|
||||
}
|
||||
@@ -22,8 +22,10 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
|
||||
/**
|
||||
@@ -33,7 +35,7 @@ public class MainThreadExecutor extends AbstractExecutorService {
|
||||
|
||||
private final Handler mHandler = new Handler( Looper.getMainLooper() );
|
||||
private final Set<Runnable> commands = Sets.newLinkedHashSet();
|
||||
private boolean shutdown;
|
||||
private boolean shutdown;
|
||||
|
||||
@Override
|
||||
public void execute(final Runnable command) {
|
||||
@@ -63,6 +65,7 @@ public class MainThreadExecutor extends AbstractExecutorService {
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
shutdown = true;
|
||||
|
||||
@@ -32,15 +32,15 @@ import javax.annotation.Nullable;
|
||||
*/
|
||||
public final class Preferences {
|
||||
|
||||
private static final String PREF_TESTS_PASSED = "integrityTestsPassed";
|
||||
private static final String PREF_NATIVE_KDF = "nativeKDF";
|
||||
private static final String PREF_REMEMBER_FULL_NAME = "rememberFullName";
|
||||
private static final String PREF_FORGET_PASSWORD = "forgetPassword";
|
||||
private static final String PREF_MASK_PASSWORD = "maskPassword";
|
||||
private static final String PREF_FULL_NAME = "fullName";
|
||||
private static final String PREF_SITE_TYPE = "siteType";
|
||||
private static final String PREF_ALGORITHM_VERSION = "algorithmVersion";
|
||||
private static Preferences instance;
|
||||
private static final String PREF_TESTS_PASSED = "integrityTestsPassed";
|
||||
private static final String PREF_NATIVE_KDF = "nativeKDF";
|
||||
private static final String PREF_REMEMBER_FULL_NAME = "rememberFullName";
|
||||
private static final String PREF_FORGET_PASSWORD = "forgetPassword";
|
||||
private static final String PREF_MASK_PASSWORD = "maskPassword";
|
||||
private static final String PREF_FULL_NAME = "fullName";
|
||||
private static final String PREF_RESULT_TYPE = "resultType";
|
||||
private static final String PREF_ALGORITHM_VERSION = "algorithmVersion";
|
||||
private static Preferences instance;
|
||||
|
||||
private Context context;
|
||||
@Nullable
|
||||
@@ -74,7 +74,7 @@ public final class Preferences {
|
||||
}
|
||||
|
||||
public boolean isAllowNativeKDF() {
|
||||
return prefs().getBoolean( PREF_NATIVE_KDF, MasterKey.isAllowNativeByDefault() );
|
||||
return prefs().getBoolean( PREF_NATIVE_KDF, true );
|
||||
}
|
||||
|
||||
public boolean setTestsPassed(final Set<String> value) {
|
||||
@@ -86,7 +86,7 @@ public final class Preferences {
|
||||
}
|
||||
|
||||
public Set<String> getTestsPassed() {
|
||||
return prefs().getStringSet( PREF_TESTS_PASSED, ImmutableSet.<String>of() );
|
||||
return prefs().getStringSet( PREF_TESTS_PASSED, ImmutableSet.of() );
|
||||
}
|
||||
|
||||
public boolean setRememberFullName(final boolean enabled) {
|
||||
@@ -138,20 +138,21 @@ public final class Preferences {
|
||||
return prefs().getString( PREF_FULL_NAME, "" );
|
||||
}
|
||||
|
||||
public boolean setDefaultSiteType(@Nonnull final MPSiteType value) {
|
||||
if (getDefaultSiteType() == value)
|
||||
public boolean setDefaultResultType(final MPResultType value) {
|
||||
if (getDefaultResultType() == value)
|
||||
return false;
|
||||
|
||||
prefs().edit().putInt( PREF_SITE_TYPE, value.ordinal() ).apply();
|
||||
prefs().edit().putInt( PREF_RESULT_TYPE, value.ordinal() ).apply();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public MPSiteType getDefaultSiteType() {
|
||||
return MPSiteType.values()[prefs().getInt( PREF_SITE_TYPE, MPSiteType.GeneratedLong.ordinal() )];
|
||||
public MPResultType getDefaultResultType() {
|
||||
return MPResultType.values()[
|
||||
prefs().getInt( PREF_RESULT_TYPE, getDefaultVersion().getAlgorithm().mpw_default_result_type().ordinal() )];
|
||||
}
|
||||
|
||||
public boolean setDefaultVersion(@Nonnull final MasterKey.Version value) {
|
||||
public boolean setDefaultVersion(final MPAlgorithm.Version value) {
|
||||
if (getDefaultVersion() == value)
|
||||
return false;
|
||||
|
||||
@@ -160,7 +161,8 @@ public final class Preferences {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public MasterKey.Version getDefaultVersion() {
|
||||
return MasterKey.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MasterKey.Version.CURRENT.ordinal() )];
|
||||
public MPAlgorithm.Version getDefaultVersion() {
|
||||
return MPAlgorithm.Version.values()[
|
||||
prefs().getInt( PREF_ALGORITHM_VERSION, MPAlgorithm.Version.CURRENT.ordinal() )];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,21 +19,21 @@
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Typeface;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 2014-08-25
|
||||
*/
|
||||
@SuppressWarnings("NewMethodNamingConvention")
|
||||
public final class Res {
|
||||
|
||||
public final Typeface sourceCodePro_Black;
|
||||
public final Typeface sourceCodePro_ExtraLight;
|
||||
public final Typeface exo_Bold;
|
||||
public final Typeface exo_ExtraBold;
|
||||
public final Typeface exo_Regular;
|
||||
public final Typeface exo_Thin;
|
||||
private final Typeface sourceCodePro_Black;
|
||||
private final Typeface sourceCodePro_ExtraLight;
|
||||
private final Typeface exo_Bold;
|
||||
private final Typeface exo_ExtraBold;
|
||||
private final Typeface exo_Regular;
|
||||
private final Typeface exo_Thin;
|
||||
|
||||
private static Res res;
|
||||
|
||||
@@ -54,4 +54,28 @@ public final class Res {
|
||||
exo_Regular = Typeface.createFromAsset( context.getResources().getAssets(), "Exo2.0-Regular.otf" );
|
||||
exo_Thin = Typeface.createFromAsset( context.getResources().getAssets(), "Exo2.0-Thin.otf" );
|
||||
}
|
||||
|
||||
public Typeface sourceCodePro_Black() {
|
||||
return sourceCodePro_Black;
|
||||
}
|
||||
|
||||
public Typeface sourceCodePro_ExtraLight() {
|
||||
return sourceCodePro_ExtraLight;
|
||||
}
|
||||
|
||||
public Typeface exo_Bold() {
|
||||
return exo_Bold;
|
||||
}
|
||||
|
||||
public Typeface exo_ExtraBold() {
|
||||
return exo_ExtraBold;
|
||||
}
|
||||
|
||||
public Typeface exo_Regular() {
|
||||
return exo_Regular;
|
||||
}
|
||||
|
||||
public Typeface exo_Thin() {
|
||||
return exo_Thin;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||
|
||||
import android.app.*;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.*;
|
||||
import android.view.View;
|
||||
import android.widget.*;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import com.google.common.base.*;
|
||||
import com.google.common.collect.*;
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.util.concurrent.*;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
public class TestActivity extends Activity implements MPTestSuite.Listener {
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final Logger logger = Logger.get( TestActivity.class );
|
||||
|
||||
private final Preferences preferences = Preferences.get( this );
|
||||
private final ListeningExecutorService backgroundExecutor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
|
||||
private final ListeningExecutorService mainExecutor = MoreExecutors.listeningDecorator( new MainThreadExecutor() );
|
||||
|
||||
@BindView(R.id.progressView)
|
||||
ProgressBar progressView;
|
||||
|
||||
@BindView(R.id.statusView)
|
||||
TextView statusView;
|
||||
|
||||
@BindView(R.id.logView)
|
||||
TextView logView;
|
||||
|
||||
@BindView(R.id.actionButton)
|
||||
Button actionButton;
|
||||
|
||||
@BindView(R.id.nativeKDFField)
|
||||
CheckBox nativeKDFField;
|
||||
|
||||
private MPTestSuite testSuite;
|
||||
private ListenableFuture<Boolean> testFuture;
|
||||
private Runnable action;
|
||||
private ImmutableSet<String> testNames;
|
||||
|
||||
public static void startNoSkip(final Context context) {
|
||||
context.startActivity( new Intent( context, TestActivity.class ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate( savedInstanceState );
|
||||
|
||||
setContentView( R.layout.activity_test );
|
||||
ButterKnife.bind( this );
|
||||
|
||||
nativeKDFField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
|
||||
preferences.setNativeKDFEnabled( isChecked );
|
||||
MasterKey.setAllowNativeByDefault( isChecked );
|
||||
}
|
||||
} );
|
||||
|
||||
try {
|
||||
setStatus( 0, 0, null );
|
||||
testSuite = new MPTestSuite();
|
||||
testSuite.setListener( this );
|
||||
testNames = FluentIterable.from( testSuite.getTests().getCases() ).transform(
|
||||
new Function<MPTests.Case, String>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public String apply(@Nullable final MPTests.Case input) {
|
||||
return (input == null)? null: input.identifier;
|
||||
}
|
||||
} ).filter( Predicates.notNull() ).toSet();
|
||||
}
|
||||
catch (final MPTestSuite.UnavailableException e) {
|
||||
logger.err( e, "While loading test suite" );
|
||||
setStatus( R.string.tests_unavailable, R.string.tests_btn_unavailable, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
nativeKDFField.setChecked( preferences.isAllowNativeKDF() );
|
||||
|
||||
if (testFuture == null)
|
||||
startTestSuite();
|
||||
}
|
||||
|
||||
private void startTestSuite() {
|
||||
if (testFuture != null)
|
||||
testFuture.cancel( true );
|
||||
|
||||
MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
|
||||
|
||||
setStatus( R.string.tests_testing, R.string.tests_btn_testing, null );
|
||||
Futures.addCallback( testFuture = backgroundExecutor.submit( testSuite ), new FutureCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable final Boolean result) {
|
||||
if ((result != null) && result)
|
||||
setStatus( R.string.tests_passed, R.string.tests_btn_passed, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
preferences.setTestsPassed( testNames );
|
||||
finish();
|
||||
}
|
||||
} );
|
||||
else
|
||||
setStatus( R.string.tests_failed, R.string.tests_btn_failed, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
startTestSuite();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
logger.err( t, "While running test suite" );
|
||||
setStatus( R.string.tests_failed, R.string.tests_btn_failed, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
} );
|
||||
}
|
||||
}, mainExecutor );
|
||||
}
|
||||
|
||||
public void onAction(final View v) {
|
||||
if (action != null)
|
||||
action.run();
|
||||
}
|
||||
|
||||
private void setStatus(final int statusId, final int buttonId, @Nullable final Runnable action) {
|
||||
this.action = action;
|
||||
|
||||
if (statusId == 0)
|
||||
statusView.setText( null );
|
||||
else
|
||||
statusView.setText( statusId );
|
||||
|
||||
if (buttonId == 0)
|
||||
actionButton.setText( null );
|
||||
else
|
||||
actionButton.setText( buttonId );
|
||||
actionButton.setEnabled( action != null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void progress(final int current, final int max, final String messageFormat, final Object... args) {
|
||||
runOnUiThread( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logView.append( strf( "%n" + messageFormat, args ) );
|
||||
|
||||
progressView.setMax( max );
|
||||
progressView.setProgress( current );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
android:id="@id/sitePasswordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nextFocusForward="@+id/siteTypeButton"
|
||||
android:nextFocusForward="@+id/resultTypeButton"
|
||||
android:gravity="center"
|
||||
android:background="@android:color/transparent"
|
||||
android:textColor="#FFFFFF"
|
||||
@@ -157,7 +157,7 @@
|
||||
android:gravity="center">
|
||||
|
||||
<Button
|
||||
android:id="@id/siteTypeButton"
|
||||
android:id="@id/resultTypeButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
@@ -175,12 +175,12 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@id/siteTypeButton"
|
||||
android:labelFor="@id/resultTypeButton"
|
||||
android:gravity="center"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:text="@string/siteType_hint" />
|
||||
android:text="@string/resultType_hint" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -253,15 +253,6 @@
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/btn_tests"
|
||||
android:onClick="integrityTests"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="0dp"
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/img_stats" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="8dp"
|
||||
tools:max="100"
|
||||
tools:progress="80"
|
||||
style="?android:progressBarStyleHorizontal" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@id/sitePasswordField"
|
||||
android:gravity="center"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:text="@string/tests_testing" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/logView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginTop="20dp"
|
||||
android:gravity="bottom"
|
||||
android:background="@android:color/transparent"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="9sp"
|
||||
android:textColor="@android:color/tertiary_text_dark" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/actionButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:enabled="false"
|
||||
android:text="@string/tests_btn_testing"
|
||||
android:onClick="onAction" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/nativeKDFField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:text="@string/nativeKDF" />
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
@@ -8,7 +8,7 @@
|
||||
<string name="masterPassword_hint">Your master password</string>
|
||||
<string name="siteName_hint">eg. google.com</string>
|
||||
<string name="sitePassword_hint">Tap to copy</string>
|
||||
<string name="siteType_hint">Type</string>
|
||||
<string name="resultType_hint">Type</string>
|
||||
<string name="siteCounter_hint">Counter</string>
|
||||
<string name="siteVersion_hint">Algorithm</string>
|
||||
<string name="empty" />
|
||||
|
||||
2
platform-darwin/External/InAppSettingsKit
vendored
2
platform-darwin/External/InAppSettingsKit
vendored
Submodule platform-darwin/External/InAppSettingsKit updated: b58b72563a...2dcb598d18
2
platform-darwin/External/Pearl
vendored
2
platform-darwin/External/Pearl
vendored
Submodule platform-darwin/External/Pearl updated: 3ceb601ba4...b713577cd6
1
platform-darwin/External/libsodium
vendored
1
platform-darwin/External/libsodium
vendored
Submodule platform-darwin/External/libsodium deleted from 4809639ae1
184
platform-darwin/MasterPassword-JNI.xcodeproj/project.pbxproj
Normal file
184
platform-darwin/MasterPassword-JNI.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,184 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
DA1554DE20B3928E00EA92C5 /* aes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aes.c; sourceTree = "<group>"; };
|
||||
DA1554DF20B3928E00EA92C5 /* mpw-algorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-algorithm.h"; sourceTree = "<group>"; };
|
||||
DA1554E020B3928E00EA92C5 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = "<group>"; };
|
||||
DA1554E120B3928E00EA92C5 /* mpw-marshal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-marshal.c"; sourceTree = "<group>"; };
|
||||
DA1554E220B3928E00EA92C5 /* mpw-algorithm_v2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v2.c"; sourceTree = "<group>"; };
|
||||
DA1554E320B3928E00EA92C5 /* mpw-types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-types.c"; sourceTree = "<group>"; };
|
||||
DA1554E420B3928E00EA92C5 /* mpw-marshal-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-marshal-util.c"; sourceTree = "<group>"; };
|
||||
DA1554E520B3928E00EA92C5 /* mpw-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-util.c"; sourceTree = "<group>"; };
|
||||
DA1554E620B3928E00EA92C5 /* mpw-algorithm_v1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v1.c"; sourceTree = "<group>"; };
|
||||
DA1554E720B3928E00EA92C5 /* aes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes.h; sourceTree = "<group>"; };
|
||||
DA1554E820B3928E00EA92C5 /* mpw-marshal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-marshal.h"; sourceTree = "<group>"; };
|
||||
DA1554E920B3928E00EA92C5 /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = "<group>"; };
|
||||
DA1554EA20B3928E00EA92C5 /* mpw-algorithm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm.c"; sourceTree = "<group>"; };
|
||||
DA1554EB20B3928E00EA92C5 /* mpw-algorithm_v0.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v0.c"; sourceTree = "<group>"; };
|
||||
DA1554EC20B3928E00EA92C5 /* mpw-types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-types.h"; sourceTree = "<group>"; };
|
||||
DA1554ED20B3928E00EA92C5 /* mpw-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-util.h"; sourceTree = "<group>"; };
|
||||
DA1554EE20B3928E00EA92C5 /* mpw-marshal-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-marshal-util.h"; sourceTree = "<group>"; };
|
||||
DA1554EF20B3928E00EA92C5 /* mpw-algorithm_v3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v3.c"; sourceTree = "<group>"; };
|
||||
DA1F44B020BCF0C200957B45 /* mpw-jni.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-jni.h"; sourceTree = "<group>"; };
|
||||
DA1F44B120BCF0C200957B45 /* mpw-jni.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-jni.c"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
DA1554D220B3924000EA92C5 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA1554DD20B3928E00EA92C5 /* core */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA1554DD20B3928E00EA92C5 /* core */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA1554DE20B3928E00EA92C5 /* aes.c */,
|
||||
DA1554E720B3928E00EA92C5 /* aes.h */,
|
||||
DA1554E920B3928E00EA92C5 /* base64.c */,
|
||||
DA1554E020B3928E00EA92C5 /* base64.h */,
|
||||
DA1554EB20B3928E00EA92C5 /* mpw-algorithm_v0.c */,
|
||||
DA1554E620B3928E00EA92C5 /* mpw-algorithm_v1.c */,
|
||||
DA1554E220B3928E00EA92C5 /* mpw-algorithm_v2.c */,
|
||||
DA1554EF20B3928E00EA92C5 /* mpw-algorithm_v3.c */,
|
||||
DA1554EA20B3928E00EA92C5 /* mpw-algorithm.c */,
|
||||
DA1554DF20B3928E00EA92C5 /* mpw-algorithm.h */,
|
||||
DA1F44B120BCF0C200957B45 /* mpw-jni.c */,
|
||||
DA1F44B020BCF0C200957B45 /* mpw-jni.h */,
|
||||
DA1554E420B3928E00EA92C5 /* mpw-marshal-util.c */,
|
||||
DA1554EE20B3928E00EA92C5 /* mpw-marshal-util.h */,
|
||||
DA1554E120B3928E00EA92C5 /* mpw-marshal.c */,
|
||||
DA1554E820B3928E00EA92C5 /* mpw-marshal.h */,
|
||||
DA1554E320B3928E00EA92C5 /* mpw-types.c */,
|
||||
DA1554EC20B3928E00EA92C5 /* mpw-types.h */,
|
||||
DA1554E520B3928E00EA92C5 /* mpw-util.c */,
|
||||
DA1554ED20B3928E00EA92C5 /* mpw-util.h */,
|
||||
);
|
||||
name = core;
|
||||
path = "../platform-independent/c/core/src";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXLegacyTarget section */
|
||||
DA1554D720B3924000EA92C5 /* MasterPassword-JNI */ = {
|
||||
isa = PBXLegacyTarget;
|
||||
buildArgumentsString = ":masterpassword-algorithm:nativeGenHeaders";
|
||||
buildConfigurationList = DA1554DA20B3924000EA92C5 /* Build configuration list for PBXLegacyTarget "MasterPassword-JNI" */;
|
||||
buildPhases = (
|
||||
);
|
||||
buildToolPath = gradle;
|
||||
buildWorkingDirectory = /Users/lhunath/Documents/workspace/lyndir/MasterPassword/gradle;
|
||||
dependencies = (
|
||||
);
|
||||
name = "MasterPassword-JNI";
|
||||
passBuildSettingsInEnvironment = 1;
|
||||
productName = "MasterPassword-JNI";
|
||||
};
|
||||
/* End PBXLegacyTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
DA1554D320B3924000EA92C5 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0930;
|
||||
ORGANIZATIONNAME = "Maarten Billemont";
|
||||
TargetAttributes = {
|
||||
DA1554D720B3924000EA92C5 = {
|
||||
CreatedOnToolsVersion = 9.3.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = DA1554D620B3924000EA92C5 /* Build configuration list for PBXProject "MasterPassword-JNI" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = DA1554D220B3924000EA92C5;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
DA1554D720B3924000EA92C5 /* MasterPassword-JNI */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
DA1554D820B3924000EA92C5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
DA1554D920B3924000EA92C5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
DA1554DB20B3924000EA92C5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"",
|
||||
);
|
||||
JAVA_HOME = /Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home;
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
"-DMPW_CPERCIVA=0",
|
||||
);
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
DA1554DC20B3924000EA92C5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(PROJECT_DIR)/../lib/libsodium/build-ios~/out/include\"",
|
||||
"\"$(PROJECT_DIR)/../lib/libjson-c/build-ios~/out/include\"",
|
||||
);
|
||||
JAVA_HOME = /Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk/Contents/Home;
|
||||
OTHER_CFLAGS = (
|
||||
"-DMPW_SODIUM=1",
|
||||
"-DMPW_CPERCIVA=0",
|
||||
);
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
DA1554D620B3924000EA92C5 /* Build configuration list for PBXProject "MasterPassword-JNI" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
DA1554D820B3924000EA92C5 /* Debug */,
|
||||
DA1554D920B3924000EA92C5 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
DA1554DA20B3924000EA92C5 /* Build configuration list for PBXLegacyTarget "MasterPassword-JNI" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
DA1554DB20B3924000EA92C5 /* Debug */,
|
||||
DA1554DC20B3924000EA92C5 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = DA1554D320B3924000EA92C5 /* Project object */;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0830"
|
||||
LastUpgradeVersion = "0920"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -26,6 +26,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
@@ -45,6 +46,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0830"
|
||||
LastUpgradeVersion = "0920"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -26,6 +26,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
@@ -45,6 +46,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0920"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA1C7AC61F1A8FD8009A3551"
|
||||
BuildableName = "mpw-bench"
|
||||
BlueprintName = "mpw-bench"
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA1C7AC61F1A8FD8009A3551"
|
||||
BuildableName = "mpw-bench"
|
||||
BlueprintName = "mpw-bench"
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA1C7AC61F1A8FD8009A3551"
|
||||
BuildableName = "mpw-bench"
|
||||
BlueprintName = "mpw-bench"
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DA1C7AC61F1A8FD8009A3551"
|
||||
BuildableName = "mpw-bench"
|
||||
BlueprintName = "mpw-bench"
|
||||
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user