Compare commits
114 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30fdb54e94 | ||
|
|
4f552be5a9 | ||
|
|
1439df9f9a | ||
|
|
e676a0e258 | ||
|
|
895df6377d | ||
|
|
3d46f60ff4 | ||
|
|
44d8ab6e53 | ||
|
|
cd70009c2c | ||
|
|
4261160902 | ||
|
|
ced7aef5d7 | ||
|
|
63100913c5 | ||
|
|
6904d4c427 | ||
|
|
4271d77225 | ||
|
|
6811773e54 | ||
|
|
060ce61030 | ||
|
|
9a5e9ced31 | ||
|
|
568401a612 | ||
|
|
92a3a0ccbd | ||
|
|
ba24c2be34 | ||
|
|
019cefd3fb | ||
|
|
eef82f7ed4 | ||
|
|
2dfe0f78b0 | ||
|
|
627144b583 | ||
|
|
fad0f5e5dd | ||
|
|
8562338b62 | ||
|
|
17de69834e | ||
|
|
aeeab7dbf6 | ||
|
|
ce60ba6c9f | ||
|
|
d22f93e564 | ||
|
|
6f4f6b8d1e | ||
|
|
6fa8ee53cd | ||
|
|
23af56c150 | ||
|
|
91828cbad7 | ||
|
|
40d2788ae0 | ||
|
|
21a3a28980 | ||
|
|
f5c7bee58f | ||
|
|
e364f5159b | ||
|
|
74f9f1ca00 | ||
|
|
328d38ac19 | ||
|
|
7735d82c7b | ||
|
|
1e7c200865 | ||
|
|
724b357dd8 | ||
|
|
a85efc5736 | ||
|
|
9eb58119ea | ||
|
|
77b4ed2cfd | ||
|
|
011416690a | ||
|
|
53eb5c8a73 | ||
|
|
2f99855cd4 | ||
|
|
18eaeec1de | ||
|
|
5ee700c9b9 | ||
|
|
a8949ca07e | ||
|
|
0a42579d9e | ||
|
|
f2f8747126 | ||
|
|
f83cdacab8 | ||
|
|
98aeb02d32 | ||
|
|
2bbaeccd05 | ||
|
|
91e0a04e66 | ||
|
|
661fc523ad | ||
|
|
b9cbaf7343 | ||
|
|
e451308fdc | ||
|
|
1b51c5efa4 | ||
|
|
a8776eec58 | ||
|
|
d9cdb7ef83 | ||
|
|
28c7a64bd2 | ||
|
|
d7193f7753 | ||
|
|
f5c7d11f0e | ||
|
|
c0ba96daa2 | ||
|
|
b374d9e04a | ||
|
|
2033ebdc72 | ||
|
|
c3bb896f40 | ||
|
|
4f7c28563d | ||
|
|
b1985a2bf2 | ||
|
|
ee50a4d025 | ||
|
|
b26f5a82d7 | ||
|
|
c044ae79cd | ||
|
|
a261538602 | ||
|
|
18daef7808 | ||
|
|
68d1ab58b7 | ||
|
|
2b660adf00 | ||
|
|
e15d01882f | ||
|
|
23491faccc | ||
|
|
5f2e1611f1 | ||
|
|
9abacaf905 | ||
|
|
322e056661 | ||
|
|
228f8e4ed1 | ||
|
|
d6415277d0 | ||
|
|
db41a6635f | ||
|
|
096919637f | ||
|
|
434d70ebff | ||
|
|
bb8829b66f | ||
|
|
10f2c107c6 | ||
|
|
03080b9ccd | ||
|
|
b00ad53e42 | ||
|
|
99e286456e | ||
|
|
46cdf56944 | ||
|
|
9d5105a9e5 | ||
|
|
3c5cb1673a | ||
|
|
13107063df | ||
|
|
8a73baa6bc | ||
|
|
b65fedf40d | ||
|
|
04ab276d93 | ||
|
|
6d88d6bde0 | ||
|
|
4103c6e659 | ||
|
|
16004f2ffe | ||
|
|
37c0d323d9 | ||
|
|
560cb1a266 | ||
|
|
738ad197b2 | ||
|
|
cfcc5287db | ||
|
|
0b5502b673 | ||
|
|
d3e3c9d720 | ||
|
|
3c3f88d820 | ||
|
|
2e2c654ec9 | ||
|
|
d361ae2381 | ||
|
|
fcbb93762a |
16
.gitignore
vendored
16
.gitignore
vendored
@@ -13,8 +13,6 @@ xcuserdata/
|
||||
DerivedData/
|
||||
|
||||
# Generated
|
||||
/platform-independent/cli-c/VERSION
|
||||
/platform-independent/cli-c/mpw-*.tar.gz
|
||||
/platform-darwin/Resources/Media/Images.xcassets/
|
||||
|
||||
# Media
|
||||
@@ -31,17 +29,3 @@ local.properties
|
||||
# Maven
|
||||
target
|
||||
dependency-reduced-pom.xml
|
||||
|
||||
# C
|
||||
core/c/*.o
|
||||
core/c/lib/*/.unpacked
|
||||
core/c/lib/*/.patched
|
||||
core/c/lib/*/src
|
||||
core/c/lib/include
|
||||
platform-independent/cli-c/cli/*.o
|
||||
platform-independent/cli-c/mpw-*.tar.gz
|
||||
platform-independent/cli-c/mpw-*.tar.gz.sig
|
||||
platform-independent/cli-c/mpw
|
||||
platform-independent/cli-c/mpw-bench
|
||||
platform-independent/cli-c/mpw-tests
|
||||
platform-independent/cli-c/VERSION
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -22,3 +22,6 @@
|
||||
[submodule "platform-darwin/External/libsodium"]
|
||||
path = platform-darwin/External/libsodium
|
||||
url = https://github.com/jedisct1/libsodium.git
|
||||
[submodule "platform-darwin/External/libjson-c"]
|
||||
path = platform-darwin/External/libjson-c
|
||||
url = https://github.com/lhunath/json-c.git
|
||||
|
||||
@@ -4,7 +4,7 @@ env: TERM=dumb SHLVL=0
|
||||
git:
|
||||
submodules: true
|
||||
script:
|
||||
- "( brew install libsodium )"
|
||||
- "( cd ./platform-independent/cli-c && ./clean && targets='mpw mpw-bench mpw-tests' ./build && ./mpw-tests )"
|
||||
- "( brew install libsodium json-c )"
|
||||
- "( cd ./platform-independent/cli-c && ./clean && targets='mpw mpw-bench mpw-tests' ./build && ./mpw-tests && ./mpw-cli-tests )"
|
||||
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword iOS' -sdk iphonesimulator )"
|
||||
- "( xcodebuild -workspace platform-darwin/MasterPassword.xcworkspace -configuration 'Test' -scheme 'MasterPassword macOS' )"
|
||||
|
||||
44
README.md
44
README.md
@@ -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.
|
||||
|
||||
When the build completes, you will have an `mpw` binary you can use. You can copy it into your `PATH` or use the `./install` script to help you do so.
|
||||
|
||||
For example:
|
||||
|
||||
./build && sudo ./install
|
||||
mpw -h
|
||||
|
||||
Normally, this is all you need to do, however note that there are a few dependencies that need to be met, depending on which targets you are building (by default, only the `mpw` target is built):
|
||||
|
||||
- `mpw`
|
||||
|
||||
The C implementation depends either on `libsodium` or Tarsnap's `scrypt` and `openssl-dev`.
|
||||
|
||||
We recommend you install `libsodium`. If `libsodium` is not installed when `./build` is executed, the script will try to download and statically link Tarsnap's `scrypt` instead. Tarsnap's `scrypt` depends on you having `openssl-dev` installed.
|
||||
|
||||
If you have `mpw_color` enabled (it is enabled by default), the build also depends on `ncurses-dev` to communicate with the terminal.
|
||||
|
||||
- `mpw-bench`
|
||||
|
||||
This tool compares the performance of a few cryptographic algorithms, including bcrypt. The `./build` script will try to automatically download and statically link `bcrypt`.
|
||||
|
||||
- `mpw-tests`
|
||||
|
||||
This tool runs a suite of tests to ensure the correct passwords are being generated by the algorithm under various circumstances. The test suite is declared in `mpw-tests.xml` which needs to exist in the current working directory when running the tool. In addition, `libxml2` is used to parse the file, so this target depends on you having it installed when running `./build`.
|
||||
|
||||
|
||||
Finally, there are a few different ways you can modify the build process.
|
||||
|
||||
To build additional targets, set the `targets` environment variable:
|
||||
|
||||
targets='mpw mpw-tests' ./build
|
||||
|
||||
To pass additional compiler arguments, eg. add a library search path, pass them as arguments to the script:
|
||||
|
||||
./build -L/usr/local/lib
|
||||
|
||||
There are a few toggleable features, to change them, pass them as environment variables:
|
||||
|
||||
mpw_color=0 ./build
|
||||
|
||||
Currently, there is only one toggleable feature:
|
||||
|
||||
- `mpw_color`: [default: 1] Colorized Identicon, depends on `ncurses-dev`.
|
||||
For detailed instructions, see [the native CLI instructions](platform-independent/cli-c/README.md).
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
155
core/c/base64.c
Normal file
155
core/c/base64.c
Normal file
@@ -0,0 +1,155 @@
|
||||
/* ====================================================================
|
||||
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by the Apache Group
|
||||
* for use in the Apache HTTP server project (http://www.apache.org/)."
|
||||
*
|
||||
* 4. The names "Apache Server" and "Apache Group" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please contact
|
||||
* apache@apache.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Apache"
|
||||
* nor may "Apache" appear in their names without prior written
|
||||
* permission of the Apache Group.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by the Apache Group
|
||||
* for use in the Apache HTTP server project (http://www.apache.org/)."
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
|
||||
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Group and was originally based
|
||||
* on public domain software written at the National Center for
|
||||
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
|
||||
* For more information on the Apache Group and the Apache HTTP server
|
||||
* project, please see <http://www.apache.org/>.
|
||||
*/
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
/* aaaack but it's fast and const should make it shared text page. */
|
||||
static const uint8_t b64ToBits[256] =
|
||||
{
|
||||
/* 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,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
|
||||
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
|
||||
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 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, 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, 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, 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, 64, 64, 64, 64, 64
|
||||
};
|
||||
|
||||
size_t mpw_base64_decode_max(const char *b64Text) {
|
||||
|
||||
register const uint8_t *b64Cursor = (uint8_t *)b64Text;
|
||||
while (b64ToBits[*(b64Cursor++)] <= 63);
|
||||
int b64Size = (int)(b64Cursor - (uint8_t *)b64Text) - 1;
|
||||
|
||||
// Every 4 b64 chars yield 3 plain bytes => len = 3 * ceil(b64Size / 4)
|
||||
return (size_t)(3 /*bytes*/ * ((b64Size + 4 /*chars*/ - 1) / 4 /*chars*/));
|
||||
}
|
||||
|
||||
int mpw_base64_decode(uint8_t *plainBuf, const char *b64Text) {
|
||||
|
||||
register const uint8_t *b64Cursor = (uint8_t *)b64Text;
|
||||
while (b64ToBits[*(b64Cursor++)] <= 63);
|
||||
int b64Remaining = (int)(b64Cursor - (uint8_t *)b64Text) - 1;
|
||||
|
||||
b64Cursor = (uint8_t *)b64Text;
|
||||
register uint8_t *plainCursor = plainBuf;
|
||||
while (b64Remaining > 4) {
|
||||
*(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[0]] << 2 | b64ToBits[b64Cursor[1]] >> 4);
|
||||
*(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[1]] << 4 | b64ToBits[b64Cursor[2]] >> 2);
|
||||
*(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[2]] << 6 | b64ToBits[b64Cursor[3]]);
|
||||
b64Cursor += 4;
|
||||
b64Remaining -= 4;
|
||||
}
|
||||
|
||||
/* Note: (b64Size == 1) would be an error, so just ingore that case */
|
||||
if (b64Remaining > 1)
|
||||
*(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[0]] << 2 | b64ToBits[b64Cursor[1]] >> 4);
|
||||
if (b64Remaining > 2)
|
||||
*(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[1]] << 4 | b64ToBits[b64Cursor[2]] >> 2);
|
||||
if (b64Remaining > 3)
|
||||
*(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[2]] << 6 | b64ToBits[b64Cursor[3]]);
|
||||
|
||||
return (int)(plainCursor - plainBuf);
|
||||
}
|
||||
|
||||
static const char basis_64[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
size_t mpw_base64_encode_max(size_t plainSize) {
|
||||
|
||||
// Every 3 plain bytes yield 4 b64 chars => len = 4 * ceil(plainSize / 3)
|
||||
return 4 /*chars*/ * (plainSize + 3 /*bytes*/ - 1) / 3 /*bytes*/;
|
||||
}
|
||||
|
||||
int mpw_base64_encode(char *b64Text, const uint8_t *plainBuf, size_t plainSize) {
|
||||
|
||||
size_t plainCursor = 0;
|
||||
char *b64Cursor = b64Text;
|
||||
for (; plainCursor < plainSize - 2; plainCursor += 3) {
|
||||
*b64Cursor++ = basis_64[((plainBuf[plainCursor] >> 2)) & 0x3F];
|
||||
*b64Cursor++ = basis_64[((plainBuf[plainCursor] & 0x3) << 4) |
|
||||
((plainBuf[plainCursor + 1] & 0xF0) >> 4)];
|
||||
*b64Cursor++ = basis_64[((plainBuf[plainCursor + 1] & 0xF) << 2) |
|
||||
((plainBuf[plainCursor + 2] & 0xC0) >> 6)];
|
||||
*b64Cursor++ = basis_64[plainBuf[plainCursor + 2] & 0x3F];
|
||||
}
|
||||
if (plainCursor < plainSize) {
|
||||
*b64Cursor++ = basis_64[(plainBuf[plainCursor] >> 2) & 0x3F];
|
||||
if (plainCursor == (plainSize - 1)) {
|
||||
*b64Cursor++ = basis_64[((plainBuf[plainCursor] & 0x3) << 4)];
|
||||
*b64Cursor++ = '=';
|
||||
}
|
||||
else {
|
||||
*b64Cursor++ = basis_64[((plainBuf[plainCursor] & 0x3) << 4) |
|
||||
((plainBuf[plainCursor + 1] & 0xF0) >> 4)];
|
||||
*b64Cursor++ = basis_64[((plainBuf[plainCursor + 1] & 0xF) << 2)];
|
||||
}
|
||||
*b64Cursor++ = '=';
|
||||
}
|
||||
|
||||
*b64Cursor = '\0';
|
||||
return (int)(b64Cursor - b64Text);
|
||||
}
|
||||
78
core/c/base64.h
Normal file
78
core/c/base64.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/* ====================================================================
|
||||
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by the Apache Group
|
||||
* for use in the Apache HTTP server project (http://www.apache.org/)."
|
||||
*
|
||||
* 4. The names "Apache Server" and "Apache Group" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please contact
|
||||
* apache@apache.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Apache"
|
||||
* nor may "Apache" appear in their names without prior written
|
||||
* permission of the Apache Group.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by the Apache Group
|
||||
* for use in the Apache HTTP server project (http://www.apache.org/)."
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
|
||||
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Group and was originally based
|
||||
* on public domain software written at the National Center for
|
||||
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
|
||||
* For more information on the Apache Group and the Apache HTTP server
|
||||
* project, please see <http://www.apache.org/>.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @return The amount of bytes needed to decode the given b64Text.
|
||||
*/
|
||||
size_t mpw_base64_decode_max(const char *b64Text);
|
||||
/** Decodes a base-64 encoded string into a plain byte buffer.
|
||||
* @param plainBuf a byte buffer, size should be at least mpw_base64_decode_max(b64Text)
|
||||
* @return The amount of bytes that were written to plainBuf.
|
||||
*/
|
||||
int mpw_base64_decode(uint8_t *plainBuf, const char *b64Text);
|
||||
|
||||
/**
|
||||
* @return The amount of characters needed to encode a plainBuf of the given size as base-64 (excluding the terminating NUL).
|
||||
*/
|
||||
size_t mpw_base64_encode_max(size_t plainSize);
|
||||
/** Encodes a plain byte buffer into a base-64 encoded string.
|
||||
* @param b64Text a character buffer, size should be at least mpw_base64_encode_max(plainSize) + 1
|
||||
* @return The amount of characters that were written to b64Text, excluding the terminating NUL.
|
||||
*/
|
||||
int mpw_base64_encode(char *b64Text, const uint8_t *plainBuf, size_t plainSize);
|
||||
@@ -1,4 +0,0 @@
|
||||
home=http://www.openwall.com/crypt/
|
||||
pkg=http://www.openwall.com/crypt/crypt_blowfish-1.3.tar.gz
|
||||
pkg_sha256=83fa01fca6996fe8d882b7f8e9ba0305a5664936100b01481ea3c6a8ce8d72fd
|
||||
patches=(arm)
|
||||
@@ -1,12 +0,0 @@
|
||||
--- x86.S 2014-11-21 09:09:58.000000000 -0500
|
||||
+++ x86.S 2014-11-21 09:11:01.000000000 -0500
|
||||
@@ -199,5 +199,9 @@
|
||||
#endif
|
||||
|
||||
#if defined(__ELF__) && defined(__linux__)
|
||||
+#if defined(__arm__)
|
||||
+.section .note.GNU-stack,"",%progbits
|
||||
+#else
|
||||
.section .note.GNU-stack,"",@progbits
|
||||
#endif
|
||||
+#endif
|
||||
@@ -1,4 +0,0 @@
|
||||
home=http://www.tarsnap.com/scrypt.html
|
||||
git=https://github.com/Tarsnap/scrypt.git
|
||||
pkg=https://www.tarsnap.com/scrypt/scrypt-1.2.1.tgz
|
||||
pkg_sha256=4621f5e7da2f802e20850436219370092e9fcda93bd598f6d4236cce33f4c577
|
||||
@@ -1,38 +0,0 @@
|
||||
diff -ruN /Users/lhunath/.src/scrypt/Makefile ./Makefile
|
||||
--- /Users/lhunath/.src/scrypt/Makefile 2014-05-02 11:28:58.000000000 -0400
|
||||
+++ ./Makefile 2014-05-02 12:07:27.000000000 -0400
|
||||
@@ -2,11 +2,11 @@
|
||||
VER?= nosse
|
||||
SRCS= main.c
|
||||
LDADD+= -lcrypto
|
||||
-WARNS?= 6
|
||||
+WARNS?= 0
|
||||
|
||||
# We have a config file for FreeBSD
|
||||
CFLAGS += -I .
|
||||
-CFLAGS += -DCONFIG_H_FILE=\"config_freebsd.h\"
|
||||
+CFLAGS += -DCONFIG_H_FILE=\"config_osx.h\"
|
||||
|
||||
# Include all possible object files containing built scrypt code.
|
||||
CLEANFILES += crypto_scrypt-ref.o
|
||||
diff -ruN /Users/lhunath/.src/scrypt/lib/util/memlimit.c ./lib/util/memlimit.c
|
||||
--- /Users/lhunath/.src/scrypt/lib/util/memlimit.c 2014-05-02 11:28:58.000000000 -0400
|
||||
+++ ./lib/util/memlimit.c 2014-05-02 11:52:42.000000000 -0400
|
||||
@@ -75,7 +75,7 @@
|
||||
* have returned to us.
|
||||
*/
|
||||
if (usermemlen == sizeof(uint64_t))
|
||||
- usermem = *(uint64_t *)usermembuf;
|
||||
+ usermem = *(uint64_t *)(void *)usermembuf;
|
||||
else if (usermemlen == sizeof(uint32_t))
|
||||
usermem = SIZE_MAX;
|
||||
else
|
||||
diff -ruN /Users/lhunath/.src/scrypt/lib/util/memlimit.c ./lib/util/memlimit.c
|
||||
--- /Users/lhunath/.src/scrypt/config_osx.h 1969-12-31 19:00:00.000000000 -0500
|
||||
+++ config_osx.h 2014-05-02 12:06:55.000000000 -0400
|
||||
@@ -0,0 +1,5 @@
|
||||
+/* A default configuration for FreeBSD, used if there is no config.h. */
|
||||
+
|
||||
+#define HAVE_POSIX_MEMALIGN 1
|
||||
+#define HAVE_SYSCTL_HW_USERMEM 1
|
||||
+#define HAVE_SYS_PARAM_H 1
|
||||
@@ -22,48 +22,174 @@
|
||||
#include "mpw-algorithm_v2.c"
|
||||
#include "mpw-algorithm_v3.c"
|
||||
|
||||
#define MP_N 32768
|
||||
#define MP_r 8
|
||||
#define MP_p 2
|
||||
#define MP_hash PearlHashSHA256
|
||||
MPMasterKey mpw_masterKey(const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) {
|
||||
|
||||
const uint8_t *mpw_masterKeyForUser(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( "fullName: %s\n", fullName );
|
||||
trc( "masterPassword.id: %s\n", masterPassword? mpw_id_buf( masterPassword, strlen( masterPassword ) ): NULL );
|
||||
if (!fullName || !masterPassword)
|
||||
return NULL;
|
||||
|
||||
switch (algorithmVersion) {
|
||||
case MPAlgorithmVersion0:
|
||||
return mpw_masterKeyForUser_v0( fullName, masterPassword );
|
||||
return mpw_masterKey_v0( fullName, masterPassword );
|
||||
case MPAlgorithmVersion1:
|
||||
return mpw_masterKeyForUser_v1( fullName, masterPassword );
|
||||
return mpw_masterKey_v1( fullName, masterPassword );
|
||||
case MPAlgorithmVersion2:
|
||||
return mpw_masterKeyForUser_v2( fullName, masterPassword );
|
||||
return mpw_masterKey_v2( fullName, masterPassword );
|
||||
case MPAlgorithmVersion3:
|
||||
return mpw_masterKeyForUser_v3( fullName, masterPassword );
|
||||
return mpw_masterKey_v3( fullName, masterPassword );
|
||||
default:
|
||||
ftl( "Unsupported version: %d", algorithmVersion );
|
||||
err( "Unsupported version: %d\n", algorithmVersion );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char *mpw_passwordForSite(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
||||
const MPSiteVariant siteVariant, const char *siteContext, const MPAlgorithmVersion algorithmVersion) {
|
||||
MPSiteKey mpw_siteKey(
|
||||
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
|
||||
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( "siteName: %s\n", siteName );
|
||||
trc( "siteCounter: %d\n", siteCounter );
|
||||
trc( "keyPurpose: %d (%s)\n", keyPurpose, mpw_nameForPurpose( keyPurpose ) );
|
||||
trc( "keyContext: %s\n", keyContext );
|
||||
if (!masterKey || !siteName)
|
||||
return NULL;
|
||||
|
||||
switch (algorithmVersion) {
|
||||
case MPAlgorithmVersion0:
|
||||
return mpw_passwordForSite_v0( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
||||
return mpw_siteKey_v0( masterKey, siteName, siteCounter, keyPurpose, keyContext );
|
||||
case MPAlgorithmVersion1:
|
||||
return mpw_passwordForSite_v1( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
||||
return mpw_siteKey_v1( masterKey, siteName, siteCounter, keyPurpose, keyContext );
|
||||
case MPAlgorithmVersion2:
|
||||
return mpw_passwordForSite_v2( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
||||
return mpw_siteKey_v2( masterKey, siteName, siteCounter, keyPurpose, keyContext );
|
||||
case MPAlgorithmVersion3:
|
||||
return mpw_passwordForSite_v3( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
||||
return mpw_siteKey_v3( masterKey, siteName, siteCounter, keyPurpose, keyContext );
|
||||
default:
|
||||
ftl( "Unsupported version: %d", algorithmVersion );
|
||||
err( "Unsupported version: %d\n", algorithmVersion );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char *mpw_siteResult(
|
||||
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
|
||||
const MPKeyPurpose keyPurpose, const char *keyContext,
|
||||
const MPResultType resultType, const char *resultParam,
|
||||
const MPAlgorithmVersion algorithmVersion) {
|
||||
|
||||
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)
|
||||
return NULL;
|
||||
|
||||
trc( "-- mpw_siteResult (algorithm: %u)\n", algorithmVersion );
|
||||
trc( "resultType: %d (%s)\n", resultType, mpw_nameForType( resultType ) );
|
||||
trc( "resultParam: %s\n", resultParam );
|
||||
|
||||
char *sitePassword = NULL;
|
||||
if (resultType & MPResultTypeClassTemplate) {
|
||||
switch (algorithmVersion) {
|
||||
case MPAlgorithmVersion0:
|
||||
return mpw_sitePasswordFromTemplate_v0( masterKey, siteKey, resultType, resultParam );
|
||||
case MPAlgorithmVersion1:
|
||||
return mpw_sitePasswordFromTemplate_v1( masterKey, siteKey, resultType, resultParam );
|
||||
case MPAlgorithmVersion2:
|
||||
return mpw_sitePasswordFromTemplate_v2( masterKey, siteKey, resultType, resultParam );
|
||||
case MPAlgorithmVersion3:
|
||||
return mpw_sitePasswordFromTemplate_v3( masterKey, siteKey, resultType, resultParam );
|
||||
default:
|
||||
err( "Unsupported version: %d\n", algorithmVersion );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (resultType & MPResultTypeClassStateful) {
|
||||
switch (algorithmVersion) {
|
||||
case MPAlgorithmVersion0:
|
||||
return mpw_sitePasswordFromCrypt_v0( masterKey, siteKey, resultType, resultParam );
|
||||
case MPAlgorithmVersion1:
|
||||
return mpw_sitePasswordFromCrypt_v1( masterKey, siteKey, resultType, resultParam );
|
||||
case MPAlgorithmVersion2:
|
||||
return mpw_sitePasswordFromCrypt_v2( masterKey, siteKey, resultType, resultParam );
|
||||
case MPAlgorithmVersion3:
|
||||
return mpw_sitePasswordFromCrypt_v3( masterKey, siteKey, resultType, resultParam );
|
||||
default:
|
||||
err( "Unsupported version: %d\n", algorithmVersion );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (resultType & MPResultTypeClassDerive) {
|
||||
switch (algorithmVersion) {
|
||||
case MPAlgorithmVersion0:
|
||||
return mpw_sitePasswordFromDerive_v0( masterKey, siteKey, resultType, resultParam );
|
||||
case MPAlgorithmVersion1:
|
||||
return mpw_sitePasswordFromDerive_v1( masterKey, siteKey, resultType, resultParam );
|
||||
case MPAlgorithmVersion2:
|
||||
return mpw_sitePasswordFromDerive_v2( masterKey, siteKey, resultType, resultParam );
|
||||
case MPAlgorithmVersion3:
|
||||
return mpw_sitePasswordFromDerive_v3( masterKey, siteKey, resultType, resultParam );
|
||||
default:
|
||||
err( "Unsupported version: %d\n", algorithmVersion );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
err( "Unsupported password type: %d\n", resultType );
|
||||
}
|
||||
|
||||
return sitePassword;
|
||||
}
|
||||
|
||||
const char *mpw_siteState(
|
||||
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
|
||||
const MPKeyPurpose keyPurpose, const char *keyContext,
|
||||
const MPResultType resultType, const char *resultParam,
|
||||
const MPAlgorithmVersion algorithmVersion) {
|
||||
|
||||
if (siteName && !strlen( siteName ))
|
||||
siteName = NULL;
|
||||
if (keyContext && !strlen( keyContext ))
|
||||
keyContext = NULL;
|
||||
if (resultParam && !strlen( resultParam ))
|
||||
resultParam = NULL;
|
||||
|
||||
MPSiteKey siteKey = mpw_siteKey_v0( masterKey, siteName, siteCounter, keyPurpose, keyContext );
|
||||
if (!siteKey)
|
||||
return NULL;
|
||||
|
||||
trc( "-- mpw_siteState (algorithm: %u)\n", algorithmVersion );
|
||||
trc( "resultType: %d (%s)\n", resultType, mpw_nameForType( resultType ) );
|
||||
trc( "resultParam: %s\n", resultParam );
|
||||
if (!masterKey || !resultParam)
|
||||
return NULL;
|
||||
|
||||
switch (algorithmVersion) {
|
||||
case MPAlgorithmVersion0:
|
||||
return mpw_siteState_v0( masterKey, siteKey, resultType, resultParam );
|
||||
case MPAlgorithmVersion1:
|
||||
return mpw_siteState_v1( masterKey, siteKey, resultType, resultParam );
|
||||
case MPAlgorithmVersion2:
|
||||
return mpw_siteState_v2( masterKey, siteKey, resultType, resultParam );
|
||||
case MPAlgorithmVersion3:
|
||||
return mpw_siteState_v3( masterKey, siteKey, resultType, resultParam );
|
||||
default:
|
||||
err( "Unsupported version: %d\n", algorithmVersion );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,10 @@
|
||||
// NOTE: mpw is currently NOT thread-safe.
|
||||
#include "mpw-types.h"
|
||||
|
||||
typedef enum(unsigned int, MPAlgorithmVersion) {
|
||||
#ifndef _MPW_ALGORITHM_H
|
||||
#define _MPW_ALGORITHM_H
|
||||
|
||||
typedef mpw_enum( unsigned int, MPAlgorithmVersion ) {
|
||||
/** V0 did math with chars whose signedness was platform-dependent. */
|
||||
MPAlgorithmVersion0,
|
||||
/** V1 miscounted the byte-length of multi-byte site names. */
|
||||
@@ -28,16 +31,39 @@ typedef enum(unsigned int, MPAlgorithmVersion) {
|
||||
MPAlgorithmVersion2,
|
||||
/** V3 is the current version. */
|
||||
MPAlgorithmVersion3,
|
||||
|
||||
MPAlgorithmVersionCurrent = MPAlgorithmVersion3,
|
||||
MPAlgorithmVersionFirst = MPAlgorithmVersion0,
|
||||
MPAlgorithmVersionLast = MPAlgorithmVersion3,
|
||||
};
|
||||
#define MPAlgorithmVersionCurrent MPAlgorithmVersion3
|
||||
|
||||
/** Derive the master key for a user based on their name and master password.
|
||||
* @return A new MP_dkLen-byte allocated buffer or NULL if an allocation error occurred. */
|
||||
const uint8_t *mpw_masterKeyForUser(
|
||||
* @return A new MPMasterKeySize-byte allocated buffer or NULL if an error occurred. */
|
||||
MPMasterKey mpw_masterKey(
|
||||
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
|
||||
|
||||
/** Encode a password for the site from the given master key and site parameters.
|
||||
* @return A newly allocated string or NULL if an allocation error occurred. */
|
||||
const char *mpw_passwordForSite(
|
||||
const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
||||
const MPSiteVariant siteVariant, const char *siteContext, const MPAlgorithmVersion algorithmVersion);
|
||||
/** Derive the site key for a user's site from the given master key and site parameters.
|
||||
* @return A new MPSiteKeySize-byte allocated buffer or NULL if an error occurred. */
|
||||
MPSiteKey mpw_siteKey(
|
||||
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
|
||||
const MPKeyPurpose keyPurpose, const char *keyContext, const MPAlgorithmVersion algorithmVersion);
|
||||
|
||||
/** 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. */
|
||||
const char *mpw_siteResult(
|
||||
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
|
||||
const MPKeyPurpose keyPurpose, const char *keyContext,
|
||||
const MPResultType resultType, const char *resultParam,
|
||||
const MPAlgorithmVersion algorithmVersion);
|
||||
|
||||
/** Encrypt a stateful site token for persistence.
|
||||
* @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(
|
||||
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
|
||||
const MPKeyPurpose keyPurpose, const char *keyContext,
|
||||
const MPResultType resultType, const char *resultParam,
|
||||
const MPAlgorithmVersion algorithmVersion);
|
||||
|
||||
#endif // _MPW_ALGORITHM_H
|
||||
|
||||
@@ -18,123 +18,238 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "mpw-types.h"
|
||||
#include "mpw-util.h"
|
||||
#include "base64.h"
|
||||
|
||||
#define MP_N 32768
|
||||
#define MP_r 8
|
||||
#define MP_p 2
|
||||
#define MP_hash PearlHashSHA256
|
||||
#define MP_N 32768LU
|
||||
#define MP_r 8U
|
||||
#define MP_p 2U
|
||||
#define MP_otp_window 5 * 60 /* s */
|
||||
|
||||
static const char *mpw_templateForType_v0(MPSiteType type, uint16_t seedByte) {
|
||||
// Algorithm version helpers.
|
||||
static const char *mpw_templateForType_v0(MPResultType type, uint16_t templateIndex) {
|
||||
|
||||
size_t count = 0;
|
||||
const char **templates = mpw_templatesForType( type, &count );
|
||||
char const *template = count? templates[seedByte % count]: NULL;
|
||||
char const *template = templates && count? templates[templateIndex % count]: NULL;
|
||||
free( templates );
|
||||
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 );
|
||||
return classCharacters[seedByte % strlen( classCharacters )];
|
||||
if (!classCharacters)
|
||||
return '\0';
|
||||
|
||||
return classCharacters[classIndex % strlen( classCharacters )];
|
||||
}
|
||||
|
||||
static const uint8_t *mpw_masterKeyForUser_v0(const char *fullName, const char *masterPassword) {
|
||||
// Algorithm version overrides.
|
||||
static MPMasterKey mpw_masterKey_v0(
|
||||
const char *fullName, const char *masterPassword) {
|
||||
|
||||
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
||||
trc( "algorithm: v%d\n", 0 );
|
||||
trc( "fullName: %s (%zu)\n", fullName, mpw_utf8_strlen( fullName ) );
|
||||
trc( "masterPassword: %s\n", masterPassword );
|
||||
trc( "key scope: %s\n", mpKeyScope );
|
||||
const char *keyScope = mpw_scopeForPurpose( MPKeyPurposeAuthentication );
|
||||
trc( "keyScope: %s\n", keyScope );
|
||||
|
||||
// Calculate the master key salt.
|
||||
// masterKeySalt = mpKeyScope . #fullName . fullName
|
||||
trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s\n",
|
||||
keyScope, mpw_hex_l( (uint32_t)mpw_utf8_strlen( fullName ) ), fullName );
|
||||
size_t masterKeySaltSize = 0;
|
||||
uint8_t *masterKeySalt = NULL;
|
||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( mpw_utf8_strlen( fullName ) ) );
|
||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, keyScope );
|
||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, (uint32_t)mpw_utf8_strlen( fullName ) );
|
||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
|
||||
if (!masterKeySalt) {
|
||||
ftl( "Could not allocate master key salt: %d\n", errno );
|
||||
err( "Could not allocate master key salt: %s\n", strerror( errno ) );
|
||||
return NULL;
|
||||
}
|
||||
trc( "masterKeySalt ID: %s\n", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
|
||||
trc( " => masterKeySalt.id: %s\n", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
|
||||
|
||||
// Calculate the master key.
|
||||
// masterKey = scrypt( masterPassword, masterKeySalt )
|
||||
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
|
||||
mpw_free( masterKeySalt, masterKeySaltSize );
|
||||
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 );
|
||||
mpw_free( &masterKeySalt, masterKeySaltSize );
|
||||
if (!masterKey) {
|
||||
ftl( "Could not allocate master key: %d\n", errno );
|
||||
err( "Could not derive master key: %s\n", strerror( errno ) );
|
||||
return NULL;
|
||||
}
|
||||
trc( "masterKey ID: %s\n", mpw_id_buf( masterKey, MP_dkLen ) );
|
||||
trc( " => masterKey.id: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) );
|
||||
|
||||
return masterKey;
|
||||
}
|
||||
|
||||
static const char *mpw_passwordForSite_v0(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
||||
const MPSiteVariant siteVariant, const char *siteContext) {
|
||||
static MPSiteKey mpw_siteKey_v0(
|
||||
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
|
||||
MPKeyPurpose keyPurpose, const char *keyContext) {
|
||||
|
||||
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
||||
trc( "algorithm: v%d\n", 0 );
|
||||
trc( "siteName: %s\n", siteName );
|
||||
trc( "siteCounter: %d\n", siteCounter );
|
||||
trc( "siteVariant: %d\n", siteVariant );
|
||||
trc( "siteType: %d\n", siteType );
|
||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
||||
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
||||
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
||||
mpw_hex_l( htonl( siteCounter ) ),
|
||||
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
||||
const char *keyScope = mpw_scopeForPurpose( keyPurpose );
|
||||
trc( "keyScope: %s\n", keyScope );
|
||||
|
||||
// OTP counter value.
|
||||
if (siteCounter == MPCounterValueTOTP)
|
||||
siteCounter = ((uint32_t)time( NULL ) / MP_otp_window) * MP_otp_window;
|
||||
|
||||
// Calculate the site seed.
|
||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
||||
size_t sitePasswordInfoSize = 0;
|
||||
uint8_t *sitePasswordInfo = NULL;
|
||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_utf8_strlen( siteName ) ) );
|
||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
||||
if (siteContext) {
|
||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_utf8_strlen( siteContext ) ) );
|
||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
||||
trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s\n",
|
||||
keyScope, mpw_hex_l( (uint32_t)mpw_utf8_strlen( siteName ) ), siteName, mpw_hex_l( siteCounter ),
|
||||
keyContext? mpw_hex_l( (uint32_t)mpw_utf8_strlen( keyContext ) ): NULL, keyContext );
|
||||
size_t siteSaltSize = 0;
|
||||
uint8_t *siteSalt = NULL;
|
||||
mpw_push_string( &siteSalt, &siteSaltSize, keyScope );
|
||||
mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)mpw_utf8_strlen( siteName ) );
|
||||
mpw_push_string( &siteSalt, &siteSaltSize, siteName );
|
||||
mpw_push_int( &siteSalt, &siteSaltSize, siteCounter );
|
||||
if (keyContext) {
|
||||
mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)mpw_utf8_strlen( keyContext ) );
|
||||
mpw_push_string( &siteSalt, &siteSaltSize, keyContext );
|
||||
}
|
||||
if (!sitePasswordInfo) {
|
||||
ftl( "Could not allocate site seed info: %d\n", errno );
|
||||
if (!siteSalt) {
|
||||
err( "Could not allocate site salt: %s\n", strerror( errno ) );
|
||||
return NULL;
|
||||
}
|
||||
trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( sitePasswordInfo, sitePasswordInfoSize ) );
|
||||
trc( " => siteSalt.id: %s\n", mpw_id_buf( siteSalt, siteSaltSize ) );
|
||||
|
||||
const char *sitePasswordSeed = (const char *)mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
|
||||
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
|
||||
if (!sitePasswordSeed) {
|
||||
ftl( "Could not allocate site seed: %d\n", errno );
|
||||
trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )\n",
|
||||
mpw_id_buf( masterKey, MPMasterKeySize ) );
|
||||
MPSiteKey siteKey = mpw_hash_hmac_sha256( masterKey, MPMasterKeySize, siteSalt, siteSaltSize );
|
||||
mpw_free( &siteSalt, siteSaltSize );
|
||||
if (!siteKey) {
|
||||
err( "Could not derive site key: %s\n", strerror( errno ) );
|
||||
return NULL;
|
||||
}
|
||||
trc( "sitePasswordSeed ID: %s\n", mpw_id_buf( sitePasswordSeed, 32 ) );
|
||||
trc( " => siteKey.id: %s\n", mpw_id_buf( siteKey, MPSiteKeySize ) );
|
||||
|
||||
return siteKey;
|
||||
}
|
||||
|
||||
static const char *mpw_sitePasswordFromTemplate_v0(
|
||||
MPMasterKey __unused masterKey, MPSiteKey siteKey, MPResultType resultType, const char __unused *resultParam) {
|
||||
|
||||
const char *_siteKey = (const char *)siteKey;
|
||||
|
||||
// Determine the template.
|
||||
const char *template = mpw_templateForType_v0( siteType, htons( sitePasswordSeed[0] ) );
|
||||
trc( "type %d, template: %s\n", siteType, template );
|
||||
if (strlen( template ) > 32) {
|
||||
ftl( "Template too long for password seed: %lu", strlen( template ) );
|
||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||
uint16_t seedByte;
|
||||
mpw_uint16( (uint16_t)_siteKey[0], (uint8_t *)&seedByte );
|
||||
const char *template = mpw_templateForType_v0( resultType, seedByte );
|
||||
trc( "template: %u => %s\n", seedByte, template );
|
||||
if (!template)
|
||||
return NULL;
|
||||
if (strlen( template ) > MPSiteKeySize) {
|
||||
err( "Template too long for password seed: %zu\n", strlen( template ) );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Encode the password from the seed using the template.
|
||||
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
|
||||
for (size_t c = 0; c < strlen( template ); ++c) {
|
||||
sitePassword[c] = mpw_characterFromClass_v0( template[c], htons( sitePasswordSeed[c + 1] ) );
|
||||
trc( "class %c, index %u (0x%02X) -> character: %c\n",
|
||||
template[c], htons( sitePasswordSeed[c + 1] ), htons( sitePasswordSeed[c + 1] ), sitePassword[c] );
|
||||
mpw_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",
|
||||
template[c], seedByte, seedByte, sitePassword[c] );
|
||||
}
|
||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||
trc( " => password: %s\n", sitePassword );
|
||||
|
||||
return sitePassword;
|
||||
}
|
||||
|
||||
static const char *mpw_sitePasswordFromCrypt_v0(
|
||||
MPMasterKey masterKey, MPSiteKey __unused siteKey, MPResultType __unused resultType, const char *cipherText) {
|
||||
|
||||
if (!cipherText) {
|
||||
err( "Missing encrypted state.\n" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Base64-decode
|
||||
uint8_t *cipherBuf = calloc( 1, mpw_base64_decode_max( cipherText ) );
|
||||
size_t bufSize = (size_t)mpw_base64_decode( cipherBuf, cipherText );
|
||||
if ((int)bufSize < 0) {
|
||||
err( "Base64 decoding error." );
|
||||
mpw_free( &cipherBuf, mpw_base64_decode_max( cipherText ) );
|
||||
return NULL;
|
||||
}
|
||||
trc( "b64 decoded: %zu bytes = %s\n", bufSize, mpw_hex( cipherBuf, bufSize ) );
|
||||
|
||||
// Decrypt
|
||||
const uint8_t *plainBytes = mpw_aes_decrypt( masterKey, MPMasterKeySize, cipherBuf, bufSize );
|
||||
mpw_free( &cipherBuf, bufSize );
|
||||
const char *plainText = strndup( (char *)plainBytes, bufSize );
|
||||
mpw_free( &plainBytes, bufSize );
|
||||
if (!plainText)
|
||||
err( "AES decryption error: %s\n", strerror( errno ) );
|
||||
trc( "decrypted -> plainText: %s = %s\n", plainText, mpw_hex( plainText, sizeof( plainText ) ) );
|
||||
|
||||
return plainText;
|
||||
}
|
||||
|
||||
static const char *mpw_sitePasswordFromDerive_v0(
|
||||
MPMasterKey __unused masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
|
||||
|
||||
switch (resultType) {
|
||||
case MPResultTypeDeriveKey: {
|
||||
if (!resultParam) {
|
||||
err( "Missing key size parameter.\n" );
|
||||
return NULL;
|
||||
}
|
||||
int resultParamInt = atoi( resultParam );
|
||||
if (resultParamInt < 128 || resultParamInt > 512 || resultParamInt % 8 != 0) {
|
||||
err( "Parameter is not a valid key size (should be 128 - 512): %s\n", resultParam );
|
||||
return NULL;
|
||||
}
|
||||
uint16_t keySize = (uint16_t)(resultParamInt / 8);
|
||||
trc( "keySize: %u\n", keySize );
|
||||
|
||||
// Derive key
|
||||
const uint8_t *resultKey = mpw_kdf_blake2b( keySize, siteKey, MPSiteKeySize, NULL, 0, 0, NULL );
|
||||
if (!resultKey) {
|
||||
err( "Could not derive result key: %s\n", strerror( errno ) );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Base64-encode
|
||||
size_t b64Max = mpw_base64_encode_max( keySize );
|
||||
char *b64Key = calloc( 1, b64Max + 1 );
|
||||
if (mpw_base64_encode( b64Key, resultKey, keySize ) < 0) {
|
||||
err( "Base64 encoding error." );
|
||||
mpw_free_string( &b64Key );
|
||||
}
|
||||
else
|
||||
trc( "b64 encoded -> key.id: %s\n", mpw_id_buf( b64Key, strlen( b64Key ) ) );
|
||||
mpw_free( &resultKey, keySize );
|
||||
|
||||
return b64Key;
|
||||
}
|
||||
default:
|
||||
err( "Unsupported derived password type: %d\n", resultType );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *mpw_siteState_v0(
|
||||
MPMasterKey masterKey, MPSiteKey __unused siteKey, MPResultType __unused resultType, const char *plainText) {
|
||||
|
||||
// Encrypt
|
||||
size_t bufSize = strlen( plainText );
|
||||
const uint8_t *cipherBuf = mpw_aes_encrypt( masterKey, MPMasterKeySize, (const uint8_t *)plainText, bufSize );
|
||||
if (!cipherBuf) {
|
||||
err( "AES encryption error: %s\n", strerror( errno ) );
|
||||
return NULL;
|
||||
}
|
||||
trc( "cipherBuf: %zu bytes = %s\n", bufSize, mpw_hex( cipherBuf, bufSize ) );
|
||||
|
||||
// Base64-encode
|
||||
size_t b64Max = mpw_base64_encode_max( bufSize );
|
||||
char *cipherText = calloc( 1, b64Max + 1 );
|
||||
if (mpw_base64_encode( cipherText, cipherBuf, bufSize ) < 0) {
|
||||
err( "Base64 encoding error." );
|
||||
mpw_free_string( &cipherText );
|
||||
}
|
||||
else
|
||||
trc( "b64 encoded -> cipherText: %s = %s\n", cipherText, mpw_hex( cipherText, sizeof( cipherText ) ) );
|
||||
mpw_free( &cipherBuf, bufSize );
|
||||
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
@@ -17,109 +17,82 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "mpw-types.h"
|
||||
#include "mpw-util.h"
|
||||
|
||||
#define MP_N 32768
|
||||
#define MP_r 8
|
||||
#define MP_p 2
|
||||
#define MP_hash PearlHashSHA256
|
||||
#define MP_N 32768LU
|
||||
#define MP_r 8U
|
||||
#define MP_p 2U
|
||||
#define MP_otp_window 5 * 60 /* s */
|
||||
|
||||
static const uint8_t *mpw_masterKeyForUser_v1(const char *fullName, const char *masterPassword) {
|
||||
// Inherited functions.
|
||||
MPMasterKey mpw_masterKey_v0(
|
||||
const char *fullName, const char *masterPassword);
|
||||
MPSiteKey mpw_siteKey_v0(
|
||||
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
|
||||
MPKeyPurpose keyPurpose, const char *keyContext);
|
||||
const char *mpw_sitePasswordFromCrypt_v0(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText);
|
||||
const char *mpw_sitePasswordFromDerive_v0(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
|
||||
const char *mpw_siteState_v0(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state);
|
||||
|
||||
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
||||
trc( "algorithm: v%d\n", 1 );
|
||||
trc( "fullName: %s (%zu)\n", fullName, mpw_utf8_strlen( fullName ) );
|
||||
trc( "masterPassword: %s\n", masterPassword );
|
||||
trc( "key scope: %s\n", mpKeyScope );
|
||||
// Algorithm version overrides.
|
||||
static MPMasterKey mpw_masterKey_v1(
|
||||
const char *fullName, const char *masterPassword) {
|
||||
|
||||
// Calculate the master key salt.
|
||||
// masterKeySalt = mpKeyScope . #fullName . fullName
|
||||
size_t masterKeySaltSize = 0;
|
||||
uint8_t *masterKeySalt = NULL;
|
||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( mpw_utf8_strlen( fullName ) ) );
|
||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
|
||||
if (!masterKeySalt) {
|
||||
ftl( "Could not allocate master key salt: %d\n", errno );
|
||||
return NULL;
|
||||
}
|
||||
trc( "masterKeySalt ID: %s\n", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
|
||||
|
||||
// Calculate the master key.
|
||||
// masterKey = scrypt( masterPassword, masterKeySalt )
|
||||
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
|
||||
mpw_free( masterKeySalt, masterKeySaltSize );
|
||||
if (!masterKey) {
|
||||
ftl( "Could not allocate master key: %d\n", errno );
|
||||
return NULL;
|
||||
}
|
||||
trc( "masterKey ID: %s\n", mpw_id_buf( masterKey, MP_dkLen ) );
|
||||
|
||||
return masterKey;
|
||||
return mpw_masterKey_v0( fullName, masterPassword );
|
||||
}
|
||||
|
||||
static const char *mpw_passwordForSite_v1(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
||||
const MPSiteVariant siteVariant, const char *siteContext) {
|
||||
static MPSiteKey mpw_siteKey_v1(
|
||||
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
|
||||
MPKeyPurpose keyPurpose, const char *keyContext) {
|
||||
|
||||
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
||||
trc( "algorithm: v%d\n", 1 );
|
||||
trc( "siteName: %s\n", siteName );
|
||||
trc( "siteCounter: %d\n", siteCounter );
|
||||
trc( "siteVariant: %d\n", siteVariant );
|
||||
trc( "siteType: %d\n", siteType );
|
||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
||||
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
||||
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
||||
mpw_hex_l( htonl( siteCounter ) ),
|
||||
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
||||
return mpw_siteKey_v0( masterKey, siteName, siteCounter, keyPurpose, keyContext );
|
||||
}
|
||||
|
||||
// Calculate the site seed.
|
||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
||||
size_t sitePasswordInfoSize = 0;
|
||||
uint8_t *sitePasswordInfo = NULL;
|
||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_utf8_strlen( siteName ) ) );
|
||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
||||
if (siteContext) {
|
||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_utf8_strlen( siteContext ) ) );
|
||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
||||
}
|
||||
if (!sitePasswordInfo) {
|
||||
ftl( "Could not allocate site seed info: %d\n", errno );
|
||||
return NULL;
|
||||
}
|
||||
trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( sitePasswordInfo, sitePasswordInfoSize ) );
|
||||
|
||||
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
|
||||
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
|
||||
if (!sitePasswordSeed) {
|
||||
ftl( "Could not allocate site seed: %d\n", errno );
|
||||
return NULL;
|
||||
}
|
||||
trc( "sitePasswordSeed ID: %s\n", mpw_id_buf( sitePasswordSeed, 32 ) );
|
||||
static const char *mpw_sitePasswordFromTemplate_v1(
|
||||
MPMasterKey __unused masterKey, MPSiteKey siteKey, MPResultType resultType, const char __unused *resultParam) {
|
||||
|
||||
// Determine the template.
|
||||
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
|
||||
trc( "type %d, template: %s\n", siteType, template );
|
||||
if (strlen( template ) > 32) {
|
||||
ftl( "Template too long for password seed: %lu", strlen( template ) );
|
||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||
uint8_t seedByte = siteKey[0];
|
||||
const char *template = mpw_templateForType( resultType, seedByte );
|
||||
trc( "template: %u => %s\n", seedByte, template );
|
||||
if (!template)
|
||||
return NULL;
|
||||
if (strlen( template ) > MPSiteKeySize) {
|
||||
err( "Template too long for password seed: %zu\n", strlen( template ) );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Encode the password from the seed using the template.
|
||||
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
|
||||
for (size_t c = 0; c < strlen( template ); ++c) {
|
||||
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
|
||||
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
|
||||
sitePassword[c] );
|
||||
seedByte = siteKey[c + 1];
|
||||
sitePassword[c] = mpw_characterFromClass( template[c], seedByte );
|
||||
trc( " - class: %c, index: %3u (0x%02hhX) => character: %c\n",
|
||||
template[c], seedByte, seedByte, sitePassword[c] );
|
||||
}
|
||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||
trc( " => password: %s\n", sitePassword );
|
||||
|
||||
return sitePassword;
|
||||
}
|
||||
|
||||
static const char *mpw_sitePasswordFromCrypt_v1(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText) {
|
||||
|
||||
return mpw_sitePasswordFromCrypt_v0( masterKey, siteKey, resultType, cipherText );
|
||||
}
|
||||
|
||||
static const char *mpw_sitePasswordFromDerive_v1(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
|
||||
|
||||
return mpw_sitePasswordFromDerive_v0( masterKey, siteKey, resultType, resultParam );
|
||||
}
|
||||
|
||||
static const char *mpw_siteState_v1(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state) {
|
||||
|
||||
return mpw_siteState_v0( masterKey, siteKey, resultType, state );
|
||||
}
|
||||
|
||||
@@ -18,108 +18,98 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "mpw-types.h"
|
||||
#include "mpw-util.h"
|
||||
|
||||
#define MP_N 32768
|
||||
#define MP_r 8
|
||||
#define MP_p 2
|
||||
#define MP_hash PearlHashSHA256
|
||||
#define MP_N 32768LU
|
||||
#define MP_r 8U
|
||||
#define MP_p 2U
|
||||
#define MP_otp_window 5 * 60 /* s */
|
||||
|
||||
static const uint8_t *mpw_masterKeyForUser_v2(const char *fullName, const char *masterPassword) {
|
||||
// Inherited functions.
|
||||
MPMasterKey mpw_masterKey_v1(
|
||||
const char *fullName, const char *masterPassword);
|
||||
const char *mpw_sitePasswordFromTemplate_v1(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
|
||||
const char *mpw_sitePasswordFromCrypt_v1(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText);
|
||||
const char *mpw_sitePasswordFromDerive_v1(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
|
||||
const char *mpw_siteState_v1(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state);
|
||||
|
||||
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
||||
trc( "algorithm: v%d\n", 2 );
|
||||
trc( "fullName: %s (%zu)\n", fullName, mpw_utf8_strlen( fullName ) );
|
||||
trc( "masterPassword: %s\n", masterPassword );
|
||||
trc( "key scope: %s\n", mpKeyScope );
|
||||
// Algorithm version overrides.
|
||||
static MPMasterKey mpw_masterKey_v2(
|
||||
const char *fullName, const char *masterPassword) {
|
||||
|
||||
// Calculate the master key salt.
|
||||
// masterKeySalt = mpKeyScope . #fullName . fullName
|
||||
size_t masterKeySaltSize = 0;
|
||||
uint8_t *masterKeySalt = NULL;
|
||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( mpw_utf8_strlen( fullName ) ) );
|
||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
|
||||
if (!masterKeySalt) {
|
||||
ftl( "Could not allocate master key salt: %d\n", errno );
|
||||
return NULL;
|
||||
}
|
||||
trc( "masterKeySalt ID: %s\n", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
|
||||
|
||||
// Calculate the master key.
|
||||
// masterKey = scrypt( masterPassword, masterKeySalt )
|
||||
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
|
||||
mpw_free( masterKeySalt, masterKeySaltSize );
|
||||
if (!masterKey) {
|
||||
ftl( "Could not allocate master key: %d\n", errno );
|
||||
return NULL;
|
||||
}
|
||||
trc( "masterKey ID: %s\n", mpw_id_buf( masterKey, MP_dkLen ) );
|
||||
|
||||
return masterKey;
|
||||
return mpw_masterKey_v1( fullName, masterPassword );
|
||||
}
|
||||
|
||||
static const char *mpw_passwordForSite_v2(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
||||
const MPSiteVariant siteVariant, const char *siteContext) {
|
||||
static MPSiteKey mpw_siteKey_v2(
|
||||
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
|
||||
MPKeyPurpose keyPurpose, const char *keyContext) {
|
||||
|
||||
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
||||
trc( "algorithm: v%d\n", 2 );
|
||||
trc( "siteName: %s\n", siteName );
|
||||
trc( "siteCounter: %d\n", siteCounter );
|
||||
trc( "siteVariant: %d\n", siteVariant );
|
||||
trc( "siteType: %d\n", siteType );
|
||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
||||
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
||||
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
||||
mpw_hex_l( htonl( siteCounter ) ),
|
||||
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
||||
const char *keyScope = mpw_scopeForPurpose( keyPurpose );
|
||||
trc( "keyScope: %s\n", keyScope );
|
||||
|
||||
// OTP counter value.
|
||||
if (siteCounter == MPCounterValueTOTP)
|
||||
siteCounter = ((uint32_t)time( NULL ) / MP_otp_window) * MP_otp_window;
|
||||
|
||||
// Calculate the site seed.
|
||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
||||
size_t sitePasswordInfoSize = 0;
|
||||
uint8_t *sitePasswordInfo = NULL;
|
||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
|
||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
||||
if (siteContext) {
|
||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
|
||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
||||
trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s\n",
|
||||
keyScope, mpw_hex_l( (uint32_t)strlen( siteName ) ), siteName, mpw_hex_l( siteCounter ),
|
||||
keyContext? mpw_hex_l( (uint32_t)strlen( keyContext ) ): NULL, keyContext );
|
||||
size_t siteSaltSize = 0;
|
||||
uint8_t *siteSalt = NULL;
|
||||
mpw_push_string( &siteSalt, &siteSaltSize, keyScope );
|
||||
mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)strlen( siteName ) );
|
||||
mpw_push_string( &siteSalt, &siteSaltSize, siteName );
|
||||
mpw_push_int( &siteSalt, &siteSaltSize, siteCounter );
|
||||
if (keyContext) {
|
||||
mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)strlen( keyContext ) );
|
||||
mpw_push_string( &siteSalt, &siteSaltSize, keyContext );
|
||||
}
|
||||
if (!sitePasswordInfo) {
|
||||
ftl( "Could not allocate site seed info: %d\n", errno );
|
||||
if (!siteSalt) {
|
||||
err( "Could not allocate site salt: %s\n", strerror( errno ) );
|
||||
return NULL;
|
||||
}
|
||||
trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( sitePasswordInfo, sitePasswordInfoSize ) );
|
||||
trc( " => siteSalt.id: %s\n", mpw_id_buf( siteSalt, siteSaltSize ) );
|
||||
|
||||
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
|
||||
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
|
||||
if (!sitePasswordSeed) {
|
||||
ftl( "Could not allocate site seed: %d\n", errno );
|
||||
trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )\n",
|
||||
mpw_id_buf( masterKey, MPMasterKeySize ) );
|
||||
MPSiteKey siteKey = mpw_hash_hmac_sha256( masterKey, MPMasterKeySize, siteSalt, siteSaltSize );
|
||||
mpw_free( &siteSalt, siteSaltSize );
|
||||
if (!siteKey) {
|
||||
err( "Could not derive site key: %s\n", strerror( errno ) );
|
||||
return NULL;
|
||||
}
|
||||
trc( "sitePasswordSeed ID: %s\n", mpw_id_buf( sitePasswordSeed, 32 ) );
|
||||
trc( " => siteKey.id: %s\n", mpw_id_buf( siteKey, MPSiteKeySize ) );
|
||||
|
||||
// Determine the template.
|
||||
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
|
||||
trc( "type %d, template: %s\n", siteType, template );
|
||||
if (strlen( template ) > 32) {
|
||||
ftl( "Template too long for password seed: %lu", strlen( template ) );
|
||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Encode the password from the seed using the template.
|
||||
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
|
||||
for (size_t c = 0; c < strlen( template ); ++c) {
|
||||
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
|
||||
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
|
||||
sitePassword[c] );
|
||||
}
|
||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||
|
||||
return sitePassword;
|
||||
return siteKey;
|
||||
}
|
||||
|
||||
static const char *mpw_sitePasswordFromTemplate_v2(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
|
||||
|
||||
return mpw_sitePasswordFromTemplate_v1( masterKey, siteKey, resultType, resultParam );
|
||||
}
|
||||
|
||||
static const char *mpw_sitePasswordFromCrypt_v2(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText) {
|
||||
|
||||
return mpw_sitePasswordFromCrypt_v1( masterKey, siteKey, resultType, cipherText );
|
||||
}
|
||||
|
||||
static const char *mpw_sitePasswordFromDerive_v2(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
|
||||
|
||||
return mpw_sitePasswordFromDerive_v1( masterKey, siteKey, resultType, resultParam );
|
||||
}
|
||||
|
||||
static const char *mpw_siteState_v2(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state) {
|
||||
|
||||
return mpw_siteState_v1( masterKey, siteKey, resultType, state );
|
||||
}
|
||||
|
||||
@@ -18,108 +18,88 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "mpw-types.h"
|
||||
#include "mpw-util.h"
|
||||
|
||||
#define MP_N 32768
|
||||
#define MP_r 8
|
||||
#define MP_p 2
|
||||
#define MP_hash PearlHashSHA256
|
||||
#define MP_N 32768LU
|
||||
#define MP_r 8U
|
||||
#define MP_p 2U
|
||||
#define MP_otp_window 5 * 60 /* s */
|
||||
|
||||
static const uint8_t *mpw_masterKeyForUser_v3(const char *fullName, const char *masterPassword) {
|
||||
// Inherited functions.
|
||||
MPSiteKey mpw_siteKey_v2(
|
||||
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
|
||||
MPKeyPurpose keyPurpose, const char *keyContext);
|
||||
const char *mpw_sitePasswordFromTemplate_v2(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
|
||||
const char *mpw_sitePasswordFromCrypt_v2(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText);
|
||||
const char *mpw_sitePasswordFromDerive_v2(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
|
||||
const char *mpw_siteState_v2(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state);
|
||||
|
||||
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
||||
trc( "algorithm: v%d\n", 3 );
|
||||
trc( "fullName: %s (%zu)\n", fullName, strlen( fullName ) );
|
||||
trc( "masterPassword: %s\n", masterPassword );
|
||||
trc( "key scope: %s\n", mpKeyScope );
|
||||
// Algorithm version overrides.
|
||||
static MPMasterKey mpw_masterKey_v3(
|
||||
const char *fullName, const char *masterPassword) {
|
||||
|
||||
const char *keyScope = mpw_scopeForPurpose( MPKeyPurposeAuthentication );
|
||||
trc( "keyScope: %s\n", keyScope );
|
||||
|
||||
// Calculate the master key salt.
|
||||
// masterKeySalt = mpKeyScope . #fullName . fullName
|
||||
trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s\n",
|
||||
keyScope, mpw_hex_l( (uint32_t)strlen( fullName ) ), fullName );
|
||||
size_t masterKeySaltSize = 0;
|
||||
uint8_t *masterKeySalt = NULL;
|
||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, htonl( strlen( fullName ) ) );
|
||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, keyScope );
|
||||
mpw_push_int( &masterKeySalt, &masterKeySaltSize, (uint32_t)strlen( fullName ) );
|
||||
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
|
||||
if (!masterKeySalt) {
|
||||
ftl( "Could not allocate master key salt: %d\n", errno );
|
||||
err( "Could not allocate master key salt: %s\n", strerror( errno ) );
|
||||
return NULL;
|
||||
}
|
||||
trc( "masterKeySalt ID: %s\n", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
|
||||
trc( " => masterKeySalt.id: %s\n", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
|
||||
|
||||
// Calculate the master key.
|
||||
// masterKey = scrypt( masterPassword, masterKeySalt )
|
||||
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
|
||||
mpw_free( masterKeySalt, masterKeySaltSize );
|
||||
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 );
|
||||
mpw_free( &masterKeySalt, masterKeySaltSize );
|
||||
if (!masterKey) {
|
||||
ftl( "Could not allocate master key: %d\n", errno );
|
||||
err( "Could not derive master key: %s\n", strerror( errno ) );
|
||||
return NULL;
|
||||
}
|
||||
trc( "masterKey ID: %s\n", mpw_id_buf( masterKey, MP_dkLen ) );
|
||||
trc( " => masterKey.id: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) );
|
||||
|
||||
return masterKey;
|
||||
}
|
||||
|
||||
static const char *mpw_passwordForSite_v3(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
||||
const MPSiteVariant siteVariant, const char *siteContext) {
|
||||
static MPSiteKey mpw_siteKey_v3(
|
||||
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
|
||||
MPKeyPurpose keyPurpose, const char *keyContext) {
|
||||
|
||||
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
||||
trc( "algorithm: v%d\n", 3 );
|
||||
trc( "siteName: %s\n", siteName );
|
||||
trc( "siteCounter: %d\n", siteCounter );
|
||||
trc( "siteVariant: %d\n", siteVariant );
|
||||
trc( "siteType: %d\n", siteType );
|
||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
||||
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
||||
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
||||
mpw_hex_l( htonl( siteCounter ) ),
|
||||
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
||||
|
||||
// Calculate the site seed.
|
||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
||||
size_t sitePasswordInfoSize = 0;
|
||||
uint8_t *sitePasswordInfo = NULL;
|
||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
|
||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
||||
if (siteContext) {
|
||||
mpw_push_int( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
|
||||
mpw_push_string( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
||||
}
|
||||
if (!sitePasswordInfo) {
|
||||
ftl( "Could not allocate site seed info: %d\n", errno );
|
||||
return NULL;
|
||||
}
|
||||
trc( "sitePasswordInfo ID: %s\n", mpw_id_buf( sitePasswordInfo, sitePasswordInfoSize ) );
|
||||
|
||||
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
|
||||
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
|
||||
if (!sitePasswordSeed) {
|
||||
ftl( "Could not allocate site seed: %d\n", errno );
|
||||
return NULL;
|
||||
}
|
||||
trc( "sitePasswordSeed ID: %s\n", mpw_id_buf( sitePasswordSeed, 32 ) );
|
||||
|
||||
// Determine the template.
|
||||
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
|
||||
trc( "type %d, template: %s\n", siteType, template );
|
||||
if (strlen( template ) > 32) {
|
||||
ftl( "Template too long for password seed: %lu", strlen( template ) );
|
||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Encode the password from the seed using the template.
|
||||
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
|
||||
for (size_t c = 0; c < strlen( template ); ++c) {
|
||||
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
|
||||
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
|
||||
sitePassword[c] );
|
||||
}
|
||||
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||
|
||||
return sitePassword;
|
||||
return mpw_siteKey_v2( masterKey, siteName, siteCounter, keyPurpose, keyContext );
|
||||
}
|
||||
|
||||
static const char *mpw_sitePasswordFromTemplate_v3(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
|
||||
|
||||
return mpw_sitePasswordFromTemplate_v2( masterKey, siteKey, resultType, resultParam );
|
||||
}
|
||||
|
||||
static const char *mpw_sitePasswordFromCrypt_v3(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText) {
|
||||
|
||||
return mpw_sitePasswordFromCrypt_v2( masterKey, siteKey, resultType, cipherText );
|
||||
}
|
||||
|
||||
static const char *mpw_sitePasswordFromDerive_v3(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
|
||||
|
||||
return mpw_sitePasswordFromDerive_v2( masterKey, siteKey, resultType, resultParam );
|
||||
}
|
||||
|
||||
static const char *mpw_siteState_v3(
|
||||
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state) {
|
||||
|
||||
return mpw_siteState_v2( masterKey, siteKey, resultType, state );
|
||||
}
|
||||
|
||||
115
core/c/mpw-marshall-util.c
Normal file
115
core/c/mpw-marshall-util.c
Normal file
@@ -0,0 +1,115 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "mpw-marshall-util.h"
|
||||
#include "mpw-util.h"
|
||||
|
||||
char *mpw_get_token(const char **in, const char *eol, char *delim) {
|
||||
|
||||
// Skip leading spaces.
|
||||
for (; **in == ' '; ++*in);
|
||||
|
||||
// Find characters up to the first delim.
|
||||
size_t len = strcspn( *in, delim );
|
||||
char *token = len && len <= (size_t)(eol - *in)? strndup( *in, len ): NULL;
|
||||
|
||||
// Advance past the delimitor.
|
||||
*in = min( eol, *in + len + 1 );
|
||||
return token;
|
||||
}
|
||||
|
||||
time_t mpw_mktime(
|
||||
const char *time) {
|
||||
|
||||
struct tm tm = { .tm_isdst = -1, .tm_gmtoff = 0 };
|
||||
if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ",
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
|
||||
&tm.tm_hour, &tm.tm_min, &tm.tm_sec ) == 6) {
|
||||
tm.tm_year -= 1900; // tm_year 0 = rfc3339 year 1900
|
||||
tm.tm_mon -= 1; // tm_mon 0 = rfc3339 month 1
|
||||
return mktime( &tm );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if MPW_JSON
|
||||
json_object *mpw_get_json_section(
|
||||
json_object *obj, const char *section) {
|
||||
|
||||
json_object *json_value = obj;
|
||||
char *sectionTokenizer = strdup( section ), *sectionToken = sectionTokenizer;
|
||||
for (sectionToken = strtok( sectionToken, "." ); sectionToken; sectionToken = strtok( NULL, "." ))
|
||||
if (!json_object_object_get_ex( json_value, sectionToken, &json_value ) || !json_value) {
|
||||
trc( "While resolving: %s: Missing value for: %s\n", section, sectionToken );
|
||||
json_value = NULL;
|
||||
break;
|
||||
}
|
||||
free( sectionTokenizer );
|
||||
|
||||
return json_value;
|
||||
}
|
||||
|
||||
const char *mpw_get_json_string(
|
||||
json_object *obj, const char *section, const char *defaultValue) {
|
||||
|
||||
json_object *json_value = mpw_get_json_section( obj, section );
|
||||
if (!json_value)
|
||||
return defaultValue;
|
||||
|
||||
return json_object_get_string( json_value );
|
||||
}
|
||||
|
||||
int64_t mpw_get_json_int(
|
||||
json_object *obj, const char *section, int64_t defaultValue) {
|
||||
|
||||
json_object *json_value = mpw_get_json_section( obj, section );
|
||||
if (!json_value)
|
||||
return defaultValue;
|
||||
|
||||
return json_object_get_int64( json_value );
|
||||
}
|
||||
|
||||
bool mpw_get_json_boolean(
|
||||
json_object *obj, const char *section, bool defaultValue) {
|
||||
|
||||
json_object *json_value = mpw_get_json_section( obj, section );
|
||||
if (!json_value)
|
||||
return defaultValue;
|
||||
|
||||
return json_object_get_boolean( json_value ) == TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool mpw_update_masterKey(MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, MPAlgorithmVersion targetKeyAlgorithm,
|
||||
const char *fullName, const char *masterPassword) {
|
||||
|
||||
if (*masterKeyAlgorithm != targetKeyAlgorithm) {
|
||||
mpw_free( masterKey, MPMasterKeySize );
|
||||
*masterKeyAlgorithm = targetKeyAlgorithm;
|
||||
*masterKey = mpw_masterKey( fullName, masterPassword, *masterKeyAlgorithm );
|
||||
if (!*masterKey) {
|
||||
err( "Couldn't derive master key for user %s, algorithm %d.\n", fullName, *masterKeyAlgorithm );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
73
core/c/mpw-marshall-util.h
Normal file
73
core/c/mpw-marshall-util.h
Normal file
@@ -0,0 +1,73 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#ifndef _MPW_MARSHALL_UTIL_H
|
||||
#define _MPW_MARSHALL_UTIL_H
|
||||
|
||||
#include <time.h>
|
||||
#if MPW_JSON
|
||||
#include "json-c/json.h"
|
||||
#endif
|
||||
|
||||
#include "mpw-algorithm.h"
|
||||
|
||||
/// Type parsing.
|
||||
|
||||
/** Get a token from a string by searching until the first character in delim, no farther than eol.
|
||||
* 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. */
|
||||
char *mpw_get_token(
|
||||
const char **in, const char *eol, char *delim);
|
||||
/** Convert an RFC 3339 time string into epoch time. */
|
||||
time_t mpw_mktime(
|
||||
const char *time);
|
||||
|
||||
/// JSON parsing.
|
||||
|
||||
#if MPW_JSON
|
||||
/** 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.
|
||||
* @return A new JSON object or NULL if one of the section's object keys was not found in the source object's tree. */
|
||||
json_object *mpw_get_json_section(
|
||||
json_object *obj, const char *section);
|
||||
/** Search for a string in a JSON object tree.
|
||||
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
|
||||
* @return A new string or defaultValue if one of the section's object keys was not found in the source object's tree. */
|
||||
const char *mpw_get_json_string(
|
||||
json_object *obj, const char *section, const char *defaultValue);
|
||||
/** 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.
|
||||
* @return The integer value or defaultValue if one of the section's object keys was not found in the source object's tree. */
|
||||
int64_t mpw_get_json_int(
|
||||
json_object *obj, const char *section, int64_t defaultValue);
|
||||
/** 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.
|
||||
* @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(
|
||||
json_object *obj, const char *section, bool defaultValue);
|
||||
#endif
|
||||
|
||||
/// mpw.
|
||||
|
||||
/** Calculate a master key if the target master key algorithm is different from the given master key algorithm.
|
||||
* @return false if an error occurred during the derivation of the master key. */
|
||||
bool mpw_update_masterKey(
|
||||
MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, MPAlgorithmVersion targetKeyAlgorithm,
|
||||
const char *fullName, const char *masterPassword);
|
||||
|
||||
#endif // _MPW_MARSHALL_UTIL_H
|
||||
924
core/c/mpw-marshall.c
Normal file
924
core/c/mpw-marshall.c
Normal file
@@ -0,0 +1,924 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "mpw-marshall.h"
|
||||
#include "mpw-util.h"
|
||||
#include "mpw-marshall-util.h"
|
||||
|
||||
MPMarshalledUser *mpw_marshall_user(
|
||||
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) {
|
||||
|
||||
MPMarshalledUser *user;
|
||||
if (!fullName || !masterPassword || !(user = malloc( sizeof( MPMarshalledUser ) )))
|
||||
return NULL;
|
||||
|
||||
*user = (MPMarshalledUser){
|
||||
.fullName = strdup( fullName ),
|
||||
.masterPassword = strdup( masterPassword ),
|
||||
.algorithm = algorithmVersion,
|
||||
.redacted = true,
|
||||
|
||||
.avatar = 0,
|
||||
.defaultType = MPResultTypeDefault,
|
||||
.lastUsed = 0,
|
||||
|
||||
.sites_count = 0,
|
||||
.sites = NULL,
|
||||
};
|
||||
return user;
|
||||
};
|
||||
|
||||
MPMarshalledSite *mpw_marshall_site(
|
||||
MPMarshalledUser *user, const char *siteName, const MPResultType resultType,
|
||||
const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion) {
|
||||
|
||||
if (!siteName || !mpw_realloc( &user->sites, NULL, sizeof( MPMarshalledSite ) * ++user->sites_count ))
|
||||
return NULL;
|
||||
|
||||
MPMarshalledSite *site = &user->sites[user->sites_count - 1];
|
||||
*site = (MPMarshalledSite){
|
||||
.name = strdup( siteName ),
|
||||
.content = NULL,
|
||||
.type = resultType,
|
||||
.counter = siteCounter,
|
||||
.algorithm = algorithmVersion,
|
||||
|
||||
.loginContent = NULL,
|
||||
.loginType = MPResultTypeTemplateName,
|
||||
|
||||
.url = NULL,
|
||||
.uses = 0,
|
||||
.lastUsed = 0,
|
||||
|
||||
.questions_count = 0,
|
||||
.questions = NULL,
|
||||
};
|
||||
return site;
|
||||
};
|
||||
|
||||
MPMarshalledQuestion *mpw_marshal_question(
|
||||
MPMarshalledSite *site, const char *keyword) {
|
||||
|
||||
if (!mpw_realloc( &site->questions, NULL, sizeof( MPMarshalledQuestion ) * ++site->questions_count ))
|
||||
return NULL;
|
||||
if (!keyword)
|
||||
keyword = "";
|
||||
|
||||
MPMarshalledQuestion *question = &site->questions[site->questions_count - 1];
|
||||
*question = (MPMarshalledQuestion){
|
||||
.keyword = strdup( keyword ),
|
||||
.content = NULL,
|
||||
.type = MPResultTypeTemplatePhrase,
|
||||
};
|
||||
return question;
|
||||
}
|
||||
|
||||
bool mpw_marshal_info_free(
|
||||
MPMarshallInfo **info) {
|
||||
|
||||
if (!info || !*info)
|
||||
return true;
|
||||
|
||||
bool success = true;
|
||||
success &= mpw_free_strings( &(*info)->fullName, &(*info)->keyID, NULL );
|
||||
success &= mpw_free( info, sizeof( MPMarshallInfo ) );
|
||||
|
||||
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) {
|
||||
MPMarshalledQuestion *question = &site->questions[q];
|
||||
success &= mpw_free_strings( &question->keyword, &question->content, NULL );
|
||||
}
|
||||
success &= mpw_free( &site->questions, sizeof( MPMarshalledQuestion ) * site->questions_count );
|
||||
}
|
||||
|
||||
success &= mpw_free( &(*user)->sites, sizeof( MPMarshalledSite ) * (*user)->sites_count );
|
||||
success &= mpw_free( user, sizeof( MPMarshalledUser ) );
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool mpw_marshall_write_flat(
|
||||
char **out, const MPMarshalledUser *user, MPMarshallError *error) {
|
||||
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Unexpected internal error." };
|
||||
if (!user->fullName || !strlen( user->fullName )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorMissing, "Missing full name." };
|
||||
return false;
|
||||
}
|
||||
if (!user->masterPassword || !strlen( user->masterPassword )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorMasterPassword, "Missing master password." };
|
||||
return false;
|
||||
}
|
||||
MPMasterKey masterKey = NULL;
|
||||
MPAlgorithmVersion masterKeyAlgorithm = user->algorithm - 1;
|
||||
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, user->algorithm, user->fullName, user->masterPassword )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." };
|
||||
return false;
|
||||
}
|
||||
|
||||
mpw_string_pushf( out, "# Master Password site export\n" );
|
||||
if (user->redacted)
|
||||
mpw_string_pushf( out, "# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n" );
|
||||
else
|
||||
mpw_string_pushf( out, "# Export of site names and passwords in clear-text.\n" );
|
||||
mpw_string_pushf( out, "# \n" );
|
||||
mpw_string_pushf( out, "##\n" );
|
||||
mpw_string_pushf( out, "# Format: %d\n", 1 );
|
||||
|
||||
char dateString[21];
|
||||
time_t now = time( NULL );
|
||||
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &now ) ))
|
||||
mpw_string_pushf( out, "# Date: %s\n", dateString );
|
||||
mpw_string_pushf( out, "# User Name: %s\n", user->fullName );
|
||||
mpw_string_pushf( out, "# Full Name: %s\n", user->fullName );
|
||||
mpw_string_pushf( out, "# Avatar: %u\n", user->avatar );
|
||||
mpw_string_pushf( out, "# Key ID: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) );
|
||||
mpw_string_pushf( out, "# Algorithm: %d\n", user->algorithm );
|
||||
mpw_string_pushf( out, "# Default Type: %d\n", user->defaultType );
|
||||
mpw_string_pushf( out, "# Passwords: %s\n", user->redacted? "PROTECTED": "VISIBLE" );
|
||||
mpw_string_pushf( out, "##\n" );
|
||||
mpw_string_pushf( out, "#\n" );
|
||||
mpw_string_pushf( out, "# Last Times Password Login\t Site\tSite\n" );
|
||||
mpw_string_pushf( out, "# used used type name\t name\tpassword\n" );
|
||||
|
||||
// Sites.
|
||||
for (size_t s = 0; s < user->sites_count; ++s) {
|
||||
MPMarshalledSite *site = &user->sites[s];
|
||||
if (!site->name || !strlen( site->name ))
|
||||
continue;
|
||||
|
||||
const char *content = NULL, *loginContent = NULL;
|
||||
if (!user->redacted) {
|
||||
// Clear Text
|
||||
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, user->fullName, user->masterPassword )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." };
|
||||
return false;
|
||||
}
|
||||
|
||||
content = mpw_siteResult( masterKey, site->name, site->counter,
|
||||
MPKeyPurposeAuthentication, NULL, site->type, site->content, site->algorithm );
|
||||
loginContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
|
||||
MPKeyPurposeIdentification, NULL, site->loginType, site->loginContent, site->algorithm );
|
||||
}
|
||||
else {
|
||||
// Redacted
|
||||
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 ) ))
|
||||
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,
|
||||
loginContent?: "", site->name, content?: "" );
|
||||
mpw_free_strings( &content, &loginContent, NULL );
|
||||
}
|
||||
mpw_free( &masterKey, MPMasterKeySize );
|
||||
|
||||
*error = (MPMarshallError){ .type = MPMarshallSuccess };
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MPW_JSON
|
||||
static bool mpw_marshall_write_json(
|
||||
char **out, const MPMarshalledUser *user, MPMarshallError *error) {
|
||||
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Unexpected internal error." };
|
||||
if (!user->fullName || !strlen( user->fullName )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorMissing, "Missing full name." };
|
||||
return false;
|
||||
}
|
||||
if (!user->masterPassword || !strlen( user->masterPassword )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorMasterPassword, "Missing master password." };
|
||||
return false;
|
||||
}
|
||||
MPMasterKey masterKey = NULL;
|
||||
MPAlgorithmVersion masterKeyAlgorithm = user->algorithm - 1;
|
||||
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, user->algorithm, user->fullName, user->masterPassword )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." };
|
||||
return false;
|
||||
}
|
||||
|
||||
// Section: "export"
|
||||
json_object *json_file = json_object_new_object();
|
||||
json_object *json_export = json_object_new_object();
|
||||
json_object_object_add( json_file, "export", json_export );
|
||||
json_object_object_add( json_export, "format", json_object_new_int( 1 ) );
|
||||
json_object_object_add( json_export, "redacted", json_object_new_boolean( user->redacted ) );
|
||||
|
||||
char dateString[21];
|
||||
time_t now = time( NULL );
|
||||
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &now ) ))
|
||||
json_object_object_add( json_export, "date", json_object_new_string( dateString ) );
|
||||
|
||||
// Section: "user"
|
||||
json_object *json_user = json_object_new_object();
|
||||
json_object_object_add( json_file, "user", json_user );
|
||||
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 ) );
|
||||
|
||||
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, "key_id", json_object_new_string( mpw_id_buf( masterKey, MPMasterKeySize ) ) );
|
||||
|
||||
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( (int32_t)user->defaultType ) );
|
||||
|
||||
// Section "sites"
|
||||
json_object *json_sites = json_object_new_object();
|
||||
json_object_object_add( json_file, "sites", json_sites );
|
||||
for (size_t s = 0; s < user->sites_count; ++s) {
|
||||
MPMarshalledSite *site = &user->sites[s];
|
||||
if (!site->name || !strlen( site->name ))
|
||||
continue;
|
||||
|
||||
const char *content = NULL, *loginContent = NULL;
|
||||
if (!user->redacted) {
|
||||
// Clear Text
|
||||
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, user->fullName, user->masterPassword )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." };
|
||||
return false;
|
||||
}
|
||||
|
||||
content = mpw_siteResult( masterKey, site->name, site->counter,
|
||||
MPKeyPurposeAuthentication, NULL, site->type, site->content, site->algorithm );
|
||||
loginContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
|
||||
MPKeyPurposeIdentification, NULL, site->loginType, site->loginContent, site->algorithm );
|
||||
}
|
||||
else {
|
||||
// Redacted
|
||||
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_object_add( json_sites, site->name, json_site );
|
||||
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( (int32_t)site->counter ) );
|
||||
json_object_object_add( json_site, "algorithm", json_object_new_int( (int32_t)site->algorithm ) );
|
||||
if (content)
|
||||
json_object_object_add( json_site, "password", json_object_new_string( content ) );
|
||||
if (loginContent)
|
||||
json_object_object_add( json_site, "login_name", json_object_new_string( loginContent ) );
|
||||
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( (int32_t)site->uses ) );
|
||||
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 *json_site_questions = json_object_new_object();
|
||||
json_object_object_add( json_site, "questions", json_site_questions );
|
||||
for (size_t q = 0; q < site->questions_count; ++q) {
|
||||
MPMarshalledQuestion *question = &site->questions[q];
|
||||
if (!question->keyword)
|
||||
continue;
|
||||
|
||||
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_question, "type", json_object_new_int( (int32_t)question->type ) );
|
||||
|
||||
if (!user->redacted) {
|
||||
// Clear Text
|
||||
const char *answerContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
|
||||
MPKeyPurposeRecovery, question->keyword, question->type, question->content, site->algorithm );
|
||||
json_object_object_add( json_site_question, "answer", json_object_new_string( answerContent ) );
|
||||
}
|
||||
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 ) );
|
||||
}
|
||||
}
|
||||
|
||||
json_object *json_site_mpw = json_object_new_object();
|
||||
json_object_object_add( json_site, "_ext_mpw", json_site_mpw );
|
||||
if (site->url)
|
||||
json_object_object_add( json_site_mpw, "url", json_object_new_string( site->url ) );
|
||||
|
||||
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_free( &masterKey, MPMasterKeySize );
|
||||
json_object_put( json_file );
|
||||
|
||||
*error = (MPMarshallError){ .type = MPMarshallSuccess };
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool mpw_marshall_write(
|
||||
char **out, const MPMarshallFormat outFormat, const MPMarshalledUser *user, MPMarshallError *error) {
|
||||
|
||||
switch (outFormat) {
|
||||
case MPMarshallFormatNone:
|
||||
*error = (MPMarshallError){ .type = MPMarshallSuccess };
|
||||
return false;
|
||||
case MPMarshallFormatFlat:
|
||||
return mpw_marshall_write_flat( out, user, error );
|
||||
#if MPW_JSON
|
||||
case MPMarshallFormatJSON:
|
||||
return mpw_marshall_write_json( out, user, error );
|
||||
#endif
|
||||
default:
|
||||
*error = (MPMarshallError){ MPMarshallErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void mpw_marshall_read_flat_info(
|
||||
const char *in, MPMarshallInfo *info) {
|
||||
|
||||
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_marshall_read_flat(
|
||||
const char *in, const char *masterPassword, MPMarshallError *error) {
|
||||
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Unexpected internal error." };
|
||||
if (!in || !strlen( in )) {
|
||||
error->type = MPMarshallErrorStructure;
|
||||
error->description = mpw_str( "No input data." );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Parse import data.
|
||||
MPMasterKey masterKey = NULL;
|
||||
MPMarshalledUser *user = NULL;
|
||||
unsigned int format = 0, avatar = 0;
|
||||
char *fullName = NULL, *keyID = NULL;
|
||||
MPAlgorithmVersion algorithm = MPAlgorithmVersionCurrent, masterKeyAlgorithm = (MPAlgorithmVersion)-1;
|
||||
MPResultType defaultType = MPResultTypeDefault;
|
||||
bool headerStarted = false, headerEnded = false, importRedacted = 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 (headerEnded)
|
||||
// Comment after header
|
||||
continue;
|
||||
if (*positionInLine == '#') {
|
||||
// ## ends header
|
||||
headerEnded = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Header
|
||||
char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" );
|
||||
char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
|
||||
if (!headerName || !headerValue) {
|
||||
error->type = MPMarshallErrorStructure;
|
||||
error->description = mpw_str( "Invalid header: %s", strndup( positionInLine, (size_t)(endOfLine - positionInLine) ) );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strcmp( headerName, "Format" ) == 0)
|
||||
format = (unsigned int)atoi( headerValue );
|
||||
if (strcmp( headerName, "Full Name" ) == 0 || strcmp( headerName, "User Name" ) == 0)
|
||||
fullName = strdup( headerValue );
|
||||
if (strcmp( headerName, "Avatar" ) == 0)
|
||||
avatar = (unsigned int)atoi( headerValue );
|
||||
if (strcmp( headerName, "Key ID" ) == 0)
|
||||
keyID = strdup( headerValue );
|
||||
if (strcmp( headerName, "Algorithm" ) == 0) {
|
||||
int value = atoi( headerValue );
|
||||
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid user algorithm version: %s", headerValue ) };
|
||||
return NULL;
|
||||
}
|
||||
algorithm = (MPAlgorithmVersion)value;
|
||||
}
|
||||
if (strcmp( headerName, "Default Type" ) == 0) {
|
||||
int value = atoi( headerValue );
|
||||
if (!mpw_nameForType( (MPResultType)value )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid user default type: %s", headerValue ) };
|
||||
return NULL;
|
||||
}
|
||||
defaultType = (MPResultType)value;
|
||||
}
|
||||
if (strcmp( headerName, "Passwords" ) == 0)
|
||||
importRedacted = strcmp( headerValue, "VISIBLE" ) != 0;
|
||||
|
||||
mpw_free_strings( &headerName, &headerValue, NULL );
|
||||
continue;
|
||||
}
|
||||
if (!headerEnded)
|
||||
continue;
|
||||
if (!fullName) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorMissing, "Missing header: Full Name" };
|
||||
return NULL;
|
||||
}
|
||||
if (positionInLine >= endOfLine)
|
||||
continue;
|
||||
|
||||
if (!user) {
|
||||
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, algorithm, fullName, masterPassword )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." };
|
||||
return NULL;
|
||||
}
|
||||
if (keyID && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorMasterPassword, "Master password doesn't match key ID." };
|
||||
return NULL;
|
||||
}
|
||||
if (!(user = mpw_marshall_user( fullName, masterPassword, algorithm ))) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't allocate a new user." };
|
||||
return NULL;
|
||||
}
|
||||
|
||||
user->redacted = importRedacted;
|
||||
user->avatar = avatar;
|
||||
user->defaultType = defaultType;
|
||||
}
|
||||
|
||||
// Site
|
||||
char *siteLoginName = NULL, *siteName = NULL, *siteContent = NULL;
|
||||
char *str_lastUsed = NULL, *str_uses = NULL, *str_type = NULL, *str_algorithm = NULL, *str_counter = NULL;
|
||||
switch (format) {
|
||||
case 0: {
|
||||
str_lastUsed = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
|
||||
str_uses = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
|
||||
char *typeAndVersion = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
|
||||
if (typeAndVersion) {
|
||||
str_type = strdup( strtok( typeAndVersion, ":" ) );
|
||||
str_algorithm = strdup( strtok( NULL, "" ) );
|
||||
mpw_free_string( &typeAndVersion );
|
||||
}
|
||||
str_counter = strdup( "1" );
|
||||
siteLoginName = NULL;
|
||||
siteName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
|
||||
siteContent = mpw_get_token( &positionInLine, endOfLine, "\n" );
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
str_lastUsed = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
|
||||
str_uses = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
|
||||
char *typeAndVersionAndCounter = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
|
||||
if (typeAndVersionAndCounter) {
|
||||
str_type = strdup( strtok( typeAndVersionAndCounter, ":" ) );
|
||||
str_algorithm = strdup( strtok( NULL, ":" ) );
|
||||
str_counter = strdup( strtok( NULL, "" ) );
|
||||
mpw_free_string( &typeAndVersionAndCounter );
|
||||
}
|
||||
siteLoginName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
|
||||
siteName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
|
||||
siteContent = mpw_get_token( &positionInLine, endOfLine, "\n" );
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
*error = (MPMarshallError){ MPMarshallErrorFormat, mpw_str( "Unexpected import format: %u", format ) };
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (siteName && str_type && str_counter && str_algorithm && str_uses && str_lastUsed) {
|
||||
MPResultType siteType = (MPResultType)atoi( str_type );
|
||||
if (!mpw_nameForType( siteType )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site type: %s: %s", siteName, str_type ) };
|
||||
return NULL;
|
||||
}
|
||||
long long int value = atoll( str_counter );
|
||||
if (value < MPCounterValueFirst || value > MPCounterValueLast) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site counter: %s: %s", siteName, str_counter ) };
|
||||
return NULL;
|
||||
}
|
||||
MPCounterValue siteCounter = (MPCounterValue)value;
|
||||
value = atoll( str_algorithm );
|
||||
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site algorithm: %s: %s", siteName, str_algorithm ) };
|
||||
return NULL;
|
||||
}
|
||||
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
|
||||
time_t siteLastUsed = mpw_mktime( str_lastUsed );
|
||||
if (!siteLastUsed) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) };
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MPMarshalledSite *site = mpw_marshall_site(
|
||||
user, siteName, siteType, siteCounter, siteAlgorithm );
|
||||
if (!site) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't allocate a new site." };
|
||||
return NULL;
|
||||
}
|
||||
|
||||
site->uses = (unsigned int)atoi( str_uses );
|
||||
site->lastUsed = siteLastUsed;
|
||||
if (!user->redacted) {
|
||||
// Clear Text
|
||||
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, fullName, masterPassword )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." };
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (siteContent && strlen( siteContent ))
|
||||
site->content = mpw_siteState( masterKey, site->name, site->counter,
|
||||
MPKeyPurposeAuthentication, NULL, site->type, siteContent, site->algorithm );
|
||||
if (siteLoginName && strlen( siteLoginName ))
|
||||
site->loginContent = mpw_siteState( masterKey, site->name, MPCounterValueInitial,
|
||||
MPKeyPurposeIdentification, NULL, site->loginType, siteLoginName, site->algorithm );
|
||||
}
|
||||
else {
|
||||
// Redacted
|
||||
if (siteContent && strlen( siteContent ))
|
||||
site->content = strdup( siteContent );
|
||||
if (siteLoginName && strlen( siteLoginName ))
|
||||
site->loginContent = strdup( siteLoginName );
|
||||
}
|
||||
}
|
||||
else {
|
||||
error->type = MPMarshallErrorMissing;
|
||||
error->description = mpw_str(
|
||||
"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 );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mpw_free_strings( &str_lastUsed, &str_uses, &str_type, &str_algorithm, &str_counter, NULL );
|
||||
mpw_free_strings( &siteLoginName, &siteName, &siteContent, NULL );
|
||||
}
|
||||
mpw_free_strings( &fullName, &keyID, NULL );
|
||||
mpw_free( &masterKey, MPMasterKeySize );
|
||||
|
||||
*error = (MPMarshallError){ .type = MPMarshallSuccess };
|
||||
return user;
|
||||
}
|
||||
|
||||
#if MPW_JSON
|
||||
static void mpw_marshall_read_json_info(
|
||||
const char *in, MPMarshallInfo *info) {
|
||||
|
||||
// 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_marshall_read_json(
|
||||
const char *in, const char *masterPassword, MPMarshallError *error) {
|
||||
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Unexpected internal error." };
|
||||
if (!in || !strlen( in )) {
|
||||
error->type = MPMarshallErrorStructure;
|
||||
error->description = mpw_str( "No input data." );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorStructure, mpw_str( "JSON error: %s", json_tokener_error_desc( json_error ) ) };
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Parse import data.
|
||||
MPMasterKey masterKey = NULL;
|
||||
MPAlgorithmVersion masterKeyAlgorithm = (MPAlgorithmVersion)-1;
|
||||
MPMarshalledUser *user = NULL;
|
||||
|
||||
// Section: "export"
|
||||
int64_t fileFormat = mpw_get_json_int( json_file, "export.format", 0 );
|
||||
if (fileFormat < 1) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorFormat, mpw_str( "Unsupported format: %u", fileFormat ) };
|
||||
return NULL;
|
||||
}
|
||||
bool fileRedacted = mpw_get_json_boolean( json_file, "export.redacted", true );
|
||||
|
||||
// Section: "user"
|
||||
unsigned int avatar = (unsigned int)mpw_get_json_int( json_file, "user.avatar", 0 );
|
||||
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 *keyID = mpw_get_json_string( json_file, "user.key_id", NULL );
|
||||
int64_t value = mpw_get_json_int( json_file, "user.algorithm", MPAlgorithmVersionCurrent );
|
||||
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid user algorithm version: %u", value ) };
|
||||
return NULL;
|
||||
}
|
||||
MPAlgorithmVersion algorithm = (MPAlgorithmVersion)value;
|
||||
MPResultType defaultType = (MPResultType)mpw_get_json_int( json_file, "user.default_type", MPResultTypeDefault );
|
||||
if (!mpw_nameForType( defaultType )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid user default type: %u", defaultType ) };
|
||||
return NULL;
|
||||
}
|
||||
time_t lastUsed = mpw_mktime( str_lastUsed );
|
||||
if (!lastUsed) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid user last used: %s", str_lastUsed ) };
|
||||
return NULL;
|
||||
}
|
||||
if (!fullName || !strlen( fullName )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorMissing, "Missing value for full name." };
|
||||
return NULL;
|
||||
}
|
||||
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, algorithm, fullName, masterPassword )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." };
|
||||
return NULL;
|
||||
}
|
||||
if (keyID && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorMasterPassword, "Master password doesn't match key ID." };
|
||||
return NULL;
|
||||
}
|
||||
if (!(user = mpw_marshall_user( fullName, masterPassword, algorithm ))) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't allocate a new user." };
|
||||
return NULL;
|
||||
}
|
||||
user->redacted = fileRedacted;
|
||||
user->avatar = avatar;
|
||||
user->defaultType = defaultType;
|
||||
user->lastUsed = lastUsed;
|
||||
|
||||
// Section "sites"
|
||||
json_object_iter json_site;
|
||||
json_object *json_sites = mpw_get_json_section( json_file, "sites" );
|
||||
json_object_object_foreachC( json_sites, json_site ) {
|
||||
const char *siteName = json_site.key;
|
||||
value = mpw_get_json_int( json_site.val, "algorithm", (int32_t)user->algorithm );
|
||||
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site algorithm version: %s: %d", siteName, value ) };
|
||||
return NULL;
|
||||
}
|
||||
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
|
||||
MPResultType siteType = (MPResultType)mpw_get_json_int( json_site.val, "type", (int32_t)user->defaultType );
|
||||
if (!mpw_nameForType( siteType )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site type: %s: %u", siteName, siteType ) };
|
||||
return NULL;
|
||||
}
|
||||
value = mpw_get_json_int( json_site.val, "counter", 1 );
|
||||
if (value < MPCounterValueFirst || value > MPCounterValueLast) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site counter: %s: %d", siteName, value ) };
|
||||
return NULL;
|
||||
}
|
||||
MPCounterValue siteCounter = (MPCounterValue)value;
|
||||
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 );
|
||||
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 );
|
||||
str_lastUsed = mpw_get_json_string( json_site.val, "last_used", NULL );
|
||||
time_t siteLastUsed = mpw_mktime( str_lastUsed );
|
||||
if (!siteLastUsed) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) };
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
MPMarshalledSite *site = mpw_marshall_site( user, siteName, siteType, siteCounter, siteAlgorithm );
|
||||
if (!site) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't allocate a new site." };
|
||||
return NULL;
|
||||
}
|
||||
|
||||
site->loginType = siteLoginType;
|
||||
site->url = siteURL? strdup( siteURL ): NULL;
|
||||
site->uses = siteUses;
|
||||
site->lastUsed = siteLastUsed;
|
||||
if (!user->redacted) {
|
||||
// Clear Text
|
||||
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, fullName, masterPassword )) {
|
||||
*error = (MPMarshallError){ MPMarshallErrorInternal, "Couldn't derive master key." };
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (siteContent && strlen( siteContent ))
|
||||
site->content = mpw_siteState( masterKey, site->name, site->counter,
|
||||
MPKeyPurposeAuthentication, NULL, site->type, siteContent, site->algorithm );
|
||||
if (siteLoginName && strlen( siteLoginName ))
|
||||
site->loginContent = mpw_siteState( masterKey, site->name, MPCounterValueInitial,
|
||||
MPKeyPurposeIdentification, NULL, site->loginType, siteLoginName, site->algorithm );
|
||||
}
|
||||
else {
|
||||
// Redacted
|
||||
if (siteContent && strlen( siteContent ))
|
||||
site->content = strdup( siteContent );
|
||||
if (siteLoginName && strlen( siteLoginName ))
|
||||
site->loginContent = strdup( siteLoginName );
|
||||
}
|
||||
|
||||
json_object_iter json_site_question;
|
||||
json_object *json_site_questions = mpw_get_json_section( json_site.val, "questions" );
|
||||
json_object_object_foreachC( json_site_questions, json_site_question ) {
|
||||
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 );
|
||||
|
||||
*error = (MPMarshallError){ .type = MPMarshallSuccess };
|
||||
return user;
|
||||
}
|
||||
#endif
|
||||
|
||||
MPMarshallInfo *mpw_marshall_read_info(
|
||||
const char *in) {
|
||||
|
||||
MPMarshallInfo *info = malloc( sizeof( MPMarshallInfo ) );
|
||||
*info = (MPMarshallInfo){ .format = MPMarshallFormatNone };
|
||||
|
||||
if (in && strlen( in )) {
|
||||
if (in[0] == '#') {
|
||||
*info = (MPMarshallInfo){ .format = MPMarshallFormatFlat };
|
||||
mpw_marshall_read_flat_info( in, info );
|
||||
}
|
||||
else if (in[0] == '{') {
|
||||
*info = (MPMarshallInfo){ .format = MPMarshallFormatJSON };
|
||||
#if MPW_JSON
|
||||
mpw_marshall_read_json_info( in, info );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
MPMarshalledUser *mpw_marshall_read(
|
||||
const char *in, const MPMarshallFormat inFormat, const char *masterPassword, MPMarshallError *error) {
|
||||
|
||||
switch (inFormat) {
|
||||
case MPMarshallFormatNone:
|
||||
*error = (MPMarshallError){ .type = MPMarshallSuccess };
|
||||
return false;
|
||||
case MPMarshallFormatFlat:
|
||||
return mpw_marshall_read_flat( in, masterPassword, error );
|
||||
#if MPW_JSON
|
||||
case MPMarshallFormatJSON:
|
||||
return mpw_marshall_read_json( in, masterPassword, error );
|
||||
#endif
|
||||
default:
|
||||
*error = (MPMarshallError){ MPMarshallErrorFormat, mpw_str( "Unsupported input format: %u", inFormat ) };
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const MPMarshallFormat mpw_formatWithName(
|
||||
const char *formatName) {
|
||||
|
||||
if (!formatName || !strlen( formatName ))
|
||||
return MPMarshallFormatNone;
|
||||
|
||||
// Lower-case to standardize it.
|
||||
size_t stdFormatNameSize = strlen( formatName );
|
||||
char stdFormatName[stdFormatNameSize + 1];
|
||||
for (size_t c = 0; c < stdFormatNameSize; ++c)
|
||||
stdFormatName[c] = (char)tolower( formatName[c] );
|
||||
stdFormatName[stdFormatNameSize] = '\0';
|
||||
|
||||
if (strncmp( mpw_nameForFormat( MPMarshallFormatNone ), stdFormatName, strlen( stdFormatName ) ) == 0)
|
||||
return MPMarshallFormatNone;
|
||||
if (strncmp( mpw_nameForFormat( MPMarshallFormatFlat ), stdFormatName, strlen( stdFormatName ) ) == 0)
|
||||
return MPMarshallFormatFlat;
|
||||
if (strncmp( mpw_nameForFormat( MPMarshallFormatJSON ), stdFormatName, strlen( stdFormatName ) ) == 0)
|
||||
return MPMarshallFormatJSON;
|
||||
|
||||
dbg( "Not a format name: %s\n", stdFormatName );
|
||||
return (MPMarshallFormat)ERR;
|
||||
}
|
||||
|
||||
const char *mpw_nameForFormat(
|
||||
const MPMarshallFormat format) {
|
||||
|
||||
switch (format) {
|
||||
case MPMarshallFormatNone:
|
||||
return "none";
|
||||
case MPMarshallFormatFlat:
|
||||
return "flat";
|
||||
case MPMarshallFormatJSON:
|
||||
return "json";
|
||||
default: {
|
||||
dbg( "Unknown format: %d\n", format );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *mpw_marshall_format_extension(
|
||||
const MPMarshallFormat format) {
|
||||
|
||||
switch (format) {
|
||||
case MPMarshallFormatNone:
|
||||
return NULL;
|
||||
case MPMarshallFormatFlat:
|
||||
return "mpsites";
|
||||
case MPMarshallFormatJSON:
|
||||
return "mpsites.json";
|
||||
default: {
|
||||
dbg( "Unknown format: %d\n", format );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
159
core/c/mpw-marshall.h
Normal file
159
core/c/mpw-marshall.h
Normal file
@@ -0,0 +1,159 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// Master Password is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Master Password is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You can find a copy of the GNU General Public License in the
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#ifndef _MPW_MARSHALL_H
|
||||
#define _MPW_MARSHALL_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "mpw-algorithm.h"
|
||||
|
||||
//// Types.
|
||||
|
||||
typedef mpw_enum( unsigned int, MPMarshallFormat ) {
|
||||
/** Generate a key for authentication. */
|
||||
MPMarshallFormatNone,
|
||||
/** Generate a key for authentication. */
|
||||
MPMarshallFormatFlat,
|
||||
/** Generate a name for identification. */
|
||||
MPMarshallFormatJSON,
|
||||
|
||||
#if MPW_JSON
|
||||
MPMarshallFormatDefault = MPMarshallFormatJSON,
|
||||
#else
|
||||
MPMarshallFormatDefault = MPMarshallFormatFlat,
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef mpw_enum( unsigned int, MPMarshallErrorType ) {
|
||||
/** The marshalling operation completed successfully. */
|
||||
MPMarshallSuccess,
|
||||
/** An error in the structure of the marshall file interrupted marshalling. */
|
||||
MPMarshallErrorStructure,
|
||||
/** The marshall file uses an unsupported format version. */
|
||||
MPMarshallErrorFormat,
|
||||
/** A required value is missing or not specified. */
|
||||
MPMarshallErrorMissing,
|
||||
/** The given master password is not valid. */
|
||||
MPMarshallErrorMasterPassword,
|
||||
/** An illegal value was specified. */
|
||||
MPMarshallErrorIllegal,
|
||||
/** An internal system error interrupted marshalling. */
|
||||
MPMarshallErrorInternal,
|
||||
};
|
||||
typedef struct MPMarshallError {
|
||||
MPMarshallErrorType type;
|
||||
const char *description;
|
||||
} MPMarshallError;
|
||||
|
||||
typedef struct MPMarshalledQuestion {
|
||||
const char *keyword;
|
||||
const char *content;
|
||||
MPResultType type;
|
||||
} MPMarshalledQuestion;
|
||||
|
||||
typedef struct MPMarshalledSite {
|
||||
const char *name;
|
||||
const char *content;
|
||||
MPResultType type;
|
||||
MPCounterValue counter;
|
||||
MPAlgorithmVersion algorithm;
|
||||
|
||||
const char *loginContent;
|
||||
MPResultType loginType;
|
||||
|
||||
const char *url;
|
||||
unsigned int uses;
|
||||
time_t lastUsed;
|
||||
|
||||
size_t questions_count;
|
||||
MPMarshalledQuestion *questions;
|
||||
} MPMarshalledSite;
|
||||
|
||||
typedef struct MPMarshalledUser {
|
||||
const char *fullName;
|
||||
const char *masterPassword;
|
||||
MPAlgorithmVersion algorithm;
|
||||
bool redacted;
|
||||
|
||||
unsigned int avatar;
|
||||
MPResultType defaultType;
|
||||
time_t lastUsed;
|
||||
|
||||
size_t sites_count;
|
||||
MPMarshalledSite *sites;
|
||||
} MPMarshalledUser;
|
||||
|
||||
typedef struct MPMarshallInfo {
|
||||
MPMarshallFormat format;
|
||||
MPAlgorithmVersion algorithm;
|
||||
const char *fullName;
|
||||
const char *keyID;
|
||||
bool redacted;
|
||||
time_t date;
|
||||
} MPMarshallInfo;
|
||||
|
||||
//// Marshalling.
|
||||
|
||||
/** Write the user and all associated data out to the given output buffer using the given marshalling format. */
|
||||
bool mpw_marshall_write(
|
||||
char **out, const MPMarshallFormat outFormat, const MPMarshalledUser *user, MPMarshallError *error);
|
||||
/** Try to read metadata on the sites in the input buffer. */
|
||||
MPMarshallInfo *mpw_marshall_read_info(
|
||||
const char *in);
|
||||
/** Unmarshall sites in the given input buffer by parsing it using the given marshalling format. */
|
||||
MPMarshalledUser *mpw_marshall_read(
|
||||
const char *in, const MPMarshallFormat inFormat, const char *masterPassword, MPMarshallError *error);
|
||||
|
||||
//// Utilities.
|
||||
|
||||
/** Create a new user object ready for marshalling. */
|
||||
MPMarshalledUser *mpw_marshall_user(
|
||||
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
|
||||
/** Create a new site attached to the given user object, ready for marshalling. */
|
||||
MPMarshalledSite *mpw_marshall_site(
|
||||
MPMarshalledUser *user,
|
||||
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(
|
||||
MPMarshalledSite *site, const char *keyword);
|
||||
/** Free the given user object and all associated data. */
|
||||
bool mpw_marshal_info_free(
|
||||
MPMarshallInfo **info);
|
||||
bool mpw_marshal_free(
|
||||
MPMarshalledUser **user);
|
||||
|
||||
//// Format.
|
||||
|
||||
/**
|
||||
* @return The purpose represented by the given name.
|
||||
*/
|
||||
const MPMarshallFormat mpw_formatWithName(
|
||||
const char *formatName);
|
||||
/**
|
||||
* @return The standard name for the given purpose.
|
||||
*/
|
||||
const char *mpw_nameForFormat(
|
||||
const MPMarshallFormat format);
|
||||
/**
|
||||
* @return The file extension that's recommended for files that use the given marshalling format.
|
||||
*/
|
||||
const char *mpw_marshall_format_extension(
|
||||
const MPMarshallFormat format);
|
||||
|
||||
#endif // _MPW_MARSHALL_H
|
||||
@@ -16,68 +16,121 @@
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef COLOR
|
||||
#include <curses.h>
|
||||
#include <term.h>
|
||||
#endif
|
||||
|
||||
#include "mpw-types.h"
|
||||
#include "mpw-util.h"
|
||||
|
||||
const MPSiteType mpw_typeWithName(const char *typeName) {
|
||||
const MPResultType mpw_typeWithName(const char *typeName) {
|
||||
|
||||
// Find what password type is represented by the type letter.
|
||||
if (strlen( typeName ) == 1) {
|
||||
if ('x' == typeName[0])
|
||||
return MPResultTypeTemplateMaximum;
|
||||
if ('l' == typeName[0])
|
||||
return MPResultTypeTemplateLong;
|
||||
if ('m' == typeName[0])
|
||||
return MPResultTypeTemplateMedium;
|
||||
if ('b' == typeName[0])
|
||||
return MPResultTypeTemplateBasic;
|
||||
if ('s' == typeName[0])
|
||||
return MPResultTypeTemplateShort;
|
||||
if ('i' == typeName[0])
|
||||
return MPResultTypeTemplatePIN;
|
||||
if ('n' == typeName[0])
|
||||
return MPResultTypeTemplateName;
|
||||
if ('P' == typeName[0])
|
||||
return MPResultTypeStatefulPersonal;
|
||||
if ('D' == typeName[0])
|
||||
return MPResultTypeStatefulDevice;
|
||||
if ('k' == typeName[0])
|
||||
return MPResultTypeDeriveKey;
|
||||
}
|
||||
|
||||
// Lower-case and trim optionally leading "Generated" string from typeName to standardize it.
|
||||
size_t stdTypeNameOffset = 0;
|
||||
size_t stdTypeNameSize = strlen( typeName );
|
||||
if (strstr(typeName, "Generated" ) == typeName)
|
||||
if (strstr( typeName, "Generated" ) == typeName)
|
||||
stdTypeNameSize -= (stdTypeNameOffset = strlen( "Generated" ));
|
||||
char stdTypeName[stdTypeNameSize + 1];
|
||||
for (size_t c = 0; c < stdTypeNameSize; ++c)
|
||||
stdTypeName[c] = (char)tolower( typeName[c + stdTypeNameOffset] );
|
||||
stdTypeName[stdTypeNameSize] = '\0';
|
||||
|
||||
// Find what site type is represented by the type name.
|
||||
if (0 == strcmp( stdTypeName, "x" ) || 0 == strcmp( stdTypeName, "max" ) || 0 == strcmp( stdTypeName, "maximum" ))
|
||||
return MPSiteTypeGeneratedMaximum;
|
||||
if (0 == strcmp( stdTypeName, "l" ) || 0 == strcmp( stdTypeName, "long" ))
|
||||
return MPSiteTypeGeneratedLong;
|
||||
if (0 == strcmp( stdTypeName, "m" ) || 0 == strcmp( stdTypeName, "med" ) || 0 == strcmp( stdTypeName, "medium" ))
|
||||
return MPSiteTypeGeneratedMedium;
|
||||
if (0 == strcmp( stdTypeName, "b" ) || 0 == strcmp( stdTypeName, "basic" ))
|
||||
return MPSiteTypeGeneratedBasic;
|
||||
if (0 == strcmp( stdTypeName, "s" ) || 0 == strcmp( stdTypeName, "short" ))
|
||||
return MPSiteTypeGeneratedShort;
|
||||
if (0 == strcmp( stdTypeName, "i" ) || 0 == strcmp( stdTypeName, "pin" ))
|
||||
return MPSiteTypeGeneratedPIN;
|
||||
if (0 == strcmp( stdTypeName, "n" ) || 0 == strcmp( stdTypeName, "name" ))
|
||||
return MPSiteTypeGeneratedName;
|
||||
if (0 == strcmp( stdTypeName, "p" ) || 0 == strcmp( stdTypeName, "phrase" ))
|
||||
return MPSiteTypeGeneratedPhrase;
|
||||
// Find what password type is represented by the type name.
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplateMaximum ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
return MPResultTypeTemplateMaximum;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplateLong ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
return MPResultTypeTemplateLong;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplateMedium ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
return MPResultTypeTemplateMedium;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplateBasic ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
return MPResultTypeTemplateBasic;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplateShort ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
return MPResultTypeTemplateShort;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplatePIN ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
return MPResultTypeTemplatePIN;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplateName ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
return MPResultTypeTemplateName;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeTemplatePhrase ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
return MPResultTypeTemplatePhrase;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeStatefulPersonal ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
return MPResultTypeStatefulPersonal;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeStatefulDevice ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
return MPResultTypeStatefulDevice;
|
||||
if (strncmp( mpw_nameForType( MPResultTypeDeriveKey ), stdTypeName, strlen( stdTypeName ) ) == 0)
|
||||
return MPResultTypeDeriveKey;
|
||||
|
||||
ftl( "Not a generated type name: %s", stdTypeName );
|
||||
return MPSiteTypeGeneratedLong;
|
||||
dbg( "Not a generated type name: %s\n", stdTypeName );
|
||||
return (MPResultType)ERR;
|
||||
}
|
||||
|
||||
const char **mpw_templatesForType(MPSiteType type, size_t *count) {
|
||||
const char *mpw_nameForType(MPResultType resultType) {
|
||||
|
||||
if (!(type & MPSiteTypeClassGenerated)) {
|
||||
ftl( "Not a generated type: %d", type );
|
||||
*count = 0;
|
||||
switch (resultType) {
|
||||
case MPResultTypeTemplateMaximum:
|
||||
return "maximum";
|
||||
case MPResultTypeTemplateLong:
|
||||
return "long";
|
||||
case MPResultTypeTemplateMedium:
|
||||
return "medium";
|
||||
case MPResultTypeTemplateBasic:
|
||||
return "basic";
|
||||
case MPResultTypeTemplateShort:
|
||||
return "short";
|
||||
case MPResultTypeTemplatePIN:
|
||||
return "pin";
|
||||
case MPResultTypeTemplateName:
|
||||
return "name";
|
||||
case MPResultTypeTemplatePhrase:
|
||||
return "phrase";
|
||||
case MPResultTypeStatefulPersonal:
|
||||
return "personal";
|
||||
case MPResultTypeStatefulDevice:
|
||||
return "device";
|
||||
case MPResultTypeDeriveKey:
|
||||
return "key";
|
||||
default: {
|
||||
dbg( "Unknown password type: %d\n", resultType );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char **mpw_templatesForType(MPResultType type, size_t *count) {
|
||||
|
||||
if (!(type & MPResultTypeClassTemplate)) {
|
||||
dbg( "Not a generated type: %d\n", type );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedMaximum: {
|
||||
return mpw_alloc_array( *count, const char *,
|
||||
case MPResultTypeTemplateMaximum:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" );
|
||||
}
|
||||
case MPSiteTypeGeneratedLong: {
|
||||
return mpw_alloc_array( *count, const char *,
|
||||
case MPResultTypeTemplateLong:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
|
||||
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
|
||||
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
|
||||
@@ -85,83 +138,89 @@ const char **mpw_templatesForType(MPSiteType type, size_t *count) {
|
||||
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
|
||||
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
|
||||
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" );
|
||||
}
|
||||
case MPSiteTypeGeneratedMedium: {
|
||||
return mpw_alloc_array( *count, const char *,
|
||||
case MPResultTypeTemplateMedium:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"CvcnoCvc", "CvcCvcno" );
|
||||
}
|
||||
case MPSiteTypeGeneratedBasic: {
|
||||
return mpw_alloc_array( *count, const char *,
|
||||
case MPResultTypeTemplateBasic:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"aaanaaan", "aannaaan", "aaannaaa" );
|
||||
}
|
||||
case MPSiteTypeGeneratedShort: {
|
||||
return mpw_alloc_array( *count, const char *,
|
||||
case MPResultTypeTemplateShort:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"Cvcn" );
|
||||
}
|
||||
case MPSiteTypeGeneratedPIN: {
|
||||
return mpw_alloc_array( *count, const char *,
|
||||
case MPResultTypeTemplatePIN:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"nnnn" );
|
||||
}
|
||||
case MPSiteTypeGeneratedName: {
|
||||
return mpw_alloc_array( *count, const char *,
|
||||
case MPResultTypeTemplateName:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"cvccvcvcv" );
|
||||
}
|
||||
case MPSiteTypeGeneratedPhrase: {
|
||||
return mpw_alloc_array( *count, const char *,
|
||||
case MPResultTypeTemplatePhrase:
|
||||
return mpw_alloc_array( count, const char *,
|
||||
"cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" );
|
||||
}
|
||||
default: {
|
||||
ftl( "Unknown generated type: %d", type );
|
||||
*count = 0;
|
||||
dbg( "Unknown generated type: %d\n", type );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) {
|
||||
const char *mpw_templateForType(MPResultType type, uint8_t templateIndex) {
|
||||
|
||||
size_t count = 0;
|
||||
const char **templates = mpw_templatesForType( type, &count );
|
||||
char const *template = count? templates[seedByte % count]: NULL;
|
||||
char const *template = templates && count? templates[templateIndex % count]: NULL;
|
||||
free( templates );
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
const MPSiteVariant mpw_variantWithName(const char *variantName) {
|
||||
const MPKeyPurpose mpw_purposeWithName(const char *purposeName) {
|
||||
|
||||
// Lower-case and trim optionally leading "generated" string from typeName to standardize it.
|
||||
size_t stdVariantNameSize = strlen( variantName );
|
||||
char stdVariantName[stdVariantNameSize + 1];
|
||||
for (size_t c = 0; c < stdVariantNameSize; ++c)
|
||||
stdVariantName[c] = (char)tolower( variantName[c] );
|
||||
stdVariantName[stdVariantNameSize] = '\0';
|
||||
size_t stdPurposeNameSize = strlen( purposeName );
|
||||
char stdPurposeName[stdPurposeNameSize + 1];
|
||||
for (size_t c = 0; c < stdPurposeNameSize; ++c)
|
||||
stdPurposeName[c] = (char)tolower( purposeName[c] );
|
||||
stdPurposeName[stdPurposeNameSize] = '\0';
|
||||
|
||||
if (0 == strcmp( stdVariantName, "p" ) || 0 == strcmp( stdVariantName, "password" ))
|
||||
return MPSiteVariantPassword;
|
||||
if (0 == strcmp( stdVariantName, "l" ) || 0 == strcmp( stdVariantName, "login" ))
|
||||
return MPSiteVariantLogin;
|
||||
if (0 == strcmp( stdVariantName, "a" ) || 0 == strcmp( stdVariantName, "answer" ))
|
||||
return MPSiteVariantAnswer;
|
||||
if (strncmp( mpw_nameForPurpose( MPKeyPurposeAuthentication ), stdPurposeName, strlen( stdPurposeName ) ) == 0)
|
||||
return MPKeyPurposeAuthentication;
|
||||
if (strncmp( mpw_nameForPurpose( MPKeyPurposeIdentification ), stdPurposeName, strlen( stdPurposeName ) ) == 0)
|
||||
return MPKeyPurposeIdentification;
|
||||
if (strncmp( mpw_nameForPurpose( MPKeyPurposeRecovery ), stdPurposeName, strlen( stdPurposeName ) ) == 0)
|
||||
return MPKeyPurposeRecovery;
|
||||
|
||||
fprintf( stderr, "Not a variant name: %s", stdVariantName );
|
||||
abort();
|
||||
dbg( "Not a purpose name: %s\n", stdPurposeName );
|
||||
return (MPKeyPurpose)ERR;
|
||||
}
|
||||
|
||||
const char *mpw_scopeForVariant(MPSiteVariant variant) {
|
||||
const char *mpw_nameForPurpose(MPKeyPurpose purpose) {
|
||||
|
||||
switch (variant) {
|
||||
case MPSiteVariantPassword: {
|
||||
return "com.lyndir.masterpassword";
|
||||
}
|
||||
case MPSiteVariantLogin: {
|
||||
return "com.lyndir.masterpassword.login";
|
||||
}
|
||||
case MPSiteVariantAnswer: {
|
||||
return "com.lyndir.masterpassword.answer";
|
||||
}
|
||||
switch (purpose) {
|
||||
case MPKeyPurposeAuthentication:
|
||||
return "authentication";
|
||||
case MPKeyPurposeIdentification:
|
||||
return "identification";
|
||||
case MPKeyPurposeRecovery:
|
||||
return "recovery";
|
||||
default: {
|
||||
fprintf( stderr, "Unknown variant: %d", variant );
|
||||
abort();
|
||||
dbg( "Unknown purpose: %d\n", purpose );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *mpw_scopeForPurpose(MPKeyPurpose purpose) {
|
||||
|
||||
switch (purpose) {
|
||||
case MPKeyPurposeAuthentication:
|
||||
return "com.lyndir.masterpassword";
|
||||
case MPKeyPurposeIdentification:
|
||||
return "com.lyndir.masterpassword.login";
|
||||
case MPKeyPurposeRecovery:
|
||||
return "com.lyndir.masterpassword.answer";
|
||||
default: {
|
||||
dbg( "Unknown purpose: %d\n", purpose );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,8 +249,8 @@ const char *mpw_charactersInClass(char characterClass) {
|
||||
case ' ':
|
||||
return " ";
|
||||
default: {
|
||||
fprintf( stderr, "Unknown character class: %c", characterClass );
|
||||
abort();
|
||||
dbg( "Unknown character class: %c\n", characterClass );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,5 +258,8 @@ const char *mpw_charactersInClass(char characterClass) {
|
||||
const char mpw_characterFromClass(char characterClass, uint8_t seedByte) {
|
||||
|
||||
const char *classCharacters = mpw_charactersInClass( characterClass );
|
||||
if (!classCharacters)
|
||||
return '\0';
|
||||
|
||||
return classCharacters[seedByte % strlen( classCharacters )];
|
||||
}
|
||||
|
||||
@@ -18,71 +18,122 @@
|
||||
|
||||
#ifndef _MPW_TYPES_H
|
||||
#define _MPW_TYPES_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef NS_ENUM
|
||||
#define enum(_type, _name) NS_ENUM(_type, _name)
|
||||
#define mpw_enum(_type, _name) NS_ENUM(_type, _name)
|
||||
#else
|
||||
#define enum(_type, _name) _type _name; enum
|
||||
#define mpw_enum(_type, _name) _type _name; enum
|
||||
#endif
|
||||
|
||||
#define MP_dkLen 64
|
||||
#ifndef __unused
|
||||
#define __unused __attribute__((unused))
|
||||
#endif
|
||||
|
||||
//// Types.
|
||||
|
||||
typedef enum( unsigned int, MPSiteVariant ) {
|
||||
#define MPMasterKeySize 64 /* bytes */
|
||||
typedef const uint8_t *MPMasterKey;
|
||||
#define MPSiteKeySize (256 / 8) /* bytes */ // Size of HMAC-SHA-256
|
||||
typedef const uint8_t *MPSiteKey;
|
||||
typedef const char *MPKeyID;
|
||||
|
||||
typedef mpw_enum( uint8_t, MPKeyPurpose ) {
|
||||
/** Generate a key for authentication. */
|
||||
MPSiteVariantPassword,
|
||||
MPKeyPurposeAuthentication,
|
||||
/** Generate a name for identification. */
|
||||
MPSiteVariantLogin,
|
||||
/** Generate an answer to a security question. */
|
||||
MPSiteVariantAnswer,
|
||||
MPKeyPurposeIdentification,
|
||||
/** Generate a recovery token. */
|
||||
MPKeyPurposeRecovery,
|
||||
};
|
||||
|
||||
typedef enum( unsigned int, MPSiteTypeClass ) {
|
||||
/** Generate the password. */
|
||||
MPSiteTypeClassGenerated = 1 << 4,
|
||||
/** Store the password. */
|
||||
MPSiteTypeClassStored = 1 << 5,
|
||||
// bit 4 - 9
|
||||
typedef mpw_enum( uint16_t, MPResultTypeClass ) {
|
||||
/** Use the site key to generate a password from a template. */
|
||||
MPResultTypeClassTemplate = 1 << 4,
|
||||
/** Use the site key to encrypt and decrypt a stateful entity. */
|
||||
MPResultTypeClassStateful = 1 << 5,
|
||||
/** Use the site key to derive a site-specific object. */
|
||||
MPResultTypeClassDerive = 1 << 6,
|
||||
};
|
||||
|
||||
typedef enum( unsigned int, MPSiteFeature ) {
|
||||
// bit 10 - 15
|
||||
typedef mpw_enum( uint16_t, MPSiteFeature ) {
|
||||
/** Export the key-protected content data. */
|
||||
MPSiteFeatureExportContent = 1 << 10,
|
||||
/** Never export content. */
|
||||
MPSiteFeatureDevicePrivate = 1 << 11,
|
||||
/** Don't use this as the primary authentication result type. */
|
||||
MPSiteFeatureAlternative = 1 << 12,
|
||||
};
|
||||
|
||||
typedef enum( unsigned int, MPSiteType) {
|
||||
MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0,
|
||||
MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0,
|
||||
MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0,
|
||||
MPSiteTypeGeneratedBasic = 0x4 | MPSiteTypeClassGenerated | 0x0,
|
||||
MPSiteTypeGeneratedShort = 0x3 | MPSiteTypeClassGenerated | 0x0,
|
||||
MPSiteTypeGeneratedPIN = 0x5 | MPSiteTypeClassGenerated | 0x0,
|
||||
MPSiteTypeGeneratedName = 0xE | MPSiteTypeClassGenerated | 0x0,
|
||||
MPSiteTypeGeneratedPhrase = 0xF | MPSiteTypeClassGenerated | 0x0,
|
||||
// bit 0-3 | MPResultTypeClass | MPSiteFeature
|
||||
typedef mpw_enum( uint32_t, MPResultType ) {
|
||||
/** pg^VMAUBk5x3p%HP%i4= */
|
||||
MPResultTypeTemplateMaximum = 0x0 | MPResultTypeClassTemplate | 0x0,
|
||||
/** BiroYena8:Kixa */
|
||||
MPResultTypeTemplateLong = 0x1 | MPResultTypeClassTemplate | 0x0,
|
||||
/** BirSuj0- */
|
||||
MPResultTypeTemplateMedium = 0x2 | MPResultTypeClassTemplate | 0x0,
|
||||
/** pO98MoD0 */
|
||||
MPResultTypeTemplateBasic = 0x4 | MPResultTypeClassTemplate | 0x0,
|
||||
/** Bir8 */
|
||||
MPResultTypeTemplateShort = 0x3 | MPResultTypeClassTemplate | 0x0,
|
||||
/** 2798 */
|
||||
MPResultTypeTemplatePIN = 0x5 | MPResultTypeClassTemplate | 0x0,
|
||||
/** birsujano */
|
||||
MPResultTypeTemplateName = 0xE | MPResultTypeClassTemplate | 0x0,
|
||||
/** bir yennoquce fefi */
|
||||
MPResultTypeTemplatePhrase = 0xF | MPResultTypeClassTemplate | 0x0,
|
||||
|
||||
MPSiteTypeStoredPersonal = 0x0 | MPSiteTypeClassStored | MPSiteFeatureExportContent,
|
||||
MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate,
|
||||
/** Custom saved password. */
|
||||
MPResultTypeStatefulPersonal = 0x0 | MPResultTypeClassStateful | MPSiteFeatureExportContent,
|
||||
/** Custom saved password that should not be exported from the device. */
|
||||
MPResultTypeStatefulDevice = 0x1 | MPResultTypeClassStateful | MPSiteFeatureDevicePrivate,
|
||||
|
||||
/** Derive a unique binary key. */
|
||||
MPResultTypeDeriveKey = 0x0 | MPResultTypeClassDerive | MPSiteFeatureAlternative,
|
||||
|
||||
MPResultTypeDefault = MPResultTypeTemplateLong,
|
||||
};
|
||||
|
||||
typedef mpw_enum ( uint32_t, MPCounterValue ) {
|
||||
/** Use a time-based counter value, resulting in a TOTP generator. */
|
||||
MPCounterValueTOTP = 0,
|
||||
/** The initial value for a site's counter. */
|
||||
MPCounterValueInitial = 1,
|
||||
|
||||
MPCounterValueDefault = MPCounterValueInitial,
|
||||
MPCounterValueFirst = MPCounterValueTOTP,
|
||||
MPCounterValueLast = UINT32_MAX,
|
||||
};
|
||||
|
||||
//// Type utilities.
|
||||
|
||||
/**
|
||||
* @return The variant represented by the given name.
|
||||
* @return The purpose represented by the given name.
|
||||
*/
|
||||
const MPSiteVariant mpw_variantWithName(const char *variantName);
|
||||
const MPKeyPurpose mpw_purposeWithName(const char *purposeName);
|
||||
/**
|
||||
* @return An internal string containing the scope identifier to apply when encoding for the given variant.
|
||||
* @return The standard name for the given purpose.
|
||||
*/
|
||||
const char *mpw_scopeForVariant(MPSiteVariant variant);
|
||||
const char *mpw_nameForPurpose(MPKeyPurpose purpose);
|
||||
/**
|
||||
* @return An internal string containing the scope identifier to apply when encoding for the given purpose.
|
||||
*/
|
||||
const char *mpw_scopeForPurpose(MPKeyPurpose purpose);
|
||||
|
||||
/**
|
||||
* @return The type represented by the given name.
|
||||
* @return The password type represented by the given name.
|
||||
*/
|
||||
const MPSiteType mpw_typeWithName(const char *typeName);
|
||||
const MPResultType mpw_typeWithName(const char *typeName);
|
||||
/**
|
||||
* @return The standard name for the given password type.
|
||||
*/
|
||||
const char *mpw_nameForType(MPResultType resultType);
|
||||
|
||||
/**
|
||||
* @return A newly allocated array of internal strings that express the templates to use for the given type.
|
||||
@@ -90,12 +141,12 @@ const MPSiteType mpw_typeWithName(const char *typeName);
|
||||
* If an unsupported type is given, count will be 0 and will return NULL.
|
||||
* The array needs to be free'ed, the strings themselves must not be free'ed or modified.
|
||||
*/
|
||||
const char **mpw_templatesForType(MPSiteType type, size_t *count);
|
||||
const char **mpw_templatesForType(MPResultType type, size_t *count);
|
||||
/**
|
||||
* @return An internal string that contains the password encoding template of the given type
|
||||
* for a seed that starts with the given byte.
|
||||
*/
|
||||
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte);
|
||||
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.
|
||||
|
||||
@@ -16,74 +16,156 @@
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if COLOR
|
||||
#if MPW_COLOR
|
||||
#include <unistd.h>
|
||||
#include <curses.h>
|
||||
#include <term.h>
|
||||
#endif
|
||||
|
||||
#if HAS_CPERCIVA
|
||||
#if MPW_CPERCIVA
|
||||
#include <scrypt/crypto_scrypt.h>
|
||||
#include <scrypt/sha256.h>
|
||||
#elif HAS_SODIUM
|
||||
#elif MPW_SODIUM
|
||||
#include "sodium.h"
|
||||
#endif
|
||||
|
||||
#ifndef trc
|
||||
int mpw_verbosity;
|
||||
#endif
|
||||
|
||||
#include "mpw-util.h"
|
||||
|
||||
void mpw_push_buf(uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize) {
|
||||
#ifdef inf_level
|
||||
int mpw_verbosity = inf_level;
|
||||
#endif
|
||||
|
||||
if (*bufferSize == (size_t)-1)
|
||||
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)
|
||||
return false;
|
||||
if (*bufferSize == (size_t)ERR)
|
||||
// The buffer was marked as broken, it is missing a previous push. Abort to avoid corrupt content.
|
||||
return;
|
||||
return false;
|
||||
|
||||
*bufferSize += pushSize;
|
||||
uint8_t *resizedBuffer = realloc( *buffer, *bufferSize );
|
||||
if (!resizedBuffer) {
|
||||
if (!mpw_realloc( buffer, bufferSize, pushSize )) {
|
||||
// realloc failed, we can't push. Mark the buffer as broken.
|
||||
mpw_free( *buffer, *bufferSize - pushSize );
|
||||
*bufferSize = (size_t)-1;
|
||||
*buffer = NULL;
|
||||
return;
|
||||
mpw_free( buffer, *bufferSize );
|
||||
*bufferSize = (size_t)ERR;
|
||||
return false;
|
||||
}
|
||||
|
||||
*buffer = resizedBuffer;
|
||||
uint8_t *pushDst = *buffer + *bufferSize - pushSize;
|
||||
memcpy( pushDst, pushBuffer, pushSize );
|
||||
uint8_t *bufferOffset = *buffer + *bufferSize - pushSize;
|
||||
memcpy( bufferOffset, pushBuffer, pushSize );
|
||||
return true;
|
||||
}
|
||||
|
||||
void mpw_push_string(uint8_t **buffer, size_t *const bufferSize, const char *pushString) {
|
||||
bool mpw_push_string(uint8_t **buffer, size_t *bufferSize, const char *pushString) {
|
||||
|
||||
mpw_push_buf( buffer, bufferSize, pushString, strlen( pushString ) );
|
||||
return pushString && mpw_push_buf( buffer, bufferSize, pushString, strlen( pushString ) );
|
||||
}
|
||||
|
||||
void mpw_push_int(uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt) {
|
||||
bool mpw_string_push(char **string, const char *pushString) {
|
||||
|
||||
mpw_push_buf( buffer, bufferSize, &pushInt, sizeof( pushInt ) );
|
||||
if (!string || !pushString)
|
||||
return false;
|
||||
if (!*string)
|
||||
*string = calloc( 1, sizeof( char ) );
|
||||
|
||||
size_t stringLength = strlen( *string );
|
||||
return pushString && mpw_push_buf( (uint8_t **const)string, &stringLength, pushString, strlen( pushString ) + 1 );
|
||||
}
|
||||
|
||||
void mpw_free(const void *buffer, const size_t bufferSize) {
|
||||
bool mpw_string_pushf(char **string, const char *pushFormat, ...) {
|
||||
|
||||
if (buffer) {
|
||||
memset( (void *)buffer, 0, bufferSize );
|
||||
free( (void *)buffer );
|
||||
}
|
||||
va_list args;
|
||||
va_start( args, pushFormat );
|
||||
bool success = mpw_string_push( string, mpw_vstr( pushFormat, args ) );
|
||||
va_end( args );
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void mpw_free_string(const char *string) {
|
||||
bool mpw_push_int(uint8_t **buffer, size_t *bufferSize, const uint32_t pushInt) {
|
||||
|
||||
mpw_free( string, strlen( string ) );
|
||||
uint8_t pushBuf[4 /* 32 / 8 */];
|
||||
mpw_uint32( pushInt, pushBuf );
|
||||
return mpw_push_buf( buffer, bufferSize, &pushBuf, sizeof( pushBuf ) );
|
||||
}
|
||||
|
||||
uint8_t const *mpw_scrypt(const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize,
|
||||
bool __mpw_realloc(const void **buffer, size_t *bufferSize, const size_t deltaSize) {
|
||||
|
||||
if (!buffer)
|
||||
return false;
|
||||
|
||||
void *newBuffer = realloc( (void *)*buffer, (bufferSize? *bufferSize: 0) + deltaSize );
|
||||
if (!newBuffer)
|
||||
return false;
|
||||
|
||||
*buffer = newBuffer;
|
||||
if (bufferSize)
|
||||
*bufferSize += deltaSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool __mpw_free(const void **buffer, const size_t bufferSize) {
|
||||
|
||||
if (!buffer || !*buffer)
|
||||
return false;
|
||||
|
||||
memset( (void *)*buffer, 0, bufferSize );
|
||||
free( (void *)*buffer );
|
||||
*buffer = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool __mpw_free_string(const char **string) {
|
||||
|
||||
return *string && __mpw_free( (const void **)string, strlen( *string ) );
|
||||
}
|
||||
|
||||
bool __mpw_free_strings(const char **strings, ...) {
|
||||
|
||||
bool success = true;
|
||||
|
||||
va_list args;
|
||||
va_start( args, strings );
|
||||
success &= mpw_free_string( strings );
|
||||
for (const char **string; (string = va_arg( args, const 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,
|
||||
uint64_t N, uint32_t r, uint32_t p) {
|
||||
|
||||
if (!secret || !salt)
|
||||
@@ -93,114 +175,315 @@ uint8_t const *mpw_scrypt(const size_t keySize, const char *secret, const uint8_
|
||||
if (!key)
|
||||
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) {
|
||||
mpw_free( key, keySize );
|
||||
mpw_free( &key, keySize );
|
||||
return NULL;
|
||||
}
|
||||
#elif HAS_SODIUM
|
||||
if (crypto_pwhash_scryptsalsa208sha256_ll( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize) != 0 ) {
|
||||
mpw_free( key, keySize );
|
||||
#elif MPW_SODIUM
|
||||
if (crypto_pwhash_scryptsalsa208sha256_ll( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) != 0) {
|
||||
mpw_free( &key, keySize );
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
#error No crypto support for mpw_scrypt.
|
||||
#endif
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
uint8_t const *mpw_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize) {
|
||||
uint8_t const *mpw_kdf_blake2b(const size_t subkeySize, const uint8_t *key, const size_t keySize,
|
||||
const uint8_t *context, const size_t contextSize, const uint64_t id, const char *personal) {
|
||||
|
||||
#if HAS_CPERCIVA
|
||||
uint8_t *const buffer = malloc( 32 );
|
||||
if (!buffer)
|
||||
if (!key || !keySize || !subkeySize) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *subkey = malloc( subkeySize );
|
||||
if (!subkey)
|
||||
return NULL;
|
||||
|
||||
HMAC_SHA256_Buf( key, keySize, salt, saltSize, buffer );
|
||||
return buffer;
|
||||
#elif HAS_SODIUM
|
||||
uint8_t *const buffer = malloc( crypto_auth_hmacsha256_BYTES );
|
||||
if (!buffer)
|
||||
#if MPW_SODIUM
|
||||
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 ||
|
||||
contextSize < crypto_generichash_blake2b_BYTES_MIN || contextSize > crypto_generichash_blake2b_BYTES_MAX ||
|
||||
(personal && strlen( personal ) > crypto_generichash_blake2b_PERSONALBYTES)) {
|
||||
errno = EINVAL;
|
||||
free( subkey );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t saltBuf[crypto_generichash_blake2b_SALTBYTES];
|
||||
bzero( saltBuf, sizeof saltBuf );
|
||||
if (id)
|
||||
mpw_uint64( id, saltBuf );
|
||||
|
||||
uint8_t personalBuf[crypto_generichash_blake2b_PERSONALBYTES];
|
||||
bzero( personalBuf, sizeof saltBuf );
|
||||
if (personal && strlen( personal ))
|
||||
memcpy( personalBuf, personal, strlen( personal ) );
|
||||
|
||||
if (crypto_generichash_blake2b_salt_personal( subkey, subkeySize, context, contextSize, key, keySize, saltBuf, personalBuf ) != 0) {
|
||||
mpw_free( &subkey, subkeySize );
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
#error No crypto support for mpw_kdf_blake2b.
|
||||
#endif
|
||||
|
||||
return subkey;
|
||||
}
|
||||
|
||||
uint8_t const *mpw_hash_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *message, const size_t messageSize) {
|
||||
|
||||
if (!key || !keySize || !message || !messageSize)
|
||||
return NULL;
|
||||
|
||||
#if MPW_CPERCIVA
|
||||
uint8_t *const mac = malloc( 32 );
|
||||
if (!mac)
|
||||
return NULL;
|
||||
|
||||
HMAC_SHA256_Buf( key, keySize, message, messageSize, mac );
|
||||
#elif MPW_SODIUM
|
||||
uint8_t *const mac = malloc( crypto_auth_hmacsha256_BYTES );
|
||||
if (!mac)
|
||||
return NULL;
|
||||
|
||||
crypto_auth_hmacsha256_state state;
|
||||
if (crypto_auth_hmacsha256_init( &state, key, keySize ) != 0 ||
|
||||
crypto_auth_hmacsha256_update( &state, salt, saltSize ) != 0 ||
|
||||
crypto_auth_hmacsha256_final( &state, buffer ) != 0) {
|
||||
mpw_free( buffer, crypto_auth_hmacsha256_BYTES );
|
||||
crypto_auth_hmacsha256_update( &state, message, messageSize ) != 0 ||
|
||||
crypto_auth_hmacsha256_final( &state, mac ) != 0) {
|
||||
mpw_free( &mac, crypto_auth_hmacsha256_BYTES );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
#else
|
||||
#error No crypto support for mpw_hmac_sha256.
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
return mac;
|
||||
}
|
||||
|
||||
const char *mpw_id_buf(const void *buf, size_t length) {
|
||||
static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t keySize, const uint8_t *buf, const size_t bufSize) {
|
||||
|
||||
#if HAS_CPERCIVA
|
||||
#if MPW_SODIUM
|
||||
if (!key || keySize < crypto_stream_KEYBYTES)
|
||||
return NULL;
|
||||
|
||||
uint8_t nonce[crypto_stream_NONCEBYTES];
|
||||
bzero( (void *)nonce, sizeof( nonce ) );
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
if (encrypt) {
|
||||
uint8_t *const cipherBuf = malloc( bufSize );
|
||||
if (crypto_stream_aes128ctr_xor( cipherBuf, buf, bufSize, nonce, key ) != 0) {
|
||||
mpw_free( &cipherBuf, bufSize );
|
||||
return NULL;
|
||||
}
|
||||
return cipherBuf;
|
||||
}
|
||||
else {
|
||||
uint8_t *const plainBuf = malloc( bufSize );
|
||||
if (crypto_stream_aes128ctr( plainBuf, bufSize, nonce, key ) != 0) {
|
||||
mpw_free( &plainBuf, bufSize );
|
||||
return NULL;
|
||||
}
|
||||
for (size_t c = 0; c < bufSize; ++c)
|
||||
plainBuf[c] = buf[c] ^ plainBuf[c];
|
||||
return plainBuf;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
#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) {
|
||||
|
||||
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) {
|
||||
|
||||
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) {
|
||||
|
||||
if (!buf)
|
||||
return "<unset>";
|
||||
|
||||
#if MPW_CPERCIVA
|
||||
uint8_t hash[32];
|
||||
SHA256_Buf( buf, length, hash );
|
||||
|
||||
return mpw_hex( hash, 32 );
|
||||
#elif HAS_SODIUM
|
||||
#elif MPW_SODIUM
|
||||
uint8_t hash[crypto_hash_sha256_BYTES];
|
||||
crypto_hash_sha256( hash, buf, length );
|
||||
|
||||
return mpw_hex( hash, crypto_hash_sha256_BYTES );
|
||||
#else
|
||||
#error No crypto support for mpw_id_buf.
|
||||
#endif
|
||||
|
||||
return mpw_hex( hash, sizeof( hash ) / sizeof( uint8_t ) );
|
||||
}
|
||||
|
||||
static char **mpw_hex_buf = NULL;
|
||||
static unsigned int mpw_hex_buf_i = 0;
|
||||
bool mpw_id_buf_equals(const char *id1, const char *id2) {
|
||||
|
||||
size_t size = strlen( id1 );
|
||||
if (size != strlen( id2 ))
|
||||
return false;
|
||||
|
||||
for (size_t c = 0; c < size; ++c)
|
||||
if (tolower( id1[c] ) != tolower( id2[c] ))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *mpw_str(const char *format, ...) {
|
||||
|
||||
va_list args;
|
||||
va_start( args, format );
|
||||
const char *str_str = mpw_vstr( format, args );
|
||||
va_end( args );
|
||||
|
||||
return str_str;
|
||||
}
|
||||
|
||||
const char *mpw_vstr(const char *format, va_list args) {
|
||||
|
||||
// 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) {
|
||||
|
||||
// FIXME
|
||||
if (!mpw_hex_buf) {
|
||||
mpw_hex_buf = malloc( 10 * sizeof( char * ) );
|
||||
for (uint8_t i = 0; i < 10; ++i)
|
||||
mpw_hex_buf[i] = NULL;
|
||||
}
|
||||
// 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)
|
||||
mpw_hex_buf = calloc( 10, sizeof( char * ) );
|
||||
mpw_hex_buf_i = (mpw_hex_buf_i + 1) % 10;
|
||||
|
||||
mpw_hex_buf[mpw_hex_buf_i] = realloc( mpw_hex_buf[mpw_hex_buf_i], length * 2 + 1 );
|
||||
for (size_t kH = 0; kH < length; kH++)
|
||||
sprintf( &(mpw_hex_buf[mpw_hex_buf_i][kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
|
||||
if (mpw_realloc( &mpw_hex_buf[mpw_hex_buf_i], NULL, length * 2 + 1 ))
|
||||
for (size_t kH = 0; kH < length; kH++)
|
||||
sprintf( &(mpw_hex_buf[mpw_hex_buf_i][kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
|
||||
|
||||
return mpw_hex_buf[mpw_hex_buf_i];
|
||||
}
|
||||
|
||||
const char *mpw_hex_l(uint32_t number) {
|
||||
|
||||
return mpw_hex( &number, sizeof( number ) );
|
||||
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
|
||||
static int putvari;
|
||||
static char *putvarc = NULL;
|
||||
static int termsetup;
|
||||
static int initputvar() {
|
||||
if (!isatty(STDERR_FILENO))
|
||||
return 0;
|
||||
if (putvarc)
|
||||
free( putvarc );
|
||||
#if MPW_COLOR
|
||||
static char *str_tputs;
|
||||
static int str_tputs_cursor;
|
||||
static const int str_tputs_max = 256;
|
||||
|
||||
static bool mpw_setupterm() {
|
||||
|
||||
if (!isatty( STDERR_FILENO ))
|
||||
return false;
|
||||
|
||||
static bool termsetup;
|
||||
if (!termsetup) {
|
||||
int status;
|
||||
if (! (termsetup = (setupterm( NULL, STDERR_FILENO, &status ) == 0 && status == 1))) {
|
||||
wrn( "Terminal doesn't support color (setupterm errno %d).\n", status );
|
||||
return 0;
|
||||
int errret;
|
||||
if (!(termsetup = (setupterm( NULL, STDERR_FILENO, &errret ) == OK))) {
|
||||
wrn( "Terminal doesn't support color (setupterm errret %d).\n", errret );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
putvarc=(char *)calloc(256, sizeof(char));
|
||||
putvari=0;
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
static int putvar(int c) {
|
||||
putvarc[putvari++]=c;
|
||||
return 0;
|
||||
|
||||
static int mpw_tputc(int c) {
|
||||
|
||||
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
|
||||
|
||||
const char *mpw_identicon(const char *fullName, const char *masterPassword) {
|
||||
@@ -214,21 +497,20 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) {
|
||||
"♨", "♩", "♪", "♫", "⚐", "⚑", "⚔", "⚖", "⚙", "⚠", "⌘", "⏎", "✄", "✆", "✈", "✉", "✌"
|
||||
};
|
||||
|
||||
const uint8_t *identiconSeed = mpw_hmac_sha256( (const uint8_t *)masterPassword, strlen( masterPassword ), (const uint8_t *)fullName, strlen( fullName ) );
|
||||
const uint8_t *identiconSeed = mpw_hash_hmac_sha256(
|
||||
(const uint8_t *)masterPassword, strlen( masterPassword ),
|
||||
(const uint8_t *)fullName, strlen( fullName ) );
|
||||
if (!identiconSeed)
|
||||
return NULL;
|
||||
|
||||
char *colorString, *resetString;
|
||||
#ifdef COLOR
|
||||
if (initputvar()) {
|
||||
#ifdef MPW_COLOR
|
||||
if (mpw_setupterm()) {
|
||||
uint8_t colorIdentifier = (uint8_t)(identiconSeed[4] % 7 + 1);
|
||||
tputs(tparm(tgetstr("AF", NULL), colorIdentifier), 1, putvar);
|
||||
colorString = calloc(strlen(putvarc) + 1, sizeof(char));
|
||||
strcpy(colorString, putvarc);
|
||||
tputs(tgetstr("me", NULL), 1, putvar);
|
||||
resetString = calloc(strlen(putvarc) + 1, sizeof(char));
|
||||
strcpy(resetString, putvarc);
|
||||
} else
|
||||
colorString = mpw_tputs( tparm( tgetstr( "AF", NULL ), colorIdentifier ), 1 );
|
||||
resetString = mpw_tputs( tgetstr( "me", NULL ), 1 );
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
colorString = calloc( 1, sizeof( char ) );
|
||||
@@ -244,9 +526,8 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) {
|
||||
accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( accessory[0] ))],
|
||||
resetString );
|
||||
|
||||
mpw_free( identiconSeed, 32 );
|
||||
free( colorString );
|
||||
free( resetString );
|
||||
mpw_free( &identiconSeed, 32 );
|
||||
mpw_free_strings( &colorString, &resetString, NULL );
|
||||
return identicon;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,99 +16,191 @@
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#ifndef _MPW_UTIL_H
|
||||
#define _MPW_UTIL_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "mpw-types.h"
|
||||
|
||||
//// Logging.
|
||||
|
||||
#ifndef trc
|
||||
extern int mpw_verbosity;
|
||||
#define trc_level 3
|
||||
#define trc(...) \
|
||||
if (mpw_verbosity >= 3) \
|
||||
fprintf( stderr, __VA_ARGS__ )
|
||||
#define trc_level 3
|
||||
/** Logging internal state. */
|
||||
#define trc(...) ({ \
|
||||
if (mpw_verbosity >= trc_level) \
|
||||
fprintf( stderr, __VA_ARGS__ ); })
|
||||
#endif
|
||||
#ifndef dbg
|
||||
#define dbg_level 2
|
||||
#define dbg(...) \
|
||||
if (mpw_verbosity >= 2) \
|
||||
fprintf( stderr, __VA_ARGS__ )
|
||||
#define dbg_level 2
|
||||
/** Logging state and events interesting when investigating issues. */
|
||||
#define dbg(...) ({ \
|
||||
if (mpw_verbosity >= dbg_level) \
|
||||
fprintf( stderr, __VA_ARGS__ ); })
|
||||
#endif
|
||||
#ifndef inf
|
||||
#define inf_level 1
|
||||
#define inf(...) \
|
||||
if (mpw_verbosity >= 1) \
|
||||
fprintf( stderr, __VA_ARGS__ )
|
||||
#define inf_level 1
|
||||
/** User messages. */
|
||||
#define inf(...) ({ \
|
||||
if (mpw_verbosity >= inf_level) \
|
||||
fprintf( stderr, __VA_ARGS__ ); })
|
||||
#endif
|
||||
#ifndef wrn
|
||||
#define wrn_level 0
|
||||
#define wrn(...) \
|
||||
if (mpw_verbosity >= 0) \
|
||||
fprintf( stderr, __VA_ARGS__ )
|
||||
#define wrn_level 0
|
||||
/** Recoverable issues and user suggestions. */
|
||||
#define wrn(...) ({ \
|
||||
if (mpw_verbosity >= wrn_level) \
|
||||
fprintf( stderr, __VA_ARGS__ ); })
|
||||
#endif
|
||||
#ifndef err
|
||||
#define err_level -1
|
||||
#define err(...) \
|
||||
if (mpw_verbosity >= -1) \
|
||||
fprintf( stderr, __VA_ARGS__ )
|
||||
#define err_level -1
|
||||
/** Unrecoverable issues. */
|
||||
#define err(...) ({ \
|
||||
if (mpw_verbosity >= err_level) \
|
||||
fprintf( stderr, __VA_ARGS__ ); })
|
||||
#endif
|
||||
#ifndef ftl
|
||||
#define ftl_level -2
|
||||
#define ftl(...) \
|
||||
do { \
|
||||
if (mpw_verbosity >= -2) \
|
||||
fprintf( stderr, __VA_ARGS__ ); \
|
||||
exit( 2 ); \
|
||||
} while (0)
|
||||
#define ftl_level -2
|
||||
/** Issues that lead to abortion. */
|
||||
#define ftl(...) ({ \
|
||||
if (mpw_verbosity >= ftl_level) \
|
||||
fprintf( stderr, __VA_ARGS__ ); })
|
||||
#endif
|
||||
|
||||
#ifndef min
|
||||
#define min(a, b) ({ \
|
||||
__typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a < _b ? _a : _b; })
|
||||
#endif
|
||||
#ifndef max
|
||||
#define max(a, b) ({ \
|
||||
__typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; })
|
||||
#endif
|
||||
#ifndef ERR
|
||||
#define ERR -1
|
||||
#endif
|
||||
#ifndef OK
|
||||
#define OK 0
|
||||
#endif
|
||||
#ifndef stringify
|
||||
#define stringify(s) #s
|
||||
#endif
|
||||
#ifndef stringify_def
|
||||
#define stringify_def(s) stringify(s)
|
||||
#endif
|
||||
|
||||
//// 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. */
|
||||
#define mpw_alloc_array(_count, _type, ...) ({ \
|
||||
_type stackElements[] = { __VA_ARGS__ }; \
|
||||
_count = sizeof( stackElements ) / sizeof( _type ); \
|
||||
if (_count) \
|
||||
*_count = sizeof( stackElements ) / sizeof( _type ); \
|
||||
_type *allocElements = malloc( sizeof( stackElements ) ); \
|
||||
memcpy( allocElements, stackElements, sizeof( stackElements ) ); \
|
||||
allocElements; \
|
||||
})
|
||||
|
||||
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
|
||||
void mpw_push_buf(
|
||||
uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize);
|
||||
bool mpw_push_buf(
|
||||
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. */
|
||||
void mpw_push_string(
|
||||
uint8_t **buffer, size_t *const bufferSize, const char *pushString);
|
||||
bool mpw_push_string(
|
||||
uint8_t **buffer, size_t *bufferSize, const char *pushString);
|
||||
/** Push a string onto another string. reallocs the target string and appends the source string. */
|
||||
bool mpw_string_push(
|
||||
char **string, const char *pushString);
|
||||
bool mpw_string_pushf(
|
||||
char **string, const char *pushFormat, ...);
|
||||
/** Push an integer onto a buffer. reallocs the given buffer and appends the given integer. */
|
||||
void mpw_push_int(
|
||||
uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt);
|
||||
/** Free a buffer after zero'ing its contents. */
|
||||
void mpw_free(
|
||||
const void *buffer, const size_t bufferSize);
|
||||
/** Free a string after zero'ing its contents. */
|
||||
void mpw_free_string(
|
||||
const char *string);
|
||||
bool mpw_push_int(
|
||||
uint8_t **buffer, size_t *bufferSize, const uint32_t pushInt);
|
||||
/** 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
|
||||
* and the buffer pointer may be updated to a new memory address.
|
||||
* On failure, the buffer and pointers will remain unaffected.
|
||||
* @param buffer A pointer to the buffer to reallocate.
|
||||
* @param bufferSize A pointer to the buffer's actual size.
|
||||
* @param deltaSize The amount to increase the buffer's size by.
|
||||
* @return true if successful, false if reallocation failed.
|
||||
*/
|
||||
#define mpw_realloc(buffer, bufferSize, deltaSize) \
|
||||
({ typeof(buffer) _b = buffer; const void *__b = *_b; (void)__b; __mpw_realloc( (const void **)_b, bufferSize, deltaSize ); })
|
||||
bool __mpw_realloc(const void **buffer, size_t *bufferSize, const size_t deltaSize);
|
||||
/** Free a buffer after zero'ing its contents, then set the reference to NULL. */
|
||||
#define mpw_free(buffer, bufferSize) \
|
||||
({ typeof(buffer) _b = buffer; const void *__b = *_b; (void)__b; __mpw_free( (const void **)_b, bufferSize ); })
|
||||
bool __mpw_free(
|
||||
const void **buffer, const 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( (const char **)_s ); })
|
||||
bool __mpw_free_string(
|
||||
const 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( (const char **)_s, __VA_ARGS__ ); })
|
||||
bool __mpw_free_strings(
|
||||
const char **strings, ...);
|
||||
|
||||
//// Cryptographic functions.
|
||||
|
||||
/** Perform a scrypt-based key derivation on the given key using the given salt and scrypt parameters.
|
||||
* @return A new keySize-size allocated buffer. */
|
||||
uint8_t const *mpw_scrypt(
|
||||
/** Derive a key from the given secret and salt using the scrypt KDF.
|
||||
* @return A new keySize allocated buffer containing the key. */
|
||||
uint8_t const *mpw_kdf_scrypt(
|
||||
const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize,
|
||||
uint64_t N, uint32_t r, uint32_t p);
|
||||
/** Calculate a SHA256-based HMAC by encrypting the given salt with the given key.
|
||||
* @return A new 32-byte allocated buffer. */
|
||||
uint8_t const *mpw_hmac_sha256(
|
||||
/** Derive a subkey from the given key using the blake2b KDF.
|
||||
* @return A new keySize allocated buffer containing the key. */
|
||||
uint8_t const *mpw_kdf_blake2b(
|
||||
const size_t subkeySize, const uint8_t *key, const size_t keySize,
|
||||
const uint8_t *context, const size_t contextSize, const uint64_t id, const char *personal);
|
||||
/** Calculate the MAC for the given message with the given key using SHA256-HMAC.
|
||||
* @return A new 32-byte allocated buffer containing the MAC. */
|
||||
uint8_t const *mpw_hash_hmac_sha256(
|
||||
const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize);
|
||||
/** Encrypt a plainBuf with the given key using AES-128-CBC.
|
||||
* @return A new bufSize allocated buffer containing the cipherBuf. */
|
||||
uint8_t const *mpw_aes_encrypt(
|
||||
const uint8_t *key, const size_t keySize, const uint8_t *plainBuf, const size_t bufSize);
|
||||
/** Decrypt a cipherBuf with the given key using AES-128-CBC.
|
||||
* @return A new bufSize allocated buffer containing the plainBuf. */
|
||||
uint8_t const *mpw_aes_decrypt(
|
||||
const uint8_t *key, const size_t keySize, const uint8_t *cipherBuf, const 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.
|
||||
|
||||
/** Compose a formatted string.
|
||||
* @return A C-string in a reused buffer, do not free or store it. */
|
||||
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.
|
||||
* @return A C-string in a reused buffer, do not free or store it. */
|
||||
const char *mpw_hex(const void *buf, size_t length);
|
||||
const char *mpw_hex_l(uint32_t number);
|
||||
/** Encode a fingerprint for a buffer.
|
||||
* @return A C-string in a reused buffer, do not free or store it. */
|
||||
const char *mpw_id_buf(const void *buf, size_t length);
|
||||
MPKeyID mpw_id_buf(const void *buf, size_t length);
|
||||
/** Compare two fingerprints for equality.
|
||||
* @return true if the buffers represent identical fingerprints. */
|
||||
bool mpw_id_buf_equals(const char *id1, const char *id2);
|
||||
/** Encode a visual fingerprint for a user.
|
||||
* @return A newly allocated string. */
|
||||
const char *mpw_identicon(const char *fullName, const char *masterPassword);
|
||||
@@ -117,3 +209,5 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword);
|
||||
|
||||
/** @return The amount of display characters in the given UTF-8 string. */
|
||||
const size_t mpw_utf8_strlen(const char *utf8String);
|
||||
|
||||
#endif // _MPW_UTIL_H
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
|
||||
<siteName>masterpasswordapp.com</siteName>
|
||||
<siteCounter>1</siteCounter>
|
||||
<siteType>GeneratedLong</siteType>
|
||||
<siteVariant>Password</siteVariant>
|
||||
<resultType>GeneratedLong</resultType>
|
||||
<keyPurpose>Authentication</keyPurpose>
|
||||
<result><!-- abstract --></result>
|
||||
</case>
|
||||
|
||||
@@ -32,45 +32,45 @@
|
||||
<result>LiheCuwhSerz6)</result>
|
||||
</case>
|
||||
<case id="v3_loginName" parent="v3">
|
||||
<siteVariant>Login</siteVariant>
|
||||
<siteType>GeneratedName</siteType>
|
||||
<keyPurpose>Identification</keyPurpose>
|
||||
<resultType>GeneratedName</resultType>
|
||||
<result>wohzaqage</result>
|
||||
</case>
|
||||
<case id="v3_securityAnswer" parent="v3">
|
||||
<siteVariant>Answer</siteVariant>
|
||||
<siteType>GeneratedPhrase</siteType>
|
||||
<keyPurpose>Recovery</keyPurpose>
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<result>xin diyjiqoja hubu</result>
|
||||
</case>
|
||||
<case id="v3_securityAnswer_context" parent="v3_securityAnswer">
|
||||
<siteContext>question</siteContext>
|
||||
<keyContext>question</keyContext>
|
||||
<result>xogx tem cegyiva jab</result>
|
||||
</case>
|
||||
<case id="v3_type_maximum" parent="v3">
|
||||
<siteType>GeneratedMaximum</siteType>
|
||||
<resultType>GeneratedMaximum</resultType>
|
||||
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||
</case>
|
||||
<case id="v3_type_medium" parent="v3">
|
||||
<siteType>GeneratedMedium</siteType>
|
||||
<resultType>GeneratedMedium</resultType>
|
||||
<result>Jej2$Quv</result>
|
||||
</case>
|
||||
<case id="v3_type_basic" parent="v3">
|
||||
<siteType>GeneratedBasic</siteType>
|
||||
<resultType>GeneratedBasic</resultType>
|
||||
<result>WAo2xIg6</result>
|
||||
</case>
|
||||
<case id="v3_type_short" parent="v3">
|
||||
<siteType>GeneratedShort</siteType>
|
||||
<resultType>GeneratedShort</resultType>
|
||||
<result>Jej2</result>
|
||||
</case>
|
||||
<case id="v3_type_pin" parent="v3">
|
||||
<siteType>GeneratedPIN</siteType>
|
||||
<resultType>GeneratedPIN</resultType>
|
||||
<result>7662</result>
|
||||
</case>
|
||||
<case id="v3_type_name" parent="v3">
|
||||
<siteType>GeneratedName</siteType>
|
||||
<resultType>GeneratedName</resultType>
|
||||
<result>jejraquvo</result>
|
||||
</case>
|
||||
<case id="v3_type_phrase" parent="v3">
|
||||
<siteType>GeneratedPhrase</siteType>
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<result>jejr quv cabsibu tam</result>
|
||||
</case>
|
||||
<case id="v3_counter_ceiling" parent="v3">
|
||||
@@ -98,45 +98,45 @@
|
||||
<result>LiheCuwhSerz6)</result>
|
||||
</case>
|
||||
<case id="v2_loginName" parent="v2">
|
||||
<siteVariant>Login</siteVariant>
|
||||
<siteType>GeneratedName</siteType>
|
||||
<keyPurpose>Identification</keyPurpose>
|
||||
<resultType>GeneratedName</resultType>
|
||||
<result>wohzaqage</result>
|
||||
</case>
|
||||
<case id="v2_securityAnswer" parent="v2">
|
||||
<siteVariant>Answer</siteVariant>
|
||||
<siteType>GeneratedPhrase</siteType>
|
||||
<keyPurpose>Recovery</keyPurpose>
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<result>xin diyjiqoja hubu</result>
|
||||
</case>
|
||||
<case id="v2_securityAnswer_context" parent="v2_securityAnswer">
|
||||
<siteContext>question</siteContext>
|
||||
<keyContext>question</keyContext>
|
||||
<result>xogx tem cegyiva jab</result>
|
||||
</case>
|
||||
<case id="v2_type_maximum" parent="v2">
|
||||
<siteType>GeneratedMaximum</siteType>
|
||||
<resultType>GeneratedMaximum</resultType>
|
||||
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||
</case>
|
||||
<case id="v2_type_medium" parent="v2">
|
||||
<siteType>GeneratedMedium</siteType>
|
||||
<resultType>GeneratedMedium</resultType>
|
||||
<result>Jej2$Quv</result>
|
||||
</case>
|
||||
<case id="v2_type_basic" parent="v2">
|
||||
<siteType>GeneratedBasic</siteType>
|
||||
<resultType>GeneratedBasic</resultType>
|
||||
<result>WAo2xIg6</result>
|
||||
</case>
|
||||
<case id="v2_type_short" parent="v2">
|
||||
<siteType>GeneratedShort</siteType>
|
||||
<resultType>GeneratedShort</resultType>
|
||||
<result>Jej2</result>
|
||||
</case>
|
||||
<case id="v2_type_pin" parent="v2">
|
||||
<siteType>GeneratedPIN</siteType>
|
||||
<resultType>GeneratedPIN</resultType>
|
||||
<result>7662</result>
|
||||
</case>
|
||||
<case id="v2_type_name" parent="v2">
|
||||
<siteType>GeneratedName</siteType>
|
||||
<resultType>GeneratedName</resultType>
|
||||
<result>jejraquvo</result>
|
||||
</case>
|
||||
<case id="v2_type_phrase" parent="v2">
|
||||
<siteType>GeneratedPhrase</siteType>
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<result>jejr quv cabsibu tam</result>
|
||||
</case>
|
||||
<case id="v2_counter_ceiling" parent="v2">
|
||||
@@ -164,45 +164,45 @@
|
||||
<result>WawiYarp2@Kodh</result>
|
||||
</case>
|
||||
<case id="v1_loginName" parent="v1">
|
||||
<siteVariant>Login</siteVariant>
|
||||
<siteType>GeneratedName</siteType>
|
||||
<keyPurpose>Identification</keyPurpose>
|
||||
<resultType>GeneratedName</resultType>
|
||||
<result>wohzaqage</result>
|
||||
</case>
|
||||
<case id="v1_securityAnswer" parent="v1">
|
||||
<siteVariant>Answer</siteVariant>
|
||||
<siteType>GeneratedPhrase</siteType>
|
||||
<keyPurpose>Recovery</keyPurpose>
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<result>xin diyjiqoja hubu</result>
|
||||
</case>
|
||||
<case id="v1_securityAnswer_context" parent="v1_securityAnswer">
|
||||
<siteContext>question</siteContext>
|
||||
<keyContext>question</keyContext>
|
||||
<result>xogx tem cegyiva jab</result>
|
||||
</case>
|
||||
<case id="v1_type_maximum" parent="v1">
|
||||
<siteType>GeneratedMaximum</siteType>
|
||||
<resultType>GeneratedMaximum</resultType>
|
||||
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||
</case>
|
||||
<case id="v1_type_medium" parent="v1">
|
||||
<siteType>GeneratedMedium</siteType>
|
||||
<resultType>GeneratedMedium</resultType>
|
||||
<result>Jej2$Quv</result>
|
||||
</case>
|
||||
<case id="v1_type_basic" parent="v1">
|
||||
<siteType>GeneratedBasic</siteType>
|
||||
<resultType>GeneratedBasic</resultType>
|
||||
<result>WAo2xIg6</result>
|
||||
</case>
|
||||
<case id="v1_type_short" parent="v1">
|
||||
<siteType>GeneratedShort</siteType>
|
||||
<resultType>GeneratedShort</resultType>
|
||||
<result>Jej2</result>
|
||||
</case>
|
||||
<case id="v1_type_pin" parent="v1">
|
||||
<siteType>GeneratedPIN</siteType>
|
||||
<resultType>GeneratedPIN</resultType>
|
||||
<result>7662</result>
|
||||
</case>
|
||||
<case id="v1_type_name" parent="v1">
|
||||
<siteType>GeneratedName</siteType>
|
||||
<resultType>GeneratedName</resultType>
|
||||
<result>jejraquvo</result>
|
||||
</case>
|
||||
<case id="v1_type_phrase" parent="v1">
|
||||
<siteType>GeneratedPhrase</siteType>
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<result>jejr quv cabsibu tam</result>
|
||||
</case>
|
||||
<case id="v1_counter_ceiling" parent="v1">
|
||||
@@ -230,45 +230,45 @@
|
||||
<result>HahiVana2@Nole</result>
|
||||
</case>
|
||||
<case id="v0_loginName" parent="v0">
|
||||
<siteVariant>Login</siteVariant>
|
||||
<siteType>GeneratedName</siteType>
|
||||
<keyPurpose>Identification</keyPurpose>
|
||||
<resultType>GeneratedName</resultType>
|
||||
<result>lozwajave</result>
|
||||
</case>
|
||||
<case id="v0_securityAnswer" parent="v0">
|
||||
<siteVariant>Answer</siteVariant>
|
||||
<siteType>GeneratedPhrase</siteType>
|
||||
<keyPurpose>Recovery</keyPurpose>
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<result>miy lirfijoja dubu</result>
|
||||
</case>
|
||||
<case id="v0_securityAnswer_context" parent="v0_securityAnswer">
|
||||
<siteContext>question</siteContext>
|
||||
<keyContext>question</keyContext>
|
||||
<result>movm bex gevrica jaf</result>
|
||||
</case>
|
||||
<case id="v0_type_maximum" parent="v0">
|
||||
<siteType>GeneratedMaximum</siteType>
|
||||
<resultType>GeneratedMaximum</resultType>
|
||||
<result>w1!3bA3icmRAc)SS@lwl</result>
|
||||
</case>
|
||||
<case id="v0_type_medium" parent="v0">
|
||||
<siteType>GeneratedMedium</siteType>
|
||||
<resultType>GeneratedMedium</resultType>
|
||||
<result>Fej7]Jug</result>
|
||||
</case>
|
||||
<case id="v0_type_basic" parent="v0">
|
||||
<siteType>GeneratedBasic</siteType>
|
||||
<resultType>GeneratedBasic</resultType>
|
||||
<result>wvH7irC1</result>
|
||||
</case>
|
||||
<case id="v0_type_short" parent="v0">
|
||||
<siteType>GeneratedShort</siteType>
|
||||
<resultType>GeneratedShort</resultType>
|
||||
<result>Fej7</result>
|
||||
</case>
|
||||
<case id="v0_type_pin" parent="v0">
|
||||
<siteType>GeneratedPIN</siteType>
|
||||
<resultType>GeneratedPIN</resultType>
|
||||
<result>2117</result>
|
||||
</case>
|
||||
<case id="v0_type_name" parent="v0">
|
||||
<siteType>GeneratedName</siteType>
|
||||
<resultType>GeneratedName</resultType>
|
||||
<result>fejrajugo</result>
|
||||
</case>
|
||||
<case id="v0_type_phrase" parent="v0">
|
||||
<siteType>GeneratedPhrase</siteType>
|
||||
<resultType>GeneratedPhrase</resultType>
|
||||
<result>fejr jug gabsibu bax</result>
|
||||
</case>
|
||||
<case id="v0_counter_ceiling" parent="v0">
|
||||
|
||||
2
platform-darwin/External/Pearl
vendored
2
platform-darwin/External/Pearl
vendored
Submodule platform-darwin/External/Pearl updated: af1cf4cba3...f513a4a63c
1
platform-darwin/External/libjson-c
vendored
Submodule
1
platform-darwin/External/libjson-c
vendored
Submodule
Submodule platform-darwin/External/libjson-c added at 3e5ad38a83
@@ -17,15 +17,17 @@
|
||||
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
|
||||
93D392FD5E2052F7D7DB3774 /* NSString+MPMarkDown.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */; };
|
||||
93D3932889B6B4206E66A6D6 /* PearlEMail.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */; };
|
||||
93D39359B0DF9823F6C56A05 /* PearlHangDetector.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39D8508BF868907E9732E /* PearlHangDetector.h */; };
|
||||
93D39392DEDA376F93C6C718 /* MPCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39BAA71DE51B4D8A1286C /* MPCell.m */; };
|
||||
93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3957D76F71A652716EECC /* MPStoreViewController.m */; };
|
||||
93D393AA69A1193401160418 /* UIView+AlphaScale.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39488AB33616661725929 /* UIView+AlphaScale.m */; };
|
||||
93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */; };
|
||||
93D3942C1B117EE4851AA7B6 /* UIView+Visible.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3952910EDB8E0EBC94BA9 /* UIView+Visible.m */; };
|
||||
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */; };
|
||||
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */; };
|
||||
93D394B5036C882B33C71872 /* MPSitesSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E7A12CC352B2825AA66 /* MPSitesSegue.m */; };
|
||||
93D39508A6814612A5B3C226 /* MPMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399B36CDB2004D7C51391 /* MPMessageViewController.m */; };
|
||||
93D39536EB550E811CCD04BC /* UIResponder+PearlFirstResponder.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */; };
|
||||
93D395373A425B05C86B2268 /* PearlHangDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39BDA5DB85FCDE4E6450A /* PearlHangDetector.m */; };
|
||||
93D3954E96236384AFA00453 /* UIScrollView+PearlAdjustInsets.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */; };
|
||||
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */; };
|
||||
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B381350802A194BF332 /* MPAvatarCell.m */; };
|
||||
@@ -38,7 +40,9 @@
|
||||
93D396BA1C74C4A06FD86437 /* PearlOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D3942A356B639724157982 /* PearlOverlay.h */; };
|
||||
93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */; };
|
||||
93D397952F5635C793C24DF1 /* NSError+PearlFullDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */; };
|
||||
93D397FCAAC6FA885247A4F9 /* PearlLinks.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39FEFC296AF6E7DF8E468 /* PearlLinks.m */; };
|
||||
93D3980046016EFD05B35BC5 /* PearlUICollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */; };
|
||||
93D39861AEE621B287EA93E9 /* PearlLinks.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D399C408B705172FA0FA02 /* PearlLinks.h */; };
|
||||
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */; };
|
||||
93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */; };
|
||||
93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; };
|
||||
@@ -47,13 +51,13 @@
|
||||
93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */; };
|
||||
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */; };
|
||||
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadItems.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */; };
|
||||
93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */; };
|
||||
93D39A5FF670957C0AF8298D /* MPSiteCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPSiteCell.m */; };
|
||||
93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; };
|
||||
93D39AA4A0BE66A872CCC02E /* NSPersistentStore+PearlMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D397F4BAFFF7CF3F1B21A4 /* NSPersistentStore+PearlMigration.h */; };
|
||||
93D39B429C67A62E29DC02DA /* MPRootSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */; };
|
||||
93D39B76DD5AB108BA8928E8 /* UIScrollView+PearlAdjustInsets.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */; };
|
||||
93D39B842AB9A5D072810D76 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */; };
|
||||
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */; };
|
||||
93D39B8F90F58A5D158DDBA3 /* MPSitesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPSitesViewController.m */; };
|
||||
93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */; };
|
||||
93D39BFB5F5F9337F6565DE3 /* UIView+Visible.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39B7B765546B1F1900CB7 /* UIView+Visible.h */; };
|
||||
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
|
||||
@@ -121,7 +125,6 @@
|
||||
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 */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
@@ -172,12 +175,14 @@
|
||||
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 */; };
|
||||
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 */; };
|
||||
DA4DA1DA1564471F00F6F596 /* libuicolor-utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */; };
|
||||
DA5A09DF171A70E4005284AB /* play.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09DD171A70E4005284AB /* play.png */; };
|
||||
DA5A09E0171A70E4005284AB /* play@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09DE171A70E4005284AB /* play@2x.png */; };
|
||||
DA5A09EA171BB0F7005284AB /* unlocked.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09E8171BB0F7005284AB /* unlocked.png */; };
|
||||
DA5A09EB171BB0F7005284AB /* unlocked@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09E9171BB0F7005284AB /* unlocked@2x.png */; };
|
||||
DA5B0B401F36469400B663F0 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = DA5B0B3E1F36469400B663F0 /* base64.c */; };
|
||||
DA5BFA49147E415C00F98B1E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA48147E415C00F98B1E /* UIKit.framework */; };
|
||||
DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
|
||||
DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4C147E415C00F98B1E /* CoreGraphics.framework */; };
|
||||
@@ -244,7 +249,11 @@
|
||||
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 */; };
|
||||
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 */; };
|
||||
DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D3969393A3A46BD27D7078 /* mpw-algorithm.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-marshall-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DAB7AE981F3DDEE000C856B1 /* mpw-marshall-util.c */; };
|
||||
DABB981615100B4000B05417 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DABB981515100B4000B05417 /* SystemConfiguration.framework */; };
|
||||
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 */; };
|
||||
@@ -397,12 +406,8 @@
|
||||
DAFE4A2F15039824003ABA7C /* PearlStringUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45F415039823003ABA7C /* PearlStringUtils.h */; };
|
||||
DAFE4A3015039824003ABA7C /* PearlStringUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE45F515039823003ABA7C /* PearlStringUtils.m */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
DAFE4A3D15039824003ABA7C /* Pearl-UIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE460915039823003ABA7C /* Pearl-UIKit.h */; };
|
||||
DAFE4A3E15039824003ABA7C /* PearlAlert.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE460A15039823003ABA7C /* PearlAlert.h */; };
|
||||
@@ -413,10 +418,6 @@
|
||||
DAFE4A4315039824003ABA7C /* PearlBoxView.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE460F15039823003ABA7C /* PearlBoxView.m */; };
|
||||
DAFE4A4415039824003ABA7C /* PearlGradientView.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE461015039823003ABA7C /* PearlGradientView.h */; };
|
||||
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 */; };
|
||||
DAFE4A4B15039824003ABA7C /* PearlMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE461715039823003ABA7C /* PearlMessageView.m */; };
|
||||
DAFE4A4C15039824003ABA7C /* PearlRootViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE461815039823003ABA7C /* PearlRootViewController.h */; };
|
||||
@@ -446,6 +447,23 @@
|
||||
DAFE4A63150399FF003ABA94 /* NSDateFormatter+RFC3339.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE4A63150399FF003ABA93 /* NSDateFormatter+RFC3339.h */; };
|
||||
/* 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 */
|
||||
DA32D01E19D111C6004F3F0E /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
@@ -476,14 +494,14 @@
|
||||
93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = "<group>"; };
|
||||
93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+PearlAdjustInsets.m"; sourceTree = "<group>"; };
|
||||
93D39149A5F1F9B174D6D061 /* MPStoreViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStoreViewController.h; sourceTree = "<group>"; };
|
||||
93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsViewController.h; sourceTree = "<group>"; };
|
||||
93D3914D7597F9A28DB9D85E /* MPSitesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSitesViewController.h; sourceTree = "<group>"; };
|
||||
93D39156E806BB78E04F78B9 /* PearlSizedTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlSizedTextView.m; sourceTree = "<group>"; };
|
||||
93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppSettingsViewController.m; sourceTree = "<group>"; };
|
||||
93D391943675426839501BB8 /* MPLogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLogsViewController.h; sourceTree = "<group>"; };
|
||||
93D391AA32F24290C424438E /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = "<group>"; };
|
||||
93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadItems.h"; sourceTree = "<group>"; };
|
||||
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRootSegue.h; sourceTree = "<group>"; };
|
||||
93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsViewController.m; sourceTree = "<group>"; };
|
||||
93D3924EE15017F8A12CB436 /* MPSitesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesViewController.m; sourceTree = "<group>"; };
|
||||
93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = "<group>"; };
|
||||
93D392C5A6572DB0EB5B82C8 /* mpw-types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-types.c"; sourceTree = "<group>"; };
|
||||
93D392D76C091DEA3319F11D /* UIView+AlphaScale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+AlphaScale.h"; sourceTree = "<group>"; };
|
||||
@@ -517,11 +535,12 @@
|
||||
93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPWebViewController.m; sourceTree = "<group>"; };
|
||||
93D399493FEDDE74DD1A0C15 /* MPRootSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRootSegue.m; sourceTree = "<group>"; };
|
||||
93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCoachmarkViewController.m; sourceTree = "<group>"; };
|
||||
93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = "<group>"; };
|
||||
93D39975CE5AEC99E3F086C7 /* MPSiteCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteCell.h; sourceTree = "<group>"; };
|
||||
93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCoachmarkViewController.h; sourceTree = "<group>"; };
|
||||
93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV2.m; sourceTree = "<group>"; };
|
||||
93D399B36CDB2004D7C51391 /* MPMessageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMessageViewController.m; sourceTree = "<group>"; };
|
||||
93D399C2F3D48E57C4803BDC /* NSPersistentStore+PearlMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSPersistentStore+PearlMigration.m"; sourceTree = "<group>"; };
|
||||
93D399C408B705172FA0FA02 /* PearlLinks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLinks.h; sourceTree = "<group>"; };
|
||||
93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUsersViewController.m; sourceTree = "<group>"; };
|
||||
93D399F244BB522A317811BB /* MPFixable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFixable.h; sourceTree = "<group>"; };
|
||||
93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIResponder+PearlFirstResponder.m"; sourceTree = "<group>"; };
|
||||
@@ -539,9 +558,10 @@
|
||||
93D39B455A71EC98C749E623 /* MPOverlayViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPOverlayViewController.h; sourceTree = "<group>"; };
|
||||
93D39B7B765546B1F1900CB7 /* UIView+Visible.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Visible.h"; sourceTree = "<group>"; };
|
||||
93D39BAA71DE51B4D8A1286C /* MPCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCell.m; sourceTree = "<group>"; };
|
||||
93D39BDA5DB85FCDE4E6450A /* PearlHangDetector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlHangDetector.m; sourceTree = "<group>"; };
|
||||
93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+MPMarkDown.m"; path = "iOS/NSString+MPMarkDown.m"; sourceTree = "<group>"; };
|
||||
93D39C426E03358384018E85 /* MPAnswersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAnswersViewController.m; sourceTree = "<group>"; };
|
||||
93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsSegue.h; sourceTree = "<group>"; };
|
||||
93D39C44361BE57AF0B3071F /* MPSitesSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSitesSegue.h; sourceTree = "<group>"; };
|
||||
93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppSettingsViewController.h; sourceTree = "<group>"; };
|
||||
93D39CB0EABD2748740992D8 /* MPMessageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMessageViewController.h; sourceTree = "<group>"; };
|
||||
93D39CC01630D0421205C4C4 /* MPNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNavigationController.m; sourceTree = "<group>"; };
|
||||
@@ -551,16 +571,18 @@
|
||||
93D39D4E713564B7654341B0 /* mpw-algorithm_v3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v3.c"; sourceTree = "<group>"; };
|
||||
93D39D6604447D7708039155 /* MPAnswersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAnswersViewController.h; sourceTree = "<group>"; };
|
||||
93D39D72239990DDAC2D75B0 /* MPTypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTypes.m; sourceTree = "<group>"; };
|
||||
93D39D8508BF868907E9732E /* PearlHangDetector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlHangDetector.h; sourceTree = "<group>"; };
|
||||
93D39D8A953779B35403AF6E /* PearlUICollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlUICollectionView.m; sourceTree = "<group>"; };
|
||||
93D39DA27D768B53C8B1330C /* MPAvatarCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAvatarCell.h; sourceTree = "<group>"; };
|
||||
93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+PearlAdjustInsets.h"; sourceTree = "<group>"; };
|
||||
93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordCell.m; sourceTree = "<group>"; };
|
||||
93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsSegue.m; sourceTree = "<group>"; };
|
||||
93D39DEA995041A13DC9CAF7 /* MPSiteCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteCell.m; sourceTree = "<group>"; };
|
||||
93D39E7A12CC352B2825AA66 /* MPSitesSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSitesSegue.m; sourceTree = "<group>"; };
|
||||
93D39F556F2F142740A65E59 /* MPWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPWebViewController.h; sourceTree = "<group>"; };
|
||||
93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = "<group>"; };
|
||||
93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = "<group>"; };
|
||||
93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSOrderedSetOrArray.h; sourceTree = "<group>"; };
|
||||
93D39FD9623E8D5571C0AEB3 /* MPAlgorithmV3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV3.m; sourceTree = "<group>"; };
|
||||
93D39FEFC296AF6E7DF8E468 /* PearlLinks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLinks.m; sourceTree = "<group>"; };
|
||||
DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
|
||||
DA071BF1190187FE00179766 /* empty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "empty@2x.png"; sourceTree = "<group>"; };
|
||||
DA071BF2190187FE00179766 /* empty.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = empty.png; sourceTree = "<group>"; };
|
||||
@@ -732,7 +754,6 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -772,10 +793,14 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
DA5A09E9171BB0F7005284AB /* unlocked@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "unlocked@2x.png"; sourceTree = "<group>"; };
|
||||
DA5B0B3E1F36469400B663F0 /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = "<group>"; };
|
||||
DA5B0B3F1F36469400B663F0 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = "<group>"; };
|
||||
DA5BFA44147E415C00F98B1E /* MasterPassword.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MasterPassword.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DA5BFA48147E415C00F98B1E /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||
DA5BFA4A147E415C00F98B1E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
@@ -791,7 +816,6 @@
|
||||
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; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -806,16 +830,16 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -847,7 +871,33 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
DAA449D11EEC4B5800E7BDD5 /* mpw-marshall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-marshall.h"; sourceTree = "<group>"; };
|
||||
DAAC35DD156BD77D00C5FD93 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; };
|
||||
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-marshall-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "mpw-marshall-util.h"; sourceTree = "<group>"; };
|
||||
DAB7AE981F3DDEE000C856B1 /* mpw-marshall-util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-marshall-util.c"; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
DABD36101711E29400CF925C /* ui_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ui_background@2x.png"; sourceTree = "<group>"; };
|
||||
@@ -1461,7 +1511,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -1500,7 +1550,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -1514,7 +1564,7 @@
|
||||
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>"; };
|
||||
DACA29711705E1A8002C6C22 /* ciphers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ciphers.plist; sourceTree = "<group>"; };
|
||||
DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dictionary.lst; sourceTree = "<group>"; };
|
||||
DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = text; path = dictionary.lst; 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>"; };
|
||||
DACA29B51705E2DE002C6C22 /* UIColor+Expanded.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+Expanded.h"; sourceTree = "<group>"; };
|
||||
@@ -1531,7 +1581,6 @@
|
||||
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>"; };
|
||||
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; };
|
||||
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>"; };
|
||||
@@ -1570,16 +1619,12 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
DAFE45FB15039823003ABA7C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Pearl.strings; sourceTree = "<group>"; };
|
||||
DAFE45F815039823003ABA7C /* README */ = {isa = PBXFileReference; fileEncoding = 1; lastKnownFileType = text; path = README; 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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
DAFE460615039823003ABA7C /* README */ = {isa = PBXFileReference; fileEncoding = 1; 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>"; };
|
||||
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>"; };
|
||||
@@ -1590,10 +1635,6 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -1606,7 +1647,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -1635,9 +1676,11 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DAB7AE5D1F3D752900C856B1 /* libjson-c.a in Frameworks */,
|
||||
DA95B50C1C476B6A0067F5EF /* LocalAuthentication.framework in Frameworks */,
|
||||
DA2C3D651BD9612F001137B3 /* libz.tbd in Frameworks */,
|
||||
DA2C3D631BD96126001137B3 /* libc++.tbd in Frameworks */,
|
||||
DAB7AE771F3D755B00C856B1 /* libjson-c.a in Frameworks */,
|
||||
DAA1761B19D86D0D0044227B /* libAttributedMarkdown.a in Frameworks */,
|
||||
DA0CC5421EB57BD4009A8ED9 /* Crashlytics.framework in Frameworks */,
|
||||
DA32D03E19D11293004F3F0E /* libKCOrderedAccessorFix.a in Frameworks */,
|
||||
@@ -1708,12 +1751,18 @@
|
||||
93D39A2239FFFE6BEC83E191 /* C */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA5B0B3E1F36469400B663F0 /* base64.c */,
|
||||
DA5B0B3F1F36469400B663F0 /* base64.h */,
|
||||
93D390A99850139D0FF0211E /* mpw-algorithm_v0.c */,
|
||||
93D396F918E6470DB846C17F /* mpw-algorithm_v1.c */,
|
||||
93D390A3B351FEF1B9EDAB56 /* mpw-algorithm_v2.c */,
|
||||
93D39D4E713564B7654341B0 /* mpw-algorithm_v3.c */,
|
||||
93D3969393A3A46BD27D7078 /* mpw-algorithm.c */,
|
||||
93D3990D850D76A94C6B7A4D /* mpw-algorithm.h */,
|
||||
DAB7AE981F3DDEE000C856B1 /* mpw-marshall-util.c */,
|
||||
DAB7AE971F3DDEE000C856B1 /* mpw-marshall-util.h */,
|
||||
DAA449D01EEC4B5800E7BDD5 /* mpw-marshall.c */,
|
||||
DAA449D11EEC4B5800E7BDD5 /* mpw-marshall.h */,
|
||||
93D392C5A6572DB0EB5B82C8 /* mpw-types.c */,
|
||||
93D39AFD17CBE324D745DAB0 /* mpw-types.h */,
|
||||
93D396C311C3725870343EE0 /* mpw-util.c */,
|
||||
@@ -2000,6 +2049,7 @@
|
||||
DA5BFA47147E415C00F98B1E /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAB7AE5C1F3D752900C856B1 /* libjson-c.a */,
|
||||
DA6701B716406A4100B61001 /* Accounts.framework */,
|
||||
DA6701DF16406BB400B61001 /* AdSupport.framework */,
|
||||
DA5BFA4E147E415C00F98B1E /* CoreData.framework */,
|
||||
@@ -2024,24 +2074,6 @@
|
||||
name = Frameworks;
|
||||
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 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2079,6 +2111,59 @@
|
||||
path = AttributedMarkdown;
|
||||
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 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2885,12 +2970,12 @@
|
||||
93D39CC01630D0421205C4C4 /* MPNavigationController.m */,
|
||||
93D39B455A71EC98C749E623 /* MPOverlayViewController.h */,
|
||||
93D395105935859D71679931 /* MPOverlayViewController.m */,
|
||||
93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */,
|
||||
93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */,
|
||||
93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */,
|
||||
93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */,
|
||||
93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */,
|
||||
93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */,
|
||||
93D39975CE5AEC99E3F086C7 /* MPSiteCell.h */,
|
||||
93D39DEA995041A13DC9CAF7 /* MPSiteCell.m */,
|
||||
93D39C44361BE57AF0B3071F /* MPSitesSegue.h */,
|
||||
93D39E7A12CC352B2825AA66 /* MPSitesSegue.m */,
|
||||
93D3914D7597F9A28DB9D85E /* MPSitesViewController.h */,
|
||||
93D3924EE15017F8A12CB436 /* MPSitesViewController.m */,
|
||||
93D392876BE5C011DE73B43F /* MPPopdownSegue.h */,
|
||||
93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */,
|
||||
DABD3BEA1711E2DC00CF925C /* MPPreferencesViewController.h */,
|
||||
@@ -2927,6 +3012,7 @@
|
||||
DACA22121705DDC5002C6C22 /* External */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAB7AE5E1F3D755B00C856B1 /* libjson-c-ios */,
|
||||
DA0978D81E9A81EE00F0BFE8 /* libsodium-ios */,
|
||||
DAA1759319D86C610044227B /* AttributedMarkdown */,
|
||||
DAFC5662172C57EC00CB5CC5 /* InAppSettingsKit */,
|
||||
@@ -3057,6 +3143,8 @@
|
||||
DAFE45F915039823003ABA7C /* Resources */,
|
||||
93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */,
|
||||
93D39789AAF49338F8AC8B02 /* NSOrderedSetOrArray.m */,
|
||||
93D39BDA5DB85FCDE4E6450A /* PearlHangDetector.m */,
|
||||
93D39D8508BF868907E9732E /* PearlHangDetector.h */,
|
||||
);
|
||||
path = Pearl;
|
||||
sourceTree = "<group>";
|
||||
@@ -3072,15 +3160,12 @@
|
||||
DAFE45FC15039823003ABA7C /* Pearl-Crypto */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA5E5C6317248959003798D8 /* lib */,
|
||||
DAFE45FD15039823003ABA7C /* Pearl-Crypto.h */,
|
||||
DA30E9D115722EE500A68B4C /* Pearl-Crypto.m */,
|
||||
DAFE45FE15039823003ABA7C /* PearlCryptUtils.h */,
|
||||
DAFE45FF15039823003ABA7C /* PearlCryptUtils.m */,
|
||||
DA45711C1F572F1E00D54152 /* PearlCryptUtils.h */,
|
||||
DA45711B1F572F1E00D54152 /* PearlCryptUtils.m */,
|
||||
DAFE460015039823003ABA7C /* PearlKeyChain.h */,
|
||||
DAFE460115039823003ABA7C /* PearlKeyChain.m */,
|
||||
DAFE460215039823003ABA7C /* PearlRSAKey.h */,
|
||||
DAFE460315039823003ABA7C /* PearlRSAKey.m */,
|
||||
DAFE460615039823003ABA7C /* README */,
|
||||
);
|
||||
path = "Pearl-Crypto";
|
||||
@@ -3108,10 +3193,6 @@
|
||||
DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */,
|
||||
DAFE461015039823003ABA7C /* PearlGradientView.h */,
|
||||
DAFE461115039823003ABA7C /* PearlGradientView.m */,
|
||||
DAFE461215039823003ABA7C /* PearlLayout.h */,
|
||||
DAFE461315039823003ABA7C /* PearlLayout.m */,
|
||||
DAFE461415039823003ABA7C /* PearlLayoutView.h */,
|
||||
DAFE461515039823003ABA7C /* PearlLayoutView.m */,
|
||||
DAFE461615039823003ABA7C /* PearlMessageView.h */,
|
||||
DAFE461715039823003ABA7C /* PearlMessageView.m */,
|
||||
DACE2F6919BA6A2A0010F92E /* PearlMutableStaticTableViewController.h */,
|
||||
@@ -3170,6 +3251,8 @@
|
||||
93D392D76C091DEA3319F11D /* UIView+AlphaScale.h */,
|
||||
93D3952910EDB8E0EBC94BA9 /* UIView+Visible.m */,
|
||||
93D39B7B765546B1F1900CB7 /* UIView+Visible.h */,
|
||||
93D39FEFC296AF6E7DF8E468 /* PearlLinks.m */,
|
||||
93D399C408B705172FA0FA02 /* PearlLinks.h */,
|
||||
);
|
||||
path = "Pearl-UIKit";
|
||||
sourceTree = "<group>";
|
||||
@@ -3205,7 +3288,6 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DA25C6B41980D3C50046CDCF /* openssl in Headers */,
|
||||
DAFE4A1315039824003ABA7C /* NSObject+PearlExport.h in Headers */,
|
||||
DAFE4A1515039824003ABA7C /* NSString+PearlNSArrayFormat.h in Headers */,
|
||||
DAFE4A1715039824003ABA7C /* NSString+PearlSEL.h in Headers */,
|
||||
@@ -3227,9 +3309,7 @@
|
||||
DAFE4A2F15039824003ABA7C /* PearlStringUtils.h in Headers */,
|
||||
DA2CA4E018D28859007798F8 /* NSTimer+PearlBlock.h in Headers */,
|
||||
DAFE4A3315039824003ABA7C /* Pearl-Crypto.h in Headers */,
|
||||
DAFE4A3415039824003ABA7C /* PearlCryptUtils.h in Headers */,
|
||||
DAFE4A3615039824003ABA7C /* PearlKeyChain.h in Headers */,
|
||||
DAFE4A3815039824003ABA7C /* PearlRSAKey.h in Headers */,
|
||||
DAE2726419CE9CB3007C5262 /* UITableViewCell+PearlDeque.h in Headers */,
|
||||
DACE2F6E19BA6A2A0010F92E /* UIView+FontScale.h in Headers */,
|
||||
DA32D01B19D046E1004F3F0E /* PearlFixedTableView.h in Headers */,
|
||||
@@ -3241,8 +3321,6 @@
|
||||
DAFE4A4215039824003ABA7C /* PearlBoxView.h in Headers */,
|
||||
DAEFB01F19BCBD9E00525079 /* UIView+LayoutGone.h in Headers */,
|
||||
DAFE4A4415039824003ABA7C /* PearlGradientView.h in Headers */,
|
||||
DAFE4A4615039824003ABA7C /* PearlLayout.h in Headers */,
|
||||
DAFE4A4815039824003ABA7C /* PearlLayoutView.h in Headers */,
|
||||
DAFE4A4A15039824003ABA7C /* PearlMessageView.h in Headers */,
|
||||
DACE2F6619BA6A0A0010F92E /* PearlProfiler.h in Headers */,
|
||||
DAFE4A4C15039824003ABA7C /* PearlRootViewController.h in Headers */,
|
||||
@@ -3280,11 +3358,43 @@
|
||||
93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */,
|
||||
93D3959696396A91961C6148 /* UIView+AlphaScale.h in Headers */,
|
||||
93D39BFB5F5F9337F6565DE3 /* UIView+Visible.h in Headers */,
|
||||
93D39861AEE621B287EA93E9 /* PearlLinks.h in Headers */,
|
||||
93D39359B0DF9823F6C56A05 /* PearlHangDetector.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* 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 */
|
||||
DA32D01F19D111C6004F3F0E /* KCOrderedAccessorFix */ = {
|
||||
isa = PBXNativeTarget;
|
||||
@@ -3308,7 +3418,6 @@
|
||||
buildConfigurationList = DA5BFA6D147E415C00F98B1E /* Build configuration list for PBXNativeTarget "MasterPassword" */;
|
||||
buildPhases = (
|
||||
DA8D88E019DA412A00B189D0 /* Run Script: genassets */,
|
||||
DA0E86A81E9BED2C00F4D60E /* Run Script: libsodium/dist-build/ios.sh */,
|
||||
DA5BFA40147E415C00F98B1E /* Sources */,
|
||||
DA5BFA41147E415C00F98B1E /* Frameworks */,
|
||||
DA5BFA42147E415C00F98B1E /* Resources */,
|
||||
@@ -3318,6 +3427,8 @@
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
DAB7AE4E1F3D618700C856B1 /* PBXTargetDependency */,
|
||||
DAB7AE501F3D618700C856B1 /* PBXTargetDependency */,
|
||||
);
|
||||
name = MasterPassword;
|
||||
productName = MasterPassword;
|
||||
@@ -3379,6 +3490,7 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = DAC77CB7148291A600BCF976 /* Build configuration list for PBXNativeTarget "Pearl" */;
|
||||
buildPhases = (
|
||||
DAB7AE4C1F3D56AD00C856B1 /* ShellScript */,
|
||||
DAC77CAB148291A600BCF976 /* Headers */,
|
||||
DAC77CA9148291A600BCF976 /* Sources */,
|
||||
DAC77CAA148291A600BCF976 /* Frameworks */,
|
||||
@@ -3444,6 +3556,16 @@
|
||||
CreatedOnToolsVersion = 6.0;
|
||||
DevelopmentTeam = HL3Q45LX9N;
|
||||
};
|
||||
DAB7AE3D1F3D464A00C856B1 = {
|
||||
CreatedOnToolsVersion = 8.3.3;
|
||||
DevelopmentTeam = HL3Q45LX9N;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
DAB7AE471F3D468300C856B1 = {
|
||||
CreatedOnToolsVersion = 8.3.3;
|
||||
DevelopmentTeam = HL3Q45LX9N;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
DAC6325C1486805C0075AEA5 = {
|
||||
DevelopmentTeam = HL3Q45LX9N;
|
||||
};
|
||||
@@ -3564,6 +3686,8 @@
|
||||
DAFC5654172C573B00CB5CC5 /* InAppSettingsKit */,
|
||||
DA32D01F19D111C6004F3F0E /* KCOrderedAccessorFix */,
|
||||
DAA1757C19D86BE70044227B /* AttributedMarkdown */,
|
||||
DAB7AE471F3D468300C856B1 /* libsodium-ios */,
|
||||
DAB7AE3D1F3D464A00C856B1 /* libjson-c-ios */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -3759,21 +3883,6 @@
|
||||
/* End PBXResourcesBuildPhase 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 */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -3804,6 +3913,19 @@
|
||||
shellScript = "exec Scripts/genassets";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
DAB7AE4C1F3D56AD00C856B1 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "";
|
||||
};
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Fabric */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -3848,6 +3970,7 @@
|
||||
DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */,
|
||||
DA0CC5941EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m in Sources */,
|
||||
DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */,
|
||||
DAA449D21EEC4B5800E7BDD5 /* mpw-marshall.c in Sources */,
|
||||
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */,
|
||||
DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */,
|
||||
DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */,
|
||||
@@ -3859,14 +3982,15 @@
|
||||
DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */,
|
||||
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */,
|
||||
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */,
|
||||
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */,
|
||||
93D39B8F90F58A5D158DDBA3 /* MPSitesViewController.m in Sources */,
|
||||
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */,
|
||||
93D39392DEDA376F93C6C718 /* MPCell.m in Sources */,
|
||||
DA0CC58E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m in Sources */,
|
||||
93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */,
|
||||
93D39A5FF670957C0AF8298D /* MPSiteCell.m in Sources */,
|
||||
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */,
|
||||
DAB7AE991F3DDEE000C856B1 /* mpw-marshall-util.c in Sources */,
|
||||
DA95B50F1C4776F00067F5EF /* NSMutableSet+Pearl.m in Sources */,
|
||||
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */,
|
||||
93D394B5036C882B33C71872 /* MPSitesSegue.m in Sources */,
|
||||
DA0CC5911EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m in Sources */,
|
||||
93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */,
|
||||
93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */,
|
||||
@@ -3879,12 +4003,14 @@
|
||||
93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */,
|
||||
93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */,
|
||||
93D390C1B93F9D3AE37DD0A5 /* MPAnswersViewController.m in Sources */,
|
||||
DA45711D1F572F1E00D54152 /* PearlCryptUtils.m in Sources */,
|
||||
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */,
|
||||
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */,
|
||||
93D39B429C67A62E29DC02DA /* MPRootSegue.m in Sources */,
|
||||
93D392FD5E2052F7D7DB3774 /* NSString+MPMarkDown.m in Sources */,
|
||||
93D395B715D15F2B56F2A2EE /* mpw-types.c in Sources */,
|
||||
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */,
|
||||
DA5B0B401F36469400B663F0 /* base64.c in Sources */,
|
||||
93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */,
|
||||
93D39E5F7F6D7F5C0FAD090F /* MPTypes.m in Sources */,
|
||||
DA0CC58C1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m in Sources */,
|
||||
@@ -3938,18 +4064,14 @@
|
||||
DAFE4A2B15039824003ABA7C /* PearlObjectUtils.m in Sources */,
|
||||
DAFE4A2E15039824003ABA7C /* PearlStrings.m in Sources */,
|
||||
DAFE4A3015039824003ABA7C /* PearlStringUtils.m in Sources */,
|
||||
DAFE4A3515039824003ABA7C /* PearlCryptUtils.m in Sources */,
|
||||
DAFE4A3715039824003ABA7C /* PearlKeyChain.m in Sources */,
|
||||
DA72BD7B19C1510C00E6ACFE /* UIView+FontScale.m in Sources */,
|
||||
DA250A17195665A100AC23F1 /* UITableView+PearlReloadItems.m in Sources */,
|
||||
DAFE4A3915039824003ABA7C /* PearlRSAKey.m in Sources */,
|
||||
DAFE4A3F15039824003ABA7C /* PearlAlert.m in Sources */,
|
||||
DAFE4A4115039824003ABA7C /* PearlArrayTVC.m in Sources */,
|
||||
DAFE4A4315039824003ABA7C /* PearlBoxView.m in Sources */,
|
||||
DAFE4A4515039824003ABA7C /* PearlGradientView.m in Sources */,
|
||||
DAFE4A4715039824003ABA7C /* PearlLayout.m in Sources */,
|
||||
DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */,
|
||||
DAFE4A4915039824003ABA7C /* PearlLayoutView.m in Sources */,
|
||||
DAE2726319CE9CB3007C5262 /* UITableViewCell+PearlDeque.m in Sources */,
|
||||
DAFE4A4B15039824003ABA7C /* PearlMessageView.m in Sources */,
|
||||
DACE2F6519BA6A0A0010F92E /* PearlProfiler.m in Sources */,
|
||||
@@ -3995,6 +4117,8 @@
|
||||
93D392A33CCE85431E910C7B /* NSOrderedSetOrArray.m in Sources */,
|
||||
93D393AA69A1193401160418 /* UIView+AlphaScale.m in Sources */,
|
||||
93D3942C1B117EE4851AA7B6 /* UIView+Visible.m in Sources */,
|
||||
93D397FCAAC6FA885247A4F9 /* PearlLinks.m in Sources */,
|
||||
93D395373A425B05C86B2268 /* PearlHangDetector.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -4023,6 +4147,19 @@
|
||||
};
|
||||
/* 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 */
|
||||
DA0CC4F51EAB99BA009A8ED9 /* IASKLocalizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
@@ -4146,6 +4283,8 @@
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libsodium/libsodium-ios/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-ios/include\"",
|
||||
"$(inherited)",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
@@ -4184,8 +4323,12 @@
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(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;
|
||||
SKIP_INSTALL = NO;
|
||||
STRIP_INSTALLED_PRODUCT = YES;
|
||||
@@ -4328,6 +4471,8 @@
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libsodium/libsodium-ios/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-ios/include\"",
|
||||
"$(inherited)",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
@@ -4416,6 +4561,8 @@
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/$(PLATFORM_NAME)/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libsodium/libsodium-ios/include\"",
|
||||
"\"$(PROJECT_DIR)/External/libjson-c/libjson-c-ios/include\"",
|
||||
"$(inherited)",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
@@ -4452,8 +4599,12 @@
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(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;
|
||||
SKIP_INSTALL = NO;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
@@ -4482,8 +4633,12 @@
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(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;
|
||||
SKIP_INSTALL = NO;
|
||||
STRIP_INSTALLED_PRODUCT = YES;
|
||||
@@ -4507,6 +4662,128 @@
|
||||
};
|
||||
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 */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@@ -4622,6 +4899,26 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
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" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
@@ -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>
|
||||
@@ -11,6 +11,7 @@
|
||||
"2FE140B36B7D26140DC8D5E5C639DC5900EFCF35" : 9223372036854775807,
|
||||
"4DDCFFD91B41F00326AD14553BD66CFD366ABD91" : 9223372036854775807,
|
||||
"3ED8592497DB6A564366943C9AAD5A46341B5076" : 9223372036854775807,
|
||||
"B38C14663FCFBB7024902D2DB1D013964189DC3B" : 9223372036854775807,
|
||||
"81A28796384A028E6C2D47C039DB8B3E5DD6D0FC" : 9223372036854775807,
|
||||
"F788B28042EDBEF29EFE34687DA79A778C2CC260" : 0
|
||||
},
|
||||
@@ -23,6 +24,7 @@
|
||||
"2FE140B36B7D26140DC8D5E5C639DC5900EFCF35" : "MasterPassword\/platform-darwin\/External\/uicolor-utilities\/",
|
||||
"4DDCFFD91B41F00326AD14553BD66CFD366ABD91" : "MasterPassword\/platform-darwin\/External\/Pearl\/",
|
||||
"3ED8592497DB6A564366943C9AAD5A46341B5076" : "MasterPassword\/platform-darwin\/External\/AttributedMarkdown\/",
|
||||
"B38C14663FCFBB7024902D2DB1D013964189DC3B" : "MasterPassword\/platform-darwin\/External\/libjson-c\/",
|
||||
"81A28796384A028E6C2D47C039DB8B3E5DD6D0FC" : "MasterPassword\/platform-darwin\/External\/libsodium\/",
|
||||
"F788B28042EDBEF29EFE34687DA79A778C2CC260" : "MasterPassword\/"
|
||||
},
|
||||
@@ -70,6 +72,11 @@
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"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",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
|
||||
86
platform-darwin/Scripts/build_libjson-c-ios
Executable file
86
platform-darwin/Scripts/build_libjson-c-ios
Executable 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
|
||||
27
platform-darwin/Scripts/build_libjson-c-osx
Executable file
27
platform-darwin/Scripts/build_libjson-c-osx
Executable 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
|
||||
86
platform-darwin/Scripts/build_libsodium-ios
Executable file
86
platform-darwin/Scripts/build_libsodium-ios
Executable 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 --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 --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 --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 --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 --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
|
||||
28
platform-darwin/Scripts/build_libsodium-osx
Executable file
28
platform-darwin/Scripts/build_libsodium-osx
Executable 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 --prefix="$prefix"
|
||||
make -j3 check
|
||||
make -j3 install
|
||||
)
|
||||
|
||||
# Cleanup
|
||||
make -s distclean
|
||||
@@ -49,52 +49,47 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
|
||||
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc;
|
||||
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit;
|
||||
|
||||
- (NSData *)keyIDForKeyData:(NSData *)keyData;
|
||||
- (NSData *)keyIDForKey:(MPMasterKey)masterKey;
|
||||
- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
||||
|
||||
- (NSString *)nameOfType:(MPSiteType)type;
|
||||
- (NSString *)shortNameOfType:(MPSiteType)type;
|
||||
- (NSString *)classNameOfType:(MPSiteType)type;
|
||||
- (Class)classOfType:(MPSiteType)type;
|
||||
- (NSString *)nameOfType:(MPResultType)type;
|
||||
- (NSString *)shortNameOfType:(MPResultType)type;
|
||||
- (NSString *)classNameOfType:(MPResultType)type;
|
||||
- (Class)classOfType:(MPResultType)type;
|
||||
- (NSArray *)allTypes;
|
||||
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType;
|
||||
- (MPSiteType)defaultType;
|
||||
- (MPSiteType)nextType:(MPSiteType)type;
|
||||
- (MPSiteType)previousType:(MPSiteType)type;
|
||||
- (NSArray *)allTypesStartingWith:(MPResultType)startingType;
|
||||
- (MPResultType)defaultType;
|
||||
- (MPResultType)nextType:(MPResultType)type;
|
||||
- (MPResultType)previousType:(MPResultType)type;
|
||||
|
||||
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key;
|
||||
- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
||||
usingKey:(MPKey *)key;
|
||||
- (NSString *)generateAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key;
|
||||
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
||||
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key;
|
||||
- (NSString *)mpwLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key;
|
||||
- (NSString *)mpwTemplateForSiteNamed:(NSString *)name ofType:(MPResultType)type
|
||||
withCounter:(MPCounterValue)counter usingKey:(MPKey *)key;
|
||||
- (NSString *)mpwAnswerForSiteNamed:(NSString *)name onQuestion:(NSString *)question usingKey:(MPKey *)key;
|
||||
- (NSString *)mpwResultForSiteNamed:(NSString *)name ofType:(MPResultType)type parameter:(NSString *)parameter
|
||||
withCounter:(MPCounterValue)counter variant:(MPKeyPurpose)purpose context:(NSString *)context usingKey:(MPKey *)key;
|
||||
|
||||
- (NSString *)storedLoginForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
|
||||
- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key;
|
||||
- (BOOL)savePassword:(NSString *)clearPassword toSite:(MPSiteEntity *)site usingKey:(MPKey *)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;
|
||||
- (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
|
||||
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)key
|
||||
result:(void ( ^ )(NSString *result))resultBlock;
|
||||
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
|
||||
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key
|
||||
result:(void ( ^ )(NSString *result))resultBlock;
|
||||
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey
|
||||
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)key
|
||||
result:(void ( ^ )(NSString *result))resultBlock;
|
||||
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey
|
||||
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)key
|
||||
result:(void ( ^ )(NSString *result))resultBlock;
|
||||
|
||||
- (void)importProtectedPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey
|
||||
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
||||
- (void)importClearTextPassword:(NSString *)clearPassword intoSite:(MPSiteEntity *)site
|
||||
usingKey:(MPKey *)siteKey;
|
||||
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey;
|
||||
- (void)importPassword:(NSString *)protectedPassword protectedByKey:(MPKey *)importKey
|
||||
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
||||
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key;
|
||||
|
||||
- (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;
|
||||
|
||||
@end
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#define CRACKING_PER_SECOND 2495000000UL
|
||||
#define CRACKING_PRICE 350
|
||||
|
||||
NSOperationQueue *_mpwQueue = nil;
|
||||
static NSOperationQueue *_mpwQueue = nil;
|
||||
|
||||
@implementation MPAlgorithmV0
|
||||
|
||||
@@ -128,143 +128,152 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
__block NSData *keyData;
|
||||
[self mpw_perform:^{
|
||||
NSDate *start = [NSDate date];
|
||||
uint8_t const *masterKeyBytes = mpw_masterKeyForUser( fullName.UTF8String, masterPassword.UTF8String, [self version] );
|
||||
if (masterKeyBytes) {
|
||||
keyData = [NSData dataWithBytes:masterKeyBytes length:MP_dkLen];
|
||||
MPMasterKey masterKey = mpw_masterKey( fullName.UTF8String, masterPassword.UTF8String, [self version] );
|
||||
if (masterKey) {
|
||||
keyData = [NSData dataWithBytes:masterKey length:MPMasterKeySize];
|
||||
trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", //
|
||||
fullName, masterPassword, [self keyIDForKeyData:keyData], -[start timeIntervalSinceNow] );
|
||||
mpw_free( masterKeyBytes, MP_dkLen );
|
||||
fullName, masterPassword, [self keyIDForKey:masterKey], -[start timeIntervalSinceNow] );
|
||||
mpw_free( &masterKey, MPMasterKeySize );
|
||||
}
|
||||
}];
|
||||
|
||||
return keyData;
|
||||
}
|
||||
|
||||
- (NSData *)keyIDForKeyData:(NSData *)keyData {
|
||||
- (NSData *)keyIDForKey:(MPMasterKey)masterKey {
|
||||
|
||||
return [keyData hashWith:PearlHashSHA256];
|
||||
return [[NSData dataWithBytesNoCopy:(void *)masterKey length:MPMasterKeySize] hashWith:PearlHashSHA256];
|
||||
}
|
||||
|
||||
- (NSString *)nameOfType:(MPSiteType)type {
|
||||
- (NSString *)nameOfType:(MPResultType)type {
|
||||
|
||||
if (!type)
|
||||
return nil;
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPResultTypeTemplateMaximum:
|
||||
return @"Maximum Security Password";
|
||||
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPResultTypeTemplateLong:
|
||||
return @"Long Password";
|
||||
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPResultTypeTemplateMedium:
|
||||
return @"Medium Password";
|
||||
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPResultTypeTemplateBasic:
|
||||
return @"Basic Password";
|
||||
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPResultTypeTemplateShort:
|
||||
return @"Short Password";
|
||||
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPResultTypeTemplatePIN:
|
||||
return @"PIN";
|
||||
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPResultTypeTemplateName:
|
||||
return @"Name";
|
||||
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
case MPResultTypeTemplatePhrase:
|
||||
return @"Phrase";
|
||||
|
||||
case MPSiteTypeStoredPersonal:
|
||||
case MPResultTypeStatefulPersonal:
|
||||
return @"Personal Password";
|
||||
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
case MPResultTypeStatefulDevice:
|
||||
return @"Device Private Password";
|
||||
|
||||
case MPResultTypeDeriveKey:
|
||||
return @"Crypto Key";
|
||||
}
|
||||
|
||||
Throw( @"Type not supported: %lu", (long)type );
|
||||
}
|
||||
|
||||
- (NSString *)shortNameOfType:(MPSiteType)type {
|
||||
- (NSString *)shortNameOfType:(MPResultType)type {
|
||||
|
||||
if (!type)
|
||||
return nil;
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPResultTypeTemplateMaximum:
|
||||
return @"Maximum";
|
||||
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPResultTypeTemplateLong:
|
||||
return @"Long";
|
||||
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPResultTypeTemplateMedium:
|
||||
return @"Medium";
|
||||
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPResultTypeTemplateBasic:
|
||||
return @"Basic";
|
||||
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPResultTypeTemplateShort:
|
||||
return @"Short";
|
||||
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPResultTypeTemplatePIN:
|
||||
return @"PIN";
|
||||
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPResultTypeTemplateName:
|
||||
return @"Name";
|
||||
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
case MPResultTypeTemplatePhrase:
|
||||
return @"Phrase";
|
||||
|
||||
case MPSiteTypeStoredPersonal:
|
||||
case MPResultTypeStatefulPersonal:
|
||||
return @"Personal";
|
||||
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
case MPResultTypeStatefulDevice:
|
||||
return @"Device";
|
||||
|
||||
case MPResultTypeDeriveKey:
|
||||
return @"Key";
|
||||
}
|
||||
|
||||
Throw( @"Type not supported: %lu", (long)type );
|
||||
}
|
||||
|
||||
- (NSString *)classNameOfType:(MPSiteType)type {
|
||||
- (NSString *)classNameOfType:(MPResultType)type {
|
||||
|
||||
return NSStringFromClass( [self classOfType:type] );
|
||||
}
|
||||
|
||||
- (Class)classOfType:(MPSiteType)type {
|
||||
- (Class)classOfType:(MPResultType)type {
|
||||
|
||||
if (!type)
|
||||
Throw( @"No type given." );
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPResultTypeTemplateMaximum:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPResultTypeTemplateLong:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPResultTypeTemplateMedium:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPResultTypeTemplateBasic:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPResultTypeTemplateShort:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPResultTypeTemplatePIN:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPResultTypeTemplateName:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
case MPResultTypeTemplatePhrase:
|
||||
return [MPGeneratedSiteEntity class];
|
||||
|
||||
case MPSiteTypeStoredPersonal:
|
||||
case MPResultTypeStatefulPersonal:
|
||||
return [MPStoredSiteEntity class];
|
||||
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
case MPResultTypeStatefulDevice:
|
||||
return [MPStoredSiteEntity class];
|
||||
|
||||
case MPResultTypeDeriveKey:
|
||||
break;
|
||||
}
|
||||
|
||||
Throw( @"Type not supported: %lu", (long)type );
|
||||
@@ -272,13 +281,13 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
|
||||
- (NSArray *)allTypes {
|
||||
|
||||
return [self allTypesStartingWith:MPSiteTypeGeneratedPhrase];
|
||||
return [self allTypesStartingWith:MPResultTypeTemplatePhrase];
|
||||
}
|
||||
|
||||
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType {
|
||||
- (NSArray *)allTypesStartingWith:(MPResultType)startingType {
|
||||
|
||||
NSMutableArray *allTypes = [[NSMutableArray alloc] initWithCapacity:8];
|
||||
MPSiteType currentType = startingType;
|
||||
MPResultType currentType = startingType;
|
||||
do {
|
||||
[allTypes addObject:@(currentType)];
|
||||
} while ((currentType = [self nextType:currentType]) != startingType);
|
||||
@@ -286,199 +295,173 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
return allTypes;
|
||||
}
|
||||
|
||||
- (MPSiteType)defaultType {
|
||||
- (MPResultType)defaultType {
|
||||
|
||||
return MPSiteTypeGeneratedLong;
|
||||
return MPResultTypeTemplateLong;
|
||||
}
|
||||
|
||||
- (MPSiteType)nextType:(MPSiteType)type {
|
||||
- (MPResultType)nextType:(MPResultType)type {
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
return MPSiteTypeGeneratedName;
|
||||
case MPSiteTypeGeneratedName:
|
||||
return MPSiteTypeGeneratedMaximum;
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
return MPSiteTypeGeneratedLong;
|
||||
case MPSiteTypeGeneratedLong:
|
||||
return MPSiteTypeGeneratedMedium;
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
return MPSiteTypeGeneratedBasic;
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
return MPSiteTypeGeneratedShort;
|
||||
case MPSiteTypeGeneratedShort:
|
||||
return MPSiteTypeGeneratedPIN;
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
return MPSiteTypeStoredPersonal;
|
||||
case MPSiteTypeStoredPersonal:
|
||||
return MPSiteTypeStoredDevicePrivate;
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
return MPSiteTypeGeneratedPhrase;
|
||||
case MPResultTypeTemplatePhrase:
|
||||
return MPResultTypeTemplateName;
|
||||
case MPResultTypeTemplateName:
|
||||
return MPResultTypeTemplateMaximum;
|
||||
case MPResultTypeTemplateMaximum:
|
||||
return MPResultTypeTemplateLong;
|
||||
case MPResultTypeTemplateLong:
|
||||
return MPResultTypeTemplateMedium;
|
||||
case MPResultTypeTemplateMedium:
|
||||
return MPResultTypeTemplateBasic;
|
||||
case MPResultTypeTemplateBasic:
|
||||
return MPResultTypeTemplateShort;
|
||||
case MPResultTypeTemplateShort:
|
||||
return MPResultTypeTemplatePIN;
|
||||
case MPResultTypeTemplatePIN:
|
||||
return MPResultTypeStatefulPersonal;
|
||||
case MPResultTypeStatefulPersonal:
|
||||
return MPResultTypeStatefulDevice;
|
||||
case MPResultTypeStatefulDevice:
|
||||
return MPResultTypeTemplatePhrase;
|
||||
case MPResultTypeDeriveKey:
|
||||
break;
|
||||
}
|
||||
|
||||
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)
|
||||
previousType = nextType;
|
||||
|
||||
return previousType;
|
||||
}
|
||||
|
||||
- (NSString *)generateLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key {
|
||||
- (NSString *)mpwLoginForSiteNamed:(NSString *)name usingKey:(MPKey *)key {
|
||||
|
||||
return [self generateContentForSiteNamed:name ofType:MPSiteTypeGeneratedName withCounter:1
|
||||
variant:MPSiteVariantLogin context:nil usingKey:key];
|
||||
return [self mpwResultForSiteNamed:name ofType:MPResultTypeTemplateName parameter:nil withCounter:MPCounterValueInitial
|
||||
variant:MPKeyPurposeIdentification context:nil usingKey:key];
|
||||
}
|
||||
|
||||
- (NSString *)generatePasswordForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
||||
usingKey:(MPKey *)key {
|
||||
- (NSString *)mpwTemplateForSiteNamed:(NSString *)name ofType:(MPResultType)type
|
||||
withCounter:(MPCounterValue)counter usingKey:(MPKey *)key {
|
||||
|
||||
return [self generateContentForSiteNamed:name ofType:type withCounter:counter
|
||||
variant:MPSiteVariantPassword context:nil usingKey:key];
|
||||
return [self mpwResultForSiteNamed:name ofType:type parameter:nil withCounter:counter
|
||||
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
|
||||
variant:MPSiteVariantAnswer context:question usingKey:key];
|
||||
return [self mpwResultForSiteNamed:name ofType:MPResultTypeTemplatePhrase parameter:nil withCounter:MPCounterValueInitial
|
||||
variant:MPKeyPurposeRecovery context:question usingKey:key];
|
||||
}
|
||||
|
||||
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
|
||||
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key {
|
||||
- (NSString *)mpwResultForSiteNamed:(NSString *)name ofType:(MPResultType)type parameter:(NSString *)parameter
|
||||
withCounter:(MPCounterValue)counter variant:(MPKeyPurpose)purpose context:(NSString *)context
|
||||
usingKey:(MPKey *)key {
|
||||
|
||||
__block NSString *content;
|
||||
__block NSString *result = nil;
|
||||
[self mpw_perform:^{
|
||||
char const *contentBytes = mpw_passwordForSite( [key keyDataForAlgorithm:self].bytes,
|
||||
name.UTF8String, type, (uint32_t)counter, variant, context.UTF8String, [self version] );
|
||||
if (contentBytes) {
|
||||
content = [NSString stringWithCString:contentBytes encoding:NSUTF8StringEncoding];
|
||||
mpw_free_string( contentBytes );
|
||||
char const *resultBytes = mpw_siteResult( [key keyForAlgorithm:self],
|
||||
name.UTF8String, counter, purpose, context.UTF8String, type, parameter.UTF8String, [self version] );
|
||||
if (resultBytes) {
|
||||
result = [NSString stringWithCString:resultBytes encoding:NSUTF8StringEncoding];
|
||||
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;
|
||||
}
|
||||
|
||||
- (NSString *)storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:(MPKey *)key {
|
||||
|
||||
return [self decryptContent:site.contentObject usingKey:key];
|
||||
}
|
||||
|
||||
- (BOOL)savePassword:(NSString *)clearContent toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||
switch (site.type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPSiteTypeGeneratedPhrase: {
|
||||
wrn( @"Cannot save content to site with generated type %lu.", (long)site.type );
|
||||
return NO;
|
||||
}
|
||||
|
||||
case MPSiteTypeStoredPersonal: {
|
||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||
(long)site.type, [site class] );
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSData *encryptionKey = [siteKey keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
||||
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
||||
encryptWithSymmetricKey:encryptionKey padding:YES];
|
||||
if ([((MPStoredSiteEntity *)site).contentObject isEqualToData:encryptedContent])
|
||||
return NO;
|
||||
|
||||
((MPStoredSiteEntity *)site).contentObject = encryptedContent;
|
||||
return YES;
|
||||
}
|
||||
case MPSiteTypeStoredDevicePrivate: {
|
||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||
(long)site.type, [site class] );
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSData *encryptionKey = [siteKey keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
||||
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
|
||||
encryptWithSymmetricKey:encryptionKey padding:YES];
|
||||
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
||||
if (!encryptedContent)
|
||||
[PearlKeyChain deleteItemForQuery:siteQuery];
|
||||
else
|
||||
[PearlKeyChain addOrUpdateItemForQuery:siteQuery withAttributes:@{
|
||||
(__bridge id)kSecValueData : encryptedContent,
|
||||
#if TARGET_OS_IPHONE
|
||||
(__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
||||
#endif
|
||||
}];
|
||||
((MPStoredSiteEntity *)site).contentObject = nil;
|
||||
return YES;
|
||||
}
|
||||
if (!(site.type & MPResultTypeClassStateful)) {
|
||||
wrn( @"Can only save content to site with a stateful type: %lu.", (long)site.type );
|
||||
return NO;
|
||||
}
|
||||
|
||||
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)) {
|
||||
[self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
[self resolveLoginForSite:site usingKey:key result:^(NSString *result_) {
|
||||
setResult( result_ );
|
||||
}];
|
||||
} );
|
||||
}
|
||||
|
||||
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
|
||||
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
[self resolvePasswordForSite:site usingKey:key result:^(NSString *result_) {
|
||||
setResult( result_ );
|
||||
}];
|
||||
} );
|
||||
}
|
||||
|
||||
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
|
||||
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
[self resolveAnswerForSite:site usingKey:key result:^(NSString *result_) {
|
||||
setResult( result_ );
|
||||
}];
|
||||
} );
|
||||
}
|
||||
|
||||
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey {
|
||||
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)key {
|
||||
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) {
|
||||
[self resolveAnswerForQuestion:question usingKey:key result:^(NSString *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;
|
||||
BOOL loginGenerated = site.loginGenerated && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateLogins];
|
||||
NSString *loginName = site.loginName;
|
||||
id<MPAlgorithm> algorithm = nil;
|
||||
if (!name.length)
|
||||
err( @"Missing name." );
|
||||
else if (!siteKey)
|
||||
else if (!key)
|
||||
err( @"Missing key." );
|
||||
else
|
||||
algorithm = site.algorithm;
|
||||
@@ -487,244 +470,144 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
resultBlock( loginName );
|
||||
else
|
||||
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) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPSiteTypeGeneratedPhrase: {
|
||||
case MPResultTypeTemplateMaximum:
|
||||
case MPResultTypeTemplateLong:
|
||||
case MPResultTypeTemplateMedium:
|
||||
case MPResultTypeTemplateBasic:
|
||||
case MPResultTypeTemplateShort:
|
||||
case MPResultTypeTemplatePIN:
|
||||
case MPResultTypeTemplateName:
|
||||
case MPResultTypeTemplatePhrase: {
|
||||
if (![site isKindOfClass:[MPGeneratedSiteEntity class]]) {
|
||||
wrn( @"Site with generated type %lu is not an MPGeneratedSiteEntity, but a %@.",
|
||||
(long)site.type, [site class] );
|
||||
break;
|
||||
}
|
||||
|
||||
NSString *name = site.name;
|
||||
MPSiteType type = site.type;
|
||||
NSUInteger counter = ((MPGeneratedSiteEntity *)site).counter;
|
||||
id<MPAlgorithm> algorithm = nil;
|
||||
if (!site.name.length)
|
||||
err( @"Missing name." );
|
||||
else if (!siteKey)
|
||||
err( @"Missing key." );
|
||||
else
|
||||
algorithm = site.algorithm;
|
||||
MPCounterValue counter = ((MPGeneratedSiteEntity *)site).counter;
|
||||
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey] );
|
||||
resultBlock( [algorithm mpwTemplateForSiteNamed:name ofType:type withCounter:counter usingKey:key] );
|
||||
} );
|
||||
break;
|
||||
}
|
||||
|
||||
case MPSiteTypeStoredPersonal: {
|
||||
case MPResultTypeStatefulPersonal:
|
||||
case MPResultTypeStatefulDevice: {
|
||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||
(long)site.type, [site class] );
|
||||
break;
|
||||
}
|
||||
|
||||
NSData *encryptedContent = ((MPStoredSiteEntity *)site).contentObject;
|
||||
NSDictionary *siteQuery = [self queryForSite:site];
|
||||
NSData *state = [PearlKeyChain dataOfItemForQuery:siteQuery];
|
||||
state = state?: ((MPStoredSiteEntity *)site).contentObject;
|
||||
|
||||
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;
|
||||
}
|
||||
case MPSiteTypeStoredDevicePrivate: {
|
||||
NSAssert( [site isKindOfClass:[MPStoredSiteEntity class]],
|
||||
@"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.", (long)site.type,
|
||||
[site class] );
|
||||
|
||||
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
||||
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:siteQuery];
|
||||
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [self decryptContent:encryptedContent usingKey:siteKey] );
|
||||
} );
|
||||
case MPResultTypeDeriveKey:
|
||||
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;
|
||||
id<MPAlgorithm> algorithm = nil;
|
||||
if (!site.name.length)
|
||||
err( @"Missing name." );
|
||||
else if (!siteKey)
|
||||
else if (!key)
|
||||
err( @"Missing key." );
|
||||
else
|
||||
algorithm = site.algorithm;
|
||||
|
||||
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 {
|
||||
|
||||
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." );
|
||||
NSString *name = question.site.name;
|
||||
NSString *keyword = question.keyword;
|
||||
id<MPAlgorithm> algorithm = nil;
|
||||
if (!name.length)
|
||||
err( @"Missing name." );
|
||||
else if (!siteKey)
|
||||
else if (!key)
|
||||
err( @"Missing key." );
|
||||
else if ([[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateAnswers])
|
||||
algorithm = question.site.algorithm;
|
||||
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [algorithm generateAnswerForSiteNamed:name onQuestion:keyword usingKey:siteKey] );
|
||||
resultBlock( [algorithm mpwAnswerForSiteNamed:name onQuestion:keyword usingKey:key] );
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)importProtectedPassword:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
|
||||
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
- (void)importPassword:(NSString *)cipherText protectedByKey:(MPKey *)importKey
|
||||
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)key {
|
||||
|
||||
NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||
switch (site.type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
break;
|
||||
|
||||
case MPSiteTypeStoredPersonal: {
|
||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||
(long)site.type, [site class] );
|
||||
break;
|
||||
}
|
||||
if ([[importKey keyIDForAlgorithm:self] isEqualToData:[siteKey keyIDForAlgorithm:self]])
|
||||
((MPStoredSiteEntity *)site).contentObject = [protectedContent decodeBase64];
|
||||
|
||||
else {
|
||||
NSString *clearContent = [self decryptContent:[protectedContent decodeBase64] usingKey:importKey];
|
||||
[self importClearTextPassword:clearContent intoSite:site usingKey:siteKey];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
break;
|
||||
NSAssert( [[key keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
|
||||
if (cipherText && cipherText.length && site.type & MPResultTypeClassStateful) {
|
||||
NSString *plainText = [self mpwResultForSiteNamed:site.name ofType:site.type parameter:cipherText
|
||||
withCounter:MPCounterValueInitial variant:MPKeyPurposeAuthentication context:nil
|
||||
usingKey:importKey];
|
||||
if (plainText)
|
||||
[self savePassword:plainText toSite:site usingKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
- (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." );
|
||||
switch (site.type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
break;
|
||||
|
||||
case MPSiteTypeStoredPersonal: {
|
||||
[self savePassword:clearContent toSite:site usingKey:siteKey];
|
||||
break;
|
||||
}
|
||||
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
break;
|
||||
}
|
||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword attributes:@{
|
||||
(__bridge id)kSecAttrService: site.type & MPSiteFeatureDevicePrivate? @"DevicePrivate": @"Private",
|
||||
(__bridge id)kSecAttrAccount: site.name
|
||||
} matches:nil];
|
||||
}
|
||||
|
||||
- (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))
|
||||
return nil;
|
||||
|
||||
NSString *result = nil;
|
||||
switch (site.type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPSiteTypeGeneratedPhrase: {
|
||||
result = nil;
|
||||
break;
|
||||
}
|
||||
|
||||
case MPSiteTypeStoredPersonal: {
|
||||
if (![site isKindOfClass:[MPStoredSiteEntity class]]) {
|
||||
wrn( @"Site with stored type %lu is not an MPStoredSiteEntity, but a %@.",
|
||||
(long)site.type, [site class] );
|
||||
break;
|
||||
}
|
||||
result = [((MPStoredSiteEntity *)site).contentObject encodeBase64];
|
||||
break;
|
||||
}
|
||||
|
||||
case MPSiteTypeStoredDevicePrivate: {
|
||||
result = nil;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
NSDictionary *siteQuery = [self queryForSite:site];
|
||||
NSData *state = [PearlKeyChain dataOfItemForQuery:siteQuery];
|
||||
return [state?: ((MPStoredSiteEntity *)site).contentObject encodeBase64];
|
||||
}
|
||||
|
||||
- (BOOL)migrateExplicitly:(BOOL)explicit {
|
||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPResultType)type byAttacker:(MPAttacker)attacker {
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSDictionary *)queryForDevicePrivateSiteNamed:(NSString *)name {
|
||||
|
||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
||||
attributes:@{
|
||||
(__bridge id)kSecAttrService: @"DevicePrivate",
|
||||
(__bridge id)kSecAttrAccount: name
|
||||
}
|
||||
matches:nil];
|
||||
}
|
||||
|
||||
- (NSString *)decryptContent:(NSData *)encryptedContent usingKey:(MPKey *)key {
|
||||
|
||||
if (!key)
|
||||
return nil;
|
||||
NSData *decryptedContent = nil;
|
||||
if ([encryptedContent length]) {
|
||||
NSData *encryptionKey = [key keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
|
||||
decryptedContent = [encryptedContent decryptWithSymmetricKey:encryptionKey padding:YES];
|
||||
}
|
||||
if (!decryptedContent)
|
||||
return nil;
|
||||
|
||||
return [[NSString alloc] initWithBytes:decryptedContent.bytes length:decryptedContent.length encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker {
|
||||
|
||||
if (!(type & MPSiteTypeClassGenerated))
|
||||
if (!(type & MPResultTypeClassTemplate))
|
||||
return NO;
|
||||
size_t count = 0;
|
||||
const char **templates = mpw_templatesForType( type, &count );
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
return NO;
|
||||
|
||||
if (!explicit) {
|
||||
if (site.type & MPSiteTypeClassGenerated) {
|
||||
if (site.type & MPResultTypeClassTemplate) {
|
||||
// This migration requires explicit permission for types of the generated class.
|
||||
site.requiresExplicitMigration = YES;
|
||||
return NO;
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
return NO;
|
||||
|
||||
if (!explicit) {
|
||||
if (site.type & MPSiteTypeClassGenerated && site.name.length != [site.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
||||
if (site.type & MPResultTypeClassTemplate && site.name.length != [site.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
||||
// This migration requires explicit permission for types of the generated class.
|
||||
site.requiresExplicitMigration = YES;
|
||||
return NO;
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
return NO;
|
||||
|
||||
if (!explicit) {
|
||||
if (site.type & MPSiteTypeClassGenerated &&
|
||||
if (site.type & MPResultTypeClassTemplate &&
|
||||
site.user.name.length != [site.user.name dataUsingEncoding:NSUTF8StringEncoding].length) {
|
||||
// This migration requires explicit permission for types of the generated class.
|
||||
site.requiresExplicitMigration = YES;
|
||||
|
||||
@@ -104,14 +104,33 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
if (![[MPAppDelegate_Shared get] canMakePayments]) {
|
||||
[PearlAlert showAlertWithTitle:@"Store Not Set Up" message:
|
||||
@"Try logging using the App Store or from Settings."
|
||||
[PearlAlert showAlertWithTitle:@"App Store Not Set Up" message:
|
||||
@"Make sure your Apple ID is set under Settings -> iTunes & App Store, "
|
||||
@"you have a payment method added to the account and purchases are"
|
||||
@"not disabled under General -> Restrictions."
|
||||
viewStyle:UIAlertViewStyleDefault initAlert:nil
|
||||
tappedButtonBlock:nil cancelTitle:@"Thanks" otherTitles:nil];
|
||||
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||
if (buttonIndex == alert.cancelButtonIndex)
|
||||
// Cancel
|
||||
return;
|
||||
if (buttonIndex == alert.firstOtherButtonIndex) {
|
||||
// Settings
|
||||
[PearlLinks openSettingsStore];
|
||||
return;
|
||||
}
|
||||
|
||||
// Try Anyway
|
||||
[self performPurchaseProductWithIdentifier:productIdentifier quantity:quantity];
|
||||
} cancelTitle:@"Cancel" otherTitles:@"Settings", @"Try Anyway", nil];
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
[self performPurchaseProductWithIdentifier:productIdentifier quantity:quantity];
|
||||
}
|
||||
|
||||
- (void)performPurchaseProductWithIdentifier:(NSString *)productIdentifier quantity:(NSInteger)quantity {
|
||||
|
||||
for (SKProduct *product in [self.products allValues])
|
||||
if ([product.productIdentifier isEqualToString:productIdentifier]) {
|
||||
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
|
||||
|
||||
@@ -95,13 +95,14 @@
|
||||
- (void)storeSavedKeyFor:(MPUserEntity *)user {
|
||||
|
||||
if (user.saveKey) {
|
||||
NSData *keyData = [self.key keyDataForAlgorithm:user.algorithm];
|
||||
if (keyData) {
|
||||
MPMasterKey masterKey = [self.key keyForAlgorithm:user.algorithm];
|
||||
if (masterKey) {
|
||||
[self forgetSavedKeyFor:user];
|
||||
|
||||
inf( @"Saving key in keychain for user: %@", user.userID );
|
||||
[PearlKeyChain addOrUpdateItemForQuery:[self createKeyQueryforUser:user origin:nil]
|
||||
withAttributes:@{ (__bridge id)kSecValueData: keyData }];
|
||||
[PearlKeyChain addOrUpdateItemForQuery:[self createKeyQueryforUser:user origin:nil] withAttributes:@{
|
||||
(__bridge id)kSecValueData: [NSData dataWithBytesNoCopy:(void *)masterKey length:MPMasterKeySize]
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,9 +248,9 @@
|
||||
#endif
|
||||
|
||||
for (MPSiteEntity *site in user.sites) {
|
||||
if (site.type & MPSiteTypeClassStored) {
|
||||
if (site.type & MPResultTypeClassStateful) {
|
||||
NSString *content;
|
||||
while (!(content = [site.algorithm storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) {
|
||||
while (!(content = [site.algorithm resolvePasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) {
|
||||
// Failed to decrypt site with the current recoveryKey. Ask user for a new one to use.
|
||||
NSString *masterPassword = nil;
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
#import "MPEntities.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
@interface MPAppDelegate_Shared : PearlAppDelegate
|
||||
|
||||
#else
|
||||
|
||||
@interface MPAppDelegate_Shared : NSObject<PearlConfigDelegate>
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
}
|
||||
|
||||
- (void)handleCoordinatorError:(NSError *)error {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -20,14 +20,6 @@
|
||||
|
||||
#import "MPFixable.h"
|
||||
|
||||
typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
||||
MPImportResultSuccess,
|
||||
MPImportResultCancelled,
|
||||
MPImportResultInvalidPassword,
|
||||
MPImportResultMalformedInput,
|
||||
MPImportResultInternalError,
|
||||
};
|
||||
|
||||
@interface MPAppDelegate_Shared(Store)
|
||||
|
||||
+ (NSManagedObjectContext *)managedObjectContextForMainThreadIfReady;
|
||||
@@ -42,10 +34,13 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
||||
|
||||
/** @param completion The block to execute after adding the site, executed from the main thread with the new site in the main MOC. */
|
||||
- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion;
|
||||
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type;
|
||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
||||
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPResultType)type;
|
||||
- (void)importSites:(NSString *)importData
|
||||
askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
|
||||
askUserPassword:(NSString *( ^ )(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword;
|
||||
- (NSString *)exportSitesRevealPasswords:(BOOL)revealPasswords;
|
||||
askUserPassword:(NSString *( ^ )(NSString *userName))userPassword
|
||||
result:(void ( ^ )(NSError *error))resultBlock;
|
||||
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
|
||||
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
||||
result:(void ( ^ )(NSString *mpsites, NSError *error))resultBlock;
|
||||
|
||||
@end
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
//==============================================================================
|
||||
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "mpw-marshall.h"
|
||||
#import "mpw-util.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#define STORE_OPTIONS NSPersistentStoreFileProtectionKey : NSFileProtectionComplete,
|
||||
@@ -488,7 +490,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
return;
|
||||
}
|
||||
|
||||
MPSiteType type = activeUser.defaultType;
|
||||
MPResultType type = activeUser.defaultType;
|
||||
id<MPAlgorithm> algorithm = MPAlgorithmDefault;
|
||||
Class entityType = [algorithm classOfType:type];
|
||||
|
||||
@@ -505,7 +507,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
}];
|
||||
}
|
||||
|
||||
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPSiteType)type {
|
||||
- (MPSiteEntity *)changeSite:(MPSiteEntity *)site saveInContext:(NSManagedObjectContext *)context toType:(MPResultType)type {
|
||||
|
||||
if (site.type == type)
|
||||
return site;
|
||||
@@ -538,370 +540,214 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
return site;
|
||||
}
|
||||
|
||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
||||
askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
|
||||
askUserPassword:(NSString *( ^ )(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))userPassword {
|
||||
- (void)importSites:(NSString *)importData
|
||||
askImportPassword:(NSString *( ^ )(NSString *userName))importPassword
|
||||
askUserPassword:(NSString *( ^ )(NSString *userName))userPassword
|
||||
result:(void ( ^ )(NSError *error))resultBlock {
|
||||
|
||||
NSAssert( ![[NSThread currentThread] isMainThread], @"This method should not be invoked from the main thread." );
|
||||
|
||||
__block MPImportResult result = MPImportResultCancelled;
|
||||
do {
|
||||
if ([MPAppDelegate_Shared managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
||||
result = [self importSites:importedSitesString askImportPassword:importPassword askUserPassword:userPassword
|
||||
saveInContext:context];
|
||||
NSError *error = [self importSites:importData askImportPassword:importPassword askUserPassword:userPassword
|
||||
saveInContext:context];
|
||||
PearlMainQueue( ^{
|
||||
resultBlock( error );
|
||||
} );
|
||||
}])
|
||||
break;
|
||||
usleep( (useconds_t)(USEC_PER_SEC * 0.2) );
|
||||
} while (YES);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (MPImportResult)importSites:(NSString *)importedSitesString
|
||||
askImportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
||||
askUserPassword:(NSString *( ^ )(NSString *userName, NSUInteger importCount, NSUInteger deleteCount))askUserPassword
|
||||
saveInContext:(NSManagedObjectContext *)context {
|
||||
- (NSError *)importSites:(NSString *)importData
|
||||
askImportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
||||
askUserPassword:(NSString *( ^ )(NSString *userName))askUserPassword
|
||||
saveInContext:(NSManagedObjectContext *)context {
|
||||
|
||||
// Compile patterns.
|
||||
static NSRegularExpression *headerPattern;
|
||||
static NSArray *sitePatterns;
|
||||
NSError *error = nil;
|
||||
if (!headerPattern) {
|
||||
headerPattern = [[NSRegularExpression alloc]
|
||||
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
|
||||
options:(NSRegularExpressionOptions)0 error:&error];
|
||||
if (error) {
|
||||
MPError( error, @"Error loading the header pattern." );
|
||||
return MPImportResultInternalError;
|
||||
// Read metadata for the import file.
|
||||
MPMarshallInfo *info = mpw_marshall_read_info( importData.UTF8String );
|
||||
if (info->format == MPMarshallFormatNone)
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshallCode userInfo:@{
|
||||
@"type" : @(MPMarshallErrorFormat),
|
||||
NSLocalizedDescriptionKey: @"This is not a Master Password import file.",
|
||||
}]), @"While importing sites." );
|
||||
|
||||
// Get master password for import file.
|
||||
MPKey *importKey;
|
||||
NSString *importMasterPassword;
|
||||
do {
|
||||
importMasterPassword = askImportPassword( @(info->fullName) );
|
||||
if (!importMasterPassword) {
|
||||
inf( @"Import cancelled." );
|
||||
mpw_marshal_info_free( &info );
|
||||
return MPError( ([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]), @"" );
|
||||
}
|
||||
}
|
||||
if (!sitePatterns) {
|
||||
sitePatterns = @[
|
||||
[[NSRegularExpression alloc] // Format 0
|
||||
initWithPattern:@"^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)? +([^\t]+)\t(.*)"
|
||||
options:(NSRegularExpressionOptions)0 error:&error],
|
||||
[[NSRegularExpression alloc] // Format 1
|
||||
initWithPattern:@"^([^ ]+) +([[:digit:]]+) +([[:digit:]]+)(:[[:digit:]]+)?(:[[:digit:]]+)? +([^\t]*)\t *([^\t]+)\t(.*)"
|
||||
options:(NSRegularExpressionOptions)0 error:&error]
|
||||
];
|
||||
if (error) {
|
||||
MPError( error, @"Error loading the site patterns." );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
|
||||
importKey = [[MPKey alloc] initForFullName:@(info->fullName) withMasterPassword:importMasterPassword];
|
||||
} while ([[[importKey keyIDForAlgorithm:MPAlgorithmForVersion( info->algorithm )] encodeHex]
|
||||
caseInsensitiveCompare:@(info->keyID)] != NSOrderedSame);
|
||||
|
||||
// Parse import data.
|
||||
inf( @"Importing sites." );
|
||||
NSUInteger importFormat = 0;
|
||||
__block MPUserEntity *user = nil;
|
||||
NSUInteger importAvatar = NSNotFound;
|
||||
NSData *importKeyID = nil;
|
||||
NSString *importBundleVersion = nil, *importUserName = nil;
|
||||
id<MPAlgorithm> importAlgorithm = nil;
|
||||
MPSiteType importDefaultType = (MPSiteType)0;
|
||||
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
|
||||
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
||||
NSMutableSet *sitesToDelete = [NSMutableSet set];
|
||||
NSMutableArray *importedSiteSites = [NSMutableArray arrayWithCapacity:[importedSiteLines count]];
|
||||
NSFetchRequest *siteFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
for (NSString *importedSiteLine in importedSiteLines) {
|
||||
if ([importedSiteLine hasPrefix:@"#"]) {
|
||||
// Comment or header
|
||||
if (!headerStarted) {
|
||||
if ([importedSiteLine isEqualToString:@"##"])
|
||||
headerStarted = YES;
|
||||
continue;
|
||||
}
|
||||
if (headerEnded)
|
||||
continue;
|
||||
if ([importedSiteLine isEqualToString:@"##"]) {
|
||||
headerEnded = YES;
|
||||
continue;
|
||||
MPMarshallError importError = { .type = MPMarshallSuccess };
|
||||
MPMarshalledUser *importUser = mpw_marshall_read( importData.UTF8String, info->format, importMasterPassword.UTF8String, &importError );
|
||||
mpw_marshal_info_free( &info );
|
||||
|
||||
@try {
|
||||
if (!importUser || importError.type != MPMarshallSuccess)
|
||||
return MPError( ([NSError errorWithDomain:MPErrorDomain code:MPErrorMarshallCode userInfo:@{
|
||||
@"type" : @(importError.type),
|
||||
NSLocalizedDescriptionKey: @(importError.description),
|
||||
}]), @"While importing sites." );
|
||||
|
||||
// Find an existing user to update.
|
||||
NSError *error = nil;
|
||||
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", @(importUser->fullName)];
|
||||
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
|
||||
if (!users)
|
||||
return MPError( error, @"While looking for user: %@.", @(importUser->fullName) );
|
||||
if ([users count] > 1)
|
||||
return MPMakeError( @"While looking for user: %@, found more than one: %zu",
|
||||
@(importUser->fullName), (size_t)[users count] );
|
||||
|
||||
// Get master key for user.
|
||||
MPUserEntity *user = [users lastObject];
|
||||
MPKey *userKey = importKey;
|
||||
while (user && ![[userKey keyIDForAlgorithm:user.algorithm] isEqualToData:user.keyID]) {
|
||||
NSString *userMasterPassword = askUserPassword( user.name );
|
||||
if (!userMasterPassword) {
|
||||
inf( @"Import cancelled." );
|
||||
return MPError( ([NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]), @"" );
|
||||
}
|
||||
|
||||
// Header
|
||||
if ([headerPattern numberOfMatchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||
range:NSMakeRange( 0, [importedSiteLine length] )] != 1) {
|
||||
err( @"Invalid header format in line: %@", importedSiteLine );
|
||||
return MPImportResultMalformedInput;
|
||||
}
|
||||
NSTextCheckingResult *headerSites = [[headerPattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
|
||||
NSString *headerName = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:1]];
|
||||
NSString *headerValue = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:2]];
|
||||
|
||||
if ([headerName isEqualToString:@"Format"]) {
|
||||
importFormat = (NSUInteger)[headerValue integerValue];
|
||||
if (importFormat >= [sitePatterns count]) {
|
||||
err( @"Unsupported import format: %lu", (unsigned long)importFormat );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
if (([headerName isEqualToString:@"User Name"] || [headerName isEqualToString:@"Full Name"]) && !importUserName) {
|
||||
importUserName = headerValue;
|
||||
|
||||
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
|
||||
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
|
||||
if (!users) {
|
||||
MPError( error, @"While looking for user: %@.", importUserName );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
if ([users count] > 1) {
|
||||
err( @"While looking for user: %@, found more than one: %lu", importUserName, (unsigned long)[users count] );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
|
||||
user = [users lastObject];
|
||||
dbg( @"Existing user? %@", [user debugDescription] );
|
||||
}
|
||||
if ([headerName isEqualToString:@"Avatar"])
|
||||
importAvatar = (NSUInteger)[headerValue integerValue];
|
||||
if ([headerName isEqualToString:@"Key ID"])
|
||||
importKeyID = [headerValue decodeHex];
|
||||
if ([headerName isEqualToString:@"Version"]) {
|
||||
importBundleVersion = headerValue;
|
||||
importAlgorithm = MPAlgorithmDefaultForBundleVersion( importBundleVersion );
|
||||
}
|
||||
if ([headerName isEqualToString:@"Algorithm"])
|
||||
importAlgorithm = MPAlgorithmForVersion( (MPAlgorithmVersion)[headerValue integerValue] );
|
||||
if ([headerName isEqualToString:@"Default Type"])
|
||||
importDefaultType = (MPSiteType)[headerValue integerValue];
|
||||
if ([headerName isEqualToString:@"Passwords"]) {
|
||||
if ([headerValue isEqualToString:@"VISIBLE"])
|
||||
clearText = YES;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
if (!headerEnded)
|
||||
continue;
|
||||
if (![importUserName length])
|
||||
return MPImportResultMalformedInput;
|
||||
if (![importedSiteLine length])
|
||||
continue;
|
||||
|
||||
// Site
|
||||
NSRegularExpression *sitePattern = sitePatterns[importFormat];
|
||||
if ([sitePattern numberOfMatchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||
range:NSMakeRange( 0, [importedSiteLine length] )] != 1) {
|
||||
err( @"Invalid site format in line: %@", importedSiteLine );
|
||||
return MPImportResultMalformedInput;
|
||||
}
|
||||
NSTextCheckingResult *siteElements = [[sitePattern matchesInString:importedSiteLine options:(NSMatchingOptions)0
|
||||
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
|
||||
NSString *lastUsed, *uses, *type, *version, *counter, *siteName, *loginName, *exportContent;
|
||||
switch (importFormat) {
|
||||
case 0:
|
||||
lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]];
|
||||
uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
|
||||
type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]];
|
||||
version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]];
|
||||
if ([version length])
|
||||
version = [version substringFromIndex:1]; // Strip the leading colon.
|
||||
counter = @"";
|
||||
loginName = @"";
|
||||
siteName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]];
|
||||
exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]];
|
||||
break;
|
||||
case 1:
|
||||
lastUsed = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:1]];
|
||||
uses = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:2]];
|
||||
type = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:3]];
|
||||
version = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:4]];
|
||||
if ([version length])
|
||||
version = [version substringFromIndex:1]; // Strip the leading colon.
|
||||
counter = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:5]];
|
||||
if ([counter length])
|
||||
counter = [counter substringFromIndex:1]; // Strip the leading colon.
|
||||
loginName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:6]];
|
||||
siteName = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:7]];
|
||||
exportContent = [importedSiteLine substringWithRange:[siteElements rangeAtIndex:8]];
|
||||
break;
|
||||
default:
|
||||
err( @"Unexpected import format: %lu", (unsigned long)importFormat );
|
||||
return MPImportResultInternalError;
|
||||
userKey = [[MPKey alloc] initForFullName:@(importUser->fullName) withMasterPassword:userMasterPassword];
|
||||
}
|
||||
|
||||
// Find existing site.
|
||||
if (user) {
|
||||
siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
|
||||
NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
|
||||
if (!existingSites) {
|
||||
MPError( error, @"Lookup of existing sites failed for site: %@, user: %@.", siteName, user.userID );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
if ([existingSites count]) {
|
||||
dbg( @"Existing sites: %@", existingSites );
|
||||
[sitesToDelete addObjectsFromArray:existingSites];
|
||||
}
|
||||
// Update or create user.
|
||||
if (!user) {
|
||||
user = [MPUserEntity insertNewObjectInContext:context];
|
||||
user.name = @(importUser->fullName);
|
||||
}
|
||||
[importedSiteSites addObject:@[ lastUsed, uses, type, version, counter, loginName, siteName, exportContent ]];
|
||||
dbg( @"Will import site: lastUsed=%@, uses=%@, type=%@, version=%@, counter=%@, loginName=%@, siteName=%@, exportContent=%@",
|
||||
lastUsed, uses, type, version, counter, loginName, siteName, exportContent );
|
||||
}
|
||||
|
||||
// Ask for confirmation to import these sites and the master password of the user.
|
||||
inf( @"Importing %lu sites, deleting %lu sites, for user: %@", (unsigned long)[importedSiteSites count],
|
||||
(unsigned long)[sitesToDelete count], [MPUserEntity idFor:importUserName] );
|
||||
NSString *userMasterPassword = askUserPassword( user? user.name: importUserName, [importedSiteSites count],
|
||||
[sitesToDelete count] );
|
||||
if (!userMasterPassword) {
|
||||
inf( @"Import cancelled." );
|
||||
return MPImportResultCancelled;
|
||||
}
|
||||
MPKey *userKey = [[MPKey alloc] initForFullName:user? user.name: importUserName withMasterPassword:userMasterPassword];
|
||||
if (user && ![[userKey keyIDForAlgorithm:user.algorithm] isEqualToData:user.keyID])
|
||||
return MPImportResultInvalidPassword;
|
||||
__block MPKey *importKey = userKey;
|
||||
if (importKeyID && ![[importKey keyIDForAlgorithm:importAlgorithm] isEqualToData:importKeyID])
|
||||
importKey = [[MPKey alloc] initForFullName:importUserName withMasterPassword:askImportPassword( importUserName )];
|
||||
if (importKeyID && ![[importKey keyIDForAlgorithm:importAlgorithm] isEqualToData:importKeyID])
|
||||
return MPImportResultInvalidPassword;
|
||||
|
||||
// Delete existing sites.
|
||||
if (sitesToDelete.count)
|
||||
[sitesToDelete enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
|
||||
inf( @"Deleting site: %@, it will be replaced by an imported site.", [obj name] );
|
||||
[context deleteObject:obj];
|
||||
}];
|
||||
|
||||
// Make sure there is a user.
|
||||
if (user) {
|
||||
if (importAvatar != NSNotFound)
|
||||
user.avatar = importAvatar;
|
||||
if (importDefaultType)
|
||||
user.defaultType = importDefaultType;
|
||||
dbg( @"Updating User: %@", [user debugDescription] );
|
||||
}
|
||||
else {
|
||||
user = [MPUserEntity insertNewObjectInContext:context];
|
||||
user.name = importUserName;
|
||||
user.algorithm = MPAlgorithmDefault;
|
||||
user.algorithm = MPAlgorithmForVersion( importUser->algorithm );
|
||||
user.keyID = [userKey keyIDForAlgorithm:user.algorithm];
|
||||
user.defaultType = importDefaultType?: user.algorithm.defaultType;
|
||||
if (importAvatar != NSNotFound)
|
||||
user.avatar = importAvatar;
|
||||
dbg( @"Created User: %@", [user debugDescription] );
|
||||
}
|
||||
user.avatar = importUser->avatar;
|
||||
user.defaultType = importUser->defaultType;
|
||||
user.lastUsed = [NSDate dateWithTimeIntervalSince1970:MAX( user.lastUsed.timeIntervalSince1970, importUser->lastUsed )];
|
||||
dbg( @"Importing user: %@", [user debugDescription] );
|
||||
|
||||
// Import new sites.
|
||||
for (NSArray *siteElements in importedSiteSites) {
|
||||
NSDate *lastUsed = [[NSDateFormatter rfc3339DateFormatter] dateFromString:siteElements[0]];
|
||||
NSUInteger uses = (unsigned)[siteElements[1] integerValue];
|
||||
MPSiteType type = (MPSiteType)[siteElements[2] integerValue];
|
||||
MPAlgorithmVersion version = (MPAlgorithmVersion)[siteElements[3] integerValue];
|
||||
NSUInteger counter = [siteElements[4] length]? (unsigned)[siteElements[4] integerValue]: NSNotFound;
|
||||
NSString *loginName = [siteElements[5] length]? siteElements[5]: nil;
|
||||
NSString *siteName = siteElements[6];
|
||||
NSString *exportContent = siteElements[7];
|
||||
// Update or create sites.
|
||||
for (size_t s = 0; s < importUser->sites_count; ++s) {
|
||||
MPMarshalledSite *importSite = &importUser->sites[s];
|
||||
|
||||
// Create new site.
|
||||
id<MPAlgorithm> algorithm = MPAlgorithmForVersion( version );
|
||||
Class entityType = [algorithm classOfType:type];
|
||||
if (!entityType) {
|
||||
err( @"Invalid site type in import file: %@ has type %lu", siteName, (long)type );
|
||||
return MPImportResultInternalError;
|
||||
// Find an existing site to update.
|
||||
NSFetchRequest *siteFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", @(importSite->name), user];
|
||||
NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
|
||||
if (!existingSites)
|
||||
return MPError( error, @"Lookup of existing sites failed for site: %@, user: %@", @(importSite->name), user.userID );
|
||||
if ([existingSites count])
|
||||
// Update existing site.
|
||||
for (MPSiteEntity *site in existingSites) {
|
||||
[self importSite:importSite protectedByKey:importKey intoSite:site usingKey:userKey];
|
||||
dbg( @"Updated site: %@", [site debugDescription] );
|
||||
}
|
||||
else {
|
||||
// Create new site.
|
||||
id<MPAlgorithm> algorithm = MPAlgorithmForVersion( importSite->algorithm );
|
||||
Class entityType = [algorithm classOfType:importSite->type];
|
||||
if (!entityType)
|
||||
return MPMakeError( @"Invalid site type in import file: %@ has type %lu", @(importSite->name), (long)importSite->type );
|
||||
|
||||
MPSiteEntity *site = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
|
||||
site.user = user;
|
||||
|
||||
[self importSite:importSite protectedByKey:importKey intoSite:site usingKey:userKey];
|
||||
dbg( @"Created site: %@", [site debugDescription] );
|
||||
}
|
||||
}
|
||||
MPSiteEntity *site = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
|
||||
site.name = siteName;
|
||||
site.loginName = loginName;
|
||||
site.user = user;
|
||||
site.type = type;
|
||||
site.uses = uses;
|
||||
site.lastUsed = lastUsed;
|
||||
site.algorithm = algorithm;
|
||||
if ([exportContent length]) {
|
||||
if (clearText)
|
||||
[site.algorithm importClearTextPassword:exportContent intoSite:site usingKey:userKey];
|
||||
else
|
||||
[site.algorithm importProtectedPassword:exportContent protectedByKey:importKey intoSite:site usingKey:userKey];
|
||||
}
|
||||
if ([site isKindOfClass:[MPGeneratedSiteEntity class]] && counter != NSNotFound)
|
||||
((MPGeneratedSiteEntity *)site).counter = counter;
|
||||
|
||||
dbg( @"Created Site: %@", [site debugDescription] );
|
||||
if (![context saveToStore])
|
||||
return MPMakeError( @"Failed saving imported changes." );
|
||||
|
||||
inf( @"Import completed successfully." );
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSitesImportedNotification object:nil userInfo:@{
|
||||
MPSitesImportedNotificationUserKey: user
|
||||
}];
|
||||
return nil;
|
||||
}
|
||||
@finally {
|
||||
mpw_marshal_free( &importUser );
|
||||
}
|
||||
|
||||
if (![context saveToStore])
|
||||
return MPImportResultInternalError;
|
||||
|
||||
inf( @"Import completed successfully." );
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSitesImportedNotification object:nil userInfo:@{
|
||||
MPSitesImportedNotificationUserKey: user
|
||||
}];
|
||||
|
||||
return MPImportResultSuccess;
|
||||
}
|
||||
|
||||
- (NSString *)exportSitesRevealPasswords:(BOOL)revealPasswords {
|
||||
- (void)importSite:(const MPMarshalledSite *)importSite protectedByKey:(MPKey *)importKey intoSite:(MPSiteEntity *)site
|
||||
usingKey:(MPKey *)userKey {
|
||||
|
||||
MPUserEntity *activeUser = [self activeUserForMainThread];
|
||||
inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", activeUser.userID );
|
||||
site.name = @(importSite->name);
|
||||
if (importSite->content)
|
||||
[site.algorithm importPassword:@(importSite->content) protectedByKey:importKey intoSite:site usingKey:userKey];
|
||||
site.type = importSite->type;
|
||||
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
((MPGeneratedSiteEntity *)site).counter = importSite->counter;
|
||||
site.algorithm = MPAlgorithmForVersion( importSite->algorithm );
|
||||
site.loginName = importSite->loginContent? @(importSite->loginContent): nil;
|
||||
site.loginGenerated = importSite->loginType & MPResultTypeClassTemplate;
|
||||
site.url = importSite->url? @(importSite->url): nil;
|
||||
site.uses = importSite->uses;
|
||||
site.lastUsed = [NSDate dateWithTimeIntervalSince1970:importSite->lastUsed];
|
||||
}
|
||||
|
||||
// Header.
|
||||
NSMutableString *export = [NSMutableString new];
|
||||
[export appendFormat:@"# Master Password site export\n"];
|
||||
if (revealPasswords)
|
||||
[export appendFormat:@"# Export of site names and passwords in clear-text.\n"];
|
||||
else
|
||||
[export appendFormat:@"# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n"];
|
||||
[export appendFormat:@"# \n"];
|
||||
[export appendFormat:@"##\n"];
|
||||
[export appendFormat:@"# Format: 1\n"];
|
||||
[export appendFormat:@"# Date: %@\n", [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]]];
|
||||
[export appendFormat:@"# User Name: %@\n", activeUser.name];
|
||||
[export appendFormat:@"# Full Name: %@\n", activeUser.name];
|
||||
[export appendFormat:@"# Avatar: %lu\n", (unsigned long)activeUser.avatar];
|
||||
[export appendFormat:@"# Key ID: %@\n", [activeUser.keyID encodeHex]];
|
||||
[export appendFormat:@"# Version: %@\n", [PearlInfoPlist get].CFBundleVersion];
|
||||
[export appendFormat:@"# Algorithm: %d\n", activeUser.algorithm.version];
|
||||
[export appendFormat:@"# Default Type: %d\n", activeUser.defaultType];
|
||||
[export appendFormat:@"# Passwords: %@\n", revealPasswords? @"VISIBLE": @"PROTECTED"];
|
||||
[export appendFormat:@"##\n"];
|
||||
[export appendFormat:@"#\n"];
|
||||
[export appendFormat:@"# Last Times Password Login\t Site\tSite\n"];
|
||||
[export appendFormat:@"# used used type name\t name\tpassword\n"];
|
||||
- (void)exportSitesRevealPasswords:(BOOL)revealPasswords
|
||||
askExportPassword:(NSString *( ^ )(NSString *userName))askImportPassword
|
||||
result:(void ( ^ )(NSString *mpsites, NSError *error))resultBlock {
|
||||
|
||||
// Sites.
|
||||
for (MPSiteEntity *site in activeUser.sites) {
|
||||
NSDate *lastUsed = site.lastUsed;
|
||||
NSUInteger uses = site.uses;
|
||||
MPSiteType type = site.type;
|
||||
id<MPAlgorithm> algorithm = site.algorithm;
|
||||
NSUInteger counter = 0;
|
||||
NSString *loginName = site.loginName;
|
||||
NSString *siteName = site.name;
|
||||
NSString *content = nil;
|
||||
[MPAppDelegate_Shared managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *user = [self activeUserInContext:context];
|
||||
NSString *masterPassword = askImportPassword( user.name );
|
||||
|
||||
// Generated-specific
|
||||
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
counter = ((MPGeneratedSiteEntity *)site).counter;
|
||||
inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", user.userID );
|
||||
MPMarshalledUser *exportUser = mpw_marshall_user( user.name.UTF8String, masterPassword.UTF8String, user.algorithm.version );
|
||||
exportUser->redacted = !revealPasswords;
|
||||
exportUser->avatar = (unsigned int)user.avatar;
|
||||
exportUser->defaultType = user.defaultType;
|
||||
exportUser->lastUsed = (time_t)user.lastUsed.timeIntervalSince1970;
|
||||
|
||||
for (MPSiteEntity *site in user.sites) {
|
||||
MPCounterValue counter = MPCounterValueInitial;
|
||||
if ([site isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
counter = ((MPGeneratedSiteEntity *)site).counter;
|
||||
NSString *content = revealPasswords
|
||||
? [site.algorithm exportPasswordForSite:site usingKey:self.key]
|
||||
: [site.algorithm resolvePasswordForSite:site usingKey:self.key];
|
||||
|
||||
// Determine the content to export.
|
||||
if (!(type & MPSiteFeatureDevicePrivate)) {
|
||||
if (revealPasswords)
|
||||
content = [site.algorithm resolvePasswordForSite:site usingKey:self.key];
|
||||
else if (type & MPSiteFeatureExportContent)
|
||||
content = [site.algorithm exportPasswordForSite:site usingKey:self.key];
|
||||
MPMarshalledSite *exportSite = mpw_marshall_site( exportUser,
|
||||
site.name.UTF8String, site.type, counter, site.algorithm.version );
|
||||
exportSite->content = content.UTF8String;
|
||||
exportSite->loginContent = site.loginName.UTF8String;
|
||||
exportSite->loginType = site.loginGenerated? MPResultTypeTemplateName: MPResultTypeStatefulPersonal;
|
||||
exportSite->url = site.url.UTF8String;
|
||||
exportSite->uses = (unsigned int)site.uses;
|
||||
exportSite->lastUsed = (time_t)site.lastUsed.timeIntervalSince1970;
|
||||
|
||||
for (MPSiteQuestionEntity *siteQuestion in site.questions)
|
||||
mpw_marshal_question( exportSite, siteQuestion.keyword.UTF8String );
|
||||
}
|
||||
|
||||
NSString *lastUsedExport = [[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed];
|
||||
long usesExport = (long)uses;
|
||||
NSString *typeExport = strf( @"%lu:%lu:%lu", (long)type, (long)[algorithm version], (long)counter );
|
||||
NSString *loginNameExport = loginName?: @"";
|
||||
NSString *contentExport = content?: @"";
|
||||
[export appendFormat:@"%@ %8ld %8S %25S\t%25S\t%@\n",
|
||||
lastUsedExport, usesExport,
|
||||
(const unsigned short *)[typeExport cStringUsingEncoding:NSUTF16StringEncoding],
|
||||
(const unsigned short *)[loginNameExport cStringUsingEncoding:NSUTF16StringEncoding],
|
||||
(const unsigned short *)[siteName cStringUsingEncoding:NSUTF16StringEncoding],
|
||||
contentExport];
|
||||
}
|
||||
char *export = NULL;
|
||||
MPMarshallError exportError = (MPMarshallError){ .type= MPMarshallSuccess };
|
||||
mpw_marshall_write( &export, MPMarshallFormatFlat, exportUser, &exportError );
|
||||
NSString *mpsites = nil;
|
||||
if (export && exportError.type == MPMarshallSuccess)
|
||||
mpsites = [NSString stringWithCString:export encoding:NSUTF8StringEncoding];
|
||||
mpw_free_string( &export );
|
||||
|
||||
return export;
|
||||
resultBlock( mpsites, exportError.type == MPMarshallSuccess? nil:
|
||||
[NSError errorWithDomain:MPErrorDomain code:MPErrorMarshallCode userInfo:@{
|
||||
@"type" : @(exportError.type),
|
||||
NSLocalizedDescriptionKey: @(exportError.description),
|
||||
}] );
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPConfig.h
|
||||
// MasterPassword
|
||||
// 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.
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import "Pearl.h"
|
||||
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPConfig.m
|
||||
// MasterPassword
|
||||
// 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.
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPEntities.h
|
||||
// MasterPassword-iOS
|
||||
// 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.
|
||||
//
|
||||
// Created by Maarten Billemont on 31/05/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
@@ -38,7 +48,7 @@
|
||||
@interface MPSiteEntity(MP)<MPFixable>
|
||||
|
||||
@property(assign) BOOL loginGenerated;
|
||||
@property(assign) MPSiteType type;
|
||||
@property(assign) MPResultType type;
|
||||
@property(readonly) NSString *typeName;
|
||||
@property(readonly) NSString *typeShortName;
|
||||
@property(readonly) NSString *typeClassName;
|
||||
@@ -61,7 +71,7 @@
|
||||
|
||||
@interface MPGeneratedSiteEntity(MP)
|
||||
|
||||
@property(assign) NSUInteger counter;
|
||||
@property(assign) MPCounterValue counter;
|
||||
|
||||
@end
|
||||
|
||||
@@ -70,7 +80,7 @@
|
||||
@property(assign) NSUInteger avatar;
|
||||
@property(assign) BOOL saveKey;
|
||||
@property(assign) BOOL touchID;
|
||||
@property(assign) MPSiteType defaultType;
|
||||
@property(assign) MPResultType defaultType;
|
||||
@property(readonly) NSString *userID;
|
||||
@property(strong) id<MPAlgorithm> algorithm;
|
||||
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPEntities.m
|
||||
// MasterPassword-iOS
|
||||
// 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.
|
||||
//
|
||||
// Created by Maarten Billemont on 31/05/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import "MPEntities.h"
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
@@ -72,9 +82,9 @@
|
||||
return MPFixableResultNoProblems;
|
||||
}
|
||||
|
||||
- (MPSiteType)type {
|
||||
- (MPResultType)type {
|
||||
|
||||
return (MPSiteType)[self.type_ unsignedIntegerValue];
|
||||
return (MPResultType)[self.type_ unsignedIntegerValue];
|
||||
}
|
||||
|
||||
- (void)setLoginGenerated:(BOOL)aLoginGenerated {
|
||||
@@ -87,7 +97,7 @@
|
||||
return [self.loginGenerated_ boolValue];
|
||||
}
|
||||
|
||||
- (void)setType:(MPSiteType)aType {
|
||||
- (void)setType:(MPResultType)aType {
|
||||
|
||||
self.type_ = @(aType);
|
||||
}
|
||||
@@ -241,7 +251,7 @@
|
||||
|
||||
MPFixableResult result = [super findAndFixInconsistenciesInContext:context];
|
||||
|
||||
if (!self.type || self.type == (MPSiteType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
|
||||
if (!self.type || self.type == (MPResultType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
|
||||
// Invalid self.type
|
||||
result = MPApplyFix( result, ^MPFixableResult {
|
||||
wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
|
||||
@@ -249,7 +259,7 @@
|
||||
self.type = self.user.defaultType;
|
||||
return MPFixableResultProblemsFixed;
|
||||
} );
|
||||
if (!self.type || self.type == (MPSiteType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
|
||||
if (!self.type || self.type == (MPResultType)NSNotFound || ![[self.algorithm allTypes] containsObject:self.type_])
|
||||
// Invalid self.user.defaultType
|
||||
result = MPApplyFix( result, ^MPFixableResult {
|
||||
wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
|
||||
@@ -260,7 +270,7 @@
|
||||
if (![self isKindOfClass:[self.algorithm classOfType:self.type]])
|
||||
// Mismatch between self.type and self.class
|
||||
result = MPApplyFix( result, ^MPFixableResult {
|
||||
for (MPSiteType newType = self.type; self.type != (newType = [self.algorithm nextType:newType]);)
|
||||
for (MPResultType newType = self.type; self.type != (newType = [self.algorithm nextType:newType]);)
|
||||
if ([self isKindOfClass:[self.algorithm classOfType:newType]]) {
|
||||
wrn( @"Mismatching type for: %@ of %@, type: %lu, class: %@. Will use %ld instead.",
|
||||
self.name, self.user.name, (long)self.type, self.class, (long)newType );
|
||||
@@ -276,12 +286,12 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSUInteger)counter {
|
||||
- (MPCounterValue)counter {
|
||||
|
||||
return [self.counter_ unsignedIntegerValue];
|
||||
return (MPCounterValue)[self.counter_ unsignedIntegerValue];
|
||||
}
|
||||
|
||||
- (void)setCounter:(NSUInteger)aCounter {
|
||||
- (void)setCounter:(MPCounterValue)aCounter {
|
||||
|
||||
self.counter_ = @(aCounter);
|
||||
}
|
||||
@@ -344,12 +354,12 @@
|
||||
self.touchID_ = @(aTouchID);
|
||||
}
|
||||
|
||||
- (MPSiteType)defaultType {
|
||||
- (MPResultType)defaultType {
|
||||
|
||||
return (MPSiteType)[self.defaultType_ unsignedIntegerValue]?: self.algorithm.defaultType;
|
||||
return (MPResultType)[self.defaultType_ unsignedIntegerValue]?: self.algorithm.defaultType;
|
||||
}
|
||||
|
||||
- (void)setDefaultType:(MPSiteType)aDefaultType {
|
||||
- (void)setDefaultType:(MPResultType)aDefaultType {
|
||||
|
||||
self.defaultType_ = @(aDefaultType);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPFixable.h
|
||||
// MPFixable
|
||||
// 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.
|
||||
//
|
||||
// Created by lhunath on 2014-04-26.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPFixable.m
|
||||
// MPFixable
|
||||
// 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.
|
||||
//
|
||||
// Created by lhunath on 2014-04-26.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import "MPFixable.h"
|
||||
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPKey
|
||||
// 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.
|
||||
//
|
||||
// Created by Maarten Billemont on 16/07/12.
|
||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPAlgorithm.h"
|
||||
#import "mpw-types.h"
|
||||
|
||||
@protocol MPAlgorithm;
|
||||
|
||||
@@ -36,8 +38,7 @@ typedef NS_ENUM( NSUInteger, MPKeyOrigin ) {
|
||||
keyOrigin:(MPKeyOrigin)origin;
|
||||
|
||||
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength;
|
||||
- (MPMasterKey)keyForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||
|
||||
- (BOOL)isEqualToKey:(MPKey *)key;
|
||||
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPKey
|
||||
// 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.
|
||||
//
|
||||
// Created by Maarten Billemont on 16/07/12.
|
||||
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import "MPAlgorithm.h"
|
||||
|
||||
@@ -22,12 +23,11 @@
|
||||
@property(nonatomic) MPKeyOrigin origin;
|
||||
@property(nonatomic, copy) NSString *fullName;
|
||||
@property(nonatomic, copy) NSData *( ^keyResolver )(id<MPAlgorithm>);
|
||||
@property(nonatomic, strong) NSCache *keyCache;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPKey {
|
||||
NSCache *_keyCache;
|
||||
};
|
||||
@implementation MPKey;
|
||||
|
||||
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
_keyCache = [NSCache new];
|
||||
self.keyCache = [NSCache new];
|
||||
|
||||
self.origin = origin;
|
||||
self.fullName = fullName;
|
||||
@@ -53,30 +53,23 @@
|
||||
|
||||
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm {
|
||||
|
||||
return [algorithm keyIDForKeyData:[self keyDataForAlgorithm:algorithm]];
|
||||
return [algorithm keyIDForKey:[self keyForAlgorithm:algorithm]];
|
||||
}
|
||||
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm {
|
||||
- (MPMasterKey)keyForAlgorithm:(id<MPAlgorithm>)algorithm {
|
||||
|
||||
@synchronized (self) {
|
||||
NSData *keyData = [_keyCache objectForKey:algorithm];
|
||||
if (keyData)
|
||||
return keyData;
|
||||
NSData *keyData = [self.keyCache objectForKey:algorithm];
|
||||
if (!keyData) {
|
||||
keyData = self.keyResolver( algorithm );
|
||||
if (keyData)
|
||||
[self.keyCache setObject:keyData forKey:algorithm];
|
||||
}
|
||||
|
||||
keyData = self.keyResolver( algorithm );
|
||||
if (keyData)
|
||||
[_keyCache setObject:keyData forKey:algorithm];
|
||||
|
||||
return keyData;
|
||||
return keyData.length == MPMasterKeySize? keyData.bytes: NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength {
|
||||
|
||||
NSData *keyData = [self keyDataForAlgorithm:algorithm];
|
||||
return [keyData subdataWithRange:NSMakeRange( 0, MIN( subKeyLength, keyData.length ) )];
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToKey:(MPKey *)key {
|
||||
|
||||
return [[self keyIDForAlgorithm:MPAlgorithmDefault] isEqualToData:[key keyIDForAlgorithm:MPAlgorithmDefault]];
|
||||
@@ -84,10 +77,7 @@
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
|
||||
if (![object isKindOfClass:[MPKey class]])
|
||||
return NO;
|
||||
|
||||
return [self isEqualToKey:object];
|
||||
return [object isKindOfClass:[MPKey class]] && [self isEqualToKey:object];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPTypes.h
|
||||
// MasterPassword
|
||||
// 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.
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import <Crashlytics/Crashlytics.h>
|
||||
#import <Crashlytics/Answers.h>
|
||||
@@ -12,6 +22,7 @@
|
||||
__BEGIN_DECLS
|
||||
extern NSString *const MPErrorDomain;
|
||||
extern NSInteger const MPErrorHangCode;
|
||||
extern NSInteger const MPErrorMarshallCode;
|
||||
|
||||
extern NSString *const MPSignedInNotification;
|
||||
extern NSString *const MPSignedOutNotification;
|
||||
@@ -28,16 +39,26 @@ __END_DECLS
|
||||
|
||||
#ifdef CRASHLYTICS
|
||||
#define MPError(error_, message, ...) ({ \
|
||||
err( message @"%@%@", ##__VA_ARGS__, error_? @"\n": @"", [error_ fullDescription]?: @"" ); \
|
||||
NSError *__error = error_; \
|
||||
err( message @"%@%@", ##__VA_ARGS__, __error && [message length]? @"\n": @"", [__error fullDescription]?: @"" ); \
|
||||
\
|
||||
if ([[MPConfig get].sendInfo boolValue]) { \
|
||||
[[Crashlytics sharedInstance] recordError:error_ withAdditionalUserInfo:@{ \
|
||||
if (__error && [[MPConfig get].sendInfo boolValue]) { \
|
||||
[[Crashlytics sharedInstance] recordError:__error withAdditionalUserInfo:@{ \
|
||||
@"location": strf( @"%@:%d %@", @(basename((char *)__FILE__)), __LINE__, NSStringFromSelector(_cmd) ), \
|
||||
}]; \
|
||||
} \
|
||||
__error; \
|
||||
})
|
||||
#else
|
||||
#define MPError(error_, ...) ({ \
|
||||
err( message @"%@%@", ##__VA_ARGS__, error_? @"\n": @"", [error_ fullDescription]?: @"" ); \
|
||||
#define MPError(error_, message, ...) ({ \
|
||||
NSError *__error = error_; \
|
||||
err( message @"%@%@", ##__VA_ARGS__, __error? @"\n": @"", [__error fullDescription]?: @"" ); \
|
||||
__error; \
|
||||
})
|
||||
#endif
|
||||
|
||||
#define MPMakeError(message, ...) ({ \
|
||||
MPError( [NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{ \
|
||||
NSLocalizedDescriptionKey: strf( message, ##__VA_ARGS__ ) \
|
||||
}], @"" ); \
|
||||
})
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPTypes.c
|
||||
// MasterPassword
|
||||
// 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.
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import "MPTypes.h"
|
||||
|
||||
NSString *const MPErrorDomain = @"MPErrorDomain";
|
||||
NSInteger const MPErrorHangCode = 1;
|
||||
NSInteger const MPErrorMarshallCode = 1;
|
||||
|
||||
NSString *const MPSignedInNotification = @"MPSignedInNotification";
|
||||
NSString *const MPSignedOutNotification = @"MPSignedOutNotification";
|
||||
|
||||
@@ -18,9 +18,13 @@
|
||||
|
||||
#import "MPGradientView.h"
|
||||
|
||||
@implementation MPGradientView {
|
||||
NSGradient *gradient;
|
||||
}
|
||||
@interface MPGradientView()
|
||||
|
||||
@property(nonatomic, strong) NSGradient *gradient;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPGradientView
|
||||
|
||||
- (id)initWithFrame:(NSRect)frame {
|
||||
|
||||
@@ -51,28 +55,28 @@
|
||||
- (void)setStartingColor:(NSColor *)startingColor {
|
||||
|
||||
_startingColor = startingColor;
|
||||
gradient = nil;
|
||||
self.gradient = nil;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setEndingColor:(NSColor *)endingColor {
|
||||
|
||||
_endingColor = endingColor;
|
||||
gradient = nil;
|
||||
self.gradient = nil;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setAngle:(NSInteger)angle {
|
||||
|
||||
_angle = angle;
|
||||
gradient = nil;
|
||||
self.gradient = nil;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setRatio:(CGFloat)ratio {
|
||||
|
||||
_ratio = ratio;
|
||||
gradient = nil;
|
||||
self.gradient = nil;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
@@ -84,7 +88,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
[(gradient?: (gradient = [[NSGradient alloc] initWithColorsAndLocations:
|
||||
[(self.gradient?: (self.gradient = [[NSGradient alloc] initWithColorsAndLocations:
|
||||
self.startingColor, (CGFloat)0.f,
|
||||
[self.startingColor blendedColorWithFraction:0.5f ofColor:self.endingColor], self.ratio,
|
||||
self.endingColor, (CGFloat)1.f, nil]))
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
#import "MPPasswordWindowController.h"
|
||||
#import "MPSitesWindowController.h"
|
||||
#import "MPInitialWindowController.h"
|
||||
|
||||
@interface MPMacAppDelegate : MPAppDelegate_Shared<NSApplicationDelegate>
|
||||
|
||||
@property(nonatomic, strong) NSStatusItem *statusView;
|
||||
@property(nonatomic, strong) MPPasswordWindowController *passwordWindowController;
|
||||
@property(nonatomic, strong) MPSitesWindowController *sitesWindowController;
|
||||
@property(nonatomic, strong) MPInitialWindowController *initialWindowController;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *lockItem;
|
||||
@property(nonatomic, weak) IBOutlet NSMenuItem *showItem;
|
||||
|
||||
@@ -269,23 +269,22 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
[openPanel close];
|
||||
|
||||
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
|
||||
^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
|
||||
if (error)
|
||||
MPError( error, @"While reading imported sites from %@.", url );
|
||||
^(NSData *importedSitesData, NSURLResponse *response, NSError *urlError) {
|
||||
if (urlError)
|
||||
[[NSAlert alertWithError:MPError( urlError, @"While reading imported sites from %@.", url )] runModal];
|
||||
if (!importedSitesData)
|
||||
return;
|
||||
|
||||
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
|
||||
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
|
||||
[self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
|
||||
__block NSString *masterPassword = nil;
|
||||
|
||||
PearlMainQueueWait( ^{
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Unlock"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
alert.messageText = @"Import File's Master Password";
|
||||
alert.informativeText = strf( @"%@'s export was done using a different master password.\n"
|
||||
@"Enter that master password to unlock the exported data.", userName );
|
||||
alert.messageText = strf( @"Importing Sites For\n%@", userName );
|
||||
alert.informativeText = @"Enter the master password used to create this export file.";
|
||||
alert.accessoryView = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
||||
[alert layout];
|
||||
if ([alert runModal] == NSAlertFirstButtonReturn)
|
||||
@@ -293,16 +292,15 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
} );
|
||||
|
||||
return masterPassword;
|
||||
} askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) {
|
||||
} askUserPassword:^NSString *(NSString *userName) {
|
||||
__block NSString *masterPassword = nil;
|
||||
|
||||
PearlMainQueueWait( ^{
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Import"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
alert.messageText = strf( @"Master Password for\n%@", userName );
|
||||
alert.informativeText = strf( @"Imports %lu sites, overwriting %lu.",
|
||||
(unsigned long)importCount, (unsigned long)deleteCount );
|
||||
alert.messageText = strf( @"Master Password For\n%@", userName );
|
||||
alert.informativeText = @"Enter the current master password for this user.";
|
||||
alert.accessoryView = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
||||
[alert layout];
|
||||
if ([alert runModal] == NSAlertFirstButtonReturn)
|
||||
@@ -310,37 +308,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
} );
|
||||
|
||||
return masterPassword;
|
||||
} result:^(NSError *error) {
|
||||
[self updateUsers];
|
||||
|
||||
if (error && !(error.domain == NSCocoaErrorDomain && error.code == NSUserCancelledError))
|
||||
[[NSAlert alertWithError:error] runModal];
|
||||
}];
|
||||
|
||||
PearlMainQueue( ^{
|
||||
switch (result) {
|
||||
case MPImportResultSuccess: {
|
||||
[self updateUsers];
|
||||
|
||||
NSAlert *alert = [NSAlert new];
|
||||
alert.messageText = @"Successfully imported sites.";
|
||||
[alert runModal];
|
||||
break;
|
||||
}
|
||||
case MPImportResultCancelled:
|
||||
break;
|
||||
case MPImportResultInternalError:
|
||||
[[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"Import failed because of an internal error."
|
||||
}]] runModal];
|
||||
break;
|
||||
case MPImportResultMalformedInput:
|
||||
[[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"The import doesn't look like a Master Password export."
|
||||
}]] runModal];
|
||||
break;
|
||||
case MPImportResultInvalidPassword:
|
||||
[[NSAlert alertWithError:[NSError errorWithDomain:MPErrorDomain code:0 userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"Incorrect master password for the import sites."
|
||||
}]] runModal];
|
||||
break;
|
||||
}
|
||||
} );
|
||||
}] resume];
|
||||
}
|
||||
|
||||
@@ -430,8 +403,8 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
|
||||
- (IBAction)terminate:(id)sender {
|
||||
|
||||
[self.passwordWindowController close];
|
||||
self.passwordWindowController = nil;
|
||||
[self.sitesWindowController close];
|
||||
self.sitesWindowController = nil;
|
||||
|
||||
[NSApp terminate:nil];
|
||||
}
|
||||
@@ -460,11 +433,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
prof_rewind( @"activeUserForMainThread" );
|
||||
|
||||
// Don't show window if we weren't already running (ie. if we haven't been activated before).
|
||||
if (!self.passwordWindowController)
|
||||
self.passwordWindowController = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
|
||||
if (!self.sitesWindowController)
|
||||
self.sitesWindowController = [[MPSitesWindowController alloc] initWithWindowNibName:@"MPSitesWindowController"];
|
||||
prof_rewind( @"initWithWindow" );
|
||||
|
||||
[self.passwordWindowController showWindow:self];
|
||||
[self.sitesWindowController showWindow:self];
|
||||
prof_finish( @"showWindow" );
|
||||
}
|
||||
|
||||
@@ -509,25 +482,43 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
if ([savePanel runModal] == NSFileHandlingPanelCancelButton)
|
||||
return;
|
||||
|
||||
NSError *coordinateError = nil;
|
||||
NSString *exportedSites = [self exportSitesRevealPasswords:revealPasswords];
|
||||
[[[NSFileCoordinator alloc] initWithFilePresenter:nil] coordinateWritingItemAtURL:savePanel.URL options:0
|
||||
error:&coordinateError byAccessor:
|
||||
^(NSURL *newURL) {
|
||||
NSError *writeError = nil;
|
||||
if (![exportedSites writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError])
|
||||
MPError( writeError, @"Could not write to the export file." );
|
||||
[self exportSitesRevealPasswords:revealPasswords
|
||||
askExportPassword:^NSString *(NSString *userName) {
|
||||
return PearlMainQueueAwait( ^id {
|
||||
NSAlert *alert = [NSAlert new];
|
||||
[alert addButtonWithTitle:@"Import"];
|
||||
[alert addButtonWithTitle:@"Cancel"];
|
||||
alert.messageText = strf( @"Master Password For\n%@", userName );
|
||||
alert.informativeText = @"Enter the current master password for this user.";
|
||||
alert.accessoryView = [[NSSecureTextField alloc] initWithFrame:NSMakeRect( 0, 0, 200, 22 )];
|
||||
[alert layout];
|
||||
if ([alert runModal] == NSAlertFirstButtonReturn)
|
||||
return ((NSTextField *)alert.accessoryView).stringValue;
|
||||
else
|
||||
return nil;
|
||||
} );
|
||||
} result:^(NSString *mpsites, NSError *error) {
|
||||
if (!mpsites || error) {
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:MPError( error, @"Failed to export mpsites." )] runModal];
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
NSError *coordinateError = nil;
|
||||
[[[NSFileCoordinator alloc] initWithFilePresenter:nil]
|
||||
coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError byAccessor:^(NSURL *newURL) {
|
||||
NSError *writeError = nil;
|
||||
if (![mpsites writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError])
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:writeError] runModal];
|
||||
[[NSAlert alertWithError:MPError( writeError, @"Could not write to the export file." )] runModal];
|
||||
} );
|
||||
}];
|
||||
if (coordinateError) {
|
||||
MPError( coordinateError, @"Write access to the export file could not be obtained." );
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:coordinateError] runModal];
|
||||
} );
|
||||
}
|
||||
}];
|
||||
if (coordinateError)
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:MPError( coordinateError, @"Could not gain access to the export file." )] runModal];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateUsers {
|
||||
@@ -604,7 +595,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
- (void)updateMenuItems {
|
||||
|
||||
MPUserEntity *activeUser = [self activeUserForMainThread];
|
||||
// if (!(self.showItem.enabled = ![self.passwordWindowController.window isVisible])) {
|
||||
// if (!(self.showItem.enabled = ![self.sitesWindowController.window isVisible])) {
|
||||
// self.showItem.title = @"Show (Showing)";
|
||||
// self.showItem.toolTip = @"Master Password is already showing.";
|
||||
// }
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPMacApplication {
|
||||
}
|
||||
@implementation MPMacApplication
|
||||
|
||||
- (void)sendEvent:(NSEvent *)event {
|
||||
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPMacConfig.h
|
||||
// MasterPassword
|
||||
// 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.
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import "MPConfig.h"
|
||||
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPMacConfig.m
|
||||
// MasterPassword
|
||||
// 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.
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
@implementation MPMacConfig
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPNoStateButton.h
|
||||
// MPNoStateButton
|
||||
// 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.
|
||||
//
|
||||
// Created by lhunath on 14-10-27.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPNoStateButton.h
|
||||
// MPNoStateButton
|
||||
// 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.
|
||||
//
|
||||
// Created by lhunath on 14-10-27.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import "MPNoStateButton.h"
|
||||
|
||||
@implementation MPNoStateButtonCell {
|
||||
}
|
||||
@implementation MPNoStateButtonCell
|
||||
|
||||
- (void)setState:(NSInteger)state {
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPPasswordWindow.h
|
||||
// MPPasswordWindow
|
||||
//
|
||||
// Created by lhunath on 2014-06-19.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface MPPasswordWindow : NSWindow
|
||||
|
||||
@end
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//
|
||||
// MPPasswordWindow.h
|
||||
// MPPasswordWindow
|
||||
//
|
||||
// Created by lhunath on 2014-06-19.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPPasswordWindow.h"
|
||||
|
||||
@implementation MPPasswordWindow
|
||||
|
||||
#pragma mark - Life
|
||||
|
||||
- (BOOL)canBecomeKeyWindow {
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - State
|
||||
|
||||
- (void)update {
|
||||
|
||||
if ([[MPMacConfig get].fullScreen boolValue]) {
|
||||
[self setLevel:NSScreenSaverWindowLevel];
|
||||
[self setFrame:self.screen.frame display:YES];
|
||||
}
|
||||
else if (self.level != NSNormalWindowLevel) {
|
||||
[self setLevel:NSNormalWindowLevel];
|
||||
[self setFrame:NSMakeRect( 0, 0, 640, 600 ) display:NO];
|
||||
[self center];
|
||||
}
|
||||
|
||||
[super update];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
@end
|
||||
@@ -7,7 +7,7 @@
|
||||
<capability name="stacking Non-gravity area distributions on NSStackView" minToolsVersion="7.0" minSystemVersion="10.11"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="MPPasswordWindowController">
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="MPSitesWindowController">
|
||||
<connections>
|
||||
<outlet property="inputLabel" destination="OnR-s6-d4P" id="p6G-Ut-cdu"/>
|
||||
<outlet property="passwordTypesBox" destination="bZe-7q-i6q" id="Ai3-pt-i6K"/>
|
||||
@@ -26,7 +26,7 @@
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Master Password" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MPPasswordWindow">
|
||||
<window title="Master Password" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MPSitesWindow">
|
||||
<windowStyleMask key="styleMask" texturedBackground="YES" unifiedTitleAndToolbar="YES" fullSizeContentView="YES"/>
|
||||
<windowCollectionBehavior key="collectionBehavior" moveToActiveSpace="YES" transient="YES" ignoresCycle="YES" fullScreenAuxiliary="YES"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="640" height="577"/>
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
//==============================================================================
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPSiteEntity.h"
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
#import "MPAlgorithm.h"
|
||||
#import "MPUserEntity.h"
|
||||
#import "MPUserEntity+CoreDataClass.h"
|
||||
|
||||
@class MPSiteEntity;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
@property(nonatomic) NSString *name;
|
||||
@property(nonatomic) NSAttributedString *displayedName;
|
||||
@property(nonatomic) MPSiteType type;
|
||||
@property(nonatomic) MPResultType type;
|
||||
@property(nonatomic) NSString *typeName;
|
||||
@property(nonatomic) NSString *content;
|
||||
@property(nonatomic) NSString *displayedContent;
|
||||
@@ -36,7 +36,7 @@
|
||||
@property(nonatomic) NSString *loginName;
|
||||
@property(nonatomic) BOOL loginGenerated;
|
||||
@property(nonatomic) NSNumber *uses;
|
||||
@property(nonatomic) NSUInteger counter;
|
||||
@property(nonatomic) MPCounterValue counter;
|
||||
@property(nonatomic) NSDate *lastUsed;
|
||||
@property(nonatomic) id<MPAlgorithm> algorithm;
|
||||
@property(nonatomic) MPAlgorithmVersion algorithmVersion;
|
||||
|
||||
@@ -17,16 +17,19 @@
|
||||
//==============================================================================
|
||||
|
||||
#import "MPSiteModel.h"
|
||||
#import "MPSiteEntity.h"
|
||||
#import "MPEntities.h"
|
||||
#import "MPAppDelegate_Shared.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "MPMacAppDelegate.h"
|
||||
|
||||
@implementation MPSiteModel {
|
||||
NSManagedObjectID *_entityOID;
|
||||
BOOL _initialized;
|
||||
}
|
||||
@interface MPSiteModel()
|
||||
|
||||
@property(nonatomic, strong) NSManagedObjectID *entityOID;
|
||||
@property(nonatomic) BOOL initialized;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPSiteModel
|
||||
|
||||
- (instancetype)initWithEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
|
||||
|
||||
@@ -34,7 +37,7 @@
|
||||
return nil;
|
||||
|
||||
[self setEntity:entity fuzzyGroups:fuzzyGroups];
|
||||
_initialized = YES;
|
||||
self.initialized = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -45,16 +48,16 @@
|
||||
return nil;
|
||||
|
||||
[self setTransientSiteName:siteName forUser:user];
|
||||
_initialized = YES;
|
||||
self.initialized = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
|
||||
|
||||
if ([_entityOID isEqual:entity.permanentObjectID])
|
||||
if ([self.entityOID isEqual:entity.permanentObjectID])
|
||||
return;
|
||||
_entityOID = entity.permanentObjectID;
|
||||
self.entityOID = entity.permanentObjectID;
|
||||
|
||||
NSString *siteName = entity.name;
|
||||
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName];
|
||||
@@ -78,7 +81,7 @@
|
||||
self.type = entity.type;
|
||||
self.typeName = entity.typeName;
|
||||
self.uses = entity.uses_;
|
||||
self.counter = [entity isKindOfClass:[MPGeneratedSiteEntity class]]? [(MPGeneratedSiteEntity *)entity counter]: 0;
|
||||
self.counter = [entity isKindOfClass:[MPGeneratedSiteEntity class]]? [(MPGeneratedSiteEntity *)entity counter]: MPCounterValueInitial;
|
||||
self.loginGenerated = entity.loginGenerated;
|
||||
|
||||
// Find all password types and the index of the current type amongst them.
|
||||
@@ -87,7 +90,7 @@
|
||||
|
||||
- (void)setTransientSiteName:(NSString *)siteName forUser:(MPUserEntity *)user {
|
||||
|
||||
_entityOID = nil;
|
||||
self.entityOID = nil;
|
||||
|
||||
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
||||
paragraphStyle.alignment = NSCenterTextAlignment;
|
||||
@@ -101,7 +104,7 @@
|
||||
self.type = user.defaultType;
|
||||
self.typeName = [self.algorithm nameOfType:self.type];
|
||||
self.uses = @0;
|
||||
self.counter = 1;
|
||||
self.counter = MPCounterValueDefault;
|
||||
|
||||
// Find all password types and the index of the current type amongst them.
|
||||
[self updateContent];
|
||||
@@ -109,28 +112,28 @@
|
||||
|
||||
- (MPSiteEntity *)entityInContext:(NSManagedObjectContext *)moc {
|
||||
|
||||
if (!_entityOID)
|
||||
if (!self.entityOID)
|
||||
return nil;
|
||||
|
||||
NSError *error;
|
||||
MPSiteEntity *entity = (MPSiteEntity *)[moc existingObjectWithID:_entityOID error:&error];
|
||||
MPSiteEntity *entity = [moc existingObjectWithID:self.entityOID error:&error];
|
||||
if (!entity)
|
||||
MPError( error, @"Couldn't retrieve active site." );
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
- (void)setCounter:(NSUInteger)counter {
|
||||
- (void)setCounter:(MPCounterValue)counter {
|
||||
|
||||
if (counter == _counter)
|
||||
if (self.counter == counter)
|
||||
return;
|
||||
_counter = counter;
|
||||
|
||||
if (!_initialized)
|
||||
if (!self.initialized)
|
||||
// This wasn't a change to the entity.
|
||||
return;
|
||||
|
||||
if (_entityOID)
|
||||
if (self.entityOID)
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *entity = [self entityInContext:context];
|
||||
if ([entity isKindOfClass:[MPGeneratedSiteEntity class]]) {
|
||||
@@ -146,15 +149,15 @@
|
||||
|
||||
- (void)setLoginGenerated:(BOOL)loginGenerated {
|
||||
|
||||
if (loginGenerated == _loginGenerated)
|
||||
if (self.loginGenerated == loginGenerated)
|
||||
return;
|
||||
_loginGenerated = loginGenerated;
|
||||
|
||||
if (!_initialized)
|
||||
if (!self.initialized)
|
||||
// This wasn't a change to the entity.
|
||||
return;
|
||||
|
||||
if (_entityOID)
|
||||
if (self.entityOID)
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *entity = [self entityInContext:context];
|
||||
entity.loginGenerated = loginGenerated;
|
||||
@@ -179,7 +182,7 @@
|
||||
self.algorithm = MPAlgorithmForVersion( algorithmVersion )?: self.algorithm;
|
||||
[self didChangeValueForKey:@"outdated"];
|
||||
|
||||
if (_entityOID)
|
||||
if (self.entityOID)
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *entity = [self entityInContext:context];
|
||||
entity.algorithm = self.algorithm;
|
||||
@@ -193,7 +196,7 @@
|
||||
|
||||
- (void)setQuestion:(NSString *)question {
|
||||
|
||||
if ([question isEqualToString:_question])
|
||||
if ([self.question isEqualToString:question])
|
||||
return;
|
||||
_question = question;
|
||||
|
||||
@@ -207,37 +210,37 @@
|
||||
|
||||
- (BOOL)generated {
|
||||
|
||||
return self.type & MPSiteTypeClassGenerated;
|
||||
return self.type & MPResultTypeClassTemplate;
|
||||
}
|
||||
|
||||
- (BOOL)stored {
|
||||
|
||||
return self.type & MPSiteTypeClassStored;
|
||||
return self.type & MPResultTypeClassStateful;
|
||||
}
|
||||
|
||||
- (BOOL)transient {
|
||||
|
||||
return _entityOID == nil;
|
||||
return self.entityOID == nil;
|
||||
}
|
||||
|
||||
- (void)updateContent {
|
||||
|
||||
if (_entityOID)
|
||||
if (self.entityOID)
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
[self updateContent:[MPSiteEntity existingObjectWithID:_entityOID inContext:context]];
|
||||
[self updateContent:[MPSiteEntity existingObjectWithID:self.entityOID inContext:context]];
|
||||
}];
|
||||
|
||||
else
|
||||
PearlNotMainQueue( ^{
|
||||
[self updatePasswordWithResult:
|
||||
[self.algorithm generatePasswordForSiteNamed:self.name ofType:self.type withCounter:self.counter
|
||||
usingKey:[MPAppDelegate_Shared get].key]];
|
||||
[self.algorithm mpwTemplateForSiteNamed:self.name ofType:self.type withCounter:self.counter
|
||||
usingKey:[MPAppDelegate_Shared get].key]];
|
||||
[self updateLoginNameWithResult:
|
||||
[self.algorithm generateLoginForSiteNamed:self.name
|
||||
usingKey:[MPAppDelegate_Shared get].key]];
|
||||
[self.algorithm mpwLoginForSiteNamed:self.name
|
||||
usingKey:[MPAppDelegate_Shared get].key]];
|
||||
[self updateAnswerWithResult:
|
||||
[self.algorithm generateAnswerForSiteNamed:self.name onQuestion:self.question
|
||||
usingKey:[MPAppDelegate_Shared get].key]];
|
||||
[self.algorithm mpwAnswerForSiteNamed:self.name onQuestion:self.question
|
||||
usingKey:[MPAppDelegate_Shared get].key]];
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -249,8 +252,8 @@
|
||||
[entity resolveLoginUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
|
||||
[self updateLoginNameWithResult:result];
|
||||
}];
|
||||
[self updateAnswerWithResult:[self.algorithm generateAnswerForSiteNamed:self.name onQuestion:self.question
|
||||
usingKey:[MPAppDelegate_Shared get].key]];
|
||||
[self updateAnswerWithResult:[self.algorithm mpwAnswerForSiteNamed:self.name onQuestion:self.question
|
||||
usingKey:[MPAppDelegate_Shared get].key]];
|
||||
}
|
||||
|
||||
- (void)updatePasswordWithResult:(NSString *)result {
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@class MPPasswordWindowController;
|
||||
@class MPSitesWindowController;
|
||||
|
||||
@interface MPSitesTableView : NSTableView
|
||||
|
||||
@property(nonatomic, weak) MPPasswordWindowController *controller;
|
||||
@property(nonatomic, weak) MPSitesWindowController *controller;
|
||||
|
||||
@end
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
//==============================================================================
|
||||
|
||||
#import "MPSitesTableView.h"
|
||||
#import "MPPasswordWindowController.h"
|
||||
#import "MPSitesWindowController.h"
|
||||
|
||||
@implementation MPSitesTableView
|
||||
|
||||
|
||||
23
platform-darwin/Source/Mac/MPSitesWindow.h
Normal file
23
platform-darwin/Source/Mac/MPSitesWindow.h
Normal file
@@ -0,0 +1,23 @@
|
||||
//==============================================================================
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface MPSitesWindow : NSWindow
|
||||
|
||||
@end
|
||||
49
platform-darwin/Source/Mac/MPSitesWindow.m
Normal file
49
platform-darwin/Source/Mac/MPSitesWindow.m
Normal file
@@ -0,0 +1,49 @@
|
||||
//==============================================================================
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import "MPSitesWindow.h"
|
||||
|
||||
@implementation MPSitesWindow
|
||||
|
||||
#pragma mark - Life
|
||||
|
||||
- (BOOL)canBecomeKeyWindow {
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - State
|
||||
|
||||
- (void)update {
|
||||
|
||||
if ([[MPMacConfig get].fullScreen boolValue]) {
|
||||
[self setLevel:NSScreenSaverWindowLevel];
|
||||
[self setFrame:self.screen.frame display:YES];
|
||||
}
|
||||
else if (self.level != NSNormalWindowLevel) {
|
||||
[self setLevel:NSNormalWindowLevel];
|
||||
[self setFrame:NSMakeRect( 0, 0, 640, 600 ) display:NO];
|
||||
[self center];
|
||||
}
|
||||
|
||||
[super update];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
@end
|
||||
@@ -1,29 +1,29 @@
|
||||
/**
|
||||
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
|
||||
*
|
||||
* See the enclosed file LICENSE for license information (LGPLv3). If you did
|
||||
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*
|
||||
* @author Maarten Billemont <lhunath@lyndir.com>
|
||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
// This file is part of Master Password.
|
||||
// Copyright (c) 2011-2017, Maarten Billemont.
|
||||
//
|
||||
// MPPasswordWindowController.h
|
||||
// MPPasswordWindowController
|
||||
// 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.
|
||||
//
|
||||
// Created by lhunath on 2014-06-18.
|
||||
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
|
||||
// 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/>.
|
||||
//==============================================================================
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "MPSiteModel.h"
|
||||
#import "MPSitesTableView.h"
|
||||
#import "MPPasswordWindow.h"
|
||||
#import "MPSitesWindow.h"
|
||||
|
||||
@class MPMacAppDelegate;
|
||||
|
||||
@interface MPPasswordWindowController : NSWindowController<NSTextViewDelegate, NSTextFieldDelegate, NSTableViewDataSource, NSTableViewDelegate>
|
||||
@interface MPSitesWindowController : NSWindowController<NSTextViewDelegate, NSTextFieldDelegate, NSTableViewDataSource, NSTableViewDelegate>
|
||||
|
||||
@property(nonatomic) NSMutableArray *sites;
|
||||
@property(nonatomic) NSString *masterPassword;
|
||||
@@ -17,18 +17,18 @@
|
||||
//==============================================================================
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import "MPPasswordWindowController.h"
|
||||
#import "MPSitesWindowController.h"
|
||||
#import "MPMacAppDelegate.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "MPAppDelegate_Key.h"
|
||||
|
||||
@interface MPPasswordWindowController()
|
||||
@interface MPSitesWindowController()
|
||||
|
||||
@property(nonatomic, strong) CAGradientLayer *siteGradient;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPPasswordWindowController
|
||||
@implementation MPSitesWindowController
|
||||
|
||||
#pragma mark - Life
|
||||
|
||||
@@ -67,8 +67,8 @@
|
||||
PearlAddNotificationObserver( MPSignedOutNotification, nil, [NSOperationQueue mainQueue], ^(id host, NSNotification *note) {
|
||||
[self updateUser];
|
||||
} );
|
||||
[self observeKeyPath:@"sitesController.selection" withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
|
||||
[_self updateSelection];
|
||||
[self observeKeyPath:@"sitesController.selection" withBlock:^(id from, id to, NSKeyValueChange cause, id self) {
|
||||
[self updateSelection];
|
||||
}];
|
||||
prof_rewind( @"observers" );
|
||||
|
||||
@@ -373,11 +373,11 @@
|
||||
NSArray *types = [site.algorithm allTypes];
|
||||
[self.passwordTypesMatrix renewRows:(NSInteger)[types count] columns:1];
|
||||
for (NSUInteger t = 0; t < [types count]; ++t) {
|
||||
MPSiteType type = (MPSiteType)[types[t] unsignedIntegerValue];
|
||||
MPResultType type = (MPResultType)[types[t] unsignedIntegerValue];
|
||||
NSString *title = [site.algorithm nameOfType:type];
|
||||
if (type & MPSiteTypeClassGenerated)
|
||||
title = strf( @"%@ – %@", [site.algorithm generatePasswordForSiteNamed:site.name ofType:type withCounter:site.counter
|
||||
usingKey:[MPMacAppDelegate get].key], title );
|
||||
if (type & MPResultTypeClassTemplate)
|
||||
title = strf( @"%@ – %@", [site.algorithm mpwTemplateForSiteNamed:site.name ofType:type withCounter:site.counter
|
||||
usingKey:[MPMacAppDelegate get].key], title );
|
||||
|
||||
NSButtonCell *cell = [self.passwordTypesMatrix cellAtRow:(NSInteger)t column:0];
|
||||
cell.tag = type;
|
||||
@@ -397,7 +397,7 @@
|
||||
switch (returnCode) {
|
||||
case NSAlertFirstButtonReturn: {
|
||||
// "Save" button.
|
||||
MPSiteType type = (MPSiteType)[self.passwordTypesMatrix.selectedCell tag];
|
||||
MPResultType type = (MPResultType)[self.passwordTypesMatrix.selectedCell tag];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *entity = [[MPMacAppDelegate get] changeSite:[self.selectedSite entityInContext:context]
|
||||
saveInContext:context toType:type];
|
||||
@@ -1,109 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ATSApplicationFontsPath</key>
|
||||
<string>.</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Master Password</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>mpsites</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeMIMETypes</key>
|
||||
<array>
|
||||
<string>text/plain</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Master Password sites</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.lyndir.masterpassword.sites</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Master Password</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>[auto]</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>[auto]</string>
|
||||
<key>Fabric</key>
|
||||
<dict>
|
||||
<key>APIKey</key>
|
||||
<string>0d10c90776f5ef5acd01ddbeaca9a6cba4814560</string>
|
||||
<key>Kits</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>KitInfo</key>
|
||||
<dict/>
|
||||
<key>KitName</key>
|
||||
<string>Crashlytics</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.productivity</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2011-2014 Lyndir</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>MPMacApplication</string>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.utf8-plain-text</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Master Password sites</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string></string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.lyndir.masterpassword.sites</string>
|
||||
<key>UTTypeSize320IconFile</key>
|
||||
<string></string>
|
||||
<key>UTTypeSize64IconFile</key>
|
||||
<string></string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>mpsites</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>ATSApplicationFontsPath</key>
|
||||
<string>.</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Master Password</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>mpsites</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeMIMETypes</key>
|
||||
<array>
|
||||
<string>text/plain</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Master Password sites</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.lyndir.masterpassword.sites</string>
|
||||
</array>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Master Password</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>[auto]</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>[auto]</string>
|
||||
<key>Fabric</key>
|
||||
<dict>
|
||||
<key>APIKey</key>
|
||||
<string>0d10c90776f5ef5acd01ddbeaca9a6cba4814560</string>
|
||||
<key>Kits</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>KitInfo</key>
|
||||
<dict/>
|
||||
<key>KitName</key>
|
||||
<string>Crashlytics</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.productivity</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2011-2014 Lyndir</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>MPMacApplication</string>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.utf8-plain-text</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Master Password sites</string>
|
||||
<key>UTTypeIconFile</key>
|
||||
<string></string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.lyndir.masterpassword.sites</string>
|
||||
<key>UTTypeSize320IconFile</key>
|
||||
<string></string>
|
||||
<key>UTTypeSize64IconFile</key>
|
||||
<string></string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>mpsites</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSBackgroundOnly</key>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2013 Maarten Billemont. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSBackgroundOnly</key>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2013 Maarten Billemont. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
|
||||
@interface MPAnswersViewController()
|
||||
|
||||
@property(nonatomic, strong) NSManagedObjectID *siteOID;
|
||||
@property(nonatomic) BOOL multiple;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPAnswersViewController {
|
||||
NSManagedObjectID *_siteOID;
|
||||
BOOL _multiple;
|
||||
}
|
||||
@implementation MPAnswersViewController
|
||||
|
||||
#pragma mark - Life
|
||||
|
||||
@@ -80,21 +80,21 @@
|
||||
|
||||
- (void)setSite:(MPSiteEntity *)site {
|
||||
|
||||
_siteOID = site.permanentObjectID;
|
||||
_multiple = [site.questions count] > 0;
|
||||
self.siteOID = site.permanentObjectID;
|
||||
self.multiple = [site.questions count] > 0;
|
||||
[self.tableView reloadData];
|
||||
[self updateAnimated:NO];
|
||||
}
|
||||
|
||||
- (void)setMultiple:(BOOL)multiple animated:(BOOL)animated {
|
||||
|
||||
_multiple = multiple;
|
||||
self.multiple = multiple;
|
||||
[self updateAnimated:animated];
|
||||
}
|
||||
|
||||
- (MPSiteEntity *)siteInContext:(NSManagedObjectContext *)context {
|
||||
|
||||
return [MPSiteEntity existingObjectWithID:_siteOID inContext:context];
|
||||
return [MPSiteEntity existingObjectWithID:self.siteOID inContext:context];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate
|
||||
@@ -109,7 +109,7 @@
|
||||
if (section == 0)
|
||||
return 3;
|
||||
|
||||
if (!_multiple)
|
||||
if (!self.multiple)
|
||||
return 0;
|
||||
|
||||
return [[self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]].questions count] + 1;
|
||||
@@ -128,7 +128,7 @@
|
||||
return [MPSendAnswersCell dequeueCellFromTableView:tableView indexPath:indexPath];
|
||||
if (indexPath.item == 2) {
|
||||
MPMultipleAnswersCell *cell = [MPMultipleAnswersCell dequeueCellFromTableView:tableView indexPath:indexPath];
|
||||
cell.accessoryType = _multiple? UITableViewCellAccessoryCheckmark: UITableViewCellAccessoryNone;
|
||||
cell.accessoryType = self.multiple? UITableViewCellAccessoryCheckmark: UITableViewCellAccessoryNone;
|
||||
return cell;
|
||||
}
|
||||
Throw( @"Unsupported row index: %@", indexPath );
|
||||
@@ -165,10 +165,10 @@
|
||||
[self copyAnswer:((MPGlobalAnswersCell *)cell).answerField.text];
|
||||
|
||||
else if ([cell isKindOfClass:[MPMultipleAnswersCell class]]) {
|
||||
if (!_multiple)
|
||||
if (!self.multiple)
|
||||
[self setMultiple:YES animated:YES];
|
||||
|
||||
else if (_multiple) {
|
||||
else if (self.multiple) {
|
||||
if (![site.questions count])
|
||||
[self setMultiple:NO animated:YES];
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
|
||||
else if ([cell isKindOfClass:[MPSendAnswersCell class]]) {
|
||||
NSString *body;
|
||||
if (!_multiple) {
|
||||
if (!self.multiple) {
|
||||
NSObject *answer = [site resolveSiteAnswerUsingKey:[MPiOSAppDelegate get].key];
|
||||
body = strf( @"Master Password generated the following security answer for your site: %@\n\n"
|
||||
@"%@\n"
|
||||
@@ -245,7 +245,7 @@
|
||||
|
||||
PearlMainQueue( ^{
|
||||
UITableViewCell *multipleAnswersCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForItem:2 inSection:0]];
|
||||
multipleAnswersCell.accessoryType = _multiple? UITableViewCellAccessoryCheckmark: UITableViewCellAccessoryNone;
|
||||
multipleAnswersCell.accessoryType = self.multiple? UITableViewCellAccessoryCheckmark: UITableViewCellAccessoryNone;
|
||||
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
@@ -291,19 +291,23 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPAnswersQuestionCell {
|
||||
NSManagedObjectID *_siteOID;
|
||||
NSManagedObjectID *_questionOID;
|
||||
__weak MPAnswersViewController *_answersVC;
|
||||
}
|
||||
@interface MPAnswersQuestionCell()
|
||||
|
||||
@property(nonatomic, strong) NSManagedObjectID *siteOID;
|
||||
@property(nonatomic, strong) NSManagedObjectID *questionOID;
|
||||
@property(nonatomic, weak) MPAnswersViewController *answersVC;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPAnswersQuestionCell
|
||||
|
||||
#pragma mark - State
|
||||
|
||||
- (void)setQuestion:(MPSiteQuestionEntity *)question forSite:(MPSiteEntity *)site inVC:(MPAnswersViewController *)answersVC {
|
||||
|
||||
_siteOID = site.permanentObjectID;
|
||||
_questionOID = question.permanentObjectID;
|
||||
_answersVC = answersVC;
|
||||
self.siteOID = site.permanentObjectID;
|
||||
self.questionOID = question.permanentObjectID;
|
||||
self.answersVC = answersVC;
|
||||
|
||||
[self updateAnswerForQuestion:question ofSite:site];
|
||||
}
|
||||
@@ -322,8 +326,8 @@
|
||||
NSString *keyword = textField.text;
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
BOOL didAddQuestionObject = NO;
|
||||
MPSiteEntity *site = [MPSiteEntity existingObjectWithID:_siteOID inContext:context];
|
||||
MPSiteQuestionEntity *question = [MPSiteQuestionEntity existingObjectWithID:_questionOID inContext:context];
|
||||
MPSiteEntity *site = [MPSiteEntity existingObjectWithID:self.siteOID inContext:context];
|
||||
MPSiteQuestionEntity *question = [MPSiteQuestionEntity existingObjectWithID:self.questionOID inContext:context];
|
||||
if (!question) {
|
||||
didAddQuestionObject = YES;
|
||||
[site addQuestionsObject:question = [MPSiteQuestionEntity insertNewObjectInContext:context]];
|
||||
@@ -333,11 +337,11 @@
|
||||
question.keyword = keyword;
|
||||
|
||||
if ([context saveToStore]) {
|
||||
_questionOID = question.permanentObjectID;
|
||||
self.questionOID = question.permanentObjectID;
|
||||
[self updateAnswerForQuestion:question ofSite:site];
|
||||
|
||||
if (didAddQuestionObject)
|
||||
[_answersVC didAddQuestion:question toSite:site];
|
||||
[self.answersVC didAddQuestion:question toSite:site];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
#import "MPAppSettingsViewController.h"
|
||||
#import "UIColor+Expanded.h"
|
||||
|
||||
@implementation MPAppSettingsViewController {
|
||||
}
|
||||
@implementation MPAppSettingsViewController
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
|
||||
|
||||
@@ -32,11 +32,12 @@ const long MPAvatarAdd = 10000;
|
||||
@property(strong, nonatomic) IBOutlet NSLayoutConstraint *avatarRaisedConstraint;
|
||||
@property(strong, nonatomic) IBOutlet NSLayoutConstraint *keyboardHeightConstraint;
|
||||
|
||||
@property(nonatomic, strong) CAAnimationGroup *targetedShadowAnimation;
|
||||
|
||||
@property(assign, nonatomic, readwrite) BOOL newUser;
|
||||
@end
|
||||
|
||||
@implementation MPAvatarCell {
|
||||
CAAnimationGroup *_targetedShadowAnimation;
|
||||
}
|
||||
@implementation MPAvatarCell
|
||||
|
||||
+ (NSString *)reuseIdentifier {
|
||||
|
||||
@@ -57,14 +58,14 @@ const long MPAvatarAdd = 10000;
|
||||
self.avatarImageView.layer.masksToBounds = NO;
|
||||
self.avatarImageView.backgroundColor = [UIColor clearColor];
|
||||
|
||||
[self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *_self) {
|
||||
_self.contentView.frame = _self.bounds;
|
||||
[self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
|
||||
self.contentView.frame = self.bounds;
|
||||
}];
|
||||
[self observeKeyPath:@"selected" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *_self) {
|
||||
[_self updateAnimated:_self.superview != nil];
|
||||
[self observeKeyPath:@"selected" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
|
||||
[self updateAnimated:self.superview != nil];
|
||||
}];
|
||||
[self observeKeyPath:@"highlighted" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *_self) {
|
||||
[_self updateAnimated:_self.superview != nil];
|
||||
[self observeKeyPath:@"highlighted" withBlock:^(id from, id to, NSKeyValueChange cause, MPAvatarCell *self) {
|
||||
[self updateAnimated:self.superview != nil];
|
||||
}];
|
||||
PearlAddNotificationObserver( UIKeyboardWillShowNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPAvatarCell *self, NSNotification *note) {
|
||||
@@ -85,9 +86,9 @@ const long MPAvatarAdd = 10000;
|
||||
pulseShadowOpacityAnimation.autoreverses = YES;
|
||||
pulseShadowOpacityAnimation.repeatCount = MAXFLOAT;
|
||||
|
||||
_targetedShadowAnimation = [CAAnimationGroup new];
|
||||
_targetedShadowAnimation.animations = @[ toShadowOpacityAnimation, pulseShadowOpacityAnimation ];
|
||||
_targetedShadowAnimation.duration = MAXFLOAT;
|
||||
self.targetedShadowAnimation = [CAAnimationGroup new];
|
||||
self.targetedShadowAnimation.animations = @[ toShadowOpacityAnimation, pulseShadowOpacityAnimation ];
|
||||
self.targetedShadowAnimation.duration = MAXFLOAT;
|
||||
self.avatarImageView.layer.shadowColor = [UIColor whiteColor].CGColor;
|
||||
self.avatarImageView.layer.shadowOffset = CGSizeZero;
|
||||
}
|
||||
@@ -96,10 +97,10 @@ const long MPAvatarAdd = 10000;
|
||||
|
||||
[super prepareForReuse];
|
||||
|
||||
_newUser = NO;
|
||||
_visibility = 0;
|
||||
_mode = MPAvatarModeLowered;
|
||||
_spinnerActive = NO;
|
||||
self.newUser = NO;
|
||||
self.visibility = 0;
|
||||
self.mode = MPAvatarModeLowered;
|
||||
self.spinnerActive = NO;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
@@ -114,13 +115,13 @@ const long MPAvatarAdd = 10000;
|
||||
|
||||
_avatar = avatar == MPAvatarAdd? MPAvatarAdd: (avatar + MPAvatarCount) % MPAvatarCount;
|
||||
|
||||
if (_avatar == MPAvatarAdd) {
|
||||
if (self.avatar == MPAvatarAdd) {
|
||||
self.avatarImageView.image = [UIImage imageNamed:@"avatar-add"];
|
||||
self.name = strl( @"New User" );
|
||||
_newUser = YES;
|
||||
self.newUser = YES;
|
||||
}
|
||||
else
|
||||
self.avatarImageView.image = [UIImage imageNamed:strf( @"avatar-%lu", (unsigned long)_avatar )];
|
||||
self.avatarImageView.image = [UIImage imageNamed:strf( @"avatar-%lu", (unsigned long)self.avatar )];
|
||||
}
|
||||
|
||||
- (NSString *)name {
|
||||
@@ -140,7 +141,7 @@ const long MPAvatarAdd = 10000;
|
||||
|
||||
- (void)setVisibility:(CGFloat)visibility animated:(BOOL)animated {
|
||||
|
||||
if (visibility == _visibility)
|
||||
if (self.visibility == visibility)
|
||||
return;
|
||||
_visibility = visibility;
|
||||
|
||||
@@ -161,7 +162,7 @@ const long MPAvatarAdd = 10000;
|
||||
|
||||
- (void)setMode:(MPAvatarMode)mode animated:(BOOL)animated {
|
||||
|
||||
if (mode == _mode)
|
||||
if (self.mode == mode)
|
||||
return;
|
||||
_mode = mode;
|
||||
|
||||
@@ -175,7 +176,7 @@ const long MPAvatarAdd = 10000;
|
||||
|
||||
- (void)setSpinnerActive:(BOOL)spinnerActive animated:(BOOL)animated {
|
||||
|
||||
if (_spinnerActive == spinnerActive)
|
||||
if (self.spinnerActive == spinnerActive)
|
||||
return;
|
||||
_spinnerActive = spinnerActive;
|
||||
|
||||
@@ -284,7 +285,7 @@ const long MPAvatarAdd = 10000;
|
||||
if (self.mode == MPAvatarModeRaisedAndMinimized)
|
||||
[self.avatarImageView.layer removeAnimationForKey:@"targetedShadow"];
|
||||
else if (![self.avatarImageView.layer animationForKey:@"targetedShadow"])
|
||||
[self.avatarImageView.layer addAnimation:_targetedShadowAnimation forKey:@"targetedShadow"];
|
||||
[self.avatarImageView.layer addAnimation:self.targetedShadowAnimation forKey:@"targetedShadow"];
|
||||
|
||||
// Avatar selection and spinner.
|
||||
if (self.mode != MPAvatarModeRaisedAndMinimized && (self.selected || self.highlighted) && !self.spinnerActive)
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#import "MPCell.h"
|
||||
|
||||
@implementation MPCell {
|
||||
}
|
||||
@implementation MPCell
|
||||
|
||||
@end
|
||||
|
||||
@@ -18,17 +18,23 @@
|
||||
|
||||
#import "MPCoachmarkViewController.h"
|
||||
|
||||
@implementation MPCoachmarkViewController {
|
||||
NSArray *_views;
|
||||
NSUInteger _nextView;
|
||||
__weak NSTimer *_viewTimer;
|
||||
}
|
||||
@interface MPCoachmarkViewController()
|
||||
|
||||
@property(nonatomic, strong) NSArray *views;
|
||||
@property(nonatomic) NSUInteger nextView;
|
||||
@property(nonatomic, weak) NSTimer *viewTimer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPCoachmarkViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
|
||||
[super viewDidLoad];
|
||||
|
||||
_views = @[ self.view0, self.view1, self.view2, self.view3, self.view4, self.view5, self.view6, self.view7, self.view8, self.view9 ];
|
||||
self.views = @[
|
||||
self.view0, self.view1, self.view2, self.view3, self.view4, self.view5, self.view6, self.view7, self.view8, self.view9
|
||||
];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
@@ -37,8 +43,8 @@
|
||||
|
||||
self.viewProgress.visible = YES;
|
||||
self.viewProgress.progress = 0;
|
||||
[_views makeObjectsPerformSelector:@selector( setVisible: ) withObject:@NO];
|
||||
_nextView = 0;
|
||||
[self.views makeObjectsPerformSelector:@selector( setVisible: ) withObject:@NO];
|
||||
self.nextView = 0;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
@@ -46,23 +52,23 @@
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
[_views[_nextView++] setVisible:YES];
|
||||
[self.views[self.nextView++] setVisible:YES];
|
||||
}];
|
||||
|
||||
_viewTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 block:^(NSTimer *timer) {
|
||||
self.viewTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 block:^(NSTimer *timer) {
|
||||
self.viewProgress.progress += 1.0f / 50;
|
||||
|
||||
if (self.viewProgress.progress == 1)
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.viewProgress.progress = 0;
|
||||
[_views[_nextView++] setVisible:YES];
|
||||
[self.views[self.nextView++] setVisible:YES];
|
||||
|
||||
if (_nextView >= [_views count]) {
|
||||
[_viewTimer invalidate];
|
||||
if (self.nextView >= [self.views count]) {
|
||||
[self.viewTimer invalidate];
|
||||
self.viewProgress.visible = NO;
|
||||
}
|
||||
}];
|
||||
} repeats:YES];
|
||||
} repeats:YES];
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
//==============================================================================
|
||||
|
||||
#import "MPUsersViewController.h"
|
||||
#import "MPPasswordsViewController.h"
|
||||
#import "MPSitesViewController.h"
|
||||
#import "MPEmergencyViewController.h"
|
||||
|
||||
typedef NS_ENUM( NSUInteger, MPCombinedMode ) {
|
||||
@@ -29,7 +29,7 @@ typedef NS_ENUM( NSUInteger, MPCombinedMode ) {
|
||||
|
||||
@property(nonatomic) MPCombinedMode mode;
|
||||
@property(nonatomic, weak) MPUsersViewController *usersVC;
|
||||
@property(nonatomic, weak) MPPasswordsViewController *passwordsVC;
|
||||
@property(nonatomic, weak) MPSitesViewController *sitesVC;
|
||||
@property(nonatomic, weak) MPEmergencyViewController *emergencyVC;
|
||||
|
||||
@end
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
#import "MPCombinedViewController.h"
|
||||
#import "MPUsersViewController.h"
|
||||
#import "MPPasswordsViewController.h"
|
||||
#import "MPSitesViewController.h"
|
||||
#import "MPEmergencyViewController.h"
|
||||
#import "MPPasswordsSegue.h"
|
||||
#import "MPSitesSegue.h"
|
||||
|
||||
@implementation MPCombinedViewController
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
[super viewDidLoad];
|
||||
|
||||
_mode = MPCombinedModeUserSelection;
|
||||
self.mode = MPCombinedModeUserSelection;
|
||||
[self performSegueWithIdentifier:@"users" sender:self];
|
||||
}
|
||||
|
||||
@@ -67,12 +67,12 @@
|
||||
if ([segue.identifier isEqualToString:@"users"])
|
||||
self.usersVC = segue.destinationViewController;
|
||||
if ([segue.identifier isEqualToString:@"passwords"]) {
|
||||
NSAssert( [segue isKindOfClass:[MPPasswordsSegue class]], @"passwords segue should be MPPasswordsSegue: %@", segue );
|
||||
NSAssert( [segue isKindOfClass:[MPSitesSegue class]], @"passwords segue should be MPSitesSegue: %@", segue );
|
||||
NSAssert( [sender isKindOfClass:[NSDictionary class]], @"sender should be dictionary: %@", sender );
|
||||
NSAssert( [[sender objectForKey:@"animated"] isKindOfClass:[NSNumber class]], @"sender should contain 'animated': %@", sender );
|
||||
[(MPPasswordsSegue *)segue setAnimated:[sender[@"animated"] boolValue]];
|
||||
[(MPSitesSegue *)segue setAnimated:[sender[@"animated"] boolValue]];
|
||||
UIViewController *destinationVC = segue.destinationViewController;
|
||||
_passwordsVC = [destinationVC isKindOfClass:[MPPasswordsViewController class]]? (MPPasswordsViewController *)destinationVC: nil;
|
||||
self.sitesVC = [destinationVC isKindOfClass:[MPSitesViewController class]]? (MPSitesViewController *)destinationVC: nil;
|
||||
}
|
||||
if ([segue.identifier isEqualToString:@"emergency"])
|
||||
self.emergencyVC = segue.destinationViewController;
|
||||
@@ -115,7 +115,7 @@
|
||||
|
||||
- (void)setMode:(MPCombinedMode)mode animated:(BOOL)animated {
|
||||
|
||||
if (_mode == mode && animated)
|
||||
if (self.mode == mode && animated)
|
||||
return;
|
||||
_mode = mode;
|
||||
|
||||
@@ -129,8 +129,8 @@
|
||||
case MPCombinedModeUserSelection: {
|
||||
self.usersVC.view.userInteractionEnabled = YES;
|
||||
[self.usersVC setActive:YES animated:animated];
|
||||
if (_passwordsVC) {
|
||||
MPPasswordsSegue *segue = [[MPPasswordsSegue alloc] initWithIdentifier:@"passwords" source:_passwordsVC destination:self];
|
||||
if (self.sitesVC) {
|
||||
MPSitesSegue *segue = [[MPSitesSegue alloc] initWithIdentifier:@"passwords" source:self.sitesVC destination:self];
|
||||
[self prepareForSegue:segue sender:@{ @"animated": @(animated) }];
|
||||
[segue perform];
|
||||
}
|
||||
|
||||
@@ -19,18 +19,22 @@
|
||||
#import "MPEmergencyViewController.h"
|
||||
#import "MPEntities.h"
|
||||
|
||||
@implementation MPEmergencyViewController {
|
||||
MPKey *_key;
|
||||
NSOperationQueue *_emergencyKeyQueue;
|
||||
NSOperationQueue *_emergencyPasswordQueue;
|
||||
}
|
||||
@interface MPEmergencyViewController()
|
||||
|
||||
@property(nonatomic, strong) MPKey *key;
|
||||
@property(nonatomic, strong) NSOperationQueue *emergencyKeyQueue;
|
||||
@property(nonatomic, strong) NSOperationQueue *emergencyPasswordQueue;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPEmergencyViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
|
||||
[super viewDidLoad];
|
||||
|
||||
[_emergencyKeyQueue = [NSOperationQueue new] setMaxConcurrentOperationCount:1];
|
||||
[_emergencyPasswordQueue = [NSOperationQueue new] setMaxConcurrentOperationCount:1];
|
||||
[self.emergencyKeyQueue = [NSOperationQueue new] setMaxConcurrentOperationCount:1];
|
||||
[self.emergencyPasswordQueue = [NSOperationQueue new] setMaxConcurrentOperationCount:1];
|
||||
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
self.dialogView.layer.cornerRadius = 5;
|
||||
@@ -117,12 +121,12 @@
|
||||
|
||||
[self.passwordButton setTitle:nil forState:UIControlStateNormal];
|
||||
[self.activity startAnimating];
|
||||
[_emergencyKeyQueue cancelAllOperations];
|
||||
[_emergencyKeyQueue addOperationWithBlock:^{
|
||||
[self.emergencyKeyQueue cancelAllOperations];
|
||||
[self.emergencyKeyQueue addOperationWithBlock:^{
|
||||
if ([masterPassword length] && [fullName length])
|
||||
_key = [[MPKey alloc] initForFullName:fullName withMasterPassword:masterPassword];
|
||||
self.key = [[MPKey alloc] initForFullName:fullName withMasterPassword:masterPassword];
|
||||
else
|
||||
_key = nil;
|
||||
self.key = nil;
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[self updatePassword];
|
||||
@@ -133,17 +137,18 @@
|
||||
- (void)updatePassword {
|
||||
|
||||
NSString *siteName = self.siteField.text;
|
||||
MPSiteType siteType = [self siteType];
|
||||
NSUInteger siteCounter = (NSUInteger)self.counterStepper.value;
|
||||
self.counterLabel.text = strf( @"%lu", (unsigned long)siteCounter );
|
||||
MPResultType siteType = [self siteType];
|
||||
MPCounterValue siteCounter = (MPCounterValue)self.counterStepper.value;
|
||||
self.counterLabel.text = strf( @"%u", siteCounter );
|
||||
|
||||
[self.passwordButton setTitle:nil forState:UIControlStateNormal];
|
||||
[self.activity startAnimating];
|
||||
[_emergencyPasswordQueue cancelAllOperations];
|
||||
[_emergencyPasswordQueue addOperationWithBlock:^{
|
||||
[self.emergencyPasswordQueue cancelAllOperations];
|
||||
[self.emergencyPasswordQueue addOperationWithBlock:^{
|
||||
NSString *sitePassword = nil;
|
||||
if (_key && [siteName length])
|
||||
sitePassword = [MPAlgorithmDefault generatePasswordForSiteNamed:siteName ofType:siteType withCounter:siteCounter usingKey:_key];
|
||||
if (self.key && [siteName length])
|
||||
sitePassword = [MPAlgorithmDefault mpwTemplateForSiteNamed:siteName ofType:siteType withCounter:siteCounter
|
||||
usingKey:self.key];
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[self.activity stopAnimating];
|
||||
@@ -152,21 +157,21 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (enum MPSiteType)siteType {
|
||||
- (MPResultType)siteType {
|
||||
|
||||
switch (self.typeControl.selectedSegmentIndex) {
|
||||
case 0:
|
||||
return MPSiteTypeGeneratedMaximum;
|
||||
return MPResultTypeTemplateMaximum;
|
||||
case 1:
|
||||
return MPSiteTypeGeneratedLong;
|
||||
return MPResultTypeTemplateLong;
|
||||
case 2:
|
||||
return MPSiteTypeGeneratedMedium;
|
||||
return MPResultTypeTemplateMedium;
|
||||
case 3:
|
||||
return MPSiteTypeGeneratedBasic;
|
||||
return MPResultTypeTemplateBasic;
|
||||
case 4:
|
||||
return MPSiteTypeGeneratedShort;
|
||||
return MPResultTypeTemplateShort;
|
||||
case 5:
|
||||
return MPSiteTypeGeneratedPIN;
|
||||
return MPResultTypeTemplatePIN;
|
||||
default:
|
||||
Throw( @"Unsupported type index: %ld", (long)self.typeControl.selectedSegmentIndex );
|
||||
}
|
||||
|
||||
@@ -18,15 +18,19 @@
|
||||
|
||||
#import "MPOverlayViewController.h"
|
||||
|
||||
@implementation MPOverlayViewController {
|
||||
NSMutableDictionary *_dismissSegueByButton;
|
||||
}
|
||||
@interface MPOverlayViewController()
|
||||
|
||||
@property(nonatomic, strong) NSMutableDictionary *dismissSegueByButton;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPOverlayViewController
|
||||
|
||||
- (void)awakeFromNib {
|
||||
|
||||
[super awakeFromNib];
|
||||
|
||||
_dismissSegueByButton = [NSMutableDictionary dictionary];
|
||||
self.dismissSegueByButton = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
@@ -60,7 +64,7 @@
|
||||
dismissButton.visible = NO;
|
||||
dismissButton.frame = self.view.bounds;
|
||||
dismissButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_dismissSegueByButton[[NSValue valueWithNonretainedObject:dismissButton]] =
|
||||
self.dismissSegueByButton[[NSValue valueWithNonretainedObject:dismissButton]] =
|
||||
[[MPOverlaySegue alloc] initWithIdentifier:@"dismiss-overlay"
|
||||
source:segue.destinationViewController destination:segue.sourceViewController];
|
||||
|
||||
@@ -71,14 +75,14 @@
|
||||
- (void)dismissOverlay:(UIButton *)dismissButton {
|
||||
|
||||
NSValue *dismissSegueKey = [NSValue valueWithNonretainedObject:dismissButton];
|
||||
[((UIStoryboardSegue *)_dismissSegueByButton[dismissSegueKey]) perform];
|
||||
[((UIStoryboardSegue *)self.dismissSegueByButton[dismissSegueKey]) perform];
|
||||
}
|
||||
|
||||
- (void)removeDismissButtonForViewController:(UIViewController *)viewController {
|
||||
|
||||
UIButton *dismissButton = nil;
|
||||
for (NSValue *dismissButtonValue in [_dismissSegueByButton allKeys])
|
||||
if (((UIStoryboardSegue *)_dismissSegueByButton[dismissButtonValue]).sourceViewController == viewController) {
|
||||
for (NSValue *dismissButtonValue in [self.dismissSegueByButton allKeys])
|
||||
if (((UIStoryboardSegue *)self.dismissSegueByButton[dismissButtonValue]).sourceViewController == viewController) {
|
||||
dismissButton = [dismissButtonValue nonretainedObjectValue];
|
||||
NSAssert( [self.view.subviews containsObject:dismissButton], @"Missing dismiss button in dictionary." );
|
||||
}
|
||||
@@ -86,7 +90,7 @@
|
||||
return;
|
||||
|
||||
NSValue *dismissSegueKey = [NSValue valueWithNonretainedObject:dismissButton];
|
||||
[_dismissSegueByButton removeObjectForKey:dismissSegueKey];
|
||||
[self.dismissSegueByButton removeObjectForKey:dismissSegueKey];
|
||||
|
||||
[UIView animateWithDuration:0.1f animations:^{
|
||||
dismissButton.visible = NO;
|
||||
|
||||
@@ -17,16 +17,15 @@
|
||||
//==============================================================================
|
||||
|
||||
#import "MPPopdownSegue.h"
|
||||
#import "MPPasswordsViewController.h"
|
||||
#import "MPSitesViewController.h"
|
||||
|
||||
@implementation MPPopdownSegue {
|
||||
}
|
||||
@implementation MPPopdownSegue
|
||||
|
||||
- (void)perform {
|
||||
|
||||
MPPasswordsViewController *passwordsVC;
|
||||
MPSitesViewController *passwordsVC;
|
||||
UIViewController *popdownVC;
|
||||
if ([self.sourceViewController isKindOfClass:[MPPasswordsViewController class]]) {
|
||||
if ([self.sourceViewController isKindOfClass:[MPSitesViewController class]]) {
|
||||
passwordsVC = self.sourceViewController;
|
||||
popdownVC = self.destinationViewController;
|
||||
UIView *popdownView = popdownVC.view;
|
||||
@@ -55,7 +54,7 @@
|
||||
}
|
||||
else {
|
||||
popdownVC = self.sourceViewController;
|
||||
for (passwordsVC = self.sourceViewController; passwordsVC && ![(id)passwordsVC isKindOfClass:[MPPasswordsViewController class]];
|
||||
for (passwordsVC = self.sourceViewController; passwordsVC && ![(id)passwordsVC isKindOfClass:[MPSitesViewController class]];
|
||||
passwordsVC = (id)passwordsVC.parentViewController);
|
||||
NSAssert( passwordsVC, @"Couldn't find passwords VC to pop back to." );
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *generated1TypeControl;
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *generated2TypeControl;
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *storedTypeControl;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *passwordTypeExample;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *typeSamplePassword;
|
||||
|
||||
- (IBAction)previousAvatar:(id)sender;
|
||||
- (IBAction)nextAvatar:(id)sender;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "UIColor+Expanded.h"
|
||||
#import "MPPasswordsViewController.h"
|
||||
#import "MPSitesViewController.h"
|
||||
#import "MPAppDelegate_InApp.h"
|
||||
|
||||
@interface MPPreferencesViewController()
|
||||
@@ -60,17 +60,17 @@
|
||||
self.touchIDSwitch.on = activeUser.touchID;
|
||||
self.touchIDSwitch.enabled = self.savePasswordSwitch.on && [[MPiOSAppDelegate get] isFeatureUnlocked:MPProductTouchID];
|
||||
|
||||
MPSiteType defaultType = activeUser.defaultType;
|
||||
MPResultType defaultType = activeUser.defaultType;
|
||||
self.generated1TypeControl.selectedSegmentIndex = [self generated1SegmentIndexForType:defaultType];
|
||||
self.generated2TypeControl.selectedSegmentIndex = [self generated2SegmentIndexForType:defaultType];
|
||||
self.storedTypeControl.selectedSegmentIndex = [self storedSegmentIndexForType:defaultType];
|
||||
PearlNotMainQueue( ^{
|
||||
NSString *examplePassword = nil;
|
||||
if (defaultType & MPSiteTypeClassGenerated)
|
||||
examplePassword = [MPAlgorithmDefault generatePasswordForSiteNamed:@"test" ofType:defaultType
|
||||
withCounter:1 usingKey:[MPiOSAppDelegate get].key];
|
||||
if (defaultType & MPResultTypeClassTemplate)
|
||||
examplePassword = [MPAlgorithmDefault mpwTemplateForSiteNamed:@"test" ofType:defaultType
|
||||
withCounter:1 usingKey:[MPiOSAppDelegate get].key];
|
||||
PearlMainQueue( ^{
|
||||
self.passwordTypeExample.text = [examplePassword length]? [NSString stringWithFormat:@"eg. %@", examplePassword]: nil;
|
||||
self.typeSamplePassword.text = [examplePassword length]? [NSString stringWithFormat:@"eg. %@", examplePassword]: nil;
|
||||
} );
|
||||
} );
|
||||
}
|
||||
@@ -108,7 +108,7 @@
|
||||
[[MPiOSAppDelegate get] showExportForVC:self];
|
||||
|
||||
if (cell == self.showHelpCell) {
|
||||
MPPasswordsViewController *passwordsVC = [self dismissPopup];
|
||||
MPSitesViewController *passwordsVC = [self dismissPopup];
|
||||
[passwordsVC performSegueWithIdentifier:@"guide" sender:self];
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
if (sender != self.storedTypeControl)
|
||||
self.storedTypeControl.selectedSegmentIndex = -1;
|
||||
|
||||
MPSiteType defaultType = [self typeForSelectedSegment];
|
||||
MPResultType defaultType = [self typeForSelectedSegment];
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
[[MPiOSAppDelegate get] activeUserInContext:context].defaultType = defaultType;
|
||||
[context saveToStore];
|
||||
@@ -230,11 +230,11 @@
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (MPPasswordsViewController *)dismissPopup {
|
||||
- (MPSitesViewController *)dismissPopup {
|
||||
|
||||
for (UIViewController *vc = self; (vc = vc.parentViewController);)
|
||||
if ([vc isKindOfClass:[MPPasswordsViewController class]]) {
|
||||
MPPasswordsViewController *passwordsVC = (MPPasswordsViewController *)vc;
|
||||
if ([vc isKindOfClass:[MPSitesViewController class]]) {
|
||||
MPSitesViewController *passwordsVC = (MPSitesViewController *)vc;
|
||||
[passwordsVC dismissPopdown:self];
|
||||
return passwordsVC;
|
||||
}
|
||||
@@ -242,7 +242,7 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (MPSiteType)typeForSelectedSegment {
|
||||
- (MPResultType)typeForSelectedSegment {
|
||||
|
||||
NSInteger selectedGenerated1Index = self.generated1TypeControl.selectedSegmentIndex;
|
||||
NSInteger selectedGenerated2Index = self.generated2TypeControl.selectedSegmentIndex;
|
||||
@@ -250,76 +250,76 @@
|
||||
|
||||
switch (selectedGenerated1Index) {
|
||||
case 0:
|
||||
return MPSiteTypeGeneratedPhrase;
|
||||
return MPResultTypeTemplatePhrase;
|
||||
case 1:
|
||||
return MPSiteTypeGeneratedName;
|
||||
return MPResultTypeTemplateName;
|
||||
default:
|
||||
switch (selectedGenerated2Index) {
|
||||
case 0:
|
||||
return MPSiteTypeGeneratedMaximum;
|
||||
case 1:
|
||||
return MPSiteTypeGeneratedLong;
|
||||
case 2:
|
||||
return MPSiteTypeGeneratedMedium;
|
||||
case 3:
|
||||
return MPSiteTypeGeneratedBasic;
|
||||
case 4:
|
||||
return MPSiteTypeGeneratedShort;
|
||||
case 5:
|
||||
return MPSiteTypeGeneratedPIN;
|
||||
default:
|
||||
|
||||
switch (selectedStoredIndex) {
|
||||
switch (selectedGenerated2Index) {
|
||||
case 0:
|
||||
return MPSiteTypeStoredPersonal;
|
||||
return MPResultTypeTemplateMaximum;
|
||||
case 1:
|
||||
return MPSiteTypeStoredDevicePrivate;
|
||||
return MPResultTypeTemplateLong;
|
||||
case 2:
|
||||
return MPResultTypeTemplateMedium;
|
||||
case 3:
|
||||
return MPResultTypeTemplateBasic;
|
||||
case 4:
|
||||
return MPResultTypeTemplateShort;
|
||||
case 5:
|
||||
return MPResultTypeTemplatePIN;
|
||||
default:
|
||||
Throw( @"unsupported selected type index: generated1=%ld, generated2=%ld, stored=%ld",
|
||||
(long)selectedGenerated1Index, (long)selectedGenerated2Index, (long)selectedStoredIndex );
|
||||
|
||||
switch (selectedStoredIndex) {
|
||||
case 0:
|
||||
return MPResultTypeStatefulPersonal;
|
||||
case 1:
|
||||
return MPResultTypeStatefulDevice;
|
||||
default:
|
||||
Throw( @"unsupported selected type index: generated1=%ld, generated2=%ld, stored=%ld",
|
||||
(long)selectedGenerated1Index, (long)selectedGenerated2Index, (long)selectedStoredIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)generated1SegmentIndexForType:(MPSiteType)type {
|
||||
- (NSInteger)generated1SegmentIndexForType:(MPResultType)type {
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
case MPResultTypeTemplatePhrase:
|
||||
return 0;
|
||||
case MPSiteTypeGeneratedName:
|
||||
case MPResultTypeTemplateName:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)generated2SegmentIndexForType:(MPSiteType)type {
|
||||
- (NSInteger)generated2SegmentIndexForType:(MPResultType)type {
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
case MPResultTypeTemplateMaximum:
|
||||
return 0;
|
||||
case MPSiteTypeGeneratedLong:
|
||||
case MPResultTypeTemplateLong:
|
||||
return 1;
|
||||
case MPSiteTypeGeneratedMedium:
|
||||
case MPResultTypeTemplateMedium:
|
||||
return 2;
|
||||
case MPSiteTypeGeneratedBasic:
|
||||
case MPResultTypeTemplateBasic:
|
||||
return 3;
|
||||
case MPSiteTypeGeneratedShort:
|
||||
case MPResultTypeTemplateShort:
|
||||
return 4;
|
||||
case MPSiteTypeGeneratedPIN:
|
||||
case MPResultTypeTemplatePIN:
|
||||
return 5;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)storedSegmentIndexForType:(MPSiteType)type {
|
||||
- (NSInteger)storedSegmentIndexForType:(MPResultType)type {
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeStoredPersonal:
|
||||
case MPResultTypeStatefulPersonal:
|
||||
return 0;
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
case MPResultTypeStatefulDevice:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
|
||||
#import "MPRootSegue.h"
|
||||
|
||||
@implementation MPRootSegue {
|
||||
}
|
||||
@implementation MPRootSegue
|
||||
|
||||
- (void)perform {
|
||||
|
||||
|
||||
@@ -20,18 +20,18 @@
|
||||
#import "MPEntities.h"
|
||||
#import "MPCell.h"
|
||||
|
||||
typedef NS_ENUM ( NSUInteger, MPPasswordCellMode ) {
|
||||
typedef NS_ENUM ( NSUInteger, MPSiteCellMode ) {
|
||||
MPPasswordCellModePassword,
|
||||
MPPasswordCellModeSettings,
|
||||
};
|
||||
|
||||
@interface MPPasswordCell : MPCell<UIScrollViewDelegate, UITextFieldDelegate>
|
||||
@interface MPSiteCell : MPCell<UIScrollViewDelegate, UITextFieldDelegate>
|
||||
|
||||
@property(nonatomic) NSArray *fuzzyGroups;
|
||||
|
||||
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated;
|
||||
- (void)setTransientSite:(NSString *)siteName animated:(BOOL)animated;
|
||||
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated;
|
||||
- (void)setMode:(MPSiteCellMode)mode animated:(BOOL)animated;
|
||||
- (MPSiteEntity *)siteInContext:(NSManagedObjectContext *)context;
|
||||
|
||||
@end
|
||||
@@ -16,13 +16,12 @@
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#import "MPPasswordCell.h"
|
||||
#import "MPSiteCell.h"
|
||||
#import "MPiOSAppDelegate.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "UIColor+Expanded.h"
|
||||
#import "MPAppDelegate_InApp.h"
|
||||
|
||||
@interface MPPasswordCell()
|
||||
@interface MPSiteCell()
|
||||
|
||||
@property(nonatomic, strong) IBOutlet UILabel *siteNameLabel;
|
||||
@property(nonatomic, strong) IBOutlet UITextField *passwordField;
|
||||
@@ -39,16 +38,16 @@
|
||||
@property(nonatomic, strong) IBOutlet UIScrollView *modeScrollView;
|
||||
@property(nonatomic, strong) IBOutlet UIButton *contentButton;
|
||||
@property(nonatomic, strong) IBOutlet UIButton *loginNameButton;
|
||||
@property(nonatomic, strong) IBOutlet UILabel *loginNameHint;
|
||||
@property(nonatomic, strong) IBOutlet UIView *indicatorView;
|
||||
|
||||
@property(nonatomic) MPPasswordCellMode mode;
|
||||
@property(nonatomic) MPSiteCellMode mode;
|
||||
@property(nonatomic, copy) NSString *transientSite;
|
||||
@property(nonatomic, strong) NSManagedObjectID *siteOID;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPPasswordCell {
|
||||
NSManagedObjectID *_siteOID;
|
||||
}
|
||||
@implementation MPSiteCell
|
||||
|
||||
#pragma mark - Life cycle
|
||||
|
||||
@@ -65,9 +64,9 @@
|
||||
|
||||
[self setupLayer];
|
||||
|
||||
[self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPPasswordCell *_self) {
|
||||
[self observeKeyPath:@"bounds" withBlock:^(id from, id to, NSKeyValueChange cause, MPSiteCell *self) {
|
||||
if (from && !CGSizeEqualToSize( [from CGRectValue].size, [to CGRectValue].size ))
|
||||
[_self setupLayer];
|
||||
[self setupLayer];
|
||||
}];
|
||||
[self.contentButton observeKeyPath:@"highlighted"
|
||||
withBlock:^(id from, id to, NSKeyValueChange cause, UIButton *button) {
|
||||
@@ -135,8 +134,8 @@
|
||||
|
||||
[super prepareForReuse];
|
||||
|
||||
_siteOID = nil;
|
||||
_fuzzyGroups = nil;
|
||||
self.siteOID = nil;
|
||||
self.fuzzyGroups = nil;
|
||||
self.transientSite = nil;
|
||||
self.mode = MPPasswordCellModePassword;
|
||||
[self updateAnimated:NO];
|
||||
@@ -153,25 +152,25 @@
|
||||
|
||||
- (void)setFuzzyGroups:(NSArray *)fuzzyGroups {
|
||||
|
||||
if (_fuzzyGroups == fuzzyGroups)
|
||||
if (self.fuzzyGroups == fuzzyGroups)
|
||||
return;
|
||||
_fuzzyGroups = fuzzyGroups;
|
||||
|
||||
[self updateSiteName:[self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]];
|
||||
}
|
||||
|
||||
- (void)setMode:(MPPasswordCellMode)mode animated:(BOOL)animated {
|
||||
- (void)setMode:(MPSiteCellMode)mode animated:(BOOL)animated {
|
||||
|
||||
if (mode == _mode)
|
||||
if (self.mode == mode)
|
||||
return;
|
||||
|
||||
_mode = mode;
|
||||
|
||||
[self updateAnimated:animated];
|
||||
}
|
||||
|
||||
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated {
|
||||
|
||||
_siteOID = site.permanentObjectID;
|
||||
self.siteOID = site.permanentObjectID;
|
||||
[self updateAnimated:animated];
|
||||
}
|
||||
|
||||
@@ -200,7 +199,7 @@
|
||||
atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES];
|
||||
|
||||
if (textField == self.loginNameField)
|
||||
self.loginNameButton.hidden = [self.loginNameField.attributedText length] || self.loginNameField.enabled;
|
||||
self.loginNameHint.hidden = [self.loginNameField.attributedText length] || self.loginNameField.enabled;
|
||||
}
|
||||
|
||||
- (IBAction)textFieldDidChange:(UITextField *)textField {
|
||||
@@ -285,7 +284,7 @@
|
||||
[PearlSheet showSheetWithTitle:@"Change Password Type" viewStyle:UIActionSheetStyleAutomatic
|
||||
initSheet:^(UIActionSheet *sheet) {
|
||||
for (NSNumber *typeNumber in [mainSite.algorithm allTypes]) {
|
||||
MPSiteType type = (MPSiteType)[typeNumber unsignedIntegerValue];
|
||||
MPResultType type = (MPResultType)[typeNumber unsignedIntegerValue];
|
||||
NSString *typeName = [mainSite.algorithm nameOfType:type];
|
||||
if (type == mainSite.type)
|
||||
[sheet addButtonWithTitle:strf( @"● %@", typeName )];
|
||||
@@ -296,7 +295,7 @@
|
||||
if (buttonIndex == [sheet cancelButtonIndex])
|
||||
return;
|
||||
|
||||
MPSiteType type = (MPSiteType)[[mainSite.algorithm allTypes][buttonIndex] unsignedIntegerValue]?:
|
||||
MPResultType type = (MPResultType)[[mainSite.algorithm allTypes][buttonIndex] unsignedIntegerValue]?:
|
||||
mainSite.user.defaultType?: mainSite.algorithm.defaultType;
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
@@ -312,7 +311,7 @@
|
||||
self.loginNameField.enabled = YES;
|
||||
self.passwordField.enabled = YES;
|
||||
|
||||
if ([self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]].type & MPSiteTypeClassStored)
|
||||
if ([self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]].type & MPResultTypeClassStateful)
|
||||
[self.passwordField becomeFirstResponder];
|
||||
else
|
||||
[self.loginNameField becomeFirstResponder];
|
||||
@@ -368,7 +367,7 @@
|
||||
}
|
||||
|
||||
- (IBAction)doAction:(UIButton *)sender {
|
||||
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
|
||||
MPSiteEntity *mainSite = [self siteInContext:mainContext];
|
||||
[PearlAlert showAlertWithTitle:@"Login Page" message:nil
|
||||
@@ -531,14 +530,14 @@
|
||||
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
|
||||
// UI
|
||||
self.backgroundColor = mainSite.url? [UIColor greenColor]: [UIColor redColor];
|
||||
//self.backgroundColor = mainSite.url? [UIColor greenColor]: [UIColor redColor];
|
||||
self.upgradeButton.gone = !mainSite.requiresExplicitMigration && ![[MPiOSConfig get].allowDowngrade boolValue];
|
||||
self.answersButton.gone = ![[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers];
|
||||
BOOL settingsMode = self.mode == MPPasswordCellModeSettings;
|
||||
self.loginNameContainer.visible = settingsMode || mainSite.loginGenerated || [mainSite.loginName length];
|
||||
self.modeButton.visible = !self.transientSite;
|
||||
self.modeButton.alpha = settingsMode? 0.5f: 0.1f;
|
||||
self.counterLabel.visible = self.counterButton.visible = mainSite.type & MPSiteTypeClassGenerated;
|
||||
self.counterLabel.visible = self.counterButton.visible = mainSite.type & MPResultTypeClassTemplate;
|
||||
self.modeButton.selected = settingsMode;
|
||||
self.strengthLabel.gone = !settingsMode;
|
||||
self.modeScrollView.scrollEnabled = !self.transientSite;
|
||||
@@ -548,9 +547,9 @@
|
||||
[self.passwordField resignFirstResponder];
|
||||
}
|
||||
if ([[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateLogins])
|
||||
[self.loginNameButton setTitle:@"Tap here to ⚙ generate username or the pencil to type one" forState:UIControlStateNormal];
|
||||
self.loginNameHint.text = @"Tap here to ⚙ generate username or the pencil to type one";
|
||||
else
|
||||
[self.loginNameButton setTitle:@"Tap the pencil to type a username" forState:UIControlStateNormal];
|
||||
self.loginNameHint.text = @"Tap the pencil to type a username";
|
||||
|
||||
// Site Name
|
||||
[self updateSiteName:mainSite];
|
||||
@@ -566,8 +565,8 @@
|
||||
// Site Password
|
||||
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
|
||||
self.passwordField.attributedPlaceholder = stra(
|
||||
mainSite.type & MPSiteTypeClassStored? strl( @"No password" ):
|
||||
mainSite.type & MPSiteTypeClassGenerated? strl( @"..." ): @"", @{
|
||||
mainSite.type & MPResultTypeClassStateful? strl( @"No password" ):
|
||||
mainSite.type & MPResultTypeClassTemplate? strl( @"..." ): @"", @{
|
||||
NSForegroundColorAttributeName: [UIColor whiteColor]
|
||||
} );
|
||||
|
||||
@@ -586,10 +585,10 @@
|
||||
|
||||
BOOL loginGenerated = site.loginGenerated;
|
||||
NSString *password = nil, *loginName = [site resolveLoginUsingKey:key];
|
||||
MPSiteType transientType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPAlgorithmDefault.defaultType;
|
||||
if (self.transientSite && transientType & MPSiteTypeClassGenerated)
|
||||
password = [MPAlgorithmDefault generatePasswordForSiteNamed:self.transientSite ofType:transientType
|
||||
withCounter:1 usingKey:key];
|
||||
MPResultType transientType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPAlgorithmDefault.defaultType;
|
||||
if (self.transientSite && transientType & MPResultTypeClassTemplate)
|
||||
password = [MPAlgorithmDefault mpwTemplateForSiteNamed:self.transientSite ofType:transientType
|
||||
withCounter:1 usingKey:key];
|
||||
else if (site)
|
||||
password = [site resolvePasswordUsingKey:key];
|
||||
|
||||
@@ -609,7 +608,7 @@
|
||||
self.loginNameGenerated.hidden = !loginGenerated;
|
||||
self.loginNameField.attributedText =
|
||||
strarm( stra( loginName?: @"", self.siteNameLabel.textAttributes ), NSParagraphStyleAttributeName, nil );
|
||||
self.loginNameButton.hidden = [loginName length] || self.loginNameField.enabled;
|
||||
self.loginNameHint.hidden = [loginName length] || self.loginNameField.enabled;
|
||||
|
||||
if (![password length]) {
|
||||
self.indicatorView.hidden = NO;
|
||||
@@ -678,7 +677,7 @@
|
||||
[self.window endEditing:YES];
|
||||
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
if ([pasteboard respondsToSelector:@selector(setItems:options:)]) {
|
||||
if ([pasteboard respondsToSelector:@selector( setItems:options: )]) {
|
||||
[pasteboard setItems:@[ @{ UIPasteboardTypeAutomatic: password } ]
|
||||
options:@{
|
||||
UIPasteboardOptionLocalOnly : @NO,
|
||||
@@ -718,7 +717,7 @@
|
||||
|
||||
- (MPSiteEntity *)siteInContext:(NSManagedObjectContext *)context {
|
||||
|
||||
return [MPSiteEntity existingObjectWithID:_siteOID inContext:context];
|
||||
return [MPSiteEntity existingObjectWithID:self.siteOID inContext:context];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MPPasswordsSegue : UIStoryboardSegue
|
||||
@interface MPSitesSegue : UIStoryboardSegue
|
||||
|
||||
@property(nonatomic, assign) BOOL animated;
|
||||
|
||||
@@ -16,12 +16,11 @@
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#import "MPPasswordsSegue.h"
|
||||
#import "MPPasswordsViewController.h"
|
||||
#import "MPSitesSegue.h"
|
||||
#import "MPSitesViewController.h"
|
||||
#import "MPCombinedViewController.h"
|
||||
|
||||
@implementation MPPasswordsSegue {
|
||||
}
|
||||
@implementation MPSitesSegue
|
||||
|
||||
- (id)initWithIdentifier:(NSString *)identifier source:(UIViewController *)source destination:(UIViewController *)destination {
|
||||
|
||||
@@ -35,34 +34,34 @@
|
||||
|
||||
- (void)perform {
|
||||
|
||||
if ([self.destinationViewController isKindOfClass:[MPPasswordsViewController class]]) {
|
||||
__weak MPPasswordsViewController *passwordsVC = self.destinationViewController;
|
||||
if ([self.destinationViewController isKindOfClass:[MPSitesViewController class]]) {
|
||||
__weak MPSitesViewController *sitesVC = self.destinationViewController;
|
||||
MPCombinedViewController *combinedVC = self.sourceViewController;
|
||||
[combinedVC addChildViewController:passwordsVC];
|
||||
passwordsVC.active = NO;
|
||||
[combinedVC addChildViewController:sitesVC];
|
||||
sitesVC.active = NO;
|
||||
|
||||
UIView *passwordsView = passwordsVC.view;
|
||||
passwordsView.frame = combinedVC.view.bounds;
|
||||
passwordsView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
[combinedVC.view insertSubview:passwordsView belowSubview:combinedVC.usersVC.view];
|
||||
UIView *sitesView = sitesVC.view;
|
||||
sitesView.frame = combinedVC.view.bounds;
|
||||
sitesView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
[combinedVC.view insertSubview:sitesView belowSubview:combinedVC.usersVC.view];
|
||||
|
||||
[passwordsVC setActive:YES animated:self.animated completion:^(BOOL finished) {
|
||||
[sitesVC setActive:YES animated:self.animated completion:^(BOOL finished) {
|
||||
if (!finished)
|
||||
return;
|
||||
|
||||
[passwordsVC didMoveToParentViewController:combinedVC];
|
||||
[sitesVC didMoveToParentViewController:combinedVC];
|
||||
}];
|
||||
}
|
||||
else if ([self.sourceViewController isKindOfClass:[MPPasswordsViewController class]]) {
|
||||
__weak MPPasswordsViewController *passwordsVC = self.sourceViewController;
|
||||
else if ([self.sourceViewController isKindOfClass:[MPSitesViewController class]]) {
|
||||
__weak MPSitesViewController *sitesVC = self.sourceViewController;
|
||||
|
||||
[passwordsVC willMoveToParentViewController:nil];
|
||||
[passwordsVC setActive:NO animated:self.animated completion:^(BOOL finished) {
|
||||
[sitesVC willMoveToParentViewController:nil];
|
||||
[sitesVC setActive:NO animated:self.animated completion:^(BOOL finished) {
|
||||
if (!finished)
|
||||
return;
|
||||
|
||||
[passwordsVC.view removeFromSuperview];
|
||||
[passwordsVC removeFromParentViewController];
|
||||
[sitesVC.view removeFromSuperview];
|
||||
[sitesVC removeFromParentViewController];
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -19,22 +19,22 @@
|
||||
@class MPSiteEntity;
|
||||
@class MPCoachmark;
|
||||
|
||||
@interface MPPasswordsViewController : UIViewController<UISearchBarDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
||||
@interface MPSitesViewController : UIViewController<UISearchBarDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
||||
|
||||
@property(strong, nonatomic) IBOutlet UIView *passwordSelectionContainer;
|
||||
@property(strong, nonatomic) IBOutlet UICollectionView *passwordCollectionView;
|
||||
@property(strong, nonatomic) IBOutlet UISearchBar *passwordsSearchBar;
|
||||
@property(strong, nonatomic) IBOutlet NSLayoutConstraint *passwordsToBottomConstraint;
|
||||
@property(strong, nonatomic) IBOutlet NSLayoutConstraint *navigationBarToTopConstraint;
|
||||
@property(strong, nonatomic) IBOutlet NSLayoutConstraint *popdownToTopConstraint;
|
||||
@property(strong, nonatomic) IBOutlet UIView *badNameTipContainer;
|
||||
@property(strong, nonatomic) IBOutlet UIView *popdownView;
|
||||
@property(strong, nonatomic) IBOutlet UIView *popdownContainer;
|
||||
@property(nonatomic, strong) IBOutlet UICollectionView *collectionView;
|
||||
@property(nonatomic, strong) IBOutlet UINavigationBar *navigationBar;
|
||||
@property(nonatomic, strong) IBOutlet UISearchBar *searchBar;
|
||||
@property(nonatomic, strong) IBOutlet NSLayoutConstraint *sitesToBottomConstraint;
|
||||
@property(nonatomic, strong) IBOutlet NSLayoutConstraint *navigationBarToTopConstraint;
|
||||
@property(nonatomic, strong) IBOutlet NSLayoutConstraint *popdownToTopConstraint;
|
||||
@property(nonatomic, strong) IBOutlet UIView *badNameTipContainer;
|
||||
@property(nonatomic, strong) IBOutlet UIView *popdownView;
|
||||
@property(nonatomic, strong) IBOutlet UIView *popdownContainer;
|
||||
|
||||
@property(assign, nonatomic) BOOL active;
|
||||
|
||||
- (void)setActive:(BOOL)active animated:(BOOL)animated completion:(void ( ^ )(BOOL finished))completion;
|
||||
- (void)reloadPasswords;
|
||||
- (void)reloadSites;
|
||||
|
||||
- (IBAction)dismissPopdown:(id)sender;
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#import "MPPasswordsViewController.h"
|
||||
#import "MPSitesViewController.h"
|
||||
#import "MPiOSAppDelegate.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "MPPopdownSegue.h"
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPPasswordCell.h"
|
||||
#import "MPSiteCell.h"
|
||||
#import "MPAnswersViewController.h"
|
||||
#import "MPMessageViewController.h"
|
||||
|
||||
@@ -31,22 +31,17 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
MPPasswordsBadNameTip = 1 << 0,
|
||||
};
|
||||
|
||||
@interface MPPasswordsViewController()<NSFetchedResultsControllerDelegate>
|
||||
@interface MPSitesViewController()<NSFetchedResultsControllerDelegate>
|
||||
|
||||
@property(nonatomic, strong) IBOutlet UINavigationBar *navigationBar;
|
||||
@property(nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
|
||||
@property(nonatomic, strong) NSArray *fuzzyGroups;
|
||||
@property(nonatomic, strong) NSCharacterSet *siteNameAcceptableCharactersSet;
|
||||
@property(nonatomic, strong) NSMutableArray<NSMutableArray *> *dataSource;
|
||||
@property(nonatomic, weak) UIViewController *popdownVC;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPPasswordsViewController {
|
||||
__weak UITapGestureRecognizer *_passwordsDismissRecognizer;
|
||||
NSFetchedResultsController *_fetchedResultsController;
|
||||
UIColor *_backgroundColor;
|
||||
UIColor *_darkenedBackgroundColor;
|
||||
__weak UIViewController *_popdownVC;
|
||||
NSCharacterSet *_siteNameAcceptableCharactersSet;
|
||||
NSArray *_fuzzyGroups;
|
||||
NSMutableArray<NSMutableArray *> *_passwordCollectionSections;
|
||||
}
|
||||
@implementation MPSitesViewController
|
||||
|
||||
#pragma mark - Life
|
||||
|
||||
@@ -57,22 +52,20 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
NSMutableCharacterSet *siteNameAcceptableCharactersSet = [[NSCharacterSet alphanumericCharacterSet] mutableCopy];
|
||||
[siteNameAcceptableCharactersSet formIntersectionWithCharacterSet:[[NSCharacterSet uppercaseLetterCharacterSet] invertedSet]];
|
||||
[siteNameAcceptableCharactersSet addCharactersInString:@"@.-+~&_;:/"];
|
||||
_siteNameAcceptableCharactersSet = siteNameAcceptableCharactersSet;
|
||||
self.siteNameAcceptableCharactersSet = siteNameAcceptableCharactersSet;
|
||||
|
||||
_backgroundColor = self.passwordCollectionView.backgroundColor;
|
||||
_darkenedBackgroundColor = [_backgroundColor colorWithAlphaComponent:0.6f];
|
||||
_passwordCollectionSections = [NSMutableArray new];
|
||||
self.dataSource = [NSMutableArray new];
|
||||
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
[self.passwordCollectionView automaticallyAdjustInsetsForKeyboard];
|
||||
self.passwordsSearchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
if ([self.passwordsSearchBar respondsToSelector:@selector( keyboardAppearance )])
|
||||
self.passwordsSearchBar.keyboardAppearance = UIKeyboardAppearanceDark;
|
||||
[self.collectionView automaticallyAdjustInsetsForKeyboard];
|
||||
self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
||||
if ([self.searchBar respondsToSelector:@selector( keyboardAppearance )])
|
||||
self.searchBar.keyboardAppearance = UIKeyboardAppearanceDark;
|
||||
else
|
||||
[self.passwordsSearchBar enumerateViews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
|
||||
[self.searchBar enumerateViews:^(UIView *subview, BOOL *stop, BOOL *recurse) {
|
||||
if ([subview isKindOfClass:[UITextField class]])
|
||||
((UITextField *)subview).keyboardAppearance = UIKeyboardAppearanceDark;
|
||||
} recurse:YES];
|
||||
} recurse:YES];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
@@ -96,9 +89,9 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
pasteboardURL = [NSURL URLWithString:pasteboard.string];
|
||||
|
||||
if (pasteboardURL.host)
|
||||
self.query = NSNullToNil( [pasteboardURL.host firstMatchGroupsOfExpression:bareHostRE][0] );
|
||||
self.query = NSNullToNil( [[pasteboardURL.host firstMatchGroupsOfExpression:bareHostRE] firstObject] );
|
||||
else
|
||||
[self reloadPasswords];
|
||||
[self reloadSites];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
@@ -131,17 +124,17 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
|
||||
if ([segue.identifier isEqualToString:@"popdown"])
|
||||
_popdownVC = segue.destinationViewController;
|
||||
self.popdownVC = segue.destinationViewController;
|
||||
if ([segue.identifier isEqualToString:@"answers"])
|
||||
((MPAnswersViewController *)segue.destinationViewController).site =
|
||||
[[MPPasswordCell findAsSuperviewOf:sender] siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
[[MPSiteCell findAsSuperviewOf:sender] siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
if ([segue.identifier isEqualToString:@"message"])
|
||||
((MPMessageViewController *)segue.destinationViewController).message = sender;
|
||||
}
|
||||
|
||||
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
|
||||
|
||||
[self.passwordCollectionView.collectionViewLayout invalidateLayout];
|
||||
[self.collectionView.collectionViewLayout invalidateLayout];
|
||||
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
||||
}
|
||||
|
||||
@@ -159,7 +152,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
insetForSectionAtIndex:(NSInteger)section {
|
||||
|
||||
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
|
||||
UIEdgeInsets occludedInsets = [self.passwordCollectionView occludedInsets];
|
||||
UIEdgeInsets occludedInsets = [self.collectionView occludedInsets];
|
||||
UIEdgeInsets insets = layout.sectionInset;
|
||||
insets.top = insets.bottom; // Undo storyboard hack for manual top-occluded insets.
|
||||
|
||||
@@ -176,19 +169,19 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
|
||||
|
||||
return [_passwordCollectionSections count];
|
||||
return [self.dataSource count];
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
|
||||
return [_passwordCollectionSections[(NSUInteger)section] count];
|
||||
return [self.dataSource[(NSUInteger)section] count];
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath];
|
||||
[cell setFuzzyGroups:_fuzzyGroups];
|
||||
id item = _passwordCollectionSections[(NSUInteger)indexPath.section][(NSUInteger)indexPath.item];
|
||||
MPSiteCell *cell = [MPSiteCell dequeueCellFromCollectionView:collectionView indexPath:indexPath];
|
||||
[cell setFuzzyGroups:self.fuzzyGroups];
|
||||
id item = self.dataSource[(NSUInteger)indexPath.section][(NSUInteger)indexPath.item];
|
||||
if ([item isKindOfClass:[MPSiteEntity class]])
|
||||
[cell setSite:item animated:NO];
|
||||
else // item == MPTransientPasswordItem
|
||||
@@ -201,8 +194,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
|
||||
|
||||
if (scrollView == self.passwordCollectionView)
|
||||
for (MPPasswordCell *cell in [self.passwordCollectionView visibleCells])
|
||||
if (scrollView == self.collectionView)
|
||||
for (MPSiteCell *cell in [self.collectionView visibleCells])
|
||||
[cell setMode:MPPasswordCellModePassword animated:YES];
|
||||
}
|
||||
|
||||
@@ -210,11 +203,11 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
|
||||
|
||||
if (controller == _fetchedResultsController)
|
||||
if (controller == self.fetchedResultsController)
|
||||
PearlMainQueue( ^{
|
||||
[self.passwordCollectionView updateDataSource:_passwordCollectionSections
|
||||
toSections:[self createPasswordCollectionSections]
|
||||
reloadItems:nil completion:nil];
|
||||
[self.collectionView updateDataSource:self.dataSource
|
||||
toSections:[self createDataSource]
|
||||
reloadItems:nil completion:nil];
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -222,7 +215,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
|
||||
|
||||
if (searchBar == self.passwordsSearchBar) {
|
||||
if (searchBar == self.searchBar) {
|
||||
searchBar.text = nil;
|
||||
return YES;
|
||||
}
|
||||
@@ -232,24 +225,22 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
|
||||
|
||||
if (searchBar == self.passwordsSearchBar) {
|
||||
[self.passwordsSearchBar setShowsCancelButton:YES animated:YES];
|
||||
if (searchBar == self.searchBar) {
|
||||
[self.searchBar setShowsCancelButton:YES animated:YES];
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.passwordCollectionView.backgroundColor = _darkenedBackgroundColor;
|
||||
self.collectionView.backgroundColor = [self.collectionView.backgroundColor colorWithAlphaComponent:0.6f];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
|
||||
|
||||
if (searchBar == self.passwordsSearchBar) {
|
||||
[self.passwordsSearchBar setShowsCancelButton:NO animated:YES];
|
||||
if (_passwordsDismissRecognizer)
|
||||
[self.view removeGestureRecognizer:_passwordsDismissRecognizer];
|
||||
if (searchBar == self.searchBar) {
|
||||
[self.searchBar setShowsCancelButton:NO animated:YES];
|
||||
[self reloadSites];
|
||||
|
||||
[self reloadPasswords];
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.passwordCollectionView.backgroundColor = _backgroundColor;
|
||||
self.collectionView.backgroundColor = [self.collectionView.backgroundColor colorWithAlphaComponent:0];
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -267,11 +258,11 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
|
||||
|
||||
if (searchBar == self.passwordsSearchBar) {
|
||||
if ([[self.query stringByTrimmingCharactersInSet:_siteNameAcceptableCharactersSet] length])
|
||||
if (searchBar == self.searchBar) {
|
||||
if ([[self.query stringByTrimmingCharactersInSet:self.siteNameAcceptableCharactersSet] length])
|
||||
[self showTips:MPPasswordsBadNameTip];
|
||||
|
||||
[self reloadPasswords];
|
||||
[self reloadSites];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +283,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSMutableArray<NSMutableArray *> *)createPasswordCollectionSections {
|
||||
- (NSMutableArray<NSMutableArray *> *)createDataSource {
|
||||
|
||||
NSString *query = self.query;
|
||||
BOOL needTransientItem = [query length] > 0;
|
||||
@@ -321,42 +312,42 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
PearlRemoveNotificationObservers();
|
||||
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
^(MPSitesViewController *self, NSNotification *note) {
|
||||
[self.view endEditing:YES];
|
||||
self.passwordSelectionContainer.visible = NO;
|
||||
self.view.visible = NO;
|
||||
} );
|
||||
PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
^(MPSitesViewController *self, NSNotification *note) {
|
||||
[UIView animateWithDuration:0.7f animations:^{
|
||||
self.passwordSelectionContainer.visible = YES;
|
||||
self.view.visible = YES;
|
||||
}];
|
||||
} );
|
||||
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
^(MPSitesViewController *self, NSNotification *note) {
|
||||
[self viewWillAppear:YES];
|
||||
} );
|
||||
PearlAddNotificationObserver( MPSignedOutNotification, nil, nil,
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
^(MPSitesViewController *self, NSNotification *note) {
|
||||
PearlMainQueue( ^{
|
||||
self->_fetchedResultsController = nil;
|
||||
self.fetchedResultsController = nil;
|
||||
self.query = nil;
|
||||
} );
|
||||
} );
|
||||
PearlAddNotificationObserver( MPCheckConfigNotification, nil, nil,
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
^(MPSitesViewController *self, NSNotification *note) {
|
||||
PearlMainQueue( ^{
|
||||
[self updateConfigKey:note.object];
|
||||
} );
|
||||
} );
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, nil, nil,
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
self->_fetchedResultsController = nil;
|
||||
[self reloadPasswords];
|
||||
^(MPSitesViewController *self, NSNotification *note) {
|
||||
self.fetchedResultsController = nil;
|
||||
[self reloadSites];
|
||||
} );
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresDidChangeNotification, nil, nil,
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
^(MPSitesViewController *self, NSNotification *note) {
|
||||
PearlMainQueue( ^{
|
||||
[self reloadPasswords];
|
||||
[self reloadSites];
|
||||
[self registerObservers];
|
||||
} );
|
||||
} );
|
||||
@@ -373,12 +364,12 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
- (void)updateConfigKey:(NSString *)key {
|
||||
|
||||
if (!key || [key isEqualToString:NSStringFromSelector( @selector( dictationSearch ) )])
|
||||
self.passwordsSearchBar.keyboardType = [[MPiOSConfig get].dictationSearch boolValue]? UIKeyboardTypeDefault: UIKeyboardTypeURL;
|
||||
self.searchBar.keyboardType = [[MPiOSConfig get].dictationSearch boolValue]? UIKeyboardTypeDefault: UIKeyboardTypeURL;
|
||||
if (!key || [key isEqualToString:NSStringFromSelector( @selector( hidePasswords ) )])
|
||||
[self.passwordCollectionView reloadData];
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
|
||||
- (void)reloadPasswords {
|
||||
- (void)reloadSites {
|
||||
|
||||
[self.fetchedResultsController.managedObjectContext performBlock:^{
|
||||
static NSRegularExpression *fuzzyRE;
|
||||
@@ -395,7 +386,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||
[fuzzyGroups addObject:[queryString substringWithRange:result.range]];
|
||||
}];
|
||||
_fuzzyGroups = fuzzyGroups;
|
||||
self.fuzzyGroups = fuzzyGroups;
|
||||
|
||||
NSError *error = nil;
|
||||
self.fetchedResultsController.fetchRequest.predicate =
|
||||
@@ -404,11 +395,11 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
MPError( error, @"Couldn't fetch sites." );
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[self.passwordCollectionView updateDataSource:_passwordCollectionSections
|
||||
toSections:[self createPasswordCollectionSections]
|
||||
reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
|
||||
for (MPPasswordCell *cell in self.passwordCollectionView.visibleCells)
|
||||
[cell setFuzzyGroups:_fuzzyGroups];
|
||||
[self.collectionView updateDataSource:self.dataSource
|
||||
toSections:[self createDataSource]
|
||||
reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
|
||||
for (MPSiteCell *cell in self.collectionView.visibleCells)
|
||||
[cell setFuzzyGroups:self.fuzzyGroups];
|
||||
}];
|
||||
} );
|
||||
}];
|
||||
@@ -418,13 +409,13 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
- (NSString *)query {
|
||||
|
||||
return [self.passwordsSearchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
return [self.searchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
}
|
||||
|
||||
- (void)setQuery:(NSString *)query {
|
||||
|
||||
self.passwordsSearchBar.text = [query stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
[self reloadPasswords];
|
||||
self.searchBar.text = [query stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
[self reloadSites];
|
||||
}
|
||||
|
||||
- (NSFetchedResultsController *)fetchedResultsController {
|
||||
@@ -436,10 +427,9 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
|
||||
];
|
||||
fetchRequest.fetchBatchSize = 10;
|
||||
_fetchedResultsController =
|
||||
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:mainContext
|
||||
sectionNameKeyPath:nil cacheName:nil];
|
||||
_fetchedResultsController.delegate = self;
|
||||
(self.fetchedResultsController = [[NSFetchedResultsController alloc]
|
||||
initWithFetchRequest:fetchRequest managedObjectContext:mainContext
|
||||
sectionNameKeyPath:nil cacheName:nil]).delegate = self;
|
||||
}];
|
||||
[self registerObservers];
|
||||
}
|
||||
@@ -458,7 +448,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
[UIView animateWithDuration:animated? 0.4f: 0 animations:^{
|
||||
[self.navigationBarToTopConstraint updatePriority:active? 1: UILayoutPriorityDefaultHigh];
|
||||
[self.passwordsToBottomConstraint updatePriority:active? 1: UILayoutPriorityDefaultHigh];
|
||||
[self.sitesToBottomConstraint updatePriority:active? 1: UILayoutPriorityDefaultHigh];
|
||||
[self.view layoutIfNeeded];
|
||||
} completion:completion];
|
||||
}
|
||||
@@ -467,8 +457,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
- (IBAction)dismissPopdown:(id)sender {
|
||||
|
||||
if (_popdownVC)
|
||||
[[[MPPopdownSegue alloc] initWithIdentifier:@"unwind-popdown" source:_popdownVC destination:self] perform];
|
||||
if (self.popdownVC)
|
||||
[[[MPPopdownSegue alloc] initWithIdentifier:@"unwind-popdown" source:self.popdownVC destination:self] perform];
|
||||
else
|
||||
self.popdownToTopConstraint.priority = UILayoutPriorityDefaultHigh;
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
#import "MPiOSAppDelegate.h"
|
||||
#import "UIColor+Expanded.h"
|
||||
#import "MPAppDelegate_InApp.h"
|
||||
#import "MPPasswordsViewController.h"
|
||||
#import "MPSitesViewController.h"
|
||||
|
||||
PearlEnum( MPDevelopmentFuelConsumption,
|
||||
MPDevelopmentFuelConsumptionQuarterly, MPDevelopmentFuelConsumptionMonthly, MPDevelopmentFuelWeekly );
|
||||
@@ -191,11 +191,11 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (MPPasswordsViewController *)dismissPopup {
|
||||
- (MPSitesViewController *)dismissPopup {
|
||||
|
||||
for (UIViewController *vc = self; (vc = vc.parentViewController);)
|
||||
if ([vc isKindOfClass:[MPPasswordsViewController class]]) {
|
||||
MPPasswordsViewController *passwordsVC = (MPPasswordsViewController *)vc;
|
||||
if ([vc isKindOfClass:[MPSitesViewController class]]) {
|
||||
MPSitesViewController *passwordsVC = (MPSitesViewController *)vc;
|
||||
[passwordsVC dismissPopdown:self];
|
||||
return passwordsVC;
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
@protocol MPTypeDelegate<NSObject>
|
||||
|
||||
@required
|
||||
- (void)didSelectType:(MPSiteType)type;
|
||||
- (MPSiteType)selectedType;
|
||||
- (void)didSelectType:(MPResultType)type;
|
||||
- (MPResultType)selectedType;
|
||||
|
||||
@optional
|
||||
- (MPSiteEntity *)selectedSite;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
@interface MPTypeViewController()
|
||||
|
||||
- (MPSiteType)typeAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (MPResultType)typeAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
@end
|
||||
|
||||
@@ -75,21 +75,21 @@
|
||||
if ([self.delegate respondsToSelector:@selector( selectedSite )])
|
||||
selectedSite = [self.delegate selectedSite];
|
||||
|
||||
MPSiteType cellType = [self typeAtIndexPath:indexPath];
|
||||
MPSiteType selectedType = selectedSite? selectedSite.type: [self.delegate selectedType];
|
||||
MPResultType cellType = [self typeAtIndexPath:indexPath];
|
||||
MPResultType selectedType = selectedSite? selectedSite.type: [self.delegate selectedType];
|
||||
cell.selected = (selectedType == cellType);
|
||||
|
||||
if (cellType != (MPSiteType)NSNotFound && cellType & MPSiteTypeClassGenerated) {
|
||||
if (cellType != (MPResultType)NSNotFound && cellType & MPResultTypeClassTemplate) {
|
||||
[(UITextField *)[cell viewWithTag:2] setText:@"..."];
|
||||
|
||||
NSString *name = selectedSite.name;
|
||||
NSUInteger counter = 0;
|
||||
MPCounterValue counter = 0;
|
||||
if ([selectedSite isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
counter = ((MPGeneratedSiteEntity *)selectedSite).counter;
|
||||
|
||||
PearlNotMainQueue( ^{
|
||||
NSString *typeContent = [MPAlgorithmDefault generatePasswordForSiteNamed:name ofType:cellType
|
||||
withCounter:counter usingKey:[MPiOSAppDelegate get].key];
|
||||
NSString *typeContent = [MPAlgorithmDefault mpwTemplateForSiteNamed:name ofType:cellType
|
||||
withCounter:counter usingKey:[MPiOSAppDelegate get].key];
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[(UITextField *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent];
|
||||
@@ -104,8 +104,8 @@
|
||||
|
||||
NSAssert( self.navigationController.topViewController == self, @"Not the currently active navigation item." );
|
||||
|
||||
MPSiteType type = [self typeAtIndexPath:indexPath];
|
||||
if (type == (MPSiteType)NSNotFound)
|
||||
MPResultType type = [self typeAtIndexPath:indexPath];
|
||||
if (type == (MPResultType)NSNotFound)
|
||||
// Selected a non-type row.
|
||||
return;
|
||||
|
||||
@@ -113,28 +113,28 @@
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
- (MPSiteType)typeAtIndexPath:(NSIndexPath *)indexPath {
|
||||
- (MPResultType)typeAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
switch (indexPath.section) {
|
||||
case 0: {
|
||||
// Generated
|
||||
switch (indexPath.row) {
|
||||
case 0:
|
||||
return (MPSiteType)NSNotFound;
|
||||
return (MPResultType)NSNotFound;
|
||||
case 1:
|
||||
return MPSiteTypeGeneratedMaximum;
|
||||
return MPResultTypeTemplateMaximum;
|
||||
case 2:
|
||||
return MPSiteTypeGeneratedLong;
|
||||
return MPResultTypeTemplateLong;
|
||||
case 3:
|
||||
return MPSiteTypeGeneratedMedium;
|
||||
return MPResultTypeTemplateMedium;
|
||||
case 4:
|
||||
return MPSiteTypeGeneratedBasic;
|
||||
return MPResultTypeTemplateBasic;
|
||||
case 5:
|
||||
return MPSiteTypeGeneratedShort;
|
||||
return MPResultTypeTemplateShort;
|
||||
case 6:
|
||||
return MPSiteTypeGeneratedPIN;
|
||||
return MPResultTypeTemplatePIN;
|
||||
case 7:
|
||||
return (MPSiteType)NSNotFound;
|
||||
return (MPResultType)NSNotFound;
|
||||
|
||||
default: {
|
||||
Throw( @"Unsupported row: %ld, when selecting generated site type.", (long)indexPath.row );
|
||||
@@ -146,13 +146,13 @@
|
||||
// Stored
|
||||
switch (indexPath.row) {
|
||||
case 0:
|
||||
return (MPSiteType)NSNotFound;
|
||||
return (MPResultType)NSNotFound;
|
||||
case 1:
|
||||
return MPSiteTypeStoredPersonal;
|
||||
return MPResultTypeStatefulPersonal;
|
||||
case 2:
|
||||
return MPSiteTypeStoredDevicePrivate;
|
||||
return MPResultTypeStatefulDevice;
|
||||
case 3:
|
||||
return (MPSiteType)NSNotFound;
|
||||
return (MPResultType)NSNotFound;
|
||||
|
||||
default: {
|
||||
Throw( @"Unsupported row: %ld, when selecting stored site type.", (long)indexPath.row );
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user