Compare commits
416 Commits
2.5-mac-1
...
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 | ||
|
|
89f6e77f67 | ||
|
|
f2fb16a0b9 | ||
|
|
e3edd42b88 | ||
|
|
cc5d246d7d | ||
|
|
ca320de6d9 | ||
|
|
ae979d7240 | ||
|
|
eb1c443940 | ||
|
|
dadcefc9bf | ||
|
|
cdbaec9751 | ||
|
|
f48d480c77 |
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
|
||||
122
README.md
122
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,15 +6,25 @@ 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/). Get in touch if you are interested in adding Master Password to any other package managers.
|
||||
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:
|
||||
|
||||
- I don't need to worry about keeping backups of my countless authentication credentials.
|
||||
- I don't need to worry that when I travel, I might not have access to my passwords vault.
|
||||
- I don't need to trust an external party, proprietary code or a service to be online and stay online.
|
||||
- If I feel at risk of my device being stolen or confiscated, I can set a fake master password, delete my user or wipe it worry-free.
|
||||
|
||||
We also have a [Frequently Asked Questions](#faq).
|
||||
|
||||
|
||||
|
||||
## What is a password?
|
||||
|
||||
The "password". Somehow, passwords have become the default solution to authentication across the web. We've long since accepted this as the way things are, but let's stop to think for a moment about what passwords actually are:
|
||||
Ah, the "password". Somehow, passwords have become the default solution to authentication across the web. We've long since accepted this as the way things are, but let's stop to think for a moment about what passwords actually are:
|
||||
|
||||
A password is a secret that is known only to the party providing a service and the party that should be allowed access to this service.
|
||||
|
||||
@@ -69,18 +72,18 @@ Master Password is *not* a password manager. It does not store your website pas
|
||||
|
||||
## Benefits
|
||||
|
||||
- You don't need to come up with a secure password every time you make a new account - Master Password gives you the key for it.
|
||||
- You don't need to try to remember a password you created two years ago for that one account - Master Password just gives you the key for it.
|
||||
- You don't need to that you can't get into that account you made at work when you come home because you don't have your work passwords with you - Master Password is always available.
|
||||
- You don't need to try to keep password lists in sync or stored somewhere easily accessible - Master Password is always available.
|
||||
- You don't need to worry what you'll do if your computer dies or you need to log into your bank while you're in the airport transit zone - Master Password is always available.
|
||||
- You don't need to worry about your password manager website getting hacked, your phone getting duplicated, somebody taking a picture of your passwords book - Master Password keeps no records.
|
||||
- You don't need to think up a new strong password every time you make a new account - Master Password gives you the key for it.
|
||||
- You don't need to try remembering a password you created two years ago for that one account - Master Password just gives you the key for it.
|
||||
- You don't need to worry about getting into that account you made at work after you come home because you don't have your office passwords with you - Master Password is availale everywhere, even offline.
|
||||
- You don't need to try to keep password lists in sync or stored somewhere easily accessible - Master Password keys can be created anywhere.
|
||||
- You don't need to worry what you'll do if your computer dies or you need to log into your bank while you're in the airport transit zone - your Master Password keys are always available, even when starting empty.
|
||||
- You don't need to worry about your password manager website getting hacked, your phone getting duplicated, somebody taking a picture of your passwords book - Master Password stores no secrets.
|
||||
|
||||
|
||||
|
||||
## 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:
|
||||
|
||||
@@ -94,10 +97,39 @@ We standardize `user-name` as your full name, `site-name` as the domain name of
|
||||
|
||||
|
||||
|
||||
## FAQ
|
||||
|
||||
1. If I lose my master password and need to set a new one, will I need to change all of my site passwords?
|
||||
|
||||
Yes. If your master password is compromised, it is only sensible for you to change all of your site passwords. Just like if you lose the keys in your pocket, you'll have to change all the locks they open. Master Password effectively enforces this security practice.
|
||||
|
||||
2. But what if I just forget my master password or I just want to change it to something else?
|
||||
|
||||
Sorry, still yes. Your master password is the secret component to your Master Password identity. If it changes, your identity changes. I wholly encourage you to think very carefully about what makes for a really memorable and good master password before just diving in with something lazy. A short phrase works great, eg. `banana coloured duckling`.
|
||||
|
||||
3. Doesn't this mean an attacker can reverse my master password from any of my site passwords?
|
||||
|
||||
Technically, yes. Practically, no.
|
||||
|
||||
You could argue that site passwords are "breadcrumbs" of your master password, but the same argument would suggest encrypted messages are breadcrumbs to the encryption key. Encryption works because it is computationally unfeasible to "guess" the encryption key that made the encrypted message, just like Master Password works because it is computationally unfeasible to "guess" your master password that made the site password.
|
||||
|
||||
4. The second step is just a HMAC-SHA-256, doesn't that make the SCRYPT completely pointless?
|
||||
|
||||
No. They are used for different reasons and one is not weaker than the other.
|
||||
|
||||
HMAC-SHA-256 is much faster to compute than SCRYPT, which leads some people to think "all an attacker needs to do is brute-force the SHA and ignore the SCRYPT". The reality is that the HMAC-SHA-256 guards a 64-byte authentication key (the `master-key`) which makes the search space for brute-forcing the HMAC wildly too large to compute.
|
||||
The `master-password` on the other hand, is only a simple phrase, which means its search space is much smaller. This is why it is guarded by a much tougher SCRYPT operation.
|
||||
|
||||
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 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).
|
||||
|
||||
|
||||
|
||||
@@ -146,58 +178,16 @@ Go into the `gradle` directory and run `./gradlew build`. All Java components w
|
||||
- `platform-android/build/outputs/apk`:
|
||||
contains the Android application package. Install it on your Android device.
|
||||
|
||||
Note that in order to build the Android application, you will need to have the Android SDK installed and either have the environment variable `ANDROID_HOME` set to its location or a `gradle/local.properties` file with its location, eg. (for Homebrew users who installed the SDK using `brew install android-sdk`):
|
||||
|
||||
sdk.dir=/usr/local/opt/android-sdk
|
||||
|
||||
|
||||
### Native CLI
|
||||
|
||||
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:
|
||||
|
||||
- `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:
|
||||
|
||||
- You can change the targets that should be built. By default, only `mpw` is built. These are the available targets:
|
||||
- `mpw`: This is the standard command-line `mpw` tool which implements all Master Password features.
|
||||
- `mpw-tests`: This is a tool to perform the standard tests script on the `mpw` implementation.
|
||||
- `mpw-bench`: This is a tool to run a benchmark on the `mpw` implementation, comparing it to the performance of other algorithms.
|
||||
- You can specify custom arguments to the compiler, pass them as arguments to the build script.
|
||||
- The build process involves some optionals, they can by toggled from their default setting by passing variables:
|
||||
- `mpw_color`: [default: 1] Colorized Identicon, depends on
|
||||
|
||||
To change the targets to build, use:
|
||||
|
||||
targets='mpw mpw-tests' ./build
|
||||
|
||||
To add a library search path, use:
|
||||
|
||||
./build -L/usr/local/lib
|
||||
|
||||
Change an optional feature:
|
||||
|
||||
mpw_color=0 ./build
|
||||
|
||||
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: d02256465a...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