Compare commits
512 Commits
2.4.1-andr
...
2.7-java-6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fae0fe425 | ||
|
|
0558176847 | ||
|
|
c553201cda | ||
|
|
665be9494b | ||
|
|
5ca81b4aa7 | ||
|
|
3cbb063926 | ||
|
|
d5551c8c8c | ||
|
|
9a40e52d53 | ||
|
|
6f0d768e69 | ||
|
|
40fdc8d248 | ||
|
|
6b9e1b8cb8 | ||
|
|
f41cdb8742 | ||
|
|
10c6d203b8 | ||
|
|
7d1aa9c9f4 | ||
|
|
c26281e3b7 | ||
|
|
f0b1f0c9e0 | ||
|
|
9682efc7c9 | ||
|
|
1264cad377 | ||
|
|
d185a0af14 | ||
|
|
4275a6cc61 | ||
|
|
c94ff429e8 | ||
|
|
00744cb264 | ||
|
|
7202fe6d1d | ||
|
|
63b4d9cd2e | ||
|
|
36a7c7f423 | ||
|
|
c2c4fb18bf | ||
|
|
3fc8acba70 | ||
|
|
f5c0c4d787 | ||
|
|
86775f1c75 | ||
|
|
2bb190f49a | ||
|
|
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 | ||
|
|
23aae490df | ||
|
|
3fcf1131ac | ||
|
|
2bdec415e9 | ||
|
|
4c12f4af56 | ||
|
|
d91140439a | ||
|
|
a7912dd1b7 | ||
|
|
05391d893e | ||
|
|
2047422b08 | ||
|
|
e3fffc1923 | ||
|
|
9bf50569cc | ||
|
|
9ef265d9de | ||
|
|
385f347b33 | ||
|
|
4058d33202 | ||
|
|
98c5ee3425 | ||
|
|
b01e370fc0 | ||
|
|
21d5ef823f | ||
|
|
da71ed6f7f | ||
|
|
5a6acb2e44 | ||
|
|
29ecbaeb19 | ||
|
|
3ebdfe1180 | ||
|
|
60ac096f99 | ||
|
|
b13bdb171e | ||
|
|
2cfa1439e6 | ||
|
|
a363a8a715 | ||
|
|
e39062a32d | ||
|
|
4c5c7b8aaf | ||
|
|
ee4e8f4229 | ||
|
|
e4d3c0b47c | ||
|
|
6e685f0036 | ||
|
|
b248f2d440 | ||
|
|
ec0712350f | ||
|
|
d440f2bcac | ||
|
|
bef55cca03 | ||
|
|
ebe5206f92 | ||
|
|
b478691980 | ||
|
|
86f956571d | ||
|
|
559934607b | ||
|
|
de4300c3d3 | ||
|
|
05f91d53db | ||
|
|
01eddf9f42 | ||
|
|
9b2c0b33ef | ||
|
|
77b095e25e | ||
|
|
93cdcc1743 | ||
|
|
2fe1ed84e6 | ||
|
|
4903df21f8 | ||
|
|
31fb9bc4e1 | ||
|
|
23dcb11a10 | ||
|
|
ee0ca569f5 | ||
|
|
8ca8ef7aab | ||
|
|
018ab83af3 | ||
|
|
58a6f17641 | ||
|
|
77bee803b8 | ||
|
|
c3f4d148a4 | ||
|
|
f94ae03bde | ||
|
|
88199db988 | ||
|
|
33ff573295 | ||
|
|
e0822b270e | ||
|
|
acc4c34b85 | ||
|
|
63b757f51a | ||
|
|
eb527d985b | ||
|
|
94159ed11a | ||
|
|
18fce4eaf8 | ||
|
|
8347c72882 | ||
|
|
f03abb1c8d | ||
|
|
10cfe95158 | ||
|
|
c6b285a9c0 |
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
|
||||||
44
.gitignore
vendored
44
.gitignore
vendored
@@ -3,40 +3,24 @@
|
|||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
/MasterPassword/Java/.idea
|
.idea
|
||||||
/.idea/*
|
|
||||||
!/.idea/encodings.xml
|
|
||||||
!/.idea/inspectionProfiles
|
|
||||||
!/.idea/projectCodeStyle.xml
|
|
||||||
!/.idea/validation.xml
|
|
||||||
*.iml
|
*.iml
|
||||||
/*.ipr
|
*.ipr
|
||||||
/*.iws
|
*.iws
|
||||||
|
out
|
||||||
|
|
||||||
# Xcode IDE
|
# Xcode IDE
|
||||||
xcuserdata/
|
xcuserdata/
|
||||||
/DerivedData/
|
DerivedData/
|
||||||
|
|
||||||
# Generated
|
# Generated
|
||||||
MasterPassword/Resources/Media/Images.xcassets/
|
/platform-darwin/Resources/Media/Images.xcassets/
|
||||||
|
/platform-darwin/Podfile.lock
|
||||||
|
/platform-darwin/Pods/
|
||||||
|
|
||||||
# Media
|
# Gradle
|
||||||
Press/Background.png
|
build
|
||||||
Press/Front-Page.png
|
.gradle
|
||||||
Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
|
local.properties
|
||||||
|
/gradle/builds
|
||||||
# IPA
|
/platform-android/.externalNativeBuild
|
||||||
/sendipa/*
|
|
||||||
!/sendipa/sendipa.conf
|
|
||||||
|
|
||||||
# C
|
|
||||||
MasterPassword/C/VERSION
|
|
||||||
MasterPassword/C/*.o
|
|
||||||
MasterPassword/C/mpw-*.tar.gz
|
|
||||||
MasterPassword/C/mpw
|
|
||||||
MasterPassword/C/mpw-bench
|
|
||||||
MasterPassword/C/mpw-tests
|
|
||||||
MasterPassword/C/lib/*/.unpacked
|
|
||||||
MasterPassword/C/lib/*/.patched
|
|
||||||
MasterPassword/C/lib/*/src
|
|
||||||
MasterPassword/C/lib/include
|
|
||||||
|
|||||||
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
|
||||||
26
.gitmodules
vendored
26
.gitmodules
vendored
@@ -1,21 +1,33 @@
|
|||||||
[submodule "External/Pearl"]
|
[submodule "External/Pearl"]
|
||||||
path = External/Pearl
|
path = platform-darwin/External/Pearl
|
||||||
url = git://github.com/Lyndir/Pearl.git
|
url = git://github.com/Lyndir/Pearl.git
|
||||||
[submodule "External/InAppSettingsKit"]
|
[submodule "External/InAppSettingsKit"]
|
||||||
path = External/InAppSettingsKit
|
path = platform-darwin/External/InAppSettingsKit
|
||||||
url = git://github.com/lhunath/InAppSettingsKit.git
|
url = git://github.com/lhunath/InAppSettingsKit.git
|
||||||
[submodule "External/KCOrderedAccessorFix"]
|
[submodule "External/KCOrderedAccessorFix"]
|
||||||
path = External/KCOrderedAccessorFix
|
path = platform-darwin/External/KCOrderedAccessorFix
|
||||||
url = https://github.com/lhunath/KCOrderedAccessorFix.git
|
url = https://github.com/lhunath/KCOrderedAccessorFix.git
|
||||||
[submodule "External/AttributedMarkdown"]
|
[submodule "External/AttributedMarkdown"]
|
||||||
path = External/AttributedMarkdown
|
path = platform-darwin/External/AttributedMarkdown
|
||||||
url = https://github.com/dreamwieber/AttributedMarkdown.git
|
url = https://github.com/dreamwieber/AttributedMarkdown.git
|
||||||
[submodule "External/uicolor-utilities"]
|
[submodule "External/uicolor-utilities"]
|
||||||
path = External/uicolor-utilities
|
path = platform-darwin/External/uicolor-utilities
|
||||||
url = git://github.com/lhunath/uicolor-utilities.git
|
url = git://github.com/lhunath/uicolor-utilities.git
|
||||||
[submodule "External/jrswizzle"]
|
[submodule "External/jrswizzle"]
|
||||||
path = External/jrswizzle
|
path = platform-darwin/External/jrswizzle
|
||||||
url = git://github.com/jonmarimba/jrswizzle.git
|
url = git://github.com/jonmarimba/jrswizzle.git
|
||||||
[submodule "MasterPassword/Web/js/mpw-js"]
|
[submodule "MasterPassword/Web/js/mpw-js"]
|
||||||
path = MasterPassword/Web/js/mpw-js
|
path = platform-independent/web/js/mpw-js
|
||||||
url = https://github.com/tmthrgd/mpw-js.git
|
url = https://github.com/tmthrgd/mpw-js.git
|
||||||
|
[submodule "lib/libsodium"]
|
||||||
|
path = lib/libsodium
|
||||||
|
url = https://github.com/jedisct1/libsodium.git
|
||||||
|
[submodule "lib/libjson-c"]
|
||||||
|
path = lib/libjson-c
|
||||||
|
url = https://github.com/json-c/json-c.git
|
||||||
|
[submodule "public/site"]
|
||||||
|
path = public/site
|
||||||
|
url = https://github.com/Lyndir/MasterPassword.git
|
||||||
|
branch = gh-pages
|
||||||
|
shallow = true
|
||||||
|
update = none
|
||||||
|
|||||||
11
.idea/encodings.xml
generated
11
.idea/encodings.xml
generated
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false">
|
|
||||||
<file url="file://$PROJECT_DIR$/MasterPassword/Java" charset="UTF-8" />
|
|
||||||
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-algorithm" charset="UTF-8" />
|
|
||||||
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-cli" charset="UTF-8" />
|
|
||||||
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-gui" charset="UTF-8" />
|
|
||||||
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-model" charset="UTF-8" />
|
|
||||||
<file url="PROJECT" charset="UTF-8" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
18
.idea/inspectionProfiles/Project_Default.xml
generated
18
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,18 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0" is_locked="false">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<option name="myLocal" value="false" />
|
|
||||||
<inspection_tool class="Convert to string" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="FunctionImplicitDeclarationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
||||||
<inspection_tool class="ImplicitIntegerAndEnumConversion" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
||||||
<inspection_tool class="MethodIsLaterInTheScope" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
||||||
<inspection_tool class="OCNotLocalizedStringInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
||||||
<inspection_tool class="OCUnusedMacroInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
||||||
<inspection_tool class="OCUnusedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
||||||
<inspection_tool class="Replace with subshell" enabled="true" level="INFO" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="SignednessMismatch" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
||||||
<inspection_tool class="UnavailableInDeploymentTarget" enabled="true" level="INFO" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="UnusedLocalVariable" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
||||||
<inspection_tool class="UnusedParameter" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
||||||
7
.idea/inspectionProfiles/profiles_settings.xml
generated
7
.idea/inspectionProfiles/profiles_settings.xml
generated
@@ -1,7 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="PROJECT_PROFILE" value="Project Default" />
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="true" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
||||||
9
.idea/projectCodeStyle.xml
generated
9
.idea/projectCodeStyle.xml
generated
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CodeStyleSettingsManager">
|
|
||||||
<option name="PER_PROJECT_SETTINGS">
|
|
||||||
<value />
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
language: objective-c
|
|
||||||
osx_image: xcode8
|
|
||||||
env: TERM=dumb SHLVL=0
|
|
||||||
git:
|
|
||||||
submodules: true
|
|
||||||
script: xcodebuild -workspace MasterPassword.xcworkspace -scheme 'MasterPassword iOS (App Store)' -configuration 'AdHoc-iOS' -sdk iphonesimulator
|
|
||||||
6
Brewfile
Normal file
6
Brewfile
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
brew "libsodium"
|
||||||
|
brew "json-c"
|
||||||
|
|
||||||
|
brew "libtool"
|
||||||
|
brew "automake"
|
||||||
|
brew "autoconf"
|
||||||
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
FROM debian:stable-slim
|
||||||
|
|
||||||
|
# For i386
|
||||||
|
#FROM i386/debian:stable-slim
|
||||||
|
#ENTRYPOINT ["linux32", "--"]
|
||||||
|
|
||||||
|
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=863199
|
||||||
|
RUN mkdir -p /usr/share/man/man1
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y default-jdk-headless git-core bash libtool automake autoconf make g++
|
||||||
|
RUN git clone --depth=3 $(: --shallow-submodules) --recurse-submodules https://gitlab.com/MasterPassword/MasterPassword.git /mpw
|
||||||
|
RUN cd /mpw/gradle && ./gradlew -i clean build
|
||||||
1
External/InAppSettingsKit
vendored
1
External/InAppSettingsKit
vendored
Submodule External/InAppSettingsKit deleted from b58b72563a
1
External/Pearl
vendored
1
External/Pearl
vendored
Submodule External/Pearl deleted from 4ac4add457
1
External/iOS/Reveal.framework
vendored
1
External/iOS/Reveal.framework
vendored
@@ -1 +0,0 @@
|
|||||||
/Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/Reveal.framework
|
|
||||||
10
MasterPassword.xcworkspace/contents.xcworkspacedata
generated
10
MasterPassword.xcworkspace/contents.xcworkspacedata
generated
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Workspace
|
|
||||||
version = "1.0">
|
|
||||||
<FileRef
|
|
||||||
location = "group:MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj">
|
|
||||||
</FileRef>
|
|
||||||
<FileRef
|
|
||||||
location = "group:MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj">
|
|
||||||
</FileRef>
|
|
||||||
</Workspace>
|
|
||||||
@@ -1,332 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# TROUBLESHOOTING
|
|
||||||
# - If you see 'undefined reference to `AES_encrypt'',
|
|
||||||
# make sure you have openssl installed.
|
|
||||||
# If libcrypto.a is in a non-standard directory, try ./build -L[your-lib-dir]
|
|
||||||
# - If you see 'undefined reference to `clock_gettime'',
|
|
||||||
# try ./build -lrt instead.
|
|
||||||
# - If you see 'x86.S:202: Error: junk at end of line, first unrecognized character is `,'',
|
|
||||||
# try commenting the line in lib/bcrypt/x86.S.
|
|
||||||
# - Take a look at the "Optional features" section. Some features have dependencies,
|
|
||||||
# either make sure you have them or disable those features.
|
|
||||||
#
|
|
||||||
# BUGS
|
|
||||||
# masterpassword@lyndir.com
|
|
||||||
#
|
|
||||||
# AUTHOR
|
|
||||||
# Maarten Billemont
|
|
||||||
#
|
|
||||||
cd "${BASH_SOURCE%/*}"
|
|
||||||
shopt -s extglob
|
|
||||||
set -e
|
|
||||||
|
|
||||||
|
|
||||||
### CONFIGURATION
|
|
||||||
|
|
||||||
# Targets to build.
|
|
||||||
if [[ $targets ]]; then
|
|
||||||
read -ra targets <<< "$targets"
|
|
||||||
else
|
|
||||||
# Default targets.
|
|
||||||
# Modify here or override using targets='mpw mpw-bench' ./build
|
|
||||||
targets=(
|
|
||||||
mpw # C CLI version of Master Password.
|
|
||||||
mpw-bench # C CLI Master Password benchmark utility.
|
|
||||||
mpw-tests # C Master Password algorithm tester.
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Optional features.
|
|
||||||
mpw_color=1 # Colorized Identicon, requires libncurses-dev
|
|
||||||
|
|
||||||
# Distribution specific configuration.
|
|
||||||
# Homebrew
|
|
||||||
if hash brew 2>/dev/null; then
|
|
||||||
opensslPath=$(brew --prefix openssl)
|
|
||||||
export CFLAGS="$CFLAGS -I$opensslPath/include"
|
|
||||||
export LDFLAGS="$LDFLAGS -L$opensslPath/lib"
|
|
||||||
fi
|
|
||||||
|
|
||||||
### DEPENDENCIES
|
|
||||||
|
|
||||||
digest() {
|
|
||||||
openssl sha -sha256 -binary < "$1" | od -t x1 -An -v | tr -d '[:space:]'
|
|
||||||
}
|
|
||||||
fetch() {
|
|
||||||
if hash wget 2>/dev/null; then
|
|
||||||
wget -O "${1##*/}" "$1"
|
|
||||||
elif hash curl 2>/dev/null; then
|
|
||||||
curl "$1" > "${1##*/}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
unpack() {
|
|
||||||
printf 'Verifying package: %s, against digest: %s...' "$1" "$2"
|
|
||||||
[[ $(digest "$1") = $2 ]] || {
|
|
||||||
printf ' mismatch!\n'
|
|
||||||
echo 2>&1 "Downloaded package doesn't match digest."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
printf ' OK!\n'
|
|
||||||
|
|
||||||
if [[ $1 = *.tar.gz || $1 = *.tgz ]]; then
|
|
||||||
tar -xvzf "$1"
|
|
||||||
|
|
||||||
elif [[ $1 = *.tar.bz2 || $1 = *.tbz2 ]]; then
|
|
||||||
tar -xvjf "$1"
|
|
||||||
|
|
||||||
elif [[ $1 = *.tar ]]; then
|
|
||||||
tar -xvf "$1"
|
|
||||||
|
|
||||||
else
|
|
||||||
echo 2>&1 "Don't know how to unpack: $1"
|
|
||||||
fi
|
|
||||||
|
|
||||||
files=( * )
|
|
||||||
if [[ -d $files ]] && (( ${#files[@]} == 1 )); then
|
|
||||||
mv "$files"/* .
|
|
||||||
rmdir "$files"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
fetchSource() (
|
|
||||||
local name=${PWD##*/}
|
|
||||||
source .source
|
|
||||||
|
|
||||||
if [[ -e .unpacked ]]; then
|
|
||||||
true
|
|
||||||
|
|
||||||
elif [[ $pkg && -e "${pkg##*/}" ]]; then
|
|
||||||
[[ -e src ]] || {
|
|
||||||
echo
|
|
||||||
echo "Unpacking: $name, using package..."
|
|
||||||
( mkdir src && cd src && unpack "../${pkg##*/}" "$pkg_sha256" )
|
|
||||||
touch .unpacked
|
|
||||||
}
|
|
||||||
|
|
||||||
elif [[ $git ]] && hash git 2>/dev/null; then
|
|
||||||
[[ -e .git ]] || {
|
|
||||||
echo
|
|
||||||
echo "Fetching: $name, using git..."
|
|
||||||
git clone "$git" src
|
|
||||||
touch .unpacked
|
|
||||||
}
|
|
||||||
|
|
||||||
elif [[ $svn ]] && hash git 2>/dev/null && [[ -x "$(git --exec-path)/git-svn" ]]; then
|
|
||||||
[[ -e .git ]] || {
|
|
||||||
echo
|
|
||||||
echo "Fetching: $name, using git-svn..."
|
|
||||||
git svn clone --prefix=origin/ --stdlayout "$svn" src
|
|
||||||
touch .unpacked
|
|
||||||
}
|
|
||||||
|
|
||||||
elif [[ $svn ]] && hash svn 2>/dev/null; then
|
|
||||||
[[ -e .svn ]] || {
|
|
||||||
echo
|
|
||||||
echo "Fetching: $name, using svn..."
|
|
||||||
svn checkout "$svn/trunk" src
|
|
||||||
touch .unpacked
|
|
||||||
}
|
|
||||||
|
|
||||||
elif [[ $pkg ]]; then
|
|
||||||
[[ -e src ]] || {
|
|
||||||
echo
|
|
||||||
echo "Fetching: $name, using package..."
|
|
||||||
fetch "$pkg"
|
|
||||||
( mkdir src && cd src && unpack "../${pkg##*/}" "$pkg_sha256" )
|
|
||||||
touch .unpacked
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
echo >&2 "error: Missing git-svn or svn."
|
|
||||||
echo >&2 "error: Please install either or manually check out the sources"
|
|
||||||
echo >&2 "error: from: $home"
|
|
||||||
echo >&2 "error: into: $PWD/src"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -e .patched ]] && (( ${#patches[@]} )); then
|
|
||||||
pushd src
|
|
||||||
for patch in "${patches[@]}"; do
|
|
||||||
echo
|
|
||||||
echo "Patching: $name, for $patch..."
|
|
||||||
patch -p0 < "../$patch.patch"
|
|
||||||
done
|
|
||||||
popd
|
|
||||||
touch .patched
|
|
||||||
fi
|
|
||||||
)
|
|
||||||
depend() {
|
|
||||||
local name=$1
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Checking dependency: $name..."
|
|
||||||
[[ -e "lib/include/$name" ]] && return
|
|
||||||
|
|
||||||
pushd "lib/$name"
|
|
||||||
fetchSource
|
|
||||||
pushd "src"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Configuring dependency: $name..."
|
|
||||||
if [[ -e configure.ac ]]; then
|
|
||||||
if [[ ! -e configure ]]; then
|
|
||||||
# create configure using autotools.
|
|
||||||
if ! hash aclocal || ! hash automake; then
|
|
||||||
echo >&2 "Need autotools to build $name. Please install automake and autoconf."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
aclocal
|
|
||||||
autoheader
|
|
||||||
autoconf
|
|
||||||
mkdir -p config.aux
|
|
||||||
automake --add-missing
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -e configure ]]; then
|
|
||||||
./configure
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Building dependency: $name..."
|
|
||||||
if [[ -e Makefile ]]; then
|
|
||||||
if ! hash make; then
|
|
||||||
echo >&2 "Need make to build $name. Please install GNU make."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
make
|
|
||||||
install -d "../../include/$name/"
|
|
||||||
find . -name '*.h' -exec install -m 444 {} "../../include/$name/" \;
|
|
||||||
else
|
|
||||||
echo >&2 "error: Don't know how to build: $name"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
popd
|
|
||||||
popd
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### MPW
|
|
||||||
mpw() {
|
|
||||||
depend scrypt
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Building target: $target..."
|
|
||||||
local CFLAGS=(
|
|
||||||
# include paths
|
|
||||||
-I"lib/include"
|
|
||||||
)
|
|
||||||
local LDFLAGS=(
|
|
||||||
# scrypt
|
|
||||||
"lib/scrypt/src/libcperciva/"*/*.o
|
|
||||||
"lib/scrypt/src/lib/crypto/"*.o
|
|
||||||
# library paths
|
|
||||||
-L"." -L"lib/scrypt/src"
|
|
||||||
# link libraries
|
|
||||||
-l"crypto"
|
|
||||||
)
|
|
||||||
# optional features
|
|
||||||
(( mpw_color )) && CFLAGS+=( -DCOLOR ) LDFLAGS+=( -l"curses" )
|
|
||||||
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
|
|
||||||
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
|
|
||||||
"${LDFLAGS[@]}" "mpw-cli.c" -o "mpw"
|
|
||||||
echo "done! Now run ./install or use ./mpw"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### MPW-BENCH
|
|
||||||
mpw-bench() {
|
|
||||||
depend scrypt
|
|
||||||
depend bcrypt
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Building target: $target..."
|
|
||||||
local CFLAGS=(
|
|
||||||
# include paths
|
|
||||||
-I"lib/include"
|
|
||||||
)
|
|
||||||
local LDFLAGS=(
|
|
||||||
# scrypt
|
|
||||||
"lib/scrypt/src/libcperciva/"*/*.o
|
|
||||||
"lib/scrypt/src/lib/crypto/"*.o
|
|
||||||
# bcrypt
|
|
||||||
"lib/bcrypt/src/crypt_blowfish.o"
|
|
||||||
"lib/bcrypt/src/crypt_gensalt.o"
|
|
||||||
"lib/bcrypt/src/wrapper.o"
|
|
||||||
"lib/bcrypt/src/x86.o"
|
|
||||||
# library paths
|
|
||||||
-L"." -L"lib/scrypt/src"
|
|
||||||
-L"lib/bcrypt/src"
|
|
||||||
# link libraries
|
|
||||||
-l"crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
|
|
||||||
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
|
|
||||||
"${LDFLAGS[@]}" "mpw-bench.c" -o "mpw-bench"
|
|
||||||
echo "done! Now use ./mpw-bench"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### MPW-TESTS
|
|
||||||
mpw-tests() {
|
|
||||||
depend scrypt
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Building target: $target..."
|
|
||||||
local CFLAGS=(
|
|
||||||
# include paths
|
|
||||||
-I"lib/include"
|
|
||||||
-I"/usr/include/libxml2"
|
|
||||||
-I"/usr/local/include/libxml2"
|
|
||||||
)
|
|
||||||
local LDFLAGS=(
|
|
||||||
# scrypt
|
|
||||||
"lib/scrypt/src/libcperciva/"*/*.o
|
|
||||||
"lib/scrypt/src/lib/crypto/"*.o
|
|
||||||
# library paths
|
|
||||||
-L"." -L"lib/scrypt/src"
|
|
||||||
# link libraries
|
|
||||||
-l"crypto" -l"xml2"
|
|
||||||
)
|
|
||||||
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-tests-util.c -o mpw-tests-util.o
|
|
||||||
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" "mpw-tests-util.o" \
|
|
||||||
"${LDFLAGS[@]}" "mpw-tests.c" -o "mpw-tests"
|
|
||||||
echo "done! Now use ./mpw-tests"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### TARGETS
|
|
||||||
|
|
||||||
haslib() {
|
|
||||||
! LC_ALL=C cc -l"$1" 2>&1 | grep -q 'library not found'
|
|
||||||
}
|
|
||||||
cc() {
|
|
||||||
if hash llvm-gcc 2>/dev/null; then
|
|
||||||
llvm-gcc "$@"
|
|
||||||
elif hash gcc 2>/dev/null; then
|
|
||||||
gcc -std=gnu99 "$@"
|
|
||||||
elif hash clang 2>/dev/null; then
|
|
||||||
clang "$@"
|
|
||||||
else
|
|
||||||
echo >&2 "Need a compiler. Please install GCC or LLVM."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Will build targets: ${targets[*]}..."
|
|
||||||
for target in "${targets[@]}"; do
|
|
||||||
"$target" "$@"
|
|
||||||
done
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Clean all generated build files.
|
|
||||||
set -e
|
|
||||||
cd "${BASH_SOURCE%/*}"
|
|
||||||
|
|
||||||
rm -vfr lib/*/{.unpacked,.patched,src} lib/include
|
|
||||||
rm -vfr *.o *.dSYM mpw mpw-bench mpw-tests
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
cd "${BASH_SOURCE%/*}"
|
|
||||||
tag=$(git describe)
|
|
||||||
commit=$(git describe --long --dirty)
|
|
||||||
[[ $tag && $commit = $tag* ]] || exit 1
|
|
||||||
git show --show-signature --pretty=format:%H --quiet "$tag" > VERSION
|
|
||||||
|
|
||||||
mpwArchive=mpw-$commit.tar.gz
|
|
||||||
[[ -e $mpwArchive ]] && echo "WARNING: $mpwArchive already exists. Will overwrite."
|
|
||||||
read -n1 -p "Will prepare and release $mpwArchive. Press a key to continue or ^C to abort."
|
|
||||||
|
|
||||||
git ls-files -z . | xargs -0 tar -Lcvzf "$mpwArchive"
|
|
||||||
echo "$mpwArchive ready, SHA256: $(openssl sha -sha256 < "$mpwArchive")"
|
|
||||||
|
|
||||||
cd ../../Site/current
|
|
||||||
ln -sf "../../MasterPassword/C/$mpwArchive"
|
|
||||||
[[ -e $_ ]]
|
|
||||||
echo "Linked from site, please update your hyperlinks to point to http://masterpasswordapp.com/$mpwArchive"
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
home=http://www.openwall.com/crypt/
|
|
||||||
pkg=http://www.openwall.com/crypt/crypt_blowfish-1.3.tar.gz
|
|
||||||
pkg_sha256=83fa01fca6996fe8d882b7f8e9ba0305a5664936100b01481ea3c6a8ce8d72fd
|
|
||||||
patches=(arm)
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
--- x86.S 2014-11-21 09:09:58.000000000 -0500
|
|
||||||
+++ x86.S 2014-11-21 09:11:01.000000000 -0500
|
|
||||||
@@ -199,5 +199,9 @@
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__ELF__) && defined(__linux__)
|
|
||||||
+#if defined(__arm__)
|
|
||||||
+.section .note.GNU-stack,"",%progbits
|
|
||||||
+#else
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
#endif
|
|
||||||
+#endif
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
home=http://www.tarsnap.com/scrypt.html
|
|
||||||
git=https://github.com/Tarsnap/scrypt.git
|
|
||||||
pkg=http://www.tarsnap.com/scrypt/scrypt-1.2.0.tgz
|
|
||||||
pkg_sha256=1754bc89405277c8ac14220377a4c240ddc34b1ce70882aa92cd01bfdc8569d4
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
diff -ruN /Users/lhunath/.src/scrypt/Makefile ./Makefile
|
|
||||||
--- /Users/lhunath/.src/scrypt/Makefile 2014-05-02 11:28:58.000000000 -0400
|
|
||||||
+++ ./Makefile 2014-05-02 12:07:27.000000000 -0400
|
|
||||||
@@ -2,11 +2,11 @@
|
|
||||||
VER?= nosse
|
|
||||||
SRCS= main.c
|
|
||||||
LDADD+= -lcrypto
|
|
||||||
-WARNS?= 6
|
|
||||||
+WARNS?= 0
|
|
||||||
|
|
||||||
# We have a config file for FreeBSD
|
|
||||||
CFLAGS += -I .
|
|
||||||
-CFLAGS += -DCONFIG_H_FILE=\"config_freebsd.h\"
|
|
||||||
+CFLAGS += -DCONFIG_H_FILE=\"config_osx.h\"
|
|
||||||
|
|
||||||
# Include all possible object files containing built scrypt code.
|
|
||||||
CLEANFILES += crypto_scrypt-ref.o
|
|
||||||
diff -ruN /Users/lhunath/.src/scrypt/lib/util/memlimit.c ./lib/util/memlimit.c
|
|
||||||
--- /Users/lhunath/.src/scrypt/lib/util/memlimit.c 2014-05-02 11:28:58.000000000 -0400
|
|
||||||
+++ ./lib/util/memlimit.c 2014-05-02 11:52:42.000000000 -0400
|
|
||||||
@@ -75,7 +75,7 @@
|
|
||||||
* have returned to us.
|
|
||||||
*/
|
|
||||||
if (usermemlen == sizeof(uint64_t))
|
|
||||||
- usermem = *(uint64_t *)usermembuf;
|
|
||||||
+ usermem = *(uint64_t *)(void *)usermembuf;
|
|
||||||
else if (usermemlen == sizeof(uint32_t))
|
|
||||||
usermem = SIZE_MAX;
|
|
||||||
else
|
|
||||||
diff -ruN /Users/lhunath/.src/scrypt/lib/util/memlimit.c ./lib/util/memlimit.c
|
|
||||||
--- /Users/lhunath/.src/scrypt/config_osx.h 1969-12-31 19:00:00.000000000 -0500
|
|
||||||
+++ config_osx.h 2014-05-02 12:06:55.000000000 -0400
|
|
||||||
@@ -0,0 +1,5 @@
|
|
||||||
+/* A default configuration for FreeBSD, used if there is no config.h. */
|
|
||||||
+
|
|
||||||
+#define HAVE_POSIX_MEMALIGN 1
|
|
||||||
+#define HAVE_SYSCTL_HW_USERMEM 1
|
|
||||||
+#define HAVE_SYS_PARAM_H 1
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-algorithm.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "mpw-algorithm.h"
|
|
||||||
#include "mpw-algorithm_v0.c"
|
|
||||||
#include "mpw-algorithm_v1.c"
|
|
||||||
#include "mpw-algorithm_v2.c"
|
|
||||||
#include "mpw-algorithm_v3.c"
|
|
||||||
|
|
||||||
#define MP_N 32768
|
|
||||||
#define MP_r 8
|
|
||||||
#define MP_p 2
|
|
||||||
#define MP_hash PearlHashSHA256
|
|
||||||
|
|
||||||
const uint8_t *mpw_masterKeyForUser(const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) {
|
|
||||||
|
|
||||||
if (!fullName || !masterPassword)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
switch (algorithmVersion) {
|
|
||||||
case MPAlgorithmVersion0:
|
|
||||||
return mpw_masterKeyForUser_v0( fullName, masterPassword );
|
|
||||||
case MPAlgorithmVersion1:
|
|
||||||
return mpw_masterKeyForUser_v1( fullName, masterPassword );
|
|
||||||
case MPAlgorithmVersion2:
|
|
||||||
return mpw_masterKeyForUser_v2( fullName, masterPassword );
|
|
||||||
case MPAlgorithmVersion3:
|
|
||||||
return mpw_masterKeyForUser_v3( fullName, masterPassword );
|
|
||||||
default:
|
|
||||||
ftl( "Unsupported version: %d", algorithmVersion );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *mpw_passwordForSite(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
|
||||||
const MPSiteVariant siteVariant, const char *siteContext, const MPAlgorithmVersion algorithmVersion) {
|
|
||||||
|
|
||||||
if (!masterKey || !siteName)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
switch (algorithmVersion) {
|
|
||||||
case MPAlgorithmVersion0:
|
|
||||||
return mpw_passwordForSite_v0( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
|
||||||
case MPAlgorithmVersion1:
|
|
||||||
return mpw_passwordForSite_v1( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
|
||||||
case MPAlgorithmVersion2:
|
|
||||||
return mpw_passwordForSite_v2( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
|
||||||
case MPAlgorithmVersion3:
|
|
||||||
return mpw_passwordForSite_v3( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
|
||||||
default:
|
|
||||||
ftl( "Unsupported version: %d", algorithmVersion );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-algorithm.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
// NOTE: mpw is currently NOT thread-safe.
|
|
||||||
#include "mpw-types.h"
|
|
||||||
|
|
||||||
typedef enum(unsigned int, MPAlgorithmVersion) {
|
|
||||||
/** V0 did math with chars whose signedness was platform-dependent. */
|
|
||||||
MPAlgorithmVersion0,
|
|
||||||
/** V1 miscounted the byte-length of multi-byte site names. */
|
|
||||||
MPAlgorithmVersion1,
|
|
||||||
/** V2 miscounted the byte-length of multi-byte user names. */
|
|
||||||
MPAlgorithmVersion2,
|
|
||||||
/** V3 is the current version. */
|
|
||||||
MPAlgorithmVersion3,
|
|
||||||
};
|
|
||||||
#define MPAlgorithmVersionCurrent MPAlgorithmVersion3
|
|
||||||
|
|
||||||
/** Derive the master key for a user based on their name and master password.
|
|
||||||
* @return A new MP_dkLen-byte allocated buffer or NULL if an allocation error occurred. */
|
|
||||||
const uint8_t *mpw_masterKeyForUser(
|
|
||||||
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
|
|
||||||
|
|
||||||
/** Encode a password for the site from the given master key and site parameters.
|
|
||||||
* @return A newly allocated string or NULL if an allocation error occurred. */
|
|
||||||
const char *mpw_passwordForSite(
|
|
||||||
const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
|
||||||
const MPSiteVariant siteVariant, const char *siteContext, const MPAlgorithmVersion algorithmVersion);
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-algorithm.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include "mpw-types.h"
|
|
||||||
#include "mpw-util.h"
|
|
||||||
|
|
||||||
#define MP_N 32768
|
|
||||||
#define MP_r 8
|
|
||||||
#define MP_p 2
|
|
||||||
#define MP_hash PearlHashSHA256
|
|
||||||
|
|
||||||
static const char *mpw_templateForType_v0(MPSiteType type, uint16_t seedByte) {
|
|
||||||
|
|
||||||
size_t count = 0;
|
|
||||||
const char **templates = mpw_templatesForType( type, &count );
|
|
||||||
char const *template = count? templates[seedByte % count]: NULL;
|
|
||||||
free( templates );
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char mpw_characterFromClass_v0(char characterClass, uint16_t seedByte) {
|
|
||||||
|
|
||||||
const char *classCharacters = mpw_charactersInClass( characterClass );
|
|
||||||
return classCharacters[seedByte % strlen( classCharacters )];
|
|
||||||
}
|
|
||||||
|
|
||||||
static const uint8_t *mpw_masterKeyForUser_v0(const char *fullName, const char *masterPassword) {
|
|
||||||
|
|
||||||
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
|
||||||
trc( "algorithm: v%d\n", 0 );
|
|
||||||
trc( "fullName: %s (%zu)\n", fullName, mpw_utf8_strlen( fullName ) );
|
|
||||||
trc( "masterPassword: %s\n", masterPassword );
|
|
||||||
trc( "key scope: %s\n", mpKeyScope );
|
|
||||||
|
|
||||||
// Calculate the master key salt.
|
|
||||||
// masterKeySalt = mpKeyScope . #fullName . fullName
|
|
||||||
size_t masterKeySaltSize = 0;
|
|
||||||
uint8_t *masterKeySalt = NULL;
|
|
||||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
|
||||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( mpw_utf8_strlen( fullName ) ) );
|
|
||||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
|
|
||||||
if (!masterKeySalt) {
|
|
||||||
ftl( "Could not allocate master key salt: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "masterKeySalt ID: %s\n", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
|
|
||||||
|
|
||||||
// Calculate the master key.
|
|
||||||
// masterKey = scrypt( masterPassword, masterKeySalt )
|
|
||||||
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
|
|
||||||
mpw_free( masterKeySalt, masterKeySaltSize );
|
|
||||||
if (!masterKey) {
|
|
||||||
ftl( "Could not allocate master key: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "masterKey ID: %s\n", mpw_id_buf( masterKey, MP_dkLen ) );
|
|
||||||
|
|
||||||
return masterKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *mpw_passwordForSite_v0(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
|
||||||
const MPSiteVariant siteVariant, const char *siteContext) {
|
|
||||||
|
|
||||||
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
|
||||||
trc( "algorithm: v%d\n", 0 );
|
|
||||||
trc( "siteName: %s\n", siteName );
|
|
||||||
trc( "siteCounter: %d\n", siteCounter );
|
|
||||||
trc( "siteVariant: %d\n", siteVariant );
|
|
||||||
trc( "siteType: %d\n", siteType );
|
|
||||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
|
||||||
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
|
||||||
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
|
||||||
mpw_hex_l( htonl( siteCounter ) ),
|
|
||||||
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
|
||||||
|
|
||||||
// Calculate the site seed.
|
|
||||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
|
||||||
size_t sitePasswordInfoSize = 0;
|
|
||||||
uint8_t *sitePasswordInfo = NULL;
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_utf8_strlen( siteName ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
|
||||||
if (siteContext) {
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_utf8_strlen( siteContext ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
|
||||||
}
|
|
||||||
if (!sitePasswordInfo) {
|
|
||||||
ftl( "Could not allocate site seed info: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( sitePasswordInfo, sitePasswordInfoSize ) );
|
|
||||||
|
|
||||||
const char *sitePasswordSeed = (const char *)mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
|
|
||||||
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
|
|
||||||
if (!sitePasswordSeed) {
|
|
||||||
ftl( "Could not allocate site seed: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "sitePasswordSeed ID: %s\n", mpw_id_buf( sitePasswordSeed, 32 ) );
|
|
||||||
|
|
||||||
// Determine the template.
|
|
||||||
const char *template = mpw_templateForType_v0( siteType, htons( sitePasswordSeed[0] ) );
|
|
||||||
trc( "type %d, template: %s\n", siteType, template );
|
|
||||||
if (strlen( template ) > 32) {
|
|
||||||
ftl( "Template too long for password seed: %lu", strlen( template ) );
|
|
||||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode the password from the seed using the template.
|
|
||||||
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
|
|
||||||
for (size_t c = 0; c < strlen( template ); ++c) {
|
|
||||||
sitePassword[c] = mpw_characterFromClass_v0( template[c], htons( sitePasswordSeed[c + 1] ) );
|
|
||||||
trc( "class %c, index %u (0x%02X) -> character: %c\n",
|
|
||||||
template[c], htons( sitePasswordSeed[c + 1] ), htons( sitePasswordSeed[c + 1] ), sitePassword[c] );
|
|
||||||
}
|
|
||||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
|
||||||
|
|
||||||
return sitePassword;
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-algorithm.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include "mpw-types.h"
|
|
||||||
#include "mpw-util.h"
|
|
||||||
|
|
||||||
#define MP_N 32768
|
|
||||||
#define MP_r 8
|
|
||||||
#define MP_p 2
|
|
||||||
#define MP_hash PearlHashSHA256
|
|
||||||
|
|
||||||
static const uint8_t *mpw_masterKeyForUser_v1(const char *fullName, const char *masterPassword) {
|
|
||||||
|
|
||||||
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
|
||||||
trc( "algorithm: v%d\n", 1 );
|
|
||||||
trc( "fullName: %s (%zu)\n", fullName, mpw_utf8_strlen( fullName ) );
|
|
||||||
trc( "masterPassword: %s\n", masterPassword );
|
|
||||||
trc( "key scope: %s\n", mpKeyScope );
|
|
||||||
|
|
||||||
// Calculate the master key salt.
|
|
||||||
// masterKeySalt = mpKeyScope . #fullName . fullName
|
|
||||||
size_t masterKeySaltSize = 0;
|
|
||||||
uint8_t *masterKeySalt = NULL;
|
|
||||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
|
||||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( mpw_utf8_strlen( fullName ) ) );
|
|
||||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
|
|
||||||
if (!masterKeySalt) {
|
|
||||||
ftl( "Could not allocate master key salt: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "masterKeySalt ID: %s\n", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
|
|
||||||
|
|
||||||
// Calculate the master key.
|
|
||||||
// masterKey = scrypt( masterPassword, masterKeySalt )
|
|
||||||
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
|
|
||||||
mpw_free( masterKeySalt, masterKeySaltSize );
|
|
||||||
if (!masterKey) {
|
|
||||||
ftl( "Could not allocate master key: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "masterKey ID: %s\n", mpw_id_buf( masterKey, MP_dkLen ) );
|
|
||||||
|
|
||||||
return masterKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *mpw_passwordForSite_v1(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
|
||||||
const MPSiteVariant siteVariant, const char *siteContext) {
|
|
||||||
|
|
||||||
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
|
||||||
trc( "algorithm: v%d\n", 1 );
|
|
||||||
trc( "siteName: %s\n", siteName );
|
|
||||||
trc( "siteCounter: %d\n", siteCounter );
|
|
||||||
trc( "siteVariant: %d\n", siteVariant );
|
|
||||||
trc( "siteType: %d\n", siteType );
|
|
||||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
|
||||||
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
|
||||||
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
|
||||||
mpw_hex_l( htonl( siteCounter ) ),
|
|
||||||
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
|
||||||
|
|
||||||
// Calculate the site seed.
|
|
||||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
|
||||||
size_t sitePasswordInfoSize = 0;
|
|
||||||
uint8_t *sitePasswordInfo = NULL;
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_utf8_strlen( siteName ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
|
||||||
if (siteContext) {
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_utf8_strlen( siteContext ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
|
||||||
}
|
|
||||||
if (!sitePasswordInfo) {
|
|
||||||
ftl( "Could not allocate site seed info: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( sitePasswordInfo, sitePasswordInfoSize ) );
|
|
||||||
|
|
||||||
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
|
|
||||||
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
|
|
||||||
if (!sitePasswordSeed) {
|
|
||||||
ftl( "Could not allocate site seed: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "sitePasswordSeed ID: %s\n", mpw_id_buf( sitePasswordSeed, 32 ) );
|
|
||||||
|
|
||||||
// Determine the template.
|
|
||||||
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
|
|
||||||
trc( "type %d, template: %s\n", siteType, template );
|
|
||||||
if (strlen( template ) > 32) {
|
|
||||||
ftl( "Template too long for password seed: %lu", strlen( template ) );
|
|
||||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode the password from the seed using the template.
|
|
||||||
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
|
|
||||||
for (size_t c = 0; c < strlen( template ); ++c) {
|
|
||||||
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
|
|
||||||
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
|
|
||||||
sitePassword[c] );
|
|
||||||
}
|
|
||||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
|
||||||
|
|
||||||
return sitePassword;
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-algorithm.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include "mpw-types.h"
|
|
||||||
#include "mpw-util.h"
|
|
||||||
|
|
||||||
#define MP_N 32768
|
|
||||||
#define MP_r 8
|
|
||||||
#define MP_p 2
|
|
||||||
#define MP_hash PearlHashSHA256
|
|
||||||
|
|
||||||
static const uint8_t *mpw_masterKeyForUser_v2(const char *fullName, const char *masterPassword) {
|
|
||||||
|
|
||||||
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
|
||||||
trc( "algorithm: v%d\n", 2 );
|
|
||||||
trc( "fullName: %s (%zu)\n", fullName, mpw_utf8_strlen( fullName ) );
|
|
||||||
trc( "masterPassword: %s\n", masterPassword );
|
|
||||||
trc( "key scope: %s\n", mpKeyScope );
|
|
||||||
|
|
||||||
// Calculate the master key salt.
|
|
||||||
// masterKeySalt = mpKeyScope . #fullName . fullName
|
|
||||||
size_t masterKeySaltSize = 0;
|
|
||||||
uint8_t *masterKeySalt = NULL;
|
|
||||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
|
||||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( mpw_utf8_strlen( fullName ) ) );
|
|
||||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
|
|
||||||
if (!masterKeySalt) {
|
|
||||||
ftl( "Could not allocate master key salt: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "masterKeySalt ID: %s\n", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
|
|
||||||
|
|
||||||
// Calculate the master key.
|
|
||||||
// masterKey = scrypt( masterPassword, masterKeySalt )
|
|
||||||
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
|
|
||||||
mpw_free( masterKeySalt, masterKeySaltSize );
|
|
||||||
if (!masterKey) {
|
|
||||||
ftl( "Could not allocate master key: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "masterKey ID: %s\n", mpw_id_buf( masterKey, MP_dkLen ) );
|
|
||||||
|
|
||||||
return masterKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *mpw_passwordForSite_v2(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
|
||||||
const MPSiteVariant siteVariant, const char *siteContext) {
|
|
||||||
|
|
||||||
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
|
||||||
trc( "algorithm: v%d\n", 2 );
|
|
||||||
trc( "siteName: %s\n", siteName );
|
|
||||||
trc( "siteCounter: %d\n", siteCounter );
|
|
||||||
trc( "siteVariant: %d\n", siteVariant );
|
|
||||||
trc( "siteType: %d\n", siteType );
|
|
||||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
|
||||||
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
|
||||||
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
|
||||||
mpw_hex_l( htonl( siteCounter ) ),
|
|
||||||
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
|
||||||
|
|
||||||
// Calculate the site seed.
|
|
||||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
|
||||||
size_t sitePasswordInfoSize = 0;
|
|
||||||
uint8_t *sitePasswordInfo = NULL;
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
|
||||||
if (siteContext) {
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
|
||||||
}
|
|
||||||
if (!sitePasswordInfo) {
|
|
||||||
ftl( "Could not allocate site seed info: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( sitePasswordInfo, sitePasswordInfoSize ) );
|
|
||||||
|
|
||||||
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
|
|
||||||
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
|
|
||||||
if (!sitePasswordSeed) {
|
|
||||||
ftl( "Could not allocate site seed: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "sitePasswordSeed ID: %s\n", mpw_id_buf( sitePasswordSeed, 32 ) );
|
|
||||||
|
|
||||||
// Determine the template.
|
|
||||||
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
|
|
||||||
trc( "type %d, template: %s\n", siteType, template );
|
|
||||||
if (strlen( template ) > 32) {
|
|
||||||
ftl( "Template too long for password seed: %lu", strlen( template ) );
|
|
||||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode the password from the seed using the template.
|
|
||||||
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
|
|
||||||
for (size_t c = 0; c < strlen( template ); ++c) {
|
|
||||||
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
|
|
||||||
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
|
|
||||||
sitePassword[c] );
|
|
||||||
}
|
|
||||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
|
||||||
|
|
||||||
return sitePassword;
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-algorithm.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include "mpw-types.h"
|
|
||||||
#include "mpw-util.h"
|
|
||||||
|
|
||||||
#define MP_N 32768
|
|
||||||
#define MP_r 8
|
|
||||||
#define MP_p 2
|
|
||||||
#define MP_hash PearlHashSHA256
|
|
||||||
|
|
||||||
static const uint8_t *mpw_masterKeyForUser_v3(const char *fullName, const char *masterPassword) {
|
|
||||||
|
|
||||||
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
|
||||||
trc( "algorithm: v%d\n", 3 );
|
|
||||||
trc( "fullName: %s (%zu)\n", fullName, strlen( fullName ) );
|
|
||||||
trc( "masterPassword: %s\n", masterPassword );
|
|
||||||
trc( "key scope: %s\n", mpKeyScope );
|
|
||||||
|
|
||||||
// Calculate the master key salt.
|
|
||||||
// masterKeySalt = mpKeyScope . #fullName . fullName
|
|
||||||
size_t masterKeySaltSize = 0;
|
|
||||||
uint8_t *masterKeySalt = NULL;
|
|
||||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
|
||||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( strlen( fullName ) ) );
|
|
||||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
|
|
||||||
if (!masterKeySalt) {
|
|
||||||
ftl( "Could not allocate master key salt: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "masterKeySalt ID: %s\n", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
|
|
||||||
|
|
||||||
// Calculate the master key.
|
|
||||||
// masterKey = scrypt( masterPassword, masterKeySalt )
|
|
||||||
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
|
|
||||||
mpw_free( masterKeySalt, masterKeySaltSize );
|
|
||||||
if (!masterKey) {
|
|
||||||
ftl( "Could not allocate master key: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "masterKey ID: %s\n", mpw_id_buf( masterKey, MP_dkLen ) );
|
|
||||||
|
|
||||||
return masterKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *mpw_passwordForSite_v3(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
|
||||||
const MPSiteVariant siteVariant, const char *siteContext) {
|
|
||||||
|
|
||||||
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
|
||||||
trc( "algorithm: v%d\n", 3 );
|
|
||||||
trc( "siteName: %s\n", siteName );
|
|
||||||
trc( "siteCounter: %d\n", siteCounter );
|
|
||||||
trc( "siteVariant: %d\n", siteVariant );
|
|
||||||
trc( "siteType: %d\n", siteType );
|
|
||||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
|
||||||
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
|
||||||
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
|
||||||
mpw_hex_l( htonl( siteCounter ) ),
|
|
||||||
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
|
||||||
|
|
||||||
// Calculate the site seed.
|
|
||||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
|
||||||
size_t sitePasswordInfoSize = 0;
|
|
||||||
uint8_t *sitePasswordInfo = NULL;
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
|
||||||
if (siteContext) {
|
|
||||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
|
|
||||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
|
||||||
}
|
|
||||||
if (!sitePasswordInfo) {
|
|
||||||
ftl( "Could not allocate site seed info: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( sitePasswordInfo, sitePasswordInfoSize ) );
|
|
||||||
|
|
||||||
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
|
|
||||||
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
|
|
||||||
if (!sitePasswordSeed) {
|
|
||||||
ftl( "Could not allocate site seed: %d\n", errno );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
trc( "sitePasswordSeed ID: %s\n", mpw_id_buf( sitePasswordSeed, 32 ) );
|
|
||||||
|
|
||||||
// Determine the template.
|
|
||||||
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
|
|
||||||
trc( "type %d, template: %s\n", siteType, template );
|
|
||||||
if (strlen( template ) > 32) {
|
|
||||||
ftl( "Template too long for password seed: %lu", strlen( template ) );
|
|
||||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode the password from the seed using the template.
|
|
||||||
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
|
|
||||||
for (size_t c = 0; c < strlen( template ); ++c) {
|
|
||||||
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
|
|
||||||
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
|
|
||||||
sitePassword[c] );
|
|
||||||
}
|
|
||||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
|
||||||
|
|
||||||
return sitePassword;
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-bench.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#include <scrypt/sha256.h>
|
|
||||||
#include <bcrypt/ow-crypt.h>
|
|
||||||
|
|
||||||
#include "mpw-algorithm.h"
|
|
||||||
#include "mpw-util.h"
|
|
||||||
|
|
||||||
#define MP_N 32768
|
|
||||||
#define MP_r 8
|
|
||||||
#define MP_p 2
|
|
||||||
#define MP_dkLen 64
|
|
||||||
#define MP_hash PearlHashSHA256
|
|
||||||
|
|
||||||
static void mpw_getTime(struct timeval *time) {
|
|
||||||
|
|
||||||
if (gettimeofday( time, NULL ) != 0)
|
|
||||||
ftl( "Could not get time: %d\n", errno );
|
|
||||||
}
|
|
||||||
|
|
||||||
static const double mpw_showSpeed(struct timeval startTime, const unsigned int iterations, const char *operation) {
|
|
||||||
|
|
||||||
struct timeval endTime;
|
|
||||||
mpw_getTime( &endTime );
|
|
||||||
|
|
||||||
const time_t dsec = (endTime.tv_sec - startTime.tv_sec);
|
|
||||||
const suseconds_t dusec = (endTime.tv_usec - startTime.tv_usec);
|
|
||||||
const double elapsed = dsec + dusec / 1000000.;
|
|
||||||
const double speed = iterations / elapsed;
|
|
||||||
|
|
||||||
fprintf( stderr, " done. " );
|
|
||||||
fprintf( stdout, "%d %s iterations in %llds %lldµs -> %.2f/s\n", iterations, operation, (long long)dsec, (long long)dusec, speed );
|
|
||||||
|
|
||||||
return speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *const argv[]) {
|
|
||||||
|
|
||||||
const char *fullName = "Robert Lee Mitchel";
|
|
||||||
const char *masterPassword = "banana colored duckling";
|
|
||||||
const char *siteName = "masterpasswordapp.com";
|
|
||||||
const uint32_t siteCounter = 1;
|
|
||||||
const MPSiteType siteType = MPSiteTypeGeneratedLong;
|
|
||||||
const MPSiteVariant siteVariant = MPSiteVariantPassword;
|
|
||||||
const char *siteContext = NULL;
|
|
||||||
struct timeval startTime;
|
|
||||||
unsigned int iterations;
|
|
||||||
float percent;
|
|
||||||
const uint8_t *masterKey;
|
|
||||||
|
|
||||||
// Start HMAC-SHA-256
|
|
||||||
// Similar to phase-two of mpw
|
|
||||||
uint8_t *sitePasswordInfo = malloc( 128 );
|
|
||||||
iterations = 3000000;
|
|
||||||
masterKey = mpw_masterKeyForUser( fullName, masterPassword, MPAlgorithmVersionCurrent );
|
|
||||||
if (!masterKey) {
|
|
||||||
ftl( "Could not allocate master key: %d\n", errno );
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
mpw_getTime( &startTime );
|
|
||||||
for (int i = 1; i <= iterations; ++i) {
|
|
||||||
free( (void *)mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, 128 ) );
|
|
||||||
|
|
||||||
if (modff(100.f * i / iterations, &percent) == 0)
|
|
||||||
fprintf( stderr, "\rhmac-sha-256: iteration %d / %d (%.0f%%)..", i, iterations, percent );
|
|
||||||
}
|
|
||||||
const double hmacSha256Speed = mpw_showSpeed( startTime, iterations, "hmac-sha-256" );
|
|
||||||
free( (void *)masterKey );
|
|
||||||
|
|
||||||
// Start BCrypt
|
|
||||||
// Similar to phase-one of mpw
|
|
||||||
int bcrypt_cost = 9;
|
|
||||||
iterations = 1000;
|
|
||||||
mpw_getTime( &startTime );
|
|
||||||
for (int i = 1; i <= iterations; ++i) {
|
|
||||||
crypt( masterPassword, crypt_gensalt( "$2b$", bcrypt_cost, fullName, strlen( fullName ) ) );
|
|
||||||
|
|
||||||
if (modff(100.f * i / iterations, &percent) == 0)
|
|
||||||
fprintf( stderr, "\rbcrypt (cost %d): iteration %d / %d (%.0f%%)..", bcrypt_cost, i, iterations, percent );
|
|
||||||
}
|
|
||||||
const double bcrypt9Speed = mpw_showSpeed( startTime, iterations, "bcrypt9" );
|
|
||||||
|
|
||||||
// Start SCrypt
|
|
||||||
// Phase one of mpw
|
|
||||||
iterations = 50;
|
|
||||||
mpw_getTime( &startTime );
|
|
||||||
for (int i = 1; i <= iterations; ++i) {
|
|
||||||
free( (void *)mpw_masterKeyForUser( fullName, masterPassword, MPAlgorithmVersionCurrent ) );
|
|
||||||
|
|
||||||
if (modff(100.f * i / iterations, &percent) == 0)
|
|
||||||
fprintf( stderr, "\rscrypt_mpw: iteration %d / %d (%.0f%%)..", i, iterations, percent );
|
|
||||||
}
|
|
||||||
const double scryptSpeed = mpw_showSpeed( startTime, iterations, "scrypt_mpw" );
|
|
||||||
|
|
||||||
// Start MPW
|
|
||||||
// Both phases of mpw
|
|
||||||
iterations = 50;
|
|
||||||
mpw_getTime( &startTime );
|
|
||||||
for (int i = 1; i <= iterations; ++i) {
|
|
||||||
masterKey = mpw_masterKeyForUser( fullName, masterPassword, MPAlgorithmVersionCurrent );
|
|
||||||
if (!masterKey) {
|
|
||||||
ftl( "Could not allocate master key: %d\n", errno );
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
free( (void *)mpw_passwordForSite(
|
|
||||||
masterKey, siteName, siteType, siteCounter, siteVariant, siteContext, MPAlgorithmVersionCurrent ) );
|
|
||||||
free( (void *)masterKey );
|
|
||||||
|
|
||||||
if (modff(100.f * i / iterations, &percent) == 0)
|
|
||||||
fprintf( stderr, "\rmpw: iteration %d / %d (%.0f%%)..", i, iterations, percent );
|
|
||||||
}
|
|
||||||
const double mpwSpeed = mpw_showSpeed( startTime, iterations, "mpw" );
|
|
||||||
|
|
||||||
// Summarize.
|
|
||||||
fprintf( stdout, "\n== SUMMARY ==\nOn this machine,\n" );
|
|
||||||
fprintf( stdout, " - mpw is %f times slower than hmac-sha-256 (reference: 320000 on an MBP Late 2013).\n", hmacSha256Speed / mpwSpeed );
|
|
||||||
fprintf( stdout, " - mpw is %f times slower than bcrypt (cost 9) (reference: 22 on an MBP Late 2013).\n", bcrypt9Speed / mpwSpeed );
|
|
||||||
fprintf( stdout, " - scrypt is %f times slower than bcrypt (cost 9) (reference: 22 on an MBP Late 2013).\n", bcrypt9Speed / scryptSpeed );
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
#define _GNU_SOURCE
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#if defined(READLINE)
|
|
||||||
#include <readline/readline.h>
|
|
||||||
#elif defined(EDITLINE)
|
|
||||||
#include <histedit.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "mpw-algorithm.h"
|
|
||||||
#include "mpw-util.h"
|
|
||||||
|
|
||||||
#define MP_env_fullname "MP_FULLNAME"
|
|
||||||
#define MP_env_sitetype "MP_SITETYPE"
|
|
||||||
#define MP_env_sitecounter "MP_SITECOUNTER"
|
|
||||||
#define MP_env_algorithm "MP_ALGORITHM"
|
|
||||||
|
|
||||||
static void usage() {
|
|
||||||
|
|
||||||
fprintf( stderr, "Usage: mpw [-u name] [-t type] [-c counter] [-a algorithm] [-V variant] [-C context] [-v|-q] [-h] site\n\n" );
|
|
||||||
fprintf( stderr, " -u name Specify the full name of the user.\n"
|
|
||||||
" Defaults to %s in env or prompts.\n\n", MP_env_fullname );
|
|
||||||
fprintf( stderr, " -t type Specify the password's template.\n"
|
|
||||||
" Defaults to %s in env or 'long' for password, 'name' for login.\n"
|
|
||||||
" x, max, maximum | 20 characters, contains symbols.\n"
|
|
||||||
" l, long | Copy-friendly, 14 characters, contains symbols.\n"
|
|
||||||
" m, med, medium | Copy-friendly, 8 characters, contains symbols.\n"
|
|
||||||
" b, basic | 8 characters, no symbols.\n"
|
|
||||||
" s, short | Copy-friendly, 4 characters, no symbols.\n"
|
|
||||||
" i, pin | 4 numbers.\n"
|
|
||||||
" n, name | 9 letter name.\n"
|
|
||||||
" p, phrase | 20 character sentence.\n\n", MP_env_sitetype );
|
|
||||||
fprintf( stderr, " -c counter The value of the counter.\n"
|
|
||||||
" Defaults to %s in env or 1.\n\n", MP_env_sitecounter );
|
|
||||||
fprintf( stderr, " -a version The algorithm version to use.\n"
|
|
||||||
" Defaults to %s in env or %d.\n\n", MP_env_algorithm, MPAlgorithmVersionCurrent );
|
|
||||||
fprintf( stderr, " -V variant The kind of content to generate.\n"
|
|
||||||
" Defaults to 'password'.\n"
|
|
||||||
" p, password | The password to log in with.\n"
|
|
||||||
" l, login | The username to log in as.\n"
|
|
||||||
" a, answer | The answer to a security question.\n\n" );
|
|
||||||
fprintf( stderr, " -C context A variant-specific context.\n"
|
|
||||||
" Defaults to empty.\n"
|
|
||||||
" -V p, password | Doesn't currently use a context.\n"
|
|
||||||
" -V l, login | Doesn't currently use a context.\n"
|
|
||||||
" -V a, answer | Empty for a universal site answer or\n"
|
|
||||||
" | the most significant word(s) of the question.\n\n" );
|
|
||||||
fprintf( stderr, " -v Increase output verbosity (can be repeated).\n\n" );
|
|
||||||
fprintf( stderr, " -q Decrease output verbosity (can be repeated).\n\n" );
|
|
||||||
fprintf( stderr, " ENVIRONMENT\n\n"
|
|
||||||
" %-14s | The full name of the user (see -u).\n"
|
|
||||||
" %-14s | The default password template (see -t).\n"
|
|
||||||
" %-14s | The default counter value (see -c).\n"
|
|
||||||
" %-14s | The default algorithm version (see -a).\n\n",
|
|
||||||
MP_env_fullname, MP_env_sitetype, MP_env_sitecounter, MP_env_algorithm );
|
|
||||||
exit( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *homedir(const char *filename) {
|
|
||||||
|
|
||||||
char *homedir = NULL;
|
|
||||||
struct passwd *passwd = getpwuid( getuid() );
|
|
||||||
if (passwd)
|
|
||||||
homedir = passwd->pw_dir;
|
|
||||||
if (!homedir)
|
|
||||||
homedir = getenv( "HOME" );
|
|
||||||
if (!homedir)
|
|
||||||
homedir = getcwd( NULL, 0 );
|
|
||||||
|
|
||||||
char *homefile = NULL;
|
|
||||||
asprintf( &homefile, "%s/%s", homedir, filename );
|
|
||||||
return homefile;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *getline_prompt(const char *prompt) {
|
|
||||||
|
|
||||||
char *buf = NULL;
|
|
||||||
size_t bufSize = 0;
|
|
||||||
ssize_t lineSize;
|
|
||||||
fprintf( stderr, "%s", prompt );
|
|
||||||
fprintf( stderr, " " );
|
|
||||||
if ((lineSize = getline( &buf, &bufSize, stdin )) <= 1) {
|
|
||||||
free( buf );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
buf[lineSize - 1] = 0;
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *const argv[]) {
|
|
||||||
|
|
||||||
// Read the environment.
|
|
||||||
const char *fullName = NULL;
|
|
||||||
const char *masterPassword = NULL;
|
|
||||||
const char *siteName = NULL;
|
|
||||||
MPSiteType siteType = MPSiteTypeGeneratedLong;
|
|
||||||
const char *siteTypeString = getenv( MP_env_sitetype );
|
|
||||||
MPSiteVariant siteVariant = MPSiteVariantPassword;
|
|
||||||
const char *siteVariantString = NULL;
|
|
||||||
const char *siteContextString = NULL;
|
|
||||||
uint32_t siteCounter = 1;
|
|
||||||
const char *siteCounterString = getenv( MP_env_sitecounter );
|
|
||||||
MPAlgorithmVersion algorithmVersion = MPAlgorithmVersionCurrent;
|
|
||||||
const char *algorithmVersionString = getenv( MP_env_algorithm );
|
|
||||||
if (algorithmVersionString && strlen( algorithmVersionString ))
|
|
||||||
if (sscanf( algorithmVersionString, "%u", &algorithmVersion ) != 1)
|
|
||||||
ftl( "Invalid %s: %s\n", MP_env_algorithm, algorithmVersionString );
|
|
||||||
|
|
||||||
// Read the options.
|
|
||||||
for (int opt; (opt = getopt( argc, argv, "u:P:t:c:V:a:C:vqh" )) != -1;)
|
|
||||||
switch (opt) {
|
|
||||||
case 'u':
|
|
||||||
fullName = strdup( optarg );
|
|
||||||
break;
|
|
||||||
case 'P':
|
|
||||||
// Do not use this. Passing your master password via the command-line
|
|
||||||
// is insecure. This is here for non-interactive testing purposes only.
|
|
||||||
masterPassword = strcpy( malloc( strlen( optarg ) + 1 ), optarg );
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
siteTypeString = optarg;
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
siteCounterString = optarg;
|
|
||||||
break;
|
|
||||||
case 'V':
|
|
||||||
siteVariantString = optarg;
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
if (sscanf( optarg, "%u", &algorithmVersion ) != 1)
|
|
||||||
ftl( "Not a version: %s\n", optarg );
|
|
||||||
break;
|
|
||||||
case 'C':
|
|
||||||
siteContextString = optarg;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
++mpw_verbosity;
|
|
||||||
break;
|
|
||||||
case 'q':
|
|
||||||
--mpw_verbosity;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
usage();
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
switch (optopt) {
|
|
||||||
case 'u':
|
|
||||||
ftl( "Missing full name to option: -%c\n", optopt );
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
ftl( "Missing type name to option: -%c\n", optopt );
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
ftl( "Missing counter value to option: -%c\n", optopt );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ftl( "Unknown option: -%c\n", optopt );
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ftl("Unexpected option: %c", opt);
|
|
||||||
}
|
|
||||||
if (optind < argc)
|
|
||||||
siteName = strdup( argv[optind] );
|
|
||||||
|
|
||||||
// Convert and validate input.
|
|
||||||
if (!fullName && (fullName = getenv( MP_env_fullname )))
|
|
||||||
fullName = strdup( fullName );
|
|
||||||
if (!fullName && !(fullName = getline_prompt( "Your full name:" )))
|
|
||||||
ftl( "Missing full name.\n" );
|
|
||||||
if (!siteName && !(siteName = getline_prompt( "Site name:" )))
|
|
||||||
ftl( "Missing site name.\n" );
|
|
||||||
if (siteCounterString)
|
|
||||||
siteCounter = (uint32_t)atol( siteCounterString );
|
|
||||||
if (siteCounter < 1)
|
|
||||||
ftl( "Invalid site counter: %d\n", siteCounter );
|
|
||||||
if (siteVariantString)
|
|
||||||
siteVariant = mpw_variantWithName( siteVariantString );
|
|
||||||
if (siteVariant == MPSiteVariantLogin)
|
|
||||||
siteType = MPSiteTypeGeneratedName;
|
|
||||||
if (siteVariant == MPSiteVariantAnswer)
|
|
||||||
siteType = MPSiteTypeGeneratedPhrase;
|
|
||||||
if (siteTypeString)
|
|
||||||
siteType = mpw_typeWithName( siteTypeString );
|
|
||||||
trc( "algorithmVersion: %u\n", algorithmVersion );
|
|
||||||
|
|
||||||
// Read the master password.
|
|
||||||
char *mpwConfigPath = homedir( ".mpw" );
|
|
||||||
if (!mpwConfigPath)
|
|
||||||
ftl( "Couldn't resolve path for configuration file: %d\n", errno );
|
|
||||||
trc( "mpwConfigPath: %s\n", mpwConfigPath );
|
|
||||||
FILE *mpwConfig = fopen( mpwConfigPath, "r" );
|
|
||||||
free( mpwConfigPath );
|
|
||||||
if (mpwConfig) {
|
|
||||||
char *line = NULL;
|
|
||||||
size_t linecap = 0;
|
|
||||||
while (getline( &line, &linecap, mpwConfig ) > 0) {
|
|
||||||
char *lineData = line;
|
|
||||||
if (strcmp( strsep( &lineData, ":" ), fullName ) == 0) {
|
|
||||||
masterPassword = strcpy( malloc( strlen( lineData ) ), strsep( &lineData, "\n" ) );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mpw_free( line, linecap );
|
|
||||||
}
|
|
||||||
while (!masterPassword || !strlen(masterPassword))
|
|
||||||
masterPassword = getpass( "Your master password: " );
|
|
||||||
|
|
||||||
// Summarize operation.
|
|
||||||
const char *identicon = mpw_identicon( fullName, masterPassword );
|
|
||||||
fprintf( stderr, "%s's password for %s:\n[ %s ]: ", fullName, siteName, identicon );
|
|
||||||
mpw_free_string( identicon );
|
|
||||||
|
|
||||||
// Output the password.
|
|
||||||
const uint8_t *masterKey = mpw_masterKeyForUser(
|
|
||||||
fullName, masterPassword, algorithmVersion );
|
|
||||||
mpw_free_string( masterPassword );
|
|
||||||
mpw_free_string( fullName );
|
|
||||||
if (!masterKey)
|
|
||||||
ftl( "Couldn't derive master key." );
|
|
||||||
|
|
||||||
const char *sitePassword = mpw_passwordForSite(
|
|
||||||
masterKey, siteName, siteType, siteCounter, siteVariant, siteContextString, algorithmVersion );
|
|
||||||
mpw_free( masterKey, MP_dkLen );
|
|
||||||
mpw_free_string( siteName );
|
|
||||||
if (!sitePassword)
|
|
||||||
ftl( "Couldn't derive site password." );
|
|
||||||
|
|
||||||
fprintf( stdout, "%s\n", sitePassword );
|
|
||||||
mpw_free_string( sitePassword );
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-tests-util.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-21.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <libxml/parser.h>
|
|
||||||
|
|
||||||
xmlNodePtr mpw_xmlTestCaseNode(
|
|
||||||
xmlNodePtr testCaseNode, const char *nodeName);
|
|
||||||
xmlChar *mpw_xmlTestCaseString(
|
|
||||||
xmlNodePtr context, const char *nodeName);
|
|
||||||
uint32_t mpw_xmlTestCaseInteger(
|
|
||||||
xmlNodePtr context, const char *nodeName);
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#define _GNU_SOURCE
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); exit(2); } while (0)
|
|
||||||
|
|
||||||
#include "mpw-algorithm.h"
|
|
||||||
#include "mpw-util.h"
|
|
||||||
|
|
||||||
#include "mpw-tests-util.h"
|
|
||||||
|
|
||||||
int main(int argc, char *const argv[]) {
|
|
||||||
|
|
||||||
int failedTests = 0;
|
|
||||||
|
|
||||||
xmlNodePtr tests = xmlDocGetRootElement( xmlParseFile( "mpw_tests.xml" ) );
|
|
||||||
for (xmlNodePtr testCase = tests->children; testCase; testCase = testCase->next) {
|
|
||||||
if (testCase->type != XML_ELEMENT_NODE || xmlStrcmp( testCase->name, BAD_CAST "case" ) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Read in the test case.
|
|
||||||
xmlChar *id = mpw_xmlTestCaseString( testCase, "id" );
|
|
||||||
uint32_t algorithm = mpw_xmlTestCaseInteger( testCase, "algorithm" );
|
|
||||||
xmlChar *fullName = mpw_xmlTestCaseString( testCase, "fullName" );
|
|
||||||
xmlChar *masterPassword = mpw_xmlTestCaseString( testCase, "masterPassword" );
|
|
||||||
xmlChar *keyID = mpw_xmlTestCaseString( testCase, "keyID" );
|
|
||||||
xmlChar *siteName = mpw_xmlTestCaseString( testCase, "siteName" );
|
|
||||||
uint32_t siteCounter = mpw_xmlTestCaseInteger( testCase, "siteCounter" );
|
|
||||||
xmlChar *siteTypeString = mpw_xmlTestCaseString( testCase, "siteType" );
|
|
||||||
xmlChar *siteVariantString = mpw_xmlTestCaseString( testCase, "siteVariant" );
|
|
||||||
xmlChar *siteContext = mpw_xmlTestCaseString( testCase, "siteContext" );
|
|
||||||
xmlChar *result = mpw_xmlTestCaseString( testCase, "result" );
|
|
||||||
|
|
||||||
MPSiteType siteType = mpw_typeWithName( (char *)siteTypeString );
|
|
||||||
MPSiteVariant siteVariant = mpw_variantWithName( (char *)siteVariantString );
|
|
||||||
|
|
||||||
// Run the test case.
|
|
||||||
fprintf( stdout, "test case %s... ", id );
|
|
||||||
if (!xmlStrlen( result )) {
|
|
||||||
fprintf( stdout, "abstract.\n" );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. calculate the master key.
|
|
||||||
const uint8_t *masterKey = mpw_masterKeyForUser(
|
|
||||||
(char *)fullName, (char *)masterPassword, algorithm );
|
|
||||||
if (!masterKey)
|
|
||||||
ftl( "Couldn't derive master key." );
|
|
||||||
|
|
||||||
// 2. calculate the site password.
|
|
||||||
const char *sitePassword = mpw_passwordForSite(
|
|
||||||
masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext, algorithm );
|
|
||||||
mpw_free( masterKey, MP_dkLen );
|
|
||||||
if (!sitePassword)
|
|
||||||
ftl( "Couldn't derive site password." );
|
|
||||||
|
|
||||||
// Check the result.
|
|
||||||
if (xmlStrcmp( result, BAD_CAST sitePassword ) == 0)
|
|
||||||
fprintf( stdout, "pass.\n" );
|
|
||||||
|
|
||||||
else {
|
|
||||||
++failedTests;
|
|
||||||
fprintf( stdout, "FAILED! (got %s != expected %s)\n", sitePassword, result );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free test case.
|
|
||||||
mpw_free_string( sitePassword );
|
|
||||||
xmlFree( id );
|
|
||||||
xmlFree( fullName );
|
|
||||||
xmlFree( masterPassword );
|
|
||||||
xmlFree( keyID );
|
|
||||||
xmlFree( siteName );
|
|
||||||
xmlFree( siteTypeString );
|
|
||||||
xmlFree( siteVariantString );
|
|
||||||
xmlFree( siteContext );
|
|
||||||
xmlFree( result );
|
|
||||||
}
|
|
||||||
|
|
||||||
return failedTests;
|
|
||||||
}
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-types.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2012-02-01.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#ifdef COLOR
|
|
||||||
#include <curses.h>
|
|
||||||
#include <term.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "mpw-types.h"
|
|
||||||
#include "mpw-util.h"
|
|
||||||
|
|
||||||
const MPSiteType mpw_typeWithName(const char *typeName) {
|
|
||||||
|
|
||||||
// Lower-case and trim optionally leading "Generated" string from typeName to standardize it.
|
|
||||||
size_t stdTypeNameOffset = 0;
|
|
||||||
size_t stdTypeNameSize = strlen( typeName );
|
|
||||||
if (strstr(typeName, "Generated" ) == typeName)
|
|
||||||
stdTypeNameSize -= (stdTypeNameOffset = strlen( "Generated" ));
|
|
||||||
char stdTypeName[stdTypeNameSize + 1];
|
|
||||||
for (size_t c = 0; c < stdTypeNameSize; ++c)
|
|
||||||
stdTypeName[c] = (char)tolower( typeName[c + stdTypeNameOffset] );
|
|
||||||
stdTypeName[stdTypeNameSize] = '\0';
|
|
||||||
|
|
||||||
// Find what site type is represented by the type name.
|
|
||||||
if (0 == strcmp( stdTypeName, "x" ) || 0 == strcmp( stdTypeName, "max" ) || 0 == strcmp( stdTypeName, "maximum" ))
|
|
||||||
return MPSiteTypeGeneratedMaximum;
|
|
||||||
if (0 == strcmp( stdTypeName, "l" ) || 0 == strcmp( stdTypeName, "long" ))
|
|
||||||
return MPSiteTypeGeneratedLong;
|
|
||||||
if (0 == strcmp( stdTypeName, "m" ) || 0 == strcmp( stdTypeName, "med" ) || 0 == strcmp( stdTypeName, "medium" ))
|
|
||||||
return MPSiteTypeGeneratedMedium;
|
|
||||||
if (0 == strcmp( stdTypeName, "b" ) || 0 == strcmp( stdTypeName, "basic" ))
|
|
||||||
return MPSiteTypeGeneratedBasic;
|
|
||||||
if (0 == strcmp( stdTypeName, "s" ) || 0 == strcmp( stdTypeName, "short" ))
|
|
||||||
return MPSiteTypeGeneratedShort;
|
|
||||||
if (0 == strcmp( stdTypeName, "i" ) || 0 == strcmp( stdTypeName, "pin" ))
|
|
||||||
return MPSiteTypeGeneratedPIN;
|
|
||||||
if (0 == strcmp( stdTypeName, "n" ) || 0 == strcmp( stdTypeName, "name" ))
|
|
||||||
return MPSiteTypeGeneratedName;
|
|
||||||
if (0 == strcmp( stdTypeName, "p" ) || 0 == strcmp( stdTypeName, "phrase" ))
|
|
||||||
return MPSiteTypeGeneratedPhrase;
|
|
||||||
|
|
||||||
ftl( "Not a generated type name: %s", stdTypeName );
|
|
||||||
return MPSiteTypeGeneratedLong;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char **mpw_templatesForType(MPSiteType type, size_t *count) {
|
|
||||||
|
|
||||||
if (!(type & MPSiteTypeClassGenerated)) {
|
|
||||||
ftl( "Not a generated type: %d", type );
|
|
||||||
*count = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case MPSiteTypeGeneratedMaximum: {
|
|
||||||
return mpw_alloc_array( *count, const char *,
|
|
||||||
"anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedLong: {
|
|
||||||
return mpw_alloc_array( *count, const char *,
|
|
||||||
"CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
|
|
||||||
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
|
|
||||||
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
|
|
||||||
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
|
|
||||||
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
|
|
||||||
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
|
|
||||||
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedMedium: {
|
|
||||||
return mpw_alloc_array( *count, const char *,
|
|
||||||
"CvcnoCvc", "CvcCvcno" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedBasic: {
|
|
||||||
return mpw_alloc_array( *count, const char *,
|
|
||||||
"aaanaaan", "aannaaan", "aaannaaa" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedShort: {
|
|
||||||
return mpw_alloc_array( *count, const char *,
|
|
||||||
"Cvcn" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedPIN: {
|
|
||||||
return mpw_alloc_array( *count, const char *,
|
|
||||||
"nnnn" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedName: {
|
|
||||||
return mpw_alloc_array( *count, const char *,
|
|
||||||
"cvccvcvcv" );
|
|
||||||
}
|
|
||||||
case MPSiteTypeGeneratedPhrase: {
|
|
||||||
return mpw_alloc_array( *count, const char *,
|
|
||||||
"cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" );
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
ftl( "Unknown generated type: %d", type );
|
|
||||||
*count = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) {
|
|
||||||
|
|
||||||
size_t count = 0;
|
|
||||||
const char **templates = mpw_templatesForType( type, &count );
|
|
||||||
char const *template = count? templates[seedByte % count]: NULL;
|
|
||||||
free( templates );
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MPSiteVariant mpw_variantWithName(const char *variantName) {
|
|
||||||
|
|
||||||
// Lower-case and trim optionally leading "generated" string from typeName to standardize it.
|
|
||||||
size_t stdVariantNameSize = strlen( variantName );
|
|
||||||
char stdVariantName[stdVariantNameSize + 1];
|
|
||||||
for (size_t c = 0; c < stdVariantNameSize; ++c)
|
|
||||||
stdVariantName[c] = (char)tolower( variantName[c] );
|
|
||||||
stdVariantName[stdVariantNameSize] = '\0';
|
|
||||||
|
|
||||||
if (0 == strcmp( stdVariantName, "p" ) || 0 == strcmp( stdVariantName, "password" ))
|
|
||||||
return MPSiteVariantPassword;
|
|
||||||
if (0 == strcmp( stdVariantName, "l" ) || 0 == strcmp( stdVariantName, "login" ))
|
|
||||||
return MPSiteVariantLogin;
|
|
||||||
if (0 == strcmp( stdVariantName, "a" ) || 0 == strcmp( stdVariantName, "answer" ))
|
|
||||||
return MPSiteVariantAnswer;
|
|
||||||
|
|
||||||
fprintf( stderr, "Not a variant name: %s", stdVariantName );
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *mpw_scopeForVariant(MPSiteVariant variant) {
|
|
||||||
|
|
||||||
switch (variant) {
|
|
||||||
case MPSiteVariantPassword: {
|
|
||||||
return "com.lyndir.masterpassword";
|
|
||||||
}
|
|
||||||
case MPSiteVariantLogin: {
|
|
||||||
return "com.lyndir.masterpassword.login";
|
|
||||||
}
|
|
||||||
case MPSiteVariantAnswer: {
|
|
||||||
return "com.lyndir.masterpassword.answer";
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
fprintf( stderr, "Unknown variant: %d", variant );
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *mpw_charactersInClass(char characterClass) {
|
|
||||||
|
|
||||||
switch (characterClass) {
|
|
||||||
case 'V':
|
|
||||||
return "AEIOU";
|
|
||||||
case 'C':
|
|
||||||
return "BCDFGHJKLMNPQRSTVWXYZ";
|
|
||||||
case 'v':
|
|
||||||
return "aeiou";
|
|
||||||
case 'c':
|
|
||||||
return "bcdfghjklmnpqrstvwxyz";
|
|
||||||
case 'A':
|
|
||||||
return "AEIOUBCDFGHJKLMNPQRSTVWXYZ";
|
|
||||||
case 'a':
|
|
||||||
return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
|
|
||||||
case 'n':
|
|
||||||
return "0123456789";
|
|
||||||
case 'o':
|
|
||||||
return "@&%?,=[]_:-+*$#!'^~;()/.";
|
|
||||||
case 'x':
|
|
||||||
return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
|
|
||||||
case ' ':
|
|
||||||
return " ";
|
|
||||||
default: {
|
|
||||||
fprintf( stderr, "Unknown character class: %c", characterClass );
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char mpw_characterFromClass(char characterClass, uint8_t seedByte) {
|
|
||||||
|
|
||||||
const char *classCharacters = mpw_charactersInClass( characterClass );
|
|
||||||
return classCharacters[seedByte % strlen( classCharacters )];
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-types.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2012-02-01.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef _MPW_TYPES_H
|
|
||||||
#define _MPW_TYPES_H
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifdef NS_ENUM
|
|
||||||
#define enum(_type, _name) NS_ENUM(_type, _name)
|
|
||||||
#else
|
|
||||||
#define enum(_type, _name) _type _name; enum
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MP_dkLen 64
|
|
||||||
|
|
||||||
//// Types.
|
|
||||||
|
|
||||||
typedef enum( unsigned int, MPSiteVariant ) {
|
|
||||||
/** Generate the password to log in with. */
|
|
||||||
MPSiteVariantPassword,
|
|
||||||
/** Generate the login name to log in as. */
|
|
||||||
MPSiteVariantLogin,
|
|
||||||
/** Generate the answer to a security question. */
|
|
||||||
MPSiteVariantAnswer,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum( unsigned int, MPSiteTypeClass ) {
|
|
||||||
/** Generate the password. */
|
|
||||||
MPSiteTypeClassGenerated = 1 << 4,
|
|
||||||
/** Store the password. */
|
|
||||||
MPSiteTypeClassStored = 1 << 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum( unsigned int, MPSiteFeature ) {
|
|
||||||
/** Export the key-protected content data. */
|
|
||||||
MPSiteFeatureExportContent = 1 << 10,
|
|
||||||
/** Never export content. */
|
|
||||||
MPSiteFeatureDevicePrivate = 1 << 11,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum( unsigned int, MPSiteType) {
|
|
||||||
MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0,
|
|
||||||
MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0,
|
|
||||||
MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0,
|
|
||||||
MPSiteTypeGeneratedBasic = 0x4 | MPSiteTypeClassGenerated | 0x0,
|
|
||||||
MPSiteTypeGeneratedShort = 0x3 | MPSiteTypeClassGenerated | 0x0,
|
|
||||||
MPSiteTypeGeneratedPIN = 0x5 | MPSiteTypeClassGenerated | 0x0,
|
|
||||||
MPSiteTypeGeneratedName = 0xE | MPSiteTypeClassGenerated | 0x0,
|
|
||||||
MPSiteTypeGeneratedPhrase = 0xF | MPSiteTypeClassGenerated | 0x0,
|
|
||||||
|
|
||||||
MPSiteTypeStoredPersonal = 0x0 | MPSiteTypeClassStored | MPSiteFeatureExportContent,
|
|
||||||
MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate,
|
|
||||||
};
|
|
||||||
|
|
||||||
//// Type utilities.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The variant represented by the given name.
|
|
||||||
*/
|
|
||||||
const MPSiteVariant mpw_variantWithName(const char *variantName);
|
|
||||||
/**
|
|
||||||
* @return An internal string containing the scope identifier to apply when encoding for the given variant.
|
|
||||||
*/
|
|
||||||
const char *mpw_scopeForVariant(MPSiteVariant variant);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The type represented by the given name.
|
|
||||||
*/
|
|
||||||
const MPSiteType mpw_typeWithName(const char *typeName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return A newly allocated array of internal strings that express the templates to use for the given type.
|
|
||||||
* The amount of elements in the array is stored in count.
|
|
||||||
* If an unsupported type is given, count will be 0 and will return NULL.
|
|
||||||
* The array needs to be free'ed, the strings themselves must not be free'ed or modified.
|
|
||||||
*/
|
|
||||||
const char **mpw_templatesForType(MPSiteType type, size_t *count);
|
|
||||||
/**
|
|
||||||
* @return An internal string that contains the password encoding template of the given type
|
|
||||||
* for a seed that starts with the given byte.
|
|
||||||
*/
|
|
||||||
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return An internal string that contains all the characters that occur in the given character class.
|
|
||||||
*/
|
|
||||||
const char *mpw_charactersInClass(char characterClass);
|
|
||||||
/**
|
|
||||||
* @return A character from given character class that encodes the given byte.
|
|
||||||
*/
|
|
||||||
const char mpw_characterFromClass(char characterClass, uint8_t seedByte);
|
|
||||||
|
|
||||||
#endif // _MPW_TYPES_H
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-util.c
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#ifdef COLOR
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <curses.h>
|
|
||||||
#include <term.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <scrypt/sha256.h>
|
|
||||||
#include <scrypt/crypto_scrypt.h>
|
|
||||||
|
|
||||||
#include "mpw-util.h"
|
|
||||||
|
|
||||||
void mpw_push_buf(uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize) {
|
|
||||||
|
|
||||||
if (*bufferSize == (size_t)-1)
|
|
||||||
// The buffer was marked as broken, it is missing a previous push. Abort to avoid corrupt content.
|
|
||||||
return;
|
|
||||||
|
|
||||||
*bufferSize += pushSize;
|
|
||||||
uint8_t *resizedBuffer = realloc( *buffer, *bufferSize );
|
|
||||||
if (!resizedBuffer) {
|
|
||||||
// realloc failed, we can't push. Mark the buffer as broken.
|
|
||||||
mpw_free( *buffer, *bufferSize - pushSize );
|
|
||||||
*bufferSize = (size_t)-1;
|
|
||||||
*buffer = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
*buffer = resizedBuffer;
|
|
||||||
uint8_t *pushDst = *buffer + *bufferSize - pushSize;
|
|
||||||
memcpy( pushDst, pushBuffer, pushSize );
|
|
||||||
}
|
|
||||||
|
|
||||||
void mpw_push_string(uint8_t **buffer, size_t *const bufferSize, const char *pushString) {
|
|
||||||
|
|
||||||
mpw_push_buf( buffer, bufferSize, pushString, strlen( pushString ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void mpw_push_int(uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt) {
|
|
||||||
|
|
||||||
mpw_push_buf( buffer, bufferSize, &pushInt, sizeof( pushInt ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void mpw_free(const void *buffer, const size_t bufferSize) {
|
|
||||||
|
|
||||||
if (buffer) {
|
|
||||||
memset( (void *)buffer, 0, bufferSize );
|
|
||||||
free( (void *)buffer );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void mpw_free_string(const char *string) {
|
|
||||||
|
|
||||||
mpw_free( string, strlen( string ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t const *mpw_scrypt(const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize,
|
|
||||||
uint64_t N, uint32_t r, uint32_t p) {
|
|
||||||
|
|
||||||
if (!secret || !salt)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
uint8_t *key = malloc( keySize );
|
|
||||||
if (!key)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (crypto_scrypt( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) < 0) {
|
|
||||||
mpw_free( key, keySize );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t const *mpw_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize) {
|
|
||||||
|
|
||||||
uint8_t *const buffer = malloc( 32 );
|
|
||||||
if (!buffer)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
HMAC_SHA256_Buf( key, keySize, salt, saltSize, buffer );
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *mpw_id_buf(const void *buf, size_t length) {
|
|
||||||
|
|
||||||
uint8_t hash[32];
|
|
||||||
SHA256_Buf( buf, length, hash );
|
|
||||||
|
|
||||||
return mpw_hex( hash, 32 );
|
|
||||||
}
|
|
||||||
|
|
||||||
static char **mpw_hex_buf = NULL;
|
|
||||||
static unsigned int mpw_hex_buf_i = 0;
|
|
||||||
|
|
||||||
const char *mpw_hex(const void *buf, size_t length) {
|
|
||||||
|
|
||||||
// FIXME
|
|
||||||
if (!mpw_hex_buf) {
|
|
||||||
mpw_hex_buf = malloc( 10 * sizeof( char * ) );
|
|
||||||
for (uint8_t i = 0; i < 10; ++i)
|
|
||||||
mpw_hex_buf[i] = NULL;
|
|
||||||
}
|
|
||||||
mpw_hex_buf_i = (mpw_hex_buf_i + 1) % 10;
|
|
||||||
|
|
||||||
mpw_hex_buf[mpw_hex_buf_i] = realloc( mpw_hex_buf[mpw_hex_buf_i], length * 2 + 1 );
|
|
||||||
for (size_t kH = 0; kH < length; kH++)
|
|
||||||
sprintf( &(mpw_hex_buf[mpw_hex_buf_i][kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
|
|
||||||
|
|
||||||
return mpw_hex_buf[mpw_hex_buf_i];
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *mpw_hex_l(uint32_t number) {
|
|
||||||
|
|
||||||
return mpw_hex( &number, sizeof( number ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef COLOR
|
|
||||||
static int putvari;
|
|
||||||
static char *putvarc = NULL;
|
|
||||||
static int termsetup;
|
|
||||||
static int initputvar() {
|
|
||||||
if (!isatty(STDERR_FILENO))
|
|
||||||
return 0;
|
|
||||||
if (putvarc)
|
|
||||||
free(putvarc);
|
|
||||||
if (!termsetup) {
|
|
||||||
int status;
|
|
||||||
if (! (termsetup = (setupterm(NULL, STDERR_FILENO, &status) == OK && status == 1))) {
|
|
||||||
wrn( "Terminal doesn't support color (setupterm errno %d).\n", status );
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
putvarc=(char *)calloc(256, sizeof(char));
|
|
||||||
putvari=0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
static int putvar(int c) {
|
|
||||||
putvarc[putvari++]=c;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char *mpw_identicon(const char *fullName, const char *masterPassword) {
|
|
||||||
|
|
||||||
const char *leftArm[] = { "╔", "╚", "╰", "═" };
|
|
||||||
const char *rightArm[] = { "╗", "╝", "╯", "═" };
|
|
||||||
const char *body[] = { "█", "░", "▒", "▓", "☺", "☻" };
|
|
||||||
const char *accessory[] = {
|
|
||||||
"◈", "◎", "◐", "◑", "◒", "◓", "☀", "☁", "☂", "☃", "☄", "★", "☆", "☎", "☏", "⎈", "⌂", "☘", "☢", "☣",
|
|
||||||
"☕", "⌚", "⌛", "⏰", "⚡", "⛄", "⛅", "☔", "♔", "♕", "♖", "♗", "♘", "♙", "♚", "♛", "♜", "♝", "♞", "♟",
|
|
||||||
"♨", "♩", "♪", "♫", "⚐", "⚑", "⚔", "⚖", "⚙", "⚠", "⌘", "⏎", "✄", "✆", "✈", "✉", "✌"
|
|
||||||
};
|
|
||||||
|
|
||||||
uint8_t identiconSeed[32];
|
|
||||||
HMAC_SHA256_Buf( masterPassword, strlen( masterPassword ), fullName, strlen( fullName ), identiconSeed );
|
|
||||||
|
|
||||||
char *colorString, *resetString;
|
|
||||||
#ifdef COLOR
|
|
||||||
if (initputvar()) {
|
|
||||||
uint8_t colorIdentifier = (uint8_t)(identiconSeed[4] % 7 + 1);
|
|
||||||
tputs(tparm(tgetstr("AF", NULL), colorIdentifier), 1, putvar);
|
|
||||||
colorString = calloc(strlen(putvarc) + 1, sizeof(char));
|
|
||||||
strcpy(colorString, putvarc);
|
|
||||||
tputs(tgetstr("me", NULL), 1, putvar);
|
|
||||||
resetString = calloc(strlen(putvarc) + 1, sizeof(char));
|
|
||||||
strcpy(resetString, putvarc);
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
colorString = calloc( 1, sizeof( char ) );
|
|
||||||
resetString = calloc( 1, sizeof( char ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
char *identicon = (char *)calloc( 256, sizeof( char ) );
|
|
||||||
snprintf( identicon, 256, "%s%s%s%s%s%s",
|
|
||||||
colorString,
|
|
||||||
leftArm[identiconSeed[0] % (sizeof( leftArm ) / sizeof( leftArm[0] ))],
|
|
||||||
body[identiconSeed[1] % (sizeof( body ) / sizeof( body[0] ))],
|
|
||||||
rightArm[identiconSeed[2] % (sizeof( rightArm ) / sizeof( rightArm[0] ))],
|
|
||||||
accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( accessory[0] ))],
|
|
||||||
resetString );
|
|
||||||
|
|
||||||
free( colorString );
|
|
||||||
free( resetString );
|
|
||||||
return identicon;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the amount of bytes used by UTF-8 to encode a single character that starts with the given byte.
|
|
||||||
*/
|
|
||||||
static int mpw_utf8_sizeof(unsigned char utf8Byte) {
|
|
||||||
|
|
||||||
if (!utf8Byte)
|
|
||||||
return 0;
|
|
||||||
if ((utf8Byte & 0x80) == 0)
|
|
||||||
return 1;
|
|
||||||
if ((utf8Byte & 0xC0) != 0xC0)
|
|
||||||
return 0;
|
|
||||||
if ((utf8Byte & 0xE0) == 0xC0)
|
|
||||||
return 2;
|
|
||||||
if ((utf8Byte & 0xF0) == 0xE0)
|
|
||||||
return 3;
|
|
||||||
if ((utf8Byte & 0xF8) == 0xF0)
|
|
||||||
return 4;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t mpw_utf8_strlen(const char *utf8String) {
|
|
||||||
|
|
||||||
size_t charlen = 0;
|
|
||||||
char *remainingString = (char *)utf8String;
|
|
||||||
for (int charByteSize; (charByteSize = mpw_utf8_sizeof( (unsigned char)*remainingString )); remainingString += charByteSize)
|
|
||||||
++charlen;
|
|
||||||
|
|
||||||
return charlen;
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
//
|
|
||||||
// mpw-util.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 2014-12-20.
|
|
||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
//// Logging.
|
|
||||||
|
|
||||||
#ifndef trc
|
|
||||||
int mpw_verbosity;
|
|
||||||
#define trc_level 3
|
|
||||||
#define trc(...) \
|
|
||||||
if (mpw_verbosity >= 3) \
|
|
||||||
fprintf( stderr, __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
#ifndef dbg
|
|
||||||
#define dbg_level 2
|
|
||||||
#define dbg(...) \
|
|
||||||
if (mpw_verbosity >= 2) \
|
|
||||||
fprintf( stderr, __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
#ifndef inf
|
|
||||||
#define inf_level 1
|
|
||||||
#define inf(...) \
|
|
||||||
if (mpw_verbosity >= 1) \
|
|
||||||
fprintf( stderr, __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
#ifndef wrn
|
|
||||||
#define wrn_level 0
|
|
||||||
#define wrn(...) \
|
|
||||||
if (mpw_verbosity >= 0) \
|
|
||||||
fprintf( stderr, __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
#ifndef err
|
|
||||||
#define err_level -1
|
|
||||||
#define err(...) \
|
|
||||||
if (mpw_verbosity >= -1) \
|
|
||||||
fprintf( stderr, __VA_ARGS__ )
|
|
||||||
#endif
|
|
||||||
#ifndef ftl
|
|
||||||
#define ftl_level -2
|
|
||||||
#define ftl(...) \
|
|
||||||
do { \
|
|
||||||
if (mpw_verbosity >= -2) \
|
|
||||||
fprintf( stderr, __VA_ARGS__ ); \
|
|
||||||
exit( 2 ); \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//// Buffers and memory.
|
|
||||||
|
|
||||||
#define mpw_alloc_array(_count, _type, ...) ({ \
|
|
||||||
_type stackElements[] = { __VA_ARGS__ }; \
|
|
||||||
_count = sizeof( stackElements ) / sizeof( _type ); \
|
|
||||||
_type *allocElements = malloc( sizeof( stackElements ) ); \
|
|
||||||
memcpy( allocElements, stackElements, sizeof( stackElements ) ); \
|
|
||||||
allocElements; \
|
|
||||||
})
|
|
||||||
|
|
||||||
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
|
|
||||||
void mpw_push_buf(
|
|
||||||
uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize);
|
|
||||||
/** Push a string onto a buffer. reallocs the given buffer and appends the given string. */
|
|
||||||
void mpw_push_string(
|
|
||||||
uint8_t **buffer, size_t *const bufferSize, const char *pushString);
|
|
||||||
/** Push an integer onto a buffer. reallocs the given buffer and appends the given integer. */
|
|
||||||
void mpw_push_int(
|
|
||||||
uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt);
|
|
||||||
/** Free a buffer after zero'ing its contents. */
|
|
||||||
void mpw_free(
|
|
||||||
const void *buffer, const size_t bufferSize);
|
|
||||||
/** Free a string after zero'ing its contents. */
|
|
||||||
void mpw_free_string(
|
|
||||||
const char *string);
|
|
||||||
|
|
||||||
//// Cryptographic functions.
|
|
||||||
|
|
||||||
/** Perform a scrypt-based key derivation on the given key using the given salt and scrypt parameters.
|
|
||||||
* @return A new keySize-size allocated buffer. */
|
|
||||||
uint8_t const *mpw_scrypt(
|
|
||||||
const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize,
|
|
||||||
uint64_t N, uint32_t r, uint32_t p);
|
|
||||||
/** Calculate a SHA256-based HMAC by encrypting the given salt with the given key.
|
|
||||||
* @return A new 32-byte allocated buffer. */
|
|
||||||
uint8_t const *mpw_hmac_sha256(
|
|
||||||
const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize);
|
|
||||||
|
|
||||||
//// Visualizers.
|
|
||||||
|
|
||||||
/** Encode a buffer as a string of hexadecimal characters.
|
|
||||||
* @return A C-string in a reused buffer, do not free or store it. */
|
|
||||||
const char *mpw_hex(const void *buf, size_t length);
|
|
||||||
const char *mpw_hex_l(uint32_t number);
|
|
||||||
/** Encode a fingerprint for a buffer.
|
|
||||||
* @return A C-string in a reused buffer, do not free or store it. */
|
|
||||||
const char *mpw_id_buf(const void *buf, size_t length);
|
|
||||||
/** Encode a visual fingerprint for a user.
|
|
||||||
* @return A newly allocated string. */
|
|
||||||
const char *mpw_identicon(const char *fullName, const char *masterPassword);
|
|
||||||
|
|
||||||
//// String utilities.
|
|
||||||
|
|
||||||
/** @return The amount of display characters in the given UTF-8 string. */
|
|
||||||
const size_t mpw_utf8_strlen(const char *utf8String);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../Java/masterpassword-tests/src/main/resources/mpw_tests.xml
|
|
||||||
8
MasterPassword/Java/.gitignore
vendored
8
MasterPassword/Java/.gitignore
vendored
@@ -1,8 +0,0 @@
|
|||||||
# Gradle
|
|
||||||
build
|
|
||||||
.gradle
|
|
||||||
local.properties
|
|
||||||
|
|
||||||
# Maven
|
|
||||||
target
|
|
||||||
dependency-reduced-pom.xml
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
allprojects {
|
|
||||||
//apply plugin: 'findbugs'
|
|
||||||
|
|
||||||
group = 'com.lyndir.masterpassword'
|
|
||||||
version = 'GIT-SNAPSHOT'
|
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
|
||||||
sourceCompatibility = "1.7"
|
|
||||||
targetCompatibility = "1.7"
|
|
||||||
}
|
|
||||||
tasks.withType(FindBugs) {
|
|
||||||
reports {
|
|
||||||
xml.enabled false
|
|
||||||
html.enabled true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildscript {
|
|
||||||
repositories {
|
|
||||||
jcenter()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath group: 'com.android.tools.build', name: 'gradle', version: '2.2.3'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
subprojects {
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
maven { url "http://maven.lyndir.com" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
org.gradle.jvmargs=-Xmx1536M
|
|
||||||
Binary file not shown.
@@ -1,12 +0,0 @@
|
|||||||
apply plugin: 'java'
|
|
||||||
|
|
||||||
description = 'Master Password Algorithm Implementation'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compile( 'com.lyndir.lhunath.opal:opal-system:1.6-p10' ) {
|
|
||||||
exclude( module: 'joda-time' )
|
|
||||||
}
|
|
||||||
compile 'com.lambdaworks:scrypt:1.4.0'
|
|
||||||
compile 'org.jetbrains:annotations:13.0'
|
|
||||||
compile 'com.google.code.findbugs:jsr305:3.0.1'
|
|
||||||
}
|
|
||||||
@@ -1,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,74 +0,0 @@
|
|||||||
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 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;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <i>07 04, 2012</i>
|
|
||||||
*
|
|
||||||
* @author lhunath
|
|
||||||
*/
|
|
||||||
public enum MPSiteFeature {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export the key-protected content data.
|
|
||||||
*/
|
|
||||||
ExportContent( 1 << 10 ),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Never export content.
|
|
||||||
*/
|
|
||||||
DevicePrivate( 1 << 11 );
|
|
||||||
|
|
||||||
MPSiteFeature(final int mask) {
|
|
||||||
this.mask = mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final int mask;
|
|
||||||
|
|
||||||
public int getMask() {
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <i>07 04, 2012</i>
|
|
||||||
*
|
|
||||||
* @author lhunath
|
|
||||||
*/
|
|
||||||
public enum MPSiteType {
|
|
||||||
|
|
||||||
GeneratedMaximum( "Max", "20 characters, contains symbols.", //
|
|
||||||
ImmutableList.of( "x", "max", "maximum" ), //
|
|
||||||
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
|
|
||||||
MPSiteTypeClass.Generated, 0x0 ),
|
|
||||||
|
|
||||||
GeneratedLong( "Long", "Copy-friendly, 14 characters, contains symbols.", //
|
|
||||||
ImmutableList.of( "l", "long" ), //
|
|
||||||
ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
|
|
||||||
new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
|
|
||||||
new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ),
|
|
||||||
new MPTemplate( "CvcvnoCvccCvcv" ), new MPTemplate( "CvcvCvccnoCvcv" ),
|
|
||||||
new MPTemplate( "CvcvCvccCvcvno" ), new MPTemplate( "CvcvnoCvcvCvcc" ),
|
|
||||||
new MPTemplate( "CvcvCvcvnoCvcc" ), new MPTemplate( "CvcvCvcvCvccno" ),
|
|
||||||
new MPTemplate( "CvccnoCvccCvcv" ), new MPTemplate( "CvccCvccnoCvcv" ),
|
|
||||||
new MPTemplate( "CvccCvccCvcvno" ), new MPTemplate( "CvcvnoCvccCvcc" ),
|
|
||||||
new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ),
|
|
||||||
new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ),
|
|
||||||
new MPTemplate( "CvccCvcvCvccno" ) ), //
|
|
||||||
MPSiteTypeClass.Generated, 0x1 ),
|
|
||||||
|
|
||||||
GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", //
|
|
||||||
ImmutableList.of( "m", "med", "medium" ), //
|
|
||||||
ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), //
|
|
||||||
MPSiteTypeClass.Generated, 0x2 ),
|
|
||||||
|
|
||||||
GeneratedBasic( "Basic", "8 characters, no symbols.", //
|
|
||||||
ImmutableList.of( "b", "basic" ), //
|
|
||||||
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" ), //
|
|
||||||
ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
|
|
||||||
MPSiteTypeClass.Generated, 0x4 ),
|
|
||||||
|
|
||||||
GeneratedPIN( "PIN", "4 numbers.", //
|
|
||||||
ImmutableList.of( "i", "pin" ), //
|
|
||||||
ImmutableList.of( new MPTemplate( "nnnn" ) ), //
|
|
||||||
MPSiteTypeClass.Generated, 0x5 ),
|
|
||||||
|
|
||||||
GeneratedName( "Name", "9 letter name.", //
|
|
||||||
ImmutableList.of( "n", "name" ), //
|
|
||||||
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
|
|
||||||
MPSiteTypeClass.Generated, 0xE ),
|
|
||||||
|
|
||||||
GeneratedPhrase( "Phrase", "20 character sentence.", //
|
|
||||||
ImmutableList.of( "p", "phrase" ), //
|
|
||||||
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ),
|
|
||||||
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
|
|
||||||
MPSiteTypeClass.Generated, 0xF ),
|
|
||||||
|
|
||||||
StoredPersonal( "Personal", "AES-encrypted, exportable.", //
|
|
||||||
ImmutableList.of( "personal" ), //
|
|
||||||
ImmutableList.<MPTemplate>of(), //
|
|
||||||
MPSiteTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ),
|
|
||||||
|
|
||||||
StoredDevicePrivate( "Device", "AES-encrypted, not exported.", //
|
|
||||||
ImmutableList.of( "device" ), //
|
|
||||||
ImmutableList.<MPTemplate>of(), //
|
|
||||||
MPSiteTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate );
|
|
||||||
|
|
||||||
static final Logger logger = Logger.get( MPSiteType.class );
|
|
||||||
|
|
||||||
private final String 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 (MPSiteFeature typeFeature : typeFeatures)
|
|
||||||
mask |= typeFeature.getMask();
|
|
||||||
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param option The option to select a type with. It is matched case insensitively.
|
|
||||||
*
|
|
||||||
* @return The type registered for the given option.
|
|
||||||
*/
|
|
||||||
public static MPSiteType forOption(final String option) {
|
|
||||||
|
|
||||||
for (final MPSiteType type : values())
|
|
||||||
if (type.getOptions().contains( option.toLowerCase() ))
|
|
||||||
return type;
|
|
||||||
|
|
||||||
throw logger.bug( "No type for option: %s", option );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param name The name fromInt the type to look up. It is matched case insensitively.
|
|
||||||
*
|
|
||||||
* @return The type registered with the given name.
|
|
||||||
*/
|
|
||||||
@Contract("!null -> !null, null -> null")
|
|
||||||
public static MPSiteType forName(@Nullable final String name) {
|
|
||||||
|
|
||||||
if (name == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
for (final MPSiteType type : values())
|
|
||||||
if (type.name().equalsIgnoreCase( name ))
|
|
||||||
return type;
|
|
||||||
|
|
||||||
throw logger.bug( "No type for name: %s", name );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param typeClass The class for which we look up types.
|
|
||||||
*
|
|
||||||
* @return All types that support the given class.
|
|
||||||
*/
|
|
||||||
public static ImmutableList<MPSiteType> forClass(final MPSiteTypeClass typeClass) {
|
|
||||||
|
|
||||||
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
|
|
||||||
for (final MPSiteType type : values())
|
|
||||||
if (type.getTypeClass() == typeClass)
|
|
||||||
types.add( type );
|
|
||||||
|
|
||||||
return types.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param type The type for which we look up types.
|
|
||||||
*
|
|
||||||
* @return The type registered with the given type.
|
|
||||||
*/
|
|
||||||
public static MPSiteType forType(final int type) {
|
|
||||||
|
|
||||||
for (MPSiteType siteType : values())
|
|
||||||
if (siteType.getType() == type)
|
|
||||||
return siteType;
|
|
||||||
|
|
||||||
throw logger.bug( "No type: %s", type );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mask The mask for which we look up types.
|
|
||||||
*
|
|
||||||
* @return All types that support the given mask.
|
|
||||||
*/
|
|
||||||
public static ImmutableList<MPSiteType> forMask(final int mask) {
|
|
||||||
|
|
||||||
int typeMask = mask & ~0xF;
|
|
||||||
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
|
|
||||||
for (MPSiteType siteType : values())
|
|
||||||
if (((siteType.getType() & ~0xF) & typeMask) != 0)
|
|
||||||
types.add( siteType );
|
|
||||||
|
|
||||||
return types.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MPTemplate getTemplateAtRollingIndex(final int templateIndex) {
|
|
||||||
return templates.get( templateIndex % templates.size() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <i>07 04, 2012</i>
|
|
||||||
*
|
|
||||||
* @author lhunath
|
|
||||||
*/
|
|
||||||
public enum MPSiteTypeClass {
|
|
||||||
Generated( 1 << 4 ),
|
|
||||||
Stored( 1 << 5 );
|
|
||||||
|
|
||||||
private final int mask;
|
|
||||||
|
|
||||||
MPSiteTypeClass(final int mask) {
|
|
||||||
this.mask = mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMask() {
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
|
||||||
import java.util.List;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 14-12-02
|
|
||||||
*/
|
|
||||||
public enum MPSiteVariant {
|
|
||||||
Password( "The password to log in with.", "Doesn't currently use a context.", //
|
|
||||||
ImmutableList.of( "p", "password" ), "com.lyndir.masterpassword" ),
|
|
||||||
Login( "The username to log in as.", "Doesn't currently use a context.", //
|
|
||||||
ImmutableList.of( "l", "login" ), "com.lyndir.masterpassword.login" ),
|
|
||||||
Answer( "The answer to a security question.", "Empty for a universal site answer or\nthe most significant word(s) of the question.", //
|
|
||||||
ImmutableList.of( "a", "answer" ), "com.lyndir.masterpassword.answer" );
|
|
||||||
|
|
||||||
static final Logger logger = Logger.get( MPSiteType.class );
|
|
||||||
|
|
||||||
private final String description;
|
|
||||||
private final String contextDescription;
|
|
||||||
private final List<String> options;
|
|
||||||
private final String scope;
|
|
||||||
|
|
||||||
MPSiteVariant(final String description, final String contextDescription, final List<String> options, final String scope) {
|
|
||||||
this.contextDescription = contextDescription;
|
|
||||||
|
|
||||||
this.options = options;
|
|
||||||
this.description = description;
|
|
||||||
this.scope = scope;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContextDescription() {
|
|
||||||
return contextDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getOptions() {
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getScope() {
|
|
||||||
return scope;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param option The option to select a variant with. It is matched case insensitively.
|
|
||||||
*
|
|
||||||
* @return The variant registered for the given option.
|
|
||||||
*/
|
|
||||||
public static MPSiteVariant forOption(final String option) {
|
|
||||||
|
|
||||||
for (final MPSiteVariant variant : values())
|
|
||||||
if (variant.getOptions().contains( option.toLowerCase() ))
|
|
||||||
return variant;
|
|
||||||
|
|
||||||
throw logger.bug( "No variant for option: %s", option );
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param name The name fromInt the variant to look up. It is matched case insensitively.
|
|
||||||
*
|
|
||||||
* @return The variant registered with the given name.
|
|
||||||
*/
|
|
||||||
@Contract("!null -> !null, null -> null")
|
|
||||||
public static MPSiteVariant forName(@Nullable final String name) {
|
|
||||||
|
|
||||||
if (name == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
for (final MPSiteVariant type : values())
|
|
||||||
if (type.name().equalsIgnoreCase( name ))
|
|
||||||
return type;
|
|
||||||
|
|
||||||
throw logger.bug( "No variant for name: %s", name );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.lyndir.lhunath.opal.system.util.MetaObject;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <i>07 04, 2012</i>
|
|
||||||
*
|
|
||||||
* @author lhunath
|
|
||||||
*/
|
|
||||||
public class MPTemplate extends MetaObject {
|
|
||||||
|
|
||||||
private final String templateString;
|
|
||||||
private final List<MPTemplateCharacterClass> template;
|
|
||||||
|
|
||||||
MPTemplate(final String templateString) {
|
|
||||||
|
|
||||||
ImmutableList.Builder<MPTemplateCharacterClass> builder = ImmutableList.builder();
|
|
||||||
for (int i = 0; i < templateString.length(); ++i)
|
|
||||||
builder.add( MPTemplateCharacterClass.forIdentifier( templateString.charAt( i ) ) );
|
|
||||||
|
|
||||||
this.templateString = templateString;
|
|
||||||
template = builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTemplateString() {
|
|
||||||
return templateString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MPTemplateCharacterClass getCharacterClassAtIndex(final int index) {
|
|
||||||
|
|
||||||
return template.get( index );
|
|
||||||
}
|
|
||||||
|
|
||||||
public int length() {
|
|
||||||
|
|
||||||
return template.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return strf( "{MPTemplate: %s}", templateString );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
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;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-08-30
|
|
||||||
*/
|
|
||||||
public abstract class MasterKey {
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
|
||||||
private static final Logger logger = Logger.get( MasterKey.class );
|
|
||||||
private static boolean allowNativeByDefault = true;
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private final String fullName;
|
|
||||||
private boolean allowNative = allowNativeByDefault;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private byte[] masterKey;
|
|
||||||
|
|
||||||
public static MasterKey create(final String fullName, final char[] masterPassword) {
|
|
||||||
|
|
||||||
return create( Version.CURRENT, fullName, masterPassword );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public static MasterKey create(Version version, final String fullName, final char[] masterPassword) {
|
|
||||||
|
|
||||||
switch (version) {
|
|
||||||
case V0:
|
|
||||||
return new MasterKeyV0( fullName ).revalidate( masterPassword );
|
|
||||||
case V1:
|
|
||||||
return new MasterKeyV1( fullName ).revalidate( masterPassword );
|
|
||||||
case V2:
|
|
||||||
return new MasterKeyV2( fullName ).revalidate( masterPassword );
|
|
||||||
case V3:
|
|
||||||
return new MasterKeyV3( fullName ).revalidate( masterPassword );
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new UnsupportedOperationException( "Unsupported version: " + version );
|
|
||||||
}
|
|
||||||
|
|
||||||
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(@NotNull final String fullName) {
|
|
||||||
|
|
||||||
this.fullName = fullName;
|
|
||||||
logger.trc( "fullName: %s", fullName );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
protected abstract byte[] deriveKey(final char[] masterPassword);
|
|
||||||
|
|
||||||
public abstract Version getAlgorithmVersion();
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getFullName() {
|
|
||||||
|
|
||||||
return fullName;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 final String siteName, final MPSiteType siteType, @Nonnull final UnsignedInteger siteCounter,
|
|
||||||
final MPSiteVariant siteVariant, @Nullable final String siteContext);
|
|
||||||
|
|
||||||
public boolean isValid() {
|
|
||||||
return masterKey != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidate() {
|
|
||||||
|
|
||||||
if (masterKey != null) {
|
|
||||||
Arrays.fill( masterKey, (byte) 0 );
|
|
||||||
masterKey = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MasterKey revalidate(final char[] masterPassword) {
|
|
||||||
invalidate();
|
|
||||||
|
|
||||||
logger.trc( "masterPassword: %s", new String( masterPassword ) );
|
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
masterKey = deriveKey( masterPassword );
|
|
||||||
|
|
||||||
if (masterKey == null)
|
|
||||||
logger.dbg( "masterKey calculation failed after %.2fs.", (System.currentTimeMillis() - start) / 1000D );
|
|
||||||
else
|
|
||||||
logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
|
|
||||||
(System.currentTimeMillis() - start) / 1000D );
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract byte[] bytesForInt(final int number);
|
|
||||||
|
|
||||||
protected abstract byte[] bytesForInt(@Nonnull final UnsignedInteger number);
|
|
||||||
|
|
||||||
protected abstract byte[] idForBytes(final byte[] bytes);
|
|
||||||
|
|
||||||
public enum Version {
|
|
||||||
/**
|
|
||||||
* bugs:
|
|
||||||
* - does math with chars whose signedness was platform-dependent.
|
|
||||||
* - miscounted the byte-length fromInt multi-byte site names.
|
|
||||||
* - miscounted the byte-length fromInt multi-byte full names.
|
|
||||||
*/
|
|
||||||
V0,
|
|
||||||
/**
|
|
||||||
* bugs:
|
|
||||||
* - miscounted the byte-length fromInt multi-byte site names.
|
|
||||||
* - miscounted the byte-length fromInt multi-byte full names.
|
|
||||||
*/
|
|
||||||
V1,
|
|
||||||
/**
|
|
||||||
* bugs:
|
|
||||||
* - miscounted the byte-length fromInt multi-byte full names.
|
|
||||||
*/
|
|
||||||
V2,
|
|
||||||
/**
|
|
||||||
* bugs:
|
|
||||||
* - no known issues.
|
|
||||||
*/
|
|
||||||
V3;
|
|
||||||
|
|
||||||
public static final Version CURRENT = V3;
|
|
||||||
|
|
||||||
public static Version fromInt(final int algorithmVersion) {
|
|
||||||
|
|
||||||
return values()[algorithmVersion];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int toInt() {
|
|
||||||
|
|
||||||
return ordinal();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toBundleVersion() {
|
|
||||||
switch (this) {
|
|
||||||
case V0:
|
|
||||||
return "1.0";
|
|
||||||
case V1:
|
|
||||||
return "2.0";
|
|
||||||
case V2:
|
|
||||||
return "2.1";
|
|
||||||
case V3:
|
|
||||||
return "2.2";
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new UnsupportedOperationException( "Unsupported version: " + this );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (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() / (300 * 1000)) * 300 );
|
|
||||||
|
|
||||||
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(), sitePasswordSeedBytes[i] > 0? (byte) 0x00: (byte) 0xFF );
|
|
||||||
buf.position( 2 );
|
|
||||||
buf.put( sitePasswordSeedBytes[i] ).rewind();
|
|
||||||
sitePasswordSeed[i] = buf.getInt() & 0xFFFF;
|
|
||||||
}
|
|
||||||
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
|
|
||||||
|
|
||||||
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
|
||||||
int templateIndex = sitePasswordSeed[0];
|
|
||||||
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
|
|
||||||
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
|
|
||||||
|
|
||||||
StringBuilder password = new StringBuilder( template.length() );
|
|
||||||
for (int i = 0; i < template.length(); ++i) {
|
|
||||||
int characterIndex = sitePasswordSeed[i + 1];
|
|
||||||
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
|
||||||
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
|
||||||
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
|
|
||||||
sitePasswordSeed[i + 1], passwordCharacter );
|
|
||||||
|
|
||||||
password.append( passwordCharacter );
|
|
||||||
}
|
|
||||||
|
|
||||||
return password.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected byte[] bytesForInt(final int 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,85 +0,0 @@
|
|||||||
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() / (300 * 1000)) * 300 );
|
|
||||||
|
|
||||||
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,84 +0,0 @@
|
|||||||
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() / (300 * 1000)) * 300 );
|
|
||||||
|
|
||||||
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,51 +0,0 @@
|
|||||||
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,9 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* @author lhunath, 15-02-04
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
@ParametersAreNonnullByDefault package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
||||||
@@ -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'
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
apply plugin: 'com.android.application'
|
|
||||||
|
|
||||||
android {
|
|
||||||
compileSdkVersion 25
|
|
||||||
buildToolsVersion "25.0.0"
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_1_7
|
|
||||||
targetCompatibility JavaVersion.VERSION_1_7
|
|
||||||
}
|
|
||||||
defaultConfig {
|
|
||||||
applicationId "com.lyndir.masterpassword"
|
|
||||||
minSdkVersion 19
|
|
||||||
targetSdkVersion 25
|
|
||||||
versionCode 20401
|
|
||||||
versionName "2.4.1"
|
|
||||||
}
|
|
||||||
// release with: STORE_PW=$(mpw masterpassword.keystore) KEY_PW=$(mpw masterpassword-android) gradle assembleRelease
|
|
||||||
signingConfigs {
|
|
||||||
release {
|
|
||||||
storeFile file('masterpassword.keystore')
|
|
||||||
storePassword System.getenv('STORE_PW')
|
|
||||||
|
|
||||||
keyAlias 'masterpassword-android'
|
|
||||||
keyPassword System.getenv('KEY_PW')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
signingConfig signingConfigs.release
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compile project( ':masterpassword-algorithm' )
|
|
||||||
compile project( ':masterpassword-tests' )
|
|
||||||
|
|
||||||
// Android dependencies
|
|
||||||
compile 'org.slf4j:slf4j-android:1.7.13-underscore'
|
|
||||||
compile 'com.jakewharton:butterknife:8.5.1'
|
|
||||||
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
|
|
||||||
compile files('libs/scrypt-1.4.0-native.jar')
|
|
||||||
}
|
|
||||||
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
/Users/lhunath/annex/secret/release-com.lyndir.masterpassword.jks
|
|
||||||
@@ -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>
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-08-25
|
|
||||||
*/
|
|
||||||
public class Res {
|
|
||||||
|
|
||||||
public static Typeface sourceCodePro_Black;
|
|
||||||
public static Typeface sourceCodePro_ExtraLight;
|
|
||||||
public static Typeface exo_Bold;
|
|
||||||
public static Typeface exo_ExtraBold;
|
|
||||||
public static Typeface exo_Regular;
|
|
||||||
public static Typeface exo_Thin;
|
|
||||||
|
|
||||||
private static boolean initialized;
|
|
||||||
|
|
||||||
public static void init(Resources resources) {
|
|
||||||
|
|
||||||
if (initialized)
|
|
||||||
return;
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
sourceCodePro_Black = Typeface.createFromAsset( resources.getAssets(), "SourceCodePro-Black.otf" );
|
|
||||||
sourceCodePro_ExtraLight = Typeface.createFromAsset( resources.getAssets(), "SourceCodePro-ExtraLight.otf" );
|
|
||||||
exo_Bold = Typeface.createFromAsset( resources.getAssets(), "Exo2.0-Bold.otf" );
|
|
||||||
exo_ExtraBold = Typeface.createFromAsset( resources.getAssets(), "Exo2.0-ExtraBold.otf" );
|
|
||||||
exo_Regular = Typeface.createFromAsset( resources.getAssets(), "Exo2.0-Regular.otf" );
|
|
||||||
exo_Thin = Typeface.createFromAsset( resources.getAssets(), "Exo2.0-Thin.otf" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
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(Context context) {
|
|
||||||
context.startActivity( new Intent( context, TestActivity.class ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate( savedInstanceState );
|
|
||||||
Res.init( getResources() );
|
|
||||||
|
|
||||||
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 (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(View v) {
|
|
||||||
if (action != null)
|
|
||||||
action.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setStatus(int statusId, int buttonId, @Nullable 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 );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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"
|
|
||||||
android:background="@drawable/background">
|
|
||||||
|
|
||||||
<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: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>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'java'
|
|
||||||
id 'application'
|
|
||||||
id 'com.github.johnrengelman.shadow' version '1.2.4'
|
|
||||||
}
|
|
||||||
|
|
||||||
description = 'Master Password CLI'
|
|
||||||
mainClassName = 'com.lyndir.masterpassword.CLI'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compile project(':masterpassword-algorithm')
|
|
||||||
|
|
||||||
compile group: 'ch.qos.logback', name: 'logback-classic', version:'1.1.2'
|
|
||||||
}
|
|
||||||
@@ -1,98 +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 CLI</name>
|
|
||||||
<description>A CLI interface to the Master Password algorithm</description>
|
|
||||||
|
|
||||||
<artifactId>masterpassword-cli</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<!-- BUILD CONFIGURATION -->
|
|
||||||
<build>
|
|
||||||
<resources>
|
|
||||||
<resource>
|
|
||||||
<directory>src/main/scripts</directory>
|
|
||||||
<filtering>true</filtering>
|
|
||||||
<targetPath>${project.build.directory}</targetPath>
|
|
||||||
</resource>
|
|
||||||
</resources>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-antrun-plugin</artifactId>
|
|
||||||
<version>1.7</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>prepare-package</id>
|
|
||||||
<phase>prepare-package</phase>
|
|
||||||
<configuration>
|
|
||||||
<target>
|
|
||||||
<chmod file="${project.build.directory}/install" perm="755" />
|
|
||||||
</target>
|
|
||||||
</configuration>
|
|
||||||
<goals>
|
|
||||||
<goal>run</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
|
||||||
<version>2.2</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>shade</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<transformers>
|
|
||||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
|
||||||
<mainClass>com.lyndir.masterpassword.CLI</mainClass>
|
|
||||||
</transformer>
|
|
||||||
</transformers>
|
|
||||||
<filters>
|
|
||||||
<filter>
|
|
||||||
<artifact>*:*</artifact>
|
|
||||||
<excludes>
|
|
||||||
<exclude>META-INF/*.SF</exclude>
|
|
||||||
<exclude>META-INF/*.DSA</exclude>
|
|
||||||
<exclude>META-INF/*.RSA</exclude>
|
|
||||||
</excludes>
|
|
||||||
</filter>
|
|
||||||
</filters>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<!-- DEPENDENCY MANAGEMENT -->
|
|
||||||
<dependencies>
|
|
||||||
|
|
||||||
<!-- PROJECT REFERENCES -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
|
||||||
<artifactId>masterpassword-algorithm</artifactId>
|
|
||||||
<version>GIT-SNAPSHOT</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>ch.qos.logback</groupId>
|
|
||||||
<artifactId>logback-classic</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2008, Maarten Billemont
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
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.Joiner;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.io.LineReader;
|
|
||||||
import com.google.common.primitives.UnsignedInteger;
|
|
||||||
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
|
|
||||||
import com.lyndir.lhunath.opal.system.util.StringUtils;
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p> <i>Jun 10, 2008</i> </p>
|
|
||||||
*
|
|
||||||
* @author mbillemo
|
|
||||||
*/
|
|
||||||
public class CLI {
|
|
||||||
|
|
||||||
public static void main(final String[] args)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
// Read information from the environment.
|
|
||||||
char[] masterPassword;
|
|
||||||
String siteName = null, context = null;
|
|
||||||
String userName = System.getenv( MPConstant.env_userName );
|
|
||||||
String siteTypeName = ifNotNullElse( System.getenv( MPConstant.env_siteType ), "" );
|
|
||||||
MPSiteType siteType = siteTypeName.isEmpty()? MPSiteType.GeneratedLong: MPSiteType.forOption( siteTypeName );
|
|
||||||
MPSiteVariant variant = MPSiteVariant.Password;
|
|
||||||
String siteCounterName = ifNotNullElse( System.getenv( MPConstant.env_siteCounter ), "" );
|
|
||||||
UnsignedInteger siteCounter = siteCounterName.isEmpty()? UnsignedInteger.valueOf( 1 ): UnsignedInteger.valueOf( siteCounterName );
|
|
||||||
|
|
||||||
// Parse information from option arguments.
|
|
||||||
boolean userNameArg = false, typeArg = false, counterArg = false, variantArg = false, contextArg = false;
|
|
||||||
for (final String arg : Arrays.asList( args ))
|
|
||||||
// Full Name
|
|
||||||
if ("-u".equals( arg ) || "--username".equals( arg ))
|
|
||||||
userNameArg = true;
|
|
||||||
else if (userNameArg) {
|
|
||||||
userName = arg;
|
|
||||||
userNameArg = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type
|
|
||||||
else if ("-t".equals( arg ) || "--type".equals( arg ))
|
|
||||||
typeArg = true;
|
|
||||||
else if (typeArg) {
|
|
||||||
siteType = MPSiteType.forOption( arg );
|
|
||||||
typeArg = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Counter
|
|
||||||
else if ("-c".equals( arg ) || "--counter".equals( arg ))
|
|
||||||
counterArg = true;
|
|
||||||
else if (counterArg) {
|
|
||||||
siteCounter = UnsignedInteger.valueOf( arg );
|
|
||||||
counterArg = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variant
|
|
||||||
else if ("-v".equals( arg ) || "--variant".equals( arg ))
|
|
||||||
variantArg = true;
|
|
||||||
else if (variantArg) {
|
|
||||||
variant = MPSiteVariant.forOption( arg );
|
|
||||||
variantArg = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Context
|
|
||||||
else if ("-C".equals( arg ) || "--context".equals( arg ))
|
|
||||||
contextArg = true;
|
|
||||||
else if (contextArg) {
|
|
||||||
context = arg;
|
|
||||||
contextArg = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Help
|
|
||||||
else if ("-h".equals( arg ) || "--help".equals( arg )) {
|
|
||||||
System.out.println();
|
|
||||||
System.out.format( "Usage: mpw [-u name] [-t type] [-c counter] site\n\n" );
|
|
||||||
System.out.format( " -u name Specify the full name of the user.\n" );
|
|
||||||
System.out.format( " Defaults to %s in env.\n\n", MPConstant.env_userName );
|
|
||||||
System.out.format( " -t type Specify the password's template.\n" );
|
|
||||||
System.out.format( " Defaults to %s in env or 'long' for password, 'name' for login.\n", MPConstant.env_siteType );
|
|
||||||
|
|
||||||
int optionsLength = 0;
|
|
||||||
Map<String, MPSiteType> typeMap = Maps.newLinkedHashMap();
|
|
||||||
for (MPSiteType elementType : MPSiteType.values()) {
|
|
||||||
String options = Joiner.on( ", " ).join( elementType.getOptions() );
|
|
||||||
typeMap.put( options, elementType );
|
|
||||||
optionsLength = Math.max( optionsLength, options.length() );
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, MPSiteType> entry : typeMap.entrySet()) {
|
|
||||||
String infoString = strf( " -v %" + optionsLength + "s | ", entry.getKey() );
|
|
||||||
String infoNewline = "\n" + StringUtils.repeat( " ", infoString.length() - 3 ) + " | ";
|
|
||||||
infoString += entry.getValue().getDescription().replaceAll( "\n", infoNewline );
|
|
||||||
System.out.println( infoString );
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
|
|
||||||
System.out.format( " -c counter The value of the counter.\n" );
|
|
||||||
System.out.format( " Defaults to %s in env or '1'.\n\n", MPConstant.env_siteCounter );
|
|
||||||
System.out.format( " -v variant The kind of content to generate.\n" );
|
|
||||||
System.out.format( " Defaults to 'password'.\n" );
|
|
||||||
|
|
||||||
optionsLength = 0;
|
|
||||||
Map<String, MPSiteVariant> variantMap = Maps.newLinkedHashMap();
|
|
||||||
for (MPSiteVariant elementVariant : MPSiteVariant.values()) {
|
|
||||||
String options = Joiner.on( ", " ).join( elementVariant.getOptions() );
|
|
||||||
variantMap.put( options, elementVariant );
|
|
||||||
optionsLength = Math.max( optionsLength, options.length() );
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, MPSiteVariant> entry : variantMap.entrySet()) {
|
|
||||||
String infoString = strf( " -v %" + optionsLength + "s | ", entry.getKey() );
|
|
||||||
String infoNewline = "\n" + StringUtils.repeat( " ", infoString.length() - 3 ) + " | ";
|
|
||||||
infoString += entry.getValue().getDescription().replaceAll( "\n", infoNewline );
|
|
||||||
System.out.println( infoString );
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
|
|
||||||
System.out.format( " -C context A variant-specific context.\n" );
|
|
||||||
System.out.format( " Defaults to empty.\n" );
|
|
||||||
for (Map.Entry<String, MPSiteVariant> entry : variantMap.entrySet()) {
|
|
||||||
String infoString = strf( " -v %" + optionsLength + "s | ", entry.getKey() );
|
|
||||||
String infoNewline = "\n" + StringUtils.repeat( " ", infoString.length() - 3 ) + " | ";
|
|
||||||
infoString += entry.getValue().getContextDescription().replaceAll( "\n", infoNewline );
|
|
||||||
System.out.println( infoString );
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
|
|
||||||
System.out.format( " ENVIRONMENT\n\n" );
|
|
||||||
System.out.format( " MP_USERNAME | The full name of the user.\n" );
|
|
||||||
System.out.format( " MP_SITETYPE | The default password template.\n" );
|
|
||||||
System.out.format( " MP_SITECOUNTER | The default counter value.\n\n" );
|
|
||||||
return;
|
|
||||||
} else
|
|
||||||
siteName = arg;
|
|
||||||
|
|
||||||
// Read missing information from the console.
|
|
||||||
Console console = System.console();
|
|
||||||
try (InputStreamReader inReader = new InputStreamReader( System.in )) {
|
|
||||||
LineReader lineReader = new LineReader( inReader );
|
|
||||||
|
|
||||||
if (siteName == null) {
|
|
||||||
System.err.format( "Site name: " );
|
|
||||||
siteName = lineReader.readLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userName == null) {
|
|
||||||
System.err.format( "User's name: " );
|
|
||||||
userName = lineReader.readLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (console != null)
|
|
||||||
masterPassword = console.readPassword( "%s's master password: ", userName );
|
|
||||||
|
|
||||||
else {
|
|
||||||
System.err.format( "%s's master password: ", userName );
|
|
||||||
masterPassword = lineReader.readLine().toCharArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode and write out the site password.
|
|
||||||
System.out.println( MasterKey.create( userName, masterPassword ).encode( siteName, siteType, siteCounter, variant, context ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Install the Master Password CLI tool.
|
|
||||||
set -e
|
|
||||||
cd "${BASH_SOURCE%/*}"
|
|
||||||
source bashlib
|
|
||||||
|
|
||||||
inf "This will install the mpw tool."
|
|
||||||
|
|
||||||
# Try to guess then ask for the bin dir to install to.
|
|
||||||
IFS=: read -a paths <<< "$PATH"
|
|
||||||
if inArray ~/bin "${paths[@]}"; then
|
|
||||||
bindir=~/bin
|
|
||||||
elif inArray ~/.bin "${paths[@]}"; then
|
|
||||||
bindir=~/.bin
|
|
||||||
elif inArray /usr/local/bin "${paths[@]}"; then
|
|
||||||
bindir=/usr/local/bin
|
|
||||||
else
|
|
||||||
bindir=~/bin
|
|
||||||
fi
|
|
||||||
bindir=$(ask -d "$bindir" "What bin directory should I install to?")
|
|
||||||
[[ -d "$bindir" ]] || mkdir "$bindir" || ftl 'Cannot create missing bin directory: %s' "$bindir" || exit
|
|
||||||
[[ -w "$bindir" ]] || ftl 'Cannot write to bin directory: %s' "$bindir" || exit
|
|
||||||
|
|
||||||
# Try to guess then ask for the share dir to install to.
|
|
||||||
sharedir=$(cd -P "$bindir/.."; [[ $bindir = */.bin ]] && printf '%s/.share' "$PWD" || printf '%s/share' "$PWD")
|
|
||||||
sharedir=$(ask -d "$sharedir" "What share directory should I install to?")
|
|
||||||
[[ -d "$sharedir" ]] || mkdir "$sharedir" || ftl 'Cannot create missing share directory: %s' "$sharedir" || exit
|
|
||||||
[[ -w "$sharedir" ]] || ftl 'Cannot write to share directory: %s' "$sharedir" || exit
|
|
||||||
|
|
||||||
# Install Master Password.
|
|
||||||
sharepath=$sharedir/masterpassword
|
|
||||||
mkdir -p "$sharepath"
|
|
||||||
cp -a "masterpassword-cli-"*".jar" bashlib mpw "$sharepath"
|
|
||||||
ex -c "%s~%SHAREPATH%~$sharepath~g|x" "$sharepath/mpw"
|
|
||||||
ln -sf "$sharepath/mpw" "$bindir/mpw"
|
|
||||||
chmod +x "$sharepath/mpw"
|
|
||||||
[[ ! -e "$bindir/bashlib" ]] && ln -s "$sharepath/bashlib" "$bindir/bashlib" ||:
|
|
||||||
|
|
||||||
# Convenience bash function.
|
|
||||||
inf "Installation successful!"
|
|
||||||
echo
|
|
||||||
inf "To improve usability, you can install an mpw function in your bash shell."
|
|
||||||
inf "This function adds the following features:"
|
|
||||||
inf " - Ask the Master Password once, remember in the shell."
|
|
||||||
inf " - Automatically put the password in the clipboard (some platforms)."
|
|
||||||
echo
|
|
||||||
inf "To do this you need the following function in ~/.bashrc:\n%s" "$(<mpw.bashrc)"
|
|
||||||
echo
|
|
||||||
inf "We can do this for you automatically now."
|
|
||||||
if ask -c Y!n "Append the mpw function to your .bashrc?"; then
|
|
||||||
cat mpw.bashrc >> ~/.bashrc
|
|
||||||
inf "Done! Don't forget to run '%s' to apply the changes!" "source ~/.bashrc"
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
inf "To begin using Master Password, type: mpw [site name]"
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Uncomment this to set your user name. Master Password will no longer ask you for a user name.
|
|
||||||
# export MP_USERNAME="Robert Lee Mitchell"
|
|
||||||
# Uncomment this to hardcode your master password. Make sure this file's permissions are tight. Master Password will not ask you for your master password anymore. This is probably not a good idea.
|
|
||||||
# export MP_PASSWORD="banana colored duckling"
|
|
||||||
|
|
||||||
cd "%SHAREPATH%"
|
|
||||||
exec java -jar masterpassword-cli-GIT-SNAPSHOT.jar "$@"
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
source bashlib
|
|
||||||
mpw() {
|
|
||||||
_nocopy() { echo >&2 "$(cat)"; }
|
|
||||||
_copy() { "$(type -P pbcopy || type -P xclip || echo _nocopy)"; }
|
|
||||||
|
|
||||||
# Empty the clipboard
|
|
||||||
:| _copy 2>/dev/null
|
|
||||||
|
|
||||||
# Ask for the user's name and password if not yet known.
|
|
||||||
MP_USERNAME=${MP_USERNAME:-$(ask -s 'Your Full Name:')}
|
|
||||||
MP_PASSWORD=${MP_PASSWORD:-$(ask -s 'Master Password:')}
|
|
||||||
|
|
||||||
# Start Master Password and copy the output.
|
|
||||||
printf %s "$(MP_USERNAME=$MP_USERNAME MP_PASSWORD=$MP_PASSWORD command mpw "$@")" | _copy
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'java'
|
|
||||||
id 'application'
|
|
||||||
id 'com.github.johnrengelman.shadow' version '1.2.4'
|
|
||||||
}
|
|
||||||
|
|
||||||
description = 'Master Password GUI'
|
|
||||||
mainClassName = 'com.lyndir.masterpassword.gui.GUI'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compile project(':masterpassword-model')
|
|
||||||
|
|
||||||
compile group: 'ch.qos.logback', name: 'logback-classic', version:'1.1.2'
|
|
||||||
compile group: 'com.yuvimasory', name: 'orange-extensions', version:'1.3.0'
|
|
||||||
}
|
|
||||||
@@ -1,158 +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 GUI</name>
|
|
||||||
<description>A GUI interface to the Master Password algorithm</description>
|
|
||||||
|
|
||||||
<artifactId>masterpassword-gui</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<!-- BUILD CONFIGURATION -->
|
|
||||||
<build>
|
|
||||||
<resources>
|
|
||||||
<resource>
|
|
||||||
<directory>src/main/resources</directory>
|
|
||||||
<filtering>false</filtering>
|
|
||||||
</resource>
|
|
||||||
</resources>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
|
||||||
<version>2.2</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>shade</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<transformers>
|
|
||||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
|
||||||
<mainClass>com.lyndir.masterpassword.gui.GUI</mainClass>
|
|
||||||
</transformer>
|
|
||||||
</transformers>
|
|
||||||
<filters>
|
|
||||||
<filter>
|
|
||||||
<artifact>*:*</artifact>
|
|
||||||
<excludes>
|
|
||||||
<exclude>META-INF/*.SF</exclude>
|
|
||||||
<exclude>META-INF/*.DSA</exclude>
|
|
||||||
<exclude>META-INF/*.RSA</exclude>
|
|
||||||
</excludes>
|
|
||||||
</filter>
|
|
||||||
</filters>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</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/*.jar</include>
|
|
||||||
</includes>
|
|
||||||
<keystore>release.jks</keystore>
|
|
||||||
<storepass>${env.PASSWORD}</storepass>
|
|
||||||
<keypass>${env.PASSWORD}</keypass>
|
|
||||||
<alias>masterpassword-desktop</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-model</artifactId>
|
|
||||||
<version>GIT-SNAPSHOT</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- EXTERNAL DEPENDENCIES -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>ch.qos.logback</groupId>
|
|
||||||
<artifactId>logback-classic</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- STUBS -->
|
|
||||||
<dependency>
|
|
||||||
<!-- http://ymasory.github.io/OrangeExtensions/ -->
|
|
||||||
<groupId>com.yuvimasory</groupId>
|
|
||||||
<artifactId>orange-extensions</artifactId>
|
|
||||||
<version>1.3.0</version>
|
|
||||||
<scope>provided</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
/Users/lhunath/SpiderOak Hive/secret/release-com.lyndir.masterpassword.jks
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.lyndir.masterpassword.MPIdenticon;
|
|
||||||
import com.lyndir.masterpassword.gui.util.Components;
|
|
||||||
import java.awt.*;
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.swing.*;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-06-11
|
|
||||||
*/
|
|
||||||
public abstract class AuthenticationPanel extends Components.GradientPanel {
|
|
||||||
|
|
||||||
protected final UnlockFrame unlockFrame;
|
|
||||||
protected final JLabel avatarLabel;
|
|
||||||
|
|
||||||
public AuthenticationPanel(final UnlockFrame unlockFrame) {
|
|
||||||
super( null, null );
|
|
||||||
this.unlockFrame = unlockFrame;
|
|
||||||
|
|
||||||
setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
|
|
||||||
|
|
||||||
// Avatar
|
|
||||||
add( Box.createVerticalGlue() );
|
|
||||||
add( avatarLabel = new JLabel( Res.avatar( 0 ) ) {
|
|
||||||
@Override
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( Integer.MAX_VALUE, Integer.MAX_VALUE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( Box.createVerticalGlue() );
|
|
||||||
|
|
||||||
avatarLabel.setToolTipText( "The avatar for your user. Click to change it." );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateUser(boolean repack) {
|
|
||||||
unlockFrame.updateUser( getSelectedUser() );
|
|
||||||
validate();
|
|
||||||
|
|
||||||
if (repack)
|
|
||||||
unlockFrame.repack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
protected abstract User getSelectedUser();
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Nonnull
|
|
||||||
public abstract char[] getMasterPassword();
|
|
||||||
|
|
||||||
public Component getFocusComponent() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterable<? extends JButton> getButtons() {
|
|
||||||
return ImmutableList.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void reset();
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
|
|
||||||
import com.lyndir.masterpassword.MPConstant;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-08-31
|
|
||||||
*/
|
|
||||||
public class Config {
|
|
||||||
|
|
||||||
private static final Config instance = new Config();
|
|
||||||
|
|
||||||
public static Config get() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean checkForUpdates() {
|
|
||||||
return ConversionUtils.toBoolean( System.getenv( MPConstant.env_checkUpdates ) ).or( true );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2008, Maarten Billemont
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
import com.google.common.io.*;
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
|
||||||
import com.lyndir.lhunath.opal.system.util.TypeUtils;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.jar.*;
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p> <i>Jun 10, 2008</i> </p>
|
|
||||||
*
|
|
||||||
* @author mbillemo
|
|
||||||
*/
|
|
||||||
public class GUI implements UnlockFrame.SignInCallback {
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
|
||||||
private static final Logger logger = Logger.get( GUI.class );
|
|
||||||
|
|
||||||
private final UnlockFrame unlockFrame = new UnlockFrame( this );
|
|
||||||
private PasswordFrame passwordFrame;
|
|
||||||
|
|
||||||
public static void main(final String[] args)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
if (Config.get().checkForUpdates())
|
|
||||||
checkUpdate();
|
|
||||||
|
|
||||||
try {
|
|
||||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
|
||||||
}
|
|
||||||
catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeUtils.<GUI>newInstance( "com.lyndir.masterpassword.gui.platform.mac.AppleGUI" ).or( new GUI() ).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkUpdate() {
|
|
||||||
try {
|
|
||||||
Enumeration<URL> manifestURLs = Thread.currentThread().getContextClassLoader().getResources( JarFile.MANIFEST_NAME );
|
|
||||||
while (manifestURLs.hasMoreElements()) {
|
|
||||||
InputStream manifestStream = manifestURLs.nextElement().openStream();
|
|
||||||
Attributes attributes = new Manifest( manifestStream ).getMainAttributes();
|
|
||||||
if (!GUI.class.getCanonicalName().equals( attributes.getValue( Attributes.Name.MAIN_CLASS ) ))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
String manifestRevision = attributes.getValue( Attributes.Name.IMPLEMENTATION_VERSION );
|
|
||||||
String upstreamRevisionURL = "http://masterpasswordapp.com/masterpassword-gui.jar.rev";
|
|
||||||
CharSource upstream = Resources.asCharSource( URI.create( upstreamRevisionURL ).toURL(), Charsets.UTF_8 );
|
|
||||||
String upstreamRevision = upstream.readFirstLine();
|
|
||||||
logger.inf( "Local Revision: <%s>", manifestRevision );
|
|
||||||
logger.inf( "Upstream Revision: <%s>", upstreamRevision );
|
|
||||||
if (manifestRevision != null && !manifestRevision.equalsIgnoreCase( upstreamRevision )) {
|
|
||||||
logger.wrn( "You are not running the current official version. Please update from:\n"
|
|
||||||
+ "http://masterpasswordapp.com/masterpassword-gui.jar" );
|
|
||||||
JOptionPane.showMessageDialog( null, "A new version of Master Password is available.\n"
|
|
||||||
+ "Please download the latest version from http://masterpasswordapp.com",
|
|
||||||
"Update Available", JOptionPane.WARNING_MESSAGE );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
logger.wrn( e, "Couldn't check for version update." );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void open() {
|
|
||||||
SwingUtilities.invokeLater( new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (passwordFrame == null)
|
|
||||||
unlockFrame.setVisible( true );
|
|
||||||
else
|
|
||||||
passwordFrame.setVisible( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void signedIn(final User user) {
|
|
||||||
passwordFrame = newPasswordFrame( user );
|
|
||||||
open();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected PasswordFrame newPasswordFrame(final User user) {
|
|
||||||
return new PasswordFrame( user );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import com.lyndir.masterpassword.gui.util.Components;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.DocumentEvent;
|
|
||||||
import javax.swing.event.DocumentListener;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-06-11
|
|
||||||
*/
|
|
||||||
public class IncognitoAuthenticationPanel extends AuthenticationPanel implements DocumentListener, ActionListener {
|
|
||||||
|
|
||||||
private final JTextField fullNameField;
|
|
||||||
private final JPasswordField masterPasswordField;
|
|
||||||
|
|
||||||
public IncognitoAuthenticationPanel(final UnlockFrame unlockFrame) {
|
|
||||||
|
|
||||||
// Full Name
|
|
||||||
super( unlockFrame );
|
|
||||||
add( Components.stud() );
|
|
||||||
|
|
||||||
JLabel fullNameLabel = Components.label( "Full Name:" );
|
|
||||||
add( fullNameLabel );
|
|
||||||
|
|
||||||
fullNameField = Components.textField();
|
|
||||||
fullNameField.setFont( Res.valueFont().deriveFont( 12f ) );
|
|
||||||
fullNameField.getDocument().addDocumentListener( this );
|
|
||||||
fullNameField.addActionListener( this );
|
|
||||||
add( fullNameField );
|
|
||||||
add( Components.stud() );
|
|
||||||
|
|
||||||
// Master Password
|
|
||||||
JLabel masterPasswordLabel = Components.label( "Master Password:" );
|
|
||||||
add( masterPasswordLabel );
|
|
||||||
|
|
||||||
masterPasswordField = Components.passwordField();
|
|
||||||
masterPasswordField.addActionListener( this );
|
|
||||||
masterPasswordField.getDocument().addDocumentListener( this );
|
|
||||||
add( masterPasswordField );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getFocusComponent() {
|
|
||||||
return fullNameField;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
masterPasswordField.setText( "" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected User getSelectedUser() {
|
|
||||||
return new IncognitoUser( fullNameField.getText() );
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public char[] getMasterPassword() {
|
|
||||||
return masterPasswordField.getPassword();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void insertUpdate(final DocumentEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeUpdate(final DocumentEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changedUpdate(final DocumentEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
unlockFrame.trySignIn( fullNameField, masterPasswordField );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import com.google.common.primitives.UnsignedInteger;
|
|
||||||
import com.lyndir.masterpassword.MPSiteType;
|
|
||||||
import com.lyndir.masterpassword.MasterKey;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 14-12-16
|
|
||||||
*/
|
|
||||||
public class IncognitoSite extends Site {
|
|
||||||
|
|
||||||
private String siteName;
|
|
||||||
private MPSiteType siteType;
|
|
||||||
private UnsignedInteger siteCounter;
|
|
||||||
private MasterKey.Version algorithmVersion;
|
|
||||||
|
|
||||||
public IncognitoSite(final String siteName, final MPSiteType siteType, final UnsignedInteger siteCounter,
|
|
||||||
final MasterKey.Version algorithmVersion) {
|
|
||||||
this.siteName = siteName;
|
|
||||||
this.siteType = siteType;
|
|
||||||
this.siteCounter = siteCounter;
|
|
||||||
this.algorithmVersion = algorithmVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MasterKey.Version getAlgorithmVersion() {
|
|
||||||
return algorithmVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) {
|
|
||||||
this.algorithmVersion = algorithmVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnsignedInteger getSiteCounter() {
|
|
||||||
return siteCounter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSiteCounter(final UnsignedInteger siteCounter) {
|
|
||||||
this.siteCounter = siteCounter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-06-08
|
|
||||||
*/
|
|
||||||
public class IncognitoUser extends User {
|
|
||||||
|
|
||||||
private final String fullName;
|
|
||||||
private char[] masterPassword;
|
|
||||||
|
|
||||||
public IncognitoUser(final String fullName) {
|
|
||||||
this.fullName = fullName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFullName() {
|
|
||||||
return fullName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected char[] getMasterPassword() {
|
|
||||||
return masterPassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void authenticate(final char[] masterPassword)
|
|
||||||
throws IncorrectMasterPasswordException {
|
|
||||||
this.masterPassword = masterPassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterable<Site> findSitesByName(final String siteName) {
|
|
||||||
return ImmutableList.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addSite(final Site site) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteSite(Site site) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import com.google.common.collect.*;
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
|
||||||
import com.lyndir.masterpassword.model.MPUser;
|
|
||||||
import com.lyndir.masterpassword.model.MPUserFileManager;
|
|
||||||
import com.lyndir.masterpassword.gui.util.Components;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.DocumentEvent;
|
|
||||||
import javax.swing.event.DocumentListener;
|
|
||||||
import javax.swing.plaf.metal.MetalComboBoxEditor;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-06-11
|
|
||||||
*/
|
|
||||||
public class ModelAuthenticationPanel extends AuthenticationPanel implements ItemListener, ActionListener, DocumentListener {
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
|
||||||
private static final Logger logger = Logger.get( ModelAuthenticationPanel.class );
|
|
||||||
|
|
||||||
private final JComboBox<ModelUser> userField;
|
|
||||||
private final JLabel masterPasswordLabel;
|
|
||||||
private final JPasswordField masterPasswordField;
|
|
||||||
|
|
||||||
public ModelAuthenticationPanel(final UnlockFrame unlockFrame) {
|
|
||||||
super( unlockFrame );
|
|
||||||
add( Components.stud() );
|
|
||||||
|
|
||||||
// Avatar
|
|
||||||
avatarLabel.addMouseListener( new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(final MouseEvent e) {
|
|
||||||
ModelUser selectedUser = getSelectedUser();
|
|
||||||
if (selectedUser != null) {
|
|
||||||
selectedUser.setAvatar( selectedUser.getAvatar() + 1 );
|
|
||||||
updateUser( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
// User
|
|
||||||
JLabel userLabel = Components.label( "User:" );
|
|
||||||
add( userLabel );
|
|
||||||
|
|
||||||
userField = Components.comboBox( readConfigUsers() );
|
|
||||||
userField.setFont( Res.valueFont().deriveFont( 12f ) );
|
|
||||||
userField.addItemListener( this );
|
|
||||||
userField.addActionListener( this );
|
|
||||||
userField.setEditor( new MetalComboBoxEditor() {
|
|
||||||
@Override
|
|
||||||
protected JTextField createEditorComponent() {
|
|
||||||
JTextField editorComponents = Components.textField();
|
|
||||||
editorComponents.setForeground( Color.red );
|
|
||||||
return editorComponents;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( userField );
|
|
||||||
add( Components.stud() );
|
|
||||||
|
|
||||||
// Master Password
|
|
||||||
masterPasswordLabel = Components.label( "Master Password:" );
|
|
||||||
add( masterPasswordLabel );
|
|
||||||
|
|
||||||
masterPasswordField = Components.passwordField();
|
|
||||||
masterPasswordField.addActionListener( this );
|
|
||||||
masterPasswordField.getDocument().addDocumentListener( this );
|
|
||||||
add( masterPasswordField );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getFocusComponent() {
|
|
||||||
return masterPasswordField.isVisible()? masterPasswordField: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void updateUser(boolean repack) {
|
|
||||||
ModelUser selectedUser = getSelectedUser();
|
|
||||||
if (selectedUser != null) {
|
|
||||||
avatarLabel.setIcon( Res.avatar( selectedUser.getAvatar() ) );
|
|
||||||
boolean showPasswordField = !selectedUser.keySaved();
|
|
||||||
if (masterPasswordField.isVisible() != showPasswordField) {
|
|
||||||
masterPasswordLabel.setVisible( showPasswordField );
|
|
||||||
masterPasswordField.setVisible( showPasswordField );
|
|
||||||
repack = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.updateUser( repack );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ModelUser getSelectedUser() {
|
|
||||||
int selectedIndex = userField.getSelectedIndex();
|
|
||||||
if (selectedIndex < 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return userField.getModel().getElementAt( selectedIndex );
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public char[] getMasterPassword() {
|
|
||||||
return masterPasswordField.getPassword();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterable<? extends JButton> getButtons() {
|
|
||||||
return ImmutableList.of( new JButton( Res.iconAdd() ) {
|
|
||||||
{
|
|
||||||
addActionListener( new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent e) {
|
|
||||||
String fullName = JOptionPane.showInputDialog( ModelAuthenticationPanel.this, //
|
|
||||||
"Enter your full name, ensuring it is correctly spelled and capitalized:",
|
|
||||||
"New User", JOptionPane.QUESTION_MESSAGE );
|
|
||||||
MPUserFileManager.get().addUser( new MPUser( fullName ) );
|
|
||||||
userField.setModel( new DefaultComboBoxModel<>( readConfigUsers() ) );
|
|
||||||
updateUser( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
setToolTipText( "Add a new user to the list." );
|
|
||||||
}
|
|
||||||
}, new JButton( Res.iconDelete() ) {
|
|
||||||
{
|
|
||||||
addActionListener( new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent e) {
|
|
||||||
ModelUser deleteUser = getSelectedUser();
|
|
||||||
if (deleteUser == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (JOptionPane.showConfirmDialog( ModelAuthenticationPanel.this, //
|
|
||||||
strf( "Are you sure you want to delete the user and sites remembered for:\n%s.",
|
|
||||||
deleteUser.getFullName() ), //
|
|
||||||
"Delete User", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE ) == JOptionPane.CANCEL_OPTION)
|
|
||||||
return;
|
|
||||||
|
|
||||||
MPUserFileManager.get().deleteUser( deleteUser.getModel() );
|
|
||||||
userField.setModel( new DefaultComboBoxModel<>( readConfigUsers() ) );
|
|
||||||
updateUser( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
setToolTipText( "Delete the selected user." );
|
|
||||||
}
|
|
||||||
}, new JButton( Res.iconQuestion() ) {
|
|
||||||
{
|
|
||||||
addActionListener( new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent e) {
|
|
||||||
JOptionPane.showMessageDialog( ModelAuthenticationPanel.this, //
|
|
||||||
strf( "Reads users and sites from the directory at:\n%s",
|
|
||||||
MPUserFileManager.get().getPath().getAbsolutePath() ), //
|
|
||||||
"Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
setToolTipText( "More information." );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
masterPasswordField.setText( "" );
|
|
||||||
}
|
|
||||||
|
|
||||||
private ModelUser[] readConfigUsers() {
|
|
||||||
return FluentIterable.from( MPUserFileManager.get().getUsers() ).transform( new Function<MPUser, ModelUser>() {
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public ModelUser apply(@Nullable final MPUser model) {
|
|
||||||
return new ModelUser( Preconditions.checkNotNull( model ) );
|
|
||||||
}
|
|
||||||
} ).toArray( ModelUser.class );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void itemStateChanged(final ItemEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
unlockFrame.trySignIn( userField );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void insertUpdate(final DocumentEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeUpdate(final DocumentEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changedUpdate(final DocumentEvent e) {
|
|
||||||
updateUser( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import com.google.common.primitives.UnsignedInteger;
|
|
||||||
import com.lyndir.masterpassword.MPSiteType;
|
|
||||||
import com.lyndir.masterpassword.MasterKey;
|
|
||||||
import com.lyndir.masterpassword.model.*;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 14-12-16
|
|
||||||
*/
|
|
||||||
public class ModelSite extends Site {
|
|
||||||
|
|
||||||
private final MPSite model;
|
|
||||||
|
|
||||||
public ModelSite(final MPSiteResult result) {
|
|
||||||
this.model = result.getSite();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MPSite getModel() {
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSiteName() {
|
|
||||||
return model.getSiteName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSiteName(final String siteName) {
|
|
||||||
model.setSiteName( siteName );
|
|
||||||
MPUserFileManager.get().save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MPSiteType getSiteType() {
|
|
||||||
return model.getSiteType();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSiteType(final MPSiteType siteType) {
|
|
||||||
if (siteType != getSiteType()) {
|
|
||||||
model.setSiteType( siteType );
|
|
||||||
MPUserFileManager.get().save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MasterKey.Version getAlgorithmVersion() {
|
|
||||||
return model.getAlgorithmVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAlgorithmVersion(final MasterKey.Version algorithmVersion) {
|
|
||||||
if (algorithmVersion != getAlgorithmVersion()) {
|
|
||||||
model.setAlgorithmVersion( algorithmVersion );
|
|
||||||
MPUserFileManager.get().save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnsignedInteger getSiteCounter() {
|
|
||||||
return model.getSiteCounter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSiteCounter(final UnsignedInteger siteCounter) {
|
|
||||||
if (siteCounter.equals( getSiteCounter() )) {
|
|
||||||
model.setSiteCounter( siteCounter );
|
|
||||||
MPUserFileManager.get().save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void use() {
|
|
||||||
model.updateLastUsed();
|
|
||||||
MPUserFileManager.get().save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import com.google.common.collect.FluentIterable;
|
|
||||||
import com.lyndir.masterpassword.model.*;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 14-12-08
|
|
||||||
*/
|
|
||||||
public class ModelUser extends User {
|
|
||||||
|
|
||||||
private final MPUser model;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private char[] masterPassword;
|
|
||||||
|
|
||||||
public ModelUser(MPUser model) {
|
|
||||||
this.model = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MPUser getModel() {
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getFullName() {
|
|
||||||
return model.getFullName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
protected char[] getMasterPassword() {
|
|
||||||
return masterPassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAvatar() {
|
|
||||||
return model.getAvatar();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAvatar(final int avatar) {
|
|
||||||
model.setAvatar(avatar % Res.avatars());
|
|
||||||
MPUserFileManager.get().save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void authenticate(final char[] masterPassword)
|
|
||||||
throws IncorrectMasterPasswordException {
|
|
||||||
putKey( model.authenticate( masterPassword ) );
|
|
||||||
this.masterPassword = masterPassword;
|
|
||||||
MPUserFileManager.get().save();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
super.reset();
|
|
||||||
|
|
||||||
if (masterPassword != null) {
|
|
||||||
Arrays.fill( masterPassword, (char) 0 );
|
|
||||||
masterPassword = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterable<Site> findSitesByName(final String query) {
|
|
||||||
return FluentIterable.from( model.findSitesByName( query ) ).transform( new Function<MPSiteResult, Site>() {
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Site apply(@Nullable final MPSiteResult site) {
|
|
||||||
return new ModelSite( Preconditions.checkNotNull( site ) );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addSite(final Site site) {
|
|
||||||
model.addSite( new MPSite( model, site.getSiteName(), site.getSiteType(), site.getSiteCounter() ) );
|
|
||||||
model.updateLastUsed();
|
|
||||||
MPUserFileManager.get().save();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteSite(Site site) {
|
|
||||||
if (site instanceof ModelSite) {
|
|
||||||
model.deleteSite(((ModelSite) site).getModel());
|
|
||||||
MPUserFileManager.get().save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean keySaved() {
|
|
||||||
// TODO
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,293 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
|
||||||
import com.google.common.collect.FluentIterable;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.common.primitives.UnsignedInteger;
|
|
||||||
import com.google.common.util.concurrent.*;
|
|
||||||
import com.lyndir.masterpassword.*;
|
|
||||||
import com.lyndir.masterpassword.gui.util.Components;
|
|
||||||
import com.lyndir.masterpassword.gui.util.UnsignedIntegerModel;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.datatransfer.StringSelection;
|
|
||||||
import java.awt.event.*;
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.*;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-06-08
|
|
||||||
*/
|
|
||||||
public class PasswordFrame extends JFrame implements DocumentListener {
|
|
||||||
|
|
||||||
private final User user;
|
|
||||||
private final Components.GradientPanel root;
|
|
||||||
private final JTextField siteNameField;
|
|
||||||
private final JButton siteActionButton;
|
|
||||||
private final JComboBox<MPSiteType> siteTypeField;
|
|
||||||
private final JComboBox<MasterKey.Version> siteVersionField;
|
|
||||||
private final JSpinner siteCounterField;
|
|
||||||
private final UnsignedIntegerModel siteCounterModel;
|
|
||||||
private final JPasswordField passwordField;
|
|
||||||
private final JLabel tipLabel;
|
|
||||||
private final JCheckBox maskPasswordField;
|
|
||||||
private final char passwordEchoChar;
|
|
||||||
private final Font passwordEchoFont;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Site currentSite;
|
|
||||||
private boolean updatingUI;
|
|
||||||
|
|
||||||
public PasswordFrame(User user)
|
|
||||||
throws HeadlessException {
|
|
||||||
super( "Master Password" );
|
|
||||||
this.user = user;
|
|
||||||
|
|
||||||
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
|
||||||
setContentPane( root = Components.gradientPanel( new FlowLayout(), Res.colors().frameBg() ) );
|
|
||||||
root.setLayout( new BoxLayout( root, BoxLayout.PAGE_AXIS ) );
|
|
||||||
root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
|
||||||
|
|
||||||
// Site
|
|
||||||
JPanel sitePanel = Components.boxLayout( BoxLayout.PAGE_AXIS );
|
|
||||||
sitePanel.setOpaque( true );
|
|
||||||
sitePanel.setBackground( Res.colors().controlBg() );
|
|
||||||
sitePanel.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
|
||||||
root.add( Components.borderPanel( sitePanel, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) );
|
|
||||||
|
|
||||||
// User
|
|
||||||
sitePanel.add( Components.label( strf( "Generating passwords for: %s", user.getFullName() ), SwingConstants.CENTER ) );
|
|
||||||
sitePanel.add( Components.stud() );
|
|
||||||
|
|
||||||
// Site Name
|
|
||||||
sitePanel.add( Components.label( "Site Name:" ) );
|
|
||||||
JComponent siteControls = Components.boxLayout( BoxLayout.LINE_AXIS, //
|
|
||||||
siteNameField = Components.textField(), Components.stud(),
|
|
||||||
siteActionButton = Components.button( "Add Site" ) );
|
|
||||||
siteNameField.getDocument().addDocumentListener( this );
|
|
||||||
siteNameField.addActionListener( new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent e) {
|
|
||||||
Futures.addCallback( updatePassword( true ), new FutureCallback<String>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(@Nullable final String sitePassword) {
|
|
||||||
StringSelection clipboardContents = new StringSelection( sitePassword );
|
|
||||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
|
|
||||||
|
|
||||||
SwingUtilities.invokeLater( new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
passwordField.setText( null );
|
|
||||||
siteNameField.setText( null );
|
|
||||||
|
|
||||||
dispatchEvent( new WindowEvent( PasswordFrame.this, WindowEvent.WINDOW_CLOSING ) );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
siteActionButton.addActionListener( new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent e) {
|
|
||||||
if (currentSite == null)
|
|
||||||
return;
|
|
||||||
else if (currentSite instanceof ModelSite)
|
|
||||||
PasswordFrame.this.user.deleteSite( currentSite );
|
|
||||||
else
|
|
||||||
PasswordFrame.this.user.addSite( currentSite );
|
|
||||||
siteNameField.requestFocus();
|
|
||||||
|
|
||||||
updatePassword( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
sitePanel.add( siteControls );
|
|
||||||
sitePanel.add( Components.stud() );
|
|
||||||
|
|
||||||
// Site Type & Counter
|
|
||||||
siteCounterModel = new UnsignedIntegerModel( UnsignedInteger.ONE, UnsignedInteger.ONE );
|
|
||||||
MPSiteType[] types = Iterables.toArray( MPSiteType.forClass( MPSiteTypeClass.Generated ), MPSiteType.class );
|
|
||||||
JComponent siteSettings = Components.boxLayout( BoxLayout.LINE_AXIS, //
|
|
||||||
siteTypeField = Components.comboBox( types ), //
|
|
||||||
Components.stud(), //
|
|
||||||
siteVersionField = Components.comboBox( MasterKey.Version.values() ), //
|
|
||||||
Components.stud(), //
|
|
||||||
siteCounterField = Components.spinner( siteCounterModel ) );
|
|
||||||
sitePanel.add( siteSettings );
|
|
||||||
siteTypeField.setFont( Res.valueFont().deriveFont( 12f ) );
|
|
||||||
siteTypeField.setSelectedItem( MPSiteType.GeneratedLong );
|
|
||||||
siteTypeField.addItemListener( new ItemListener() {
|
|
||||||
@Override
|
|
||||||
public void itemStateChanged(final ItemEvent e) {
|
|
||||||
updatePassword( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
siteVersionField.setFont( Res.valueFont().deriveFont( 12f ) );
|
|
||||||
siteVersionField.setAlignmentX( RIGHT_ALIGNMENT );
|
|
||||||
siteVersionField.setSelectedItem( MasterKey.Version.CURRENT );
|
|
||||||
siteVersionField.addItemListener( new ItemListener() {
|
|
||||||
@Override
|
|
||||||
public void itemStateChanged(final ItemEvent e) {
|
|
||||||
updatePassword( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
siteCounterField.setFont( Res.valueFont().deriveFont( 12f ) );
|
|
||||||
siteCounterField.setAlignmentX( RIGHT_ALIGNMENT );
|
|
||||||
siteCounterField.addChangeListener( new ChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void stateChanged(final ChangeEvent e) {
|
|
||||||
updatePassword( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Mask
|
|
||||||
maskPasswordField = Components.checkBox( "Hide Password" );
|
|
||||||
maskPasswordField.setAlignmentX( Component.CENTER_ALIGNMENT );
|
|
||||||
maskPasswordField.setSelected( true );
|
|
||||||
maskPasswordField.addItemListener( new ItemListener() {
|
|
||||||
@Override
|
|
||||||
public void itemStateChanged(ItemEvent e) {
|
|
||||||
updateMask();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Password
|
|
||||||
passwordField = Components.passwordField();
|
|
||||||
passwordField.setAlignmentX( Component.CENTER_ALIGNMENT );
|
|
||||||
passwordField.setHorizontalAlignment( JTextField.CENTER );
|
|
||||||
passwordField.putClientProperty( "JPasswordField.cutCopyAllowed", true );
|
|
||||||
passwordField.setEditable( false );
|
|
||||||
passwordField.setBackground( null );
|
|
||||||
passwordField.setBorder( null );
|
|
||||||
passwordEchoChar = passwordField.getEchoChar();
|
|
||||||
passwordEchoFont = passwordField.getFont().deriveFont( 40f );
|
|
||||||
updateMask();
|
|
||||||
|
|
||||||
// Tip
|
|
||||||
tipLabel = Components.label( " ", SwingConstants.CENTER );
|
|
||||||
tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
|
|
||||||
JPanel passwordContainer = Components.boxLayout( BoxLayout.PAGE_AXIS, maskPasswordField, Box.createGlue(), passwordField,
|
|
||||||
Box.createGlue(), tipLabel );
|
|
||||||
passwordContainer.setOpaque( true );
|
|
||||||
passwordContainer.setBackground( Color.white );
|
|
||||||
passwordContainer.setBorder( BorderFactory.createEmptyBorder( 8, 8, 8, 8 ) );
|
|
||||||
root.add( Box.createVerticalStrut( 8 ) );
|
|
||||||
root.add( Components.borderPanel( passwordContainer, BorderFactory.createLoweredSoftBevelBorder(), Res.colors().frameBg() ),
|
|
||||||
BorderLayout.SOUTH );
|
|
||||||
|
|
||||||
pack();
|
|
||||||
setMinimumSize( new Dimension( Math.max( 600, getPreferredSize().width ), Math.max( 300, getPreferredSize().height ) ) );
|
|
||||||
pack();
|
|
||||||
|
|
||||||
setLocationByPlatform( true );
|
|
||||||
setLocationRelativeTo( null );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateMask() {
|
|
||||||
passwordField.setEchoChar( maskPasswordField.isSelected()? passwordEchoChar: (char) 0 );
|
|
||||||
passwordField.setFont( maskPasswordField.isSelected()? passwordEchoFont: Res.bigValueFont().deriveFont( 40f ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private ListenableFuture<String> updatePassword(boolean allowNameCompletion) {
|
|
||||||
|
|
||||||
final String siteNameQuery = siteNameField.getText();
|
|
||||||
if (updatingUI)
|
|
||||||
return Futures.immediateCancelledFuture();
|
|
||||||
if (siteNameQuery == null || siteNameQuery.isEmpty() || !user.isKeyAvailable()) {
|
|
||||||
siteActionButton.setVisible( false );
|
|
||||||
tipLabel.setText( null );
|
|
||||||
passwordField.setText( null );
|
|
||||||
return Futures.immediateCancelledFuture();
|
|
||||||
}
|
|
||||||
|
|
||||||
final MPSiteType siteType = siteTypeField.getModel().getElementAt( siteTypeField.getSelectedIndex() );
|
|
||||||
final MasterKey.Version siteVersion = siteVersionField.getItemAt( siteVersionField.getSelectedIndex() );
|
|
||||||
final UnsignedInteger siteCounter = siteCounterModel.getNumber();
|
|
||||||
|
|
||||||
Iterable<Site> siteResults = user.findSitesByName( siteNameQuery );
|
|
||||||
if (!allowNameCompletion)
|
|
||||||
siteResults = FluentIterable.from( siteResults ).filter( new Predicate<Site>() {
|
|
||||||
@Override
|
|
||||||
public boolean apply(@Nullable Site siteResult) {
|
|
||||||
return siteResult != null && siteNameQuery.equals( siteResult.getSiteName() );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
final Site site = ifNotNullElse( Iterables.getFirst( siteResults, null ),
|
|
||||||
new IncognitoSite( siteNameQuery, siteType, siteCounter, siteVersion ) );
|
|
||||||
if (currentSite != null && currentSite.getSiteName().equals( site.getSiteName() )) {
|
|
||||||
site.setSiteType( siteType );
|
|
||||||
site.setAlgorithmVersion( siteVersion );
|
|
||||||
site.setSiteCounter( siteCounter );
|
|
||||||
}
|
|
||||||
|
|
||||||
ListenableFuture<String> passwordFuture = Res.execute( this, new Callable<String>() {
|
|
||||||
@Override
|
|
||||||
public String call()
|
|
||||||
throws Exception {
|
|
||||||
return user.getKey( site.getAlgorithmVersion() )
|
|
||||||
.encode( site.getSiteName(), site.getSiteType(), site.getSiteCounter(), MPSiteVariant.Password, null );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
Futures.addCallback( passwordFuture, new FutureCallback<String>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(@Nullable final String sitePassword) {
|
|
||||||
SwingUtilities.invokeLater( new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
updatingUI = true;
|
|
||||||
currentSite = site;
|
|
||||||
siteActionButton.setVisible( user instanceof ModelUser );
|
|
||||||
if (currentSite instanceof ModelSite)
|
|
||||||
siteActionButton.setText( "Delete Site" );
|
|
||||||
else
|
|
||||||
siteActionButton.setText( "Add Site" );
|
|
||||||
siteTypeField.setSelectedItem( currentSite.getSiteType() );
|
|
||||||
siteVersionField.setSelectedItem( currentSite.getAlgorithmVersion() );
|
|
||||||
siteCounterField.setValue( currentSite.getSiteCounter() );
|
|
||||||
siteNameField.setText( currentSite.getSiteName() );
|
|
||||||
if (siteNameField.getText().startsWith( siteNameQuery ))
|
|
||||||
siteNameField.select( siteNameQuery.length(), siteNameField.getText().length() );
|
|
||||||
|
|
||||||
passwordField.setText( sitePassword );
|
|
||||||
tipLabel.setText( "Press [Enter] to copy the password. Then paste it into the password field." );
|
|
||||||
updatingUI = false;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(final Throwable t) {
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
return passwordFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void insertUpdate(final DocumentEvent e) {
|
|
||||||
updatePassword( true );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeUpdate(final DocumentEvent e) {
|
|
||||||
updatePassword( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changedUpdate(final DocumentEvent e) {
|
|
||||||
updatePassword( true );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,338 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.io.Resources;
|
|
||||||
import com.google.common.util.concurrent.JdkFutureAdapters;
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
|
||||||
import com.lyndir.masterpassword.MPIdenticon;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.WindowAdapter;
|
|
||||||
import java.awt.event.WindowEvent;
|
|
||||||
import java.awt.image.ImageObserver;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.ref.SoftReference;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-06-11
|
|
||||||
*/
|
|
||||||
public abstract class Res {
|
|
||||||
|
|
||||||
private static final WeakHashMap<Window, ExecutorService> executorByWindow = new WeakHashMap<>();
|
|
||||||
private static final Logger logger = Logger.get( Res.class );
|
|
||||||
private static final Colors colors = new Colors();
|
|
||||||
|
|
||||||
public static Future<?> execute(final Window host, final Runnable job) {
|
|
||||||
return getExecutor( host ).submit( new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
job.run();
|
|
||||||
}
|
|
||||||
catch (Throwable t) {
|
|
||||||
logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <V> ListenableFuture<V> execute(final Window host, final Callable<V> job) {
|
|
||||||
ExecutorService executor = getExecutor( host );
|
|
||||||
return JdkFutureAdapters.listenInPoolThread( executor.submit( new Callable<V>() {
|
|
||||||
@Override
|
|
||||||
public V call()
|
|
||||||
throws Exception {
|
|
||||||
try {
|
|
||||||
return job.call();
|
|
||||||
}
|
|
||||||
catch (Throwable t) {
|
|
||||||
logger.err( t, "Unexpected: %s", t.getLocalizedMessage() );
|
|
||||||
throw t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} ), executor );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ExecutorService getExecutor(final Window host) {
|
|
||||||
ExecutorService executor = executorByWindow.get( host );
|
|
||||||
|
|
||||||
if (executor == null) {
|
|
||||||
executorByWindow.put( host, executor = Executors.newSingleThreadExecutor() );
|
|
||||||
|
|
||||||
host.addWindowListener( new WindowAdapter() {
|
|
||||||
@Override
|
|
||||||
public void windowClosed(final WindowEvent e) {
|
|
||||||
ExecutorService executor = executorByWindow.remove( host );
|
|
||||||
if (executor != null)
|
|
||||||
executor.shutdownNow();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Icon iconAdd() {
|
|
||||||
return new RetinaIcon( Resources.getResource( "media/icon_add@2x.png" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Icon iconDelete() {
|
|
||||||
return new RetinaIcon( Resources.getResource( "media/icon_delete@2x.png" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Icon iconQuestion() {
|
|
||||||
return new RetinaIcon( Resources.getResource( "media/icon_question@2x.png" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Icon avatar(final int index) {
|
|
||||||
return new RetinaIcon( Resources.getResource( strf( "media/avatar-%d@2x.png", index % avatars() ) ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int avatars() {
|
|
||||||
return 19;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font emoticonsFont() {
|
|
||||||
return emoticonsRegular();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font controlFont() {
|
|
||||||
return arimoRegular();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font valueFont() {
|
|
||||||
return sourceSansProRegular();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font bigValueFont() {
|
|
||||||
return sourceSansProBlack();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font emoticonsRegular() {
|
|
||||||
return font( "fonts/Emoticons-Regular.otf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font sourceCodeProRegular() {
|
|
||||||
return font( "fonts/SourceCodePro-Regular.otf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font sourceCodeProBlack() {
|
|
||||||
return font( "fonts/SourceCodePro-Bold.otf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font sourceSansProRegular() {
|
|
||||||
return font( "fonts/SourceSansPro-Regular.otf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font sourceSansProBlack() {
|
|
||||||
return font( "fonts/SourceSansPro-Bold.otf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font exoBold() {
|
|
||||||
return font( "fonts/Exo2.0-Bold.otf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font exoExtraBold() {
|
|
||||||
return font( "fonts/Exo2.0-ExtraBold.otf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font exoRegular() {
|
|
||||||
return font( "fonts/Exo2.0-Regular.otf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font exoThin() {
|
|
||||||
return font( "fonts/Exo2.0-Thin.otf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font arimoBold() {
|
|
||||||
return font( "fonts/Arimo-Bold.ttf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font arimoBoldItalic() {
|
|
||||||
return font( "fonts/Arimo-BoldItalic.ttf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font arimoItalic() {
|
|
||||||
return font( "fonts/Arimo-Italic.ttf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Font arimoRegular() {
|
|
||||||
return font( "fonts/Arimo-Regular.ttf" );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Font font(String fontResourceName) {
|
|
||||||
Map<String, SoftReference<Font>> fontsByResourceName = Maps.newHashMap();
|
|
||||||
SoftReference<Font> fontRef = fontsByResourceName.get( fontResourceName );
|
|
||||||
Font font = fontRef == null? null: fontRef.get();
|
|
||||||
if (font == null)
|
|
||||||
try {
|
|
||||||
fontsByResourceName.put( fontResourceName, new SoftReference<>(
|
|
||||||
font = Font.createFont( Font.TRUETYPE_FONT, Resources.getResource( fontResourceName ).openStream() ) ) );
|
|
||||||
}
|
|
||||||
catch (FontFormatException | IOException e) {
|
|
||||||
throw Throwables.propagate( e );
|
|
||||||
}
|
|
||||||
|
|
||||||
return font;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Colors colors() {
|
|
||||||
return colors;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class RetinaIcon extends ImageIcon {
|
|
||||||
|
|
||||||
private static final Pattern scalePattern = Pattern.compile( ".*@(\\d+)x.[^.]+$" );
|
|
||||||
|
|
||||||
private final float scale;
|
|
||||||
|
|
||||||
public RetinaIcon(final URL url) {
|
|
||||||
super( url );
|
|
||||||
|
|
||||||
Matcher scaleMatcher = scalePattern.matcher( url.getPath() );
|
|
||||||
if (scaleMatcher.matches())
|
|
||||||
scale = Float.parseFloat( scaleMatcher.group( 1 ) );
|
|
||||||
else
|
|
||||||
scale = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//private static URL retinaURL(final URL url) {
|
|
||||||
// try {
|
|
||||||
// final boolean[] isRetina = new boolean[1];
|
|
||||||
// new apple.awt.CImage.HiDPIScaledImage(1,1, BufferedImage.TYPE_INT_ARGB) {
|
|
||||||
// @Override
|
|
||||||
// public void drawIntoImage(BufferedImage image, float v) {
|
|
||||||
// isRetina[0] = v > 1;
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// return isRetina[0];
|
|
||||||
// } catch (Throwable e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// return url;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getIconWidth() {
|
|
||||||
return (int) (super.getIconWidth() / scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getIconHeight() {
|
|
||||||
return (int) (super.getIconHeight() / scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
|
|
||||||
ImageObserver observer = ifNotNullElse( getImageObserver(), c );
|
|
||||||
|
|
||||||
Image image = getImage();
|
|
||||||
int width = image.getWidth( observer );
|
|
||||||
int height = image.getHeight( observer );
|
|
||||||
final Graphics2D g2d = (Graphics2D) g.create( x, y, width, height );
|
|
||||||
|
|
||||||
g2d.scale( 1 / scale, 1 / scale );
|
|
||||||
g2d.drawImage( image, 0, 0, observer );
|
|
||||||
g2d.scale( 1, 1 );
|
|
||||||
g2d.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static class Colors {
|
|
||||||
|
|
||||||
private final Color frameBg = Color.decode( "#5A5D6B" );
|
|
||||||
private final Color controlBg = Color.decode( "#ECECEC" );
|
|
||||||
private final Color controlBorder = Color.decode( "#BFBFBF" );
|
|
||||||
|
|
||||||
public Color frameBg() {
|
|
||||||
return frameBg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color controlBg() {
|
|
||||||
return controlBg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color controlBorder() {
|
|
||||||
return controlBorder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color fromIdenticonColor(MPIdenticon.Color identiconColor, BackgroundMode backgroundMode) {
|
|
||||||
switch (identiconColor) {
|
|
||||||
case RED:
|
|
||||||
switch (backgroundMode) {
|
|
||||||
case DARK:
|
|
||||||
return Color.decode( "#dc322f" );
|
|
||||||
case LIGHT:
|
|
||||||
return Color.decode( "#dc322f" );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GREEN:
|
|
||||||
switch (backgroundMode) {
|
|
||||||
case DARK:
|
|
||||||
return Color.decode( "#859900" );
|
|
||||||
case LIGHT:
|
|
||||||
return Color.decode( "#859900" );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case YELLOW:
|
|
||||||
switch (backgroundMode) {
|
|
||||||
case DARK:
|
|
||||||
return Color.decode( "#b58900" );
|
|
||||||
case LIGHT:
|
|
||||||
return Color.decode( "#b58900" );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BLUE:
|
|
||||||
switch (backgroundMode) {
|
|
||||||
case DARK:
|
|
||||||
return Color.decode( "#268bd2" );
|
|
||||||
case LIGHT:
|
|
||||||
return Color.decode( "#268bd2" );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MAGENTA:
|
|
||||||
switch (backgroundMode) {
|
|
||||||
case DARK:
|
|
||||||
return Color.decode( "#d33682" );
|
|
||||||
case LIGHT:
|
|
||||||
return Color.decode( "#d33682" );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CYAN:
|
|
||||||
switch (backgroundMode) {
|
|
||||||
case DARK:
|
|
||||||
return Color.decode( "#2aa198" );
|
|
||||||
case LIGHT:
|
|
||||||
return Color.decode( "#2aa198" );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MONO:
|
|
||||||
switch (backgroundMode) {
|
|
||||||
case DARK:
|
|
||||||
return Color.decode( "#93a1a1" );
|
|
||||||
case LIGHT:
|
|
||||||
return Color.decode( "#586e75" );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalArgumentException( strf( "Color: %s or mode: %s not supported: ", identiconColor, backgroundMode ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum BackgroundMode {
|
|
||||||
DARK, LIGHT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
|
||||||
|
|
||||||
import com.google.common.primitives.UnsignedInteger;
|
|
||||||
import com.lyndir.masterpassword.MPSiteType;
|
|
||||||
import com.lyndir.masterpassword.MasterKey;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 14-12-16
|
|
||||||
*/
|
|
||||||
public abstract class Site {
|
|
||||||
|
|
||||||
public abstract String getSiteName();
|
|
||||||
|
|
||||||
public abstract void setSiteName(final String siteName);
|
|
||||||
|
|
||||||
public abstract MPSiteType getSiteType();
|
|
||||||
|
|
||||||
public abstract void setSiteType(final MPSiteType siteType);
|
|
||||||
|
|
||||||
public abstract MasterKey.Version getAlgorithmVersion();
|
|
||||||
|
|
||||||
public abstract void setAlgorithmVersion(final MasterKey.Version algorithmVersion);
|
|
||||||
|
|
||||||
public abstract UnsignedInteger getSiteCounter();
|
|
||||||
|
|
||||||
public abstract void setSiteCounter(final UnsignedInteger siteCounter);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return strf( "{%s: %s}", getClass().getSimpleName(), getSiteName() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
|
||||||
|
|
||||||
import com.lyndir.masterpassword.MPIdenticon;
|
|
||||||
import com.lyndir.masterpassword.gui.util.Components;
|
|
||||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.*;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-06-08
|
|
||||||
*/
|
|
||||||
public class UnlockFrame extends JFrame {
|
|
||||||
|
|
||||||
private final SignInCallback signInCallback;
|
|
||||||
private final Components.GradientPanel root;
|
|
||||||
private final JLabel identiconLabel;
|
|
||||||
private final JButton signInButton;
|
|
||||||
private final JPanel authenticationContainer;
|
|
||||||
private AuthenticationPanel authenticationPanel;
|
|
||||||
private boolean incognito;
|
|
||||||
public User user;
|
|
||||||
|
|
||||||
public UnlockFrame(final SignInCallback signInCallback)
|
|
||||||
throws HeadlessException {
|
|
||||||
super( "Unlock Master Password" );
|
|
||||||
this.signInCallback = signInCallback;
|
|
||||||
|
|
||||||
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
|
||||||
addWindowFocusListener( new WindowAdapter() {
|
|
||||||
@Override
|
|
||||||
public void windowGainedFocus(WindowEvent e) {
|
|
||||||
root.setGradientColor( Res.colors().frameBg() );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void windowLostFocus(WindowEvent e) {
|
|
||||||
root.setGradientColor( Color.RED );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Sign In
|
|
||||||
JPanel signInBox = Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = Components.button( "Sign In" ),
|
|
||||||
Box.createGlue() );
|
|
||||||
signInBox.setBackground( null );
|
|
||||||
|
|
||||||
setContentPane( root = Components.gradientPanel( new FlowLayout(), Res.colors().frameBg() ) );
|
|
||||||
root.setLayout( new BoxLayout( root, BoxLayout.PAGE_AXIS ) );
|
|
||||||
root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
|
||||||
root.add( Components.borderPanel( authenticationContainer = Components.boxLayout( BoxLayout.PAGE_AXIS ),
|
|
||||||
BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) );
|
|
||||||
root.add( Box.createVerticalStrut( 8 ) );
|
|
||||||
root.add( identiconLabel = Components.label( " ", SwingConstants.CENTER ) );
|
|
||||||
root.add( Box.createVerticalStrut( 8 ) );
|
|
||||||
root.add( signInBox );
|
|
||||||
|
|
||||||
authenticationContainer.setOpaque( true );
|
|
||||||
authenticationContainer.setBackground( Res.colors().controlBg() );
|
|
||||||
authenticationContainer.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
|
||||||
identiconLabel.setFont( Res.emoticonsFont().deriveFont( 14.f ) );
|
|
||||||
identiconLabel.setToolTipText(
|
|
||||||
"A representation of your identity across all Master Password apps.\nIt should always be the same." );
|
|
||||||
signInButton.addActionListener( new AbstractAction() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent e) {
|
|
||||||
trySignIn();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
createAuthenticationPanel();
|
|
||||||
|
|
||||||
setLocationByPlatform( true );
|
|
||||||
setLocationRelativeTo( null );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void repack() {
|
|
||||||
pack();
|
|
||||||
setMinimumSize( new Dimension( Math.max( 300, getPreferredSize().width ), Math.max( 300, getPreferredSize().height ) ) );
|
|
||||||
pack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createAuthenticationPanel() {
|
|
||||||
authenticationContainer.removeAll();
|
|
||||||
|
|
||||||
if (incognito) {
|
|
||||||
authenticationPanel = new IncognitoAuthenticationPanel( this );
|
|
||||||
} else {
|
|
||||||
authenticationPanel = new ModelAuthenticationPanel( this );
|
|
||||||
}
|
|
||||||
authenticationPanel.updateUser( false );
|
|
||||||
authenticationContainer.add( authenticationPanel );
|
|
||||||
authenticationContainer.add( Components.stud() );
|
|
||||||
|
|
||||||
final JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" );
|
|
||||||
incognitoCheckBox.setToolTipText( "Log in without saving any information." );
|
|
||||||
incognitoCheckBox.setSelected( incognito );
|
|
||||||
incognitoCheckBox.addItemListener( new ItemListener() {
|
|
||||||
@Override
|
|
||||||
public void itemStateChanged(final ItemEvent e) {
|
|
||||||
incognito = incognitoCheckBox.isSelected();
|
|
||||||
SwingUtilities.invokeLater( new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
createAuthenticationPanel();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
JComponent toolsPanel = Components.boxLayout( BoxLayout.LINE_AXIS, incognitoCheckBox, Box.createGlue() );
|
|
||||||
authenticationContainer.add( toolsPanel );
|
|
||||||
for (JButton button : authenticationPanel.getButtons()) {
|
|
||||||
toolsPanel.add( button );
|
|
||||||
button.setBorder( BorderFactory.createEmptyBorder() );
|
|
||||||
button.setMargin( new Insets( 0, 0, 0, 0 ) );
|
|
||||||
button.setAlignmentX( RIGHT_ALIGNMENT );
|
|
||||||
button.setContentAreaFilled( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
checkSignIn();
|
|
||||||
validate();
|
|
||||||
repack();
|
|
||||||
|
|
||||||
SwingUtilities.invokeLater( new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ifNotNullElse( authenticationPanel.getFocusComponent(), signInButton ).requestFocusInWindow();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateUser(@Nullable User user) {
|
|
||||||
this.user = user;
|
|
||||||
checkSignIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean checkSignIn() {
|
|
||||||
String fullName = user == null? "": user.getFullName();
|
|
||||||
char[] masterPassword = authenticationPanel.getMasterPassword();
|
|
||||||
boolean enabled = !fullName.isEmpty() && masterPassword.length > 0;
|
|
||||||
|
|
||||||
if (fullName.isEmpty() || masterPassword.length == 0)
|
|
||||||
identiconLabel.setText( " " );
|
|
||||||
else {
|
|
||||||
MPIdenticon identicon = new MPIdenticon( fullName, masterPassword );
|
|
||||||
identiconLabel.setText( identicon.getText() );
|
|
||||||
identiconLabel.setForeground( Res.colors().fromIdenticonColor( identicon.getColor(), Res.Colors.BackgroundMode.DARK ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
signInButton.setEnabled( enabled );
|
|
||||||
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void trySignIn(final JComponent... signInComponents) {
|
|
||||||
if (!checkSignIn())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (JComponent signInComponent : signInComponents)
|
|
||||||
signInComponent.setEnabled( false );
|
|
||||||
|
|
||||||
signInButton.setEnabled( false );
|
|
||||||
signInButton.setText( "Signing In..." );
|
|
||||||
|
|
||||||
Res.execute( this, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
user.authenticate( authenticationPanel.getMasterPassword() );
|
|
||||||
|
|
||||||
SwingUtilities.invokeLater( new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
signInCallback.signedIn( user );
|
|
||||||
dispose();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
catch (final IncorrectMasterPasswordException e) {
|
|
||||||
SwingUtilities.invokeLater( new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
JOptionPane.showMessageDialog( null, e.getLocalizedMessage(), "Sign In Failed", JOptionPane.ERROR_MESSAGE );
|
|
||||||
authenticationPanel.reset();
|
|
||||||
signInButton.setText( "Sign In" );
|
|
||||||
for (JComponent signInComponent : signInComponents)
|
|
||||||
signInComponent.setEnabled( true );
|
|
||||||
checkSignIn();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SignInCallback {
|
|
||||||
|
|
||||||
void signedIn(User user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.lyndir.masterpassword.MasterKey;
|
|
||||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.Objects;
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-06-08
|
|
||||||
*/
|
|
||||||
public abstract class User {
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private final EnumMap<MasterKey.Version, MasterKey> keyByVersion = Maps.newEnumMap( MasterKey.Version.class );
|
|
||||||
|
|
||||||
public abstract String getFullName();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
protected abstract char[] getMasterPassword();
|
|
||||||
|
|
||||||
public abstract void authenticate(final char[] masterPassword)
|
|
||||||
throws IncorrectMasterPasswordException;
|
|
||||||
|
|
||||||
public int getAvatar() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isKeyAvailable() {
|
|
||||||
return getMasterPassword() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public MasterKey getKey(MasterKey.Version algorithmVersion) {
|
|
||||||
char[] masterPassword = Preconditions.checkNotNull( getMasterPassword(), "User is not authenticated: " + getFullName() );
|
|
||||||
|
|
||||||
MasterKey key = keyByVersion.get( algorithmVersion );
|
|
||||||
if (key == null)
|
|
||||||
putKey( key = MasterKey.create( algorithmVersion, getFullName(), masterPassword ) );
|
|
||||||
if (!key.isValid())
|
|
||||||
key.revalidate( masterPassword );
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void putKey(MasterKey masterKey) {
|
|
||||||
MasterKey oldKey = keyByVersion.put( masterKey.getAlgorithmVersion(), masterKey );
|
|
||||||
if (oldKey != null)
|
|
||||||
oldKey.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reset() {
|
|
||||||
for (MasterKey key : keyByVersion.values())
|
|
||||||
key.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract Iterable<Site> findSitesByName(final String siteName);
|
|
||||||
|
|
||||||
public abstract void addSite(final Site site);
|
|
||||||
|
|
||||||
public abstract void deleteSite(final Site site);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(final Object obj) {
|
|
||||||
return this == obj || obj instanceof User && Objects.equals( getFullName(), ((User) obj).getFullName() );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode( getFullName() );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getFullName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* @author lhunath, 15-02-04
|
|
||||||
*/
|
|
||||||
|
|
||||||
@ParametersAreNonnullByDefault
|
|
||||||
package com.lyndir.masterpassword.gui;
|
|
||||||
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui.platform.mac;
|
|
||||||
|
|
||||||
import com.apple.eawt.*;
|
|
||||||
import com.lyndir.masterpassword.gui.GUI;
|
|
||||||
import com.lyndir.masterpassword.gui.PasswordFrame;
|
|
||||||
import com.lyndir.masterpassword.gui.User;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-06-10
|
|
||||||
*/
|
|
||||||
public class AppleGUI extends GUI {
|
|
||||||
|
|
||||||
public AppleGUI() {
|
|
||||||
|
|
||||||
Application application = Application.getApplication();
|
|
||||||
application.addAppEventListener( new AppForegroundListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void appMovedToBackground(AppEvent.AppForegroundEvent arg0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void appRaisedToForeground(AppEvent.AppForegroundEvent arg0) {
|
|
||||||
open();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
application.addAppEventListener( new AppReOpenedListener() {
|
|
||||||
@Override
|
|
||||||
public void appReOpened(AppEvent.AppReOpenedEvent arg0) {
|
|
||||||
open();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected PasswordFrame newPasswordFrame(final User user) {
|
|
||||||
PasswordFrame frame = super.newPasswordFrame( user );
|
|
||||||
frame.setDefaultCloseOperation( WindowConstants.HIDE_ON_CLOSE );
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui.util;
|
|
||||||
|
|
||||||
import com.lyndir.masterpassword.gui.Res;
|
|
||||||
import java.awt.*;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.border.Border;
|
|
||||||
import javax.swing.border.CompoundBorder;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2014-06-08
|
|
||||||
*/
|
|
||||||
public abstract class Components {
|
|
||||||
|
|
||||||
public static GradientPanel boxLayout(int axis, Component... components) {
|
|
||||||
GradientPanel container = gradientPanel( null, null );
|
|
||||||
// container.setBackground( Color.red );
|
|
||||||
container.setLayout( new BoxLayout( container, axis ) );
|
|
||||||
for (Component component : components)
|
|
||||||
container.add( component );
|
|
||||||
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GradientPanel borderPanel(final JComponent component, @Nullable final Border border) {
|
|
||||||
return borderPanel( component, border, null );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GradientPanel borderPanel(final JComponent component, @Nullable final Border border, @Nullable Color background) {
|
|
||||||
GradientPanel box = boxLayout( BoxLayout.LINE_AXIS, component );
|
|
||||||
|
|
||||||
if (border != null)
|
|
||||||
box.setBorder( border );
|
|
||||||
|
|
||||||
if (background != null)
|
|
||||||
box.setBackground( background );
|
|
||||||
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GradientPanel gradientPanel(@Nullable final LayoutManager layout, @Nullable final Color color) {
|
|
||||||
return new GradientPanel( layout, color ) {
|
|
||||||
{
|
|
||||||
setOpaque( color != null );
|
|
||||||
setBackground( null );
|
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JTextField textField() {
|
|
||||||
return new JTextField() {
|
|
||||||
{
|
|
||||||
setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
|
|
||||||
BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) );
|
|
||||||
setFont( Res.valueFont().deriveFont( 12f ) );
|
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JPasswordField passwordField() {
|
|
||||||
return new JPasswordField() {
|
|
||||||
{
|
|
||||||
setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
|
|
||||||
BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) );
|
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JButton button(String label) {
|
|
||||||
return new JButton( label ) {
|
|
||||||
{
|
|
||||||
setFont( Res.controlFont().deriveFont( 12f ) );
|
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( 20, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Component stud() {
|
|
||||||
Dimension studDimension = new Dimension( 8, 8 );
|
|
||||||
Box.Filler rigidArea = new Box.Filler( studDimension, studDimension, studDimension );
|
|
||||||
rigidArea.setAlignmentX( Component.LEFT_ALIGNMENT );
|
|
||||||
rigidArea.setAlignmentY( Component.BOTTOM_ALIGNMENT );
|
|
||||||
rigidArea.setBackground( Color.red );
|
|
||||||
return rigidArea;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JSpinner spinner(final SpinnerModel model) {
|
|
||||||
return new JSpinner( model ) {
|
|
||||||
{
|
|
||||||
CompoundBorder editorBorder = BorderFactory.createCompoundBorder(
|
|
||||||
BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
|
|
||||||
BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) );
|
|
||||||
((DefaultEditor) getEditor()).getTextField().setBorder( editorBorder );
|
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
|
||||||
setBorder( null );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( 20, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JLabel label(@Nullable String label) {
|
|
||||||
return label( label, SwingConstants.LEADING );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param horizontalAlignment One of the following constants
|
|
||||||
* defined in <code>SwingConstants</code>:
|
|
||||||
* <code>LEFT</code>,
|
|
||||||
* <code>CENTER</code>,
|
|
||||||
* <code>RIGHT</code>,
|
|
||||||
* <code>LEADING</code> or
|
|
||||||
* <code>TRAILING</code>.
|
|
||||||
*/
|
|
||||||
public static JLabel label(@Nullable final String label, final int horizontalAlignment) {
|
|
||||||
return new JLabel( label, horizontalAlignment ) {
|
|
||||||
{
|
|
||||||
setFont( Res.controlFont().deriveFont( 12f ) );
|
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JCheckBox checkBox(final String label) {
|
|
||||||
return new JCheckBox( label ) {
|
|
||||||
{
|
|
||||||
setFont( Res.controlFont().deriveFont( 12f ) );
|
|
||||||
setBackground( null );
|
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@SafeVarargs
|
|
||||||
public static <V> JComboBox<V> comboBox(final V... values) {
|
|
||||||
return comboBox( new DefaultComboBoxModel<>( values ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <M> JComboBox<M> comboBox(final ComboBoxModel<M> model) {
|
|
||||||
return new JComboBox<M>( model ) {
|
|
||||||
{
|
|
||||||
// CompoundBorder editorBorder = BorderFactory.createCompoundBorder(
|
|
||||||
// BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
|
|
||||||
// BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) );
|
|
||||||
// ((JComponent) ((BasicComboBoxEditor) getEditor()).getEditorComponent()).setBorder(editorBorder);
|
|
||||||
setFont( Res.controlFont().deriveFont( 12f ) );
|
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
|
||||||
// setBorder(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dimension getMaximumSize() {
|
|
||||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class GradientPanel extends JPanel {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Color gradientColor;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private GradientPaint paint;
|
|
||||||
|
|
||||||
protected GradientPanel(@Nullable final LayoutManager layout, @Nullable final Color gradientColor) {
|
|
||||||
super( layout );
|
|
||||||
this.gradientColor = gradientColor;
|
|
||||||
setBackground( null );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Color getGradientColor() {
|
|
||||||
return gradientColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGradientColor(@Nullable final Color gradientColor) {
|
|
||||||
this.gradientColor = gradientColor;
|
|
||||||
revalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doLayout() {
|
|
||||||
super.doLayout();
|
|
||||||
|
|
||||||
if (gradientColor != null) {
|
|
||||||
paint = new GradientPaint( new Point( 0, 0 ), gradientColor, new Point( getWidth(), getHeight() ), gradientColor.darker() );
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void paintComponent(final Graphics g) {
|
|
||||||
super.paintComponent( g );
|
|
||||||
|
|
||||||
if (paint != null) {
|
|
||||||
((Graphics2D) g).setPaint( paint );
|
|
||||||
g.fillRect( 0, 0, getWidth(), getHeight() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.gui.util;
|
|
||||||
|
|
||||||
import com.google.common.primitives.UnsignedInteger;
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lhunath, 2016-10-29
|
|
||||||
*/
|
|
||||||
public class UnsignedIntegerModel extends SpinnerNumberModel {
|
|
||||||
|
|
||||||
public UnsignedIntegerModel() {
|
|
||||||
this( UnsignedInteger.ZERO, UnsignedInteger.ZERO, UnsignedInteger.MAX_VALUE, UnsignedInteger.ONE );
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnsignedIntegerModel(final UnsignedInteger value) {
|
|
||||||
this( value, UnsignedInteger.ZERO, UnsignedInteger.MAX_VALUE, UnsignedInteger.ONE );
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnsignedIntegerModel(final UnsignedInteger value, final UnsignedInteger minimum) {
|
|
||||||
this( value, minimum, UnsignedInteger.MAX_VALUE, UnsignedInteger.ONE );
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnsignedIntegerModel(final UnsignedInteger value, final UnsignedInteger minimum, final UnsignedInteger maximum) {
|
|
||||||
this( value, minimum, maximum, UnsignedInteger.ONE );
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnsignedIntegerModel(final UnsignedInteger value, final UnsignedInteger minimum, final UnsignedInteger maximum,
|
|
||||||
final UnsignedInteger stepSize) {
|
|
||||||
super( value, minimum, maximum, stepSize );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UnsignedInteger getNumber() {
|
|
||||||
return (UnsignedInteger) super.getNumber();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* @author lhunath, 15-02-04
|
|
||||||
*/
|
|
||||||
|
|
||||||
@ParametersAreNonnullByDefault
|
|
||||||
package com.lyndir.masterpassword.gui.util;
|
|
||||||
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB |
@@ -1,19 +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 (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 (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(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(final MPSite site, final 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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user