2
0

Compare commits

..

109 Commits

Author SHA1 Message Date
Maarten Billemont
a85eff4277 Include semi-standard getline. 2017-09-25 02:59:09 -04:00
Maarten Billemont
98f1c776be Fix some warnings. 2017-09-25 02:56:37 -04:00
Maarten Billemont
6b554c67ed More standard memset_s 2017-09-25 02:53:34 -04:00
Maarten Billemont
f2ae35080d mpw-2.6-cli-3 release. 2017-09-24 15:40:19 -04:00
Maarten Billemont
0ff6c93a95 Document default key size. 2017-09-24 13:18:33 -04:00
Maarten Billemont
9147600b97 Travis cache doesn't need to be disabled anymore. 2017-09-24 13:14:58 -04:00
Maarten Billemont
fafe56166e bzero is nonstandard. Replace with memset_s. 2017-09-24 13:14:16 -04:00
Maarten Billemont
0a024b2594 AES-CBC needs PKCS#7 padding. 2017-09-24 13:06:19 -04:00
Maarten Billemont
b4c2a393f1 Clean up aes state, default to 512 key size, improve log output. 2017-09-24 12:00:38 -04:00
Maarten Billemont
39dcef46d2 Show in Travis log why tests fail. 2017-09-24 00:26:58 -04:00
Maarten Billemont
d6a88583f5 AES needs to be CBC, not CTR. 2017-09-23 20:14:53 -04:00
Maarten Billemont
1c17b84dcf Some tweaks for Travis. 2017-09-23 19:24:06 -04:00
Maarten Billemont
cecaf1b5cc Log fixes, test improvements and some refactoring. 2017-09-23 19:11:06 -04:00
Maarten Billemont
888338e107 Fix siteKey algorithm for siteState. 2017-09-23 19:09:19 -04:00
Maarten Billemont
32055abf29 ciphers.plist is no longer needed. 2017-09-22 20:08:46 -04:00
Maarten Billemont
0f72dffaf1 Updated keyID in test case. 2017-09-22 19:05:59 -04:00
Maarten Billemont
5d1be43b65 Deep Java refactoring to match the C API logic and clean up some OO oddities. 2017-09-22 19:03:50 -04:00
Maarten Billemont
dc7089c38c mpw-tests was not checking mpw_tests.xml's keyID. 2017-09-22 18:23:08 -04:00
Maarten Billemont
34540f0844 Finish rename Marshall -> Marshal. 2017-09-22 15:20:14 -04:00
Maarten Billemont
e818713484 Fix filenames in build 2017-09-22 14:21:31 -04:00
Maarten Billemont
6e2289994c Fix gradle build with missing local.properties. 2017-09-21 10:32:38 -04:00
Maarten Billemont
05a9ba46d0 Marshal refactoring to prepare for new format. 2017-09-20 17:45:49 -04:00
Maarten Billemont
70bb30ba0c Skip masterpassword-android if not set up to build it. 2017-09-20 16:52:20 -04:00
Maarten Billemont
444d7e9b35 Source fix-ups: single l marshal, copyright, .travis. 2017-09-20 12:43:03 -04:00
Maarten Billemont
47164c7a92 Marshal has only one l. 2017-09-20 10:48:04 -04:00
Maarten Billemont
ad00ceb4ce Harmonize C/Java code more, WIP crypt/derive in Java. 2017-09-19 14:52:43 -04:00
Maarten Billemont
473e3ca11f Run gradle test in Travis. 2017-09-19 13:46:27 -04:00
Maarten Billemont
35c0431cec Update Java to match C's internal changes. 2017-09-19 13:45:51 -04:00
Maarten Billemont
70c784db83 Update MP_FULLNAME in mpw.bashrc. 2017-09-15 13:24:45 -04:00
Maarten Billemont
d448099a2d -s is -P now. 2017-09-14 23:14:00 -04:00
Maarten Billemont
e3a7ea57e0 Type key is K, subkey context is not bound by BYTES_MIN/MAX. 2017-09-14 23:03:39 -04:00
Maarten Billemont
fa6133200e Added bashlib. 2017-09-14 16:44:32 -04:00
Maarten Billemont
dfa67bdca9 Added some scripts to do math on password strength and generate random dictionary passphrases. 2017-09-14 16:31:11 -04:00
Maarten Billemont
8c9c4ef7b2 Describe how to use the cmake alternative build system. 2017-09-10 14:17:06 -04:00
Maarten Billemont
1adb18a7e7 Fixed #206 - 'p' trigger for phrase was missing. 2017-09-10 14:03:56 -04:00
Maarten Billemont
f50fdb7777 Some build tool updates, primarily cmake. 2017-09-10 13:57:14 -04:00
Maarten Billemont
33bf2c93d0 Some fixes to the CSS for videos. 2017-09-08 11:19:10 -04:00
Maarten Billemont
f2abcc9e43 A few fixes to the about video. 2017-09-08 10:31:43 -04:00
Maarten Billemont
5ef69aa045 Make bcrypt code more standard. 2017-09-07 00:05:55 -04:00
Maarten Billemont
1c0f274868 Include for waitpid. 2017-09-06 23:54:52 -04:00
Maarten Billemont
1f592f50a9 Release 2.6-cli-2 2017-09-06 00:34:09 -04:00
Maarten Billemont
30fdb54e94 Fix support for building without MPW_JSON enabled. 2017-09-06 00:32:57 -04:00
Maarten Billemont
4f552be5a9 Update cmake for source and improve feature checking in ./build 2017-09-06 00:31:49 -04:00
Maarten Billemont
1439df9f9a Add license headers to cli source files. 2017-09-04 19:37:36 -04:00
Maarten Billemont
e676a0e258 Release 2.6-cli-1. 2017-09-04 14:50:57 -04:00
Maarten Billemont
895df6377d Only distribute release tags, include TAG and VERSION. 2017-09-04 14:48:40 -04:00
Maarten Billemont
3d46f60ff4 Add VERSION to distribution archive. 2017-09-04 14:38:08 -04:00
Maarten Billemont
44d8ab6e53 Remove some pointless local's. 2017-09-04 14:29:25 -04:00
Maarten Billemont
cd70009c2c Move instructions into cli-c for distribution. 2017-09-04 14:17:20 -04:00
Maarten Billemont
4261160902 Some more refactoring. 2017-09-03 17:00:35 -04:00
Maarten Billemont
ced7aef5d7 Fix target assignment of mpw-cli-util.c 2017-09-03 16:53:14 -04:00
Maarten Billemont
63100913c5 libjson-c's upstream still has build issues, switch back to our fork. 2017-09-03 15:48:43 -04:00
Maarten Billemont
6904d4c427 purposeResult is not an owned reference. 2017-09-03 15:46:38 -04:00
Maarten Billemont
4271d77225 Remove debugging code. 2017-09-03 15:43:22 -04:00
Maarten Billemont
6811773e54 Refactor CLI code to make the flow more clear and the free'ing more reliable. 2017-09-03 15:41:12 -04:00
Maarten Billemont
060ce61030 Fix ./build's targets interpretation & other improvements. 2017-09-01 11:16:09 -04:00
Maarten Billemont
9a5e9ced31 More consistent spacing in usage output. 2017-08-31 15:32:58 -04:00
Maarten Billemont
568401a612 Extract cli utilities into mpw-cli-util to keep things cleaner. 2017-08-31 15:30:42 -04:00
Maarten Billemont
92a3a0ccbd Switch libjson-c back to upstream and fix some build issues with it. 2017-08-31 15:14:08 -04:00
Maarten Billemont
ba24c2be34 Rename env vars to make it clear they are for the mpw binary, add PATH support for askpass. 2017-08-31 13:42:03 -04:00
Maarten Billemont
019cefd3fb Add support for askpass program for reading answers from the user. 2017-08-31 13:37:28 -04:00
Maarten Billemont
eef82f7ed4 Don't infinite loop when prompting if no terminal is available. 2017-08-31 11:49:36 -04:00
Maarten Billemont
2dfe0f78b0 Build & link all sources at once to allow link-time optimization. 2017-08-31 11:48:34 -04:00
Maarten Billemont
627144b583 Make MPW_JSON optional as documented. 2017-08-31 11:48:10 -04:00
Maarten Billemont
fad0f5e5dd Add PearlCryptUtils back since PearlKeyChain uses it. 2017-08-30 19:05:42 -04:00
Maarten Billemont
8562338b62 Support for reading master password from an FD. 2017-08-30 19:05:21 -04:00
Maarten Billemont
17de69834e Declare __unused if not declared by compiler. 2017-08-30 13:27:59 -04:00
Maarten Billemont
aeeab7dbf6 Improve build script documentation and targets variable. 2017-08-30 10:18:23 -04:00
Maarten Billemont
ce60ba6c9f External libs configuration no longer used. 2017-08-30 09:58:01 -04:00
Maarten Billemont
d22f93e564 Format code. 2017-08-30 09:57:15 -04:00
Maarten Billemont
6f4f6b8d1e Copy TOTP counter support to v2 override. 2017-08-30 09:54:16 -04:00
Maarten Billemont
6fa8ee53cd Currently unused implementation of HOTP. 2017-08-30 09:40:51 -04:00
Maarten Billemont
23af56c150 Slight clean-up of types, includes and warnings. 2017-08-30 09:39:35 -04:00
Maarten Billemont
91828cbad7 Test script for CLI. 2017-08-30 09:38:23 -04:00
Maarten Billemont
40d2788ae0 Implement OTP counter feature for counter values of 0. 2017-08-30 09:35:55 -04:00
Maarten Billemont
21a3a28980 Copy args so we can re-use it. 2017-08-29 12:06:40 -04:00
Maarten Billemont
f5c7bee58f Remove unused Pearl-Crypto. 2017-08-29 01:01:38 -04:00
Maarten Billemont
e364f5159b Fix build warnings. 2017-08-28 23:48:24 -04:00
Maarten Billemont
74f9f1ca00 Fix up objc code to match new C API. 2017-08-28 19:37:51 -04:00
Maarten Billemont
328d38ac19 Remove PearlLayout. 2017-08-28 19:34:22 -04:00
Maarten Billemont
7735d82c7b Silence unused variable warnings for compiler check variables. 2017-08-28 19:25:51 -04:00
Maarten Billemont
1e7c200865 Remove non-standard asprintf from mpw-cli. 2017-08-28 18:25:58 -04:00
Maarten Billemont
724b357dd8 Create path for mpw.d sites files if it doesn't exist yet. 2017-08-28 17:58:57 -04:00
Maarten Billemont
a85efc5736 Remove all build-time external dependency fetching/building logic. 2017-08-27 11:17:45 -04:00
Maarten Billemont
9eb58119ea Remove dependency on external bcrypt. 2017-08-27 10:47:39 -04:00
Maarten Billemont
77b4ed2cfd Remove dependency on asprintf. 2017-08-27 09:25:53 -04:00
Maarten Billemont
011416690a Some warning cleanup. 2017-08-27 09:04:18 -04:00
Maarten Billemont
53eb5c8a73 Refactoring and fix up mpw_color. 2017-08-27 08:53:58 -04:00
Maarten Billemont
2f99855cd4 Remove non-standard host-endian functions. 2017-08-27 07:46:34 -04:00
Maarten Billemont
18eaeec1de Fix some rewrite bugs. 2017-08-23 00:53:14 -04:00
Maarten Billemont
5ee700c9b9 Small fix. 2017-08-23 00:05:50 -04:00
Maarten Billemont
a8949ca07e NULL out free'ed references. 2017-08-23 00:01:23 -04:00
Maarten Billemont
0a42579d9e Improved free'ing on error conditions. 2017-08-22 18:38:36 -04:00
Maarten Billemont
f2f8747126 Support for persisting login/question type & stateful types, null checking, cleanup and rewrite of CLI state. 2017-08-22 18:18:24 -04:00
Maarten Billemont
f83cdacab8 Document -M, -P, allow saving login name. 2017-08-22 11:38:04 -04:00
Maarten Billemont
98aeb02d32 Forgot to merge in i386 2017-08-13 19:06:06 -04:00
Maarten Billemont
2bbaeccd05 Forgot to merge in i386 2017-08-13 18:32:03 -04:00
Maarten Billemont
91e0a04e66 Add support for i386 in libjson-c and libsodium builds. 2017-08-13 16:53:18 -04:00
Maarten Billemont
661fc523ad Don't pass nil error to crashlytics. 2017-08-13 16:49:56 -04:00
Maarten Billemont
b9cbaf7343 Cross-compile fixes for iOS. 2017-08-13 15:52:08 -04:00
Maarten Billemont
e451308fdc Refactoring regression, use right algorithm version for siteKey. 2017-08-13 11:35:15 -04:00
Maarten Billemont
1b51c5efa4 Build script update. 2017-08-13 11:02:05 -04:00
Maarten Billemont
a8776eec58 Fix C cli API. 2017-08-13 08:50:16 -04:00
Maarten Billemont
d9cdb7ef83 Fix error name collision. 2017-08-13 01:00:03 -04:00
Maarten Billemont
28c7a64bd2 Fork json-c temporarily to fix some code issues. 2017-08-13 00:30:25 -04:00
Maarten Billemont
d7193f7753 Adapt macOS for new APIs. 2017-08-12 22:26:48 -04:00
Maarten Billemont
f5c7d11f0e Add marshalling metadata lookup & adapt iOS for new APIs. 2017-08-12 21:57:47 -04:00
Maarten Billemont
c0ba96daa2 Update Darwin platform project with solid support for linking libsodium & libjson-c 2017-08-11 01:42:03 -04:00
Maarten Billemont
b374d9e04a Some type fixes. 2017-08-10 21:29:59 -04:00
188 changed files with 29256 additions and 6293 deletions

16
.gitignore vendored
View File

@@ -13,8 +13,6 @@ xcuserdata/
DerivedData/ DerivedData/
# Generated # Generated
/platform-independent/cli-c/VERSION
/platform-independent/cli-c/mpw-*.tar.gz
/platform-darwin/Resources/Media/Images.xcassets/ /platform-darwin/Resources/Media/Images.xcassets/
# Media # Media
@@ -31,17 +29,3 @@ local.properties
# Maven # Maven
target target
dependency-reduced-pom.xml dependency-reduced-pom.xml
# C
core/c/*.o
core/c/lib/*/.unpacked
core/c/lib/*/.patched
core/c/lib/*/src
core/c/lib/include
platform-independent/cli-c/cli/*.o
platform-independent/cli-c/mpw-*.tar.gz
platform-independent/cli-c/mpw-*.tar.gz.sig
platform-independent/cli-c/mpw
platform-independent/cli-c/mpw-bench
platform-independent/cli-c/mpw-tests
platform-independent/cli-c/VERSION

2
.gitmodules vendored
View File

@@ -24,4 +24,4 @@
url = https://github.com/jedisct1/libsodium.git url = https://github.com/jedisct1/libsodium.git
[submodule "platform-darwin/External/libjson-c"] [submodule "platform-darwin/External/libjson-c"]
path = platform-darwin/External/libjson-c path = platform-darwin/External/libjson-c
url = https://github.com/json-c/json-c.git url = https://github.com/lhunath/json-c.git

View File

@@ -1,10 +1,12 @@
language: objective-c language: objective-c
os: osx
osx_image: xcode8.3 osx_image: xcode8.3
env: TERM=dumb SHLVL=0 env: TERM=dumb SHLVL=0
git: git:
submodules: true submodules: true
script: script:
- "( brew install libsodium )" - "( brew install libsodium json-c )"
- "( cd ./platform-independent/cli-c && ./clean && targets='mpw mpw-bench mpw-tests' ./build && ./mpw-tests )" - "( cd ./platform-independent/cli-c && ./clean && targets='mpw mpw-bench mpw-tests' ./build && ./mpw-tests && ./mpw-cli-tests )"
- "( cd ./gradle && ./gradlew --info clean test )"
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword iOS' -sdk iphonesimulator )" - "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword iOS' -sdk iphonesimulator )"
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword macOS' )" - "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword macOS' )"

View File

@@ -194,49 +194,7 @@ Note that in order to build the Android application, you will need to have the A
Go into the `platform-independent/cli-c` directory and run `./build`. The native command-line client will then be built. Go into the `platform-independent/cli-c` directory and run `./build`. The native command-line client will then be built.
When the build completes, you will have an `mpw` binary you can use. You can copy it into your `PATH` or use the `./install` script to help you do so. For detailed instructions, see [the native CLI instructions](platform-independent/cli-c/README.md).
For example:
./build && sudo ./install
mpw -h
Normally, this is all you need to do, however note that there are a few dependencies that need to be met, depending on which targets you are building (by default, only the `mpw` target is built):
- `mpw`
The C implementation depends either on `libsodium` or Tarsnap's `scrypt` and `openssl-dev`.
We recommend you install `libsodium`. If `libsodium` is not installed when `./build` is executed, the script will try to download and statically link Tarsnap's `scrypt` instead. Tarsnap's `scrypt` depends on you having `openssl-dev` installed.
If you have `mpw_color` enabled (it is enabled by default), the build also depends on `ncurses-dev` to communicate with the terminal.
- `mpw-bench`
This tool compares the performance of a few cryptographic algorithms, including bcrypt. The `./build` script will try to automatically download and statically link `bcrypt`.
- `mpw-tests`
This tool runs a suite of tests to ensure the correct passwords are being generated by the algorithm under various circumstances. The test suite is declared in `mpw-tests.xml` which needs to exist in the current working directory when running the tool. In addition, `libxml2` is used to parse the file, so this target depends on you having it installed when running `./build`.
Finally, there are a few different ways you can modify the build process.
To build additional targets, set the `targets` environment variable:
targets='mpw mpw-tests' ./build
To pass additional compiler arguments, eg. add a library search path, pass them as arguments to the script:
./build -L/usr/local/lib
There are a few toggleable features, to change them, pass them as environment variables:
mpw_color=0 ./build
Currently, there is only one toggleable feature:
- `mpw_color`: [default: 1] Colorized Identicon, depends on `ncurses-dev`.
## Support ## Support

606
core/c/aes.c Normal file
View File

@@ -0,0 +1,606 @@
/*
Source: https://github.com/kokke/tiny-AES-c
This is an implementation of the AES algorithm, specifically ECB and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
ECB-AES128
----------
plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
key:
2b7e151628aed2a6abf7158809cf4f3c
resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
For AES192/256 the block size is proportionally larger.
*/
/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include <string.h>
#include "aes.h"
#include "mpw-util.h"
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4
#if defined(AES_256) && (AES_256 == 1)
#define Nk 8
#define KEYLEN 32
#define Nr 14
#define keyExpSize 240
#elif defined(AES_192) && (AES_192 == 1)
#define Nk 6
#define KEYLEN 24
#define Nr 12
#define keyExpSize 208
#elif defined(AES_128) && (AES_128 == 1)
#define Nk 4 // The number of 32 bit words in a key.
#define KEYLEN 16 // Key length in bytes
#define Nr 10 // The number of rounds in AES Cipher.
#define keyExpSize 176
#else
#error Must define either AES_128, AES_192 or AES_256.
#endif
// jcallan@github points out that declaring Multiply as a function
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES128-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif
/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];
static state_t* state;
// The array that stores the round keys.
static uint8_t RoundKey[keyExpSize];
// The Key input to the AES Program
static const uint8_t* Key;
#if defined(AES_CBC) && AES_CBC
// Initial Vector used only for CBC mode
static uint8_t* Iv;
#endif
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM -
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
static const uint8_t rsbox[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
// The round constant word array, Rcon[i], contains the values given by
// x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
/*
* Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES128-C/pull/12),
* that you can remove most of the elements in the Rcon array, because they are unused.
*
* From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
*
* "Only the first some of these constants are actually used up to rcon[10] for AES-128 (as 11 round keys are needed),
* up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
*
* ... which is why the full array below has been 'disabled' below.
*/
#if 0
static const uint8_t Rcon[256] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d };
#endif
/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
static uint8_t getSBoxValue(uint8_t num)
{
return sbox[num];
}
static uint8_t getSBoxInvert(uint8_t num)
{
return rsbox[num];
}
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
static void KeyExpansion(void)
{
uint32_t i;
uint8_t k, tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for (i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
//i == Nk
for (; i < Nb * (Nr + 1); ++i)
{
{
tempa[0]=RoundKey[(i-1) * 4 + 0];
tempa[1]=RoundKey[(i-1) * 4 + 1];
tempa[2]=RoundKey[(i-1) * 4 + 2];
tempa[3]=RoundKey[(i-1) * 4 + 3];
}
if (i % Nk == 0)
{
// This function shifts the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
k = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = k;
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
#if defined(AES256) && (AES256 == 1)
if (i % Nk == 4)
{
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
#endif
RoundKey[i * 4 + 0] = RoundKey[(i - Nk) * 4 + 0] ^ tempa[0];
RoundKey[i * 4 + 1] = RoundKey[(i - Nk) * 4 + 1] ^ tempa[1];
RoundKey[i * 4 + 2] = RoundKey[(i - Nk) * 4 + 2] ^ tempa[2];
RoundKey[i * 4 + 3] = RoundKey[(i - Nk) * 4 + 3] ^ tempa[3];
}
}
// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round)
{
uint8_t i,j;
for (i=0;i<4;++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[round * Nb * 4 + i * Nb + j];
}
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(void)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(void)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return (uint8_t)((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}
// MixColumns function mixes the columns of the state matrix
static void MixColumns(void)
{
uint8_t i;
uint8_t Tmp,Tm,t;
for (i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}
// Multiply is used to multiply numbers in the field GF(2^8)
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y>>1 & 1) * xtime(x)) ^
((y>>2 & 1) * xtime(xtime(x))) ^
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))));
}
#else
#define Multiply(x, y) (uint8_t) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#endif
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(void)
{
int i;
uint8_t a, b, c, d;
for (i = 0; i < 4; ++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(void)
{
uint8_t i,j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(void)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
// Rotate second row 2 columns to right
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to right
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
// Cipher is the main function that encrypts the PlainText.
static void Cipher(void)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for (round = 1; round < Nr; ++round)
{
SubBytes();
ShiftRows();
MixColumns();
AddRoundKey(round);
}
// The last round is given below.
// The MixColumns function is not here in the last round.
SubBytes();
ShiftRows();
AddRoundKey(Nr);
}
static void InvCipher(void)
{
uint8_t round=0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for (round = (Nr - 1); round > 0; --round)
{
InvShiftRows();
InvSubBytes();
AddRoundKey(round);
InvMixColumns();
}
// The last round is given below.
// The MixColumns function is not here in the last round.
InvShiftRows();
InvSubBytes();
AddRoundKey(0);
}
/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
#if defined(AES_ECB) && (AES_ECB == 1)
void AES_ECB_encrypt(uint8_t *output, const uint8_t *input, const uint32_t length, const uint8_t *key)
{
// Copy input to output, and work in-memory on output
memcpy(output, input, length);
state = (state_t*)output;
Key = key;
KeyExpansion();
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher();
mpw_zero( RoundKey, keyExpSize );
}
void AES_ECB_decrypt(uint8_t *output, const uint8_t *input, const uint32_t length, const uint8_t *key)
{
// Copy input to output, and work in-memory on output
memcpy(output, input, length);
state = (state_t*)output;
// The KeyExpansion routine must be called before encryption.
Key = key;
KeyExpansion();
InvCipher();
mpw_zero( RoundKey, keyExpSize );
}
#endif // #if defined(AES_ECB) && (AES_ECB == 1)
#if defined(AES_CBC) && (AES_CBC == 1)
static void XorWithIv(uint8_t* buf)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) //WAS for(i = 0; i < KEYLEN; ++i) but the block in AES is always 128bit so 16 bytes!
{
buf[i] ^= Iv[i];
}
}
void AES_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv)
{
uintptr_t i;
uint8_t extra = (uint8_t)(length % AES_BLOCKLEN); /* Remaining bytes in the last non-full block */
// Skip the key expansion if key is passed as 0
if (0 != key)
{
Key = key;
KeyExpansion();
}
if (iv != 0)
{
Iv = (uint8_t*)iv;
}
for (i = 0; i < length; i += AES_BLOCKLEN)
{
XorWithIv(input);
memcpy(output, input, AES_BLOCKLEN);
state = (state_t*)output;
Cipher();
Iv = output;
input += AES_BLOCKLEN;
output += AES_BLOCKLEN;
//printf("Step %d - %d", i/16, i);
}
if (extra)
{
memcpy(output, input, extra);
state = (state_t*)output;
Cipher();
}
mpw_zero( RoundKey, keyExpSize );
}
void AES_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv)
{
uintptr_t i;
uint8_t extra = (uint8_t)(length % AES_BLOCKLEN); /* Remaining bytes in the last non-full block */
// Skip the key expansion if key is passed as 0
if (0 != key)
{
Key = key;
KeyExpansion();
}
// If iv is passed as 0, we continue to encrypt without re-setting the Iv
if (iv != 0)
{
Iv = (uint8_t*)iv;
}
for (i = 0; i < length; i += AES_BLOCKLEN)
{
memcpy(output, input, AES_BLOCKLEN);
state = (state_t*)output;
InvCipher();
XorWithIv(output);
Iv = input;
input += AES_BLOCKLEN;
output += AES_BLOCKLEN;
}
if (extra)
{
memcpy(output, input, extra);
state = (state_t*)output;
InvCipher();
}
mpw_zero( RoundKey, keyExpSize );
}
#endif // #if defined(AES_CBC) && (AES_CBC == 1)

50
core/c/aes.h Normal file
View File

@@ -0,0 +1,50 @@
/*
Source: https://github.com/kokke/tiny-AES-c
This is an implementation of the AES algorithm, specifically ECB and CBC mode.
*/
#ifndef _AES_H_
#define _AES_H_
#include <stdint.h>
// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// AES_CBC enables AES encryption in CBC-mode of operation.
// AES_ECB enables the basic ECB 16-byte block algorithm. Both can be enabled simultaneously.
// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
#ifndef AES_CBC
#define AES_CBC 1
#endif
#ifndef AES_ECB
#define AES_ECB 1
#endif
#define AES_128 1
//#define AES_192 1
//#define AES_256 1
#define AES_BLOCKLEN 16 //Block length in bytes AES is 128b block only
#if defined(AES_ECB) && (AES_ECB == 1)
void AES_ECB_encrypt(uint8_t *output, const uint8_t *input, const uint32_t length, const uint8_t *key);
void AES_ECB_decrypt(uint8_t *output, const uint8_t *input, const uint32_t length, const uint8_t *key);
#endif // #if defined(AES_ECB) && (AES_ECB == !)
#if defined(AES_CBC) && (AES_CBC == 1)
void AES_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, const uint32_t length, const uint8_t* key, const uint8_t* iv);
void AES_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, const uint32_t length, const uint8_t* key, const uint8_t* iv);
#endif // #if defined(AES_CBC) && (AES_CBC == 1)
#endif //_AES_H_

View File

@@ -57,7 +57,7 @@
#include "base64.h" #include "base64.h"
/* aaaack but it's fast and const should make it shared text page. */ /* aaaack but it's fast and const should make it shared text page. */
static const unsigned char b64ToBits[256] = static const uint8_t b64ToBits[256] =
{ {
/* ASCII table */ /* ASCII table */
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
@@ -97,20 +97,20 @@ int mpw_base64_decode(uint8_t *plainBuf, const char *b64Text) {
b64Cursor = (uint8_t *)b64Text; b64Cursor = (uint8_t *)b64Text;
register uint8_t *plainCursor = plainBuf; register uint8_t *plainCursor = plainBuf;
while (b64Remaining > 4) { while (b64Remaining > 4) {
*(plainCursor++) = (b64ToBits[b64Cursor[0]] << 2 | b64ToBits[b64Cursor[1]] >> 4); *(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[0]] << 2 | b64ToBits[b64Cursor[1]] >> 4);
*(plainCursor++) = (b64ToBits[b64Cursor[1]] << 4 | b64ToBits[b64Cursor[2]] >> 2); *(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[1]] << 4 | b64ToBits[b64Cursor[2]] >> 2);
*(plainCursor++) = (b64ToBits[b64Cursor[2]] << 6 | b64ToBits[b64Cursor[3]]); *(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[2]] << 6 | b64ToBits[b64Cursor[3]]);
b64Cursor += 4; b64Cursor += 4;
b64Remaining -= 4; b64Remaining -= 4;
} }
/* Note: (b64Size == 1) would be an error, so just ingore that case */ /* Note: (b64Size == 1) would be an error, so just ingore that case */
if (b64Remaining > 1) if (b64Remaining > 1)
*(plainCursor++) = (b64ToBits[b64Cursor[0]] << 2 | b64ToBits[b64Cursor[1]] >> 4); *(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[0]] << 2 | b64ToBits[b64Cursor[1]] >> 4);
if (b64Remaining > 2) if (b64Remaining > 2)
*(plainCursor++) = (b64ToBits[b64Cursor[1]] << 4 | b64ToBits[b64Cursor[2]] >> 2); *(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[1]] << 4 | b64ToBits[b64Cursor[2]] >> 2);
if (b64Remaining > 3) if (b64Remaining > 3)
*(plainCursor++) = (b64ToBits[b64Cursor[2]] << 6 | b64ToBits[b64Cursor[3]]); *(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[2]] << 6 | b64ToBits[b64Cursor[3]]);
return (int)(plainCursor - plainBuf); return (int)(plainCursor - plainBuf);
} }
@@ -126,7 +126,7 @@ size_t mpw_base64_encode_max(size_t plainSize) {
int mpw_base64_encode(char *b64Text, const uint8_t *plainBuf, size_t plainSize) { int mpw_base64_encode(char *b64Text, const uint8_t *plainBuf, size_t plainSize) {
int plainCursor = 0; size_t plainCursor = 0;
char *b64Cursor = b64Text; char *b64Cursor = b64Text;
for (; plainCursor < plainSize - 2; plainCursor += 3) { for (; plainCursor < plainSize - 2; plainCursor += 3) {
*b64Cursor++ = basis_64[((plainBuf[plainCursor] >> 2)) & 0x3F]; *b64Cursor++ = basis_64[((plainBuf[plainCursor] >> 2)) & 0x3F];

View File

@@ -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)

View File

@@ -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

View File

@@ -1,4 +0,0 @@
home=http://www.tarsnap.com/scrypt.html
git=https://github.com/Tarsnap/scrypt.git
pkg=https://www.tarsnap.com/scrypt/scrypt-1.2.1.tgz
pkg_sha256=4621f5e7da2f802e20850436219370092e9fcda93bd598f6d4236cce33f4c577

View File

@@ -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

View File

@@ -24,9 +24,14 @@
MPMasterKey mpw_masterKey(const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) { MPMasterKey mpw_masterKey(const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) {
if (fullName && !strlen( fullName ))
fullName = NULL;
if (masterPassword && !strlen( masterPassword ))
masterPassword = NULL;
trc( "-- mpw_masterKey (algorithm: %u)\n", algorithmVersion ); trc( "-- mpw_masterKey (algorithm: %u)\n", algorithmVersion );
trc( "fullName: %s\n", fullName ); trc( "fullName: %s\n", fullName );
trc( "masterPassword.id: %s\n", mpw_id_buf( masterPassword, strlen( masterPassword ) ) ); trc( "masterPassword.id: %s\n", masterPassword? mpw_id_buf( masterPassword, strlen( masterPassword ) ): NULL );
if (!fullName || !masterPassword) if (!fullName || !masterPassword)
return NULL; return NULL;
@@ -49,6 +54,11 @@ MPSiteKey mpw_siteKey(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext, const MPAlgorithmVersion algorithmVersion) { const MPKeyPurpose keyPurpose, const char *keyContext, const MPAlgorithmVersion algorithmVersion) {
if (siteName && !strlen( siteName ))
siteName = NULL;
if (keyContext && !strlen( keyContext ))
keyContext = NULL;
trc( "-- mpw_siteKey (algorithm: %u)\n", algorithmVersion ); trc( "-- mpw_siteKey (algorithm: %u)\n", algorithmVersion );
trc( "siteName: %s\n", siteName ); trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter ); trc( "siteCounter: %d\n", siteCounter );
@@ -78,7 +88,14 @@ const char *mpw_siteResult(
const MPResultType resultType, const char *resultParam, const MPResultType resultType, const char *resultParam,
const MPAlgorithmVersion algorithmVersion) { const MPAlgorithmVersion algorithmVersion) {
MPSiteKey siteKey = mpw_siteKey_v0( masterKey, siteName, siteCounter, keyPurpose, keyContext ); if (siteName && !strlen( siteName ))
siteName = NULL;
if (keyContext && !strlen( keyContext ))
keyContext = NULL;
if (resultParam && !strlen( resultParam ))
resultParam = NULL;
MPSiteKey siteKey = mpw_siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext, algorithmVersion );
if (!siteKey) if (!siteKey)
return NULL; return NULL;
@@ -102,7 +119,7 @@ const char *mpw_siteResult(
return NULL; return NULL;
} }
} }
else if (resultType & MPResultTypeClassState) { else if (resultType & MPResultTypeClassStateful) {
switch (algorithmVersion) { switch (algorithmVersion) {
case MPAlgorithmVersion0: case MPAlgorithmVersion0:
return mpw_sitePasswordFromCrypt_v0( masterKey, siteKey, resultType, resultParam ); return mpw_sitePasswordFromCrypt_v0( masterKey, siteKey, resultType, resultParam );
@@ -142,28 +159,35 @@ const char *mpw_siteResult(
const char *mpw_siteState( const char *mpw_siteState(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext, const MPKeyPurpose keyPurpose, const char *keyContext,
const MPResultType resultType, const char *state, const MPResultType resultType, const char *resultParam,
const MPAlgorithmVersion algorithmVersion) { const MPAlgorithmVersion algorithmVersion) {
MPSiteKey siteKey = mpw_siteKey_v0( masterKey, siteName, siteCounter, keyPurpose, keyContext ); if (siteName && !strlen( siteName ))
siteName = NULL;
if (keyContext && !strlen( keyContext ))
keyContext = NULL;
if (resultParam && !strlen( resultParam ))
resultParam = NULL;
MPSiteKey siteKey = mpw_siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext, algorithmVersion );
if (!siteKey) if (!siteKey)
return NULL; return NULL;
trc( "-- mpw_siteState (algorithm: %u)\n", algorithmVersion ); trc( "-- mpw_siteState (algorithm: %u)\n", algorithmVersion );
trc( "resultType: %d (%s)\n", resultType, mpw_nameForType( resultType ) ); trc( "resultType: %d (%s)\n", resultType, mpw_nameForType( resultType ) );
trc( "state: %s\n", state ); trc( "resultParam: %zu bytes = %s\n", sizeof( resultParam ), resultParam );
if (!masterKey || !state) if (!masterKey || !resultParam)
return NULL; return NULL;
switch (algorithmVersion) { switch (algorithmVersion) {
case MPAlgorithmVersion0: case MPAlgorithmVersion0:
return mpw_siteState_v0( masterKey, siteKey, resultType, state ); return mpw_siteState_v0( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion1: case MPAlgorithmVersion1:
return mpw_siteState_v1( masterKey, siteKey, resultType, state ); return mpw_siteState_v1( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion2: case MPAlgorithmVersion2:
return mpw_siteState_v2( masterKey, siteKey, resultType, state ); return mpw_siteState_v2( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion3: case MPAlgorithmVersion3:
return mpw_siteState_v3( masterKey, siteKey, resultType, state ); return mpw_siteState_v3( masterKey, siteKey, resultType, resultParam );
default: default:
err( "Unsupported version: %d\n", algorithmVersion ); err( "Unsupported version: %d\n", algorithmVersion );
return NULL; return NULL;

View File

@@ -22,7 +22,7 @@
#ifndef _MPW_ALGORITHM_H #ifndef _MPW_ALGORITHM_H
#define _MPW_ALGORITHM_H #define _MPW_ALGORITHM_H
typedef enum( unsigned int, MPAlgorithmVersion ) { typedef mpw_enum( unsigned int, MPAlgorithmVersion ) {
/** V0 did math with chars whose signedness was platform-dependent. */ /** V0 did math with chars whose signedness was platform-dependent. */
MPAlgorithmVersion0, MPAlgorithmVersion0,
/** V1 miscounted the byte-length of multi-byte site names. */ /** V1 miscounted the byte-length of multi-byte site names. */
@@ -48,7 +48,8 @@ MPSiteKey mpw_siteKey(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext, const MPAlgorithmVersion algorithmVersion); const MPKeyPurpose keyPurpose, const char *keyContext, const MPAlgorithmVersion algorithmVersion);
/** Encode a password for the site from the given site key. /** Generate a site result token from the given parameters.
* @param resultParam A parameter for the resultType. For stateful result types, the output of mpw_siteState.
* @return A newly allocated string or NULL if an error occurred. */ * @return A newly allocated string or NULL if an error occurred. */
const char *mpw_siteResult( const char *mpw_siteResult(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
@@ -56,12 +57,13 @@ const char *mpw_siteResult(
const MPResultType resultType, const char *resultParam, const MPResultType resultType, const char *resultParam,
const MPAlgorithmVersion algorithmVersion); const MPAlgorithmVersion algorithmVersion);
/** Perform symmetric encryption on a secret token's plainText. /** Encrypt a stateful site token for persistence.
* @return The newly allocated cipherText of the secret token encrypted by the masterKey. */ * @param resultParam A parameter for the resultType. For stateful result types, the desired mpw_siteResult.
* @return A newly allocated string or NULL if an error occurred. */
const char *mpw_siteState( const char *mpw_siteState(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext, const MPKeyPurpose keyPurpose, const char *keyContext,
const MPResultType resultType, const char *state, const MPResultType resultType, const char *resultParam,
const MPAlgorithmVersion algorithmVersion); const MPAlgorithmVersion algorithmVersion);
#endif // _MPW_ALGORITHM_H #endif // _MPW_ALGORITHM_H

View File

@@ -18,33 +18,33 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <arpa/inet.h> #include <time.h>
#include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"
#include "base64.h" #include "base64.h"
#define MP_N 32768LU #define MP_N 32768LU
#define MP_r 8U #define MP_r 8U
#define MP_p 2U #define MP_p 2U
#define MP_otp_window 5 * 60 /* s */
// Algorithm version helpers. // Algorithm version helpers.
static const char *mpw_templateForType_v0(MPResultType type, uint16_t seedByte) { static const char *mpw_templateForType_v0(MPResultType type, uint16_t templateIndex) {
size_t count = 0; size_t count = 0;
const char **templates = mpw_templatesForType( type, &count ); const char **templates = mpw_templatesForType( type, &count );
char const *template = templates && count? templates[seedByte % count]: NULL; char const *template = templates && count? templates[templateIndex % count]: NULL;
free( templates ); free( templates );
return template; return template;
} }
static const char mpw_characterFromClass_v0(char characterClass, uint16_t seedByte) { static const char mpw_characterFromClass_v0(char characterClass, uint16_t classIndex) {
const char *classCharacters = mpw_charactersInClass( characterClass ); const char *classCharacters = mpw_charactersInClass( characterClass );
if (!classCharacters) if (!classCharacters)
return '\0'; return '\0';
return classCharacters[seedByte % strlen( classCharacters )]; return classCharacters[classIndex % strlen( classCharacters )];
} }
// Algorithm version overrides. // Algorithm version overrides.
@@ -56,11 +56,11 @@ static MPMasterKey mpw_masterKey_v0(
// Calculate the master key salt. // Calculate the master key salt.
trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s\n", trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s\n",
keyScope, mpw_hex_l( htonl( mpw_utf8_strlen( fullName ) ) ), fullName ); keyScope, mpw_hex_l( (uint32_t)mpw_utf8_strlen( fullName ) ), fullName );
size_t masterKeySaltSize = 0; size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL; uint8_t *masterKeySalt = NULL;
mpw_push_string( &masterKeySalt, &masterKeySaltSize, keyScope ); mpw_push_string( &masterKeySalt, &masterKeySaltSize, keyScope );
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( mpw_utf8_strlen( fullName ) ) ); mpw_push_int( &masterKeySalt, &masterKeySaltSize, (uint32_t)mpw_utf8_strlen( fullName ) );
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName ); mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) { if (!masterKeySalt) {
err( "Could not allocate master key salt: %s\n", strerror( errno ) ); err( "Could not allocate master key salt: %s\n", strerror( errno ) );
@@ -71,9 +71,9 @@ static MPMasterKey mpw_masterKey_v0(
// Calculate the master key. // Calculate the master key.
trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )\n", MP_N, MP_r, MP_p ); trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )\n", MP_N, MP_r, MP_p );
MPMasterKey masterKey = mpw_kdf_scrypt( MPMasterKeySize, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p ); MPMasterKey masterKey = mpw_kdf_scrypt( MPMasterKeySize, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( masterKeySalt, masterKeySaltSize ); mpw_free( &masterKeySalt, masterKeySaltSize );
if (!masterKey) { if (!masterKey) {
err( "Could not allocate master key: %s\n", strerror( errno ) ); err( "Could not derive master key: %s\n", strerror( errno ) );
return NULL; return NULL;
} }
trc( " => masterKey.id: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) ); trc( " => masterKey.id: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) );
@@ -82,31 +82,32 @@ static MPMasterKey mpw_masterKey_v0(
} }
static MPSiteKey mpw_siteKey_v0( static MPSiteKey mpw_siteKey_v0(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext) { MPKeyPurpose keyPurpose, const char *keyContext) {
const char *keyScope = mpw_scopeForPurpose( keyPurpose ); const char *keyScope = mpw_scopeForPurpose( keyPurpose );
trc( "keyScope: %s\n", keyScope ); trc( "keyScope: %s\n", keyScope );
// TODO: Implement MPCounterValueTOTP // OTP counter value.
if (siteCounter == MPCounterValueTOTP)
siteCounter = ((uint32_t)time( NULL ) / MP_otp_window) * MP_otp_window;
// Calculate the site seed. // Calculate the site seed.
trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s\n", trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s\n",
keyScope, mpw_hex_l( htonl( mpw_utf8_strlen( siteName ) ) ), siteName, mpw_hex_l( htonl( siteCounter ) ), keyScope, mpw_hex_l( (uint32_t)mpw_utf8_strlen( siteName ) ), siteName, mpw_hex_l( siteCounter ),
keyContext? mpw_hex_l( htonl( mpw_utf8_strlen( keyContext ) ) ): NULL, keyContext ); keyContext? mpw_hex_l( (uint32_t)mpw_utf8_strlen( keyContext ) ): NULL, keyContext );
size_t siteSaltSize = 0; size_t siteSaltSize = 0;
uint8_t *siteSalt = NULL; uint8_t *siteSalt = NULL;
mpw_push_string( &siteSalt, &siteSaltSize, keyScope ); mpw_push_string( &siteSalt, &siteSaltSize, keyScope );
mpw_push_int( &siteSalt, &siteSaltSize, htonl( mpw_utf8_strlen( siteName ) ) ); mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)mpw_utf8_strlen( siteName ) );
mpw_push_string( &siteSalt, &siteSaltSize, siteName ); mpw_push_string( &siteSalt, &siteSaltSize, siteName );
mpw_push_int( &siteSalt, &siteSaltSize, htonl( siteCounter ) ); mpw_push_int( &siteSalt, &siteSaltSize, siteCounter );
if (keyContext) { if (keyContext) {
mpw_push_int( &siteSalt, &siteSaltSize, htonl( mpw_utf8_strlen( keyContext ) ) ); mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)mpw_utf8_strlen( keyContext ) );
mpw_push_string( &siteSalt, &siteSaltSize, keyContext ); mpw_push_string( &siteSalt, &siteSaltSize, keyContext );
} }
if (!siteSalt || !siteSaltSize) { if (!siteSalt) {
err( "Could not allocate site salt: %s\n", strerror( errno ) ); err( "Could not allocate site salt: %s\n", strerror( errno ) );
mpw_free( siteSalt, siteSaltSize );
return NULL; return NULL;
} }
trc( " => siteSalt.id: %s\n", mpw_id_buf( siteSalt, siteSaltSize ) ); trc( " => siteSalt.id: %s\n", mpw_id_buf( siteSalt, siteSaltSize ) );
@@ -114,7 +115,7 @@ static MPSiteKey mpw_siteKey_v0(
trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )\n", trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )\n",
mpw_id_buf( masterKey, MPMasterKeySize ) ); mpw_id_buf( masterKey, MPMasterKeySize ) );
MPSiteKey siteKey = mpw_hash_hmac_sha256( masterKey, MPMasterKeySize, siteSalt, siteSaltSize ); MPSiteKey siteKey = mpw_hash_hmac_sha256( masterKey, MPMasterKeySize, siteSalt, siteSaltSize );
mpw_free( siteSalt, siteSaltSize ); mpw_free( &siteSalt, siteSaltSize );
if (!siteKey) { if (!siteKey) {
err( "Could not derive site key: %s\n", strerror( errno ) ); err( "Could not derive site key: %s\n", strerror( errno ) );
return NULL; return NULL;
@@ -125,12 +126,15 @@ static MPSiteKey mpw_siteKey_v0(
} }
static const char *mpw_sitePasswordFromTemplate_v0( static const char *mpw_sitePasswordFromTemplate_v0(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam) { MPMasterKey __unused masterKey, MPSiteKey siteKey, MPResultType resultType, const char __unused *resultParam) {
const char *_siteKey = (const char *)siteKey;
// Determine the template. // Determine the template.
const char *_siteKey = (const char *)siteKey; uint16_t seedByte;
const char *template = mpw_templateForType_v0( resultType, htons( _siteKey[0] ) ); mpw_uint16( (uint16_t)_siteKey[0], (uint8_t *)&seedByte );
trc( "template: %u => %s\n", htons( _siteKey[0] ), template ); const char *template = mpw_templateForType_v0( resultType, seedByte );
trc( "template: %u => %s\n", seedByte, template );
if (!template) if (!template)
return NULL; return NULL;
if (strlen( template ) > MPSiteKeySize) { if (strlen( template ) > MPSiteKeySize) {
@@ -139,11 +143,12 @@ static const char *mpw_sitePasswordFromTemplate_v0(
} }
// Encode the password from the seed using the template. // Encode the password from the seed using the template.
char *sitePassword = calloc( strlen( template ) + 1, sizeof( char ) ); char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
for (size_t c = 0; c < strlen( template ); ++c) { for (size_t c = 0; c < strlen( template ); ++c) {
sitePassword[c] = mpw_characterFromClass_v0( template[c], htons( _siteKey[c + 1] ) ); mpw_uint16( (uint16_t)_siteKey[c + 1], (uint8_t *)&seedByte );
sitePassword[c] = mpw_characterFromClass_v0( template[c], seedByte );
trc( " - class: %c, index: %5u (0x%02hX) => character: %c\n", trc( " - class: %c, index: %5u (0x%02hX) => character: %c\n",
template[c], htons( _siteKey[c + 1] ), htons( _siteKey[c + 1] ), sitePassword[c] ); template[c], seedByte, seedByte, sitePassword[c] );
} }
trc( " => password: %s\n", sitePassword ); trc( " => password: %s\n", sitePassword );
@@ -151,7 +156,7 @@ static const char *mpw_sitePasswordFromTemplate_v0(
} }
static const char *mpw_sitePasswordFromCrypt_v0( static const char *mpw_sitePasswordFromCrypt_v0(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *cipherText) { MPMasterKey masterKey, MPSiteKey __unused siteKey, MPResultType __unused resultType, const char *cipherText) {
if (!cipherText) { if (!cipherText) {
err( "Missing encrypted state.\n" ); err( "Missing encrypted state.\n" );
@@ -160,28 +165,28 @@ static const char *mpw_sitePasswordFromCrypt_v0(
// Base64-decode // Base64-decode
uint8_t *cipherBuf = calloc( 1, mpw_base64_decode_max( cipherText ) ); uint8_t *cipherBuf = calloc( 1, mpw_base64_decode_max( cipherText ) );
size_t bufSize = (size_t)mpw_base64_decode( cipherBuf, cipherText ); size_t bufSize = (size_t)mpw_base64_decode( cipherBuf, cipherText ), cipherBufSize = bufSize;
if ((int)bufSize < 0) { if ((int)bufSize < 0) {
err( "Base64 decoding error." ); err( "Base64 decoding error." );
mpw_free( cipherBuf, mpw_base64_decode_max( cipherText ) ); mpw_free( &cipherBuf, mpw_base64_decode_max( cipherText ) );
return NULL; return NULL;
} }
trc( "b64 decoded: %zu bytes = %s\n", bufSize, mpw_hex( cipherBuf, bufSize ) ); trc( "b64 decoded: %zu bytes = %s\n", bufSize, mpw_hex( cipherBuf, bufSize ) );
// Decrypt // Decrypt
const uint8_t *plainBytes = mpw_aes_decrypt( masterKey, MPMasterKeySize, cipherBuf, bufSize ); const uint8_t *plainBytes = mpw_aes_decrypt( masterKey, MPMasterKeySize, cipherBuf, &bufSize );
mpw_free( &cipherBuf, cipherBufSize );
const char *plainText = strndup( (char *)plainBytes, bufSize ); const char *plainText = strndup( (char *)plainBytes, bufSize );
mpw_free( plainBytes, bufSize ); mpw_free( &plainBytes, bufSize );
if (!plainText) if (!plainText)
err( "AES decryption error: %s\n", strerror( errno ) ); err( "AES decryption error: %s\n", strerror( errno ) );
trc( "decrypted -> plainText: %s = %s\n", plainText, mpw_hex( plainText, sizeof( plainText ) ) ); trc( "decrypted -> plainText: %zu bytes = %s = %s\n", strlen( plainText ), plainText, mpw_hex( plainText, strlen( plainText ) ) );
mpw_free( cipherBuf, bufSize );
return plainText; return plainText;
} }
static const char *mpw_sitePasswordFromDerive_v0( static const char *mpw_sitePasswordFromDerive_v0(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam) { MPMasterKey __unused masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
switch (resultType) { switch (resultType) {
case MPResultTypeDeriveKey: { case MPResultTypeDeriveKey: {
@@ -190,6 +195,8 @@ static const char *mpw_sitePasswordFromDerive_v0(
return NULL; return NULL;
} }
int resultParamInt = atoi( resultParam ); int resultParamInt = atoi( resultParam );
if (!resultParamInt)
resultParamInt = 512;
if (resultParamInt < 128 || resultParamInt > 512 || resultParamInt % 8 != 0) { if (resultParamInt < 128 || resultParamInt > 512 || resultParamInt % 8 != 0) {
err( "Parameter is not a valid key size (should be 128 - 512): %s\n", resultParam ); err( "Parameter is not a valid key size (should be 128 - 512): %s\n", resultParam );
return NULL; return NULL;
@@ -206,15 +213,16 @@ static const char *mpw_sitePasswordFromDerive_v0(
// Base64-encode // Base64-encode
size_t b64Max = mpw_base64_encode_max( keySize ); size_t b64Max = mpw_base64_encode_max( keySize );
char *sitePassword = calloc( 1, b64Max + 1 ); char *b64Key = calloc( 1, b64Max + 1 );
if (mpw_base64_encode( sitePassword, resultKey, keySize ) < 0) { if (mpw_base64_encode( b64Key, resultKey, keySize ) < 0) {
err( "Base64 encoding error." ); err( "Base64 encoding error." );
mpw_free_string( sitePassword ); mpw_free_string( &b64Key );
sitePassword = NULL;
} }
trc( "b64 encoded -> key.id: %s\n", mpw_id_buf( sitePassword, strlen( sitePassword ) ) ); else
trc( "b64 encoded -> key: %s\n", b64Key );
mpw_free( &resultKey, keySize );
return sitePassword; return b64Key;
} }
default: default:
err( "Unsupported derived password type: %d\n", resultType ); err( "Unsupported derived password type: %d\n", resultType );
@@ -223,11 +231,11 @@ static const char *mpw_sitePasswordFromDerive_v0(
} }
static const char *mpw_siteState_v0( static const char *mpw_siteState_v0(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *plainText) { MPMasterKey masterKey, MPSiteKey __unused siteKey, MPResultType __unused resultType, const char *plainText) {
// Encrypt // Encrypt
size_t bufSize = strlen( plainText ); size_t bufSize = strlen( plainText );
const uint8_t *cipherBuf = mpw_aes_encrypt( masterKey, MPMasterKeySize, (const uint8_t *)plainText, bufSize ); const uint8_t *cipherBuf = mpw_aes_encrypt( masterKey, MPMasterKeySize, (const uint8_t *)plainText, &bufSize );
if (!cipherBuf) { if (!cipherBuf) {
err( "AES encryption error: %s\n", strerror( errno ) ); err( "AES encryption error: %s\n", strerror( errno ) );
return NULL; return NULL;
@@ -239,11 +247,11 @@ static const char *mpw_siteState_v0(
char *cipherText = calloc( 1, b64Max + 1 ); char *cipherText = calloc( 1, b64Max + 1 );
if (mpw_base64_encode( cipherText, cipherBuf, bufSize ) < 0) { if (mpw_base64_encode( cipherText, cipherBuf, bufSize ) < 0) {
err( "Base64 encoding error." ); err( "Base64 encoding error." );
mpw_free_string( cipherText ); mpw_free_string( &cipherText );
cipherText = NULL;
} }
trc( "b64 encoded -> cipherText: %s = %s\n", cipherText, mpw_hex( cipherText, sizeof( cipherText ) ) ); else
mpw_free( cipherBuf, bufSize ); trc( "b64 encoded -> cipherText: %s\n", cipherText );
mpw_free( &cipherBuf, bufSize );
return cipherText; return cipherText;
} }

View File

@@ -17,28 +17,26 @@
//============================================================================== //==============================================================================
#include <string.h> #include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"
#define MP_N 32768LU #define MP_N 32768LU
#define MP_r 8U #define MP_r 8U
#define MP_p 2U #define MP_p 2U
#define MP_otp_window 5 * 60 /* s */
// Inherited functions. // Inherited functions.
MPMasterKey mpw_masterKey_v0( MPMasterKey mpw_masterKey_v0(
const char *fullName, const char *masterPassword); const char *fullName, const char *masterPassword);
MPSiteKey mpw_siteKey_v0( MPSiteKey mpw_siteKey_v0(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext); MPKeyPurpose keyPurpose, const char *keyContext);
const char *mpw_sitePasswordFromCrypt_v0( const char *mpw_sitePasswordFromCrypt_v0(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *cipherText); MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText);
const char *mpw_sitePasswordFromDerive_v0( const char *mpw_sitePasswordFromDerive_v0(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam); MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
const char *mpw_siteState_v0( const char *mpw_siteState_v0(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *state); MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state);
// Algorithm version overrides. // Algorithm version overrides.
static MPMasterKey mpw_masterKey_v1( static MPMasterKey mpw_masterKey_v1(
@@ -48,18 +46,19 @@ static MPMasterKey mpw_masterKey_v1(
} }
static MPSiteKey mpw_siteKey_v1( static MPSiteKey mpw_siteKey_v1(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext) { MPKeyPurpose keyPurpose, const char *keyContext) {
return mpw_siteKey_v0( masterKey, siteName, siteCounter, keyPurpose, keyContext ); return mpw_siteKey_v0( masterKey, siteName, siteCounter, keyPurpose, keyContext );
} }
static const char *mpw_sitePasswordFromTemplate_v1( static const char *mpw_sitePasswordFromTemplate_v1(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam) { MPMasterKey __unused masterKey, MPSiteKey siteKey, MPResultType resultType, const char __unused *resultParam) {
// Determine the template. // Determine the template.
const char *template = mpw_templateForType( resultType, siteKey[0] ); uint8_t seedByte = siteKey[0];
trc( "template: %u => %s\n", siteKey[0], template ); const char *template = mpw_templateForType( resultType, seedByte );
trc( "template: %u => %s\n", seedByte, template );
if (!template) if (!template)
return NULL; return NULL;
if (strlen( template ) > MPSiteKeySize) { if (strlen( template ) > MPSiteKeySize) {
@@ -70,9 +69,10 @@ static const char *mpw_sitePasswordFromTemplate_v1(
// Encode the password from the seed using the template. // Encode the password from the seed using the template.
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) ); char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
for (size_t c = 0; c < strlen( template ); ++c) { for (size_t c = 0; c < strlen( template ); ++c) {
sitePassword[c] = mpw_characterFromClass( template[c], siteKey[c + 1] ); seedByte = siteKey[c + 1];
sitePassword[c] = mpw_characterFromClass( template[c], seedByte );
trc( " - class: %c, index: %3u (0x%02hhX) => character: %c\n", trc( " - class: %c, index: %3u (0x%02hhX) => character: %c\n",
template[c], siteKey[c + 1], siteKey[c + 1], sitePassword[c] ); template[c], seedByte, seedByte, sitePassword[c] );
} }
trc( " => password: %s\n", sitePassword ); trc( " => password: %s\n", sitePassword );
@@ -80,19 +80,19 @@ static const char *mpw_sitePasswordFromTemplate_v1(
} }
static const char *mpw_sitePasswordFromCrypt_v1( static const char *mpw_sitePasswordFromCrypt_v1(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *cipherText) { MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText) {
return mpw_sitePasswordFromCrypt_v0( masterKey, siteKey, resultType, cipherText ); return mpw_sitePasswordFromCrypt_v0( masterKey, siteKey, resultType, cipherText );
} }
static const char *mpw_sitePasswordFromDerive_v1( static const char *mpw_sitePasswordFromDerive_v1(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam) { MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
return mpw_sitePasswordFromDerive_v0( masterKey, siteKey, resultType, resultParam ); return mpw_sitePasswordFromDerive_v0( masterKey, siteKey, resultType, resultParam );
} }
static const char *mpw_siteState_v1( static const char *mpw_siteState_v1(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *state) { MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state) {
return mpw_siteState_v0( masterKey, siteKey, resultType, state ); return mpw_siteState_v0( masterKey, siteKey, resultType, state );
} }

View File

@@ -18,26 +18,26 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <arpa/inet.h> #include <time.h>
#include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"
#define MP_N 32768LU #define MP_N 32768LU
#define MP_r 8U #define MP_r 8U
#define MP_p 2U #define MP_p 2U
#define MP_otp_window 5 * 60 /* s */
// Inherited functions. // Inherited functions.
MPMasterKey mpw_masterKey_v1( MPMasterKey mpw_masterKey_v1(
const char *fullName, const char *masterPassword); const char *fullName, const char *masterPassword);
const char *mpw_sitePasswordFromTemplate_v1( const char *mpw_sitePasswordFromTemplate_v1(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam); MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
const char *mpw_sitePasswordFromCrypt_v1( const char *mpw_sitePasswordFromCrypt_v1(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *cipherText); MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText);
const char *mpw_sitePasswordFromDerive_v1( const char *mpw_sitePasswordFromDerive_v1(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam); MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
const char *mpw_siteState_v1( const char *mpw_siteState_v1(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *state); MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state);
// Algorithm version overrides. // Algorithm version overrides.
static MPMasterKey mpw_masterKey_v2( static MPMasterKey mpw_masterKey_v2(
@@ -47,31 +47,32 @@ static MPMasterKey mpw_masterKey_v2(
} }
static MPSiteKey mpw_siteKey_v2( static MPSiteKey mpw_siteKey_v2(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext) { MPKeyPurpose keyPurpose, const char *keyContext) {
const char *keyScope = mpw_scopeForPurpose( keyPurpose ); const char *keyScope = mpw_scopeForPurpose( keyPurpose );
trc( "keyScope: %s\n", keyScope ); trc( "keyScope: %s\n", keyScope );
// TODO: Implement MPCounterValueTOTP // OTP counter value.
if (siteCounter == MPCounterValueTOTP)
siteCounter = ((uint32_t)time( NULL ) / MP_otp_window) * MP_otp_window;
// Calculate the site seed. // Calculate the site seed.
trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s\n", trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s\n",
keyScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName, mpw_hex_l( htonl( siteCounter ) ), keyScope, mpw_hex_l( (uint32_t)strlen( siteName ) ), siteName, mpw_hex_l( siteCounter ),
keyContext? mpw_hex_l( htonl( strlen( keyContext ) ) ): NULL, keyContext ); keyContext? mpw_hex_l( (uint32_t)strlen( keyContext ) ): NULL, keyContext );
size_t siteSaltSize = 0; size_t siteSaltSize = 0;
uint8_t *siteSalt = NULL; uint8_t *siteSalt = NULL;
mpw_push_string( &siteSalt, &siteSaltSize, keyScope ); mpw_push_string( &siteSalt, &siteSaltSize, keyScope );
mpw_push_int( &siteSalt, &siteSaltSize, htonl( strlen( siteName ) ) ); mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)strlen( siteName ) );
mpw_push_string( &siteSalt, &siteSaltSize, siteName ); mpw_push_string( &siteSalt, &siteSaltSize, siteName );
mpw_push_int( &siteSalt, &siteSaltSize, htonl( siteCounter ) ); mpw_push_int( &siteSalt, &siteSaltSize, siteCounter );
if (keyContext) { if (keyContext) {
mpw_push_int( &siteSalt, &siteSaltSize, htonl( strlen( keyContext ) ) ); mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)strlen( keyContext ) );
mpw_push_string( &siteSalt, &siteSaltSize, keyContext ); mpw_push_string( &siteSalt, &siteSaltSize, keyContext );
} }
if (!siteSalt || !siteSaltSize) { if (!siteSalt) {
err( "Could not allocate site salt: %s\n", strerror( errno ) ); err( "Could not allocate site salt: %s\n", strerror( errno ) );
mpw_free( siteSalt, siteSaltSize );
return NULL; return NULL;
} }
trc( " => siteSalt.id: %s\n", mpw_id_buf( siteSalt, siteSaltSize ) ); trc( " => siteSalt.id: %s\n", mpw_id_buf( siteSalt, siteSaltSize ) );
@@ -79,9 +80,9 @@ static MPSiteKey mpw_siteKey_v2(
trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )\n", trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )\n",
mpw_id_buf( masterKey, MPMasterKeySize ) ); mpw_id_buf( masterKey, MPMasterKeySize ) );
MPSiteKey siteKey = mpw_hash_hmac_sha256( masterKey, MPMasterKeySize, siteSalt, siteSaltSize ); MPSiteKey siteKey = mpw_hash_hmac_sha256( masterKey, MPMasterKeySize, siteSalt, siteSaltSize );
mpw_free( siteSalt, siteSaltSize ); mpw_free( &siteSalt, siteSaltSize );
if (!siteKey) { if (!siteKey) {
err( "Could not allocate site key: %s\n", strerror( errno ) ); err( "Could not derive site key: %s\n", strerror( errno ) );
return NULL; return NULL;
} }
trc( " => siteKey.id: %s\n", mpw_id_buf( siteKey, MPSiteKeySize ) ); trc( " => siteKey.id: %s\n", mpw_id_buf( siteKey, MPSiteKeySize ) );
@@ -90,25 +91,25 @@ static MPSiteKey mpw_siteKey_v2(
} }
static const char *mpw_sitePasswordFromTemplate_v2( static const char *mpw_sitePasswordFromTemplate_v2(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam) { MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
return mpw_sitePasswordFromTemplate_v1( masterKey, siteKey, resultType, resultParam ); return mpw_sitePasswordFromTemplate_v1( masterKey, siteKey, resultType, resultParam );
} }
static const char *mpw_sitePasswordFromCrypt_v2( static const char *mpw_sitePasswordFromCrypt_v2(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *cipherText) { MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText) {
return mpw_sitePasswordFromCrypt_v1( masterKey, siteKey, resultType, cipherText ); return mpw_sitePasswordFromCrypt_v1( masterKey, siteKey, resultType, cipherText );
} }
static const char *mpw_sitePasswordFromDerive_v2( static const char *mpw_sitePasswordFromDerive_v2(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam) { MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
return mpw_sitePasswordFromDerive_v1( masterKey, siteKey, resultType, resultParam ); return mpw_sitePasswordFromDerive_v1( masterKey, siteKey, resultType, resultParam );
} }
static const char *mpw_siteState_v2( static const char *mpw_siteState_v2(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *state) { MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state) {
return mpw_siteState_v1( masterKey, siteKey, resultType, state ); return mpw_siteState_v1( masterKey, siteKey, resultType, state );
} }

View File

@@ -18,27 +18,26 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <arpa/inet.h>
#include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"
#define MP_N 32768LU #define MP_N 32768LU
#define MP_r 8U #define MP_r 8U
#define MP_p 2U #define MP_p 2U
#define MP_otp_window 5 * 60 /* s */
// Inherited functions. // Inherited functions.
MPSiteKey mpw_siteKey_v2( MPSiteKey mpw_siteKey_v2(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext); MPKeyPurpose keyPurpose, const char *keyContext);
const char *mpw_sitePasswordFromTemplate_v2( const char *mpw_sitePasswordFromTemplate_v2(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam); MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
const char *mpw_sitePasswordFromCrypt_v2( const char *mpw_sitePasswordFromCrypt_v2(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *cipherText); MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText);
const char *mpw_sitePasswordFromDerive_v2( const char *mpw_sitePasswordFromDerive_v2(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam); MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
const char *mpw_siteState_v2( const char *mpw_siteState_v2(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *state); MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state);
// Algorithm version overrides. // Algorithm version overrides.
static MPMasterKey mpw_masterKey_v3( static MPMasterKey mpw_masterKey_v3(
@@ -49,11 +48,11 @@ static MPMasterKey mpw_masterKey_v3(
// Calculate the master key salt. // Calculate the master key salt.
trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s\n", trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s\n",
keyScope, mpw_hex_l( htonl( strlen( fullName ) ) ), fullName ); keyScope, mpw_hex_l( (uint32_t)strlen( fullName ) ), fullName );
size_t masterKeySaltSize = 0; size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL; uint8_t *masterKeySalt = NULL;
mpw_push_string( &masterKeySalt, &masterKeySaltSize, keyScope ); mpw_push_string( &masterKeySalt, &masterKeySaltSize, keyScope );
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( strlen( fullName ) ) ); mpw_push_int( &masterKeySalt, &masterKeySaltSize, (uint32_t)strlen( fullName ) );
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName ); mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) { if (!masterKeySalt) {
err( "Could not allocate master key salt: %s\n", strerror( errno ) ); err( "Could not allocate master key salt: %s\n", strerror( errno ) );
@@ -64,9 +63,9 @@ static MPMasterKey mpw_masterKey_v3(
// Calculate the master key. // Calculate the master key.
trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )\n", MP_N, MP_r, MP_p ); trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )\n", MP_N, MP_r, MP_p );
MPMasterKey masterKey = mpw_kdf_scrypt( MPMasterKeySize, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p ); MPMasterKey masterKey = mpw_kdf_scrypt( MPMasterKeySize, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( masterKeySalt, masterKeySaltSize ); mpw_free( &masterKeySalt, masterKeySaltSize );
if (!masterKey) { if (!masterKey) {
err( "Could not allocate master key: %s\n", strerror( errno ) ); err( "Could not derive master key: %s\n", strerror( errno ) );
return NULL; return NULL;
} }
trc( " => masterKey.id: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) ); trc( " => masterKey.id: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) );
@@ -75,32 +74,32 @@ static MPMasterKey mpw_masterKey_v3(
} }
static MPSiteKey mpw_siteKey_v3( static MPSiteKey mpw_siteKey_v3(
const MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter, MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext) { MPKeyPurpose keyPurpose, const char *keyContext) {
return mpw_siteKey_v2( masterKey, siteName, siteCounter, keyPurpose, keyContext ); return mpw_siteKey_v2( masterKey, siteName, siteCounter, keyPurpose, keyContext );
} }
static const char *mpw_sitePasswordFromTemplate_v3( static const char *mpw_sitePasswordFromTemplate_v3(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam) { MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
return mpw_sitePasswordFromTemplate_v2( masterKey, siteKey, resultType, resultParam ); return mpw_sitePasswordFromTemplate_v2( masterKey, siteKey, resultType, resultParam );
} }
static const char *mpw_sitePasswordFromCrypt_v3( static const char *mpw_sitePasswordFromCrypt_v3(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *cipherText) { MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText) {
return mpw_sitePasswordFromCrypt_v2( masterKey, siteKey, resultType, cipherText ); return mpw_sitePasswordFromCrypt_v2( masterKey, siteKey, resultType, cipherText );
} }
static const char *mpw_sitePasswordFromDerive_v3( static const char *mpw_sitePasswordFromDerive_v3(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *resultParam) { MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
return mpw_sitePasswordFromDerive_v2( masterKey, siteKey, resultType, resultParam ); return mpw_sitePasswordFromDerive_v2( masterKey, siteKey, resultType, resultParam );
} }
static const char *mpw_siteState_v3( static const char *mpw_siteState_v3(
MPMasterKey masterKey, MPSiteKey siteKey, const MPResultType resultType, const char *state) { MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state) {
return mpw_siteState_v2( masterKey, siteKey, resultType, state ); return mpw_siteState_v2( masterKey, siteKey, resultType, state );
} }

View File

@@ -18,10 +18,10 @@
#include <string.h> #include <string.h>
#include "mpw-marshall-util.h" #include "mpw-marshal-util.h"
#include "mpw-util.h" #include "mpw-util.h"
char *mpw_get_token(char **in, char *eol, char *delim) { char *mpw_get_token(const char **in, const char *eol, char *delim) {
// Skip leading spaces. // Skip leading spaces.
for (; **in == ' '; ++*in); for (; **in == ' '; ++*in);
@@ -44,12 +44,31 @@ time_t mpw_mktime(
&tm.tm_hour, &tm.tm_min, &tm.tm_sec ) == 6) { &tm.tm_hour, &tm.tm_min, &tm.tm_sec ) == 6) {
tm.tm_year -= 1900; // tm_year 0 = rfc3339 year 1900 tm.tm_year -= 1900; // tm_year 0 = rfc3339 year 1900
tm.tm_mon -= 1; // tm_mon 0 = rfc3339 month 1 tm.tm_mon -= 1; // tm_mon 0 = rfc3339 month 1
/*
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_ARITHMETIC (code=EXC_I386_DIV, subcode=0x0)
frame #0: 0x00007fff9fe4d219 libsystem_notify.dylib`_nc_table_find_64 + 22
libsystem_notify.dylib`_nc_table_find_64:
-> 0x7fff9fe4d219 <+22>: divl 0x4(%rdi)
0x7fff9fe4d21c <+25>: movq 0x8(%rdi), %rax
0x7fff9fe4d220 <+29>: movq (%rax,%rdx,8), %rcx
0x7fff9fe4d224 <+33>: xorl %eax, %eax
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_ARITHMETIC (code=EXC_I386_DIV, subcode=0x0)
* frame #0: 0x00007fff9fe4d219 libsystem_notify.dylib`_nc_table_find_64 + 22
frame #1: 0x00007fff9fe4a21e libsystem_notify.dylib`registration_node_find + 53
frame #2: 0x00007fff9fe4b78d libsystem_notify.dylib`notify_check + 105
frame #3: 0x00007fff9fccc164 libsystem_c.dylib`notify_check_tz + 24
frame #4: 0x00007fff9fccbd97 libsystem_c.dylib`tzsetwall_basic + 45
frame #5: 0x00007fff9fccdcd0 libsystem_c.dylib`mktime + 46
frame #6: 0x0000000100009496 mpw`mpw_mktime(time="2017-04-16T03:16:35Z") at mpw-marshal-util.c:47
*/
return mktime( &tm ); return mktime( &tm );
} }
return false; return false;
} }
#if MPW_JSON
json_object *mpw_get_json_section( json_object *mpw_get_json_section(
json_object *obj, const char *section) { json_object *obj, const char *section) {
@@ -76,14 +95,14 @@ const char *mpw_get_json_string(
return json_object_get_string( json_value ); return json_object_get_string( json_value );
} }
int32_t mpw_get_json_int( int64_t mpw_get_json_int(
json_object *obj, const char *section, int32_t defaultValue) { json_object *obj, const char *section, int64_t defaultValue) {
json_object *json_value = mpw_get_json_section( obj, section ); json_object *json_value = mpw_get_json_section( obj, section );
if (!json_value) if (!json_value)
return defaultValue; return defaultValue;
return json_object_get_int( json_value ); return json_object_get_int64( json_value );
} }
bool mpw_get_json_boolean( bool mpw_get_json_boolean(
@@ -95,15 +114,15 @@ bool mpw_get_json_boolean(
return json_object_get_boolean( json_value ) == TRUE; return json_object_get_boolean( json_value ) == TRUE;
} }
#endif
bool mpw_update_masterKey(MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, MPAlgorithmVersion targetKeyAlgorithm, bool mpw_update_masterKey(MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, MPAlgorithmVersion targetKeyAlgorithm,
const char *fullName, const char *masterPassword) { const char *fullName, const char *masterPassword) {
if (*masterKeyAlgorithm != targetKeyAlgorithm) { if (*masterKeyAlgorithm != targetKeyAlgorithm) {
mpw_free( *masterKey, MPMasterKeySize ); mpw_free( masterKey, MPMasterKeySize );
*masterKeyAlgorithm = targetKeyAlgorithm; *masterKeyAlgorithm = targetKeyAlgorithm;
*masterKey = mpw_masterKey( *masterKey = mpw_masterKey( fullName, masterPassword, *masterKeyAlgorithm );
fullName, masterPassword, *masterKeyAlgorithm );
if (!*masterKey) { if (!*masterKey) {
err( "Couldn't derive master key for user %s, algorithm %d.\n", fullName, *masterKeyAlgorithm ); err( "Couldn't derive master key for user %s, algorithm %d.\n", fullName, *masterKeyAlgorithm );
return false; return false;

View File

@@ -16,11 +16,13 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>. // LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//============================================================================== //==============================================================================
#ifndef _MPW_MARSHALL_UTIL_H #ifndef _MPW_MARSHAL_UTIL_H
#define _MPW_MARSHALL_UTIL_H #define _MPW_MARSHAL_UTIL_H
#include <time.h> #include <time.h>
#include <json-c/json.h> #if MPW_JSON
#include "json-c/json.h"
#endif
#include "mpw-algorithm.h" #include "mpw-algorithm.h"
@@ -30,13 +32,14 @@
* The input string reference is advanced beyond the token delimitor if one is found. * The input string reference is advanced beyond the token delimitor if one is found.
* @return A new string containing the token or NULL if the delim wasn't found before eol. */ * @return A new string containing the token or NULL if the delim wasn't found before eol. */
char *mpw_get_token( char *mpw_get_token(
char **in, char *eol, char *delim); const char **in, const char *eol, char *delim);
/** Convert an RFC 3339 time string into epoch time. */ /** Convert an RFC 3339 time string into epoch time. */
time_t mpw_mktime( time_t mpw_mktime(
const char *time); const char *time);
/// JSON parsing. /// JSON parsing.
#if MPW_JSON
/** Search for a JSON child object in a JSON object tree. /** Search for a JSON child object in a JSON object tree.
* @param section A dot-delimited list of JSON object keys to walk toward the child object. * @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return A new JSON object or NULL if one of the section's object keys was not found in the source object's tree. */ * @return A new JSON object or NULL if one of the section's object keys was not found in the source object's tree. */
@@ -50,13 +53,14 @@ const char *mpw_get_json_string(
/** Search for an integer in a JSON object tree. /** Search for an integer in a JSON object tree.
* @param section A dot-delimited list of JSON object keys to walk toward the child object. * @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return The integer value or defaultValue if one of the section's object keys was not found in the source object's tree. */ * @return The integer value or defaultValue if one of the section's object keys was not found in the source object's tree. */
int32_t mpw_get_json_int( int64_t mpw_get_json_int(
json_object *obj, const char *section, int32_t defaultValue); json_object *obj, const char *section, int64_t defaultValue);
/** Search for a boolean in a JSON object tree. /** Search for a boolean in a JSON object tree.
* @param section A dot-delimited list of JSON object keys to walk toward the child object. * @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return The boolean value or defaultValue if one of the section's object keys was not found in the source object's tree. */ * @return The boolean value or defaultValue if one of the section's object keys was not found in the source object's tree. */
bool mpw_get_json_boolean( bool mpw_get_json_boolean(
json_object *obj, const char *section, bool defaultValue); json_object *obj, const char *section, bool defaultValue);
#endif
/// mpw. /// mpw.
@@ -66,4 +70,4 @@ bool mpw_update_masterKey(
MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, MPAlgorithmVersion targetKeyAlgorithm, MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, MPAlgorithmVersion targetKeyAlgorithm,
const char *fullName, const char *masterPassword); const char *fullName, const char *masterPassword);
#endif // _MPW_MARSHALL_UTIL_H #endif // _MPW_MARSHAL_UTIL_H

View File

@@ -21,11 +21,11 @@
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include "mpw-marshall.h" #include "mpw-marshal.h"
#include "mpw-util.h" #include "mpw-util.h"
#include "mpw-marshall-util.h" #include "mpw-marshal-util.h"
MPMarshalledUser *mpw_marshall_user( MPMarshalledUser *mpw_marshal_user(
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) { const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) {
MPMarshalledUser *user; MPMarshalledUser *user;
@@ -48,7 +48,7 @@ MPMarshalledUser *mpw_marshall_user(
return user; return user;
}; };
MPMarshalledSite *mpw_marshall_site( MPMarshalledSite *mpw_marshal_site(
MPMarshalledUser *user, const char *siteName, const MPResultType resultType, MPMarshalledUser *user, const char *siteName, const MPResultType resultType,
const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion) { const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion) {
@@ -63,8 +63,8 @@ MPMarshalledSite *mpw_marshall_site(
.counter = siteCounter, .counter = siteCounter,
.algorithm = algorithmVersion, .algorithm = algorithmVersion,
.loginName = NULL, .loginContent = NULL,
.loginGenerated = false, .loginType = MPResultTypeTemplateName,
.url = NULL, .url = NULL,
.uses = 0, .uses = 0,
@@ -79,56 +79,75 @@ MPMarshalledSite *mpw_marshall_site(
MPMarshalledQuestion *mpw_marshal_question( MPMarshalledQuestion *mpw_marshal_question(
MPMarshalledSite *site, const char *keyword) { MPMarshalledSite *site, const char *keyword) {
if (!keyword || !mpw_realloc( &site->questions, NULL, sizeof( MPMarshalledQuestion ) * ++site->questions_count )) if (!mpw_realloc( &site->questions, NULL, sizeof( MPMarshalledQuestion ) * ++site->questions_count ))
return NULL; return NULL;
if (!keyword)
keyword = "";
MPMarshalledQuestion *question = &site->questions[site->questions_count - 1]; MPMarshalledQuestion *question = &site->questions[site->questions_count - 1];
*question = (MPMarshalledQuestion){ *question = (MPMarshalledQuestion){
.keyword = strdup( keyword ), .keyword = strdup( keyword ),
.content = NULL,
.type = MPResultTypeTemplatePhrase,
}; };
return question; return question;
} }
bool mpw_marshal_free( bool mpw_marshal_info_free(
MPMarshalledUser *user) { MPMarshalInfo **info) {
if (!user) if (!info || !*info)
return true; return true;
bool success = true; bool success = true;
for (size_t s = 0; s < user->sites_count; ++s) { success &= mpw_free_strings( &(*info)->fullName, &(*info)->keyID, NULL );
MPMarshalledSite *site = &user->sites[s]; success &= mpw_free( info, sizeof( MPMarshalInfo ) );
success &= mpw_free_string( site->name );
return success;
}
bool mpw_marshal_free(
MPMarshalledUser **user) {
if (!user || !*user)
return true;
bool success = true;
success &= mpw_free_strings( &(*user)->fullName, &(*user)->masterPassword, NULL );
for (size_t s = 0; s < (*user)->sites_count; ++s) {
MPMarshalledSite *site = &(*user)->sites[s];
success &= mpw_free_strings( &site->name, &site->content, &site->loginContent, &site->url, NULL );
for (size_t q = 0; q < site->questions_count; ++q) { for (size_t q = 0; q < site->questions_count; ++q) {
MPMarshalledQuestion *question = &site->questions[q]; MPMarshalledQuestion *question = &site->questions[q];
success &= mpw_free_string( question->keyword ); success &= mpw_free_strings( &question->keyword, &question->content, NULL );
} }
success &= mpw_free( site->questions, sizeof( MPMarshalledQuestion ) * site->questions_count ); success &= mpw_free( &site->questions, sizeof( MPMarshalledQuestion ) * site->questions_count );
} }
success &= mpw_free( user->sites, sizeof( MPMarshalledSite ) * user->sites_count );
success &= mpw_free_string( user->fullName ); success &= mpw_free( &(*user)->sites, sizeof( MPMarshalledSite ) * (*user)->sites_count );
success &= mpw_free_string( user->masterPassword );
success &= mpw_free( user, sizeof( MPMarshalledUser ) ); success &= mpw_free( user, sizeof( MPMarshalledUser ) );
return success; return success;
} }
static bool mpw_marshall_write_flat( static bool mpw_marshal_write_flat(
char **out, const MPMarshalledUser *user, MPMarshallError *error) { char **out, const MPMarshalledUser *user, MPMarshalError *error) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Unexpected internal error." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!user->fullName || !strlen( user->fullName )) { if (!user->fullName || !strlen( user->fullName )) {
*error = (MPMarshallError){ MPMarshallErrorMissing, "Missing full name." }; *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
return false; return false;
} }
if (!user->masterPassword || !strlen( user->masterPassword )) { if (!user->masterPassword || !strlen( user->masterPassword )) {
*error = (MPMarshallError){ MPMarshallErrorMasterPassword, "Missing master password." }; *error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Missing master password." };
return false; return false;
} }
MPMasterKey masterKey = NULL; MPMasterKey masterKey = NULL;
MPAlgorithmVersion masterKeyAlgorithm = user->algorithm - 1; MPAlgorithmVersion masterKeyAlgorithm = user->algorithm - 1;
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, user->algorithm, user->fullName, user->masterPassword )) { if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, user->algorithm, user->fullName, user->masterPassword )) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return false; return false;
} }
@@ -163,50 +182,56 @@ static bool mpw_marshall_write_flat(
if (!site->name || !strlen( site->name )) if (!site->name || !strlen( site->name ))
continue; continue;
const char *content = NULL; const char *content = NULL, *loginContent = NULL;
if (!user->redacted) { if (!user->redacted) {
// Clear Text // Clear Text
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, user->fullName, user->masterPassword )) { if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, user->fullName, user->masterPassword )) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return false; return false;
} }
if (site->type & MPResultTypeClassTemplate) content = mpw_siteResult( masterKey, site->name, site->counter,
content = mpw_siteResult( masterKey, site->name, site->counter, MPKeyPurposeAuthentication, NULL, site->type, site->content, site->algorithm );
MPKeyPurposeAuthentication, NULL, site->type, site->content, site->algorithm ); loginContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeIdentification, NULL, site->loginType, site->loginContent, site->algorithm );
} }
else if (site->type & MPSiteFeatureExportContent && site->content && strlen( site->content )) else {
// Redacted // Redacted
content = strdup( site->content ); if (site->type & MPSiteFeatureExportContent && site->content && strlen( site->content ))
content = strdup( site->content );
if (site->loginType & MPSiteFeatureExportContent && site->loginContent && strlen( site->loginContent ))
loginContent = strdup( site->loginContent );
}
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) )) if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) ))
mpw_string_pushf( out, "%s %8ld %lu:%lu:%lu %25s\t%25s\t%s\n", mpw_string_pushf( out, "%s %8ld %lu:%lu:%lu %25s\t%25s\t%s\n",
dateString, (long)site->uses, (long)site->type, (long)site->algorithm, (long)site->counter, dateString, (long)site->uses, (long)site->type, (long)site->algorithm, (long)site->counter,
site->loginName?: "", site->name, content?: "" ); loginContent?: "", site->name, content?: "" );
mpw_free_string( content ); mpw_free_strings( &content, &loginContent, NULL );
} }
mpw_free( masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
*error = (MPMarshallError){ MPMarshallSuccess }; *error = (MPMarshalError){ .type = MPMarshalSuccess };
return true; return true;
} }
static bool mpw_marshall_write_json( #if MPW_JSON
char **out, const MPMarshalledUser *user, MPMarshallError *error) { static bool mpw_marshal_write_json(
char **out, const MPMarshalledUser *user, MPMarshalError *error) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Unexpected internal error." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!user->fullName || !strlen( user->fullName )) { if (!user->fullName || !strlen( user->fullName )) {
*error = (MPMarshallError){ MPMarshallErrorMissing, "Missing full name." }; *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
return false; return false;
} }
if (!user->masterPassword || !strlen( user->masterPassword )) { if (!user->masterPassword || !strlen( user->masterPassword )) {
*error = (MPMarshallError){ MPMarshallErrorMasterPassword, "Missing master password." }; *error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Missing master password." };
return false; return false;
} }
MPMasterKey masterKey = NULL; MPMasterKey masterKey = NULL;
MPAlgorithmVersion masterKeyAlgorithm = user->algorithm - 1; MPAlgorithmVersion masterKeyAlgorithm = user->algorithm - 1;
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, user->algorithm, user->fullName, user->masterPassword )) { if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, user->algorithm, user->fullName, user->masterPassword )) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return false; return false;
} }
@@ -225,15 +250,15 @@ static bool mpw_marshall_write_json(
// Section: "user" // Section: "user"
json_object *json_user = json_object_new_object(); json_object *json_user = json_object_new_object();
json_object_object_add( json_file, "user", json_user ); json_object_object_add( json_file, "user", json_user );
json_object_object_add( json_user, "avatar", json_object_new_int( (int)user->avatar ) ); json_object_object_add( json_user, "avatar", json_object_new_int( (int32_t)user->avatar ) );
json_object_object_add( json_user, "full_name", json_object_new_string( user->fullName ) ); json_object_object_add( json_user, "full_name", json_object_new_string( user->fullName ) );
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &user->lastUsed ) )) if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &user->lastUsed ) ))
json_object_object_add( json_user, "last_used", json_object_new_string( dateString ) ); json_object_object_add( json_user, "last_used", json_object_new_string( dateString ) );
json_object_object_add( json_user, "key_id", json_object_new_string( mpw_id_buf( masterKey, MPMasterKeySize ) ) ); json_object_object_add( json_user, "key_id", json_object_new_string( mpw_id_buf( masterKey, MPMasterKeySize ) ) );
json_object_object_add( json_user, "algorithm", json_object_new_int( (int)user->algorithm ) ); json_object_object_add( json_user, "algorithm", json_object_new_int( (int32_t)user->algorithm ) );
json_object_object_add( json_user, "default_type", json_object_new_int( (int)user->defaultType ) ); json_object_object_add( json_user, "default_type", json_object_new_int( (int32_t)user->defaultType ) );
// Section "sites" // Section "sites"
json_object *json_sites = json_object_new_object(); json_object *json_sites = json_object_new_object();
@@ -243,34 +268,39 @@ static bool mpw_marshall_write_json(
if (!site->name || !strlen( site->name )) if (!site->name || !strlen( site->name ))
continue; continue;
const char *content = NULL; const char *content = NULL, *loginContent = NULL;
if (!user->redacted) { if (!user->redacted) {
// Clear Text // Clear Text
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, user->fullName, user->masterPassword )) { if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, user->fullName, user->masterPassword )) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return false; return false;
} }
if (site->type & MPResultTypeClassTemplate) content = mpw_siteResult( masterKey, site->name, site->counter,
content = mpw_siteResult( masterKey, site->name, site->counter, MPKeyPurposeAuthentication, NULL, site->type, site->content, site->algorithm );
MPKeyPurposeAuthentication, NULL, site->type, site->content, site->algorithm ); loginContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeIdentification, NULL, site->loginType, site->loginContent, site->algorithm );
} }
else if (site->type & MPSiteFeatureExportContent && site->content && strlen( site->content )) else {
// Redacted // Redacted
content = strdup( site->content ); if (site->type & MPSiteFeatureExportContent && site->content && strlen( site->content ))
content = strdup( site->content );
if (site->loginType & MPSiteFeatureExportContent && site->loginContent && strlen( site->loginContent ))
loginContent = strdup( site->loginContent );
}
json_object *json_site = json_object_new_object(); json_object *json_site = json_object_new_object();
json_object_object_add( json_sites, site->name, json_site ); json_object_object_add( json_sites, site->name, json_site );
json_object_object_add( json_site, "type", json_object_new_int( (int)site->type ) ); json_object_object_add( json_site, "type", json_object_new_int( (int32_t)site->type ) );
json_object_object_add( json_site, "counter", json_object_new_int( (int)site->counter ) ); json_object_object_add( json_site, "counter", json_object_new_int( (int32_t)site->counter ) );
json_object_object_add( json_site, "algorithm", json_object_new_int( (int)site->algorithm ) ); json_object_object_add( json_site, "algorithm", json_object_new_int( (int32_t)site->algorithm ) );
if (content) if (content)
json_object_object_add( json_site, "password", json_object_new_string( content ) ); json_object_object_add( json_site, "password", json_object_new_string( content ) );
if (site->loginName) if (loginContent)
json_object_object_add( json_site, "login_name", json_object_new_string( site->loginName ) ); json_object_object_add( json_site, "login_name", json_object_new_string( loginContent ) );
json_object_object_add( json_site, "login_generated", json_object_new_boolean( site->loginGenerated ) ); json_object_object_add( json_site, "login_type", json_object_new_int( (int32_t)site->loginType ) );
json_object_object_add( json_site, "uses", json_object_new_int( (int)site->uses ) ); json_object_object_add( json_site, "uses", json_object_new_int( (int32_t)site->uses ) );
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) )) if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) ))
json_object_object_add( json_site, "last_used", json_object_new_string( dateString ) ); json_object_object_add( json_site, "last_used", json_object_new_string( dateString ) );
@@ -283,13 +313,18 @@ static bool mpw_marshall_write_json(
json_object *json_site_question = json_object_new_object(); json_object *json_site_question = json_object_new_object();
json_object_object_add( json_site_questions, question->keyword, json_site_question ); json_object_object_add( json_site_questions, question->keyword, json_site_question );
json_object_object_add( json_site_question, "type", json_object_new_int( (int32_t)question->type ) );
if (!user->redacted) { if (!user->redacted) {
// Clear Text // Clear Text
const char *answer = mpw_siteResult( masterKey, site->name, MPCounterValueInitial, const char *answerContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeRecovery, question->keyword, MPResultTypeTemplatePhrase, NULL, site->algorithm ); MPKeyPurposeRecovery, question->keyword, question->type, question->content, site->algorithm );
if (answer) json_object_object_add( json_site_question, "answer", json_object_new_string( answerContent ) );
json_object_object_add( json_site_question, "answer", json_object_new_string( answer ) ); }
else {
// Redacted
if (site->type & MPSiteFeatureExportContent && question->content && strlen( question->content ))
json_object_object_add( json_site_question, "answer", json_object_new_string( question->content ) );
} }
} }
@@ -298,35 +333,93 @@ static bool mpw_marshall_write_json(
if (site->url) if (site->url)
json_object_object_add( json_site_mpw, "url", json_object_new_string( site->url ) ); json_object_object_add( json_site_mpw, "url", json_object_new_string( site->url ) );
mpw_free_string( content ); mpw_free_strings( &content, &loginContent, NULL );
} }
mpw_string_pushf( out, "%s\n", json_object_to_json_string_ext( json_file, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED ) ); mpw_string_pushf( out, "%s\n", json_object_to_json_string_ext( json_file, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED ) );
mpw_free( masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
json_object_put( json_file ); json_object_put( json_file );
*error = (MPMarshallError){ MPMarshallSuccess }; *error = (MPMarshalError){ .type = MPMarshalSuccess };
return true; return true;
} }
#endif
bool mpw_marshall_write( bool mpw_marshal_write(
char **out, const MPMarshallFormat outFormat, const MPMarshalledUser *user, MPMarshallError *error) { char **out, const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error) {
switch (outFormat) { switch (outFormat) {
case MPMarshallFormatFlat: case MPMarshalFormatNone:
return mpw_marshall_write_flat( out, user, error ); *error = (MPMarshalError){ .type = MPMarshalSuccess };
case MPMarshallFormatJSON: return false;
return mpw_marshall_write_json( out, user, error ); case MPMarshalFormatFlat:
return mpw_marshal_write_flat( out, user, error );
#if MPW_JSON
case MPMarshalFormatJSON:
return mpw_marshal_write_json( out, user, error );
#endif
default: default:
*error = (MPMarshallError){ MPMarshallErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) }; *error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) };
return false; return false;
} }
} }
static MPMarshalledUser *mpw_marshall_read_flat( static void mpw_marshal_read_flat_info(
char *in, const char *masterPassword, MPMarshallError *error) { const char *in, MPMarshalInfo *info) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Unexpected internal error." }; info->algorithm = MPAlgorithmVersionCurrent;
// Parse import data.
bool headerStarted = false;
for (const char *endOfLine, *positionInLine = in; (endOfLine = strstr( positionInLine, "\n" )); positionInLine = endOfLine + 1) {
// Comment or header
if (*positionInLine == '#') {
++positionInLine;
if (!headerStarted) {
if (*positionInLine == '#')
// ## starts header
headerStarted = true;
// Comment before header
continue;
}
if (*positionInLine == '#')
// ## ends header
break;
// Header
char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" );
char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
if (!headerName || !headerValue)
continue;
if (strcmp( headerName, "Algorithm" ) == 0)
info->algorithm = (MPAlgorithmVersion)atoi( headerValue );
if (strcmp( headerName, "Full Name" ) == 0 || strcmp( headerName, "User Name" ) == 0)
info->fullName = strdup( headerValue );
if (strcmp( headerName, "Key ID" ) == 0)
info->keyID = strdup( headerValue );
if (strcmp( headerName, "Passwords" ) == 0)
info->redacted = strcmp( headerValue, "VISIBLE" ) != 0;
if (strcmp( headerName, "Date" ) == 0)
info->date = mpw_mktime( headerValue );
mpw_free_strings( &headerName, &headerValue, NULL );
continue;
}
}
}
static MPMarshalledUser *mpw_marshal_read_flat(
const char *in, const char *masterPassword, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!in || !strlen( in )) {
error->type = MPMarshalErrorStructure;
error->description = mpw_str( "No input data." );
return NULL;
}
// Parse import data. // Parse import data.
MPMasterKey masterKey = NULL; MPMasterKey masterKey = NULL;
@@ -336,7 +429,7 @@ static MPMarshalledUser *mpw_marshall_read_flat(
MPAlgorithmVersion algorithm = MPAlgorithmVersionCurrent, masterKeyAlgorithm = (MPAlgorithmVersion)-1; MPAlgorithmVersion algorithm = MPAlgorithmVersionCurrent, masterKeyAlgorithm = (MPAlgorithmVersion)-1;
MPResultType defaultType = MPResultTypeDefault; MPResultType defaultType = MPResultTypeDefault;
bool headerStarted = false, headerEnded = false, importRedacted = false; bool headerStarted = false, headerEnded = false, importRedacted = false;
for (char *endOfLine, *positionInLine = in; (endOfLine = strstr( positionInLine, "\n" )); positionInLine = endOfLine + 1) { for (const char *endOfLine, *positionInLine = in; (endOfLine = strstr( positionInLine, "\n" )); positionInLine = endOfLine + 1) {
// Comment or header // Comment or header
if (*positionInLine == '#') { if (*positionInLine == '#') {
@@ -362,7 +455,7 @@ static MPMarshalledUser *mpw_marshall_read_flat(
char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" ); char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" );
char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" ); char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
if (!headerName || !headerValue) { if (!headerName || !headerValue) {
error->type = MPMarshallErrorStructure; error->type = MPMarshalErrorStructure;
error->description = mpw_str( "Invalid header: %s", strndup( positionInLine, (size_t)(endOfLine - positionInLine) ) ); error->description = mpw_str( "Invalid header: %s", strndup( positionInLine, (size_t)(endOfLine - positionInLine) ) );
return NULL; return NULL;
} }
@@ -378,7 +471,7 @@ static MPMarshalledUser *mpw_marshall_read_flat(
if (strcmp( headerName, "Algorithm" ) == 0) { if (strcmp( headerName, "Algorithm" ) == 0) {
int value = atoi( headerValue ); int value = atoi( headerValue );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) { if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid user algorithm version: %s", headerValue ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user algorithm version: %s", headerValue ) };
return NULL; return NULL;
} }
algorithm = (MPAlgorithmVersion)value; algorithm = (MPAlgorithmVersion)value;
@@ -386,7 +479,7 @@ static MPMarshalledUser *mpw_marshall_read_flat(
if (strcmp( headerName, "Default Type" ) == 0) { if (strcmp( headerName, "Default Type" ) == 0) {
int value = atoi( headerValue ); int value = atoi( headerValue );
if (!mpw_nameForType( (MPResultType)value )) { if (!mpw_nameForType( (MPResultType)value )) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid user default type: %s", headerValue ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %s", headerValue ) };
return NULL; return NULL;
} }
defaultType = (MPResultType)value; defaultType = (MPResultType)value;
@@ -394,14 +487,13 @@ static MPMarshalledUser *mpw_marshall_read_flat(
if (strcmp( headerName, "Passwords" ) == 0) if (strcmp( headerName, "Passwords" ) == 0)
importRedacted = strcmp( headerValue, "VISIBLE" ) != 0; importRedacted = strcmp( headerValue, "VISIBLE" ) != 0;
mpw_free_string( headerName ); mpw_free_strings( &headerName, &headerValue, NULL );
mpw_free_string( headerValue );
continue; continue;
} }
if (!headerEnded) if (!headerEnded)
continue; continue;
if (!fullName) { if (!fullName) {
*error = (MPMarshallError){ MPMarshallErrorMissing, "Missing header: Full Name" }; *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing header: Full Name" };
return NULL; return NULL;
} }
if (positionInLine >= endOfLine) if (positionInLine >= endOfLine)
@@ -409,15 +501,15 @@ static MPMarshalledUser *mpw_marshall_read_flat(
if (!user) { if (!user) {
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, algorithm, fullName, masterPassword )) { if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, algorithm, fullName, masterPassword )) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return NULL; return NULL;
} }
if (keyID && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) { if (keyID && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
*error = (MPMarshallError){ MPMarshallErrorMasterPassword, "Master password doesn't match key ID." }; *error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Master password doesn't match key ID." };
return NULL; return NULL;
} }
if (!(user = mpw_marshall_user( fullName, masterPassword, algorithm ))) { if (!(user = mpw_marshal_user( fullName, masterPassword, algorithm ))) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't allocate a new user." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." };
return NULL; return NULL;
} }
@@ -437,7 +529,7 @@ static MPMarshalledUser *mpw_marshall_read_flat(
if (typeAndVersion) { if (typeAndVersion) {
str_type = strdup( strtok( typeAndVersion, ":" ) ); str_type = strdup( strtok( typeAndVersion, ":" ) );
str_algorithm = strdup( strtok( NULL, "" ) ); str_algorithm = strdup( strtok( NULL, "" ) );
mpw_free_string( typeAndVersion ); mpw_free_string( &typeAndVersion );
} }
str_counter = strdup( "1" ); str_counter = strdup( "1" );
siteLoginName = NULL; siteLoginName = NULL;
@@ -453,7 +545,7 @@ static MPMarshalledUser *mpw_marshall_read_flat(
str_type = strdup( strtok( typeAndVersionAndCounter, ":" ) ); str_type = strdup( strtok( typeAndVersionAndCounter, ":" ) );
str_algorithm = strdup( strtok( NULL, ":" ) ); str_algorithm = strdup( strtok( NULL, ":" ) );
str_counter = strdup( strtok( NULL, "" ) ); str_counter = strdup( strtok( NULL, "" ) );
mpw_free_string( typeAndVersionAndCounter ); mpw_free_string( &typeAndVersionAndCounter );
} }
siteLoginName = mpw_get_token( &positionInLine, endOfLine, "\t\n" ); siteLoginName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
siteName = mpw_get_token( &positionInLine, endOfLine, "\t\n" ); siteName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
@@ -461,7 +553,7 @@ static MPMarshalledUser *mpw_marshall_read_flat(
break; break;
} }
default: { default: {
*error = (MPMarshallError){ MPMarshallErrorFormat, mpw_str( "Unexpected import format: %u", format ) }; *error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unexpected import format: %u", format ) };
return NULL; return NULL;
} }
} }
@@ -469,88 +561,116 @@ static MPMarshalledUser *mpw_marshall_read_flat(
if (siteName && str_type && str_counter && str_algorithm && str_uses && str_lastUsed) { if (siteName && str_type && str_counter && str_algorithm && str_uses && str_lastUsed) {
MPResultType siteType = (MPResultType)atoi( str_type ); MPResultType siteType = (MPResultType)atoi( str_type );
if (!mpw_nameForType( siteType )) { if (!mpw_nameForType( siteType )) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site type: %s: %s", siteName, str_type ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %s", siteName, str_type ) };
return NULL; return NULL;
} }
long long int value = atoll( str_counter ); long long int value = atoll( str_counter );
if (value < MPCounterValueFirst || value > MPCounterValueLast) { if (value < MPCounterValueFirst || value > MPCounterValueLast) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site counter: %s: %s", siteName, str_counter ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %s", siteName, str_counter ) };
return NULL; return NULL;
} }
MPCounterValue siteCounter = (MPCounterValue)value; MPCounterValue siteCounter = (MPCounterValue)value;
value = atoll( str_algorithm ); value = atoll( str_algorithm );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) { if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site algorithm: %s: %s", siteName, str_algorithm ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm: %s: %s", siteName, str_algorithm ) };
return NULL; return NULL;
} }
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value; MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
time_t siteLastUsed = mpw_mktime( str_lastUsed ); time_t siteLastUsed = mpw_mktime( str_lastUsed );
if (!siteLastUsed) { if (!siteLastUsed) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) };
return NULL; return NULL;
} }
MPMarshalledSite *site = mpw_marshall_site( MPMarshalledSite *site = mpw_marshal_site(
user, siteName, siteType, siteCounter, siteAlgorithm ); user, siteName, siteType, siteCounter, siteAlgorithm );
if (!site) { if (!site) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't allocate a new site." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new site." };
return NULL; return NULL;
} }
site->loginName = siteLoginName? strdup( siteLoginName ): NULL;
site->uses = (unsigned int)atoi( str_uses ); site->uses = (unsigned int)atoi( str_uses );
site->lastUsed = siteLastUsed; site->lastUsed = siteLastUsed;
if (siteContent && strlen( siteContent )) { if (!user->redacted) {
if (!user->redacted) { // Clear Text
// Clear Text if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, fullName, masterPassword )) {
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, fullName, masterPassword )) { *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." }; return NULL;
return NULL; }
}
if (siteContent && strlen( siteContent ))
site->content = mpw_siteState( masterKey, site->name, site->counter, site->content = mpw_siteState( masterKey, site->name, site->counter,
MPKeyPurposeAuthentication, NULL, site->type, siteContent, site->algorithm ); MPKeyPurposeAuthentication, NULL, site->type, siteContent, site->algorithm );
} if (siteLoginName && strlen( siteLoginName ))
else site->loginContent = mpw_siteState( masterKey, site->name, MPCounterValueInitial,
// Redacted MPKeyPurposeIdentification, NULL, site->loginType, siteLoginName, site->algorithm );
}
else {
// Redacted
if (siteContent && strlen( siteContent ))
site->content = strdup( siteContent ); site->content = strdup( siteContent );
if (siteLoginName && strlen( siteLoginName ))
site->loginContent = strdup( siteLoginName );
} }
} }
else { else {
error->type = MPMarshallErrorMissing; error->type = MPMarshalErrorMissing;
error->description = mpw_str( error->description = mpw_str(
"Missing one of: lastUsed=%s, uses=%s, type=%s, version=%s, counter=%s, loginName=%s, siteName=%s", "Missing one of: lastUsed=%s, uses=%s, type=%s, version=%s, counter=%s, loginName=%s, siteName=%s",
str_lastUsed, str_uses, str_type, str_algorithm, str_counter, siteLoginName, siteName ); str_lastUsed, str_uses, str_type, str_algorithm, str_counter, siteLoginName, siteName );
return NULL; return NULL;
} }
mpw_free_string( str_lastUsed ); mpw_free_strings( &str_lastUsed, &str_uses, &str_type, &str_algorithm, &str_counter, NULL );
mpw_free_string( str_uses ); mpw_free_strings( &siteLoginName, &siteName, &siteContent, NULL );
mpw_free_string( str_type );
mpw_free_string( str_algorithm );
mpw_free_string( str_counter );
mpw_free_string( siteLoginName );
mpw_free_string( siteName );
mpw_free_string( siteContent );
} }
mpw_free_string( fullName ); mpw_free_strings( &fullName, &keyID, NULL );
mpw_free_string( keyID ); mpw_free( &masterKey, MPMasterKeySize );
mpw_free( masterKey, MPMasterKeySize );
*error = (MPMarshallError){ MPMarshallSuccess }; *error = (MPMarshalError){ .type = MPMarshalSuccess };
return user; return user;
} }
static MPMarshalledUser *mpw_marshall_read_json( #if MPW_JSON
char *in, const char *masterPassword, MPMarshallError *error) { static void mpw_marshal_read_json_info(
const char *in, MPMarshalInfo *info) {
*error = (MPMarshallError){ MPMarshallErrorInternal }; // Parse JSON.
enum json_tokener_error json_error = json_tokener_success;
json_object *json_file = json_tokener_parse_verbose( in, &json_error );
if (!json_file || json_error != json_tokener_success)
return;
// Section: "export"
int64_t fileFormat = mpw_get_json_int( json_file, "export.format", 0 );
if (fileFormat < 1)
return;
info->redacted = mpw_get_json_boolean( json_file, "export.redacted", true );
info->date = mpw_mktime( mpw_get_json_string( json_file, "export.date", NULL ) );
// Section: "user"
info->algorithm = (MPAlgorithmVersion)mpw_get_json_int( json_file, "user.algorithm", MPAlgorithmVersionCurrent );
info->fullName = strdup( mpw_get_json_string( json_file, "user.full_name", NULL ) );
info->keyID = strdup( mpw_get_json_string( json_file, "user.key_id", NULL ) );
json_object_put( json_file );
}
static MPMarshalledUser *mpw_marshal_read_json(
const char *in, const char *masterPassword, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!in || !strlen( in )) {
error->type = MPMarshalErrorStructure;
error->description = mpw_str( "No input data." );
return NULL;
}
// Parse JSON. // Parse JSON.
enum json_tokener_error json_error = json_tokener_success; enum json_tokener_error json_error = json_tokener_success;
json_object *json_file = json_tokener_parse_verbose( in, &json_error ); json_object *json_file = json_tokener_parse_verbose( in, &json_error );
if (!json_file || json_error != json_tokener_success) { if (!json_file || json_error != json_tokener_success) {
*error = (MPMarshallError){ MPMarshallErrorStructure, mpw_str( "JSON error: %s", json_tokener_error_desc( json_error ) ) }; *error = (MPMarshalError){ MPMarshalErrorStructure, mpw_str( "JSON error: %s", json_tokener_error_desc( json_error ) ) };
return NULL; return NULL;
} }
@@ -560,9 +680,9 @@ static MPMarshalledUser *mpw_marshall_read_json(
MPMarshalledUser *user = NULL; MPMarshalledUser *user = NULL;
// Section: "export" // Section: "export"
unsigned int fileFormat = (unsigned int)mpw_get_json_int( json_file, "export.format", 0 ); int64_t fileFormat = mpw_get_json_int( json_file, "export.format", 0 );
if (fileFormat < 1) { if (fileFormat < 1) {
*error = (MPMarshallError){ MPMarshallErrorFormat, mpw_str( "Unsupported format: %u", fileFormat ) }; *error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported format: %u", fileFormat ) };
return NULL; return NULL;
} }
bool fileRedacted = mpw_get_json_boolean( json_file, "export.redacted", true ); bool fileRedacted = mpw_get_json_boolean( json_file, "export.redacted", true );
@@ -572,36 +692,36 @@ static MPMarshalledUser *mpw_marshall_read_json(
const char *fullName = mpw_get_json_string( json_file, "user.full_name", NULL ); const char *fullName = mpw_get_json_string( json_file, "user.full_name", NULL );
const char *str_lastUsed = mpw_get_json_string( json_file, "user.last_used", NULL ); const char *str_lastUsed = mpw_get_json_string( json_file, "user.last_used", NULL );
const char *keyID = mpw_get_json_string( json_file, "user.key_id", NULL ); const char *keyID = mpw_get_json_string( json_file, "user.key_id", NULL );
int32_t value = mpw_get_json_int( json_file, "user.algorithm", MPAlgorithmVersionCurrent ); int64_t value = mpw_get_json_int( json_file, "user.algorithm", MPAlgorithmVersionCurrent );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) { if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid user algorithm version: %u", value ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user algorithm version: %u", value ) };
return NULL; return NULL;
} }
MPAlgorithmVersion algorithm = (MPAlgorithmVersion)value; MPAlgorithmVersion algorithm = (MPAlgorithmVersion)value;
MPResultType defaultType = (MPResultType)mpw_get_json_int( json_file, "user.default_type", MPResultTypeDefault ); MPResultType defaultType = (MPResultType)mpw_get_json_int( json_file, "user.default_type", MPResultTypeDefault );
if (!mpw_nameForType( defaultType )) { if (!mpw_nameForType( defaultType )) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid user default type: %u", defaultType ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %u", defaultType ) };
return NULL; return NULL;
} }
time_t lastUsed = mpw_mktime( str_lastUsed ); time_t lastUsed = mpw_mktime( str_lastUsed );
if (!lastUsed) { if (!lastUsed) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid user last used: %s", str_lastUsed ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user last used: %s", str_lastUsed ) };
return NULL; return NULL;
} }
if (!fullName || !strlen( fullName )) { if (!fullName || !strlen( fullName )) {
*error = (MPMarshallError){ MPMarshallErrorMissing, "Missing value for full name." }; *error = (MPMarshalError){ MPMarshalErrorMissing, "Missing value for full name." };
return NULL; return NULL;
} }
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, algorithm, fullName, masterPassword )) { if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, algorithm, fullName, masterPassword )) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return NULL; return NULL;
} }
if (keyID && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) { if (keyID && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
*error = (MPMarshallError){ MPMarshallErrorMasterPassword, "Master password doesn't match key ID." }; *error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Master password doesn't match key ID." };
return NULL; return NULL;
} }
if (!(user = mpw_marshall_user( fullName, masterPassword, algorithm ))) { if (!(user = mpw_marshal_user( fullName, masterPassword, algorithm ))) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't allocate a new user." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." };
return NULL; return NULL;
} }
user->redacted = fileRedacted; user->redacted = fileRedacted;
@@ -614,92 +734,143 @@ static MPMarshalledUser *mpw_marshall_read_json(
json_object *json_sites = mpw_get_json_section( json_file, "sites" ); json_object *json_sites = mpw_get_json_section( json_file, "sites" );
json_object_object_foreachC( json_sites, json_site ) { json_object_object_foreachC( json_sites, json_site ) {
const char *siteName = json_site.key; const char *siteName = json_site.key;
value = mpw_get_json_int( json_site.val, "algorithm", user->algorithm ); value = mpw_get_json_int( json_site.val, "algorithm", (int32_t)user->algorithm );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) { if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site algorithm version: %s: %d", siteName, value ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm version: %s: %d", siteName, value ) };
return NULL; return NULL;
} }
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value; MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
MPResultType siteType = (MPResultType)mpw_get_json_int( json_site.val, "type", user->defaultType ); MPResultType siteType = (MPResultType)mpw_get_json_int( json_site.val, "type", (int32_t)user->defaultType );
if (!mpw_nameForType( siteType )) { if (!mpw_nameForType( siteType )) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site type: %s: %u", siteName, siteType ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %u", siteName, siteType ) };
return NULL; return NULL;
} }
value = mpw_get_json_int( json_site.val, "counter", 1 ); value = mpw_get_json_int( json_site.val, "counter", 1 );
if (value < MPCounterValueFirst || value > MPCounterValueLast) { if (value < MPCounterValueFirst || value > MPCounterValueLast) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site counter: %s: %d", siteName, value ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %d", siteName, value ) };
return NULL; return NULL;
} }
MPCounterValue siteCounter = (MPCounterValue)value; MPCounterValue siteCounter = (MPCounterValue)value;
const char *siteContent = mpw_get_json_string( json_site.val, "password", NULL ); const char *siteContent = mpw_get_json_string( json_site.val, "password", NULL );
const char *siteLoginName = mpw_get_json_string( json_site.val, "login_name", NULL ); const char *siteLoginName = mpw_get_json_string( json_site.val, "login_name", NULL );
bool siteLoginGenerated = mpw_get_json_boolean( json_site.val, "login_generated", false ); MPResultType siteLoginType = (MPResultType)mpw_get_json_int( json_site.val, "login_type", MPResultTypeTemplateName );
unsigned int siteUses = (unsigned int)mpw_get_json_int( json_site.val, "uses", 0 ); unsigned int siteUses = (unsigned int)mpw_get_json_int( json_site.val, "uses", 0 );
str_lastUsed = mpw_get_json_string( json_site.val, "last_used", NULL ); str_lastUsed = mpw_get_json_string( json_site.val, "last_used", NULL );
time_t siteLastUsed = mpw_mktime( str_lastUsed ); time_t siteLastUsed = mpw_mktime( str_lastUsed );
if (!siteLastUsed) { if (!siteLastUsed) {
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) }; *error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) };
return NULL; return NULL;
} }
json_object *json_site_mpw = mpw_get_json_section( json_site.val, "_ext_mpw" ); json_object *json_site_mpw = mpw_get_json_section( json_site.val, "_ext_mpw" );
const char *siteURL = mpw_get_json_string( json_site_mpw, "url", NULL ); const char *siteURL = mpw_get_json_string( json_site_mpw, "url", NULL );
MPMarshalledSite *site = mpw_marshall_site( user, siteName, siteType, siteCounter, siteAlgorithm ); MPMarshalledSite *site = mpw_marshal_site( user, siteName, siteType, siteCounter, siteAlgorithm );
if (!site) { if (!site) {
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't allocate a new site." }; *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new site." };
return NULL; return NULL;
} }
site->loginName = siteLoginName? strdup( siteLoginName ): NULL; site->loginType = siteLoginType;
site->loginGenerated = siteLoginGenerated;
site->url = siteURL? strdup( siteURL ): NULL; site->url = siteURL? strdup( siteURL ): NULL;
site->uses = siteUses; site->uses = siteUses;
site->lastUsed = siteLastUsed; site->lastUsed = siteLastUsed;
if (siteContent && strlen( siteContent )) { if (!user->redacted) {
if (!user->redacted) { // Clear Text
// Clear Text if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, fullName, masterPassword )) {
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, fullName, masterPassword )) { *error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." }; return NULL;
return NULL; }
}
if (siteContent && strlen( siteContent ))
site->content = mpw_siteState( masterKey, site->name, site->counter, site->content = mpw_siteState( masterKey, site->name, site->counter,
MPKeyPurposeAuthentication, NULL, site->type, siteContent, site->algorithm ); MPKeyPurposeAuthentication, NULL, site->type, siteContent, site->algorithm );
} if (siteLoginName && strlen( siteLoginName ))
else site->loginContent = mpw_siteState( masterKey, site->name, MPCounterValueInitial,
// Redacted MPKeyPurposeIdentification, NULL, site->loginType, siteLoginName, site->algorithm );
}
else {
// Redacted
if (siteContent && strlen( siteContent ))
site->content = strdup( siteContent ); site->content = strdup( siteContent );
if (siteLoginName && strlen( siteLoginName ))
site->loginContent = strdup( siteLoginName );
} }
json_object_iter json_site_question; json_object_iter json_site_question;
json_object *json_site_questions = mpw_get_json_section( json_site.val, "questions" ); json_object *json_site_questions = mpw_get_json_section( json_site.val, "questions" );
json_object_object_foreachC( json_site_questions, json_site_question ) json_object_object_foreachC( json_site_questions, json_site_question ) {
mpw_marshal_question( site, json_site_question.key ); MPMarshalledQuestion *question = mpw_marshal_question( site, json_site_question.key );
const char *answerContent = mpw_get_json_string( json_site_question.val, "answer", NULL );
question->type = (MPResultType)mpw_get_json_int( json_site_question.val, "type", MPResultTypeTemplatePhrase );
if (!user->redacted) {
// Clear Text
if (answerContent && strlen( answerContent ))
question->content = mpw_siteState( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeRecovery, question->keyword, question->type, answerContent, site->algorithm );
}
else {
// Redacted
if (answerContent && strlen( answerContent ))
question->content = strdup( answerContent );
}
}
} }
json_object_put( json_file ); json_object_put( json_file );
*error = (MPMarshallError){ MPMarshallSuccess }; *error = (MPMarshalError){ .type = MPMarshalSuccess };
return user; return user;
} }
#endif
MPMarshalledUser *mpw_marshall_read( MPMarshalInfo *mpw_marshal_read_info(
char *in, const MPMarshallFormat inFormat, const char *masterPassword, MPMarshallError *error) { const char *in) {
MPMarshalInfo *info = malloc( sizeof( MPMarshalInfo ) );
*info = (MPMarshalInfo){ .format = MPMarshalFormatNone };
if (in && strlen( in )) {
if (in[0] == '#') {
*info = (MPMarshalInfo){ .format = MPMarshalFormatFlat };
mpw_marshal_read_flat_info( in, info );
}
else if (in[0] == '{') {
*info = (MPMarshalInfo){ .format = MPMarshalFormatJSON };
#if MPW_JSON
mpw_marshal_read_json_info( in, info );
#endif
}
}
return info;
}
MPMarshalledUser *mpw_marshal_read(
const char *in, const MPMarshalFormat inFormat, const char *masterPassword, MPMarshalError *error) {
switch (inFormat) { switch (inFormat) {
case MPMarshallFormatFlat: case MPMarshalFormatNone:
return mpw_marshall_read_flat( in, masterPassword, error ); *error = (MPMarshalError){ .type = MPMarshalSuccess };
case MPMarshallFormatJSON: return false;
return mpw_marshall_read_json( in, masterPassword, error ); case MPMarshalFormatFlat:
return mpw_marshal_read_flat( in, masterPassword, error );
#if MPW_JSON
case MPMarshalFormatJSON:
return mpw_marshal_read_json( in, masterPassword, error );
#endif
default: default:
*error = (MPMarshallError){ MPMarshallErrorFormat, mpw_str( "Unsupported input format: %u", inFormat ) }; *error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported input format: %u", inFormat ) };
return NULL; return NULL;
} }
} }
const MPMarshallFormat mpw_formatWithName( const MPMarshalFormat mpw_formatWithName(
const char *formatName) { const char *formatName) {
if (!formatName || !strlen( formatName ))
return MPMarshalFormatNone;
// Lower-case to standardize it. // Lower-case to standardize it.
size_t stdFormatNameSize = strlen( formatName ); size_t stdFormatNameSize = strlen( formatName );
char stdFormatName[stdFormatNameSize + 1]; char stdFormatName[stdFormatNameSize + 1];
@@ -707,22 +878,26 @@ const MPMarshallFormat mpw_formatWithName(
stdFormatName[c] = (char)tolower( formatName[c] ); stdFormatName[c] = (char)tolower( formatName[c] );
stdFormatName[stdFormatNameSize] = '\0'; stdFormatName[stdFormatNameSize] = '\0';
if (strncmp( mpw_nameForFormat( MPMarshallFormatFlat ), stdFormatName, strlen( stdFormatName ) ) == 0) if (strncmp( mpw_nameForFormat( MPMarshalFormatNone ), stdFormatName, strlen( stdFormatName ) ) == 0)
return MPMarshallFormatFlat; return MPMarshalFormatNone;
if (strncmp( mpw_nameForFormat( MPMarshallFormatJSON ), stdFormatName, strlen( stdFormatName ) ) == 0) if (strncmp( mpw_nameForFormat( MPMarshalFormatFlat ), stdFormatName, strlen( stdFormatName ) ) == 0)
return MPMarshallFormatJSON; return MPMarshalFormatFlat;
if (strncmp( mpw_nameForFormat( MPMarshalFormatJSON ), stdFormatName, strlen( stdFormatName ) ) == 0)
return MPMarshalFormatJSON;
dbg( "Not a format name: %s\n", stdFormatName ); dbg( "Not a format name: %s\n", stdFormatName );
return (MPMarshallFormat)ERR; return (MPMarshalFormat)ERR;
} }
const char *mpw_nameForFormat( const char *mpw_nameForFormat(
const MPMarshallFormat format) { const MPMarshalFormat format) {
switch (format) { switch (format) {
case MPMarshallFormatFlat: case MPMarshalFormatNone:
return "none";
case MPMarshalFormatFlat:
return "flat"; return "flat";
case MPMarshallFormatJSON: case MPMarshalFormatJSON:
return "json"; return "json";
default: { default: {
dbg( "Unknown format: %d\n", format ); dbg( "Unknown format: %d\n", format );
@@ -731,13 +906,15 @@ const char *mpw_nameForFormat(
} }
} }
const char *mpw_marshall_format_extension( const char *mpw_marshal_format_extension(
const MPMarshallFormat format) { const MPMarshalFormat format) {
switch (format) { switch (format) {
case MPMarshallFormatFlat: case MPMarshalFormatNone:
return NULL;
case MPMarshalFormatFlat:
return "mpsites"; return "mpsites";
case MPMarshallFormatJSON: case MPMarshalFormatJSON:
return "mpsites.json"; return "mpsites.json";
default: { default: {
dbg( "Unknown format: %d\n", format ); dbg( "Unknown format: %d\n", format );

View File

@@ -16,8 +16,8 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>. // LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//============================================================================== //==============================================================================
#ifndef _MPW_MARSHALL_H #ifndef _MPW_MARSHAL_H
#define _MPW_MARSHALL_H #define _MPW_MARSHAL_H
#include <time.h> #include <time.h>
@@ -25,38 +25,46 @@
//// Types. //// Types.
typedef enum( unsigned int, MPMarshallFormat ) { typedef mpw_enum( unsigned int, MPMarshalFormat ) {
/** Generate a key for authentication. */ /** Do not marshal. */
MPMarshallFormatFlat, MPMarshalFormatNone,
/** Generate a name for identification. */ /** Marshal using the line-based plain-text format. */
MPMarshallFormatJSON, MPMarshalFormatFlat,
/** Marshal using the JSON structured format. */
MPMarshalFormatJSON,
MPMarshallFormatDefault = MPMarshallFormatJSON, #if MPW_JSON
MPMarshalFormatDefault = MPMarshalFormatJSON,
#else
MPMarshalFormatDefault = MPMarshalFormatFlat,
#endif
}; };
typedef enum( unsigned int, MPMarshallErrorType ) { typedef mpw_enum( unsigned int, MPMarshalErrorType ) {
/** The marshalling operation completed successfully. */ /** The marshalling operation completed successfully. */
MPMarshallSuccess, MPMarshalSuccess,
/** An error in the structure of the marshall file interrupted marshalling. */ /** An error in the structure of the marshall file interrupted marshalling. */
MPMarshallErrorStructure, MPMarshalErrorStructure,
/** The marshall file uses an unsupported format version. */ /** The marshall file uses an unsupported format version. */
MPMarshallErrorFormat, MPMarshalErrorFormat,
/** A required value is missing or not specified. */ /** A required value is missing or not specified. */
MPMarshallErrorMissing, MPMarshalErrorMissing,
/** The given master password is not valid. */ /** The given master password is not valid. */
MPMarshallErrorMasterPassword, MPMarshalErrorMasterPassword,
/** An illegal value was specified. */ /** An illegal value was specified. */
MPMarshallErrorIllegal, MPMarshalErrorIllegal,
/** An internal system error interrupted marshalling. */ /** An internal system error interrupted marshalling. */
MPMarshallErrorInternal, MPMarshalErrorInternal,
}; };
typedef struct MPMarshallError { typedef struct MPMarshalError {
MPMarshallErrorType type; MPMarshalErrorType type;
const char *description; const char *description;
} MPMarshallError; } MPMarshalError;
typedef struct MPMarshalledQuestion { typedef struct MPMarshalledQuestion {
const char *keyword; const char *keyword;
const char *content;
MPResultType type;
} MPMarshalledQuestion; } MPMarshalledQuestion;
typedef struct MPMarshalledSite { typedef struct MPMarshalledSite {
@@ -66,8 +74,8 @@ typedef struct MPMarshalledSite {
MPCounterValue counter; MPCounterValue counter;
MPAlgorithmVersion algorithm; MPAlgorithmVersion algorithm;
const char *loginName; const char *loginContent;
bool loginGenerated; MPResultType loginType;
const char *url; const char *url;
unsigned int uses; unsigned int uses;
@@ -91,38 +99,61 @@ typedef struct MPMarshalledUser {
MPMarshalledSite *sites; MPMarshalledSite *sites;
} MPMarshalledUser; } MPMarshalledUser;
typedef struct MPMarshalInfo {
MPMarshalFormat format;
MPAlgorithmVersion algorithm;
const char *fullName;
const char *keyID;
bool redacted;
time_t date;
} MPMarshalInfo;
//// Marshalling. //// Marshalling.
bool mpw_marshall_write( /** Write the user and all associated data out to the given output buffer using the given marshalling format. */
char **out, const MPMarshallFormat outFormat, const MPMarshalledUser *user, MPMarshallError *error); bool mpw_marshal_write(
MPMarshalledUser *mpw_marshall_read( char **out, const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error);
char *in, const MPMarshallFormat inFormat, const char *masterPassword, MPMarshallError *error); /** Try to read metadata on the sites in the input buffer. */
MPMarshalInfo *mpw_marshal_read_info(
const char *in);
/** Unmarshall sites in the given input buffer by parsing it using the given marshalling format. */
MPMarshalledUser *mpw_marshal_read(
const char *in, const MPMarshalFormat inFormat, const char *masterPassword, MPMarshalError *error);
//// Utilities. //// Utilities.
MPMarshalledUser *mpw_marshall_user( /** Create a new user object ready for marshalling. */
MPMarshalledUser *mpw_marshal_user(
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion); const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
MPMarshalledSite *mpw_marshall_site( /** Create a new site attached to the given user object, ready for marshalling. */
MPMarshalledSite *mpw_marshal_site(
MPMarshalledUser *user, MPMarshalledUser *user,
const char *siteName, const MPResultType resultType, const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion); const char *siteName, const MPResultType resultType, const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion);
/** Create a new question attached to the given site object, ready for marshalling. */
MPMarshalledQuestion *mpw_marshal_question( MPMarshalledQuestion *mpw_marshal_question(
MPMarshalledSite *site, const char *keyword); MPMarshalledSite *site, const char *keyword);
/** Free the given user object and all associated data. */
bool mpw_marshal_info_free(
MPMarshalInfo **info);
bool mpw_marshal_free( bool mpw_marshal_free(
MPMarshalledUser *user); MPMarshalledUser **user);
//// Format. //// Format.
/** /**
* @return The purpose represented by the given name. * @return The purpose represented by the given name.
*/ */
const MPMarshallFormat mpw_formatWithName( const MPMarshalFormat mpw_formatWithName(
const char *formatName); const char *formatName);
/** /**
* @return The standard name for the given purpose. * @return The standard name for the given purpose.
*/ */
const char *mpw_nameForFormat( const char *mpw_nameForFormat(
const MPMarshallFormat format); const MPMarshalFormat format);
const char *mpw_marshall_format_extension( /**
const MPMarshallFormat format); * @return The file extension that's recommended for files that use the given marshalling format.
*/
const char *mpw_marshal_format_extension(
const MPMarshalFormat format);
#endif // _MPW_MARSHALL_H #endif // _MPW_MARSHAL_H

View File

@@ -16,16 +16,9 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>. // LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//============================================================================== //==============================================================================
#include <stdlib.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#ifdef COLOR
#include <curses.h>
#include <term.h>
#endif
#include "mpw-types.h" #include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"
@@ -47,22 +40,21 @@ const MPResultType mpw_typeWithName(const char *typeName) {
return MPResultTypeTemplatePIN; return MPResultTypeTemplatePIN;
if ('n' == typeName[0]) if ('n' == typeName[0])
return MPResultTypeTemplateName; return MPResultTypeTemplateName;
if ('p' == typeName[0])
return MPResultTypeTemplatePhrase;
if ('P' == typeName[0]) if ('P' == typeName[0])
return MPResultTypeStatePersonal; return MPResultTypeStatefulPersonal;
if ('D' == typeName[0]) if ('D' == typeName[0])
return MPResultTypeStateDevice; return MPResultTypeStatefulDevice;
if ('k' == typeName[0]) if ('K' == typeName[0])
return MPResultTypeDeriveKey; return MPResultTypeDeriveKey;
} }
// Lower-case and trim optionally leading "Generated" string from typeName to standardize it. // Lower-case typeName to standardize it.
size_t stdTypeNameOffset = 0;
size_t stdTypeNameSize = strlen( typeName ); size_t stdTypeNameSize = strlen( typeName );
if (strstr( typeName, "Generated" ) == typeName)
stdTypeNameSize -= (stdTypeNameOffset = strlen( "Generated" ));
char stdTypeName[stdTypeNameSize + 1]; char stdTypeName[stdTypeNameSize + 1];
for (size_t c = 0; c < stdTypeNameSize; ++c) for (size_t c = 0; c < stdTypeNameSize; ++c)
stdTypeName[c] = (char)tolower( typeName[c + stdTypeNameOffset] ); stdTypeName[c] = (char)tolower( typeName[c] );
stdTypeName[stdTypeNameSize] = '\0'; stdTypeName[stdTypeNameSize] = '\0';
// Find what password type is represented by the type name. // Find what password type is represented by the type name.
@@ -82,10 +74,10 @@ const MPResultType mpw_typeWithName(const char *typeName) {
return MPResultTypeTemplateName; return MPResultTypeTemplateName;
if (strncmp( mpw_nameForType( MPResultTypeTemplatePhrase ), stdTypeName, strlen( stdTypeName ) ) == 0) if (strncmp( mpw_nameForType( MPResultTypeTemplatePhrase ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeTemplatePhrase; return MPResultTypeTemplatePhrase;
if (strncmp( mpw_nameForType( MPResultTypeStatePersonal ), stdTypeName, strlen( stdTypeName ) ) == 0) if (strncmp( mpw_nameForType( MPResultTypeStatefulPersonal ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeStatePersonal; return MPResultTypeStatefulPersonal;
if (strncmp( mpw_nameForType( MPResultTypeStateDevice ), stdTypeName, strlen( stdTypeName ) ) == 0) if (strncmp( mpw_nameForType( MPResultTypeStatefulDevice ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeStateDevice; return MPResultTypeStatefulDevice;
if (strncmp( mpw_nameForType( MPResultTypeDeriveKey ), stdTypeName, strlen( stdTypeName ) ) == 0) if (strncmp( mpw_nameForType( MPResultTypeDeriveKey ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeDeriveKey; return MPResultTypeDeriveKey;
@@ -112,9 +104,9 @@ const char *mpw_nameForType(MPResultType resultType) {
return "name"; return "name";
case MPResultTypeTemplatePhrase: case MPResultTypeTemplatePhrase:
return "phrase"; return "phrase";
case MPResultTypeStatePersonal: case MPResultTypeStatefulPersonal:
return "personal"; return "personal";
case MPResultTypeStateDevice: case MPResultTypeStatefulDevice:
return "device"; return "device";
case MPResultTypeDeriveKey: case MPResultTypeDeriveKey:
return "key"; return "key";
@@ -170,11 +162,11 @@ const char **mpw_templatesForType(MPResultType type, size_t *count) {
} }
} }
const char *mpw_templateForType(MPResultType type, uint8_t seedByte) { const char *mpw_templateForType(MPResultType type, uint8_t templateIndex) {
size_t count = 0; size_t count = 0;
const char **templates = mpw_templatesForType( type, &count ); const char **templates = mpw_templatesForType( type, &count );
char const *template = templates && count? templates[seedByte % count]: NULL; char const *template = templates && count? templates[templateIndex % count]: NULL;
free( templates ); free( templates );
return template; return template;

View File

@@ -23,11 +23,15 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
//#ifdef NS_ENUM #ifdef NS_ENUM
//#define enum(_type, _name) NS_ENUM(_type, _name) #define mpw_enum(_type, _name) NS_ENUM(_type, _name)
//#else #else
#define enum(_type, _name) _type _name; enum #define mpw_enum(_type, _name) _type _name; enum
//#endif #endif
#ifndef __unused
#define __unused __attribute__((unused))
#endif
//// Types. //// Types.
@@ -37,7 +41,7 @@ typedef const uint8_t *MPMasterKey;
typedef const uint8_t *MPSiteKey; typedef const uint8_t *MPSiteKey;
typedef const char *MPKeyID; typedef const char *MPKeyID;
typedef enum( uint8_t, MPKeyPurpose ) { typedef mpw_enum( uint8_t, MPKeyPurpose ) {
/** Generate a key for authentication. */ /** Generate a key for authentication. */
MPKeyPurposeAuthentication, MPKeyPurposeAuthentication,
/** Generate a name for identification. */ /** Generate a name for identification. */
@@ -47,17 +51,17 @@ typedef enum( uint8_t, MPKeyPurpose ) {
}; };
// bit 4 - 9 // bit 4 - 9
typedef enum( uint16_t, MPResultTypeClass ) { typedef mpw_enum( uint16_t, MPResultTypeClass ) {
/** Use the site key to generate a password from a template. */ /** Use the site key to generate a password from a template. */
MPResultTypeClassTemplate = 1 << 4, MPResultTypeClassTemplate = 1 << 4,
/** Use the site key to encrypt and decrypt a stateful entity. */ /** Use the site key to encrypt and decrypt a stateful entity. */
MPResultTypeClassState = 1 << 5, MPResultTypeClassStateful = 1 << 5,
/** Use the site key to derive a site-specific object. */ /** Use the site key to derive a site-specific object. */
MPResultTypeClassDerive = 1 << 6, MPResultTypeClassDerive = 1 << 6,
}; };
// bit 10 - 15 // bit 10 - 15
typedef enum( uint16_t, MPSiteFeature ) { typedef mpw_enum( uint16_t, MPSiteFeature ) {
/** Export the key-protected content data. */ /** Export the key-protected content data. */
MPSiteFeatureExportContent = 1 << 10, MPSiteFeatureExportContent = 1 << 10,
/** Never export content. */ /** Never export content. */
@@ -67,7 +71,7 @@ typedef enum( uint16_t, MPSiteFeature ) {
}; };
// bit 0-3 | MPResultTypeClass | MPSiteFeature // bit 0-3 | MPResultTypeClass | MPSiteFeature
typedef enum( uint32_t, MPResultType ) { typedef mpw_enum( uint32_t, MPResultType ) {
/** pg^VMAUBk5x3p%HP%i4= */ /** pg^VMAUBk5x3p%HP%i4= */
MPResultTypeTemplateMaximum = 0x0 | MPResultTypeClassTemplate | 0x0, MPResultTypeTemplateMaximum = 0x0 | MPResultTypeClassTemplate | 0x0,
/** BiroYena8:Kixa */ /** BiroYena8:Kixa */
@@ -86,9 +90,9 @@ typedef enum( uint32_t, MPResultType ) {
MPResultTypeTemplatePhrase = 0xF | MPResultTypeClassTemplate | 0x0, MPResultTypeTemplatePhrase = 0xF | MPResultTypeClassTemplate | 0x0,
/** Custom saved password. */ /** Custom saved password. */
MPResultTypeStatePersonal = 0x0 | MPResultTypeClassState | MPSiteFeatureExportContent, MPResultTypeStatefulPersonal = 0x0 | MPResultTypeClassStateful | MPSiteFeatureExportContent,
/** Custom saved password that should not be exported from the device. */ /** Custom saved password that should not be exported from the device. */
MPResultTypeStateDevice = 0x1 | MPResultTypeClassState | MPSiteFeatureDevicePrivate, MPResultTypeStatefulDevice = 0x1 | MPResultTypeClassStateful | MPSiteFeatureDevicePrivate,
/** Derive a unique binary key. */ /** Derive a unique binary key. */
MPResultTypeDeriveKey = 0x0 | MPResultTypeClassDerive | MPSiteFeatureAlternative, MPResultTypeDeriveKey = 0x0 | MPResultTypeClassDerive | MPSiteFeatureAlternative,
@@ -96,7 +100,7 @@ typedef enum( uint32_t, MPResultType ) {
MPResultTypeDefault = MPResultTypeTemplateLong, MPResultTypeDefault = MPResultTypeTemplateLong,
}; };
typedef enum ( uint32_t, MPCounterValue ) { typedef mpw_enum ( uint32_t, MPCounterValue ) {
/** Use a time-based counter value, resulting in a TOTP generator. */ /** Use a time-based counter value, resulting in a TOTP generator. */
MPCounterValueTOTP = 0, MPCounterValueTOTP = 0,
/** The initial value for a site's counter. */ /** The initial value for a site's counter. */
@@ -142,7 +146,7 @@ const char **mpw_templatesForType(MPResultType type, size_t *count);
* @return An internal string that contains the password encoding template of the given type * @return An internal string that contains the password encoding template of the given type
* for a seed that starts with the given byte. * for a seed that starts with the given byte.
*/ */
const char *mpw_templateForType(MPResultType type, uint8_t seedByte); const char *mpw_templateForType(MPResultType type, uint8_t templateIndex);
/** /**
* @return An internal string that contains all the characters that occur in the given character class. * @return An internal string that contains all the characters that occur in the given character class.

View File

@@ -26,34 +26,60 @@
#include <term.h> #include <term.h>
#endif #endif
#if HAS_CPERCIVA #if MPW_CPERCIVA
#include <scrypt/crypto_scrypt.h> #include <scrypt/crypto_scrypt.h>
#include <scrypt/sha256.h> #include <scrypt/sha256.h>
#elif HAS_SODIUM #elif MPW_SODIUM
#include "sodium.h" #include "sodium.h"
#ifdef SODIUM_LIBRARY_MINIMAL
#include "crypto_stream_aes128ctr.h"
#include "crypto_kdf_blake2b.h"
#endif
#endif #endif
#define AES_ECB 0
#define AES_CBC 1
#include "aes.h"
#include "mpw-util.h" #include "mpw-util.h"
#ifdef inf_level
int mpw_verbosity = inf_level; int mpw_verbosity = inf_level;
#endif
bool mpw_push_buf(uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize) { void mpw_uint16(const uint16_t number, uint8_t buf[2]) {
buf[0] = (uint8_t)((number >> 8L) & UINT8_MAX);
buf[1] = (uint8_t)((number >> 0L) & UINT8_MAX);
}
void mpw_uint32(const uint32_t number, uint8_t buf[4]) {
buf[0] = (uint8_t)((number >> 24) & UINT8_MAX);
buf[1] = (uint8_t)((number >> 16) & UINT8_MAX);
buf[2] = (uint8_t)((number >> 8L) & UINT8_MAX);
buf[3] = (uint8_t)((number >> 0L) & UINT8_MAX);
}
void mpw_uint64(const uint64_t number, uint8_t buf[8]) {
buf[0] = (uint8_t)((number >> 56) & UINT8_MAX);
buf[1] = (uint8_t)((number >> 48) & UINT8_MAX);
buf[2] = (uint8_t)((number >> 40) & UINT8_MAX);
buf[3] = (uint8_t)((number >> 32) & UINT8_MAX);
buf[4] = (uint8_t)((number >> 24) & UINT8_MAX);
buf[5] = (uint8_t)((number >> 16) & UINT8_MAX);
buf[6] = (uint8_t)((number >> 8L) & UINT8_MAX);
buf[7] = (uint8_t)((number >> 0L) & UINT8_MAX);
}
bool mpw_push_buf(uint8_t **buffer, size_t *bufferSize, const void *pushBuffer, const size_t pushSize) {
if (!buffer || !bufferSize || !pushBuffer || !pushSize) if (!buffer || !bufferSize || !pushBuffer || !pushSize)
return false; return false;
if (*bufferSize == ERR) if (*bufferSize == (size_t)ERR)
// The buffer was marked as broken, it is missing a previous push. Abort to avoid corrupt content. // The buffer was marked as broken, it is missing a previous push. Abort to avoid corrupt content.
return false; return false;
if (!mpw_realloc( buffer, bufferSize, pushSize )) { if (!mpw_realloc( buffer, bufferSize, pushSize )) {
// realloc failed, we can't push. Mark the buffer as broken. // realloc failed, we can't push. Mark the buffer as broken.
mpw_free( *buffer, *bufferSize ); mpw_free( buffer, *bufferSize );
*bufferSize = (size_t)ERR; *bufferSize = (size_t)ERR;
*buffer = NULL;
return false; return false;
} }
@@ -62,13 +88,15 @@ bool mpw_push_buf(uint8_t **const buffer, size_t *const bufferSize, const void *
return true; return true;
} }
bool mpw_push_string(uint8_t **const buffer, size_t *const bufferSize, const char *pushString) { bool mpw_push_string(uint8_t **buffer, size_t *bufferSize, const char *pushString) {
return pushString && mpw_push_buf( buffer, bufferSize, pushString, strlen( pushString ) ); return pushString && mpw_push_buf( buffer, bufferSize, pushString, strlen( pushString ) );
} }
bool mpw_string_push(char **const string, const char *pushString) { bool mpw_string_push(char **string, const char *pushString) {
if (!string || !pushString)
return false;
if (!*string) if (!*string)
*string = calloc( 1, sizeof( char ) ); *string = calloc( 1, sizeof( char ) );
@@ -76,29 +104,29 @@ bool mpw_string_push(char **const string, const char *pushString) {
return pushString && mpw_push_buf( (uint8_t **const)string, &stringLength, pushString, strlen( pushString ) + 1 ); return pushString && mpw_push_buf( (uint8_t **const)string, &stringLength, pushString, strlen( pushString ) + 1 );
} }
bool mpw_string_pushf(char **const string, const char *pushFormat, ...) { bool mpw_string_pushf(char **string, const char *pushFormat, ...) {
va_list args; va_list args;
va_start( args, pushFormat ); va_start( args, pushFormat );
char *pushString = NULL; bool success = mpw_string_push( string, mpw_vstr( pushFormat, args ) );
bool success = vasprintf( &pushString, pushFormat, args ) >= 0 && mpw_string_push( string, pushString );
va_end( args ); va_end( args );
mpw_free_string( pushString );
return success; return success;
} }
bool mpw_push_int(uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt) { bool mpw_push_int(uint8_t **buffer, size_t *bufferSize, const uint32_t pushInt) {
return mpw_push_buf( buffer, bufferSize, &pushInt, sizeof( pushInt ) ); uint8_t pushBuf[4 /* 32 / 8 */];
mpw_uint32( pushInt, pushBuf );
return mpw_push_buf( buffer, bufferSize, &pushBuf, sizeof( pushBuf ) );
} }
bool __mpw_realloc(void **buffer, size_t *bufferSize, const size_t deltaSize) { bool __mpw_realloc(const void **buffer, size_t *bufferSize, const size_t deltaSize) {
if (!buffer) if (!buffer)
return false; return false;
void *newBuffer = realloc( *buffer, (bufferSize? *bufferSize: 0) + deltaSize ); void *newBuffer = realloc( (void *)*buffer, (bufferSize? *bufferSize: 0) + deltaSize );
if (!newBuffer) if (!newBuffer)
return false; return false;
@@ -109,19 +137,42 @@ bool __mpw_realloc(void **buffer, size_t *bufferSize, const size_t deltaSize) {
return true; return true;
} }
bool mpw_free(const void *buffer, const size_t bufferSize) { void mpw_zero(void *buffer, size_t bufferSize) {
if (!buffer) uint8_t *b = buffer;
for (; bufferSize > 0; --bufferSize)
*b++ = 0;
}
bool __mpw_free(void **buffer, const size_t bufferSize) {
if (!buffer || !*buffer)
return false; return false;
memset( (void *)buffer, 0, bufferSize ); mpw_zero( *buffer, bufferSize );
free( (void *)buffer ); free( *buffer );
*buffer = NULL;
return true; return true;
} }
bool mpw_free_string(const char *string) { bool __mpw_free_string(char **string) {
return string && mpw_free( string, strlen( string ) ); return *string && __mpw_free( (void **)string, strlen( *string ) );
}
bool __mpw_free_strings(char **strings, ...) {
bool success = true;
va_list args;
va_start( args, strings );
success &= mpw_free_string( strings );
for (char **string; (string = va_arg( args, char ** ));)
success &= mpw_free_string( string );
va_end( args );
return success;
} }
uint8_t const *mpw_kdf_scrypt(const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize, uint8_t const *mpw_kdf_scrypt(const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize,
@@ -134,14 +185,14 @@ uint8_t const *mpw_kdf_scrypt(const size_t keySize, const char *secret, const ui
if (!key) if (!key)
return NULL; return NULL;
#if HAS_CPERCIVA #if MPW_CPERCIVA
if (crypto_scrypt( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) < 0) { if (crypto_scrypt( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) < 0) {
mpw_free( key, keySize ); mpw_free( &key, keySize );
return NULL; return NULL;
} }
#elif HAS_SODIUM #elif MPW_SODIUM
if (crypto_pwhash_scryptsalsa208sha256_ll( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) != 0) { if (crypto_pwhash_scryptsalsa208sha256_ll( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) != 0) {
mpw_free( key, keySize ); mpw_free( &key, keySize );
return NULL; return NULL;
} }
#else #else
@@ -163,10 +214,9 @@ uint8_t const *mpw_kdf_blake2b(const size_t subkeySize, const uint8_t *key, cons
if (!subkey) if (!subkey)
return NULL; return NULL;
#if HAS_SODIUM #if MPW_SODIUM
if (keySize < crypto_generichash_blake2b_KEYBYTES_MIN || keySize > crypto_generichash_blake2b_KEYBYTES_MAX || if (keySize < crypto_generichash_blake2b_KEYBYTES_MIN || keySize > crypto_generichash_blake2b_KEYBYTES_MAX ||
subkeySize < crypto_generichash_blake2b_KEYBYTES_MIN || subkeySize > crypto_generichash_blake2b_KEYBYTES_MAX || subkeySize < crypto_generichash_blake2b_KEYBYTES_MIN || subkeySize > crypto_generichash_blake2b_KEYBYTES_MAX ||
contextSize < crypto_generichash_blake2b_BYTES_MIN || contextSize > crypto_generichash_blake2b_BYTES_MAX ||
(personal && strlen( personal ) > crypto_generichash_blake2b_PERSONALBYTES)) { (personal && strlen( personal ) > crypto_generichash_blake2b_PERSONALBYTES)) {
errno = EINVAL; errno = EINVAL;
free( subkey ); free( subkey );
@@ -174,19 +224,17 @@ uint8_t const *mpw_kdf_blake2b(const size_t subkeySize, const uint8_t *key, cons
} }
uint8_t saltBuf[crypto_generichash_blake2b_SALTBYTES]; uint8_t saltBuf[crypto_generichash_blake2b_SALTBYTES];
bzero( saltBuf, sizeof saltBuf ); mpw_zero( saltBuf, sizeof saltBuf );
if (id) { if (id)
uint64_t id_n = htonll( id ); mpw_uint64( id, saltBuf );
memcpy( saltBuf, &id_n, sizeof id_n );
}
uint8_t personalBuf[crypto_generichash_blake2b_PERSONALBYTES]; uint8_t personalBuf[crypto_generichash_blake2b_PERSONALBYTES];
bzero( personalBuf, sizeof saltBuf ); mpw_zero( personalBuf, sizeof personalBuf );
if (personal && strlen( personal )) if (personal && strlen( personal ))
memcpy( personalBuf, personal, strlen( personal ) ); memcpy( personalBuf, personal, strlen( personal ) );
if (crypto_generichash_blake2b_salt_personal( subkey, subkeySize, context, contextSize, key, keySize, saltBuf, personalBuf ) != 0) { if (crypto_generichash_blake2b_salt_personal( subkey, subkeySize, context, contextSize, key, keySize, saltBuf, personalBuf ) != 0) {
mpw_free( subkey, subkeySize ); mpw_free( &subkey, subkeySize );
return NULL; return NULL;
} }
#else #else
@@ -201,13 +249,13 @@ uint8_t const *mpw_hash_hmac_sha256(const uint8_t *key, const size_t keySize, co
if (!key || !keySize || !message || !messageSize) if (!key || !keySize || !message || !messageSize)
return NULL; return NULL;
#if HAS_CPERCIVA #if MPW_CPERCIVA
uint8_t *const mac = malloc( 32 ); uint8_t *const mac = malloc( 32 );
if (!mac) if (!mac)
return NULL; return NULL;
HMAC_SHA256_Buf( key, keySize, message, messageSize, mac ); HMAC_SHA256_Buf( key, keySize, message, messageSize, mac );
#elif HAS_SODIUM #elif MPW_SODIUM
uint8_t *const mac = malloc( crypto_auth_hmacsha256_BYTES ); uint8_t *const mac = malloc( crypto_auth_hmacsha256_BYTES );
if (!mac) if (!mac)
return NULL; return NULL;
@@ -216,7 +264,7 @@ uint8_t const *mpw_hash_hmac_sha256(const uint8_t *key, const size_t keySize, co
if (crypto_auth_hmacsha256_init( &state, key, keySize ) != 0 || if (crypto_auth_hmacsha256_init( &state, key, keySize ) != 0 ||
crypto_auth_hmacsha256_update( &state, message, messageSize ) != 0 || crypto_auth_hmacsha256_update( &state, message, messageSize ) != 0 ||
crypto_auth_hmacsha256_final( &state, mac ) != 0) { crypto_auth_hmacsha256_final( &state, mac ) != 0) {
mpw_free( mac, crypto_auth_hmacsha256_BYTES ); mpw_free( &mac, crypto_auth_hmacsha256_BYTES );
return NULL; return NULL;
} }
#else #else
@@ -226,63 +274,88 @@ uint8_t const *mpw_hash_hmac_sha256(const uint8_t *key, const size_t keySize, co
return mac; return mac;
} }
static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t keySize, const uint8_t *buf, const size_t bufSize) { static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t keySize, const uint8_t *buf, size_t *bufSize) {
#if HAS_SODIUM if (!key || keySize < 16)
if (!key || keySize < crypto_stream_KEYBYTES)
return NULL; return NULL;
uint8_t nonce[crypto_stream_NONCEBYTES]; // IV = zero
bzero( (void *)nonce, sizeof( nonce ) ); uint8_t iv[16];
mpw_zero( iv, sizeof iv );
#pragma GCC diagnostic push // Add PKCS#7 padding
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" uint32_t aesSize = (uint32_t)*bufSize;
#pragma clang diagnostic push if (encrypt)
#pragma clang diagnostic ignored "-Wdeprecated-declarations" aesSize = (aesSize / 16) * 16 + 16;
if (encrypt) { uint8_t aesBuf[aesSize];
uint8_t *const cipherBuf = malloc( bufSize ); memcpy( aesBuf, buf, *bufSize );
if (crypto_stream_aes128ctr_xor( cipherBuf, buf, bufSize, nonce, key ) != 0) { memset( aesBuf + *bufSize, aesSize - *bufSize, aesSize - *bufSize );
mpw_free( cipherBuf, bufSize ); uint8_t *resultBuf = malloc( aesSize );
return NULL;
} if (encrypt)
return cipherBuf; AES_CBC_encrypt_buffer( resultBuf, aesBuf, aesSize, key, iv );
} else
else { AES_CBC_decrypt_buffer( resultBuf, aesBuf, aesSize, key, iv );
uint8_t *const plainBuf = malloc( bufSize ); mpw_zero( aesBuf, aesSize );
if (crypto_stream_aes128ctr( plainBuf, bufSize, nonce, key ) != 0) { mpw_zero( iv, 16 );
mpw_free( plainBuf, bufSize );
return NULL; // Truncate PKCS#7 padding
} if (encrypt)
for (size_t c = 0; c < bufSize; ++c) *bufSize = aesSize;
plainBuf[c] = buf[c] ^ plainBuf[c]; else
return plainBuf; *bufSize -= resultBuf[aesSize - 1];
}
#pragma clang diagnostic pop return resultBuf;
#pragma GCC diagnostic pop
#else
#error No crypto support for mpw_aes.
#endif
} }
uint8_t const *mpw_aes_encrypt(const uint8_t *key, const size_t keySize, const uint8_t *plainBuf, const size_t bufSize) { uint8_t const *mpw_aes_encrypt(const uint8_t *key, const size_t keySize, const uint8_t *plainBuf, size_t *bufSize) {
return mpw_aes( true, key, keySize, plainBuf, bufSize ); return mpw_aes( true, key, keySize, plainBuf, bufSize );
} }
uint8_t const *mpw_aes_decrypt(const uint8_t *key, const size_t keySize, const uint8_t *cipherBuf, const size_t bufSize) { uint8_t const *mpw_aes_decrypt(const uint8_t *key, const size_t keySize, const uint8_t *cipherBuf, size_t *bufSize) {
return mpw_aes( false, key, keySize, cipherBuf, bufSize ); return mpw_aes( false, key, keySize, cipherBuf, bufSize );
} }
#if UNUSED
const char *mpw_hotp(const uint8_t *key, size_t keySize, uint64_t movingFactor, uint8_t digits, uint8_t truncationOffset) {
// Hash the moving factor with the key.
uint8_t counter[8];
mpw_uint64( movingFactor, counter );
uint8_t hash[20];
hmac_sha1( key, keySize, counter, sizeof( counter ), hash );
// Determine the offset to select OTP bytes from.
int offset;
if ((truncationOffset >= 0) && (truncationOffset < (sizeof( hash ) - 4)))
offset = truncationOffset;
else
offset = hash[sizeof( hash ) - 1] & 0xf;
// Select four bytes from the truncation offset.
uint32_t otp = 0U
| ((hash[offset + 0] & 0x7f) << 24)
| ((hash[offset + 1] & 0xff) << 16)
| ((hash[offset + 2] & 0xff) << 8)
| ((hash[offset + 3] & 0xff) << 0);
// Render the OTP as `digits` decimal digits.
otp %= (int)pow(10, digits);
return strdup( mpw_str( "%0*d", digits, otp ) );
}
#endif
MPKeyID mpw_id_buf(const void *buf, size_t length) { MPKeyID mpw_id_buf(const void *buf, size_t length) {
if (!buf) if (!buf)
return "<unset>"; return "<unset>";
#if HAS_CPERCIVA #if MPW_CPERCIVA
uint8_t hash[32]; uint8_t hash[32];
SHA256_Buf( buf, length, hash ); SHA256_Buf( buf, length, hash );
#elif HAS_SODIUM #elif MPW_SODIUM
uint8_t hash[crypto_hash_sha256_BYTES]; uint8_t hash[crypto_hash_sha256_BYTES];
crypto_hash_sha256( hash, buf, length ); crypto_hash_sha256( hash, buf, length );
#else #else
@@ -305,24 +378,50 @@ bool mpw_id_buf_equals(const char *id1, const char *id2) {
return true; return true;
} }
static char *str_str;
const char *mpw_str(const char *format, ...) { const char *mpw_str(const char *format, ...) {
va_list args; va_list args;
va_start( args, format ); va_start( args, format );
vasprintf( &str_str, format, args ); const char *str_str = mpw_vstr( format, args );
va_end( args ); va_end( args );
return str_str; return str_str;
} }
static char **mpw_hex_buf; const char *mpw_vstr(const char *format, va_list args) {
static unsigned int mpw_hex_buf_i;
// TODO: We should find a way to get rid of this shared storage medium.
// TODO: Not thread-safe
static char *str_str;
static size_t str_str_max;
if (!str_str && !(str_str = calloc( str_str_max = 1, sizeof( char ) )))
return NULL;
do {
va_list args_attempt;
va_copy( args_attempt, args );
size_t len = (size_t)vsnprintf( str_str, str_str_max, format, args_attempt );
va_end( args_attempt );
if ((int)len < 0)
return NULL;
if (len < str_str_max)
break;
if (!mpw_realloc( &str_str, &str_str_max, len - str_str_max + 1 ))
return NULL;
} while (true);
return str_str;
}
const char *mpw_hex(const void *buf, size_t length) { const char *mpw_hex(const void *buf, size_t length) {
// FIXME: Not thread-safe // TODO: We should find a way to get rid of this shared storage medium.
// TODO: Not thread-safe
static char **mpw_hex_buf;
static unsigned int mpw_hex_buf_i;
if (!mpw_hex_buf) if (!mpw_hex_buf)
mpw_hex_buf = calloc( 10, sizeof( char * ) ); mpw_hex_buf = calloc( 10, sizeof( char * ) );
mpw_hex_buf_i = (mpw_hex_buf_i + 1) % 10; mpw_hex_buf_i = (mpw_hex_buf_i + 1) % 10;
@@ -336,34 +435,60 @@ const char *mpw_hex(const void *buf, size_t length) {
const char *mpw_hex_l(uint32_t number) { const char *mpw_hex_l(uint32_t number) {
return mpw_hex( &number, sizeof( number ) ); uint8_t buf[4 /* 32 / 8 */];
buf[0] = (uint8_t)((number >> 24) & UINT8_MAX);
buf[1] = (uint8_t)((number >> 16) & UINT8_MAX);
buf[2] = (uint8_t)((number >> 8L) & UINT8_MAX);
buf[3] = (uint8_t)((number >> 0L) & UINT8_MAX);
return mpw_hex( &buf, sizeof( buf ) );
} }
#ifdef COLOR #if MPW_COLOR
static int putvari; static char *str_tputs;
static char *putvarc = NULL; static int str_tputs_cursor;
static int termsetup; static const int str_tputs_max = 256;
static int initputvar() {
if (!isatty(STDERR_FILENO)) static bool mpw_setupterm() {
return 0;
if (putvarc) if (!isatty( STDERR_FILENO ))
free( putvarc ); return false;
static bool termsetup;
if (!termsetup) { if (!termsetup) {
int status; int errret;
if (! (termsetup = (setupterm( NULL, STDERR_FILENO, &status ) == 0 && status == 1))) { if (!(termsetup = (setupterm( NULL, STDERR_FILENO, &errret ) == OK))) {
wrn( "Terminal doesn't support color (setupterm errno %d).\n", status ); wrn( "Terminal doesn't support color (setupterm errret %d).\n", errret );
return 0; return false;
} }
} }
putvarc=(char *)calloc(256, sizeof(char)); return true;
putvari=0;
return 1;
} }
static int putvar(int c) {
putvarc[putvari++]=c; static int mpw_tputc(int c) {
return 0;
if (++str_tputs_cursor < str_tputs_max) {
str_tputs[str_tputs_cursor] = (char)c;
return OK;
}
return ERR;
} }
static char *mpw_tputs(const char *str, int affcnt) {
if (str_tputs)
mpw_free( &str_tputs, str_tputs_max );
str_tputs = calloc( str_tputs_max, sizeof( char ) );
str_tputs_cursor = -1;
char *result = tputs( str, affcnt, mpw_tputc ) == ERR? NULL: strndup( str_tputs, str_tputs_max );
if (str_tputs)
mpw_free( &str_tputs, str_tputs_max );
return result;
}
#endif #endif
const char *mpw_identicon(const char *fullName, const char *masterPassword) { const char *mpw_identicon(const char *fullName, const char *masterPassword) {
@@ -377,23 +502,20 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) {
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
}; };
const uint8_t *identiconSeed = mpw_hash_hmac_sha256( (const uint8_t *)masterPassword, strlen( masterPassword ), const uint8_t *identiconSeed = mpw_hash_hmac_sha256(
(const uint8_t *)fullName, (const uint8_t *)masterPassword, strlen( masterPassword ),
strlen( fullName ) ); (const uint8_t *)fullName, strlen( fullName ) );
if (!identiconSeed) if (!identiconSeed)
return NULL; return NULL;
char *colorString, *resetString; char *colorString, *resetString;
#ifdef COLOR #ifdef MPW_COLOR
if (initputvar()) { if (mpw_setupterm()) {
uint8_t colorIdentifier = (uint8_t)(identiconSeed[4] % 7 + 1); uint8_t colorIdentifier = (uint8_t)(identiconSeed[4] % 7 + 1);
tputs(tparm(tgetstr("AF", NULL), colorIdentifier), 1, putvar); colorString = mpw_tputs( tparm( tgetstr( "AF", NULL ), colorIdentifier ), 1 );
colorString = calloc(strlen(putvarc) + 1, sizeof(char)); resetString = mpw_tputs( tgetstr( "me", NULL ), 1 );
strcpy(colorString, putvarc); }
tputs(tgetstr("me", NULL), 1, putvar); else
resetString = calloc(strlen(putvarc) + 1, sizeof(char));
strcpy(resetString, putvarc);
} else
#endif #endif
{ {
colorString = calloc( 1, sizeof( char ) ); colorString = calloc( 1, sizeof( char ) );
@@ -409,9 +531,8 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) {
accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( accessory[0] ))], accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( accessory[0] ))],
resetString ); resetString );
mpw_free( identiconSeed, 32 ); mpw_free( &identiconSeed, 32 );
free( colorString ); mpw_free_strings( &colorString, &resetString, NULL );
free( resetString );
return identicon; return identicon;
} }

View File

@@ -31,42 +31,42 @@ extern int mpw_verbosity;
#define trc_level 3 #define trc_level 3
/** Logging internal state. */ /** Logging internal state. */
#define trc(...) ({ \ #define trc(...) ({ \
if (mpw_verbosity >= 3) \ if (mpw_verbosity >= trc_level) \
fprintf( stderr, __VA_ARGS__ ); }) fprintf( stderr, __VA_ARGS__ ); })
#endif #endif
#ifndef dbg #ifndef dbg
#define dbg_level 2 #define dbg_level 2
/** Logging state and events interesting when investigating issues. */ /** Logging state and events interesting when investigating issues. */
#define dbg(...) ({ \ #define dbg(...) ({ \
if (mpw_verbosity >= 2) \ if (mpw_verbosity >= dbg_level) \
fprintf( stderr, __VA_ARGS__ ); }) fprintf( stderr, __VA_ARGS__ ); })
#endif #endif
#ifndef inf #ifndef inf
#define inf_level 1 #define inf_level 1
/** User messages. */ /** User messages. */
#define inf(...) ({ \ #define inf(...) ({ \
if (mpw_verbosity >= 1) \ if (mpw_verbosity >= inf_level) \
fprintf( stderr, __VA_ARGS__ ); }) fprintf( stderr, __VA_ARGS__ ); })
#endif #endif
#ifndef wrn #ifndef wrn
#define wrn_level 0 #define wrn_level 0
/** Recoverable issues and user suggestions. */ /** Recoverable issues and user suggestions. */
#define wrn(...) ({ \ #define wrn(...) ({ \
if (mpw_verbosity >= 0) \ if (mpw_verbosity >= wrn_level) \
fprintf( stderr, __VA_ARGS__ ); }) fprintf( stderr, __VA_ARGS__ ); })
#endif #endif
#ifndef err #ifndef err
#define err_level -1 #define err_level -1
/** Unrecoverable issues. */ /** Unrecoverable issues. */
#define err(...) ({ \ #define err(...) ({ \
if (mpw_verbosity >= -1) \ if (mpw_verbosity >= err_level) \
fprintf( stderr, __VA_ARGS__ ); }) fprintf( stderr, __VA_ARGS__ ); })
#endif #endif
#ifndef ftl #ifndef ftl
#define ftl_level -2 #define ftl_level -2
/** Issues that lead to abortion. */ /** Issues that lead to abortion. */
#define ftl(...) ({ \ #define ftl(...) ({ \
if (mpw_verbosity >= -2) \ if (mpw_verbosity >= ftl_level) \
fprintf( stderr, __VA_ARGS__ ); }) fprintf( stderr, __VA_ARGS__ ); })
#endif #endif
@@ -85,6 +85,9 @@ extern int mpw_verbosity;
#ifndef ERR #ifndef ERR
#define ERR -1 #define ERR -1
#endif #endif
#ifndef OK
#define OK 0
#endif
#ifndef stringify #ifndef stringify
#define stringify(s) #s #define stringify(s) #s
#endif #endif
@@ -94,6 +97,11 @@ extern int mpw_verbosity;
//// Buffers and memory. //// Buffers and memory.
/** Write a number to a byte buffer using mpw's endianness (big/network endian). */
void mpw_uint16(const uint16_t number, uint8_t buf[2]);
void mpw_uint32(const uint32_t number, uint8_t buf[4]);
void mpw_uint64(const uint64_t number, uint8_t buf[8]);
/** Allocate a new array of _type, assign its element count to _count if not NULL and populate it with the varargs. */ /** Allocate a new array of _type, assign its element count to _count if not NULL and populate it with the varargs. */
#define mpw_alloc_array(_count, _type, ...) ({ \ #define mpw_alloc_array(_count, _type, ...) ({ \
_type stackElements[] = { __VA_ARGS__ }; \ _type stackElements[] = { __VA_ARGS__ }; \
@@ -106,18 +114,18 @@ extern int mpw_verbosity;
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */ /** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
bool mpw_push_buf( bool mpw_push_buf(
uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize); uint8_t **buffer, size_t *bufferSize, const void *pushBuffer, const size_t pushSize);
/** Push a string onto a buffer. reallocs the given buffer and appends the given string. */ /** Push a string onto a buffer. reallocs the given buffer and appends the given string. */
bool mpw_push_string( bool mpw_push_string(
uint8_t **buffer, size_t *const bufferSize, const char *pushString); uint8_t **buffer, size_t *bufferSize, const char *pushString);
/** Push a string onto another string. reallocs the target string and appends the source string. */ /** Push a string onto another string. reallocs the target string and appends the source string. */
bool mpw_string_push( bool mpw_string_push(
char **const string, const char *pushString); char **string, const char *pushString);
bool mpw_string_pushf( bool mpw_string_pushf(
char **const string, const char *pushFormat, ...); char **string, const char *pushFormat, ...);
/** Push an integer onto a buffer. reallocs the given buffer and appends the given integer. */ /** Push an integer onto a buffer. reallocs the given buffer and appends the given integer. */
bool mpw_push_int( bool mpw_push_int(
uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt); uint8_t **buffer, size_t *bufferSize, const uint32_t pushInt);
/** Reallocate the given buffer from the given size by adding the delta size. /** Reallocate the given buffer from the given size by adding the delta size.
* On success, the buffer size pointer will be updated to the buffer's new size * On success, the buffer size pointer will be updated to the buffer's new size
* and the buffer pointer may be updated to a new memory address. * and the buffer pointer may be updated to a new memory address.
@@ -128,14 +136,25 @@ bool mpw_push_int(
* @return true if successful, false if reallocation failed. * @return true if successful, false if reallocation failed.
*/ */
#define mpw_realloc(buffer, bufferSize, deltaSize) \ #define mpw_realloc(buffer, bufferSize, deltaSize) \
__mpw_realloc( (void **)buffer, bufferSize, deltaSize ) ({ __typeof__(buffer) _b = buffer; const void *__b = *_b; (void)__b; __mpw_realloc( (const void **)_b, bufferSize, deltaSize ); })
bool __mpw_realloc(void **buffer, size_t *bufferSize, const size_t deltaSize); bool __mpw_realloc(const void **buffer, size_t *bufferSize, const size_t deltaSize);
/** Free a buffer after zero'ing its contents. */ void mpw_zero(
bool mpw_free( void *buffer, size_t bufferSize);
const void *buffer, const size_t bufferSize); /** Free a buffer after zero'ing its contents, then set the reference to NULL. */
/** Free a string after zero'ing its contents. */ #define mpw_free(buffer, bufferSize) \
bool mpw_free_string( ({ __typeof__(buffer) _b = buffer; const void *__b = *_b; (void)__b; __mpw_free( (void **)_b, bufferSize ); })
const char *string); bool __mpw_free(
void **buffer, size_t bufferSize);
/** Free a string after zero'ing its contents, then set the reference to NULL. */
#define mpw_free_string(string) \
({ __typeof__(string) _s = string; const char *__s = *_s; (void)__s; __mpw_free_string( (char **)_s ); })
bool __mpw_free_string(
char **string);
/** Free strings after zero'ing their contents, then set the references to NULL. Terminate the va_list with NULL. */
#define mpw_free_strings(strings, ...) \
({ __typeof__(strings) _s = strings; const char *__s = *_s; (void)__s; __mpw_free_strings( (char **)_s, __VA_ARGS__ ); })
bool __mpw_free_strings(
char **strings, ...);
//// Cryptographic functions. //// Cryptographic functions.
@@ -156,17 +175,24 @@ uint8_t const *mpw_hash_hmac_sha256(
/** Encrypt a plainBuf with the given key using AES-128-CBC. /** Encrypt a plainBuf with the given key using AES-128-CBC.
* @return A new bufSize allocated buffer containing the cipherBuf. */ * @return A new bufSize allocated buffer containing the cipherBuf. */
uint8_t const *mpw_aes_encrypt( uint8_t const *mpw_aes_encrypt(
const uint8_t *key, const size_t keySize, const uint8_t *plainBuf, const size_t bufSize); const uint8_t *key, const size_t keySize, const uint8_t *plainBuf, size_t *bufSize);
/** Decrypt a cipherBuf with the given key using AES-128-CBC. /** Decrypt a cipherBuf with the given key using AES-128-CBC.
* @return A new bufSize allocated buffer containing the plainBuf. */ * @return A new bufSize allocated buffer containing the plainBuf. */
uint8_t const *mpw_aes_decrypt( uint8_t const *mpw_aes_decrypt(
const uint8_t *key, const size_t keySize, const uint8_t *cipherBuf, const size_t bufSize); const uint8_t *key, const size_t keySize, const uint8_t *cipherBuf, size_t *bufSize);
/** Calculate an OTP using RFC-4226.
* @return A newly allocated string containing exactly `digits` decimal OTP digits. */
#if UNUSED
const char *mpw_hotp(
const uint8_t *key, size_t keySize, uint64_t movingFactor, uint8_t digits, uint8_t truncationOffset);
#endif
//// Visualizers. //// Visualizers.
/** Compose a formatted string. /** Compose a formatted string.
* @return A C-string in a reused buffer, do not free or store it. */ * @return A C-string in a reused buffer, do not free or store it. */
const char *mpw_str(const char *format, ...); const char *mpw_str(const char *format, ...);
const char *mpw_vstr(const char *format, va_list args);
/** Encode a buffer as a string of hexadecimal characters. /** Encode a buffer as a string of hexadecimal characters.
* @return A C-string in a reused buffer, do not free or store it. */ * @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(const void *buf, size_t length);

View File

@@ -5,9 +5,10 @@ plugins {
description = 'Master Password Algorithm Implementation' description = 'Master Password Algorithm Implementation'
dependencies { dependencies {
compile (group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.6-p10') { compile (group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.6-p11') {
exclude( module: 'joda-time' ) exclude( module: 'joda-time' )
} }
compile group: 'com.lyndir.lhunath.opal', name: 'opal-crypto', version: '1.6-p11'
compile group: 'com.lambdaworks', name: 'scrypt', version: '1.4.0' compile group: 'com.lambdaworks', name: 'scrypt', version: '1.4.0'
compile group: 'org.jetbrains', name: 'annotations', version: '13.0' compile group: 'org.jetbrains', name: 'annotations', version: '13.0'

View File

@@ -23,7 +23,7 @@
<dependency> <dependency>
<groupId>com.lyndir.lhunath.opal</groupId> <groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-system</artifactId> <artifactId>opal-system</artifactId>
<version>1.6-p9</version> <version>1.6-p11</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>joda-time</groupId> <groupId>joda-time</groupId>
@@ -31,6 +31,11 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-crypto</artifactId>
<version>1.6-p11</version>
</dependency>
<!-- EXTERNAL DEPENDENCIES --> <!-- EXTERNAL DEPENDENCIES -->
<dependency> <dependency>

View File

@@ -0,0 +1,99 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import com.google.common.base.Charsets;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests;
import com.lyndir.lhunath.opal.system.MessageDigests;
import java.io.Serializable;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import javax.annotation.Nullable;
/**
* @see MPMasterKey.Version
*/
public interface MPAlgorithm {
/**
* mpw: validity for the time-based rolling counter.
*/
int mpw_otp_window = 5 * 60 /* s */;
/**
* mpw: Key ID hash.
*/
MessageDigests mpw_hash = MessageDigests.SHA256;
/**
* mpw: Site digest.
*/
MessageAuthenticationDigests mpw_digest = MessageAuthenticationDigests.HmacSHA256;
/**
* mpw: Platform-agnostic byte order.
*/
ByteOrder mpw_byteOrder = ByteOrder.BIG_ENDIAN;
/**
* mpw: Input character encoding.
*/
Charset mpw_charset = Charsets.UTF_8;
/**
* mpw: Master key size (byte).
*/
int mpw_dkLen = 64;
/**
* scrypt: Parallelization parameter.
*/
int scrypt_p = 2;
/**
* scrypt: Memory cost parameter.
*/
int scrypt_r = 8;
/**
* scrypt: CPU cost parameter.
*/
int scrypt_N = 32768;
MPMasterKey.Version getAlgorithmVersion();
byte[] masterKey(String fullName, char[] masterPassword);
byte[] siteKey(byte[] masterKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
@Nullable String keyContext);
String siteResult(byte[] masterKey, final byte[] siteKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
@Nullable String keyContext, MPResultType resultType, @Nullable String resultParam);
String sitePasswordFromTemplate(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
String sitePasswordFromCrypt(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
String sitePasswordFromDerive(byte[] masterKey, byte[] siteKey, MPResultType resultType, @Nullable String resultParam);
String siteState(byte[] masterKey, final byte[] siteKey, String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose,
@Nullable String keyContext, MPResultType resultType, String resultParam);
}

View File

@@ -0,0 +1,248 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import static com.lyndir.masterpassword.MPUtils.*;
import com.google.common.base.*;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.UnsignedInteger;
import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.crypto.CryptUtils;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import java.nio.*;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.annotation.Nullable;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
/**
* @author lhunath, 2014-08-30
* @see MPMasterKey.Version#V0
*/
public class MPAlgorithmV0 implements MPAlgorithm {
protected final Logger logger = Logger.get( getClass() );
@Override
public MPMasterKey.Version getAlgorithmVersion() {
return MPMasterKey.Version.V0;
}
@Override
public byte[] masterKey(final String fullName, final char[] masterPassword) {
byte[] fullNameBytes = fullName.getBytes( mpw_charset );
byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
String keyScope = MPKeyPurpose.Authentication.getScope();
logger.trc( "keyScope: %s", keyScope );
// Calculate the master key salt.
logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName );
byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( mpw_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
// Calculate the master key.
logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%d, r=%d, p=%d )",
scrypt_N, scrypt_r, scrypt_p );
byte[] masterPasswordBytes = bytesForChars( masterPassword );
byte[] masterKey = scrypt( masterKeySalt, masterPasswordBytes );
Arrays.fill( masterKeySalt, (byte) 0 );
Arrays.fill( masterPasswordBytes, (byte) 0 );
logger.trc( " => masterKey.id: %s", CodeUtils.encodeHex( idForBytes( masterKey ) ) );
return masterKey;
}
protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) {
try {
//if (isAllowNative())
return SCrypt.scrypt( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen );
//else
// return SCrypt.scryptJ( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen );
}
catch (final GeneralSecurityException e) {
throw logger.bug( e );
}
}
@Override
public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext) {
String keyScope = keyPurpose.getScope();
logger.trc( "keyScope: %s", keyScope );
// OTP counter value.
if (siteCounter.longValue() == 0)
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (mpw_otp_window * 1000)) * mpw_otp_window );
// Calculate the site seed.
byte[] siteNameBytes = siteName.getBytes( mpw_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( mpw_charset );
byte[] keyContextLengthBytes = (keyContextBytes == null)? null: bytesForInt( keyContextBytes.length );
logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
(keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (keyContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", CodeUtils.encodeHex( idForBytes( masterKey ) ) );
byte[] sitePasswordSeedBytes = mpw_digest.of( masterKey, sitePasswordInfo );
logger.trc( " => siteKey.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
return sitePasswordSeedBytes;
}
@Override
public String siteResult(final byte[] masterKey, final byte[] siteKey, final String siteName, final UnsignedInteger siteCounter,
final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) {
switch (resultType.getTypeClass()) {
case Template:
return sitePasswordFromTemplate( masterKey, siteKey, resultType, resultParam );
case Stateful:
return sitePasswordFromCrypt( masterKey, siteKey, resultType, resultParam );
case Derive:
return sitePasswordFromDerive( masterKey, siteKey, resultType, resultParam );
}
throw logger.bug( "Unsupported result type class: %s", resultType.getTypeClass() );
}
@Override
public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType,
@Nullable final String resultParam) {
int[] _siteKey = new int[siteKey.length];
for (int i = 0; i < siteKey.length; ++i) {
ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( mpw_byteOrder );
Arrays.fill( buf.array(), (byte) ((siteKey[i] > 0)? 0x00: 0xFF) );
buf.position( 2 );
buf.put( siteKey[i] ).rewind();
_siteKey[i] = buf.getInt() & 0xFFFF;
}
// Determine the template.
Preconditions.checkState( _siteKey.length > 0 );
int templateIndex = _siteKey[0];
MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "template: %d => %s", templateIndex, template.getTemplateString() );
// Encode the password from the seed using the template.
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = _siteKey[i + 1];
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( " - class: %c, index: %5d (0x%2H) => character: %c",
characterClass.getIdentifier(), characterIndex, _siteKey[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
logger.trc( " => password: %s", password );
return password.toString();
}
@Override
public String sitePasswordFromCrypt(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType,
@Nullable final String resultParam) {
Preconditions.checkNotNull( resultParam );
Preconditions.checkArgument( !resultParam.isEmpty() );
try {
// Base64-decode
byte[] cipherBuf = CryptUtils.decodeBase64( resultParam );
logger.trc( "b64 decoded: %d bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
// Decrypt
byte[] plainBuf = CryptUtils.decrypt( cipherBuf, masterKey, true );
String plainText = mpw_charset.decode( ByteBuffer.wrap( plainBuf ) ).toString();
logger.trc( "decrypted -> plainText: %d bytes = %s = %s", plainBuf.length, plainText, CodeUtils.encodeHex( plainBuf ) );
return plainText;
}
catch (final BadPaddingException e) {
throw Throwables.propagate( e );
}
}
@Override
public String sitePasswordFromDerive(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType,
@Nullable final String resultParam) {
if (resultType == MPResultType.DeriveKey) {
int resultParamInt = ConversionUtils.toIntegerNN( resultParam );
if (resultParamInt == 0)
resultParamInt = 512;
if ((resultParamInt < 128) || (resultParamInt > 512) || ((resultParamInt % 8) != 0))
throw logger.bug( "Parameter is not a valid key size (should be 128 - 512): %s", resultParam );
int keySize = resultParamInt / 8;
logger.trc( "keySize: %d", keySize );
// Derive key
byte[] resultKey = null; // TODO: mpw_kdf_blake2b( keySize, siteKey, MPSiteKeySize, NULL, 0, 0, NULL );
if (resultKey == null)
throw logger.bug( "Could not derive result key." );
// Base64-encode
String b64Key = Verify.verifyNotNull( CryptUtils.encodeBase64( resultKey ) );
logger.trc( "b64 encoded -> key: %s", b64Key );
return b64Key;
} else
throw logger.bug( "Unsupported derived password type: %s", resultType );
}
@Override
public String siteState(final byte[] masterKey, final byte[] siteKey, final String siteName, final UnsignedInteger siteCounter,
final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final MPResultType resultType, final String resultParam) {
try {
// Encrypt
byte[] cipherBuf = CryptUtils.encrypt( resultParam.getBytes( mpw_charset ), masterKey, true );
logger.trc( "cipherBuf: %d bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) );
// Base64-encode
String cipherText = Verify.verifyNotNull( CryptUtils.encodeBase64( cipherBuf ) );
logger.trc( "b64 encoded -> cipherText: %s", cipherText );
return cipherText;
}
catch (final IllegalBlockSizeException e) {
throw logger.bug( e );
}
}
}

View File

@@ -0,0 +1,62 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
import javax.annotation.Nullable;
/**
* @see MPMasterKey.Version#V1
*
* @author lhunath, 2014-08-30
*/
public class MPAlgorithmV1 extends MPAlgorithmV0 {
@Override
public MPMasterKey.Version getAlgorithmVersion() {
return MPMasterKey.Version.V1;
}
@Override
public String sitePasswordFromTemplate(final byte[] masterKey, final byte[] siteKey, final MPResultType resultType, @Nullable final String resultParam) {
// Determine the template.
Preconditions.checkState( siteKey.length > 0 );
int templateIndex = siteKey[0] & 0xFF; // Convert to unsigned int.
MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "template: %d => %s", templateIndex, template.getTemplateString() );
// Encode the password from the seed using the template.
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = siteKey[i + 1] & 0xFF; // Convert to unsigned int.
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( " - class: %c, index: %3d (0x%2H) => character: %c",
characterClass.getIdentifier(), characterIndex, siteKey[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
logger.trc( " => password: %s", password );
return password.toString();
}
}

View File

@@ -0,0 +1,74 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import static com.lyndir.masterpassword.MPUtils.*;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
import javax.annotation.Nullable;
/**
* @see MPMasterKey.Version#V2
*
* @author lhunath, 2014-08-30
*/
public class MPAlgorithmV2 extends MPAlgorithmV1 {
@Override
public MPMasterKey.Version getAlgorithmVersion() {
return MPMasterKey.Version.V2;
}
@Override
public byte[] siteKey(final byte[] masterKey, final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext) {
String keyScope = keyPurpose.getScope();
logger.trc( "keyScope: %s", keyScope );
// OTP counter value.
if (siteCounter.longValue() == 0)
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPAlgorithm.mpw_otp_window * 1000)) * MPAlgorithm.mpw_otp_window );
// Calculate the site seed.
byte[] siteNameBytes = siteName.getBytes( MPAlgorithm.mpw_charset );
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( MPAlgorithm.mpw_charset );
byte[] keyContextLengthBytes = (keyContextBytes == null)? null: bytesForInt( keyContextBytes.length );
logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ),
(keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext );
byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( MPAlgorithm.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (keyContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes );
logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", CodeUtils.encodeHex( idForBytes( masterKey ) ) );
byte[] sitePasswordSeedBytes = MPAlgorithm.mpw_digest.of( masterKey, sitePasswordInfo );
logger.trc( " => siteKey.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
return sitePasswordSeedBytes;
}
}

View File

@@ -0,0 +1,67 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import static com.lyndir.masterpassword.MPUtils.*;
import com.google.common.primitives.Bytes;
import com.lyndir.lhunath.opal.system.CodeUtils;
import java.util.Arrays;
/**
* @see MPMasterKey.Version#V3
*
* @author lhunath, 2014-08-30
*/
public class MPAlgorithmV3 extends MPAlgorithmV2 {
@Override
public MPMasterKey.Version getAlgorithmVersion() {
return MPMasterKey.Version.V3;
}
@Override
public byte[] masterKey(final String fullName, final char[] masterPassword) {
byte[] fullNameBytes = fullName.getBytes( MPAlgorithm.mpw_charset );
byte[] fullNameLengthBytes = MPUtils.bytesForInt( fullNameBytes.length );
String keyScope = MPKeyPurpose.Authentication.getScope();
logger.trc( "keyScope: %s", keyScope );
// Calculate the master key salt.
logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName );
byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( MPAlgorithm.mpw_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
// Calculate the master key.
logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%d, r=%d, p=%d )",
MPAlgorithm.scrypt_N, MPAlgorithm.scrypt_r, MPAlgorithm.scrypt_p );
byte[] mpBytes = bytesForChars( masterPassword );
byte[] masterKey = scrypt( masterKeySalt, mpBytes );
Arrays.fill( masterKeySalt, (byte) 0 );
Arrays.fill( mpBytes, (byte) 0 );
logger.trc( " => masterKey.id: %s", CodeUtils.encodeHex( idForBytes( masterKey ) ) );
return masterKey;
}
}

View File

@@ -18,11 +18,8 @@
package com.lyndir.masterpassword; package com.lyndir.masterpassword;
import com.google.common.base.Charsets; import org.joda.time.format.DateTimeFormatter;
import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests; import org.joda.time.format.ISODateTimeFormat;
import com.lyndir.lhunath.opal.system.MessageDigests;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
/** /**
@@ -32,68 +29,18 @@ public final class MPConstant {
/* Environment */ /* 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. * mpw: default path to look for run configuration files if the platform default is not desired.
*/ */
public static final String env_rcDir = "MP_RCDIR"; public static final String env_rcDir = "MPW_RCDIR";
/** /**
* mpw: permit automatic update checks. * mpw: permit automatic update checks.
*/ */
public static final String env_checkUpdates = "MP_CHECKUPDATES"; public static final String env_checkUpdates = "MPW_CHECKUPDATES";
/* Algorithm */ /* Algorithm */
/**
* scrypt: CPU cost parameter.
*/
public static final int scrypt_N = 32768;
/**
* scrypt: Memory cost parameter.
*/
public static final int scrypt_r = 8;
/**
* scrypt: Parallelization parameter.
*/
public static final int scrypt_p = 2;
/**
* mpw: Master key size (byte).
*/
public static final int mpw_dkLen = 64;
/**
* mpw: Input character encoding.
*/
public static final Charset mpw_charset = Charsets.UTF_8;
/**
* mpw: Platform-agnostic byte order.
*/
public static final ByteOrder mpw_byteOrder = ByteOrder.BIG_ENDIAN;
/**
* mpw: Site digest.
*/
public static final MessageAuthenticationDigests mpw_digest = MessageAuthenticationDigests.HmacSHA256;
/**
* mpw: Key ID hash.
*/
public static final MessageDigests mpw_hash = MessageDigests.SHA256;
/**
* mpw: validity for the time-based rolling counter.
*/
public static final int mpw_counter_timeout = 5 * 60 /* s */;
public static final int MS_PER_S = 1000; public static final int MS_PER_S = 1000;
public static final DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTimeNoMillis();
} }

View File

@@ -16,13 +16,10 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>. // LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//============================================================================== //==============================================================================
/**
*
* @author lhunath, 15-02-04
*/
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword; package com.lyndir.masterpassword;
import javax.annotation.ParametersAreNonnullByDefault; /**
* @author lhunath, 2017-09-21
*/
public class MPInvalidatedException extends Exception {
}

View File

@@ -0,0 +1,97 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.Locale;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
/**
* @author lhunath, 14-12-02
*/
public enum MPKeyPurpose {
/**
* Generate a key for authentication.
*/
Authentication( "authentication", "Generate a key for authentication.", "com.lyndir.masterpassword" ),
/**
* Generate a name for identification.
*/
Identification( "identification", "Generate a name for identification.", "com.lyndir.masterpassword.login" ),
/**
* Generate a recovery token.
*/
Recovery( "recovery", "Generate a recovery token.", "com.lyndir.masterpassword.answer" );
static final Logger logger = Logger.get( MPResultType.class );
private final String shortName;
private final String description;
private final String scope;
MPKeyPurpose(final String shortName, final String description, @NonNls final String scope) {
this.shortName = shortName;
this.description = description;
this.scope = scope;
}
public String getShortName() {
return shortName;
}
public String getDescription() {
return description;
}
public String getScope() {
return scope;
}
/**
* @param shortNamePrefix The name for the purpose to look up. It is a case insensitive prefix of the purpose's short name.
*
* @return The purpose registered with the given name.
*/
@Nullable
@Contract("!null -> !null")
public static MPKeyPurpose forName(@Nullable final String shortNamePrefix) {
if (shortNamePrefix == null)
return null;
for (final MPKeyPurpose type : values())
if (type.getShortName().toLowerCase( Locale.ROOT ).startsWith( shortNamePrefix.toLowerCase( Locale.ROOT ) ))
return type;
throw logger.bug( "No purpose for name: %s", shortNamePrefix );
}
public static MPKeyPurpose forInt(final int keyPurpose) {
return values()[keyPurpose];
}
public int toInt() {
return ordinal();
}
}

View File

@@ -0,0 +1,246 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import static com.lyndir.masterpassword.MPUtils.*;
import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.Arrays;
import java.util.EnumMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* @author lhunath, 2014-08-30
*/
public class MPMasterKey {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPMasterKey.class );
private final EnumMap<Version, byte[]> keyByVersion = new EnumMap<>( Version.class );
private final String fullName;
private final char[] masterPassword;
private boolean invalidated;
/**
* @param masterPassword The characters of the user's master password. Note: this array is held by reference and its contents
* invalidated on {@link #invalidate()}.
*/
@SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
public MPMasterKey(final String fullName, final char[] masterPassword) {
this.fullName = fullName;
this.masterPassword = masterPassword;
}
/**
* Derive the master key for a user based on their name and master password.
*
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
*/
private byte[] masterKey(final Version algorithmVersion)
throws MPInvalidatedException {
Preconditions.checkArgument( masterPassword.length > 0 );
if (invalidated)
throw new MPInvalidatedException();
byte[] key = keyByVersion.get( algorithmVersion );
if (key == null) {
logger.trc( "-- mpw_masterKey (algorithm: %d)", algorithmVersion.toInt() );
logger.trc( "fullName: %s", fullName );
logger.trc( "masterPassword.id: %s", CodeUtils.encodeHex( idForBytes( bytesForChars( masterPassword ) ) ) );
keyByVersion.put( algorithmVersion, key = algorithmVersion.getAlgorithm().masterKey( fullName, masterPassword ) );
}
return key;
}
/**
* Derive the master key for a user based on their name and master password.
*
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
*/
private byte[] siteKey(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final Version algorithmVersion)
throws MPInvalidatedException {
Preconditions.checkArgument( !siteName.isEmpty() );
byte[] masterKey = masterKey( algorithmVersion );
logger.trc( "-- mpw_siteKey (algorithm: %d)", algorithmVersion.toInt() );
logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %s", siteCounter );
logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() );
logger.trc( "keyContext: %s", keyContext );
return algorithmVersion.getAlgorithm().siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext );
}
/**
* Generate a site result token.
*
* @param siteName A site identifier.
* @param siteCounter The result identifier.
* @param keyPurpose The intended purpose for this site result.
* @param keyContext A site-scoped result modifier.
* @param resultType The type of result to generate.
* @param resultParam A parameter for the resultType. For stateful result types, the output of
* {@link #siteState(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, Version)}.
*
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
*/
public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
final Version algorithmVersion)
throws MPInvalidatedException {
byte[] masterKey = masterKey( algorithmVersion );
byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithmVersion );
logger.trc( "-- mpw_siteResult (algorithm: %d)", algorithmVersion.toInt() );
logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() );
logger.trc( "resultParam: %s", resultParam );
return algorithmVersion.getAlgorithm().siteResult(
masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
}
/**
* Encrypt a stateful site token for persistence.
*
* @param siteName A site identifier.
* @param siteCounter The result identifier.
* @param keyPurpose The intended purpose for the site token.
* @param keyContext A site-scoped key modifier.
* @param resultType The type of result token to encrypt.
* @param resultParam The result token desired from
* {@link #siteResult(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String, Version)}.
*
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
*/
public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose,
@Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam,
final Version algorithmVersion)
throws MPInvalidatedException {
Preconditions.checkNotNull( resultParam );
Preconditions.checkArgument( !resultParam.isEmpty() );
byte[] masterKey = masterKey( algorithmVersion );
byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext, algorithmVersion );
logger.trc( "-- mpw_siteState (algorithm: %d)", algorithmVersion.toInt() );
logger.trc( "resultType: %d (%s)", resultType.getType(), resultType.getShortName() );
logger.trc( "resultParam: %d bytes = %s", resultParam.getBytes( MPAlgorithm.mpw_charset ).length, resultParam );
return algorithmVersion.getAlgorithm().siteState(
masterKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
}
@Nonnull
public String getFullName() {
return fullName;
}
/**
* Calculate an identifier for the master key.
*
* @throws MPInvalidatedException {@link #invalidate()} has been called on this object.
*/
public byte[] getKeyID(final Version algorithmVersion)
throws MPInvalidatedException {
return idForBytes( masterKey( algorithmVersion ) );
}
/**
* Wipe this key's secrets from memory, making the object permanently unusable.
*/
public void invalidate() {
invalidated = true;
for (final byte[] key : keyByVersion.values())
Arrays.fill( key, (byte) 0 );
Arrays.fill( masterPassword, (char) 0 );
}
/**
* The algorithm iterations.
*/
public enum Version {
/**
* bugs:
* - does math with chars whose signedness was platform-dependent.
* - miscounted the byte-length for multi-byte site names.
* - miscounted the byte-length for multi-byte user names.
*/
V0( new MPAlgorithmV0() ),
/**
* bugs:
* - miscounted the byte-length for multi-byte site names.
* - miscounted the byte-length for multi-byte user names.
*/
V1( new MPAlgorithmV1() ),
/**
* bugs:
* - miscounted the byte-length for multi-byte user names.
*/
V2( new MPAlgorithmV2() ),
/**
* bugs:
* - no known issues.
*/
V3( new MPAlgorithmV3() );
public static final Version CURRENT = V3;
private final MPAlgorithm algorithm;
Version(final MPAlgorithm algorithm) {
this.algorithm = algorithm;
}
public MPAlgorithm getAlgorithm() {
return algorithm;
}
public static Version fromInt(final int algorithmVersion) {
return values()[algorithmVersion];
}
public int toInt() {
return ordinal();
}
}
}

View File

@@ -24,7 +24,6 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.*; import java.util.*;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
/** /**
@@ -32,15 +31,21 @@ import org.jetbrains.annotations.NonNls;
* *
* @author lhunath * @author lhunath
*/ */
public enum MPSiteType { public enum MPResultType {
// bit 0-3 | MPResultTypeClass | MPSiteFeature
GeneratedMaximum( "Max", "20 characters, contains symbols.", // /**
ImmutableList.of( "x", "max", "maximum" ), // NON-NLS * pg^VMAUBk5x3p%HP%i4=
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), // */
MPSiteTypeClass.Generated, 0x0 ), GeneratedMaximum( "maximum", "20 characters, contains symbols.", //
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ),
new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
MPResultTypeClass.Template, 0x0 ),
GeneratedLong( "Long", "Copy-friendly, 14 characters, contains symbols.", // /**
ImmutableList.of( "l", "long" ), // NON-NLS * BiroYena8:Kixa
*/
GeneratedLong( "long", "Copy-friendly, 14 characters, contains symbols.", //
ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ), ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ), new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ),
@@ -52,65 +57,92 @@ public enum MPSiteType {
new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ), new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ),
new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ), new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ),
new MPTemplate( "CvccCvcvCvccno" ) ), // new MPTemplate( "CvccCvcvCvccno" ) ), //
MPSiteTypeClass.Generated, 0x1 ), MPResultTypeClass.Template, 0x1 ),
GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", // /**
ImmutableList.of( "m", "med", "medium" ), // NON-NLS * BirSuj0-
ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), // */
MPSiteTypeClass.Generated, 0x2 ), GeneratedMedium( "medium", "Copy-friendly, 8 characters, contains symbols.", //
ImmutableList.of( new MPTemplate( "CvcnoCvc" ),
new MPTemplate( "CvcCvcno" ) ), //
MPResultTypeClass.Template, 0x2 ),
GeneratedBasic( "Basic", "8 characters, no symbols.", // /**
ImmutableList.of( "b", "basic" ), // NON-NLS * pO98MoD0
ImmutableList.of( new MPTemplate( "aaanaaan" ), new MPTemplate( "aannaaan" ), new MPTemplate( "aaannaaa" ) ), // */
MPSiteTypeClass.Generated, 0x3 ), GeneratedBasic( "basic", "8 characters, no symbols.", //
ImmutableList.of( new MPTemplate( "aaanaaan" ),
new MPTemplate( "aannaaan" ),
new MPTemplate( "aaannaaa" ) ), //
MPResultTypeClass.Template, 0x3 ),
GeneratedShort( "Short", "Copy-friendly, 4 characters, no symbols.", // /**
ImmutableList.of( "s", "short" ), // NON-NLS * Bir8
*/
GeneratedShort( "short", "Copy-friendly, 4 characters, no symbols.", //
ImmutableList.of( new MPTemplate( "Cvcn" ) ), // ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
MPSiteTypeClass.Generated, 0x4 ), MPResultTypeClass.Template, 0x4 ),
GeneratedPIN( "PIN", "4 numbers.", // /**
ImmutableList.of( "i", "pin" ), // NON-NLS * 2798
*/
GeneratedPIN( "pin", "4 numbers.", //
ImmutableList.of( new MPTemplate( "nnnn" ) ), // ImmutableList.of( new MPTemplate( "nnnn" ) ), //
MPSiteTypeClass.Generated, 0x5 ), MPResultTypeClass.Template, 0x5 ),
GeneratedName( "Name", "9 letter name.", // /**
ImmutableList.of( "n", "name" ), // NON-NLS * birsujano
*/
GeneratedName( "name", "9 letter name.", //
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), // ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
MPSiteTypeClass.Generated, 0xE ), MPResultTypeClass.Template, 0xE ),
GeneratedPhrase( "Phrase", "20 character sentence.", // /**
ImmutableList.of( "p", "phrase" ), // NON-NLS * bir yennoquce fefi
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ), */
GeneratedPhrase( "phrase", "20 character sentence.", //
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ),
new MPTemplate( "cvc cvccvcvcv cvcv" ),
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), // new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
MPSiteTypeClass.Generated, 0xF ), MPResultTypeClass.Template, 0xF ),
StoredPersonal( "Personal", "AES-encrypted, exportable.", // /**
ImmutableList.of( "personal" ), // NON-NLS * Custom saved password.
*/
StoredPersonal( "personal", "AES-encrypted, exportable.", //
ImmutableList.<MPTemplate>of(), // ImmutableList.<MPTemplate>of(), //
MPSiteTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ), MPResultTypeClass.Stateful, 0x0, MPSiteFeature.ExportContent ),
StoredDevicePrivate( "Device", "AES-encrypted, not exported.", // /**
ImmutableList.of( "device" ), // NON-NLS * Custom saved password that should not be exported from the device.
*/
StoredDevicePrivate( "device", "AES-encrypted, not exported.", //
ImmutableList.<MPTemplate>of(), // ImmutableList.<MPTemplate>of(), //
MPSiteTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate ); MPResultTypeClass.Stateful, 0x1, MPSiteFeature.DevicePrivate ),
static final Logger logger = Logger.get( MPSiteType.class ); /**
* Derive a unique binary key.
*/
DeriveKey( "key", "Encryption key.", //
ImmutableList.<MPTemplate>of(), //
MPResultTypeClass.Derive, 0x0, MPSiteFeature.Alternative );
public static final MPResultType DEFAULT = GeneratedLong;
static final Logger logger = Logger.get( MPResultType.class );
private final String shortName; private final String shortName;
private final String description; private final String description;
private final List<String> options;
private final List<MPTemplate> templates; private final List<MPTemplate> templates;
private final MPSiteTypeClass typeClass; private final MPResultTypeClass typeClass;
private final int typeIndex; private final int typeIndex;
private final Set<MPSiteFeature> typeFeatures; private final Set<MPSiteFeature> typeFeatures;
MPSiteType(final String shortName, final String description, final List<String> options, final List<MPTemplate> templates, MPResultType(final String shortName, final String description, final List<MPTemplate> templates,
final MPSiteTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) { final MPResultTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) {
this.shortName = shortName; this.shortName = shortName;
this.description = description; this.description = description;
this.options = options;
this.templates = templates; this.templates = templates;
this.typeClass = typeClass; this.typeClass = typeClass;
this.typeIndex = typeIndex; this.typeIndex = typeIndex;
@@ -131,11 +163,7 @@ public enum MPSiteType {
return description; return description;
} }
public List<String> getOptions() { public MPResultTypeClass getTypeClass() {
return options;
}
public MPSiteTypeClass getTypeClass() {
return typeClass; return typeClass;
} }
@@ -154,35 +182,22 @@ public enum MPSiteType {
} }
/** /**
* @param option The option to select a type with. It is matched case insensitively. * @param shortNamePrefix The name for the type to look up. It is a case insensitive prefix of the type's short name.
*
* @return The type registered for the given option.
*/
public static MPSiteType forOption(final String option) {
for (final MPSiteType type : values())
if (type.getOptions().contains( option.toLowerCase( Locale.ROOT ) ))
return type;
throw logger.bug( "No type for option: %s", option );
}
/**
* @param name The name fromInt the type to look up. It is matched case insensitively.
* *
* @return The type registered with the given name. * @return The type registered with the given name.
*/ */
@Nullable
@Contract("!null -> !null") @Contract("!null -> !null")
public static MPSiteType forName(@Nullable final String name) { public static MPResultType forName(@Nullable final String shortNamePrefix) {
if (name == null) if (shortNamePrefix == null)
return null; return null;
for (final MPSiteType type : values()) for (final MPResultType type : values())
if (type.name().equalsIgnoreCase( name )) if (type.getShortName().toLowerCase( Locale.ROOT ).startsWith( shortNamePrefix.toLowerCase( Locale.ROOT ) ))
return type; return type;
throw logger.bug( "No type for name: %s", name ); throw logger.bug( "No type for name: %s", shortNamePrefix );
} }
/** /**
@@ -190,10 +205,10 @@ public enum MPSiteType {
* *
* @return All types that support the given class. * @return All types that support the given class.
*/ */
public static ImmutableList<MPSiteType> forClass(final MPSiteTypeClass typeClass) { public static ImmutableList<MPResultType> forClass(final MPResultTypeClass typeClass) {
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder(); ImmutableList.Builder<MPResultType> types = ImmutableList.builder();
for (final MPSiteType type : values()) for (final MPResultType type : values())
if (type.getTypeClass() == typeClass) if (type.getTypeClass() == typeClass)
types.add( type ); types.add( type );
@@ -205,27 +220,28 @@ public enum MPSiteType {
* *
* @return The type registered with the given type. * @return The type registered with the given type.
*/ */
public static MPSiteType forType(final int type) { public static MPResultType forType(final int type) {
for (final MPSiteType siteType : values()) for (final MPResultType resultType : values())
if (siteType.getType() == type) if (resultType.getType() == type)
return siteType; return resultType;
throw logger.bug( "No type: %s", type ); throw logger.bug( "No type: %s", type );
} }
/** /**
* @param mask The mask for which we look up types. * @param mask The type mask for which we look up types.
* *
* @return All types that support the given mask. * @return All types that support the given mask's class & features.
*/ */
public static ImmutableList<MPSiteType> forMask(final int mask) { public static ImmutableList<MPResultType> forMask(final int mask) {
int typeMask = mask & ~0xF; int typeMask = mask & ~0xF; // Ignore resultType bit 0-3
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
for (final MPSiteType siteType : values()) ImmutableList.Builder<MPResultType> types = ImmutableList.builder();
if (((siteType.getType() & ~0xF) & typeMask) != 0) for (final MPResultType resultType : values())
types.add( siteType ); if (((resultType.getType() & ~0xF) & typeMask) != 0)
types.add( resultType );
return types.build(); return types.build();
} }

View File

@@ -0,0 +1,51 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public enum MPResultTypeClass {
// bit 4 - 9
/**
* Use the site key to generate a password from a template.
*/
Template( 1 << 4 ),
/**
* Use the site key to encrypt and decrypt a stateful entity.
*/
Stateful( 1 << 5 ),
/**
* Use the site key to derive a site-specific object.
*/
Derive( 1 << 6 );
private final int mask;
MPResultTypeClass(final int mask) {
this.mask = mask;
}
public int getMask() {
return mask;
}
}

View File

@@ -24,6 +24,7 @@ package com.lyndir.masterpassword;
* @author lhunath * @author lhunath
*/ */
public enum MPSiteFeature { public enum MPSiteFeature {
// bit 10 - 15
/** /**
* Export the key-protected content data. * Export the key-protected content data.
@@ -33,7 +34,12 @@ public enum MPSiteFeature {
/** /**
* Never export content. * Never export content.
*/ */
DevicePrivate( 1 << 11 ); DevicePrivate( 1 << 11 ),
/**
* Don't use this as the primary authentication result type.
*/
Alternative( 1 << 12 );
MPSiteFeature(final int mask) { MPSiteFeature(final int mask) {
this.mask = mask; this.mask = mask;

View File

@@ -1,103 +0,0 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import com.google.common.collect.ImmutableList;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.List;
import java.util.Locale;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
/**
* @author lhunath, 14-12-02
*/
public enum MPSiteVariant {
Password( "Generate a key for authentication.", "Doesn't currently use a context.", //
ImmutableList.of( "p", "password" ), "com.lyndir.masterpassword" ), // NON-NLS
Login( "Generate a name for identification.", "Doesn't currently use a context.", //
ImmutableList.of( "l", "login" ), "com.lyndir.masterpassword.login" ), // NON-NLS
Answer( "Generate an answer to a security question.", "Empty for a universal site answer or\nthe most significant word(s) of the question.", //
ImmutableList.of( "a", "answer" ), "com.lyndir.masterpassword.answer" ); // NON-NLS
static final Logger logger = Logger.get( MPSiteType.class );
private final String description;
private final String contextDescription;
private final List<String> options;
private final String scope;
MPSiteVariant(final String description, final String contextDescription, final List<String> options, @NonNls final String scope) {
this.contextDescription = contextDescription;
this.options = options;
this.description = description;
this.scope = scope;
}
public String getDescription() {
return description;
}
public String getContextDescription() {
return contextDescription;
}
public List<String> getOptions() {
return options;
}
public String getScope() {
return scope;
}
/**
* @param option The option to select a variant with. It is matched case insensitively.
*
* @return The variant registered for the given option.
*/
public static MPSiteVariant forOption(final String option) {
for (final MPSiteVariant variant : values())
if (variant.getOptions().contains( option.toLowerCase( Locale.ROOT ) ))
return variant;
throw logger.bug( "No variant for option: %s", option );
}
/**
* @param name The name fromInt the variant to look up. It is matched case insensitively.
*
* @return The variant registered with the given name.
*/
@Contract("!null -> !null")
public static MPSiteVariant forName(@Nullable final String name) {
if (name == null)
return null;
for (final MPSiteVariant type : values())
if (type.name().equalsIgnoreCase( name ))
return type;
throw logger.bug( "No variant for name: %s", name );
}
}

View File

@@ -0,0 +1,53 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import com.google.common.primitives.UnsignedInteger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.Arrays;
/**
* @author lhunath, 2017-09-20
*/
public final class MPUtils {
public static byte[] bytesForInt(final int number) {
return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MPAlgorithm.mpw_byteOrder ).putInt( number ).array();
}
public static byte[] bytesForInt(final UnsignedInteger number) {
return ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MPAlgorithm.mpw_byteOrder ).putInt( number.intValue() ).array();
}
public static byte[] bytesForChars(final char[] characters) {
ByteBuffer byteBuffer = MPAlgorithm.mpw_charset.encode( CharBuffer.wrap( characters ) );
byte[] bytes = new byte[byteBuffer.remaining()];
byteBuffer.get( bytes );
Arrays.fill( byteBuffer.array(), (byte) 0 );
return bytes;
}
public static byte[] idForBytes(final byte[] bytes) {
return MPAlgorithm.mpw_hash.of( bytes );
}
}

View File

@@ -1,219 +0,0 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.Arrays;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* @author lhunath, 2014-08-30
*/
public abstract class MasterKey {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKey.class );
private static boolean allowNativeByDefault = true;
@Nonnull
private final String fullName;
private boolean allowNative = allowNativeByDefault;
@Nullable
private byte[] masterKey;
@SuppressWarnings("MethodCanBeVariableArityMethod")
public static MasterKey create(final String fullName, final char[] masterPassword) {
return create( Version.CURRENT, fullName, masterPassword );
}
@Nonnull
@SuppressWarnings("MethodCanBeVariableArityMethod")
public static MasterKey create(final Version version, final String fullName, final char[] masterPassword) {
switch (version) {
case V0:
return new MasterKeyV0( fullName ).revalidate( masterPassword );
case V1:
return new MasterKeyV1( fullName ).revalidate( masterPassword );
case V2:
return new MasterKeyV2( fullName ).revalidate( masterPassword );
case V3:
return new MasterKeyV3( fullName ).revalidate( masterPassword );
}
throw new UnsupportedOperationException( strf( "Unsupported version: %s", version ) );
}
public static boolean isAllowNativeByDefault() {
return allowNativeByDefault;
}
/**
* Native libraries are useful for speeding up the performance of cryptographical functions.
* Sometimes, however, we may prefer to use Java-only code.
* For instance, for auditability / trust or because the native code doesn't work on our CPU/platform.
* <p/>
* This setter affects the default setting for any newly created {@link MasterKey}s.
*
* @param allowNative false to disallow the use of native libraries.
*/
public static void setAllowNativeByDefault(final boolean allowNative) {
allowNativeByDefault = allowNative;
}
protected MasterKey(@Nonnull final String fullName) {
this.fullName = fullName;
logger.trc( "fullName: %s", fullName );
}
@Nullable
@SuppressWarnings("MethodCanBeVariableArityMethod")
protected abstract byte[] deriveKey(char[] masterPassword);
public abstract Version getAlgorithmVersion();
@Nonnull
public String getFullName() {
return fullName;
}
public boolean isAllowNative() {
return allowNative;
}
public MasterKey setAllowNative(final boolean allowNative) {
this.allowNative = allowNative;
return this;
}
@Nonnull
protected byte[] getKey() {
Preconditions.checkState( isValid() );
return Preconditions.checkNotNull( masterKey );
}
public byte[] getKeyID() {
return idForBytes( getKey() );
}
public abstract String encode(@Nonnull String siteName, MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
MPSiteVariant siteVariant, @Nullable String siteContext);
public boolean isValid() {
return masterKey != null;
}
public void invalidate() {
if (masterKey != null) {
Arrays.fill( masterKey, (byte) 0 );
masterKey = null;
}
}
@SuppressWarnings("MethodCanBeVariableArityMethod")
public MasterKey revalidate(final char[] masterPassword) {
invalidate();
logger.trc( "masterPassword: %s", new String( masterPassword ) );
long start = System.currentTimeMillis();
masterKey = deriveKey( masterPassword );
if (masterKey == null)
logger.dbg( "masterKey calculation failed after %.2fs.", (double)(System.currentTimeMillis() - start) / MPConstant.MS_PER_S );
else
logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
(double)(System.currentTimeMillis() - start) / MPConstant.MS_PER_S );
return this;
}
protected abstract byte[] bytesForInt(int number);
protected abstract byte[] bytesForInt(@Nonnull UnsignedInteger number);
protected abstract byte[] idForBytes(byte[] bytes);
public enum Version {
/**
* bugs:
* - does math with chars whose signedness was platform-dependent.
* - miscounted the byte-length fromInt multi-byte site names.
* - miscounted the byte-length fromInt multi-byte full names.
*/
V0,
/**
* bugs:
* - miscounted the byte-length fromInt multi-byte site names.
* - miscounted the byte-length fromInt multi-byte full names.
*/
V1,
/**
* bugs:
* - miscounted the byte-length fromInt multi-byte full names.
*/
V2,
/**
* bugs:
* - no known issues.
*/
V3;
public static final Version CURRENT = V3;
public static Version fromInt(final int algorithmVersion) {
return values()[algorithmVersion];
}
public int toInt() {
return ordinal();
}
public String toBundleVersion() {
switch (this) {
case V0:
return "1.0";
case V1:
return "2.0";
case V2:
return "2.1";
case V3:
return "2.2";
}
throw new UnsupportedOperationException( strf( "Unsupported version: %s", this ) );
}
}
}

View File

@@ -1,170 +0,0 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.UnsignedInteger;
import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.nio.*;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* bugs:
* - V2: miscounted the byte-length fromInt multi-byte full names.
* - V1: miscounted the byte-length fromInt multi-byte site names.
* - V0: does math with chars whose signedness was platform-dependent.
*
* @author lhunath, 2014-08-30
*/
public class MasterKeyV0 extends MasterKey {
private static final int MP_intLen = 32;
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV0.class );
public MasterKeyV0(final String fullName) {
super( fullName );
}
@Override
public Version getAlgorithmVersion() {
return Version.V0;
}
@Nullable
@Override
protected byte[] deriveKey(final char[] masterPassword) {
String fullName = getFullName();
byte[] fullNameBytes = fullName.getBytes( MPConstant.mpw_charset );
byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
String mpKeyScope = MPSiteVariant.Password.getScope();
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( "key scope: %s", mpKeyScope );
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
Arrays.fill( mpBytesBuf.array(), (byte) 0 );
return scrypt( masterKeySalt, mpBytes );
}
@Nullable
protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) {
try {
if (isAllowNative())
return SCrypt.scrypt( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen );
else
return SCrypt.scryptJ( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen );
}
catch (final GeneralSecurityException e) {
logger.bug( e );
return null;
}
finally {
Arrays.fill( mpBytes, (byte) 0 );
}
}
@Override
public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
final MPSiteVariant siteVariant, @Nullable final String siteContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %d", siteCounter.longValue() );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
if (siteCounter.longValue() == 0)
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout );
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset );
byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
(siteContextBytes == null)? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeedBytes = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length];
for (int i = 0; i < sitePasswordSeedBytes.length; ++i) {
ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN );
Arrays.fill( buf.array(), (byte) ((sitePasswordSeedBytes[i] > 0)? 0x00: 0xFF) );
buf.position( 2 );
buf.put( sitePasswordSeedBytes[i] ).rewind();
sitePasswordSeed[i] = buf.getInt() & 0xFFFF;
}
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0];
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1];
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
return password.toString();
}
@Override
protected byte[] bytesForInt(final int number) {
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number ).array();
}
@Override
protected byte[] bytesForInt(@Nonnull final UnsignedInteger number) {
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number.intValue() ).array();
}
@Override
protected byte[] idForBytes(final byte[] bytes) {
return MPConstant.mpw_hash.of( bytes );
}
}

View File

@@ -1,103 +0,0 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* bugs:
* - V2: miscounted the byte-length fromInt multi-byte full names.
* - V1: miscounted the byte-length fromInt multi-byte site names.
*
* @author lhunath, 2014-08-30
*/
public class MasterKeyV1 extends MasterKeyV0 {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV1.class );
public MasterKeyV1(final String fullName) {
super( fullName );
}
@Override
public Version getAlgorithmVersion() {
return Version.V1;
}
@Override
public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
final MPSiteVariant siteVariant, @Nullable final String siteContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %d", siteCounter.longValue() );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
if (siteCounter.longValue() == 0)
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout );
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset );
byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
(siteContextBytes == null)? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
return password.toString();
}
}

View File

@@ -1,102 +0,0 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* bugs:
* - V2: miscounted the byte-length fromInt multi-byte full names.
*
* @author lhunath, 2014-08-30
*/
public class MasterKeyV2 extends MasterKeyV1 {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV2.class );
public MasterKeyV2(final String fullName) {
super( fullName );
}
@Override
public Version getAlgorithmVersion() {
return Version.V2;
}
@Override
public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter,
final MPSiteVariant siteVariant, @Nullable final String siteContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %d", siteCounter.longValue() );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
if (siteCounter.longValue() == 0)
siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout );
String siteScope = siteVariant.getScope();
byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset );
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
byte[] siteCounterBytes = bytesForInt( siteCounter );
byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset );
byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
(siteContextBytes == null)? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
return password.toString();
}
}

View File

@@ -1,69 +0,0 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import com.google.common.primitives.Bytes;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.Arrays;
import javax.annotation.Nullable;
/**
* bugs:
* - no known issues.
*
* @author lhunath, 2014-08-30
*/
public class MasterKeyV3 extends MasterKeyV2 {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV3.class );
public MasterKeyV3(final String fullName) {
super( fullName );
}
@Override
public Version getAlgorithmVersion() {
return Version.V3;
}
@Nullable
@Override
protected byte[] deriveKey(final char[] masterPassword) {
byte[] fullNameBytes = getFullName().getBytes( MPConstant.mpw_charset );
byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length );
String mpKeyScope = MPSiteVariant.Password.getScope();
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( "key scope: %s", mpKeyScope );
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) );
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
Arrays.fill( mpBytesBuf.array(), (byte) 0 );
return scrypt( masterKeySalt, mpBytes );
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,205 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.*;
import javax.annotation.Nullable;
import org.joda.time.Instant;
/**
* @author lhunath, 14-12-05
*/
public class MPFileSite extends MPSite {
private final MPFileUser user;
private String siteName;
@Nullable
private String siteContent;
private UnsignedInteger siteCounter;
private MPResultType resultType;
private MPMasterKey.Version algorithmVersion;
@Nullable
private String loginContent;
@Nullable
private MPResultType loginType;
@Nullable
private String url;
private int uses;
private Instant lastUsed;
public MPFileSite(final MPFileUser user, final String siteName) {
this( user, siteName, DEFAULT_COUNTER, MPResultType.DEFAULT, MPMasterKey.Version.CURRENT );
}
public MPFileSite(final MPFileUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType,
final MPMasterKey.Version algorithmVersion) {
this.user = user;
this.siteName = siteName;
this.siteCounter = siteCounter;
this.resultType = resultType;
this.algorithmVersion = algorithmVersion;
this.lastUsed = new Instant();
}
protected MPFileSite(final MPFileUser user, final String siteName, @Nullable final String siteContent,
final UnsignedInteger siteCounter,
final MPResultType resultType, final MPMasterKey.Version algorithmVersion,
@Nullable final String loginContent, @Nullable final MPResultType loginType,
@Nullable final String url, final int uses, final Instant lastUsed) {
this.user = user;
this.siteName = siteName;
this.siteContent = siteContent;
this.siteCounter = siteCounter;
this.resultType = resultType;
this.algorithmVersion = algorithmVersion;
this.loginContent = loginContent;
this.loginType = loginType;
this.url = url;
this.uses = uses;
this.lastUsed = lastUsed;
}
public String resultFor(final MPMasterKey masterKey)
throws MPInvalidatedException {
return resultFor( masterKey, MPKeyPurpose.Authentication, null );
}
public String resultFor(final MPMasterKey masterKey, final MPKeyPurpose keyPurpose, @Nullable final String keyContext)
throws MPInvalidatedException {
return resultFor( masterKey, keyPurpose, keyContext, getSiteContent() );
}
public String loginFor(final MPMasterKey masterKey)
throws MPInvalidatedException {
if (loginType == null)
loginType = MPResultType.GeneratedName;
return loginFor( masterKey, loginType, loginContent );
}
public MPFileUser getUser() {
return user;
}
@Override
public String getSiteName() {
return siteName;
}
@Override
public void setSiteName(final String siteName) {
this.siteName = siteName;
}
@Nullable
public String getSiteContent() {
return siteContent;
}
public void setSitePassword(final MPMasterKey masterKey, @Nullable final MPResultType resultType, @Nullable final String result)
throws MPInvalidatedException {
this.resultType = resultType;
if (result == null)
this.siteContent = null;
else
this.siteContent = masterKey.siteState(
getSiteName(), getSiteCounter(), MPKeyPurpose.Authentication, null, getResultType(), result, getAlgorithmVersion() );
}
@Override
public UnsignedInteger getSiteCounter() {
return siteCounter;
}
@Override
public void setSiteCounter(final UnsignedInteger siteCounter) {
this.siteCounter = siteCounter;
}
@Override
public MPResultType getResultType() {
return resultType;
}
@Override
public void setResultType(final MPResultType resultType) {
this.resultType = resultType;
}
@Override
public MPMasterKey.Version getAlgorithmVersion() {
return algorithmVersion;
}
@Override
public void setAlgorithmVersion(final MPMasterKey.Version algorithmVersion) {
this.algorithmVersion = algorithmVersion;
}
@Nullable
public MPResultType getLoginType() {
return loginType;
}
@Nullable
public String getLoginContent() {
return loginContent;
}
public void setLoginName(final MPMasterKey masterKey, @Nullable final MPResultType loginType, @Nullable final String result)
throws MPInvalidatedException {
this.loginType = loginType;
if (this.loginType != null)
if (result == null)
this.loginContent = null;
else
this.loginContent = masterKey.siteState(
siteName, DEFAULT_COUNTER, MPKeyPurpose.Identification, null, this.loginType, result, algorithmVersion );
}
@Nullable
public String getUrl() {
return url;
}
public void setUrl(@Nullable final String url) {
this.url = url;
}
public int getUses() {
return uses;
}
public Instant getLastUsed() {
return lastUsed;
}
public void use() {
uses++;
lastUsed = new Instant();
user.use();
}
}

View File

@@ -0,0 +1,172 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import com.google.common.collect.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.*;
import java.util.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.joda.time.*;
/**
* @author lhunath, 14-12-07
*/
public class MPFileUser extends MPUser<MPFileSite> implements Comparable<MPFileUser> {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPFileUser.class );
private final String fullName;
private final Collection<MPFileSite> sites = Sets.newHashSet();
@Nullable
private byte[] keyID;
private MPMasterKey.Version algorithmVersion;
private int avatar;
private MPResultType defaultType;
private ReadableInstant lastUsed;
public MPFileUser(final String fullName) {
this( fullName, null, MPMasterKey.Version.CURRENT );
}
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPMasterKey.Version algorithmVersion) {
this( fullName, keyID, algorithmVersion, 0, MPResultType.DEFAULT, new Instant() );
}
public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPMasterKey.Version algorithmVersion, final int avatar,
final MPResultType defaultType, final ReadableInstant lastUsed) {
this.fullName = fullName;
this.keyID = (keyID == null)? null: keyID.clone();
this.algorithmVersion = algorithmVersion;
this.avatar = avatar;
this.defaultType = defaultType;
this.lastUsed = lastUsed;
}
@Override
public String getFullName() {
return fullName;
}
@Override
public MPMasterKey.Version getAlgorithmVersion() {
return algorithmVersion;
}
public void setAlgorithmVersion(final MPMasterKey.Version algorithmVersion) {
this.algorithmVersion = algorithmVersion;
}
@Override
public int getAvatar() {
return avatar;
}
public void setAvatar(final int avatar) {
this.avatar = avatar;
}
public MPResultType getDefaultType() {
return defaultType;
}
public void setDefaultType(final MPResultType defaultType) {
this.defaultType = defaultType;
}
public ReadableInstant getLastUsed() {
return lastUsed;
}
public void use() {
lastUsed = new Instant();
}
public Iterable<MPFileSite> getSites() {
return sites;
}
@Override
public void addSite(final MPFileSite site) {
sites.add( site );
}
@Override
public void deleteSite(final MPFileSite site) {
sites.remove( site );
}
@Override
public Collection<MPFileSite> findSites(final String query) {
ImmutableList.Builder<MPFileSite> results = ImmutableList.builder();
for (final MPFileSite site : getSites())
if (site.getSiteName().startsWith( query ))
results.add( site );
return results.build();
}
/**
* Performs an authentication attempt against the keyID for this user.
*
* Note: If this user doesn't have a keyID set yet, authentication will always succeed and the key ID will be set as a result.
*
* @param masterPassword The password to authenticate with.
*
* @return The master key for the user if authentication was successful.
*
* @throws MPIncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID.
*/
@Nonnull
@Override
public MPMasterKey authenticate(final char[] masterPassword)
throws MPIncorrectMasterPasswordException {
try {
key = new MPMasterKey( getFullName(), masterPassword );
if ((keyID == null) || (keyID.length == 0))
keyID = key.getKeyID( algorithmVersion );
else if (!Arrays.equals( key.getKeyID( algorithmVersion ), keyID ))
throw new MPIncorrectMasterPasswordException( this );
return key;
}
catch (final MPInvalidatedException e) {
throw logger.bug( e );
}
}
void save()
throws MPInvalidatedException {
MPFileUserManager.get().save( this, getMasterKey() );
}
@Override
public int compareTo(final MPFileUser o) {
int comparison = getLastUsed().compareTo( o.getLastUsed() );
if (comparison == 0)
comparison = getFullName().compareTo( o.getFullName() );
return comparison;
}
}

View File

@@ -0,0 +1,139 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.google.common.base.*;
import com.google.common.collect.*;
import com.google.common.io.CharSink;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.*;
import java.io.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Manages user data stored in user-specific {@code .mpsites} files under {@code .mpw.d}.
*
* @author lhunath, 14-12-07
*/
public class MPFileUserManager extends MPUserManager {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPFileUserManager.class );
private static final MPFileUserManager instance;
static {
String rcDir = System.getenv( MPConstant.env_rcDir );
if (rcDir != null)
instance = create( new File( rcDir ) );
else
instance = create( new File( ifNotNullElseNullable( System.getProperty( "user.home" ), System.getenv( "HOME" ) ), ".mpw.d" ) );
}
private final File userFilesDirectory;
public static MPFileUserManager get() {
MPUserManager.instance = instance;
return instance;
}
public static MPFileUserManager create(final File userFilesDirectory) {
return new MPFileUserManager( userFilesDirectory );
}
protected MPFileUserManager(final File userFilesDirectory) {
super( unmarshallUsers( userFilesDirectory ) );
this.userFilesDirectory = userFilesDirectory;
}
private static Iterable<MPFileUser> unmarshallUsers(final File userFilesDirectory) {
if (!userFilesDirectory.mkdirs() && !userFilesDirectory.isDirectory()) {
logger.err( "Couldn't create directory for user files: %s", userFilesDirectory );
return ImmutableList.of();
}
return FluentIterable.from( listUserFiles( userFilesDirectory ) ).transform( new Function<File, MPFileUser>() {
@Nullable
@Override
public MPFileUser apply(@Nullable final File file) {
try {
return new MPFlatUnmarshaller().unmarshall( Preconditions.checkNotNull( file ) );
}
catch (final IOException e) {
logger.err( e, "Couldn't read user from: %s", file );
return null;
}
}
} ).filter( Predicates.notNull() );
}
private static ImmutableList<File> listUserFiles(final File userFilesDirectory) {
return ImmutableList.copyOf( ifNotNullElse( userFilesDirectory.listFiles( new FilenameFilter() {
@Override
public boolean accept(final File dir, final String name) {
return name.endsWith( ".mpsites" );
}
} ), new File[0] ) );
}
@Override
public void deleteUser(final MPFileUser user) {
super.deleteUser( user );
// Remove deleted users.
File userFile = getUserFile( user );
if (userFile.exists() && !userFile.delete())
logger.err( "Couldn't delete file: %s", userFile );
}
/**
* Write the current user state to disk.
*/
public void save(final MPFileUser user, final MPMasterKey masterKey)
throws MPInvalidatedException {
try {
new CharSink() {
@Override
public Writer openStream()
throws IOException {
return new OutputStreamWriter( new FileOutputStream( getUserFile( user ) ), Charsets.UTF_8 );
}
}.write( new MPFlatMarshaller().marshall( user, masterKey, MPMarshaller.ContentMode.PROTECTED ) );
}
catch (final IOException e) {
logger.err( e, "Unable to save sites for user: %s", user );
}
}
@Nonnull
private File getUserFile(final MPFileUser user) {
return new File( userFilesDirectory, user.getFullName() + ".mpsites" );
}
/**
* @return The location on the file system where the user models are stored.
*/
public File getPath() {
return userFilesDirectory;
}
}

View File

@@ -0,0 +1,80 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.lyndir.masterpassword.*;
import org.joda.time.Instant;
/**
* @author lhunath, 2017-09-20
*/
public class MPFlatMarshaller implements MPMarshaller {
private static final int FORMAT = 1;
@Override
public String marshall(final MPFileUser user, final MPMasterKey masterKey, final ContentMode contentMode)
throws MPInvalidatedException {
StringBuilder content = new StringBuilder();
content.append( "# Master Password site export\n" );
content.append( "# " ).append( contentMode.description() ).append( '\n' );
content.append( "# \n" );
content.append( "##\n" );
content.append( "# Format: " ).append( FORMAT ).append( '\n' );
content.append( "# Date: " ).append( MPConstant.dateTimeFormatter.print( new Instant() ) ).append( '\n' );
content.append( "# User Name: " ).append( user.getFullName() ).append( '\n' );
content.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
content.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
content.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
content.append( "# Algorithm: " ).append( MPMasterKey.Version.CURRENT.toInt() ).append( '\n' );
content.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
content.append( "# Passwords: " ).append( contentMode.name() ).append( '\n' );
content.append( "##\n" );
content.append( "#\n" );
content.append( "# Last Times Password Login\t Site\tSite\n" );
content.append( "# used used type name\t name\tpassword\n" );
for (final MPFileSite site : user.getSites()) {
String loginName = site.getLoginContent();
String password = site.getSiteContent();
if (!contentMode.isRedacted()) {
loginName = site.loginFor( masterKey );
password = site.resultFor( masterKey );
}
content.append( strf( "%s %8d %8s %25s\t%25s\t%s\n", //
MPConstant.dateTimeFormatter.print( site.getLastUsed() ), // lastUsed
site.getUses(), // uses
strf( "%d:%d:%d", //
site.getResultType().getType(), // type
site.getAlgorithmVersion().toInt(), // algorithm
site.getSiteCounter().intValue() ), // counter
ifNotNullElse( loginName, "" ), // loginName
site.getSiteName(), // siteName
ifNotNullElse( password, "" ) // password
) );
}
return content.toString();
}
}

View File

@@ -0,0 +1,139 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import com.google.common.base.*;
import com.google.common.io.CharStreams;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import com.lyndir.masterpassword.*;
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import org.joda.time.DateTime;
/**
* @author lhunath, 14-12-07
*/
public class MPFlatUnmarshaller implements MPUnmarshaller {
private static final Pattern[] unmarshallFormats = {
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)? +([^\t]+)\t(.*)" ),
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)?(:\\d+)? +([^\t]*)\t *([^\t]+)\t(.*)" ) };
private static final Pattern headerFormat = Pattern.compile( "^#\\s*([^:]+): (.*)" );
private static final Pattern colon = Pattern.compile( ":" );
@Nonnull
@Override
public MPFileUser unmarshall(@Nonnull final File file)
throws IOException {
try (Reader reader = new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 )) {
return unmarshall( CharStreams.toString( reader ) );
}
}
@Nonnull
@Override
public MPFileUser unmarshall(@Nonnull final String content) {
MPFileUser user = null;
byte[] keyID = null;
String fullName = null;
int mpVersion = 0, importFormat = 0, avatar = 0;
boolean clearContent = false, headerStarted = false;
MPResultType defaultType = MPResultType.DEFAULT;
//noinspection HardcodedLineSeparator
for (final String line : Splitter.on( CharMatcher.anyOf( "\r\n" ) ).omitEmptyStrings().split( content ))
// Header delimitor.
if (line.startsWith( "##" ))
if (!headerStarted)
// Starts the header.
headerStarted = true;
else
// Ends the header.
user = new MPFileUser( fullName, keyID, MPMasterKey.Version.fromInt( mpVersion ), avatar, defaultType, new DateTime( 0 ) );
// Comment.
else if (line.startsWith( "#" )) {
if (headerStarted && (user == null)) {
// In header.
Matcher headerMatcher = headerFormat.matcher( line );
if (headerMatcher.matches()) {
String name = headerMatcher.group( 1 ), value = headerMatcher.group( 2 );
if ("Full Name".equalsIgnoreCase( name ) || "User Name".equalsIgnoreCase( name ))
fullName = value;
else if ("Key ID".equalsIgnoreCase( name ))
keyID = CodeUtils.decodeHex( value );
else if ("Algorithm".equalsIgnoreCase( name ))
mpVersion = ConversionUtils.toIntegerNN( value );
else if ("Format".equalsIgnoreCase( name ))
importFormat = ConversionUtils.toIntegerNN( value );
else if ("Avatar".equalsIgnoreCase( name ))
avatar = ConversionUtils.toIntegerNN( value );
else if ("Passwords".equalsIgnoreCase( name ))
clearContent = "visible".equalsIgnoreCase( value );
else if ("Default Type".equalsIgnoreCase( name ))
defaultType = MPResultType.forType( ConversionUtils.toIntegerNN( value ) );
}
}
}
// No comment.
else if (user != null) {
Matcher siteMatcher = unmarshallFormats[importFormat].matcher( line );
if (!siteMatcher.matches())
return null;
MPFileSite site;
switch (importFormat) {
case 0:
site = new MPFileSite( user, //
siteMatcher.group( 5 ), siteMatcher.group( 6 ), MPFileSite.DEFAULT_COUNTER,
MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ),
null, null, null, ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
break;
case 1:
site = new MPFileSite( user, //
siteMatcher.group( 7 ), siteMatcher.group( 8 ),
UnsignedInteger.valueOf( colon.matcher( siteMatcher.group( 5 ) ).replaceAll( "" ) ),
MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
MPMasterKey.Version.fromInt( ConversionUtils.toIntegerNN(
colon.matcher( siteMatcher.group( 4 ) ).replaceAll( "" ) ) ),
siteMatcher.group( 6 ), MPResultType.GeneratedName, null,
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ),
MPConstant.dateTimeFormatter.parseDateTime( siteMatcher.group( 1 ) ).toInstant() );
break;
default:
throw new UnsupportedOperationException( "Unexpected format: " + importFormat );
}
user.addSite( site );
}
return Preconditions.checkNotNull( user, "No full header found in import file." );
}
}

View File

@@ -0,0 +1,37 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
/**
* @author lhunath, 14-12-17
*/
public class MPIncorrectMasterPasswordException extends Exception {
private final MPFileUser user;
public MPIncorrectMasterPasswordException(final MPFileUser user) {
super( "Incorrect master password for user: " + user.getFullName() );
this.user = user;
}
public MPFileUser getUser() {
return user;
}
}

View File

@@ -0,0 +1,34 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import com.lyndir.masterpassword.MPMasterKey;
/**
* @author lhunath, 2017-09-20
*/
public class MPJSONMarshaller implements MPMarshaller {
@Override
public String marshall(final MPFileUser user, final MPMasterKey masterKey, final ContentMode contentMode) {
// TODO
return null;
}
}

View File

@@ -0,0 +1,45 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import java.io.File;
import java.io.IOException;
import javax.annotation.Nonnull;
/**
* @author lhunath, 2017-09-20
*/
public class MPJSONUnmarshaller implements MPUnmarshaller {
@Nonnull
@Override
public MPFileUser unmarshall(@Nonnull final File file)
throws IOException {
// TODO
return null;
}
@Nonnull
@Override
public MPFileUser unmarshall(@Nonnull final String content) {
// TODO
return null;
}
}

View File

@@ -0,0 +1,60 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
/**
* @author lhunath, 2017-09-20
*/
public enum MPMarshalFormat {
/**
* Marshal using the line-based plain-text format.
*/
Flat {
@Override
public MPMarshaller marshaller() {
return new MPFlatMarshaller();
}
@Override
public MPUnmarshaller unmarshaller() {
return new MPFlatUnmarshaller();
}
},
/**
* Marshal using the JSON structured format.
*/
JSON {
@Override
public MPMarshaller marshaller() {
return new MPJSONMarshaller();
}
@Override
public MPUnmarshaller unmarshaller() {
return new MPJSONUnmarshaller();
}
};
public static final MPMarshalFormat DEFAULT = JSON;
public abstract MPMarshaller marshaller();
public abstract MPUnmarshaller unmarshaller();
}

View File

@@ -0,0 +1,52 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model;
import com.lyndir.masterpassword.MPInvalidatedException;
import com.lyndir.masterpassword.MPMasterKey;
/**
* @author lhunath, 14-12-07
*/
public interface MPMarshaller {
String marshall(MPFileUser user, MPMasterKey masterKey, ContentMode contentMode)
throws MPInvalidatedException;
enum ContentMode {
PROTECTED( "Export of site names and stored passwords (unless device-private) encrypted with the master key." ),
VISIBLE( "Export of site names and passwords in clear-text." );
private final String description;
private boolean redacted;
ContentMode(final String description) {
this.description = description;
}
public String description() {
return description;
}
public boolean isRedacted() {
return redacted;
}
}
}

View File

@@ -1,3 +1,21 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model; package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf; import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
@@ -6,138 +24,58 @@ import com.google.common.primitives.UnsignedInteger;
import com.lyndir.masterpassword.*; import com.lyndir.masterpassword.*;
import java.util.Objects; import java.util.Objects;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joda.time.Instant;
/** /**
* @author lhunath, 14-12-05 * @author lhunath, 14-12-16
*/ */
public class MPSite { public abstract class MPSite {
public static final MPSiteType DEFAULT_TYPE = MPSiteType.GeneratedLong; public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.ONE;
public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.valueOf( 1 );
private final MPUser user; public abstract String getSiteName();
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) { public abstract void setSiteName(String siteName);
this( user, siteName, DEFAULT_TYPE, DEFAULT_COUNTER );
public abstract UnsignedInteger getSiteCounter();
public abstract void setSiteCounter(UnsignedInteger siteCounter);
public abstract MPResultType getResultType();
public abstract void setResultType(MPResultType resultType);
public abstract MPMasterKey.Version getAlgorithmVersion();
public abstract void setAlgorithmVersion(MPMasterKey.Version algorithmVersion);
public String resultFor(final MPMasterKey masterKey, final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
@Nullable final String siteContent)
throws MPInvalidatedException {
return masterKey.siteResult(
getSiteName(), getSiteCounter(), keyPurpose, keyContext, getResultType(), siteContent, getAlgorithmVersion() );
} }
public MPSite(final MPUser user, final String siteName, final MPSiteType siteType, final UnsignedInteger siteCounter) { public String loginFor(final MPMasterKey masterKey, final MPResultType loginType, @Nullable final String loginContent)
this.user = user; throws MPInvalidatedException {
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, return masterKey.siteResult(
final MPSiteType siteType, final UnsignedInteger siteCounter, final int uses, @Nullable final String loginName, getSiteName(), DEFAULT_COUNTER, MPKeyPurpose.Identification, null, loginType, loginContent, getAlgorithmVersion() );
@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 @Override
public boolean equals(final Object obj) { public boolean equals(final Object obj) {
return (this == obj) || ((obj instanceof MPSite) && Objects.equals( siteName, ((MPSite) obj).siteName )); return (this == obj) || ((obj instanceof MPSite) && Objects.equals( getSiteName(), ((MPSite) obj).getSiteName() ));
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hashCode( siteName ); return Objects.hashCode( getSiteName() );
} }
@Override @Override
public String toString() { public String toString() {
return strf( "{MPSite: %s}", siteName ); return strf( "{%s: %s}", getClass().getSimpleName(), getSiteName() );
} }
} }

View File

@@ -1,131 +0,0 @@
package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.google.common.base.Preconditions;
import com.lyndir.masterpassword.MasterKey;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.joda.time.Instant;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
/**
* @author lhunath, 14-12-07
*/
public class MPSiteMarshaller {
private static final DateTimeFormatter rfc3339 = ISODateTimeFormat.dateTimeNoMillis();
private final StringBuilder export = new StringBuilder();
private ContentMode contentMode = ContentMode.PROTECTED;
private MasterKey masterKey;
public static MPSiteMarshaller marshallSafe(final MPUser user) {
MPSiteMarshaller marshaller = new MPSiteMarshaller();
marshaller.marshallHeaderForSafeContent( user );
for (final MPSite site : user.getSites())
marshaller.marshallSite( site );
return marshaller;
}
public static MPSiteMarshaller marshallVisible(final MPUser user, final MasterKey masterKey) {
MPSiteMarshaller marshaller = new MPSiteMarshaller();
marshaller.marshallHeaderForVisibleContentWithKey( user, masterKey );
for (final MPSite site : user.getSites())
marshaller.marshallSite( site );
return marshaller;
}
private String marshallHeaderForSafeContent(final MPUser user) {
return marshallHeader( ContentMode.PROTECTED, user, null );
}
private String marshallHeaderForVisibleContentWithKey(final MPUser user, final MasterKey masterKey) {
return marshallHeader( ContentMode.VISIBLE, user, masterKey );
}
private String marshallHeader(final ContentMode contentMode, final MPUser user, @Nullable final MasterKey masterKey) {
this.contentMode = contentMode;
this.masterKey = masterKey;
StringBuilder header = new StringBuilder();
header.append( "# Master Password site export\n" );
header.append( "# " ).append( this.contentMode.description() ).append( '\n' );
header.append( "# \n" );
header.append( "##\n" );
header.append( "# Format: 1\n" );
header.append( "# Date: " ).append( rfc3339.print( new Instant() ) ).append( '\n' );
header.append( "# User Name: " ).append( user.getFullName() ).append( '\n' );
header.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' );
header.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' );
header.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' );
header.append( "# Version: " ).append( MasterKey.Version.CURRENT.toBundleVersion() ).append( '\n' );
header.append( "# Algorithm: " ).append( MasterKey.Version.CURRENT.toInt() ).append( '\n' );
header.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' );
header.append( "# Passwords: " ).append( this.contentMode.name() ).append( '\n' );
header.append( "##\n" );
header.append( "#\n" );
header.append( "# Last Times Password Login\t Site\tSite\n" );
header.append( "# used used type name\t name\tpassword\n" );
export.append( header );
return header.toString();
}
public String marshallSite(final MPSite site) {
String exportLine = strf( "%s %8d %8s %25s\t%25s\t%s", //
rfc3339.print( site.getLastUsed() ), // lastUsed
site.getUses(), // uses
strf( "%d:%d:%d", //
site.getSiteType().getType(), // type
site.getAlgorithmVersion().toInt(), // algorithm
site.getSiteCounter().intValue() ), // counter
ifNotNullElse( site.getLoginName(), "" ), // loginName
site.getSiteName(), // siteName
ifNotNullElse( contentMode.contentForSite( site, masterKey ), "" ) // password
);
export.append( exportLine ).append( '\n' );
return exportLine;
}
public String getExport() {
return export.toString();
}
public ContentMode getContentMode() {
return contentMode;
}
public enum ContentMode {
PROTECTED( "Export of site names and stored passwords (unless device-private) encrypted with the master key." ) {
@Override
public String contentForSite(final MPSite site, @Nullable final MasterKey masterKey) {
return site.exportContent();
}
},
VISIBLE( "Export of site names and passwords in clear-text." ) {
@Override
public String contentForSite(final MPSite site, @Nonnull final MasterKey masterKey) {
return site.resultFor( Preconditions.checkNotNull( masterKey, "Master key is required when content mode is VISIBLE." ) );
}
};
private final String description;
ContentMode(final String description) {
this.description = description;
}
public String description() {
return description;
}
public abstract String contentForSite(MPSite site, MasterKey masterKey);
}
}

View File

@@ -1,3 +1,21 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model; package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf; import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
@@ -10,13 +28,13 @@ import java.util.Objects;
*/ */
public class MPSiteResult { public class MPSiteResult {
private final MPSite site; private final MPFileSite site;
public MPSiteResult(final MPSite site) { public MPSiteResult(final MPFileSite site) {
this.site = site; this.site = site;
} }
public MPSite getSite() { public MPFileSite getSite() {
return site; return site;
} }

View File

@@ -1,163 +0,0 @@
package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.io.CharStreams;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import com.lyndir.lhunath.opal.system.util.NNOperation;
import com.lyndir.masterpassword.MPSiteType;
import com.lyndir.masterpassword.MasterKey;
import java.io.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
/**
* @author lhunath, 14-12-07
*/
public class MPSiteUnmarshaller {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPSite.class );
private static final DateTimeFormatter rfc3339 = ISODateTimeFormat.dateTimeNoMillis();
private static final Pattern[] unmarshallFormats = {
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)? +([^\t]+)\t(.*)" ),
Pattern.compile( "^([^ ]+) +(\\d+) +(\\d+)(:\\d+)?(:\\d+)? +([^\t]*)\t *([^\t]+)\t(.*)" ) };
private static final Pattern headerFormat = Pattern.compile( "^#\\s*([^:]+): (.*)" );
private final int importFormat;
@SuppressWarnings({ "FieldCanBeLocal", "unused" })
private final int mpVersion;
@SuppressWarnings({ "FieldCanBeLocal", "unused" })
private final boolean clearContent;
private final MPUser user;
@Nonnull
public static MPSiteUnmarshaller unmarshall(@Nonnull final File file)
throws IOException {
try (Reader reader = new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 )) {
return unmarshall( CharStreams.readLines( reader ) );
}
}
@Nonnull
public static MPSiteUnmarshaller unmarshall(@Nonnull final List<String> lines) {
byte[] keyID = null;
String fullName = null;
int mpVersion = 0, importFormat = 0, avatar = 0;
boolean clearContent = false, headerStarted = false;
MPSiteType defaultType = MPSiteType.GeneratedLong;
MPSiteUnmarshaller marshaller = null;
final ImmutableList.Builder<MPSite> sites = ImmutableList.builder();
for (final String line : lines)
// Header delimitor.
if (line.startsWith( "##" ))
if (!headerStarted)
// Starts the header.
headerStarted = true;
else
// Ends the header.
marshaller = new MPSiteUnmarshaller( importFormat, mpVersion, fullName, keyID, avatar, defaultType, clearContent );
// Comment.
else if (line.startsWith( "#" )) {
if (headerStarted && (marshaller == null)) {
// In header.
Matcher headerMatcher = headerFormat.matcher( line );
if (headerMatcher.matches()) {
String name = headerMatcher.group( 1 ), value = headerMatcher.group( 2 );
if ("Full Name".equalsIgnoreCase( name ) || "User Name".equalsIgnoreCase( name ))
fullName = value;
else if ("Key ID".equalsIgnoreCase( name ))
keyID = CodeUtils.decodeHex( value );
else if ("Algorithm".equalsIgnoreCase( name ))
mpVersion = ConversionUtils.toIntegerNN( value );
else if ("Format".equalsIgnoreCase( name ))
importFormat = ConversionUtils.toIntegerNN( value );
else if ("Avatar".equalsIgnoreCase( name ))
avatar = ConversionUtils.toIntegerNN( value );
else if ("Passwords".equalsIgnoreCase( name ))
clearContent = "visible".equalsIgnoreCase( value );
else if ("Default Type".equalsIgnoreCase( name ))
defaultType = MPSiteType.forType( ConversionUtils.toIntegerNN( value ) );
}
}
}
// No comment.
else if (marshaller != null)
ifNotNull( marshaller.unmarshallSite( line ), new NNOperation<MPSite>() {
@Override
public void apply(@Nonnull final MPSite site) {
sites.add( site );
}
} );
return Preconditions.checkNotNull( marshaller, "No full header found in import file." );
}
protected MPSiteUnmarshaller(final int importFormat, final int mpVersion, final String fullName, final byte[] keyID, final int avatar,
final MPSiteType defaultType, final boolean clearContent) {
this.importFormat = importFormat;
this.mpVersion = mpVersion;
this.clearContent = clearContent;
user = new MPUser( fullName, keyID, MasterKey.Version.fromInt( mpVersion ), avatar, defaultType, new DateTime( 0 ) );
}
@Nullable
public MPSite unmarshallSite(@Nonnull final String siteLine) {
Matcher siteMatcher = unmarshallFormats[importFormat].matcher( siteLine );
if (!siteMatcher.matches())
return null;
MPSite site;
switch (importFormat) {
case 0:
site = new MPSite( user, //
MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
siteMatcher.group( 5 ), //
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPSite.DEFAULT_COUNTER, //
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
null, //
siteMatcher.group( 6 ) );
break;
case 1:
site = new MPSite( user, //
MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), //
rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), //
siteMatcher.group( 7 ), //
MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ),
UnsignedInteger.valueOf( siteMatcher.group( 5 ).replace( ":", "" ) ), //
ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), //
siteMatcher.group( 6 ), //
siteMatcher.group( 8 ) );
break;
default:
throw logger.bug( "Unexpected format: %d", importFormat );
}
user.addSite( site );
return site;
}
public MPUser getUser() {
return user;
}
}

View File

@@ -16,24 +16,21 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>. // LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//============================================================================== //==============================================================================
package com.lyndir.masterpassword; package com.lyndir.masterpassword.model;
import java.io.*;
import javax.annotation.Nonnull;
/** /**
* <i>07 04, 2012</i> * @author lhunath, 14-12-07
*
* @author lhunath
*/ */
public enum MPSiteTypeClass { public interface MPUnmarshaller {
Generated( 1 << 4 ),
Stored( 1 << 5 );
private final int mask; @Nonnull
MPFileUser unmarshall(@Nonnull File file)
throws IOException;
MPSiteTypeClass(final int mask) { @Nonnull
this.mask = mask; MPFileUser unmarshall(@Nonnull String content);
}
public int getMask() {
return mask;
}
} }

View File

@@ -1,153 +1,86 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model; package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*; import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.google.common.collect.ImmutableList; import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.masterpassword.MPSiteType; import com.lyndir.masterpassword.MPInvalidatedException;
import com.lyndir.masterpassword.MasterKey; import com.lyndir.masterpassword.MPMasterKey;
import java.util.*; import java.util.*;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joda.time.*;
/** /**
* @author lhunath, 14-12-07 * @author lhunath, 2014-06-08
*/ */
public class MPUser implements Comparable<MPUser> { public abstract class MPUser<S extends MPSite> {
private final String fullName;
private final Collection<MPSite> sites = Sets.newHashSet();
@Nullable @Nullable
private byte[] keyID; protected MPMasterKey key;
private final MasterKey.Version algorithmVersion;
private int avatar;
private MPSiteType defaultType;
private ReadableInstant lastUsed;
public MPUser(final String fullName) { public abstract String getFullName();
this( fullName, null );
public boolean isMasterKeyAvailable() {
return key != null;
} }
public MPUser(final String fullName, @Nullable final byte[] keyID) {
this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPSiteType.GeneratedLong, new DateTime() );
}
public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar,
final MPSiteType defaultType, final ReadableInstant lastUsed) {
this.fullName = fullName;
this.keyID = (keyID == null)? null: keyID.clone();
this.algorithmVersion = algorithmVersion;
this.avatar = avatar;
this.defaultType = defaultType;
this.lastUsed = lastUsed;
}
public Collection<MPSiteResult> findSitesByName(final String query) {
ImmutableList.Builder<MPSiteResult> results = ImmutableList.builder();
for (final MPSite site : getSites())
if (site.getSiteName().startsWith( query ))
results.add( new MPSiteResult( site ) );
return results.build();
}
public void addSite(final MPSite site) {
sites.add( site );
}
public void deleteSite(final MPSite site) {
sites.remove( site );
}
public String getFullName() {
return fullName;
}
public boolean hasKeyID() {
return keyID != null;
}
public String exportKeyID() {
return CodeUtils.encodeHex( keyID );
}
/**
* Performs an authentication attempt against the keyID for this user.
*
* Note: If this user doesn't have a keyID set yet, authentication will always succeed and the key ID will be set as a result.
*
* @param masterPassword The password to authenticate with.
*
* @return The master key for the user if authentication was successful.
*
* @throws IncorrectMasterPasswordException If authentication fails due to the given master password not matching the user's keyID.
*/
@Nonnull @Nonnull
@SuppressWarnings("MethodCanBeVariableArityMethod") public MPMasterKey getMasterKey() {
public MasterKey authenticate(final char[] masterPassword) return Preconditions.checkNotNull( key, "User is not authenticated: " + getFullName() );
throws IncorrectMasterPasswordException {
MasterKey masterKey = MasterKey.create( algorithmVersion, getFullName(), masterPassword );
if ((keyID == null) || (keyID.length == 0))
keyID = masterKey.getKeyID();
else if (!Arrays.equals( masterKey.getKeyID(), keyID ))
throw new IncorrectMasterPasswordException( this );
return masterKey;
} }
public String exportKeyID()
throws MPInvalidatedException {
return CodeUtils.encodeHex( getMasterKey().getKeyID( getAlgorithmVersion() ) );
}
public abstract MPMasterKey.Version getAlgorithmVersion();
public int getAvatar() { public int getAvatar() {
return avatar; return 0;
} }
public void setAvatar(final int avatar) { public abstract void addSite(S site);
this.avatar = avatar;
}
public MPSiteType getDefaultType() { public abstract void deleteSite(S site);
return defaultType;
}
public void setDefaultType(final MPSiteType defaultType) { public abstract Collection<S> findSites(String query);
this.defaultType = defaultType;
}
public ReadableInstant getLastUsed() { @Nonnull
return lastUsed; public abstract MPMasterKey authenticate(char[] masterPassword)
} throws MPIncorrectMasterPasswordException;
public void updateLastUsed() { @Override
lastUsed = new Instant(); public int hashCode() {
} return Objects.hashCode( getFullName() );
public Iterable<MPSite> getSites() {
return sites;
} }
@Override @Override
public boolean equals(final Object obj) { public boolean equals(final Object obj) {
return (this == obj) || ((obj instanceof MPUser) && Objects.equals( fullName, ((MPUser) obj).fullName )); return (this == obj) || ((obj instanceof MPUser) && Objects.equals( getFullName(), ((MPUser<?>) obj).getFullName() ));
}
@Override
public int hashCode() {
return Objects.hashCode( fullName );
} }
@Override @Override
public String toString() { public String toString() {
return strf( "{MPUser: %s}", fullName ); return strf( "{%s: %s}", getClass().getSimpleName(), getFullName() );
}
@Override
public int compareTo(final MPUser o) {
int comparison = lastUsed.compareTo( o.lastUsed );
if (comparison == 0)
comparison = fullName.compareTo( o.fullName );
return comparison;
} }
} }

View File

@@ -1,124 +0,0 @@
package com.lyndir.masterpassword.model;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.google.common.base.*;
import com.google.common.collect.*;
import com.google.common.io.CharSink;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.MPConstant;
import java.io.*;
import javax.annotation.Nullable;
/**
* Manages user data stored in user-specific {@code .mpsites} files under {@code .mpw.d}.
* @author lhunath, 14-12-07
*/
public class MPUserFileManager extends MPUserManager {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPUserFileManager.class );
private static final MPUserFileManager instance;
static {
String rcDir = System.getenv( MPConstant.env_rcDir );
if (rcDir != null)
instance = create( new File( rcDir ) );
else
instance = create( new File( ifNotNullElseNullable( System.getProperty( "user.home" ), System.getenv( "HOME" ) ), ".mpw.d" ) );
}
private final File userFilesDirectory;
public static MPUserFileManager get() {
MPUserManager.instance = instance;
return instance;
}
public static MPUserFileManager create(final File userFilesDirectory) {
return new MPUserFileManager( userFilesDirectory );
}
protected MPUserFileManager(final File userFilesDirectory) {
super( unmarshallUsers( userFilesDirectory ) );
this.userFilesDirectory = userFilesDirectory;
}
private static Iterable<MPUser> unmarshallUsers(final File userFilesDirectory) {
if (!userFilesDirectory.mkdirs() && !userFilesDirectory.isDirectory()) {
logger.err( "Couldn't create directory for user files: %s", userFilesDirectory );
return ImmutableList.of();
}
return FluentIterable.from( listUserFiles( userFilesDirectory ) ).transform( new Function<File, MPUser>() {
@Nullable
@Override
public MPUser apply(@Nullable final File file) {
try {
return MPSiteUnmarshaller.unmarshall( Preconditions.checkNotNull( file ) ).getUser();
}
catch (final IOException e) {
logger.err( e, "Couldn't read user from: %s", file );
return null;
}
}
} ).filter( Predicates.notNull() );
}
private static ImmutableList<File> listUserFiles(final File userFilesDirectory) {
return ImmutableList.copyOf( ifNotNullElse( userFilesDirectory.listFiles( new FilenameFilter() {
@Override
public boolean accept(final File dir, final String name) {
return name.endsWith( ".mpsites" );
}
} ), new File[0] ) );
}
@Override
public void addUser(final MPUser user) {
super.addUser( user );
save();
}
@Override
public void deleteUser(final MPUser user) {
super.deleteUser( user );
save();
}
/**
* Write the current user state to disk.
*/
public void save() {
// Save existing users.
for (final MPUser user : getUsers())
try {
new CharSink() {
@Override
public Writer openStream()
throws IOException {
File mpsitesFile = new File( userFilesDirectory, user.getFullName() + ".mpsites" );
return new OutputStreamWriter( new FileOutputStream( mpsitesFile ), Charsets.UTF_8 );
}
}.write( MPSiteMarshaller.marshallSafe( user ).getExport() );
}
catch (final IOException e) {
logger.err( e, "Unable to save sites for user: %s", user );
}
// Remove deleted users.
for (final File userFile : listUserFiles( userFilesDirectory ))
if (getUserNamed( userFile.getName().replaceFirst( "\\.mpsites$", "" ) ) == null)
if (!userFile.delete())
logger.err( "Couldn't delete file: %s", userFile );
}
/**
* @return The location on the file system where the user models are stored.
*/
public File getPath() {
return userFilesDirectory;
}
}

View File

@@ -1,6 +1,25 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword.model; package com.lyndir.masterpassword.model;
import com.google.common.collect.*; import com.google.common.collect.*;
import com.lyndir.masterpassword.MPInvalidatedException;
import java.util.*; import java.util.*;
@@ -9,31 +28,31 @@ import java.util.*;
*/ */
public abstract class MPUserManager { public abstract class MPUserManager {
private final Map<String, MPUser> usersByName = Maps.newHashMap(); private final Map<String, MPFileUser> usersByName = Maps.newHashMap();
static MPUserManager instance; static MPUserManager instance;
public static MPUserManager get() { public static MPUserManager get() {
return instance; return instance;
} }
protected MPUserManager(final Iterable<MPUser> users) { protected MPUserManager(final Iterable<MPFileUser> users) {
for (final MPUser user : users) for (final MPFileUser user : users)
usersByName.put( user.getFullName(), user ); usersByName.put( user.getFullName(), user );
} }
public SortedSet<MPUser> getUsers() { public SortedSet<MPFileUser> getUsers() {
return FluentIterable.from( usersByName.values() ).toSortedSet( Ordering.natural() ); return FluentIterable.from( usersByName.values() ).toSortedSet( Ordering.natural() );
} }
public MPUser getUserNamed(final String fullName) { public MPFileUser getUserNamed(final String fullName) {
return usersByName.get( fullName ); return usersByName.get( fullName );
} }
public void addUser(final MPUser user) { public void addUser(final MPFileUser user) {
usersByName.put( user.getFullName(), user ); usersByName.put( user.getFullName(), user );
} }
public void deleteUser(final MPUser user) { public void deleteUser(final MPFileUser user) {
usersByName.remove( user.getFullName() ); usersByName.remove( user.getFullName() );
} }
} }

View File

@@ -1,3 +1,21 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
/** /**
* *
* @author lhunath, 15-02-04 * @author lhunath, 15-02-04

View File

@@ -23,12 +23,10 @@ import com.google.common.collect.Lists;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils; import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import com.lyndir.lhunath.opal.system.util.NNFunctionNN;
import java.io.IOException; import java.io.IOException;
import java.util.Deque; import java.util.Deque;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import javax.annotation.Nonnull;
import javax.xml.parsers.*; import javax.xml.parsers.*;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@@ -38,7 +36,7 @@ import org.xml.sax.ext.DefaultHandler2;
/** /**
* @author lhunath, 2015-12-22 * @author lhunath, 2015-12-22
*/ */
@SuppressWarnings("HardCodedStringLiteral") @SuppressWarnings({ "HardCodedStringLiteral", "ProhibitedExceptionDeclared" })
public class MPTestSuite implements Callable<Boolean> { public class MPTestSuite implements Callable<Boolean> {
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
@@ -99,12 +97,12 @@ public class MPTestSuite implements Callable<Boolean> {
currentCase.siteName = text; currentCase.siteName = text;
if ("siteCounter".equals( qName )) if ("siteCounter".equals( qName ))
currentCase.siteCounter = text.isEmpty()? null: UnsignedInteger.valueOf( text ); currentCase.siteCounter = text.isEmpty()? null: UnsignedInteger.valueOf( text );
if ("siteType".equals( qName )) if ("resultType".equals( qName ))
currentCase.siteType = text; currentCase.resultType = text;
if ("siteVariant".equals( qName )) if ("keyPurpose".equals( qName ))
currentCase.siteVariant = text; currentCase.keyPurpose = text;
if ("siteContext".equals( qName )) if ("keyContext".equals( qName ))
currentCase.siteContext = text; currentCase.keyContext = text;
if ("result".equals( qName )) if ("result".equals( qName ))
currentCase.result = text; currentCase.result = text;
} }
@@ -134,7 +132,8 @@ public class MPTestSuite implements Callable<Boolean> {
return tests; return tests;
} }
public boolean forEach(final String testName, final NNFunctionNN<MPTests.Case, Boolean> testFunction) { public boolean forEach(final String testName, final TestCase testFunction)
throws Exception {
List<MPTests.Case> cases = tests.getCases(); List<MPTests.Case> cases = tests.getCases();
for (int c = 0; c < cases.size(); c++) { for (int c = 0; c < cases.size(); c++) {
MPTests.Case testCase = cases.get( c ); MPTests.Case testCase = cases.get( c );
@@ -144,7 +143,7 @@ public class MPTestSuite implements Callable<Boolean> {
progress( Logger.Target.INFO, c, cases.size(), // progress( Logger.Target.INFO, c, cases.size(), //
"[%s] on %s...", testName, testCase.getIdentifier() ); "[%s] on %s...", testName, testCase.getIdentifier() );
if (!testFunction.apply( testCase )) { if (!testFunction.run( testCase )) {
progress( Logger.Target.ERROR, cases.size(), cases.size(), // progress( Logger.Target.ERROR, cases.size(), cases.size(), //
"[%s] on %s: FAILED!", testName, testCase.getIdentifier() ); "[%s] on %s: FAILED!", testName, testCase.getIdentifier() );
@@ -168,13 +167,14 @@ public class MPTestSuite implements Callable<Boolean> {
@Override @Override
public Boolean call() public Boolean call()
throws Exception { throws Exception {
return forEach( "mpw", new NNFunctionNN<MPTests.Case, Boolean>() { return forEach( "mpw", new TestCase() {
@Nonnull
@Override @Override
public Boolean apply(@Nonnull final MPTests.Case testCase) { public boolean run(final MPTests.Case testCase)
MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() ); throws Exception {
String sitePassword = masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), testCase.getMasterPassword().toCharArray() );
testCase.getSiteVariant(), testCase.getSiteContext() ); String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
testCase.getKeyContext(), testCase.getResultType(),
null, testCase.getAlgorithm() );
return testCase.getResult().equals( sitePassword ); return testCase.getResult().equals( sitePassword );
} }
@@ -195,4 +195,11 @@ public class MPTestSuite implements Callable<Boolean> {
void progress(int current, int max, String messageFormat, Object... args); void progress(int current, int max, String messageFormat, Object... args);
} }
public interface TestCase {
boolean run(MPTests.Case testCase)
throws Exception;
}
} }

View File

@@ -66,18 +66,18 @@ public class MPTests {
public static class Case { public static class Case {
String identifier; String identifier;
String parent; String parent;
Integer algorithm; Integer algorithm;
String fullName; String fullName;
String masterPassword; String masterPassword;
String keyID; String keyID;
String siteName; String siteName;
UnsignedInteger siteCounter; UnsignedInteger siteCounter;
String siteType; String resultType;
String siteVariant; String keyPurpose;
String siteContext; String keyContext;
String result; String result;
private transient Case parentCase; private transient Case parentCase;
@@ -130,25 +130,25 @@ public class MPTests {
return checkNotNull( parentCase.siteCounter ); return checkNotNull( parentCase.siteCounter );
} }
} ); } );
siteType = ifNotNullElse( siteType, new NNSupplier<String>() { resultType = ifNotNullElse( resultType, new NNSupplier<String>() {
@Nonnull @Nonnull
@Override @Override
public String get() { public String get() {
return checkNotNull( parentCase.siteType ); return checkNotNull( parentCase.resultType );
} }
} ); } );
siteVariant = ifNotNullElse( siteVariant, new NNSupplier<String>() { keyPurpose = ifNotNullElse( keyPurpose, new NNSupplier<String>() {
@Nonnull @Nonnull
@Override @Override
public String get() { public String get() {
return checkNotNull( parentCase.siteVariant ); return checkNotNull( parentCase.keyPurpose );
} }
} ); } );
siteContext = ifNotNullElse( siteContext, new NNSupplier<String>() { keyContext = ifNotNullElse( keyContext, new NNSupplier<String>() {
@Nonnull @Nonnull
@Override @Override
public String get() { public String get() {
return (parentCase == null)? "": checkNotNull( parentCase.siteContext ); return (parentCase == null)? "": checkNotNull( parentCase.keyContext );
} }
} ); } );
result = ifNotNullElse( result, new NNSupplier<String>() { result = ifNotNullElse( result, new NNSupplier<String>() {
@@ -171,8 +171,8 @@ public class MPTests {
} }
@Nonnull @Nonnull
public MasterKey.Version getAlgorithm() { public MPMasterKey.Version getAlgorithm() {
return MasterKey.Version.fromInt( checkNotNull( algorithm ) ); return MPMasterKey.Version.fromInt( checkNotNull( algorithm ) );
} }
@Nonnull @Nonnull
@@ -181,8 +181,8 @@ public class MPTests {
} }
@Nonnull @Nonnull
public char[] getMasterPassword() { public String getMasterPassword() {
return checkNotNull( masterPassword ).toCharArray(); return checkNotNull( masterPassword );
} }
@Nonnull @Nonnull
@@ -200,18 +200,18 @@ public class MPTests {
} }
@Nonnull @Nonnull
public MPSiteType getSiteType() { public MPResultType getResultType() {
return MPSiteType.forName( checkNotNull( siteType ) ); return MPResultType.forName( checkNotNull( resultType ) );
} }
@Nonnull @Nonnull
public MPSiteVariant getSiteVariant() { public MPKeyPurpose getKeyPurpose() {
return MPSiteVariant.forName( checkNotNull( siteVariant ) ); return MPKeyPurpose.forName( checkNotNull( keyPurpose ) );
} }
@Nonnull @Nonnull
public String getSiteContext() { public String getKeyContext() {
return checkNotNull( siteContext ); return checkNotNull( keyContext );
} }
@Nonnull @Nonnull

View File

@@ -1,279 +0,0 @@
<tests>
<!-- Default values for all parameters. -->
<case id="default">
<algorithm>-1</algorithm>
<fullName>Robert Lee Mitchell</fullName>
<masterPassword>banana colored duckling</masterPassword>
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
<siteName>masterpasswordapp.com</siteName>
<siteCounter>1</siteCounter>
<resultType>GeneratedLong</resultType>
<keyPurpose>Authentication</keyPurpose>
<result><!-- abstract --></result>
</case>
<!-- Algorithm 3 -->
<case id="v3" parent="default">
<algorithm>3</algorithm>
<result>Jejr5[RepuSosp</result>
</case>
<case id="v3_mb_fullName" parent="v3">
<fullName></fullName>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>NopaDajh8=Fene</result>
</case>
<case id="v3_mb_masterPassword" parent="v3">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>QesuHirv5-Xepl</result>
</case>
<case id="v3_mb_siteName" parent="v3">
<siteName></siteName>
<result>LiheCuwhSerz6)</result>
</case>
<case id="v3_loginName" parent="v3">
<keyPurpose>Identification</keyPurpose>
<resultType>GeneratedName</resultType>
<result>wohzaqage</result>
</case>
<case id="v3_securityAnswer" parent="v3">
<keyPurpose>Recovery</keyPurpose>
<resultType>GeneratedPhrase</resultType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v3_securityAnswer_context" parent="v3_securityAnswer">
<keyContext>question</keyContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v3_type_maximum" parent="v3">
<resultType>GeneratedMaximum</resultType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v3_type_medium" parent="v3">
<resultType>GeneratedMedium</resultType>
<result>Jej2$Quv</result>
</case>
<case id="v3_type_basic" parent="v3">
<resultType>GeneratedBasic</resultType>
<result>WAo2xIg6</result>
</case>
<case id="v3_type_short" parent="v3">
<resultType>GeneratedShort</resultType>
<result>Jej2</result>
</case>
<case id="v3_type_pin" parent="v3">
<resultType>GeneratedPIN</resultType>
<result>7662</result>
</case>
<case id="v3_type_name" parent="v3">
<resultType>GeneratedName</resultType>
<result>jejraquvo</result>
</case>
<case id="v3_type_phrase" parent="v3">
<resultType>GeneratedPhrase</resultType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v3_counter_ceiling" parent="v3">
<siteCounter>4294967295</siteCounter>
<result>XambHoqo6[Peni</result>
</case>
<!-- Algorithm 2 -->
<case id="v2" parent="default">
<algorithm>2</algorithm>
<result>Jejr5[RepuSosp</result>
</case>
<case id="v2_mb_fullName" parent="v2">
<fullName></fullName>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>WaqoGuho2[Xaxw</result>
</case>
<case id="v2_mb_masterPassword" parent="v2">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>QesuHirv5-Xepl</result>
</case>
<case id="v2_mb_siteName" parent="v2">
<siteName></siteName>
<result>LiheCuwhSerz6)</result>
</case>
<case id="v2_loginName" parent="v2">
<keyPurpose>Identification</keyPurpose>
<resultType>GeneratedName</resultType>
<result>wohzaqage</result>
</case>
<case id="v2_securityAnswer" parent="v2">
<keyPurpose>Recovery</keyPurpose>
<resultType>GeneratedPhrase</resultType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v2_securityAnswer_context" parent="v2_securityAnswer">
<keyContext>question</keyContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v2_type_maximum" parent="v2">
<resultType>GeneratedMaximum</resultType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v2_type_medium" parent="v2">
<resultType>GeneratedMedium</resultType>
<result>Jej2$Quv</result>
</case>
<case id="v2_type_basic" parent="v2">
<resultType>GeneratedBasic</resultType>
<result>WAo2xIg6</result>
</case>
<case id="v2_type_short" parent="v2">
<resultType>GeneratedShort</resultType>
<result>Jej2</result>
</case>
<case id="v2_type_pin" parent="v2">
<resultType>GeneratedPIN</resultType>
<result>7662</result>
</case>
<case id="v2_type_name" parent="v2">
<resultType>GeneratedName</resultType>
<result>jejraquvo</result>
</case>
<case id="v2_type_phrase" parent="v2">
<resultType>GeneratedPhrase</resultType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v2_counter_ceiling" parent="v2">
<siteCounter>4294967295</siteCounter>
<result>XambHoqo6[Peni</result>
</case>
<!-- Algorithm 1 -->
<case id="v1" parent="default">
<algorithm>1</algorithm>
<result>Jejr5[RepuSosp</result>
</case>
<case id="v1_mb_fullName" parent="v1">
<fullName></fullName>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>WaqoGuho2[Xaxw</result>
</case>
<case id="v1_mb_masterPassword" parent="v1">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>QesuHirv5-Xepl</result>
</case>
<case id="v1_mb_siteName" parent="v1">
<siteName></siteName>
<result>WawiYarp2@Kodh</result>
</case>
<case id="v1_loginName" parent="v1">
<keyPurpose>Identification</keyPurpose>
<resultType>GeneratedName</resultType>
<result>wohzaqage</result>
</case>
<case id="v1_securityAnswer" parent="v1">
<keyPurpose>Recovery</keyPurpose>
<resultType>GeneratedPhrase</resultType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v1_securityAnswer_context" parent="v1_securityAnswer">
<keyContext>question</keyContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v1_type_maximum" parent="v1">
<resultType>GeneratedMaximum</resultType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v1_type_medium" parent="v1">
<resultType>GeneratedMedium</resultType>
<result>Jej2$Quv</result>
</case>
<case id="v1_type_basic" parent="v1">
<resultType>GeneratedBasic</resultType>
<result>WAo2xIg6</result>
</case>
<case id="v1_type_short" parent="v1">
<resultType>GeneratedShort</resultType>
<result>Jej2</result>
</case>
<case id="v1_type_pin" parent="v1">
<resultType>GeneratedPIN</resultType>
<result>7662</result>
</case>
<case id="v1_type_name" parent="v1">
<resultType>GeneratedName</resultType>
<result>jejraquvo</result>
</case>
<case id="v1_type_phrase" parent="v1">
<resultType>GeneratedPhrase</resultType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v1_counter_ceiling" parent="v1">
<siteCounter>4294967295</siteCounter>
<result>XambHoqo6[Peni</result>
</case>
<!-- Algorithm 0 -->
<case id="v0" parent="default">
<algorithm>0</algorithm>
<result>Feji5@ReduWosh</result>
</case>
<case id="v0_mb_fullName" parent="v0">
<fullName></fullName>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>HajrYudo7@Mamh</result>
</case>
<case id="v0_mb_masterPassword" parent="v0">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>MewmDini0]Meho</result>
</case>
<case id="v0_mb_siteName" parent="v0">
<siteName></siteName>
<result>HahiVana2@Nole</result>
</case>
<case id="v0_loginName" parent="v0">
<keyPurpose>Identification</keyPurpose>
<resultType>GeneratedName</resultType>
<result>lozwajave</result>
</case>
<case id="v0_securityAnswer" parent="v0">
<keyPurpose>Recovery</keyPurpose>
<resultType>GeneratedPhrase</resultType>
<result>miy lirfijoja dubu</result>
</case>
<case id="v0_securityAnswer_context" parent="v0_securityAnswer">
<keyContext>question</keyContext>
<result>movm bex gevrica jaf</result>
</case>
<case id="v0_type_maximum" parent="v0">
<resultType>GeneratedMaximum</resultType>
<result>w1!3bA3icmRAc)SS@lwl</result>
</case>
<case id="v0_type_medium" parent="v0">
<resultType>GeneratedMedium</resultType>
<result>Fej7]Jug</result>
</case>
<case id="v0_type_basic" parent="v0">
<resultType>GeneratedBasic</resultType>
<result>wvH7irC1</result>
</case>
<case id="v0_type_short" parent="v0">
<resultType>GeneratedShort</resultType>
<result>Fej7</result>
</case>
<case id="v0_type_pin" parent="v0">
<resultType>GeneratedPIN</resultType>
<result>2117</result>
</case>
<case id="v0_type_name" parent="v0">
<resultType>GeneratedName</resultType>
<result>fejrajugo</result>
</case>
<case id="v0_type_phrase" parent="v0">
<resultType>GeneratedPhrase</resultType>
<result>fejr jug gabsibu bax</result>
</case>
<case id="v0_counter_ceiling" parent="v0">
<siteCounter>4294967295</siteCounter>
<result>QateDojh1@Hecn</result>
</case>
</tests>

View File

@@ -0,0 +1 @@
../../../../../mpw_tests.xml

View File

@@ -0,0 +1,130 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import static org.testng.Assert.*;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.Random;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class MPMasterKeyTest {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MPMasterKeyTest.class );
private MPTestSuite testSuite;
@BeforeMethod
public void setUp()
throws Exception {
testSuite = new MPTestSuite();
}
@Test
public void testMasterKey()
throws Exception {
testSuite.forEach( "testMasterKey", new MPTestSuite.TestCase() {
@Override
public boolean run(final MPTests.Case testCase)
throws Exception {
char[] masterPassword = testCase.getMasterPassword().toCharArray();
MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), masterPassword );
// Test key
assertEquals(
CodeUtils.encodeHex( masterKey.getKeyID( testCase.getAlgorithm() ) ),
testCase.getKeyID(),
"[testMasterKey] keyID mismatch: " + testCase );
// Test invalidation
masterKey.invalidate();
try {
masterKey.getKeyID( testCase.getAlgorithm() );
fail( "[testMasterKey] invalidate ineffective: " + testCase );
}
catch (final MPInvalidatedException ignored) {
}
assertNotEquals(
masterPassword,
testCase.getMasterPassword().toCharArray(),
"[testMasterKey] masterPassword not wiped: " + testCase );
return true;
}
} );
}
@Test
public void testSiteResult()
throws Exception {
testSuite.forEach( "testSiteResult", new MPTestSuite.TestCase() {
@Override
public boolean run(final MPTests.Case testCase)
throws Exception {
char[] masterPassword = testCase.getMasterPassword().toCharArray();
MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), masterPassword );
// Test site result
assertEquals(
masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
testCase.getKeyContext(), testCase.getResultType(),
null, testCase.getAlgorithm() ),
testCase.getResult(),
"[testSiteResult] result mismatch: " + testCase );
return true;
}
} );
}
@Test
public void testSiteState()
throws Exception {
MPTests.Case testCase = testSuite.getTests().getDefaultCase();
char[] masterPassword = testCase.getMasterPassword().toCharArray();
MPMasterKey masterKey = new MPMasterKey( testCase.getFullName(), masterPassword );
Random random = new Random();
StringBuilder password = new StringBuilder();
for (int p = 0; p < 8; ++p)
password.append( (char) (random.nextInt( Character.MAX_CODE_POINT - Character.MIN_CODE_POINT ) + Character.MIN_CODE_POINT) );
for (final MPMasterKey.Version version : MPMasterKey.Version.values()) {
MPResultType resultType = MPResultType.StoredPersonal;
// Test site state
String state = masterKey.siteState( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
testCase.getKeyContext(), resultType, password.toString(), version );
assertEquals(
masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(),
testCase.getKeyContext(), resultType, state, version ),
password.toString(),
"[testSiteState] state mismatch: " + testCase );
}
}
}

View File

@@ -1,112 +0,0 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
package com.lyndir.masterpassword;
import static org.testng.Assert.*;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.NNFunctionNN;
import javax.annotation.Nonnull;
import org.jetbrains.annotations.NonNls;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class MasterKeyTest {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyTest.class );
@NonNls
private MPTestSuite testSuite;
@BeforeMethod
public void setUp()
throws Exception {
testSuite = new MPTestSuite();
}
@Test
public void testEncode()
throws Exception {
testSuite.forEach( "testEncode", new NNFunctionNN<MPTests.Case, Boolean>() {
@Nonnull
@Override
public Boolean apply(@Nonnull final MPTests.Case testCase) {
MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
assertEquals(
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(),
testCase.getSiteVariant(), testCase.getSiteContext() ),
testCase.getResult(), "[testEncode] Failed test case: " + testCase );
return true;
}
} );
}
@Test
public void testGetUserName()
throws Exception {
MPTests.Case defaultCase = testSuite.getTests().getDefaultCase();
assertEquals( MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
defaultCase.getFullName(), "[testGetUserName] Failed test case: " + defaultCase );
}
@Test
public void testGetKeyID()
throws Exception {
testSuite.forEach( "testGetKeyID", new NNFunctionNN<MPTests.Case, Boolean>() {
@Nonnull
@Override
public Boolean apply(@Nonnull final MPTests.Case testCase) {
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ),
testCase.getKeyID(), "[testGetKeyID] Failed test case: " + testCase );
return true;
}
} );
}
@Test
public void testInvalidate()
throws Exception {
try {
MPTests.Case defaultCase = testSuite.getTests().getDefaultCase();
MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() );
masterKey.invalidate();
masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(),
defaultCase.getSiteVariant(), defaultCase.getSiteContext() );
fail( "[testInvalidate] Master key should have been invalidated, but was still usable." );
}
catch (final IllegalStateException ignored) {
}
}
}

View File

@@ -6,7 +6,7 @@
</encoder> </encoder>
</appender> </appender>
<logger name="com.lyndir" level="${mp.log.level:-INFO}" /> <logger name="com.lyndir" level="${mp.log.level:-TRACE}" />
<root level="INFO"> <root level="INFO">
<appender-ref ref="stdout" /> <appender-ref ref="stdout" />

279
core/mpw_tests.xml Normal file
View File

@@ -0,0 +1,279 @@
<tests>
<!-- Default values for all parameters. -->
<case id="default">
<algorithm>-1</algorithm>
<fullName>Robert Lee Mitchell</fullName>
<masterPassword>banana colored duckling</masterPassword>
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
<siteName>masterpasswordapp.com</siteName>
<siteCounter>1</siteCounter>
<resultType>Long</resultType>
<keyPurpose>Authentication</keyPurpose>
<result><!-- abstract --></result>
</case>
<!-- Algorithm 3 -->
<case id="v3" parent="default">
<algorithm>3</algorithm>
<result>Jejr5[RepuSosp</result>
</case>
<case id="v3_mb_fullName" parent="v3">
<fullName></fullName>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>NopaDajh8=Fene</result>
</case>
<case id="v3_mb_masterPassword" parent="v3">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>QesuHirv5-Xepl</result>
</case>
<case id="v3_mb_siteName" parent="v3">
<siteName></siteName>
<result>LiheCuwhSerz6)</result>
</case>
<case id="v3_loginName" parent="v3">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<result>wohzaqage</result>
</case>
<case id="v3_securityAnswer" parent="v3">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v3_securityAnswer_context" parent="v3_securityAnswer">
<keyContext>question</keyContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v3_type_maximum" parent="v3">
<resultType>Maximum</resultType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v3_type_medium" parent="v3">
<resultType>Medium</resultType>
<result>Jej2$Quv</result>
</case>
<case id="v3_type_basic" parent="v3">
<resultType>Basic</resultType>
<result>WAo2xIg6</result>
</case>
<case id="v3_type_short" parent="v3">
<resultType>Short</resultType>
<result>Jej2</result>
</case>
<case id="v3_type_pin" parent="v3">
<resultType>PIN</resultType>
<result>7662</result>
</case>
<case id="v3_type_name" parent="v3">
<resultType>Name</resultType>
<result>jejraquvo</result>
</case>
<case id="v3_type_phrase" parent="v3">
<resultType>Phrase</resultType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v3_counter_ceiling" parent="v3">
<siteCounter>4294967295</siteCounter>
<result>XambHoqo6[Peni</result>
</case>
<!-- Algorithm 2 -->
<case id="v2" parent="default">
<algorithm>2</algorithm>
<result>Jejr5[RepuSosp</result>
</case>
<case id="v2_mb_fullName" parent="v2">
<fullName></fullName>
<keyID>4D5851D0B093D65DE0CF13D94877270468C0B65A6E42CA50D393AC9B99C457B5</keyID>
<result>WaqoGuho2[Xaxw</result>
</case>
<case id="v2_mb_masterPassword" parent="v2">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>QesuHirv5-Xepl</result>
</case>
<case id="v2_mb_siteName" parent="v2">
<siteName></siteName>
<result>LiheCuwhSerz6)</result>
</case>
<case id="v2_loginName" parent="v2">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<result>wohzaqage</result>
</case>
<case id="v2_securityAnswer" parent="v2">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v2_securityAnswer_context" parent="v2_securityAnswer">
<keyContext>question</keyContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v2_type_maximum" parent="v2">
<resultType>Maximum</resultType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v2_type_medium" parent="v2">
<resultType>Medium</resultType>
<result>Jej2$Quv</result>
</case>
<case id="v2_type_basic" parent="v2">
<resultType>Basic</resultType>
<result>WAo2xIg6</result>
</case>
<case id="v2_type_short" parent="v2">
<resultType>Short</resultType>
<result>Jej2</result>
</case>
<case id="v2_type_pin" parent="v2">
<resultType>PIN</resultType>
<result>7662</result>
</case>
<case id="v2_type_name" parent="v2">
<resultType>Name</resultType>
<result>jejraquvo</result>
</case>
<case id="v2_type_phrase" parent="v2">
<resultType>Phrase</resultType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v2_counter_ceiling" parent="v2">
<siteCounter>4294967295</siteCounter>
<result>XambHoqo6[Peni</result>
</case>
<!-- Algorithm 1 -->
<case id="v1" parent="default">
<algorithm>1</algorithm>
<result>Jejr5[RepuSosp</result>
</case>
<case id="v1_mb_fullName" parent="v1">
<fullName></fullName>
<keyID>4D5851D0B093D65DE0CF13D94877270468C0B65A6E42CA50D393AC9B99C457B5</keyID>
<result>WaqoGuho2[Xaxw</result>
</case>
<case id="v1_mb_masterPassword" parent="v1">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>QesuHirv5-Xepl</result>
</case>
<case id="v1_mb_siteName" parent="v1">
<siteName></siteName>
<result>WawiYarp2@Kodh</result>
</case>
<case id="v1_loginName" parent="v1">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<result>wohzaqage</result>
</case>
<case id="v1_securityAnswer" parent="v1">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v1_securityAnswer_context" parent="v1_securityAnswer">
<keyContext>question</keyContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v1_type_maximum" parent="v1">
<resultType>Maximum</resultType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v1_type_medium" parent="v1">
<resultType>Medium</resultType>
<result>Jej2$Quv</result>
</case>
<case id="v1_type_basic" parent="v1">
<resultType>Basic</resultType>
<result>WAo2xIg6</result>
</case>
<case id="v1_type_short" parent="v1">
<resultType>Short</resultType>
<result>Jej2</result>
</case>
<case id="v1_type_pin" parent="v1">
<resultType>PIN</resultType>
<result>7662</result>
</case>
<case id="v1_type_name" parent="v1">
<resultType>Name</resultType>
<result>jejraquvo</result>
</case>
<case id="v1_type_phrase" parent="v1">
<resultType>Phrase</resultType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v1_counter_ceiling" parent="v1">
<siteCounter>4294967295</siteCounter>
<result>XambHoqo6[Peni</result>
</case>
<!-- Algorithm 0 -->
<case id="v0" parent="default">
<algorithm>0</algorithm>
<result>Feji5@ReduWosh</result>
</case>
<case id="v0_mb_fullName" parent="v0">
<fullName></fullName>
<keyID>4D5851D0B093D65DE0CF13D94877270468C0B65A6E42CA50D393AC9B99C457B5</keyID>
<result>HajrYudo7@Mamh</result>
</case>
<case id="v0_mb_masterPassword" parent="v0">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>MewmDini0]Meho</result>
</case>
<case id="v0_mb_siteName" parent="v0">
<siteName></siteName>
<result>HahiVana2@Nole</result>
</case>
<case id="v0_loginName" parent="v0">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<result>lozwajave</result>
</case>
<case id="v0_securityAnswer" parent="v0">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<result>miy lirfijoja dubu</result>
</case>
<case id="v0_securityAnswer_context" parent="v0_securityAnswer">
<keyContext>question</keyContext>
<result>movm bex gevrica jaf</result>
</case>
<case id="v0_type_maximum" parent="v0">
<resultType>Maximum</resultType>
<result>w1!3bA3icmRAc)SS@lwl</result>
</case>
<case id="v0_type_medium" parent="v0">
<resultType>Medium</resultType>
<result>Fej7]Jug</result>
</case>
<case id="v0_type_basic" parent="v0">
<resultType>Basic</resultType>
<result>wvH7irC1</result>
</case>
<case id="v0_type_short" parent="v0">
<resultType>Short</resultType>
<result>Fej7</result>
</case>
<case id="v0_type_pin" parent="v0">
<resultType>PIN</resultType>
<result>2117</result>
</case>
<case id="v0_type_name" parent="v0">
<resultType>Name</resultType>
<result>fejrajugo</result>
</case>
<case id="v0_type_phrase" parent="v0">
<resultType>Phrase</resultType>
<result>fejr jug gabsibu bax</result>
</case>
<case id="v0_counter_ceiling" parent="v0">
<siteCounter>4294967295</siteCounter>
<result>QateDojh1@Hecn</result>
</case>
</tests>

View File

@@ -6,20 +6,18 @@
<option name="PM_INSTALL_OPTIONS" value="" /> <option name="PM_INSTALL_OPTIONS" value="" />
<option name="ACTIVITY_EXTRA_FLAGS" value="" /> <option name="ACTIVITY_EXTRA_FLAGS" value="" />
<option name="MODE" value="default_activity" /> <option name="MODE" value="default_activity" />
<option name="TARGET_SELECTION_MODE" value="SHOW_DIALOG" />
<option name="PREFERRED_AVD" value="" /> <option name="PREFERRED_AVD" value="" />
<option name="CLEAR_LOGCAT" value="false" /> <option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" /> <option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" /> <option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
<option name="FORCE_STOP_RUNNING_APP" value="true" /> <option name="FORCE_STOP_RUNNING_APP" value="true" />
<option name="DEBUGGER_TYPE" value="Java" /> <option name="TARGET_SELECTION_MODE" value="SHOW_DIALOG" />
<option name="USE_LAST_SELECTED_DEVICE" value="false" /> <option name="USE_LAST_SELECTED_DEVICE" value="false" />
<option name="PREFERRED_AVD" value="" /> <option name="PREFERRED_AVD" value="" />
<option name="DEBUGGER_TYPE" value="Java" />
<Java /> <Java />
<Profilers> <Profilers>
<option name="ENABLE_ADVANCED_PROFILING" value="false" /> <option name="ENABLE_ADVANCED_PROFILING" value="false" />
<option name="GAPID_ENABLED" value="false" />
<option name="GAPID_DISABLE_PCS" value="false" />
<option name="SUPPORT_LIB_ENABLED" value="true" /> <option name="SUPPORT_LIB_ENABLED" value="true" />
<option name="INSTRUMENTATION_ENABLED" value="true" /> <option name="INSTRUMENTATION_ENABLED" value="true" />
</Profilers> </Profilers>

View File

@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="GUI" type="Application" factoryName="Application"> <configuration default="false" name="GUI" type="Application" factoryName="Application" show_console_on_std_err="true">
<option name="MAIN_CLASS_NAME" value="com.lyndir.masterpassword.gui.GUI" /> <option name="MAIN_CLASS_NAME" value="com.lyndir.masterpassword.gui.GUI" />
<option name="VM_PARAMETERS" value="" /> <option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
@@ -9,7 +9,7 @@
<option name="ENABLE_SWING_INSPECTOR" value="false" /> <option name="ENABLE_SWING_INSPECTOR" value="false" />
<option name="ENV_VARIABLES" /> <option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" /> <option name="PASS_PARENT_ENVS" value="true" />
<module name="gui" /> <module name="masterpassword-gui" />
<envs /> <envs />
<method /> <method />
</configuration> </configuration>

View File

@@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Tests" type="TestNG" factoryName="TestNG"> <configuration default="false" name="Tests" type="TestNG" factoryName="TestNG" show_console_on_std_err="true">
<module name="tests" /> <module name="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" /> <option name="ALTERNATIVE_JRE_PATH" />
<option name="SUITE_NAME" value="" /> <option name="SUITE_NAME" value="" />
@@ -17,7 +17,7 @@
<option name="ENV_VARIABLES" /> <option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" /> <option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE"> <option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" /> <value defaultName="wholeProject" />
</option> </option>
<option name="USE_DEFAULT_REPORTERS" value="false" /> <option name="USE_DEFAULT_REPORTERS" value="false" />
<option name="PROPERTIES_FILE" value="" /> <option name="PROPERTIES_FILE" value="" />

View File

@@ -1,3 +1,3 @@
<component name="DependencyValidationManager"> <component name="DependencyValidationManager">
<scope name="masterpassword" pattern="com.lyndir.masterpassword.*" /> <scope name="masterpassword" pattern="com.lyndir.masterpassword..*" />
</component> </component>

View File

@@ -22,7 +22,7 @@ buildscript {
} }
dependencies { dependencies {
classpath group: 'com.android.tools.build', name: 'gradle', version: '2.2.3' classpath group: 'com.android.tools.build', name: 'gradle', version: '2.3.0'
} }
} }

View File

@@ -22,7 +22,6 @@
<module>masterpassword-tests</module> <module>masterpassword-tests</module>
<module>masterpassword-algorithm</module> <module>masterpassword-algorithm</module>
<module>masterpassword-model</module> <module>masterpassword-model</module>
<module>masterpassword-cli</module>
<module>masterpassword-gui</module> <module>masterpassword-gui</module>
</modules> </modules>

View File

@@ -1,5 +1,11 @@
rootProject.name = 'masterpassword' rootProject.name = 'masterpassword'
def local = new Properties();
try {
local.load(file('local.properties').newDataInputStream())
} catch (FileNotFoundException ignored) {
}
include 'masterpassword-algorithm' include 'masterpassword-algorithm'
project(':masterpassword-algorithm').projectDir = new File( '../core/java/algorithm' ) project(':masterpassword-algorithm').projectDir = new File( '../core/java/algorithm' )
@@ -9,11 +15,12 @@ project(':masterpassword-model').projectDir = new File( '../core/java/model' )
include 'masterpassword-tests' include 'masterpassword-tests'
project(':masterpassword-tests').projectDir = new File( '../core/java/tests' ) project(':masterpassword-tests').projectDir = new File( '../core/java/tests' )
include 'masterpassword-cli'
project(':masterpassword-cli').projectDir = new File( '../platform-independent/cli-java' )
include 'masterpassword-gui' include 'masterpassword-gui'
project(':masterpassword-gui').projectDir = new File( '../platform-independent/gui-java' ) project(':masterpassword-gui').projectDir = new File( '../platform-independent/gui-java' )
include 'masterpassword-android' if (local.containsKey('sdk.dir')) {
project(':masterpassword-android').projectDir = new File( '../platform-android' ) include 'masterpassword-android'
project(':masterpassword-android').projectDir = new File( '../platform-android' )
} else {
logger.warn( "Skipping masterpassword-android since sdk.dir is not defined in local.properties." )
}

View File

@@ -33,7 +33,6 @@ import android.view.WindowManager;
import android.widget.*; import android.widget.*;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.google.common.util.concurrent.*; import com.google.common.util.concurrent.*;
@@ -52,12 +51,12 @@ public class EmergencyActivity extends Activity {
private static final int PASSWORD_NOTIFICATION = 0; private static final int PASSWORD_NOTIFICATION = 0;
public static final int CLIPBOARD_CLEAR_DELAY = 20 /* s */ * MPConstant.MS_PER_S; public static final int CLIPBOARD_CLEAR_DELAY = 20 /* s */ * MPConstant.MS_PER_S;
private final Preferences preferences = Preferences.get( this ); private final Preferences preferences = Preferences.get( this );
private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() ); private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
private final ImmutableList<MPSiteType> allSiteTypes = ImmutableList.copyOf( MPSiteType.forClass( MPSiteTypeClass.Generated ) ); private final ImmutableList<MPResultType> allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Template ) );
private final ImmutableList<MasterKey.Version> allVersions = ImmutableList.copyOf( MasterKey.Version.values() ); private final ImmutableList<MPMasterKey.Version> allVersions = ImmutableList.copyOf( MPMasterKey.Version.values() );
private ListenableFuture<MasterKey> masterKeyFuture; private MPMasterKey masterKey;
@BindView(R.id.progressView) @BindView(R.id.progressView)
ProgressBar progressView; ProgressBar progressView;
@@ -71,8 +70,8 @@ public class EmergencyActivity extends Activity {
@BindView(R.id.siteNameField) @BindView(R.id.siteNameField)
EditText siteNameField; EditText siteNameField;
@BindView(R.id.siteTypeButton) @BindView(R.id.resultTypeButton)
Button siteTypeButton; Button resultTypeButton;
@BindView(R.id.counterField) @BindView(R.id.counterField)
Button siteCounterButton; Button siteCounterButton;
@@ -97,7 +96,6 @@ public class EmergencyActivity extends Activity {
private int id_userName; private int id_userName;
private int id_masterPassword; private int id_masterPassword;
private int id_version;
private String sitePassword; private String sitePassword;
public static void start(final Context context) { public static void start(final Context context) {
@@ -131,15 +129,15 @@ public class EmergencyActivity extends Activity {
updateSitePassword(); updateSitePassword();
} }
} ); } );
siteTypeButton.setOnClickListener( new View.OnClickListener() { resultTypeButton.setOnClickListener( new View.OnClickListener() {
@Override @Override
public void onClick(final View v) { public void onClick(final View v) {
@SuppressWarnings("SuspiciousMethodCalls") @SuppressWarnings("SuspiciousMethodCalls")
MPSiteType siteType = MPResultType resultType =
allSiteTypes.get( (allSiteTypes.indexOf( siteTypeButton.getTag() ) + 1) % allSiteTypes.size() ); allResultTypes.get( (allResultTypes.indexOf( resultTypeButton.getTag() ) + 1) % allResultTypes.size() );
preferences.setDefaultSiteType( siteType ); preferences.setDefaultResultType( resultType );
siteTypeButton.setTag( siteType ); resultTypeButton.setTag( resultType );
siteTypeButton.setText( siteType.getShortName() ); resultTypeButton.setText( resultType.getShortName() );
updateSitePassword(); updateSitePassword();
} }
} ); } );
@@ -156,7 +154,7 @@ public class EmergencyActivity extends Activity {
@Override @Override
public void onClick(final View v) { public void onClick(final View v) {
@SuppressWarnings("SuspiciousMethodCalls") @SuppressWarnings("SuspiciousMethodCalls")
MasterKey.Version siteVersion = MPMasterKey.Version siteVersion =
allVersions.get( (allVersions.indexOf( siteVersionButton.getTag() ) + 1) % allVersions.size() ); allVersions.get( (allVersions.indexOf( siteVersionButton.getTag() ) + 1) % allVersions.size() );
preferences.setDefaultVersion( siteVersion ); preferences.setDefaultVersion( siteVersion );
siteVersionButton.setTag( siteVersion ); siteVersionButton.setTag( siteVersion );
@@ -213,17 +211,17 @@ public class EmergencyActivity extends Activity {
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() ); // FIXME: MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
fullNameField.setText( preferences.getFullName() ); fullNameField.setText( preferences.getFullName() );
rememberFullNameField.setChecked( preferences.isRememberFullName() ); rememberFullNameField.setChecked( preferences.isRememberFullName() );
forgetPasswordField.setChecked( preferences.isForgetPassword() ); forgetPasswordField.setChecked( preferences.isForgetPassword() );
maskPasswordField.setChecked( preferences.isMaskPassword() ); maskPasswordField.setChecked( preferences.isMaskPassword() );
sitePasswordField.setTransformationMethod( preferences.isMaskPassword()? new PasswordTransformationMethod(): null ); sitePasswordField.setTransformationMethod( preferences.isMaskPassword()? new PasswordTransformationMethod(): null );
MPSiteType defaultSiteType = preferences.getDefaultSiteType(); MPResultType defaultResultType = preferences.getDefaultResultType();
siteTypeButton.setTag( defaultSiteType ); resultTypeButton.setTag( defaultResultType );
siteTypeButton.setText( defaultSiteType.getShortName() ); resultTypeButton.setText( defaultResultType.getShortName() );
MasterKey.Version defaultVersion = preferences.getDefaultVersion(); MPMasterKey.Version defaultVersion = preferences.getDefaultVersion();
siteVersionButton.setTag( defaultVersion ); siteVersionButton.setTag( defaultVersion );
siteVersionButton.setText( defaultVersion.name() ); siteVersionButton.setText( defaultVersion.name() );
siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) ); siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) );
@@ -241,10 +239,8 @@ public class EmergencyActivity extends Activity {
if (preferences.isForgetPassword()) { if (preferences.isForgetPassword()) {
synchronized (this) { synchronized (this) {
id_userName = id_masterPassword = 0; id_userName = id_masterPassword = 0;
if (masterKeyFuture != null) { if (masterKey != null)
masterKeyFuture.cancel( true ); masterKey = null;
masterKeyFuture = null;
}
masterPasswordField.setText( "" ); masterPasswordField.setText( "" );
} }
@@ -260,23 +256,17 @@ public class EmergencyActivity extends Activity {
private synchronized void updateMasterKey() { private synchronized void updateMasterKey() {
final String fullName = fullNameField.getText().toString(); final String fullName = fullNameField.getText().toString();
final char[] masterPassword = masterPasswordField.getText().toString().toCharArray(); final char[] masterPassword = masterPasswordField.getText().toString().toCharArray();
final MasterKey.Version version = (MasterKey.Version) siteVersionButton.getTag();
if ((id_userName == fullName.hashCode()) if ((id_userName == fullName.hashCode())
&& (id_masterPassword == Arrays.hashCode( masterPassword )) && (id_masterPassword == Arrays.hashCode( masterPassword )))
&& (id_version == version.ordinal())) if (masterKey != null)
if ((masterKeyFuture != null) && !masterKeyFuture.isCancelled())
return; return;
id_userName = fullName.hashCode(); id_userName = fullName.hashCode();
id_masterPassword = Arrays.hashCode( masterPassword ); id_masterPassword = Arrays.hashCode( masterPassword );
id_version = version.ordinal();
if (preferences.isRememberFullName()) if (preferences.isRememberFullName())
preferences.setFullName( fullName ); preferences.setFullName( fullName );
if (masterKeyFuture != null)
masterKeyFuture.cancel( true );
if (fullName.isEmpty() || (masterPassword.length == 0)) { if (fullName.isEmpty() || (masterPassword.length == 0)) {
sitePasswordField.setText( "" ); sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE ); progressView.setVisibility( View.INVISIBLE );
@@ -285,43 +275,21 @@ public class EmergencyActivity extends Activity {
sitePasswordField.setText( "" ); sitePasswordField.setText( "" );
progressView.setVisibility( View.VISIBLE ); progressView.setVisibility( View.VISIBLE );
(masterKeyFuture = executor.submit( new Callable<MasterKey>() { masterKey = new MPMasterKey( fullName, masterPassword );
@Override updateSitePassword();
public MasterKey call()
throws Exception {
try {
return MasterKey.create( version, fullName, masterPassword );
}
catch (final Exception e) {
sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE );
logger.err( e, "While generating master key." );
throw e;
}
}
} )).addListener( new Runnable() {
@Override
public void run() {
runOnUiThread( new Runnable() {
@Override
public void run() {
updateSitePassword();
}
} );
}
}, executor );
} }
private void updateSitePassword() { private void updateSitePassword() {
final String siteName = siteNameField.getText().toString(); final String siteName = siteNameField.getText().toString();
final MPSiteType type = (MPSiteType) siteTypeButton.getTag(); final MPResultType type = (MPResultType) resultTypeButton.getTag();
final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() ); final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
final MPMasterKey.Version version = (MPMasterKey.Version) siteVersionButton.getTag();
if ((masterKeyFuture == null) || siteName.isEmpty() || (type == null)) { if ((masterKey == null) || siteName.isEmpty() || (type == null)) {
sitePasswordField.setText( "" ); sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE ); progressView.setVisibility( View.INVISIBLE );
if (masterKeyFuture == null) if (masterKey == null)
updateMasterKey(); updateMasterKey();
return; return;
} }
@@ -332,7 +300,7 @@ public class EmergencyActivity extends Activity {
@Override @Override
public void run() { public void run() {
try { try {
sitePassword = masterKeyFuture.get().encode( siteName, type, counter, MPSiteVariant.Password, null ); sitePassword = masterKey.siteResult( siteName, counter, MPKeyPurpose.Authentication, null, type, null, version );
runOnUiThread( new Runnable() { runOnUiThread( new Runnable() {
@Override @Override
@@ -342,16 +310,10 @@ public class EmergencyActivity extends Activity {
} }
} ); } );
} }
catch (final InterruptedException ignored) { catch (final MPInvalidatedException ignored) {
sitePasswordField.setText( "" ); sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE ); progressView.setVisibility( View.INVISIBLE );
} }
catch (final ExecutionException e) {
sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE );
logger.err( e, "While generating site password." );
throw Throwables.propagate( e );
}
catch (final RuntimeException e) { catch (final RuntimeException e) {
sitePasswordField.setText( "" ); sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE ); progressView.setVisibility( View.INVISIBLE );
@@ -363,10 +325,9 @@ public class EmergencyActivity extends Activity {
} }
public void integrityTests(final View view) { public void integrityTests(final View view) {
if (masterKeyFuture != null) { if (masterKey != null)
masterKeyFuture.cancel( true ); masterKey = null;
masterKeyFuture = null;
}
TestActivity.startNoSkip( this ); TestActivity.startNoSkip( this );
} }

View File

@@ -38,7 +38,7 @@ public final class Preferences {
private static final String PREF_FORGET_PASSWORD = "forgetPassword"; private static final String PREF_FORGET_PASSWORD = "forgetPassword";
private static final String PREF_MASK_PASSWORD = "maskPassword"; private static final String PREF_MASK_PASSWORD = "maskPassword";
private static final String PREF_FULL_NAME = "fullName"; private static final String PREF_FULL_NAME = "fullName";
private static final String PREF_SITE_TYPE = "siteType"; private static final String PREF_RESULT_TYPE = "resultType";
private static final String PREF_ALGORITHM_VERSION = "algorithmVersion"; private static final String PREF_ALGORITHM_VERSION = "algorithmVersion";
private static Preferences instance; private static Preferences instance;
@@ -74,7 +74,7 @@ public final class Preferences {
} }
public boolean isAllowNativeKDF() { public boolean isAllowNativeKDF() {
return prefs().getBoolean( PREF_NATIVE_KDF, MasterKey.isAllowNativeByDefault() ); return prefs().getBoolean( PREF_NATIVE_KDF, true );
} }
public boolean setTestsPassed(final Set<String> value) { public boolean setTestsPassed(final Set<String> value) {
@@ -138,20 +138,20 @@ public final class Preferences {
return prefs().getString( PREF_FULL_NAME, "" ); return prefs().getString( PREF_FULL_NAME, "" );
} }
public boolean setDefaultSiteType(@Nonnull final MPSiteType value) { public boolean setDefaultResultType(final MPResultType value) {
if (getDefaultSiteType() == value) if (getDefaultResultType() == value)
return false; return false;
prefs().edit().putInt( PREF_SITE_TYPE, value.ordinal() ).apply(); prefs().edit().putInt( PREF_RESULT_TYPE, value.ordinal() ).apply();
return true; return true;
} }
@Nonnull @Nonnull
public MPSiteType getDefaultSiteType() { public MPResultType getDefaultResultType() {
return MPSiteType.values()[prefs().getInt( PREF_SITE_TYPE, MPSiteType.GeneratedLong.ordinal() )]; return MPResultType.values()[prefs().getInt( PREF_RESULT_TYPE, MPResultType.DEFAULT.ordinal() )];
} }
public boolean setDefaultVersion(@Nonnull final MasterKey.Version value) { public boolean setDefaultVersion(final MPMasterKey.Version value) {
if (getDefaultVersion() == value) if (getDefaultVersion() == value)
return false; return false;
@@ -160,7 +160,7 @@ public final class Preferences {
} }
@Nonnull @Nonnull
public MasterKey.Version getDefaultVersion() { public MPMasterKey.Version getDefaultVersion() {
return MasterKey.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MasterKey.Version.CURRENT.ordinal() )]; return MPMasterKey.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MPMasterKey.Version.CURRENT.ordinal() )];
} }
} }

View File

@@ -80,7 +80,7 @@ public class TestActivity extends Activity implements MPTestSuite.Listener {
@Override @Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) { public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
preferences.setNativeKDFEnabled( isChecked ); preferences.setNativeKDFEnabled( isChecked );
MasterKey.setAllowNativeByDefault( isChecked ); // TODO: MasterKey.setAllowNativeByDefault( isChecked );
} }
} ); } );
@@ -122,7 +122,7 @@ public class TestActivity extends Activity implements MPTestSuite.Listener {
if (testFuture != null) if (testFuture != null)
testFuture.cancel( true ); testFuture.cancel( true );
MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() ); // TODO: MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
setStatus( R.string.tests_testing, R.string.tests_btn_testing, null ); setStatus( R.string.tests_testing, R.string.tests_btn_testing, null );
Futures.addCallback( testFuture = backgroundExecutor.submit( testSuite ), new FutureCallback<Boolean>() { Futures.addCallback( testFuture = backgroundExecutor.submit( testSuite ), new FutureCallback<Boolean>() {

View File

@@ -105,7 +105,7 @@
android:id="@id/sitePasswordField" android:id="@id/sitePasswordField"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:nextFocusForward="@+id/siteTypeButton" android:nextFocusForward="@+id/resultTypeButton"
android:gravity="center" android:gravity="center"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
@@ -157,7 +157,7 @@
android:gravity="center"> android:gravity="center">
<Button <Button
android:id="@id/siteTypeButton" android:id="@id/resultTypeButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
@@ -175,12 +175,12 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:labelFor="@id/siteTypeButton" android:labelFor="@id/resultTypeButton"
android:gravity="center" android:gravity="center"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:textSize="12sp" android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark" android:textColor="@android:color/tertiary_text_dark"
android:text="@string/siteType_hint" /> android:text="@string/resultType_hint" />
</LinearLayout> </LinearLayout>

View File

@@ -8,7 +8,7 @@
<string name="masterPassword_hint">Your master password</string> <string name="masterPassword_hint">Your master password</string>
<string name="siteName_hint">eg. google.com</string> <string name="siteName_hint">eg. google.com</string>
<string name="sitePassword_hint">Tap to copy</string> <string name="sitePassword_hint">Tap to copy</string>
<string name="siteType_hint">Type</string> <string name="resultType_hint">Type</string>
<string name="siteCounter_hint">Counter</string> <string name="siteCounter_hint">Counter</string>
<string name="siteVersion_hint">Algorithm</string> <string name="siteVersion_hint">Algorithm</string>
<string name="empty" /> <string name="empty" />

View File

@@ -125,7 +125,6 @@
DA25C5FF197DBF200046CDCF /* icon_thumbs-up@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD384B1711E29700CF925C /* icon_thumbs-up@2x.png */; }; DA25C5FF197DBF200046CDCF /* icon_thumbs-up@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD384B1711E29700CF925C /* icon_thumbs-up@2x.png */; };
DA25C600197DBF260046CDCF /* icon_trash.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38501711E29700CF925C /* icon_trash.png */; }; DA25C600197DBF260046CDCF /* icon_trash.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38501711E29700CF925C /* icon_trash.png */; };
DA25C601197DBF260046CDCF /* icon_trash@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38511711E29700CF925C /* icon_trash@2x.png */; }; DA25C601197DBF260046CDCF /* icon_trash@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38511711E29700CF925C /* icon_trash@2x.png */; };
DA25C6B41980D3C50046CDCF /* openssl in Headers */ = {isa = PBXBuildFile; fileRef = DA25C6B31980D3C10046CDCF /* openssl */; settings = {ATTRIBUTES = (Public, ); }; };
DA29992F19C86F5700AF7DF1 /* thumb_generated_login@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29992D19C86F5700AF7DF1 /* thumb_generated_login@2x.png */; }; DA29992F19C86F5700AF7DF1 /* thumb_generated_login@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29992D19C86F5700AF7DF1 /* thumb_generated_login@2x.png */; };
DA29993019C86F5700AF7DF1 /* thumb_generated_login.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29992E19C86F5700AF7DF1 /* thumb_generated_login.png */; }; DA29993019C86F5700AF7DF1 /* thumb_generated_login.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29992E19C86F5700AF7DF1 /* thumb_generated_login.png */; };
DA29993219C9132F00AF7DF1 /* thumb_generated_login@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29993119C9132F00AF7DF1 /* thumb_generated_login@3x.png */; }; DA29993219C9132F00AF7DF1 /* thumb_generated_login@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29993119C9132F00AF7DF1 /* thumb_generated_login@3x.png */; };
@@ -176,6 +175,7 @@
DA45224A190628A1008F650A /* icon_wrench@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD386B1711E29700CF925C /* icon_wrench@2x.png */; }; DA45224A190628A1008F650A /* icon_wrench@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD386B1711E29700CF925C /* icon_wrench@2x.png */; };
DA45224B190628B2008F650A /* icon_gear.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37821711E29500CF925C /* icon_gear.png */; }; DA45224B190628B2008F650A /* icon_gear.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37821711E29500CF925C /* icon_gear.png */; };
DA45224C190628B2008F650A /* icon_gear@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37831711E29500CF925C /* icon_gear@2x.png */; }; DA45224C190628B2008F650A /* icon_gear@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37831711E29500CF925C /* icon_gear@2x.png */; };
DA45711D1F572F1E00D54152 /* PearlCryptUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DA45711B1F572F1E00D54152 /* PearlCryptUtils.m */; };
DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; }; DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; };
DA4DA1DA1564471F00F6F596 /* libuicolor-utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */; }; DA4DA1DA1564471F00F6F596 /* libuicolor-utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */; };
DA5A09DF171A70E4005284AB /* play.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09DD171A70E4005284AB /* play.png */; }; DA5A09DF171A70E4005284AB /* play.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09DD171A70E4005284AB /* play.png */; };
@@ -249,8 +249,12 @@
DAA1765219D8B82B0044227B /* copy_pw.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA1763C19D8B82B0044227B /* copy_pw.png */; }; DAA1765219D8B82B0044227B /* copy_pw.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA1763C19D8B82B0044227B /* copy_pw.png */; };
DAA1765319D8B82B0044227B /* choose_type@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA1763D19D8B82B0044227B /* choose_type@2x.png */; }; DAA1765319D8B82B0044227B /* choose_type@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA1763D19D8B82B0044227B /* choose_type@2x.png */; };
DAA1765419D8B82B0044227B /* choose_type.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA1763E19D8B82B0044227B /* choose_type.png */; }; DAA1765419D8B82B0044227B /* choose_type.png in Resources */ = {isa = PBXBuildFile; fileRef = DAA1763E19D8B82B0044227B /* choose_type.png */; };
DAA449D21EEC4B5800E7BDD5 /* mpw-marshall.c in Sources */ = {isa = PBXBuildFile; fileRef = DAA449D01EEC4B5800E7BDD5 /* mpw-marshall.c */; }; DAA449D21EEC4B5800E7BDD5 /* mpw-marshal.c in Sources */ = {isa = PBXBuildFile; fileRef = DAA449D01EEC4B5800E7BDD5 /* mpw-marshal.c */; };
DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D3969393A3A46BD27D7078 /* mpw-algorithm.c */; }; DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D3969393A3A46BD27D7078 /* mpw-algorithm.c */; };
DAB07C9D1F7725C500CC6D43 /* aes.c in Sources */ = {isa = PBXBuildFile; fileRef = DAB07C9B1F7725C500CC6D43 /* aes.c */; };
DAB7AE5D1F3D752900C856B1 /* libjson-c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB7AE5C1F3D752900C856B1 /* libjson-c.a */; };
DAB7AE771F3D755B00C856B1 /* libjson-c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB7AE761F3D755B00C856B1 /* libjson-c.a */; };
DAB7AE991F3DDEE000C856B1 /* mpw-marshal-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DAB7AE981F3DDEE000C856B1 /* mpw-marshal-util.c */; };
DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DABB981515100B4000B05417 /* SystemConfiguration.framework */; }; DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DABB981515100B4000B05417 /* SystemConfiguration.framework */; };
DABD39371711E29700CF925C /* avatar-0.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD366C1711E29400CF925C /* avatar-0.png */; }; DABD39371711E29700CF925C /* avatar-0.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD366C1711E29400CF925C /* avatar-0.png */; };
DABD39381711E29700CF925C /* avatar-0@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD366D1711E29400CF925C /* avatar-0@2x.png */; }; DABD39381711E29700CF925C /* avatar-0@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD366D1711E29400CF925C /* avatar-0@2x.png */; };
@@ -346,7 +350,6 @@
DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; }; DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
DAC8DF47192831E100BA7D71 /* icon_key.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379A1711E29600CF925C /* icon_key.png */; }; DAC8DF47192831E100BA7D71 /* icon_key.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379A1711E29600CF925C /* icon_key.png */; };
DAC8DF48192831E100BA7D71 /* icon_key@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379B1711E29600CF925C /* icon_key@2x.png */; }; DAC8DF48192831E100BA7D71 /* icon_key@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379B1711E29600CF925C /* icon_key@2x.png */; };
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA29711705E1A8002C6C22 /* ciphers.plist */; };
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; }; DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; };
DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29771705E2BD002C6C22 /* JRSwizzle.h */; }; DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29771705E2BD002C6C22 /* JRSwizzle.h */; };
DACA299A1705E2BD002C6C22 /* JRSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = DACA298C1705E2BD002C6C22 /* JRSwizzle.m */; }; DACA299A1705E2BD002C6C22 /* JRSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = DACA298C1705E2BD002C6C22 /* JRSwizzle.m */; };
@@ -403,12 +406,8 @@
DAFE4A2F15039824003ABA7C /* PearlStringUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45F415039823003ABA7C /* PearlStringUtils.h */; }; DAFE4A2F15039824003ABA7C /* PearlStringUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45F415039823003ABA7C /* PearlStringUtils.h */; };
DAFE4A3015039824003ABA7C /* PearlStringUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE45F515039823003ABA7C /* PearlStringUtils.m */; }; DAFE4A3015039824003ABA7C /* PearlStringUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE45F515039823003ABA7C /* PearlStringUtils.m */; };
DAFE4A3315039824003ABA7C /* Pearl-Crypto.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45FD15039823003ABA7C /* Pearl-Crypto.h */; }; DAFE4A3315039824003ABA7C /* Pearl-Crypto.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45FD15039823003ABA7C /* Pearl-Crypto.h */; };
DAFE4A3415039824003ABA7C /* PearlCryptUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45FE15039823003ABA7C /* PearlCryptUtils.h */; };
DAFE4A3515039824003ABA7C /* PearlCryptUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE45FF15039823003ABA7C /* PearlCryptUtils.m */; };
DAFE4A3615039824003ABA7C /* PearlKeyChain.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE460015039823003ABA7C /* PearlKeyChain.h */; }; DAFE4A3615039824003ABA7C /* PearlKeyChain.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE460015039823003ABA7C /* PearlKeyChain.h */; };
DAFE4A3715039824003ABA7C /* PearlKeyChain.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE460115039823003ABA7C /* PearlKeyChain.m */; }; DAFE4A3715039824003ABA7C /* PearlKeyChain.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE460115039823003ABA7C /* PearlKeyChain.m */; };
DAFE4A3815039824003ABA7C /* PearlRSAKey.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE460215039823003ABA7C /* PearlRSAKey.h */; };
DAFE4A3915039824003ABA7C /* PearlRSAKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE460315039823003ABA7C /* PearlRSAKey.m */; };
DAFE4A3C15039824003ABA7C /* Pearl-UIKit-Dependencies.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE460815039823003ABA7C /* Pearl-UIKit-Dependencies.h */; }; DAFE4A3C15039824003ABA7C /* Pearl-UIKit-Dependencies.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE460815039823003ABA7C /* Pearl-UIKit-Dependencies.h */; };
DAFE4A3D15039824003ABA7C /* Pearl-UIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE460915039823003ABA7C /* Pearl-UIKit.h */; }; DAFE4A3D15039824003ABA7C /* Pearl-UIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE460915039823003ABA7C /* Pearl-UIKit.h */; };
DAFE4A3E15039824003ABA7C /* PearlAlert.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE460A15039823003ABA7C /* PearlAlert.h */; }; DAFE4A3E15039824003ABA7C /* PearlAlert.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE460A15039823003ABA7C /* PearlAlert.h */; };
@@ -419,10 +418,6 @@
DAFE4A4315039824003ABA7C /* PearlBoxView.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE460F15039823003ABA7C /* PearlBoxView.m */; }; DAFE4A4315039824003ABA7C /* PearlBoxView.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE460F15039823003ABA7C /* PearlBoxView.m */; };
DAFE4A4415039824003ABA7C /* PearlGradientView.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE461015039823003ABA7C /* PearlGradientView.h */; }; DAFE4A4415039824003ABA7C /* PearlGradientView.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE461015039823003ABA7C /* PearlGradientView.h */; };
DAFE4A4515039824003ABA7C /* PearlGradientView.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE461115039823003ABA7C /* PearlGradientView.m */; }; DAFE4A4515039824003ABA7C /* PearlGradientView.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE461115039823003ABA7C /* PearlGradientView.m */; };
DAFE4A4615039824003ABA7C /* PearlLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE461215039823003ABA7C /* PearlLayout.h */; };
DAFE4A4715039824003ABA7C /* PearlLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE461315039823003ABA7C /* PearlLayout.m */; };
DAFE4A4815039824003ABA7C /* PearlLayoutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE461415039823003ABA7C /* PearlLayoutView.h */; };
DAFE4A4915039824003ABA7C /* PearlLayoutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE461515039823003ABA7C /* PearlLayoutView.m */; };
DAFE4A4A15039824003ABA7C /* PearlMessageView.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE461615039823003ABA7C /* PearlMessageView.h */; }; DAFE4A4A15039824003ABA7C /* PearlMessageView.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE461615039823003ABA7C /* PearlMessageView.h */; };
DAFE4A4B15039824003ABA7C /* PearlMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE461715039823003ABA7C /* PearlMessageView.m */; }; DAFE4A4B15039824003ABA7C /* PearlMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE461715039823003ABA7C /* PearlMessageView.m */; };
DAFE4A4C15039824003ABA7C /* PearlRootViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE461815039823003ABA7C /* PearlRootViewController.h */; }; DAFE4A4C15039824003ABA7C /* PearlRootViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE461815039823003ABA7C /* PearlRootViewController.h */; };
@@ -452,6 +447,23 @@
DAFE4A63150399FF003ABA94 /* NSDateFormatter+RFC3339.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */; }; DAFE4A63150399FF003ABA94 /* NSDateFormatter+RFC3339.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
DAB7AE4D1F3D618700C856B1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA5BFA3B147E415C00F98B1E /* Project object */;
proxyType = 1;
remoteGlobalIDString = DAB7AE471F3D468300C856B1;
remoteInfo = "libsodium-ios";
};
DAB7AE4F1F3D618700C856B1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA5BFA3B147E415C00F98B1E /* Project object */;
proxyType = 1;
remoteGlobalIDString = DAB7AE3D1F3D464A00C856B1;
remoteInfo = "libjson-c";
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
DA32D01E19D111C6004F3F0E /* CopyFiles */ = { DA32D01E19D111C6004F3F0E /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase; isa = PBXCopyFilesBuildPhase;
@@ -742,7 +754,6 @@
DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+PearlReloadItems.h"; sourceTree = "<group>"; }; DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+PearlReloadItems.h"; sourceTree = "<group>"; };
DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionReusableView+PearlDequeue.m"; sourceTree = "<group>"; }; DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionReusableView+PearlDequeue.m"; sourceTree = "<group>"; };
DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionReusableView+PearlDequeue.h"; sourceTree = "<group>"; }; DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionReusableView+PearlDequeue.h"; sourceTree = "<group>"; };
DA25C6B31980D3C10046CDCF /* openssl */ = {isa = PBXFileReference; lastKnownFileType = folder; path = openssl; sourceTree = "<group>"; };
DA29992D19C86F5700AF7DF1 /* thumb_generated_login@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_generated_login@2x.png"; sourceTree = "<group>"; }; DA29992D19C86F5700AF7DF1 /* thumb_generated_login@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_generated_login@2x.png"; sourceTree = "<group>"; };
DA29992E19C86F5700AF7DF1 /* thumb_generated_login.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = thumb_generated_login.png; sourceTree = "<group>"; }; DA29992E19C86F5700AF7DF1 /* thumb_generated_login.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = thumb_generated_login.png; sourceTree = "<group>"; };
DA29993119C9132F00AF7DF1 /* thumb_generated_login@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_generated_login@3x.png"; sourceTree = "<group>"; }; DA29993119C9132F00AF7DF1 /* thumb_generated_login@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "thumb_generated_login@3x.png"; sourceTree = "<group>"; };
@@ -782,6 +793,8 @@
DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Storyboard.storyboard; sourceTree = "<group>"; }; DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Storyboard.storyboard; sourceTree = "<group>"; };
DA3B844D190FC5DF00246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Crashlytics.framework; path = ../../../External/iOS/Crashlytics.framework; sourceTree = "<group>"; }; DA3B844D190FC5DF00246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Crashlytics.framework; path = ../../../External/iOS/Crashlytics.framework; sourceTree = "<group>"; };
DA3BCFCA19BD09D5006B2681 /* SourceCodePro-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Regular.otf"; sourceTree = "<group>"; }; DA3BCFCA19BD09D5006B2681 /* SourceCodePro-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Regular.otf"; sourceTree = "<group>"; };
DA45711B1F572F1E00D54152 /* PearlCryptUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlCryptUtils.m; sourceTree = "<group>"; };
DA45711C1F572F1E00D54152 /* PearlCryptUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlCryptUtils.h; sourceTree = "<group>"; };
DA5A09DD171A70E4005284AB /* play.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = play.png; sourceTree = "<group>"; }; DA5A09DD171A70E4005284AB /* play.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = play.png; sourceTree = "<group>"; };
DA5A09DE171A70E4005284AB /* play@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "play@2x.png"; sourceTree = "<group>"; }; DA5A09DE171A70E4005284AB /* play@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "play@2x.png"; sourceTree = "<group>"; };
DA5A09E8171BB0F7005284AB /* unlocked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = unlocked.png; sourceTree = "<group>"; }; DA5A09E8171BB0F7005284AB /* unlocked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = unlocked.png; sourceTree = "<group>"; };
@@ -803,7 +816,6 @@
DA67460C18DE7F0C00DFE240 /* Exo2.0-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-Bold.otf"; sourceTree = "<group>"; }; DA67460C18DE7F0C00DFE240 /* Exo2.0-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Exo2.0-Bold.otf"; sourceTree = "<group>"; };
DA70EC7F1811B13C00F65DB2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; DA70EC7F1811B13C00F65DB2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
DA771FE41E6E1595004D7EDE /* MasterPassword-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MasterPassword-Prefix.pch"; sourceTree = "<group>"; }; DA771FE41E6E1595004D7EDE /* MasterPassword-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MasterPassword-Prefix.pch"; sourceTree = "<group>"; };
DA7F2C5E1C48B70D00404A26 /* libopensslcrypto-ios-sim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopensslcrypto-ios-sim.a"; sourceTree = "<group>"; };
DA854C8118D4CFBF00106317 /* avatar-add@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add@2x.png"; sourceTree = "<group>"; }; DA854C8118D4CFBF00106317 /* avatar-add@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add@2x.png"; sourceTree = "<group>"; };
DA854C8218D4CFBF00106317 /* avatar-add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add.png"; sourceTree = "<group>"; }; DA854C8218D4CFBF00106317 /* avatar-add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add.png"; sourceTree = "<group>"; };
DA92614C1BE1A57500369DE5 /* MPAppDelegate_InApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_InApp.h; sourceTree = "<group>"; }; DA92614C1BE1A57500369DE5 /* MPAppDelegate_InApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_InApp.h; sourceTree = "<group>"; };
@@ -818,16 +830,16 @@
DAA1411D1922FF020032B392 /* PearlTween.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlTween.h; sourceTree = "<group>"; }; DAA1411D1922FF020032B392 /* PearlTween.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlTween.h; sourceTree = "<group>"; };
DAA1411F1922FF020032B392 /* map-macro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "map-macro.h"; sourceTree = "<group>"; }; DAA1411F1922FF020032B392 /* map-macro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "map-macro.h"; sourceTree = "<group>"; };
DAA1757D19D86BE70044227B /* libAttributedMarkdown.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libAttributedMarkdown.a; sourceTree = BUILT_PRODUCTS_DIR; }; DAA1757D19D86BE70044227B /* libAttributedMarkdown.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libAttributedMarkdown.a; sourceTree = BUILT_PRODUCTS_DIR; };
DAA175B119D86C620044227B /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; }; DAA175B119D86C620044227B /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
DAA175B619D86C620044227B /* markdown_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = markdown_lib.h; sourceTree = "<group>"; }; DAA175B619D86C620044227B /* markdown_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = markdown_lib.h; sourceTree = "<group>"; };
DAA175B719D86C620044227B /* markdown_lib.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = markdown_lib.m; sourceTree = "<group>"; }; DAA175B719D86C620044227B /* markdown_lib.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = markdown_lib.m; sourceTree = "<group>"; };
DAA175B819D86C620044227B /* markdown_output.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = markdown_output.m; sourceTree = "<group>"; }; DAA175B819D86C620044227B /* markdown_output.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = markdown_output.m; sourceTree = "<group>"; };
DAA175B919D86C620044227B /* markdown_parser.leg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = markdown_parser.leg; sourceTree = "<group>"; }; DAA175B919D86C620044227B /* markdown_parser.leg */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = text; path = markdown_parser.leg; sourceTree = "<group>"; };
DAA175BA19D86C620044227B /* markdown_parser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = markdown_parser.m; sourceTree = "<group>"; }; DAA175BA19D86C620044227B /* markdown_parser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = markdown_parser.m; sourceTree = "<group>"; };
DAA175BB19D86C620044227B /* markdown_peg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = markdown_peg.h; sourceTree = "<group>"; }; DAA175BB19D86C620044227B /* markdown_peg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = markdown_peg.h; sourceTree = "<group>"; };
DAA175EB19D86C620044227B /* parsing_functions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = parsing_functions.m; sourceTree = "<group>"; }; DAA175EB19D86C620044227B /* parsing_functions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = parsing_functions.m; sourceTree = "<group>"; };
DAA175EC19D86C620044227B /* platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platform.h; sourceTree = "<group>"; }; DAA175EC19D86C620044227B /* platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platform.h; sourceTree = "<group>"; };
DAA175ED19D86C620044227B /* README.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.markdown; sourceTree = "<group>"; }; DAA175ED19D86C620044227B /* README.markdown */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = net.daringfireball.markdown; path = README.markdown; sourceTree = "<group>"; };
DAA175EE19D86C620044227B /* README_PEG-MARKDWON.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "README_PEG-MARKDWON.markdown"; sourceTree = "<group>"; }; DAA175EE19D86C620044227B /* README_PEG-MARKDWON.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "README_PEG-MARKDWON.markdown"; sourceTree = "<group>"; };
DAA175EF19D86C620044227B /* utility_functions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = utility_functions.m; sourceTree = "<group>"; }; DAA175EF19D86C620044227B /* utility_functions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = utility_functions.m; sourceTree = "<group>"; };
DAA1761C19D86E800044227B /* attributed-markdown.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "attributed-markdown.pch"; sourceTree = "<group>"; }; DAA1761C19D86E800044227B /* attributed-markdown.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "attributed-markdown.pch"; sourceTree = "<group>"; };
@@ -859,9 +871,35 @@
DAA1763C19D8B82B0044227B /* copy_pw.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = copy_pw.png; sourceTree = "<group>"; }; DAA1763C19D8B82B0044227B /* copy_pw.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = copy_pw.png; sourceTree = "<group>"; };
DAA1763D19D8B82B0044227B /* choose_type@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "choose_type@2x.png"; sourceTree = "<group>"; }; DAA1763D19D8B82B0044227B /* choose_type@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "choose_type@2x.png"; sourceTree = "<group>"; };
DAA1763E19D8B82B0044227B /* choose_type.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = choose_type.png; sourceTree = "<group>"; }; DAA1763E19D8B82B0044227B /* choose_type.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = choose_type.png; sourceTree = "<group>"; };
DAA449D01EEC4B5800E7BDD5 /* mpw-marshall.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-marshall.c"; sourceTree = "<group>"; }; DAA449D01EEC4B5800E7BDD5 /* mpw-marshal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-marshal.c"; sourceTree = "<group>"; };
DAA449D11EEC4B5800E7BDD5 /* mpw-marshall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-marshall.h"; sourceTree = "<group>"; }; DAA449D11EEC4B5800E7BDD5 /* mpw-marshal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-marshal.h"; sourceTree = "<group>"; };
DAAC35DD156BD77D00C5FD93 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; }; DAAC35DD156BD77D00C5FD93 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; };
DAB07C9B1F7725C500CC6D43 /* aes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aes.c; sourceTree = "<group>"; };
DAB07C9C1F7725C500CC6D43 /* aes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes.h; sourceTree = "<group>"; };
DAB7AE5C1F3D752900C856B1 /* libjson-c.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libjson-c.a"; path = "External/libjson-c/libjson-c-ios/lib/libjson-c.a"; sourceTree = "<group>"; };
DAB7AE611F3D755B00C856B1 /* arraylist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arraylist.h; sourceTree = "<group>"; };
DAB7AE621F3D755B00C856B1 /* bits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bits.h; sourceTree = "<group>"; };
DAB7AE631F3D755B00C856B1 /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = "<group>"; };
DAB7AE641F3D755B00C856B1 /* json.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json.h; sourceTree = "<group>"; };
DAB7AE651F3D755B00C856B1 /* json_c_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_c_version.h; sourceTree = "<group>"; };
DAB7AE661F3D755B00C856B1 /* json_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_config.h; sourceTree = "<group>"; };
DAB7AE671F3D755B00C856B1 /* json_inttypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_inttypes.h; sourceTree = "<group>"; };
DAB7AE681F3D755B00C856B1 /* json_object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_object.h; sourceTree = "<group>"; };
DAB7AE691F3D755B00C856B1 /* json_object_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_object_iterator.h; sourceTree = "<group>"; };
DAB7AE6A1F3D755B00C856B1 /* json_object_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_object_private.h; sourceTree = "<group>"; };
DAB7AE6B1F3D755B00C856B1 /* json_pointer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_pointer.h; sourceTree = "<group>"; };
DAB7AE6C1F3D755B00C856B1 /* json_tokener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_tokener.h; sourceTree = "<group>"; };
DAB7AE6D1F3D755B00C856B1 /* json_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_util.h; sourceTree = "<group>"; };
DAB7AE6E1F3D755B00C856B1 /* json_visit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_visit.h; sourceTree = "<group>"; };
DAB7AE6F1F3D755B00C856B1 /* linkhash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = linkhash.h; sourceTree = "<group>"; };
DAB7AE701F3D755B00C856B1 /* math_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = math_compat.h; sourceTree = "<group>"; };
DAB7AE711F3D755B00C856B1 /* printbuf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = printbuf.h; sourceTree = "<group>"; };
DAB7AE721F3D755B00C856B1 /* random_seed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = random_seed.h; sourceTree = "<group>"; };
DAB7AE731F3D755B00C856B1 /* strdup_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = strdup_compat.h; sourceTree = "<group>"; };
DAB7AE741F3D755B00C856B1 /* vasprintf_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vasprintf_compat.h; sourceTree = "<group>"; };
DAB7AE761F3D755B00C856B1 /* libjson-c.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libjson-c.a"; sourceTree = "<group>"; };
DAB7AE971F3DDEE000C856B1 /* mpw-marshal-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-marshal-util.h"; sourceTree = "<group>"; };
DAB7AE981F3DDEE000C856B1 /* mpw-marshal-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-marshal-util.c"; sourceTree = "<group>"; };
DABB981515100B4000B05417 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; DABB981515100B4000B05417 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
DABD360F1711E29400CF925C /* ui_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ui_background.png; sourceTree = "<group>"; }; DABD360F1711E29400CF925C /* ui_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ui_background.png; sourceTree = "<group>"; };
DABD36101711E29400CF925C /* ui_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ui_background@2x.png"; sourceTree = "<group>"; }; DABD36101711E29400CF925C /* ui_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ui_background@2x.png"; sourceTree = "<group>"; };
@@ -1475,7 +1513,7 @@
DABD38C11711E29700CF925C /* tip_location_teal@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_teal@2x.png"; sourceTree = "<group>"; }; DABD38C11711E29700CF925C /* tip_location_teal@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_teal@2x.png"; sourceTree = "<group>"; };
DABD38C21711E29700CF925C /* tip_location_wood.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_location_wood.png; sourceTree = "<group>"; }; DABD38C21711E29700CF925C /* tip_location_wood.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = tip_location_wood.png; sourceTree = "<group>"; };
DABD38C31711E29700CF925C /* tip_location_wood@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_wood@2x.png"; sourceTree = "<group>"; }; DABD38C31711E29700CF925C /* tip_location_wood@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tip_location_wood@2x.png"; sourceTree = "<group>"; };
DABD38C81711E29700CF925C /* jquery-1.6.1.min.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "jquery-1.6.1.min.js"; sourceTree = "<group>"; }; DABD38C81711E29700CF925C /* jquery-1.6.1.min.js */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = sourcecode.javascript; path = "jquery-1.6.1.min.js"; sourceTree = "<group>"; };
DABD38C91711E29700CF925C /* keypad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = keypad.png; sourceTree = "<group>"; }; DABD38C91711E29700CF925C /* keypad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = keypad.png; sourceTree = "<group>"; };
DABD38CA1711E29700CF925C /* logo-bare.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "logo-bare.png"; sourceTree = "<group>"; }; DABD38CA1711E29700CF925C /* logo-bare.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "logo-bare.png"; sourceTree = "<group>"; };
DABD38CB1711E29700CF925C /* menu-icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu-icon.png"; sourceTree = "<group>"; }; DABD38CB1711E29700CF925C /* menu-icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu-icon.png"; sourceTree = "<group>"; };
@@ -1514,7 +1552,7 @@
DABD3BF01711E2DC00CF925C /* MPiOSConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPiOSConfig.h; sourceTree = "<group>"; }; DABD3BF01711E2DC00CF925C /* MPiOSConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPiOSConfig.h; sourceTree = "<group>"; };
DABD3BF11711E2DC00CF925C /* MPiOSConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPiOSConfig.m; sourceTree = "<group>"; }; DABD3BF11711E2DC00CF925C /* MPiOSConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPiOSConfig.m; sourceTree = "<group>"; };
DABD3BF31711E2DC00CF925C /* MasterPassword-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "MasterPassword-Info.plist"; sourceTree = "<group>"; }; DABD3BF31711E2DC00CF925C /* MasterPassword-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "MasterPassword-Info.plist"; sourceTree = "<group>"; };
DABD3BF81711E2DC00CF925C /* MasterPassword.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = MasterPassword.entitlements; sourceTree = "<group>"; }; DABD3BF81711E2DC00CF925C /* MasterPassword.entitlements */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = text.xml; path = MasterPassword.entitlements; sourceTree = "<group>"; };
DABD3BF91711E2DC00CF925C /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; }; DABD3BF91711E2DC00CF925C /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
DABD3BFB1711E2DC00CF925C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; DABD3BFB1711E2DC00CF925C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
DABD3BFC1711E2DC00CF925C /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; DABD3BFC1711E2DC00CF925C /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
@@ -1527,8 +1565,7 @@
DAC632871486D95D0075AEA5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; DAC632871486D95D0075AEA5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
DAC77CAD148291A600BCF976 /* libPearl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPearl.a; sourceTree = BUILT_PRODUCTS_DIR; }; DAC77CAD148291A600BCF976 /* libPearl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPearl.a; sourceTree = BUILT_PRODUCTS_DIR; };
DAC77CB1148291A600BCF976 /* Pearl-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Pearl-Prefix.pch"; path = "../../Source/Pearl/Pearl-Prefix.pch"; sourceTree = "<group>"; }; DAC77CB1148291A600BCF976 /* Pearl-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Pearl-Prefix.pch"; path = "../../Source/Pearl/Pearl-Prefix.pch"; sourceTree = "<group>"; };
DACA29711705E1A8002C6C22 /* ciphers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ciphers.plist; sourceTree = "<group>"; }; DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = text; path = dictionary.lst; sourceTree = "<group>"; };
DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dictionary.lst; sourceTree = "<group>"; };
DACA29771705E2BD002C6C22 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = "<group>"; }; DACA29771705E2BD002C6C22 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = "<group>"; };
DACA298C1705E2BD002C6C22 /* JRSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JRSwizzle.m; sourceTree = "<group>"; }; DACA298C1705E2BD002C6C22 /* JRSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JRSwizzle.m; sourceTree = "<group>"; };
DACA29B51705E2DE002C6C22 /* UIColor+Expanded.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+Expanded.h"; sourceTree = "<group>"; }; DACA29B51705E2DE002C6C22 /* UIColor+Expanded.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+Expanded.h"; sourceTree = "<group>"; };
@@ -1545,7 +1582,6 @@
DAE1EF2317E942DE00BC0086 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; }; DAE1EF2317E942DE00BC0086 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
DAE2726119CE9CB3007C5262 /* UITableViewCell+PearlDeque.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewCell+PearlDeque.m"; sourceTree = "<group>"; }; DAE2726119CE9CB3007C5262 /* UITableViewCell+PearlDeque.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableViewCell+PearlDeque.m"; sourceTree = "<group>"; };
DAE2726219CE9CB3007C5262 /* UITableViewCell+PearlDeque.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewCell+PearlDeque.h"; sourceTree = "<group>"; }; DAE2726219CE9CB3007C5262 /* UITableViewCell+PearlDeque.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableViewCell+PearlDeque.h"; sourceTree = "<group>"; };
DAE8E65119867AB500416A0F /* libopensslcrypto-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopensslcrypto-ios.a"; sourceTree = "<group>"; };
DAEBC45214F6364500987BF6 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; DAEBC45214F6364500987BF6 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Touches.m"; sourceTree = "<group>"; }; DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Touches.m"; sourceTree = "<group>"; };
DAEC85B218E3DD9A007FC0DF /* PearlUINavigationBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUINavigationBar.m; sourceTree = "<group>"; }; DAEC85B218E3DD9A007FC0DF /* PearlUINavigationBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUINavigationBar.m; sourceTree = "<group>"; };
@@ -1584,16 +1620,12 @@
DAFE45F315039823003ABA7C /* PearlStrings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlStrings.m; sourceTree = "<group>"; }; DAFE45F315039823003ABA7C /* PearlStrings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlStrings.m; sourceTree = "<group>"; };
DAFE45F415039823003ABA7C /* PearlStringUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlStringUtils.h; sourceTree = "<group>"; }; DAFE45F415039823003ABA7C /* PearlStringUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlStringUtils.h; sourceTree = "<group>"; };
DAFE45F515039823003ABA7C /* PearlStringUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlStringUtils.m; sourceTree = "<group>"; }; DAFE45F515039823003ABA7C /* PearlStringUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlStringUtils.m; sourceTree = "<group>"; };
DAFE45F815039823003ABA7C /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = "<group>"; }; DAFE45F815039823003ABA7C /* README */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
DAFE45FB15039823003ABA7C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Pearl.strings; sourceTree = "<group>"; }; DAFE45FB15039823003ABA7C /* en */ = {isa = PBXFileReference; fileEncoding = 2483028224; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Pearl.strings; sourceTree = "<group>"; };
DAFE45FD15039823003ABA7C /* Pearl-Crypto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Pearl-Crypto.h"; sourceTree = "<group>"; }; DAFE45FD15039823003ABA7C /* Pearl-Crypto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Pearl-Crypto.h"; sourceTree = "<group>"; };
DAFE45FE15039823003ABA7C /* PearlCryptUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlCryptUtils.h; sourceTree = "<group>"; };
DAFE45FF15039823003ABA7C /* PearlCryptUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlCryptUtils.m; sourceTree = "<group>"; };
DAFE460015039823003ABA7C /* PearlKeyChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlKeyChain.h; sourceTree = "<group>"; }; DAFE460015039823003ABA7C /* PearlKeyChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlKeyChain.h; sourceTree = "<group>"; };
DAFE460115039823003ABA7C /* PearlKeyChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlKeyChain.m; sourceTree = "<group>"; }; DAFE460115039823003ABA7C /* PearlKeyChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlKeyChain.m; sourceTree = "<group>"; };
DAFE460215039823003ABA7C /* PearlRSAKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlRSAKey.h; sourceTree = "<group>"; }; DAFE460615039823003ABA7C /* README */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
DAFE460315039823003ABA7C /* PearlRSAKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlRSAKey.m; sourceTree = "<group>"; };
DAFE460615039823003ABA7C /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
DAFE460815039823003ABA7C /* Pearl-UIKit-Dependencies.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Pearl-UIKit-Dependencies.h"; sourceTree = "<group>"; }; DAFE460815039823003ABA7C /* Pearl-UIKit-Dependencies.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Pearl-UIKit-Dependencies.h"; sourceTree = "<group>"; };
DAFE460915039823003ABA7C /* Pearl-UIKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Pearl-UIKit.h"; sourceTree = "<group>"; }; DAFE460915039823003ABA7C /* Pearl-UIKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Pearl-UIKit.h"; sourceTree = "<group>"; };
DAFE460A15039823003ABA7C /* PearlAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlAlert.h; sourceTree = "<group>"; }; DAFE460A15039823003ABA7C /* PearlAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlAlert.h; sourceTree = "<group>"; };
@@ -1604,10 +1636,6 @@
DAFE460F15039823003ABA7C /* PearlBoxView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlBoxView.m; sourceTree = "<group>"; }; DAFE460F15039823003ABA7C /* PearlBoxView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlBoxView.m; sourceTree = "<group>"; };
DAFE461015039823003ABA7C /* PearlGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlGradientView.h; sourceTree = "<group>"; }; DAFE461015039823003ABA7C /* PearlGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlGradientView.h; sourceTree = "<group>"; };
DAFE461115039823003ABA7C /* PearlGradientView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlGradientView.m; sourceTree = "<group>"; }; DAFE461115039823003ABA7C /* PearlGradientView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlGradientView.m; sourceTree = "<group>"; };
DAFE461215039823003ABA7C /* PearlLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLayout.h; sourceTree = "<group>"; };
DAFE461315039823003ABA7C /* PearlLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLayout.m; sourceTree = "<group>"; };
DAFE461415039823003ABA7C /* PearlLayoutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLayoutView.h; sourceTree = "<group>"; };
DAFE461515039823003ABA7C /* PearlLayoutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLayoutView.m; sourceTree = "<group>"; };
DAFE461615039823003ABA7C /* PearlMessageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlMessageView.h; sourceTree = "<group>"; }; DAFE461615039823003ABA7C /* PearlMessageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlMessageView.h; sourceTree = "<group>"; };
DAFE461715039823003ABA7C /* PearlMessageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlMessageView.m; sourceTree = "<group>"; }; DAFE461715039823003ABA7C /* PearlMessageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlMessageView.m; sourceTree = "<group>"; };
DAFE461815039823003ABA7C /* PearlRootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlRootViewController.h; sourceTree = "<group>"; }; DAFE461815039823003ABA7C /* PearlRootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlRootViewController.h; sourceTree = "<group>"; };
@@ -1620,7 +1648,7 @@
DAFE461F15039823003ABA7C /* PearlUIUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUIUtils.m; sourceTree = "<group>"; }; DAFE461F15039823003ABA7C /* PearlUIUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUIUtils.m; sourceTree = "<group>"; };
DAFE462015039823003ABA7C /* PearlValidatingTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlValidatingTextField.h; sourceTree = "<group>"; }; DAFE462015039823003ABA7C /* PearlValidatingTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlValidatingTextField.h; sourceTree = "<group>"; };
DAFE462115039823003ABA7C /* PearlValidatingTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlValidatingTextField.m; sourceTree = "<group>"; }; DAFE462115039823003ABA7C /* PearlValidatingTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlValidatingTextField.m; sourceTree = "<group>"; };
DAFE462415039823003ABA7C /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README; sourceTree = "<group>"; }; DAFE462415039823003ABA7C /* README */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
DAFE4A1115039824003ABA7C /* UIImage+PearlScaling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+PearlScaling.h"; sourceTree = "<group>"; }; DAFE4A1115039824003ABA7C /* UIImage+PearlScaling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+PearlScaling.h"; sourceTree = "<group>"; };
DAFE4A1215039824003ABA7C /* UIImage+PearlScaling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+PearlScaling.m"; sourceTree = "<group>"; }; DAFE4A1215039824003ABA7C /* UIImage+PearlScaling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+PearlScaling.m"; sourceTree = "<group>"; };
DAFE4A60150399FF003ABA7C /* PearlAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlAppDelegate.m; sourceTree = "<group>"; }; DAFE4A60150399FF003ABA7C /* PearlAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlAppDelegate.m; sourceTree = "<group>"; };
@@ -1649,9 +1677,11 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DAB7AE5D1F3D752900C856B1 /* libjson-c.a in Frameworks */,
DA95B50C1C476B6A0067F5EF /* LocalAuthentication.framework in Frameworks */, DA95B50C1C476B6A0067F5EF /* LocalAuthentication.framework in Frameworks */,
DA2C3D651BD9612F001137B3 /* libz.tbd in Frameworks */, DA2C3D651BD9612F001137B3 /* libz.tbd in Frameworks */,
DA2C3D631BD96126001137B3 /* libc++.tbd in Frameworks */, DA2C3D631BD96126001137B3 /* libc++.tbd in Frameworks */,
DAB7AE771F3D755B00C856B1 /* libjson-c.a in Frameworks */,
DAA1761B19D86D0D0044227B /* libAttributedMarkdown.a in Frameworks */, DAA1761B19D86D0D0044227B /* libAttributedMarkdown.a in Frameworks */,
DA0CC5421EB57BD4009A8ED9 /* Crashlytics.framework in Frameworks */, DA0CC5421EB57BD4009A8ED9 /* Crashlytics.framework in Frameworks */,
DA32D03E19D11293004F3F0E /* libKCOrderedAccessorFix.a in Frameworks */, DA32D03E19D11293004F3F0E /* libKCOrderedAccessorFix.a in Frameworks */,
@@ -1722,6 +1752,8 @@
93D39A2239FFFE6BEC83E191 /* C */ = { 93D39A2239FFFE6BEC83E191 /* C */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DAB07C9B1F7725C500CC6D43 /* aes.c */,
DAB07C9C1F7725C500CC6D43 /* aes.h */,
DA5B0B3E1F36469400B663F0 /* base64.c */, DA5B0B3E1F36469400B663F0 /* base64.c */,
DA5B0B3F1F36469400B663F0 /* base64.h */, DA5B0B3F1F36469400B663F0 /* base64.h */,
93D390A99850139D0FF0211E /* mpw-algorithm_v0.c */, 93D390A99850139D0FF0211E /* mpw-algorithm_v0.c */,
@@ -1730,8 +1762,10 @@
93D39D4E713564B7654341B0 /* mpw-algorithm_v3.c */, 93D39D4E713564B7654341B0 /* mpw-algorithm_v3.c */,
93D3969393A3A46BD27D7078 /* mpw-algorithm.c */, 93D3969393A3A46BD27D7078 /* mpw-algorithm.c */,
93D3990D850D76A94C6B7A4D /* mpw-algorithm.h */, 93D3990D850D76A94C6B7A4D /* mpw-algorithm.h */,
DAA449D01EEC4B5800E7BDD5 /* mpw-marshall.c */, DAB7AE981F3DDEE000C856B1 /* mpw-marshal-util.c */,
DAA449D11EEC4B5800E7BDD5 /* mpw-marshall.h */, DAB7AE971F3DDEE000C856B1 /* mpw-marshal-util.h */,
DAA449D01EEC4B5800E7BDD5 /* mpw-marshal.c */,
DAA449D11EEC4B5800E7BDD5 /* mpw-marshal.h */,
93D392C5A6572DB0EB5B82C8 /* mpw-types.c */, 93D392C5A6572DB0EB5B82C8 /* mpw-types.c */,
93D39AFD17CBE324D745DAB0 /* mpw-types.h */, 93D39AFD17CBE324D745DAB0 /* mpw-types.h */,
93D396C311C3725870343EE0 /* mpw-util.c */, 93D396C311C3725870343EE0 /* mpw-util.c */,
@@ -2018,6 +2052,7 @@
DA5BFA47147E415C00F98B1E /* Frameworks */ = { DA5BFA47147E415C00F98B1E /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DAB7AE5C1F3D752900C856B1 /* libjson-c.a */,
DA6701B716406A4100B61001 /* Accounts.framework */, DA6701B716406A4100B61001 /* Accounts.framework */,
DA6701DF16406BB400B61001 /* AdSupport.framework */, DA6701DF16406BB400B61001 /* AdSupport.framework */,
DA5BFA4E147E415C00F98B1E /* CoreData.framework */, DA5BFA4E147E415C00F98B1E /* CoreData.framework */,
@@ -2042,24 +2077,6 @@
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DA5E5C6317248959003798D8 /* lib */ = {
isa = PBXGroup;
children = (
DA5E5C6417248959003798D8 /* include */,
DA7F2C5E1C48B70D00404A26 /* libopensslcrypto-ios-sim.a */,
DAE8E65119867AB500416A0F /* libopensslcrypto-ios.a */,
);
path = lib;
sourceTree = "<group>";
};
DA5E5C6417248959003798D8 /* include */ = {
isa = PBXGroup;
children = (
DA25C6B31980D3C10046CDCF /* openssl */,
);
path = include;
sourceTree = "<group>";
};
DAA141181922FED80032B392 /* iOS */ = { DAA141181922FED80032B392 /* iOS */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -2097,6 +2114,59 @@
path = AttributedMarkdown; path = AttributedMarkdown;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DAB7AE5E1F3D755B00C856B1 /* libjson-c-ios */ = {
isa = PBXGroup;
children = (
DAB7AE5F1F3D755B00C856B1 /* include */,
DAB7AE751F3D755B00C856B1 /* lib */,
);
name = "libjson-c-ios";
path = "libjson-c/libjson-c-ios";
sourceTree = "<group>";
};
DAB7AE5F1F3D755B00C856B1 /* include */ = {
isa = PBXGroup;
children = (
DAB7AE601F3D755B00C856B1 /* json-c */,
);
path = include;
sourceTree = "<group>";
};
DAB7AE601F3D755B00C856B1 /* json-c */ = {
isa = PBXGroup;
children = (
DAB7AE611F3D755B00C856B1 /* arraylist.h */,
DAB7AE621F3D755B00C856B1 /* bits.h */,
DAB7AE631F3D755B00C856B1 /* debug.h */,
DAB7AE641F3D755B00C856B1 /* json.h */,
DAB7AE651F3D755B00C856B1 /* json_c_version.h */,
DAB7AE661F3D755B00C856B1 /* json_config.h */,
DAB7AE671F3D755B00C856B1 /* json_inttypes.h */,
DAB7AE681F3D755B00C856B1 /* json_object.h */,
DAB7AE691F3D755B00C856B1 /* json_object_iterator.h */,
DAB7AE6A1F3D755B00C856B1 /* json_object_private.h */,
DAB7AE6B1F3D755B00C856B1 /* json_pointer.h */,
DAB7AE6C1F3D755B00C856B1 /* json_tokener.h */,
DAB7AE6D1F3D755B00C856B1 /* json_util.h */,
DAB7AE6E1F3D755B00C856B1 /* json_visit.h */,
DAB7AE6F1F3D755B00C856B1 /* linkhash.h */,
DAB7AE701F3D755B00C856B1 /* math_compat.h */,
DAB7AE711F3D755B00C856B1 /* printbuf.h */,
DAB7AE721F3D755B00C856B1 /* random_seed.h */,
DAB7AE731F3D755B00C856B1 /* strdup_compat.h */,
DAB7AE741F3D755B00C856B1 /* vasprintf_compat.h */,
);
path = "json-c";
sourceTree = "<group>";
};
DAB7AE751F3D755B00C856B1 /* lib */ = {
isa = PBXGroup;
children = (
DAB7AE761F3D755B00C856B1 /* libjson-c.a */,
);
path = lib;
sourceTree = "<group>";
};
DABD360D1711E29400CF925C /* Media */ = { DABD360D1711E29400CF925C /* Media */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -2945,6 +3015,7 @@
DACA22121705DDC5002C6C22 /* External */ = { DACA22121705DDC5002C6C22 /* External */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DAB7AE5E1F3D755B00C856B1 /* libjson-c-ios */,
DA0978D81E9A81EE00F0BFE8 /* libsodium-ios */, DA0978D81E9A81EE00F0BFE8 /* libsodium-ios */,
DAA1759319D86C610044227B /* AttributedMarkdown */, DAA1759319D86C610044227B /* AttributedMarkdown */,
DAFC5662172C57EC00CB5CC5 /* InAppSettingsKit */, DAFC5662172C57EC00CB5CC5 /* InAppSettingsKit */,
@@ -2971,7 +3042,6 @@
DACA29701705E1A8002C6C22 /* Data */ = { DACA29701705E1A8002C6C22 /* Data */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DACA29711705E1A8002C6C22 /* ciphers.plist */,
DACA29721705E1A8002C6C22 /* dictionary.lst */, DACA29721705E1A8002C6C22 /* dictionary.lst */,
); );
path = Data; path = Data;
@@ -3092,15 +3162,12 @@
DAFE45FC15039823003ABA7C /* Pearl-Crypto */ = { DAFE45FC15039823003ABA7C /* Pearl-Crypto */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA5E5C6317248959003798D8 /* lib */,
DAFE45FD15039823003ABA7C /* Pearl-Crypto.h */, DAFE45FD15039823003ABA7C /* Pearl-Crypto.h */,
DA30E9D115722EE500A68B4C /* Pearl-Crypto.m */, DA30E9D115722EE500A68B4C /* Pearl-Crypto.m */,
DAFE45FE15039823003ABA7C /* PearlCryptUtils.h */, DA45711C1F572F1E00D54152 /* PearlCryptUtils.h */,
DAFE45FF15039823003ABA7C /* PearlCryptUtils.m */, DA45711B1F572F1E00D54152 /* PearlCryptUtils.m */,
DAFE460015039823003ABA7C /* PearlKeyChain.h */, DAFE460015039823003ABA7C /* PearlKeyChain.h */,
DAFE460115039823003ABA7C /* PearlKeyChain.m */, DAFE460115039823003ABA7C /* PearlKeyChain.m */,
DAFE460215039823003ABA7C /* PearlRSAKey.h */,
DAFE460315039823003ABA7C /* PearlRSAKey.m */,
DAFE460615039823003ABA7C /* README */, DAFE460615039823003ABA7C /* README */,
); );
path = "Pearl-Crypto"; path = "Pearl-Crypto";
@@ -3128,10 +3195,6 @@
DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */, DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */,
DAFE461015039823003ABA7C /* PearlGradientView.h */, DAFE461015039823003ABA7C /* PearlGradientView.h */,
DAFE461115039823003ABA7C /* PearlGradientView.m */, DAFE461115039823003ABA7C /* PearlGradientView.m */,
DAFE461215039823003ABA7C /* PearlLayout.h */,
DAFE461315039823003ABA7C /* PearlLayout.m */,
DAFE461415039823003ABA7C /* PearlLayoutView.h */,
DAFE461515039823003ABA7C /* PearlLayoutView.m */,
DAFE461615039823003ABA7C /* PearlMessageView.h */, DAFE461615039823003ABA7C /* PearlMessageView.h */,
DAFE461715039823003ABA7C /* PearlMessageView.m */, DAFE461715039823003ABA7C /* PearlMessageView.m */,
DACE2F6919BA6A2A0010F92E /* PearlMutableStaticTableViewController.h */, DACE2F6919BA6A2A0010F92E /* PearlMutableStaticTableViewController.h */,
@@ -3227,7 +3290,6 @@
isa = PBXHeadersBuildPhase; isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DA25C6B41980D3C50046CDCF /* openssl in Headers */,
DAFE4A1315039824003ABA7C /* NSObject+PearlExport.h in Headers */, DAFE4A1315039824003ABA7C /* NSObject+PearlExport.h in Headers */,
DAFE4A1515039824003ABA7C /* NSString+PearlNSArrayFormat.h in Headers */, DAFE4A1515039824003ABA7C /* NSString+PearlNSArrayFormat.h in Headers */,
DAFE4A1715039824003ABA7C /* NSString+PearlSEL.h in Headers */, DAFE4A1715039824003ABA7C /* NSString+PearlSEL.h in Headers */,
@@ -3249,9 +3311,7 @@
DAFE4A2F15039824003ABA7C /* PearlStringUtils.h in Headers */, DAFE4A2F15039824003ABA7C /* PearlStringUtils.h in Headers */,
DA2CA4E018D28859007798F8 /* NSTimer+PearlBlock.h in Headers */, DA2CA4E018D28859007798F8 /* NSTimer+PearlBlock.h in Headers */,
DAFE4A3315039824003ABA7C /* Pearl-Crypto.h in Headers */, DAFE4A3315039824003ABA7C /* Pearl-Crypto.h in Headers */,
DAFE4A3415039824003ABA7C /* PearlCryptUtils.h in Headers */,
DAFE4A3615039824003ABA7C /* PearlKeyChain.h in Headers */, DAFE4A3615039824003ABA7C /* PearlKeyChain.h in Headers */,
DAFE4A3815039824003ABA7C /* PearlRSAKey.h in Headers */,
DAE2726419CE9CB3007C5262 /* UITableViewCell+PearlDeque.h in Headers */, DAE2726419CE9CB3007C5262 /* UITableViewCell+PearlDeque.h in Headers */,
DACE2F6E19BA6A2A0010F92E /* UIView+FontScale.h in Headers */, DACE2F6E19BA6A2A0010F92E /* UIView+FontScale.h in Headers */,
DA32D01B19D046E1004F3F0E /* PearlFixedTableView.h in Headers */, DA32D01B19D046E1004F3F0E /* PearlFixedTableView.h in Headers */,
@@ -3263,8 +3323,6 @@
DAFE4A4215039824003ABA7C /* PearlBoxView.h in Headers */, DAFE4A4215039824003ABA7C /* PearlBoxView.h in Headers */,
DAEFB01F19BCBD9E00525079 /* UIView+LayoutGone.h in Headers */, DAEFB01F19BCBD9E00525079 /* UIView+LayoutGone.h in Headers */,
DAFE4A4415039824003ABA7C /* PearlGradientView.h in Headers */, DAFE4A4415039824003ABA7C /* PearlGradientView.h in Headers */,
DAFE4A4615039824003ABA7C /* PearlLayout.h in Headers */,
DAFE4A4815039824003ABA7C /* PearlLayoutView.h in Headers */,
DAFE4A4A15039824003ABA7C /* PearlMessageView.h in Headers */, DAFE4A4A15039824003ABA7C /* PearlMessageView.h in Headers */,
DACE2F6619BA6A0A0010F92E /* PearlProfiler.h in Headers */, DACE2F6619BA6A0A0010F92E /* PearlProfiler.h in Headers */,
DAFE4A4C15039824003ABA7C /* PearlRootViewController.h in Headers */, DAFE4A4C15039824003ABA7C /* PearlRootViewController.h in Headers */,
@@ -3309,6 +3367,36 @@
}; };
/* End PBXHeadersBuildPhase section */ /* End PBXHeadersBuildPhase section */
/* Begin PBXLegacyTarget section */
DAB7AE3D1F3D464A00C856B1 /* libjson-c-ios */ = {
isa = PBXLegacyTarget;
buildArgumentsString = "$(ACTION)";
buildConfigurationList = DAB7AE411F3D464A00C856B1 /* Build configuration list for PBXLegacyTarget "libjson-c-ios" */;
buildPhases = (
);
buildToolPath = "Scripts/build_libjson-c-ios";
buildWorkingDirectory = "";
dependencies = (
);
name = "libjson-c-ios";
passBuildSettingsInEnvironment = 1;
productName = "libjson-c";
};
DAB7AE471F3D468300C856B1 /* libsodium-ios */ = {
isa = PBXLegacyTarget;
buildArgumentsString = "$(ACTION)";
buildConfigurationList = DAB7AE481F3D468300C856B1 /* Build configuration list for PBXLegacyTarget "libsodium-ios" */;
buildPhases = (
);
buildToolPath = "Scripts/build_libsodium-ios";
dependencies = (
);
name = "libsodium-ios";
passBuildSettingsInEnvironment = 1;
productName = "libsodium-ios";
};
/* End PBXLegacyTarget section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
DA32D01F19D111C6004F3F0E /* KCOrderedAccessorFix */ = { DA32D01F19D111C6004F3F0E /* KCOrderedAccessorFix */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
@@ -3332,7 +3420,6 @@
buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword" */; buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword" */;
buildPhases = ( buildPhases = (
DA8D88E019DA412A00B189D0 /* Run Script: genassets */, DA8D88E019DA412A00B189D0 /* Run Script: genassets */,
DA0E86A81E9BED2C00F4D60E /* Run Script: libsodium/dist-build/ios.sh */,
DA5BFA40147E415C00F98B1E /* Sources */, DA5BFA40147E415C00F98B1E /* Sources */,
DA5BFA41147E415C00F98B1E /* Frameworks */, DA5BFA41147E415C00F98B1E /* Frameworks */,
DA5BFA42147E415C00F98B1E /* Resources */, DA5BFA42147E415C00F98B1E /* Resources */,
@@ -3342,6 +3429,8 @@
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
DAB7AE4E1F3D618700C856B1 /* PBXTargetDependency */,
DAB7AE501F3D618700C856B1 /* PBXTargetDependency */,
); );
name = MasterPassword; name = MasterPassword;
productName = MasterPassword; productName = MasterPassword;
@@ -3403,6 +3492,7 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = DAC77CB7148291A600BCF976 /* Build configuration list for PBXNativeTarget "Pearl" */; buildConfigurationList = DAC77CB7148291A600BCF976 /* Build configuration list for PBXNativeTarget "Pearl" */;
buildPhases = ( buildPhases = (
DAB7AE4C1F3D56AD00C856B1 /* ShellScript */,
DAC77CAB148291A600BCF976 /* Headers */, DAC77CAB148291A600BCF976 /* Headers */,
DAC77CA9148291A600BCF976 /* Sources */, DAC77CA9148291A600BCF976 /* Sources */,
DAC77CAA148291A600BCF976 /* Frameworks */, DAC77CAA148291A600BCF976 /* Frameworks */,
@@ -3468,6 +3558,16 @@
CreatedOnToolsVersion = 6.0; CreatedOnToolsVersion = 6.0;
DevelopmentTeam = HL3Q45LX9N; DevelopmentTeam = HL3Q45LX9N;
}; };
DAB7AE3D1F3D464A00C856B1 = {
CreatedOnToolsVersion = 8.3.3;
DevelopmentTeam = HL3Q45LX9N;
ProvisioningStyle = Automatic;
};
DAB7AE471F3D468300C856B1 = {
CreatedOnToolsVersion = 8.3.3;
DevelopmentTeam = HL3Q45LX9N;
ProvisioningStyle = Automatic;
};
DAC6325C1486805C0075AEA5 = { DAC6325C1486805C0075AEA5 = {
DevelopmentTeam = HL3Q45LX9N; DevelopmentTeam = HL3Q45LX9N;
}; };
@@ -3588,6 +3688,8 @@
DAFC5654172C573B00CB5CC5 /* InAppSettingsKit */, DAFC5654172C573B00CB5CC5 /* InAppSettingsKit */,
DA32D01F19D111C6004F3F0E /* KCOrderedAccessorFix */, DA32D01F19D111C6004F3F0E /* KCOrderedAccessorFix */,
DAA1757C19D86BE70044227B /* AttributedMarkdown */, DAA1757C19D86BE70044227B /* AttributedMarkdown */,
DAB7AE471F3D468300C856B1 /* libsodium-ios */,
DAB7AE3D1F3D464A00C856B1 /* libjson-c-ios */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@@ -3599,7 +3701,6 @@
files = ( files = (
DAFE4A5A1503982E003ABA7C /* Pearl.strings in Resources */, DAFE4A5A1503982E003ABA7C /* Pearl.strings in Resources */,
DA0CC53B1EB57B5C009A8ED9 /* Fabric.plist in Resources */, DA0CC53B1EB57B5C009A8ED9 /* Fabric.plist in Resources */,
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */,
DA32D04F19D2F59B004F3F0E /* meter_fuel@2x.png in Resources */, DA32D04F19D2F59B004F3F0E /* meter_fuel@2x.png in Resources */,
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */, DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
DA45224C190628B2008F650A /* icon_gear@2x.png in Resources */, DA45224C190628B2008F650A /* icon_gear@2x.png in Resources */,
@@ -3783,21 +3884,6 @@
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */
DA0E86A81E9BED2C00F4D60E /* Run Script: libsodium/dist-build/ios.sh */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script: libsodium/dist-build/ios.sh";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = "/bin/sh -e";
shellScript = "cd External/libsodium\n[[ -d libsodium-ios ]] && exit\n\n# Xcode misinterpretes autogen.sh's stderr output as errors so we try to silence it.\n[[ -e configure ]] || { err=$(./autogen.sh 2>&1 >&3); } 3>&1 || { x=$?; echo >&2 \"$err\"; exit $x; }\n./dist-build/ios.sh";
showEnvVarsInLog = 0;
};
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */ = { DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -3828,6 +3914,19 @@
shellScript = "exec Scripts/genassets"; shellScript = "exec Scripts/genassets";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
DAB7AE4C1F3D56AD00C856B1 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "";
};
DAD3125D155288AA00A3F9ED /* Run Script: Fabric */ = { DAD3125D155288AA00A3F9ED /* Run Script: Fabric */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -3872,7 +3971,7 @@
DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */, DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */,
DA0CC5941EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m in Sources */, DA0CC5941EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m in Sources */,
DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */, DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */,
DAA449D21EEC4B5800E7BDD5 /* mpw-marshall.c in Sources */, DAA449D21EEC4B5800E7BDD5 /* mpw-marshal.c in Sources */,
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */, DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */,
DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */, DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */,
DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */, DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */,
@@ -3890,6 +3989,7 @@
DA0CC58E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m in Sources */, DA0CC58E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m in Sources */,
93D39A5FF670957C0AF8298D /* MPSiteCell.m in Sources */, 93D39A5FF670957C0AF8298D /* MPSiteCell.m in Sources */,
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */, 93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */,
DAB7AE991F3DDEE000C856B1 /* mpw-marshal-util.c in Sources */,
DA95B50F1C4776F00067F5EF /* NSMutableSet+Pearl.m in Sources */, DA95B50F1C4776F00067F5EF /* NSMutableSet+Pearl.m in Sources */,
93D394B5036C882B33C71872 /* MPSitesSegue.m in Sources */, 93D394B5036C882B33C71872 /* MPSitesSegue.m in Sources */,
DA0CC5911EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m in Sources */, DA0CC5911EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m in Sources */,
@@ -3899,11 +3999,13 @@
93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */, 93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */,
DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */, DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */,
DA0CC54E1EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld in Sources */, DA0CC54E1EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld in Sources */,
DAB07C9D1F7725C500CC6D43 /* aes.c in Sources */,
93D39EAA4D064193074D3021 /* MPFixable.m in Sources */, 93D39EAA4D064193074D3021 /* MPFixable.m in Sources */,
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */, 93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */,
93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */, 93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */,
93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */, 93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */,
93D390C1B93F9D3AE37DD0A5 /* MPAnswersViewController.m in Sources */, 93D390C1B93F9D3AE37DD0A5 /* MPAnswersViewController.m in Sources */,
DA45711D1F572F1E00D54152 /* PearlCryptUtils.m in Sources */,
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */, 93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */,
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */, 93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */,
93D39B429C67A62E29DC02DA /* MPRootSegue.m in Sources */, 93D39B429C67A62E29DC02DA /* MPRootSegue.m in Sources */,
@@ -3964,18 +4066,14 @@
DAFE4A2B15039824003ABA7C /* PearlObjectUtils.m in Sources */, DAFE4A2B15039824003ABA7C /* PearlObjectUtils.m in Sources */,
DAFE4A2E15039824003ABA7C /* PearlStrings.m in Sources */, DAFE4A2E15039824003ABA7C /* PearlStrings.m in Sources */,
DAFE4A3015039824003ABA7C /* PearlStringUtils.m in Sources */, DAFE4A3015039824003ABA7C /* PearlStringUtils.m in Sources */,
DAFE4A3515039824003ABA7C /* PearlCryptUtils.m in Sources */,
DAFE4A3715039824003ABA7C /* PearlKeyChain.m in Sources */, DAFE4A3715039824003ABA7C /* PearlKeyChain.m in Sources */,
DA72BD7B19C1510C00E6ACFE /* UIView+FontScale.m in Sources */, DA72BD7B19C1510C00E6ACFE /* UIView+FontScale.m in Sources */,
DA250A17195665A100AC23F1 /* UITableView+PearlReloadItems.m in Sources */, DA250A17195665A100AC23F1 /* UITableView+PearlReloadItems.m in Sources */,
DAFE4A3915039824003ABA7C /* PearlRSAKey.m in Sources */,
DAFE4A3F15039824003ABA7C /* PearlAlert.m in Sources */, DAFE4A3F15039824003ABA7C /* PearlAlert.m in Sources */,
DAFE4A4115039824003ABA7C /* PearlArrayTVC.m in Sources */, DAFE4A4115039824003ABA7C /* PearlArrayTVC.m in Sources */,
DAFE4A4315039824003ABA7C /* PearlBoxView.m in Sources */, DAFE4A4315039824003ABA7C /* PearlBoxView.m in Sources */,
DAFE4A4515039824003ABA7C /* PearlGradientView.m in Sources */, DAFE4A4515039824003ABA7C /* PearlGradientView.m in Sources */,
DAFE4A4715039824003ABA7C /* PearlLayout.m in Sources */,
DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */, DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */,
DAFE4A4915039824003ABA7C /* PearlLayoutView.m in Sources */,
DAE2726319CE9CB3007C5262 /* UITableViewCell+PearlDeque.m in Sources */, DAE2726319CE9CB3007C5262 /* UITableViewCell+PearlDeque.m in Sources */,
DAFE4A4B15039824003ABA7C /* PearlMessageView.m in Sources */, DAFE4A4B15039824003ABA7C /* PearlMessageView.m in Sources */,
DACE2F6519BA6A0A0010F92E /* PearlProfiler.m in Sources */, DACE2F6519BA6A0A0010F92E /* PearlProfiler.m in Sources */,
@@ -4051,6 +4149,19 @@
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
DAB7AE4E1F3D618700C856B1 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DAB7AE471F3D468300C856B1 /* libsodium-ios */;
targetProxy = DAB7AE4D1F3D618700C856B1 /* PBXContainerItemProxy */;
};
DAB7AE501F3D618700C856B1 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DAB7AE3D1F3D464A00C856B1 /* libjson-c-ios */;
targetProxy = DAB7AE4F1F3D618700C856B1 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */ /* Begin PBXVariantGroup section */
DA0CC4F51EAB99BA009A8ED9 /* IASKLocalizable.strings */ = { DA0CC4F51EAB99BA009A8ED9 /* IASKLocalizable.strings */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
@@ -4174,6 +4285,8 @@
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"", "\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
"\"$(PROJECT_DIR)/External/libsodium/libsodium-ios/include\"",
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-ios/include\"",
"$(inherited)", "$(inherited)",
); );
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -4212,8 +4325,12 @@
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib", "$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
"$(PROJECT_DIR)/External/libjson-c/libjson-c-ios/lib",
);
OTHER_CFLAGS = (
"-DMPW_SODIUM=1",
"-DMPW_CPERCIVA=0",
); );
OTHER_CFLAGS = "-DHAS_SODIUM=1";
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword; PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword;
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
STRIP_INSTALLED_PRODUCT = YES; STRIP_INSTALLED_PRODUCT = YES;
@@ -4356,6 +4473,8 @@
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"", "\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
"\"$(PROJECT_DIR)/External/libsodium/libsodium-ios/include\"",
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-ios/include\"",
"$(inherited)", "$(inherited)",
); );
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -4444,6 +4563,8 @@
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"", "\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
"\"$(PROJECT_DIR)/External/libsodium/libsodium-ios/include\"",
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-ios/include\"",
"$(inherited)", "$(inherited)",
); );
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
@@ -4480,8 +4601,12 @@
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib", "$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
"$(PROJECT_DIR)/External/libjson-c/libjson-c-ios/lib",
);
OTHER_CFLAGS = (
"-DMPW_SODIUM=1",
"-DMPW_CPERCIVA=0",
); );
OTHER_CFLAGS = "-DHAS_SODIUM=1";
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword; PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword;
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = 1;
@@ -4510,8 +4635,12 @@
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib", "$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
"$(PROJECT_DIR)/External/libjson-c/libjson-c-ios/lib",
);
OTHER_CFLAGS = (
"-DMPW_SODIUM=1",
"-DMPW_CPERCIVA=0",
); );
OTHER_CFLAGS = "-DHAS_SODIUM=1";
PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword; PRODUCT_BUNDLE_IDENTIFIER = com.lyndir.lhunath.MasterPassword;
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
STRIP_INSTALLED_PRODUCT = YES; STRIP_INSTALLED_PRODUCT = YES;
@@ -4535,6 +4664,128 @@
}; };
name = Release; name = Release;
}; };
DAB7AE3E1F3D464A00C856B1 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
DEBUGGING_SYMBOLS = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
MTL_ENABLE_DEBUG_INFO = YES;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
DAB7AE3F1F3D464A00C856B1 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
DAB7AE401F3D464A00C856B1 /* Test */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Test;
};
DAB7AE491F3D468300C856B1 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
DEBUGGING_SYMBOLS = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
MTL_ENABLE_DEBUG_INFO = YES;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
DAB7AE4A1F3D468300C856B1 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
DAB7AE4B1F3D468300C856B1 /* Test */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "";
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Test;
};
DAC632661486805C0075AEA5 /* Debug */ = { DAC632661486805C0075AEA5 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
@@ -4650,6 +4901,26 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Test; defaultConfigurationName = Test;
}; };
DAB7AE411F3D464A00C856B1 /* Build configuration list for PBXLegacyTarget "libjson-c-ios" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DAB7AE3E1F3D464A00C856B1 /* Debug */,
DAB7AE3F1F3D464A00C856B1 /* Release */,
DAB7AE401F3D464A00C856B1 /* Test */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Test;
};
DAB7AE481F3D468300C856B1 /* Build configuration list for PBXLegacyTarget "libsodium-ios" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DAB7AE491F3D468300C856B1 /* Debug */,
DAB7AE4A1F3D468300C856B1 /* Release */,
DAB7AE4B1F3D468300C856B1 /* Test */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Test;
};
DAC632651486805C0075AEA5 /* Build configuration list for PBXNativeTarget "uicolor-utilities" */ = { DAC632651486805C0075AEA5 /* Build configuration list for PBXNativeTarget "uicolor-utilities" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0830"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA1C7AC61F1A8FD8009A3551"
BuildableName = "mpw-bench"
BlueprintName = "mpw-bench"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA1C7AC61F1A8FD8009A3551"
BuildableName = "mpw-bench"
BlueprintName = "mpw-bench"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA1C7AC61F1A8FD8009A3551"
BuildableName = "mpw-bench"
BlueprintName = "mpw-bench"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA1C7AC61F1A8FD8009A3551"
BuildableName = "mpw-bench"
BlueprintName = "mpw-bench"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0830"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA1C7AA61F1A8F24009A3551"
BuildableName = "mpw-cli"
BlueprintName = "mpw-cli"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA1C7AA61F1A8F24009A3551"
BuildableName = "mpw-cli"
BlueprintName = "mpw-cli"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA1C7AA61F1A8F24009A3551"
BuildableName = "mpw-cli"
BlueprintName = "mpw-cli"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DA1C7AA61F1A8F24009A3551"
BuildableName = "mpw-cli"
BlueprintName = "mpw-cli"
ReferencedContainer = "container:MasterPassword-macOS.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -11,6 +11,7 @@
"2FE140B36B7D26140DC8D5E5C639DC5900EFCF35" : 9223372036854775807, "2FE140B36B7D26140DC8D5E5C639DC5900EFCF35" : 9223372036854775807,
"4DDCFFD91B41F00326AD14553BD66CFD366ABD91" : 9223372036854775807, "4DDCFFD91B41F00326AD14553BD66CFD366ABD91" : 9223372036854775807,
"3ED8592497DB6A564366943C9AAD5A46341B5076" : 9223372036854775807, "3ED8592497DB6A564366943C9AAD5A46341B5076" : 9223372036854775807,
"B38C14663FCFBB7024902D2DB1D013964189DC3B" : 9223372036854775807,
"81A28796384A028E6C2D47C039DB8B3E5DD6D0FC" : 9223372036854775807, "81A28796384A028E6C2D47C039DB8B3E5DD6D0FC" : 9223372036854775807,
"F788B28042EDBEF29EFE34687DA79A778C2CC260" : 0 "F788B28042EDBEF29EFE34687DA79A778C2CC260" : 0
}, },
@@ -23,6 +24,7 @@
"2FE140B36B7D26140DC8D5E5C639DC5900EFCF35" : "MasterPassword\/platform-darwin\/External\/uicolor-utilities\/", "2FE140B36B7D26140DC8D5E5C639DC5900EFCF35" : "MasterPassword\/platform-darwin\/External\/uicolor-utilities\/",
"4DDCFFD91B41F00326AD14553BD66CFD366ABD91" : "MasterPassword\/platform-darwin\/External\/Pearl\/", "4DDCFFD91B41F00326AD14553BD66CFD366ABD91" : "MasterPassword\/platform-darwin\/External\/Pearl\/",
"3ED8592497DB6A564366943C9AAD5A46341B5076" : "MasterPassword\/platform-darwin\/External\/AttributedMarkdown\/", "3ED8592497DB6A564366943C9AAD5A46341B5076" : "MasterPassword\/platform-darwin\/External\/AttributedMarkdown\/",
"B38C14663FCFBB7024902D2DB1D013964189DC3B" : "MasterPassword\/platform-darwin\/External\/libjson-c\/",
"81A28796384A028E6C2D47C039DB8B3E5DD6D0FC" : "MasterPassword\/platform-darwin\/External\/libsodium\/", "81A28796384A028E6C2D47C039DB8B3E5DD6D0FC" : "MasterPassword\/platform-darwin\/External\/libsodium\/",
"F788B28042EDBEF29EFE34687DA79A778C2CC260" : "MasterPassword\/" "F788B28042EDBEF29EFE34687DA79A778C2CC260" : "MasterPassword\/"
}, },
@@ -70,6 +72,11 @@
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8A15A8EA0B3D0B497C4883425BC74DF995224BB3" "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8A15A8EA0B3D0B497C4883425BC74DF995224BB3"
}, },
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/json-c\/json-c.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "B38C14663FCFBB7024902D2DB1D013964189DC3B"
},
{ {
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:Lyndir\/MasterPassword.git", "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:Lyndir\/MasterPassword.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",

View File

@@ -1,90 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MPGeneratedSiteEntity</key>
<dict>
<key>Login Name</key>
<array>
<string>cvccvcvcv</string>
</array>
<key>Phrase</key>
<array>
<string>cvcc cvc cvccvcv cvc</string>
<string>cvc cvccvcvcv cvcv</string>
<string>cv cvccv cvc cvcvccv</string>
</array>
<key>Maximum Security Password</key>
<array>
<string>anoxxxxxxxxxxxxxxxxx</string>
<string>axxxxxxxxxxxxxxxxxno</string>
</array>
<key>Long Password</key>
<array>
<string>CvcvnoCvcvCvcv</string>
<string>CvcvCvcvnoCvcv</string>
<string>CvcvCvcvCvcvno</string>
<string>CvccnoCvcvCvcv</string>
<string>CvccCvcvnoCvcv</string>
<string>CvccCvcvCvcvno</string>
<string>CvcvnoCvccCvcv</string>
<string>CvcvCvccnoCvcv</string>
<string>CvcvCvccCvcvno</string>
<string>CvcvnoCvcvCvcc</string>
<string>CvcvCvcvnoCvcc</string>
<string>CvcvCvcvCvccno</string>
<string>CvccnoCvccCvcv</string>
<string>CvccCvccnoCvcv</string>
<string>CvccCvccCvcvno</string>
<string>CvcvnoCvccCvcc</string>
<string>CvcvCvccnoCvcc</string>
<string>CvcvCvccCvccno</string>
<string>CvccnoCvcvCvcc</string>
<string>CvccCvcvnoCvcc</string>
<string>CvccCvcvCvccno</string>
</array>
<key>Medium Password</key>
<array>
<string>CvcnoCvc</string>
<string>CvcCvcno</string>
</array>
<key>Basic Password</key>
<array>
<string>aaanaaan</string>
<string>aannaaan</string>
<string>aaannaaa</string>
</array>
<key>Short Password</key>
<array>
<string>Cvcn</string>
</array>
<key>PIN</key>
<array>
<string>nnnn</string>
</array>
</dict>
<key>MPCharacterClasses</key>
<dict>
<key>V</key>
<string>AEIOU</string>
<key>C</key>
<string>BCDFGHJKLMNPQRSTVWXYZ</string>
<key>v</key>
<string>aeiou</string>
<key>c</key>
<string>bcdfghjklmnpqrstvwxyz</string>
<key>A</key>
<string>AEIOUBCDFGHJKLMNPQRSTVWXYZ</string>
<key>a</key>
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz</string>
<key>n</key>
<string>0123456789</string>
<key>o</key>
<string>@&amp;%?,=[]_:-+*$#!'^~;()/.</string>
<key>x</key>
<string>AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&amp;*()</string>
<key> </key>
<string> </string>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,86 @@
#!/usr/bin/env bash
set -e
cd "${BASH_SOURCE%/*}/../External/libjson-c"
[[ -e "${prefix=$PWD/libjson-c-ios}/lib/libjson-c.a" ]] && exit
# Prepare
autoreconf -Iautoconf-archive/m4 --verbose --install --symlink 2>&1 | sed 's/^\([^:]*\):[0-9]\{1,\}: /\1: /'
rm -rf "${prefix=$PWD/libjson-c-ios}"
mkdir -p "$prefix/lib" \
"${prefix_i386=$prefix/tmp/i386}" \
"${prefix_x86_64=$prefix/tmp/x86_64}" \
"${prefix_armv7=$prefix/tmp/armv7}" \
"${prefix_armv7s=$prefix/tmp/armv7s}" \
"${prefix_arm64=$prefix/tmp/arm64}"
# Targets
(
## ARCH: i386
export SDKROOT="$(xcrun --show-sdk-path --sdk iphonesimulator)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphonesimulator)/usr/bin:$PATH"
export CFLAGS="-arch i386 -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -O2 -g $CFLAGS"
export LDFLAGS="-arch i386 -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $LDFLAGS"
export CPPFLAGS="$CFLAGS $CPPFLAGS"
[[ -e Makefile ]] && make -s clean
./configure --host=i686-apple --disable-shared --prefix="$prefix_i386"
make -j3 install
)
(
## ARCH: x86_64
export SDKROOT="$(xcrun --show-sdk-path --sdk iphonesimulator)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphonesimulator)/usr/bin:$PATH"
export CFLAGS="-arch x86_64 -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -O2 -g $CFLAGS"
export LDFLAGS="-arch x86_64 -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $LDFLAGS"
export CPPFLAGS="$CFLAGS $CPPFLAGS"
[[ -e Makefile ]] && make -s clean
./configure --host=x86_64-apple --disable-shared --prefix="$prefix_x86_64"
make -j3 install
)
(
## ARCH: armv7
export SDKROOT="$(xcrun --show-sdk-path --sdk iphoneos)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphoneos)/usr/bin:$PATH"
export CFLAGS="-mthumb -arch armv7 -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -O2 -g $CFLAGS"
export LDFLAGS="-mthumb -arch armv7 -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $LDFLAGS"
export CPPFLAGS="$CFLAGS $CPPFLAGS"
[[ -e Makefile ]] && make -s clean
./configure --host=x86_64-apple --target=arm-apple --disable-shared --prefix="$prefix_armv7"
make -j3 install
)
(
## ARCH: armv7s
export SDKROOT="$(xcrun --show-sdk-path --sdk iphoneos)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphoneos)/usr/bin:$PATH"
export CFLAGS="-mthumb -arch armv7s -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -O2 -g $CFLAGS"
export LDFLAGS="-mthumb -arch armv7s -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $LDFLAGS"
export CPPFLAGS="$CFLAGS $CPPFLAGS"
[[ -e Makefile ]] && make -s clean
./configure --host=x86_64-apple --target=arm-apple --disable-shared --prefix="$prefix_armv7s"
make -j3 install
)
(
## ARCH: arm64
export SDKROOT="$(xcrun --show-sdk-path --sdk iphoneos)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphoneos)/usr/bin:$PATH"
export CFLAGS="-mthumb -arch arm64 -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -O2 -g $CFLAGS"
export LDFLAGS="-mthumb -arch arm64 -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} $LDFLAGS"
export CPPFLAGS="$CFLAGS $CPPFLAGS"
[[ -e Makefile ]] && make -s clean
./configure --host=x86_64-apple --target=arm-apple --disable-shared --prefix="$prefix_arm64"
make -j3 install
)
# Merge Binaries
mv -f -- "$prefix_arm64/include" "$prefix/"
lipo -create \
"$prefix_i386/lib/libjson-c.a" \
"$prefix_x86_64/lib/libjson-c.a" \
"$prefix_armv7/lib/libjson-c.a" \
"$prefix_armv7s/lib/libjson-c.a" \
"$prefix_arm64/lib/libjson-c.a" \
-output "$prefix/lib/libjson-c.a"
# Cleanup
rm -rf -- "$prefix/tmp"
make -s really-clean

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -e
cd "${BASH_SOURCE%/*}/../External/libjson-c"
[[ -e "${prefix=$PWD/libjson-c-osx}/lib/libjson-c.a" ]] && exit
# Prepare
autoreconf -Iautoconf-archive/m4 --verbose --install --symlink 2>&1 | sed 's/^\([^:]*\):[0-9]\{1,\}: /\1: /'
rm -rf "${prefix=$PWD/libjson-c-osx}"
mkdir -p "$prefix"
# Targets
(
## ARCH: x86_64
export SDKROOT="$(xcrun --show-sdk-path --sdk macosx)"
export PATH="$(xcrun --show-sdk-platform-path --sdk macosx)/usr/bin:$PATH"
export CFLAGS="-arch x86_64 -isysroot $SDKROOT -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET:-10.8} -O2 -g $CFLAGS" # -flto
export LDFLAGS="-arch x86_64 -isysroot $SDKROOT -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET:-10.8} $LDFLAGS" # -flto
export CPPFLAGS="$CFLAGS $CPPFLAGS"
[[ -e Makefile ]] && make -s clean
./configure --disable-shared --prefix="$prefix"
make -j3 check
make -j3 install
)
# Cleanup
make -s really-clean

View File

@@ -0,0 +1,86 @@
#!/usr/bin/env bash
set -e
cd "${BASH_SOURCE%/*}/../External/libsodium"
[[ -e "${prefix=$PWD/libsodium-ios}/lib/libsodium.a" ]] && exit
# Prepare
autoreconf --verbose --install --symlink 2>&1 | sed 's/^\([^:]*\):[0-9]\{1,\}: /\1: /'
rm -rf "${prefix=$PWD/libsodium-ios}"
mkdir -p "$prefix/lib" \
"${prefix_i386=$prefix/tmp/i386}" \
"${prefix_x86_64=$prefix/tmp/x86_64}" \
"${prefix_armv7=$prefix/tmp/armv7}" \
"${prefix_armv7s=$prefix/tmp/armv7s}" \
"${prefix_arm64=$prefix/tmp/arm64}"
# Targets
(
## ARCH: i386
export SDKROOT="$(xcrun --show-sdk-path --sdk iphonesimulator)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphonesimulator)/usr/bin:$PATH"
export CFLAGS="-arch i386 -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -O2 -g -flto $CFLAGS"
export LDFLAGS="-arch i386 -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -flto $LDFLAGS"
export CPPFLAGS="$CFLAGS $CPPFLAGS"
[[ -e Makefile ]] && make -s distclean
./configure --host=i686-apple --disable-shared --enable-minimal --prefix="$prefix_i386"
make -j3 install
)
(
## ARCH: x86_64
export SDKROOT="$(xcrun --show-sdk-path --sdk iphonesimulator)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphonesimulator)/usr/bin:$PATH"
export CFLAGS="-arch x86_64 -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -O2 -g -flto $CFLAGS"
export LDFLAGS="-arch x86_64 -isysroot $SDKROOT -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -flto $LDFLAGS"
export CPPFLAGS="$CFLAGS $CPPFLAGS"
[[ -e Makefile ]] && make -s distclean
./configure --host=x86_64-apple --disable-shared --enable-minimal --prefix="$prefix_x86_64"
make -j3 install
)
(
## ARCH: armv7
export SDKROOT="$(xcrun --show-sdk-path --sdk iphoneos)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphoneos)/usr/bin:$PATH"
export CFLAGS="-mthumb -arch armv7 -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -O2 -g -flto $CFLAGS"
export LDFLAGS="-mthumb -arch armv7 -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -flto $LDFLAGS"
export CPPFLAGS="$CFLAGS $CPPFLAGS"
[[ -e Makefile ]] && make -s distclean
./configure --host=x86_64-apple --target=arm-apple --disable-shared --enable-minimal --prefix="$prefix_armv7"
make -j3 install
)
(
## ARCH: armv7s
export SDKROOT="$(xcrun --show-sdk-path --sdk iphoneos)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphoneos)/usr/bin:$PATH"
export CFLAGS="-mthumb -arch armv7s -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -O2 -g -flto $CFLAGS"
export LDFLAGS="-mthumb -arch armv7s -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -flto $LDFLAGS"
export CPPFLAGS="$CFLAGS $CPPFLAGS"
[[ -e Makefile ]] && make -s distclean
./configure --host=x86_64-apple --target=arm-apple --disable-shared --enable-minimal --prefix="$prefix_armv7s"
make -j3 install
)
(
## ARCH: arm64
export SDKROOT="$(xcrun --show-sdk-path --sdk iphoneos)"
export PATH="$(xcrun --show-sdk-platform-path --sdk iphoneos)/usr/bin:$PATH"
export CFLAGS="-mthumb -arch arm64 -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -O2 -g -flto $CFLAGS"
export LDFLAGS="-mthumb -arch arm64 -isysroot $SDKROOT -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET:-8.0} -flto $LDFLAGS"
export CPPFLAGS="$CFLAGS $CPPFLAGS"
[[ -e Makefile ]] && make -s distclean
./configure --host=x86_64-apple --target=arm-apple --disable-shared --enable-minimal --prefix="$prefix_arm64"
make -j3 install
)
# Merge Binaries
mv -f -- "$prefix_arm64/include" "$prefix/"
lipo -create \
"$prefix_i386/lib/libsodium.a" \
"$prefix_x86_64/lib/libsodium.a" \
"$prefix_armv7/lib/libsodium.a" \
"$prefix_armv7s/lib/libsodium.a" \
"$prefix_arm64/lib/libsodium.a" \
-output "$prefix/lib/libsodium.a"
# Cleanup
rm -rf -- "$prefix/tmp"
make -s distclean

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -e
cd "${BASH_SOURCE%/*}/../External/libsodium"
[[ -e "${prefix=$PWD/libsodium-osx}/lib/libsodium.a" ]] && exit
# Inspired by libsodium/dist-build/osx.sh
# Prepare
autoreconf --verbose --install --symlink 2>&1 | sed 's/^\([^:]*\):[0-9]\{1,\}: /\1: /'
rm -rf "${prefix=$PWD/libsodium-osx}"
mkdir -p "$prefix"
# Targets
(
## ARCH: x86_64
export SDKROOT="$(xcrun --show-sdk-path --sdk macosx)"
export PATH="$(xcrun --show-sdk-platform-path --sdk macosx)/usr/bin:$PATH"
export CFLAGS="-arch x86_64 -isysroot $SDKROOT -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET:-10.8} -O2 -g -flto $CFLAGS"
export LDFLAGS="-arch x86_64 -isysroot $SDKROOT -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET:-10.8} -flto $LDFLAGS"
export CPPFLAGS="$CFLAGS $CPPFLAGS"
[[ -e Makefile ]] && make -s distclean
./configure --disable-shared --enable-minimal --prefix="$prefix"
make -j3 check
make -j3 install
)
# Cleanup
make -s distclean

View File

@@ -52,49 +52,44 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
- (NSData *)keyIDForKey:(MPMasterKey)masterKey; - (NSData *)keyIDForKey:(MPMasterKey)masterKey;
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword; - (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
- (NSString *)nameOfType:(MPSiteType)type; - (NSString *)nameOfType:(MPResultType)type;
- (NSString *)shortNameOfType:(MPSiteType)type; - (NSString *)shortNameOfType:(MPResultType)type;
- (NSString *)classNameOfType:(MPSiteType)type; - (NSString *)classNameOfType:(MPResultType)type;
- (Class)classOfType:(MPSiteType)type; - (Class)classOfType:(MPResultType)type;
- (NSArray *)allTypes; - (NSArray *)allTypes;
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType; - (NSArray *)allTypesStartingWith:(MPResultType)startingType;
- (MPSiteType)defaultType; - (MPResultType)defaultType;
- (MPSiteType)nextType:(MPSiteType)type; - (MPResultType)nextType:(MPResultType)type;
- (MPSiteType)previousType:(MPSiteType)type; - (MPResultType)previousType:(MPResultType)type;
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key; - (NSString *)mpwLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key;
- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter - (NSString *)mpwTemplateForSiteNamed:(NSString *)name ofType:(MPResultType)type
usingKey:(MPKey *)key; withCounter:(MPCounterValue)counter usingKey:(MPKey *)key;
- (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key; - (NSString *)mpwAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key;
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter - (NSString *)mpwResultForSiteNamed:(NSString *)name ofType:(MPResultType)type parameter:(NSString *)parameter
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key; withCounter:(MPCounterValue)counter variant:(MPKeyPurpose)purpose context:(NSString *)context usingKey:(MPKey *)key;
- (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key; - (BOOL)savePassword:(NSString *)clearPassword toSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
- (BOOL)savePassword:(NSString *)clearPassword toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey; - (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)key;
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey; - (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey;
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
result:(void ( ^ )(NSString *result))resultBlock; result:(void ( ^ )(NSString *result))resultBlock;
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey - (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key
result:(void ( ^ )(NSString *result))resultBlock; result:(void ( ^ )(NSString *result))resultBlock;
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey - (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)key
result:(void ( ^ )(NSString *result))resultBlock; result:(void ( ^ )(NSString *result))resultBlock;
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey - (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)key
result:(void ( ^ )(NSString *result))resultBlock; result:(void ( ^ )(NSString *result))resultBlock;
- (void)importProtectedPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey - (void)importPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey; intoSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
- (void)importClearTextPassword:(NSString *)clearPassword intoSite:(MPSiteEntity *)site - (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
usingKey:(MPKey *)siteKey;
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker; - (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPResultType)type byAttacker:(MPAttacker)attacker;
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker; - (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordString:(NSString *)password byAttacker:(MPAttacker)attacker;
@end @end

View File

@@ -133,7 +133,7 @@ static NSOperationQueue *_mpwQueue = nil;
keyData = [NSData dataWithBytes:masterKey length:MPMasterKeySize]; keyData = [NSData dataWithBytes:masterKey length:MPMasterKeySize];
trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", // trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", //
fullName, masterPassword, [self keyIDForKey:masterKey], -[start timeIntervalSinceNow] ); fullName, masterPassword, [self keyIDForKey:masterKey], -[start timeIntervalSinceNow] );
mpw_free( masterKey, MPMasterKeySize ); mpw_free( &masterKey, MPMasterKeySize );
} }
}]; }];
@@ -145,126 +145,135 @@ static NSOperationQueue *_mpwQueue = nil;
return [[NSData dataWithBytesNoCopy:(void *)masterKey length:MPMasterKeySize] hashWith:PearlHashSHA256]; return [[NSData dataWithBytesNoCopy:(void *)masterKey length:MPMasterKeySize] hashWith:PearlHashSHA256];
} }
- (NSString *)nameOfType:(MPSiteType)type { - (NSString *)nameOfType:(MPResultType)type {
if (!type) if (!type)
return nil; return nil;
switch (type) { switch (type) {
case MPSiteTypeGeneratedMaximum: case MPResultTypeTemplateMaximum:
return @"Maximum Security Password"; return @"Maximum Security Password";
case MPSiteTypeGeneratedLong: case MPResultTypeTemplateLong:
return @"Long Password"; return @"Long Password";
case MPSiteTypeGeneratedMedium: case MPResultTypeTemplateMedium:
return @"Medium Password"; return @"Medium Password";
case MPSiteTypeGeneratedBasic: case MPResultTypeTemplateBasic:
return @"Basic Password"; return @"Basic Password";
case MPSiteTypeGeneratedShort: case MPResultTypeTemplateShort:
return @"Short Password"; return @"Short Password";
case MPSiteTypeGeneratedPIN: case MPResultTypeTemplatePIN:
return @"PIN"; return @"PIN";
case MPSiteTypeGeneratedName: case MPResultTypeTemplateName:
return @"Name"; return @"Name";
case MPSiteTypeGeneratedPhrase: case MPResultTypeTemplatePhrase:
return @"Phrase"; return @"Phrase";
case MPSiteTypeStoredPersonal: case MPResultTypeStatefulPersonal:
return @"Personal Password"; return @"Personal Password";
case MPSiteTypeStoredDevicePrivate: case MPResultTypeStatefulDevice:
return @"Device Private Password"; return @"Device Private Password";
case MPResultTypeDeriveKey:
return @"Crypto Key";
} }
Throw( @"Type not supported: %lu", (long)type ); Throw( @"Type not supported: %lu", (long)type );
} }
- (NSString *)shortNameOfType:(MPSiteType)type { - (NSString *)shortNameOfType:(MPResultType)type {
if (!type) if (!type)
return nil; return nil;
switch (type) { switch (type) {
case MPSiteTypeGeneratedMaximum: case MPResultTypeTemplateMaximum:
return @"Maximum"; return @"Maximum";
case MPSiteTypeGeneratedLong: case MPResultTypeTemplateLong:
return @"Long"; return @"Long";
case MPSiteTypeGeneratedMedium: case MPResultTypeTemplateMedium:
return @"Medium"; return @"Medium";
case MPSiteTypeGeneratedBasic: case MPResultTypeTemplateBasic:
return @"Basic"; return @"Basic";
case MPSiteTypeGeneratedShort: case MPResultTypeTemplateShort:
return @"Short"; return @"Short";
case MPSiteTypeGeneratedPIN: case MPResultTypeTemplatePIN:
return @"PIN"; return @"PIN";
case MPSiteTypeGeneratedName: case MPResultTypeTemplateName:
return @"Name"; return @"Name";
case MPSiteTypeGeneratedPhrase: case MPResultTypeTemplatePhrase:
return @"Phrase"; return @"Phrase";
case MPSiteTypeStoredPersonal: case MPResultTypeStatefulPersonal:
return @"Personal"; return @"Personal";
case MPSiteTypeStoredDevicePrivate: case MPResultTypeStatefulDevice:
return @"Device"; return @"Device";
case MPResultTypeDeriveKey:
return @"Key";
} }
Throw( @"Type not supported: %lu", (long)type ); Throw( @"Type not supported: %lu", (long)type );
} }
- (NSString *)classNameOfType:(MPSiteType)type { - (NSString *)classNameOfType:(MPResultType)type {
return NSStringFromClass( [self classOfType:type] ); return NSStringFromClass( [self classOfType:type] );
} }
- (Class)classOfType:(MPSiteType)type { - (Class)classOfType:(MPResultType)type {
if (!type) if (!type)
Throw( @"No type given." ); Throw( @"No type given." );
switch (type) { switch (type) {
case MPSiteTypeGeneratedMaximum: case MPResultTypeTemplateMaximum:
return [MPGeneratedSiteEntity class]; return [MPGeneratedSiteEntity class];
case MPSiteTypeGeneratedLong: case MPResultTypeTemplateLong:
return [MPGeneratedSiteEntity class]; return [MPGeneratedSiteEntity class];
case MPSiteTypeGeneratedMedium: case MPResultTypeTemplateMedium:
return [MPGeneratedSiteEntity class]; return [MPGeneratedSiteEntity class];
case MPSiteTypeGeneratedBasic: case MPResultTypeTemplateBasic:
return [MPGeneratedSiteEntity class]; return [MPGeneratedSiteEntity class];
case MPSiteTypeGeneratedShort: case MPResultTypeTemplateShort:
return [MPGeneratedSiteEntity class]; return [MPGeneratedSiteEntity class];
case MPSiteTypeGeneratedPIN: case MPResultTypeTemplatePIN:
return [MPGeneratedSiteEntity class]; return [MPGeneratedSiteEntity class];
case MPSiteTypeGeneratedName: case MPResultTypeTemplateName:
return [MPGeneratedSiteEntity class]; return [MPGeneratedSiteEntity class];
case MPSiteTypeGeneratedPhrase: case MPResultTypeTemplatePhrase:
return [MPGeneratedSiteEntity class]; return [MPGeneratedSiteEntity class];
case MPSiteTypeStoredPersonal: case MPResultTypeStatefulPersonal:
return [MPStoredSiteEntity class]; return [MPStoredSiteEntity class];
case MPSiteTypeStoredDevicePrivate: case MPResultTypeStatefulDevice:
return [MPStoredSiteEntity class]; return [MPStoredSiteEntity class];
case MPResultTypeDeriveKey:
break;
} }
Throw( @"Type not supported: %lu", (long)type ); Throw( @"Type not supported: %lu", (long)type );
@@ -272,13 +281,13 @@ static NSOperationQueue *_mpwQueue = nil;
- (NSArray *)allTypes { - (NSArray *)allTypes {
return [self allTypesStartingWith:MPSiteTypeGeneratedPhrase]; return [self allTypesStartingWith:MPResultTypeTemplatePhrase];
} }
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType { - (NSArray *)allTypesStartingWith:(MPResultType)startingType {
NSMutableArray *allTypes = [[NSMutableArray alloc] initWithCapacity:8]; NSMutableArray *allTypes = [[NSMutableArray alloc] initWithCapacity:8];
MPSiteType currentType = startingType; MPResultType currentType = startingType;
do { do {
[allTypes addObject:@(currentType)]; [allTypes addObject:@(currentType)];
} while ((currentType = [self nextType:currentType]) != startingType); } while ((currentType = [self nextType:currentType]) != startingType);
@@ -286,199 +295,173 @@ static NSOperationQueue *_mpwQueue = nil;
return allTypes; return allTypes;
} }
- (MPSiteType)defaultType { - (MPResultType)defaultType {
return MPSiteTypeGeneratedLong; return MPResultTypeTemplateLong;
} }
- (MPSiteType)nextType:(MPSiteType)type { - (MPResultType)nextType:(MPResultType)type {
switch (type) { switch (type) {
case MPSiteTypeGeneratedPhrase: case MPResultTypeTemplatePhrase:
return MPSiteTypeGeneratedName; return MPResultTypeTemplateName;
case MPSiteTypeGeneratedName: case MPResultTypeTemplateName:
return MPSiteTypeGeneratedMaximum; return MPResultTypeTemplateMaximum;
case MPSiteTypeGeneratedMaximum: case MPResultTypeTemplateMaximum:
return MPSiteTypeGeneratedLong; return MPResultTypeTemplateLong;
case MPSiteTypeGeneratedLong: case MPResultTypeTemplateLong:
return MPSiteTypeGeneratedMedium; return MPResultTypeTemplateMedium;
case MPSiteTypeGeneratedMedium: case MPResultTypeTemplateMedium:
return MPSiteTypeGeneratedBasic; return MPResultTypeTemplateBasic;
case MPSiteTypeGeneratedBasic: case MPResultTypeTemplateBasic:
return MPSiteTypeGeneratedShort; return MPResultTypeTemplateShort;
case MPSiteTypeGeneratedShort: case MPResultTypeTemplateShort:
return MPSiteTypeGeneratedPIN; return MPResultTypeTemplatePIN;
case MPSiteTypeGeneratedPIN: case MPResultTypeTemplatePIN:
return MPSiteTypeStoredPersonal; return MPResultTypeStatefulPersonal;
case MPSiteTypeStoredPersonal: case MPResultTypeStatefulPersonal:
return MPSiteTypeStoredDevicePrivate; return MPResultTypeStatefulDevice;
case MPSiteTypeStoredDevicePrivate: case MPResultTypeStatefulDevice:
return MPSiteTypeGeneratedPhrase; return MPResultTypeTemplatePhrase;
case MPResultTypeDeriveKey:
break;
} }
return [self defaultType]; return [self defaultType];
} }
- (MPSiteType)previousType:(MPSiteType)type { - (MPResultType)previousType:(MPResultType)type {
MPSiteType previousType = type, nextType = type; MPResultType previousType = type, nextType = type;
while ((nextType = [self nextType:nextType]) != type) while ((nextType = [self nextType:nextType]) != type)
previousType = nextType; previousType = nextType;
return previousType; return previousType;
} }
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key { - (NSString *)mpwLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key {
return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedName withCounter:1 return [self mpwResultForSiteNamed:name ofType:MPResultTypeTemplateName parameter:nil withCounter:MPCounterValueInitial
variant:MPKeyPurposeIdentification context:nil usingKey:key]; variant:MPKeyPurposeIdentification context:nil usingKey:key];
} }
- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter - (NSString *)mpwTemplateForSiteNamed:(NSString *)name ofType:(MPResultType)type
usingKey:(MPKey *)key { withCounter:(MPCounterValue)counter usingKey:(MPKey *)key {
return [self generateContentForSiteNamed:name ofType:type withCounter:counter return [self mpwResultForSiteNamed:name ofType:type parameter:nil withCounter:counter
variant:MPKeyPurposeAuthentication context:nil usingKey:key]; variant:MPKeyPurposeAuthentication context:nil usingKey:key];
} }
- (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key { - (NSString *)mpwAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key {
return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedPhrase withCounter:1 return [self mpwResultForSiteNamed:name ofType:MPResultTypeTemplatePhrase parameter:nil withCounter:MPCounterValueInitial
variant:MPKeyPurposeRecovery context:question usingKey:key]; variant:MPKeyPurposeRecovery context:question usingKey:key];
} }
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter - (NSString *)mpwResultForSiteNamed:(NSString *)name ofType:(MPResultType)type parameter:(NSString *)parameter
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key { withCounter:(MPCounterValue)counter variant:(MPKeyPurpose)purpose context:(NSString *)context
usingKey:(MPKey *)key {
__block NSString *content = nil; __block NSString *result = nil;
[self mpw_perform:^{ [self mpw_perform:^{
char const *contentBytes = mpw_passwordForSite( [key keyForAlgorithm:self], char const *resultBytes = mpw_siteResult( [key keyForAlgorithm:self],
name.UTF8String, type, (uint32_t)counter, variant, context.UTF8String, [self version] ); name.UTF8String, counter, purpose, context.UTF8String, type, parameter.UTF8String, [self version] );
if (contentBytes) { if (resultBytes) {
content = [NSString stringWithCString:contentBytes encoding:NSUTF8StringEncoding]; result = [NSString stringWithCString:resultBytes encoding:NSUTF8StringEncoding];
mpw_free_string( contentBytes ); mpw_free_string( &resultBytes );
} }
}]; }];
return content; return result;
} }
- (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key { - (BOOL)savePassword:(NSString *)plainText toSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
return nil; if (!(site.type & MPResultTypeClassStateful)) {
} wrn( @"Can only save content to site with a stateful type: %lu.", (long)site.type );
return NO;
- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key {
return [self decryptContent:site.contentObject usingKey:key];
}
- (BOOL)savePassword:(NSString *)clearContent toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
switch (site.type) {
case MPSiteTypeGeneratedMaximum:
case MPSiteTypeGeneratedLong:
case MPSiteTypeGeneratedMedium:
case MPSiteTypeGeneratedBasic:
case MPSiteTypeGeneratedShort:
case MPSiteTypeGeneratedPIN:
case MPSiteTypeGeneratedName:
case MPSiteTypeGeneratedPhrase: {
wrn( @"Cannot save content to site with generated type %lu.", (long)site.type );
return NO;
}
case MPSiteTypeStoredPersonal: {
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
(long)site.type, [site class] );
return NO;
}
NSData *encryptionKey = [siteKey keyForAlgorithm:self trimmedLength:PearlCryptKeySize];
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
encryptWithSymmetricKey:encryptionKey padding:YES];
if ([((MPStoredSiteEntity *)site).contentObject isEqualToData:encryptedContent])
return NO;
((MPStoredSiteEntity *)site).contentObject = encryptedContent;
return YES;
}
case MPSiteTypeStoredDevicePrivate: {
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
(long)site.type, [site class] );
return NO;
}
NSData *encryptionKey = [siteKey keyForAlgorithm:self trimmedLength:PearlCryptKeySize];
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
encryptWithSymmetricKey:encryptionKey padding:YES];
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
if (!encryptedContent)
[PearlKeyChain deleteItemForQuery:siteQuery];
else
[PearlKeyChain addOrUpdateItemForQuery:siteQuery withAttributes:@{
(__bridge id)kSecValueData : encryptedContent,
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
#endif
}];
((MPStoredSiteEntity *)site).contentObject = nil;
return YES;
}
} }
Throw( @"Unsupported type: %ld", (long)site.type ); NSAssert( [[key keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
(long)site.type, [site class] );
return NO;
}
__block NSData *state = nil;
if (plainText)
[self mpw_perform:^{
char const *stateBytes = mpw_siteState( [key keyForAlgorithm:self], site.name.UTF8String,
MPCounterValueInitial, MPKeyPurposeAuthentication, NULL, site.type, plainText.UTF8String, [self version] );
if (stateBytes) {
state = [[NSString stringWithCString:stateBytes encoding:NSUTF8StringEncoding] decodeBase64];
mpw_free_string( &stateBytes );
}
}];
NSDictionary *siteQuery = [self queryForSite:site];
if (!state)
[PearlKeyChain deleteItemForQuery:siteQuery];
else
[PearlKeyChain addOrUpdateItemForQuery:siteQuery withAttributes:@{
(__bridge id)kSecValueData: state,
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible:
site.type & MPSiteFeatureDevicePrivate? (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly
: (__bridge id)kSecAttrAccessibleWhenUnlocked,
#endif
}];
((MPStoredSiteEntity *)site).contentObject = nil;
return YES;
} }
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey { - (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
return PearlAwait( ^(void (^setResult)(id)) { return PearlAwait( ^(void (^setResult)(id)) {
[self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) { [self resolveLoginForSite:site usingKey:key result:^(NSString *result_) {
setResult( result_ ); setResult( result_ );
}]; }];
} ); } );
} }
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey { - (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
return PearlAwait( ^(void (^setResult)(id)) { return PearlAwait( ^(void (^setResult)(id)) {
[self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) { [self resolvePasswordForSite:site usingKey:key result:^(NSString *result_) {
setResult( result_ ); setResult( result_ );
}]; }];
} ); } );
} }
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey { - (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
return PearlAwait( ^(void (^setResult)(id)) { return PearlAwait( ^(void (^setResult)(id)) {
[self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) { [self resolveAnswerForSite:site usingKey:key result:^(NSString *result_) {
setResult( result_ ); setResult( result_ );
}]; }];
} ); } );
} }
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey { - (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)key {
return PearlAwait( ^(void (^setResult)(id)) { return PearlAwait( ^(void (^setResult)(id)) {
[self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) { [self resolveAnswerForQuestion:question usingKey:key result:^(NSString *result_) {
setResult( result_ ); setResult( result_ );
}]; }];
} ); } );
} }
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock { - (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key result:(void ( ^ )(NSString *result))resultBlock {
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." ); NSAssert( [[key keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
NSString *name = site.name; NSString *name = site.name;
BOOL loginGenerated = site.loginGenerated && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateLogins]; BOOL loginGenerated = site.loginGenerated && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateLogins];
NSString *loginName = site.loginName; NSString *loginName = site.loginName;
id<MPAlgorithm> algorithm = nil; id<MPAlgorithm> algorithm = nil;
if (!name.length) if (!name.length)
err( @"Missing name." ); err( @"Missing name." );
else if (!siteKey) else if (!key)
err( @"Missing key." ); err( @"Missing key." );
else else
algorithm = site.algorithm; algorithm = site.algorithm;
@@ -487,244 +470,144 @@ static NSOperationQueue *_mpwQueue = nil;
resultBlock( loginName ); resultBlock( loginName );
else else
PearlNotMainQueue( ^{ PearlNotMainQueue( ^{
resultBlock( [algorithm generateLoginForSiteNamed:name usingKey:siteKey] ); resultBlock( [algorithm mpwLoginForSiteNamed:name usingKey:key] );
} ); } );
} }
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock { - (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key result:(void ( ^ )(NSString *result))resultBlock {
NSAssert( [[key keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
NSString *name = site.name;
MPResultType type = site.type;
id<MPAlgorithm> algorithm = nil;
if (!site.name.length)
err( @"Missing name." );
else if (!key)
err( @"Missing key." );
else
algorithm = site.algorithm;
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
switch (site.type) { switch (site.type) {
case MPSiteTypeGeneratedMaximum: case MPResultTypeTemplateMaximum:
case MPSiteTypeGeneratedLong: case MPResultTypeTemplateLong:
case MPSiteTypeGeneratedMedium: case MPResultTypeTemplateMedium:
case MPSiteTypeGeneratedBasic: case MPResultTypeTemplateBasic:
case MPSiteTypeGeneratedShort: case MPResultTypeTemplateShort:
case MPSiteTypeGeneratedPIN: case MPResultTypeTemplatePIN:
case MPSiteTypeGeneratedName: case MPResultTypeTemplateName:
case MPSiteTypeGeneratedPhrase: { case MPResultTypeTemplatePhrase: {
if (![site isKindOfClass:[MPGeneratedSiteEntity class]]) { if (![site isKindOfClass:[MPGeneratedSiteEntity class]]) {
wrn( @"Site with generated type %lu is not an MPGeneratedSiteEntity, but a %@.", wrn( @"Site with generated type %lu is not an MPGeneratedSiteEntity, but a %@.",
(long)site.type, [site class] ); (long)site.type, [site class] );
break; break;
} }
NSString *name = site.name; MPCounterValue counter = ((MPGeneratedSiteEntity *)site).counter;
MPSiteType type = site.type;
NSUInteger counter = ((MPGeneratedSiteEntity *)site).counter;
id<MPAlgorithm> algorithm = nil;
if (!site.name.length)
err( @"Missing name." );
else if (!siteKey)
err( @"Missing key." );
else
algorithm = site.algorithm;
PearlNotMainQueue( ^{ PearlNotMainQueue( ^{
resultBlock( [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey] ); resultBlock( [algorithm mpwTemplateForSiteNamed:name ofType:type withCounter:counter usingKey:key] );
} ); } );
break; break;
} }
case MPSiteTypeStoredPersonal: { case MPResultTypeStatefulPersonal:
case MPResultTypeStatefulDevice: {
if (![site isKindOfClass:[MPStoredSiteEntity class]]) { if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.", wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
(long)site.type, [site class] ); (long)site.type, [site class] );
break; break;
} }
NSData *encryptedContent = ((MPStoredSiteEntity *)site).contentObject; NSDictionary *siteQuery = [self queryForSite:site];
NSData *state = [PearlKeyChain dataOfItemForQuery:siteQuery];
state = state?: ((MPStoredSiteEntity *)site).contentObject;
PearlNotMainQueue( ^{ PearlNotMainQueue( ^{
resultBlock( [self decryptContent:encryptedContent usingKey:siteKey] ); resultBlock( [algorithm mpwResultForSiteNamed:name ofType:type parameter:[state encodeBase64]
withCounter:MPCounterValueInitial variant:MPKeyPurposeAuthentication context:nil
usingKey:key] );
} ); } );
break; break;
} }
case MPSiteTypeStoredDevicePrivate: {
NSAssert( [site isKindOfClass:[MPStoredSiteEntity class]],
@"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.", (long)site.type,
[site class] );
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name]; case MPResultTypeDeriveKey:
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:siteQuery];
PearlNotMainQueue( ^{
resultBlock( [self decryptContent:encryptedContent usingKey:siteKey] );
} );
break; break;
}
} }
Throw( @"Type not supported: %lu", (long)type );
} }
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock { - (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)key result:(void ( ^ )(NSString *result))resultBlock {
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." ); NSAssert( [[key keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
NSString *name = site.name; NSString *name = site.name;
id<MPAlgorithm> algorithm = nil; id<MPAlgorithm> algorithm = nil;
if (!site.name.length) if (!site.name.length)
err( @"Missing name." ); err( @"Missing name." );
else if (!siteKey) else if (!key)
err( @"Missing key." ); err( @"Missing key." );
else else
algorithm = site.algorithm; algorithm = site.algorithm;
PearlNotMainQueue( ^{ PearlNotMainQueue( ^{
resultBlock( [algorithm generateAnswerForSiteNamed:name onQuestion:nil usingKey:siteKey] ); resultBlock( [algorithm mpwAnswerForSiteNamed:name onQuestion:nil usingKey:key] );
} ); } );
} }
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey - (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)key
result:(void ( ^ )(NSString *result))resultBlock { result:(void ( ^ )(NSString *result))resultBlock {
NSAssert( [[siteKey keyIDForAlgorithm:question.site.user.algorithm] isEqualToData:question.site.user.keyID], NSAssert( [[key keyIDForAlgorithm:question.site.user.algorithm] isEqualToData:question.site.user.keyID],
@"Site does not belong to current user." ); @"Site does not belong to current user." );
NSString *name = question.site.name; NSString *name = question.site.name;
NSString *keyword = question.keyword; NSString *keyword = question.keyword;
id<MPAlgorithm> algorithm = nil; id<MPAlgorithm> algorithm = nil;
if (!name.length) if (!name.length)
err( @"Missing name." ); err( @"Missing name." );
else if (!siteKey) else if (!key)
err( @"Missing key." ); err( @"Missing key." );
else if ([[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateAnswers]) else if ([[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateAnswers])
algorithm = question.site.algorithm; algorithm = question.site.algorithm;
PearlNotMainQueue( ^{ PearlNotMainQueue( ^{
resultBlock( [algorithm generateAnswerForSiteNamed:name onQuestion:keyword usingKey:siteKey] ); resultBlock( [algorithm mpwAnswerForSiteNamed:name onQuestion:keyword usingKey:key] );
} ); } );
} }
- (void)importProtectedPassword:(NSString *)protectedContent protectedByKey:(MPKey *)importKey - (void)importPassword:(NSString *)cipherText protectedByKey:(MPKey *)importKey
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey { intoSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." ); NSAssert( [[key keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
switch (site.type) { if (cipherText && cipherText.length && site.type & MPResultTypeClassStateful) {
case MPSiteTypeGeneratedMaximum: NSString *plainText = [self mpwResultForSiteNamed:site.name ofType:site.type parameter:cipherText
case MPSiteTypeGeneratedLong: withCounter:MPCounterValueInitial variant:MPKeyPurposeAuthentication context:nil
case MPSiteTypeGeneratedMedium: usingKey:importKey];
case MPSiteTypeGeneratedBasic: if (plainText)
case MPSiteTypeGeneratedShort: [self savePassword:plainText toSite:site usingKey:key];
case MPSiteTypeGeneratedPIN:
case MPSiteTypeGeneratedName:
case MPSiteTypeGeneratedPhrase:
break;
case MPSiteTypeStoredPersonal: {
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
(long)site.type, [site class] );
break;
}
if ([[importKey keyIDForAlgorithm:self] isEqualToData:[siteKey keyIDForAlgorithm:self]])
((MPStoredSiteEntity *)site).contentObject = [protectedContent decodeBase64];
else {
NSString *clearContent = [self decryptContent:[protectedContent decodeBase64] usingKey:importKey];
[self importClearTextPassword:clearContent intoSite:site usingKey:siteKey];
}
break;
}
case MPSiteTypeStoredDevicePrivate:
break;
} }
} }
- (void)importClearTextPassword:(NSString *)clearContent intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey { - (NSDictionary *)queryForSite:(MPSiteEntity *)site {
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." ); return [PearlKeyChain createQueryForClass:kSecClassGenericPassword attributes:@{
switch (site.type) { (__bridge id)kSecAttrService: site.type & MPSiteFeatureDevicePrivate? @"DevicePrivate": @"Private",
case MPSiteTypeGeneratedMaximum: (__bridge id)kSecAttrAccount: site.name
case MPSiteTypeGeneratedLong: } matches:nil];
case MPSiteTypeGeneratedMedium:
case MPSiteTypeGeneratedBasic:
case MPSiteTypeGeneratedShort:
case MPSiteTypeGeneratedPIN:
case MPSiteTypeGeneratedName:
case MPSiteTypeGeneratedPhrase:
break;
case MPSiteTypeStoredPersonal: {
[self savePassword:clearContent toSite:site usingKey:siteKey];
break;
}
case MPSiteTypeStoredDevicePrivate:
break;
}
} }
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey { - (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
if (!(site.type & MPSiteFeatureExportContent)) if (!(site.type & MPSiteFeatureExportContent))
return nil; return nil;
NSString *result = nil; NSDictionary *siteQuery = [self queryForSite:site];
switch (site.type) { NSData *state = [PearlKeyChain dataOfItemForQuery:siteQuery];
case MPSiteTypeGeneratedMaximum: return [state?: ((MPStoredSiteEntity *)site).contentObject encodeBase64];
case MPSiteTypeGeneratedLong:
case MPSiteTypeGeneratedMedium:
case MPSiteTypeGeneratedBasic:
case MPSiteTypeGeneratedShort:
case MPSiteTypeGeneratedPIN:
case MPSiteTypeGeneratedName:
case MPSiteTypeGeneratedPhrase: {
result = nil;
break;
}
case MPSiteTypeStoredPersonal: {
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
(long)site.type, [site class] );
break;
}
result = [((MPStoredSiteEntity *)site).contentObject encodeBase64];
break;
}
case MPSiteTypeStoredDevicePrivate: {
result = nil;
break;
}
}
return result;
} }
- (BOOL)migrateExplicitly:(BOOL)explicit { - (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPResultType)type byAttacker:(MPAttacker)attacker {
return NO; if (!(type & MPResultTypeClassTemplate))
}
- (NSDictionary *)queryForDevicePrivateSiteNamed:(NSString *)name {
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
attributes:@{
(__bridge id)kSecAttrService: @"DevicePrivate",
(__bridge id)kSecAttrAccount: name
}
matches:nil];
}
- (NSString *)decryptContent:(NSData *)encryptedContent usingKey:(MPKey *)key {
if (!key)
return nil;
NSData *decryptedContent = nil;
if ([encryptedContent length]) {
NSData *encryptionKey = [key keyForAlgorithm:self trimmedLength:PearlCryptKeySize];
decryptedContent = [encryptedContent decryptWithSymmetricKey:encryptionKey padding:YES];
}
if (!decryptedContent)
return nil;
return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding];
}
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker {
if (!(type & MPSiteTypeClassGenerated))
return NO; return NO;
size_t count = 0; size_t count = 0;
const char **templates = mpw_templatesForType( type, &count ); const char **templates = mpw_templatesForType( type, &count );

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