2
0

Reorganize core source and add Docker support to CLI.

This commit is contained in:
Maarten Billemont
2018-06-05 20:01:46 -04:00
parent 8e41cba7ac
commit c2aafd8602
215 changed files with 65 additions and 47 deletions

View File

@@ -0,0 +1,80 @@
import org.gradle.internal.jvm.Jvm
plugins {
id 'cpp-library'
id 'base'
}
description = 'Master Password Algorithm Implementation'
artifacts {
'default' task( type: Zip, "archive" ) {
components.withType( ComponentWithRuntimeFile ) {
if (isOptimized()) {
from getRuntimeFile()
into standardOperatingSystem( linkTask.get().targetPlatform.get() ) + '/' +
standardArchitecture( linkTask.get().targetPlatform.get() )
}
}
}
}
library {
linkage.set( [Linkage.STATIC, Linkage.SHARED] )
// Reconfigure the toolchain from C++ to C.
toolChains {
withType( GccCompatibleToolChain ) {
//TODO: Cross-compiling, blocked by: https://github.com/gradle/gradle-native/issues/169
//setTargets( "arm", "arm64", "x86-64", "x86" )
eachPlatform {
cppCompiler.withArguments { addAll( ["-x", "c", "-std=c11", "-Werror", "-DMPW_SODIUM=1"] ) }
}
}
}
components.withType( CppComponent ) {
cppSource.from fileTree( dir: "src", include: "**/*.c" )
}
// Cross-compile for these host platforms.
operatingSystems.set( [objects.named( OperatingSystemFamily, OperatingSystemFamily.WINDOWS ),
objects.named( OperatingSystemFamily, OperatingSystemFamily.LINUX ),
objects.named( OperatingSystemFamily, OperatingSystemFamily.MAC_OS )] )
//
binaries.configureEach {
// Resolve a standard name for the platform.
def platform = standardOperatingSystem( targetPlatform )
project.dependencies {
// Depend on JDK for JNI support.
add( includePathConfiguration.name,
files( new File( Jvm.current().javaHome, "include" ) ) { first().eachDir { from it } } )
// Depend on libsodium from `lib`; run `lib/bin/build_libsodium-${os}` first.
add( includePathConfiguration.name,
files( "../../lib/libsodium/build-${platform}~/out/include" ) )
add( linkLibraries.name,
fileTree( "../../lib/libsodium/build-${platform}~/out/lib" ) )
}
}
}
static String standardOperatingSystem(NativePlatform platform) {
OperatingSystem os = platform.getOperatingSystem()
if (os.isWindows()) {
return OperatingSystemFamily.WINDOWS
} else if (os.isLinux()) {
return OperatingSystemFamily.LINUX
} else if (os.isMacOsX()) {
return OperatingSystemFamily.MAC_OS
}
return os.name.toLowerCase()
}
static String standardArchitecture(NativePlatform platform) {
Architecture arch = platform.getArchitecture()
return arch.name.toLowerCase().replaceAll( "-", "_" )
}

View File

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

View File

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

View File

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

View 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);

View File

@@ -0,0 +1,226 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include "mpw-algorithm.h"
#include "mpw-algorithm_v0.c"
#include "mpw-algorithm_v1.c"
#include "mpw-algorithm_v2.c"
#include "mpw-algorithm_v3.c"
MPMasterKey mpw_masterKey(const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) {
if (fullName && !strlen( fullName ))
fullName = NULL;
if (masterPassword && !strlen( masterPassword ))
masterPassword = NULL;
trc( "-- mpw_masterKey (algorithm: %u)", algorithmVersion );
trc( "fullName: %s", fullName );
trc( "masterPassword.id: %s", masterPassword? mpw_id_buf( masterPassword, strlen( masterPassword ) ): NULL );
if (!fullName || !masterPassword)
return NULL;
switch (algorithmVersion) {
case MPAlgorithmVersion0:
return mpw_masterKey_v0( fullName, masterPassword );
case MPAlgorithmVersion1:
return mpw_masterKey_v1( fullName, masterPassword );
case MPAlgorithmVersion2:
return mpw_masterKey_v2( fullName, masterPassword );
case MPAlgorithmVersion3:
return mpw_masterKey_v3( fullName, masterPassword );
default:
err( "Unsupported version: %d", algorithmVersion );
return NULL;
}
}
MPSiteKey mpw_siteKey(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext, const MPAlgorithmVersion algorithmVersion) {
if (keyContext && !strlen( keyContext ))
keyContext = NULL;
trc( "-- mpw_siteKey (algorithm: %u)", algorithmVersion );
trc( "siteName: %s", siteName );
trc( "siteCounter: %d", siteCounter );
trc( "keyPurpose: %d (%s)", keyPurpose, mpw_nameForPurpose( keyPurpose ) );
trc( "keyContext: %s", keyContext );
if (!masterKey || !siteName)
return NULL;
switch (algorithmVersion) {
case MPAlgorithmVersion0:
return mpw_siteKey_v0( masterKey, siteName, siteCounter, keyPurpose, keyContext );
case MPAlgorithmVersion1:
return mpw_siteKey_v1( masterKey, siteName, siteCounter, keyPurpose, keyContext );
case MPAlgorithmVersion2:
return mpw_siteKey_v2( masterKey, siteName, siteCounter, keyPurpose, keyContext );
case MPAlgorithmVersion3:
return mpw_siteKey_v3( masterKey, siteName, siteCounter, keyPurpose, keyContext );
default:
err( "Unsupported version: %d", 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 (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)", algorithmVersion );
trc( "resultType: %d (%s)", resultType, mpw_nameForType( resultType ) );
trc( "resultParam: %s", 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", 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", 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", algorithmVersion );
return NULL;
}
}
else {
err( "Unsupported password type: %d", 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 (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_siteState (algorithm: %u)", algorithmVersion );
trc( "resultType: %d (%s)", resultType, mpw_nameForType( resultType ) );
trc( "resultParam: %zu bytes = %s", sizeof( resultParam ), 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", algorithmVersion );
return NULL;
}
}
MPIdenticon mpw_identicon(const char *fullName, const char *masterPassword) {
const char *leftArm[] = { "", "", "", "" };
const char *rightArm[] = { "", "", "", "" };
const char *body[] = { "", "", "", "", "", "" };
const char *accessory[] = {
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
};
const uint8_t *identiconSeed = NULL;
if (fullName && strlen( fullName ) && masterPassword && strlen( masterPassword ))
identiconSeed = mpw_hash_hmac_sha256(
(const uint8_t *)masterPassword, strlen( masterPassword ),
(const uint8_t *)fullName, strlen( fullName ) );
if (!identiconSeed)
return (MPIdenticon){
.leftArm = "",
.body = "",
.rightArm = "",
.accessory = "",
.color=0,
};
MPIdenticon identicon = {
.leftArm = leftArm[identiconSeed[0] % (sizeof( leftArm ) / sizeof( leftArm[0] ))],
.body = body[identiconSeed[1] % (sizeof( body ) / sizeof( body[0] ))],
.rightArm = rightArm[identiconSeed[2] % (sizeof( rightArm ) / sizeof( rightArm[0] ))],
.accessory = accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( accessory[0] ))],
.color = (uint8_t)(identiconSeed[4] % (MPIdenticonColorLast - MPIdenticonColorFirst + 1) + MPIdenticonColorFirst),
};
mpw_free( &identiconSeed, 32 );
return identicon;
}

View File

@@ -0,0 +1,72 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
// NOTE: mpw is currently NOT thread-safe.
#include "mpw-types.h"
#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. */
MPAlgorithmVersion1,
/** V2 miscounted the byte-length of multi-byte user names. */
MPAlgorithmVersion2,
/** V3 is the current version. */
MPAlgorithmVersion3,
MPAlgorithmVersionCurrent = MPAlgorithmVersion3,
MPAlgorithmVersionFirst = MPAlgorithmVersion0,
MPAlgorithmVersionLast = MPAlgorithmVersion3,
};
/** Derive the master key for a user based on their name and master password.
* @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);
/** 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);
/** @return A fingerprint for a user. */
MPIdenticon mpw_identicon(const char *fullName, const char *masterPassword);
#endif // _MPW_ALGORITHM_H

View File

@@ -0,0 +1,258 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include <string.h>
#include <errno.h>
#include <time.h>
#include "mpw-util.h"
#include "base64.h"
#define MP_N 32768LU
#define MP_r 8U
#define MP_p 2U
#define MP_otp_window 5 * 60 /* s */
// 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 = templates && count? templates[templateIndex % count]: NULL;
free( templates );
return template;
}
static const char mpw_characterFromClass_v0(char characterClass, uint16_t classIndex) {
const char *classCharacters = mpw_charactersInClass( characterClass );
if (!classCharacters)
return '\0';
return classCharacters[classIndex % strlen( classCharacters )];
}
// Algorithm version overrides.
static MPMasterKey mpw_masterKey_v0(
const char *fullName, const char *masterPassword) {
const char *keyScope = mpw_scopeForPurpose( MPKeyPurposeAuthentication );
trc( "keyScope: %s", keyScope );
// Calculate the master key salt.
trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, mpw_hex_l( (uint32_t)mpw_utf8_strlen( fullName ) ), fullName );
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
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) {
err( "Could not allocate master key salt: %s", strerror( errno ) );
return NULL;
}
trc( " => masterKeySalt.id: %s", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )", MP_N, MP_r, MP_p );
MPMasterKey masterKey = mpw_kdf_scrypt( MPMasterKeySize,
(uint8_t *)masterPassword, strlen( masterPassword ), masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( &masterKeySalt, masterKeySaltSize );
if (!masterKey) {
err( "Could not derive master key: %s", strerror( errno ) );
return NULL;
}
trc( " => masterKey.id: %s", mpw_id_buf( masterKey, MPMasterKeySize ) );
return masterKey;
}
static MPSiteKey mpw_siteKey_v0(
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
MPKeyPurpose keyPurpose, const char *keyContext) {
const char *keyScope = mpw_scopeForPurpose( keyPurpose );
trc( "keyScope: %s", keyScope );
// OTP counter value.
if (siteCounter == MPCounterValueTOTP)
siteCounter = ((uint32_t)time( NULL ) / MP_otp_window) * MP_otp_window;
// Calculate the site seed.
trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
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 (!siteSalt) {
err( "Could not allocate site salt: %s", strerror( errno ) );
return NULL;
}
trc( " => siteSalt.id: %s", mpw_id_buf( siteSalt, siteSaltSize ) );
trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )",
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", strerror( errno ) );
return NULL;
}
trc( " => siteKey.id: %s", 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.
uint16_t seedByte;
mpw_uint16( (uint16_t)_siteKey[0], (uint8_t *)&seedByte );
const char *template = mpw_templateForType_v0( resultType, seedByte );
trc( "template: %u => %s", seedByte, template );
if (!template)
return NULL;
if (strlen( template ) > MPSiteKeySize) {
err( "Template too long for password seed: %zu", 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) {
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",
template[c], seedByte, seedByte, sitePassword[c] );
}
trc( " => password: %s", 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." );
return NULL;
}
// Base64-decode
uint8_t *cipherBuf = calloc( 1, mpw_base64_decode_max( cipherText ) );
size_t bufSize = (size_t)mpw_base64_decode( cipherBuf, cipherText ), cipherBufSize = bufSize;
if ((int)bufSize < 0) {
err( "Base64 decoding error." );
mpw_free( &cipherBuf, mpw_base64_decode_max( cipherText ) );
return NULL;
}
trc( "b64 decoded: %zu bytes = %s", bufSize, mpw_hex( cipherBuf, bufSize ) );
// Decrypt
const uint8_t *plainBytes = mpw_aes_decrypt( masterKey, MPMasterKeySize, cipherBuf, &bufSize );
mpw_free( &cipherBuf, cipherBufSize );
const char *plainText = mpw_strndup( (char *)plainBytes, bufSize );
mpw_free( &plainBytes, bufSize );
if (!plainText)
err( "AES decryption error: %s", strerror( errno ) );
trc( "decrypted -> plainText: %zu bytes = %s = %s", strlen( plainText ), plainText, mpw_hex( plainText, strlen( 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." );
return NULL;
}
int resultParamInt = atoi( resultParam );
if (!resultParamInt)
resultParamInt = 512;
if (resultParamInt < 128 || resultParamInt > 512 || resultParamInt % 8 != 0) {
err( "Parameter is not a valid key size (should be 128 - 512): %s", resultParam );
return NULL;
}
uint16_t keySize = (uint16_t)(resultParamInt / 8);
trc( "keySize: %u", 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", 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: %s", b64Key );
mpw_free( &resultKey, keySize );
return b64Key;
}
default:
err( "Unsupported derived password type: %d", 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", strerror( errno ) );
return NULL;
}
trc( "cipherBuf: %zu bytes = %s", 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", cipherText );
mpw_free( &cipherBuf, bufSize );
return cipherText;
}

View File

@@ -0,0 +1,98 @@
//==============================================================================
// 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-util.h"
#define MP_N 32768LU
#define MP_r 8U
#define MP_p 2U
#define MP_otp_window 5 * 60 /* s */
// 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);
// Algorithm version overrides.
static MPMasterKey mpw_masterKey_v1(
const char *fullName, const char *masterPassword) {
return mpw_masterKey_v0( fullName, masterPassword );
}
static MPSiteKey mpw_siteKey_v1(
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
MPKeyPurpose keyPurpose, const char *keyContext) {
return mpw_siteKey_v0( masterKey, siteName, siteCounter, keyPurpose, keyContext );
}
static const char *mpw_sitePasswordFromTemplate_v1(
MPMasterKey __unused masterKey, MPSiteKey siteKey, MPResultType resultType, const char __unused *resultParam) {
// Determine the template.
uint8_t seedByte = siteKey[0];
const char *template = mpw_templateForType( resultType, seedByte );
trc( "template: %u => %s", seedByte, template );
if (!template)
return NULL;
if (strlen( template ) > MPSiteKeySize) {
err( "Template too long for password seed: %zu", 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) {
seedByte = siteKey[c + 1];
sitePassword[c] = mpw_characterFromClass( template[c], seedByte );
trc( " - class: %c, index: %3u (0x%02hhX) => character: %c",
template[c], seedByte, seedByte, sitePassword[c] );
}
trc( " => password: %s", 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 );
}

View 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 <errno.h>
#include <time.h>
#include "mpw-util.h"
#define MP_N 32768LU
#define MP_r 8U
#define MP_p 2U
#define MP_otp_window 5 * 60 /* s */
// 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);
// Algorithm version overrides.
static MPMasterKey mpw_masterKey_v2(
const char *fullName, const char *masterPassword) {
return mpw_masterKey_v1( fullName, masterPassword );
}
static MPSiteKey mpw_siteKey_v2(
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
MPKeyPurpose keyPurpose, const char *keyContext) {
const char *keyScope = mpw_scopeForPurpose( keyPurpose );
trc( "keyScope: %s", keyScope );
// OTP counter value.
if (siteCounter == MPCounterValueTOTP)
siteCounter = ((uint32_t)time( NULL ) / MP_otp_window) * MP_otp_window;
// Calculate the site seed.
trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
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 (!siteSalt) {
err( "Could not allocate site salt: %s", strerror( errno ) );
return NULL;
}
trc( " => siteSalt.id: %s", mpw_id_buf( siteSalt, siteSaltSize ) );
trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )",
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", strerror( errno ) );
return NULL;
}
trc( " => siteKey.id: %s", mpw_id_buf( siteKey, MPSiteKeySize ) );
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 );
}

View File

@@ -0,0 +1,106 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include <string.h>
#include <errno.h>
#include "mpw-util.h"
#define MP_N 32768LU
#define MP_r 8U
#define MP_p 2U
#define MP_otp_window 5 * 60 /* s */
// 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);
// Algorithm version overrides.
static MPMasterKey mpw_masterKey_v3(
const char *fullName, const char *masterPassword) {
const char *keyScope = mpw_scopeForPurpose( MPKeyPurposeAuthentication );
trc( "keyScope: %s", keyScope );
// Calculate the master key salt.
trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, mpw_hex_l( (uint32_t)strlen( fullName ) ), fullName );
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_push_string( &masterKeySalt, &masterKeySaltSize, keyScope );
mpw_push_int( &masterKeySalt, &masterKeySaltSize, (uint32_t)strlen( fullName ) );
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
err( "Could not allocate master key salt: %s", strerror( errno ) );
return NULL;
}
trc( " => masterKeySalt.id: %s", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )", MP_N, MP_r, MP_p );
MPMasterKey masterKey = mpw_kdf_scrypt( MPMasterKeySize,
(uint8_t *)masterPassword, strlen( masterPassword ), masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( &masterKeySalt, masterKeySaltSize );
if (!masterKey) {
err( "Could not derive master key: %s", strerror( errno ) );
return NULL;
}
trc( " => masterKey.id: %s", mpw_id_buf( masterKey, MPMasterKeySize ) );
return masterKey;
}
static MPSiteKey mpw_siteKey_v3(
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
MPKeyPurpose keyPurpose, const char *keyContext) {
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 );
}

View File

@@ -0,0 +1,113 @@
#include <string.h>
#include "mpw-jni.h"
#include "mpw-algorithm.h"
#include "mpw-util.h"
// TODO: We may need to zero the jbytes safely.
/* native int _masterKey(final String fullName, final byte[] masterPassword, final Version version) */
JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1masterKey(JNIEnv *env, jobject obj,
jstring fullName, jbyteArray masterPassword, jint algorithmVersion) {
const char *fullNameString = (*env)->GetStringUTFChars( env, fullName, NULL );
jbyte *masterPasswordString = (*env)->GetByteArrayElements( env, masterPassword, NULL );
MPMasterKey masterKeyBytes = mpw_masterKey( fullNameString, (char *)masterPasswordString, (MPAlgorithmVersion)algorithmVersion );
(*env)->ReleaseStringUTFChars( env, fullName, fullNameString );
(*env)->ReleaseByteArrayElements( env, masterPassword, masterPasswordString, JNI_ABORT );
if (!masterKeyBytes)
return NULL;
jbyteArray masterKey = (*env)->NewByteArray( env, (jsize)MPMasterKeySize );
(*env)->SetByteArrayRegion( env, masterKey, 0, (jsize)MPMasterKeySize, (jbyte *)masterKeyBytes );
mpw_free( &masterKeyBytes, MPMasterKeySize );
return masterKey;
}
/* native int _siteKey(final byte[] masterKey, final String siteName, final long siteCounter,
final MPKeyPurpose keyPurpose, @Nullable final String keyContext, final Version version) */
JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteKey(JNIEnv *env, jobject obj,
jbyteArray masterKey, jstring siteName, jlong siteCounter, jint keyPurpose, jstring keyContext, jint algorithmVersion) {
jbyte *masterKeyBytes = (*env)->GetByteArrayElements( env, masterKey, NULL );
const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL );
const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL;
MPMasterKey siteKeyBytes = mpw_siteKey(
(MPMasterKey)masterKeyBytes, siteNameString, (MPCounterValue)siteCounter,
(MPKeyPurpose)keyPurpose, keyContextString, (MPAlgorithmVersion)algorithmVersion );
(*env)->ReleaseByteArrayElements( env, masterKey, masterKeyBytes, JNI_ABORT );
(*env)->ReleaseStringUTFChars( env, siteName, siteNameString );
(*env)->ReleaseStringUTFChars( env, keyContext, keyContextString );
if (!siteKeyBytes)
return NULL;
jbyteArray siteKey = (*env)->NewByteArray( env, (jsize)MPMasterKeySize );
(*env)->SetByteArrayRegion( env, siteKey, 0, (jsize)MPMasterKeySize, (jbyte *)siteKeyBytes );
mpw_free( &siteKeyBytes, MPSiteKeySize );
return siteKey;
}
/* native String _siteResult(final byte[] masterKey, final byte[] siteKey, final String siteName, final long siteCounter,
final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
final MPResultType resultType, @Nullable final String resultParam, final Version version) */
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteResult(JNIEnv *env, jobject obj,
jbyteArray masterKey, jbyteArray siteKey, jstring siteName, jlong siteCounter, jint keyPurpose, jstring keyContext,
jint resultType, jstring resultParam, jint algorithmVersion) {
jbyte *masterKeyBytes = (*env)->GetByteArrayElements( env, masterKey, NULL );
jbyte *siteKeyBytes = (*env)->GetByteArrayElements( env, siteKey, NULL );
const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL );
const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL;
const char *resultParamString = resultParam? (*env)->GetStringUTFChars( env, resultParam, NULL ): NULL;
const char *siteResultString = mpw_siteResult(
(MPMasterKey)masterKeyBytes, siteNameString, (MPCounterValue)siteCounter,
(MPKeyPurpose)keyPurpose, keyContextString, (MPResultType)resultType, resultParamString, (MPAlgorithmVersion)algorithmVersion );
(*env)->ReleaseByteArrayElements( env, masterKey, masterKeyBytes, JNI_ABORT );
(*env)->ReleaseByteArrayElements( env, siteKey, siteKeyBytes, JNI_ABORT );
(*env)->ReleaseStringUTFChars( env, siteName, siteNameString );
(*env)->ReleaseStringUTFChars( env, keyContext, keyContextString );
(*env)->ReleaseStringUTFChars( env, resultParam, resultParamString );
if (!siteResultString)
return NULL;
jstring siteResult = (*env)->NewStringUTF( env, siteResultString );
mpw_free_string( &siteResultString );
return siteResult;
}
/* native String _siteState(final byte[] masterKey, final byte[] siteKey, final String siteName, final long siteCounter,
final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
final MPResultType resultType, final String resultParam, final Version version) */
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteState(JNIEnv *env, jobject obj,
jbyteArray masterKey, jbyteArray siteKey, jstring siteName, jlong siteCounter, jint keyPurpose, jstring keyContext,
jint resultType, jstring resultParam, jint algorithmVersion) {
jbyte *masterKeyBytes = (*env)->GetByteArrayElements( env, masterKey, NULL );
jbyte *siteKeyBytes = (*env)->GetByteArrayElements( env, siteKey, NULL );
const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL );
const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL;
const char *resultParamString = (*env)->GetStringUTFChars( env, resultParam, NULL );
const char *siteStateString = mpw_siteState(
(MPMasterKey)masterKeyBytes, siteNameString, (MPCounterValue)siteCounter,
(MPKeyPurpose)keyPurpose, keyContextString, (MPResultType)resultType, resultParamString, (MPAlgorithmVersion)algorithmVersion );
(*env)->ReleaseByteArrayElements( env, masterKey, masterKeyBytes, JNI_ABORT );
(*env)->ReleaseByteArrayElements( env, siteKey, siteKeyBytes, JNI_ABORT );
(*env)->ReleaseStringUTFChars( env, siteName, siteNameString );
(*env)->ReleaseStringUTFChars( env, keyContext, keyContextString );
(*env)->ReleaseStringUTFChars( env, resultParam, resultParamString );
if (!siteStateString)
return NULL;
jstring siteState = (*env)->NewStringUTF( env, siteStateString );
mpw_free_string( &siteStateString );
return siteState;
}

View File

@@ -0,0 +1,47 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_lyndir_masterpassword_impl_MPAlgorithmV0 */
#ifndef _Included_com_lyndir_masterpassword_impl_MPAlgorithmV0
#define _Included_com_lyndir_masterpassword_impl_MPAlgorithmV0
#ifdef __cplusplus
extern "C" {
#endif
#undef com_lyndir_masterpassword_impl_MPAlgorithmV0_AES_BLOCKSIZE
#define com_lyndir_masterpassword_impl_MPAlgorithmV0_AES_BLOCKSIZE 128L
/*
* Class: com_lyndir_masterpassword_impl_MPAlgorithmV0
* Method: _masterKey
* Signature: (Ljava/lang/String;[BI)[B
*/
JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1masterKey
(JNIEnv *, jobject, jstring, jbyteArray, jint);
/*
* Class: com_lyndir_masterpassword_impl_MPAlgorithmV0
* Method: _siteKey
* Signature: ([BLjava/lang/String;JILjava/lang/String;I)[B
*/
JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteKey
(JNIEnv *, jobject, jbyteArray, jstring, jlong, jint, jstring, jint);
/*
* Class: com_lyndir_masterpassword_impl_MPAlgorithmV0
* Method: _siteResult
* Signature: ([B[BLjava/lang/String;JILjava/lang/String;ILjava/lang/String;I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteResult
(JNIEnv *, jobject, jbyteArray, jbyteArray, jstring, jlong, jint, jstring, jint, jstring, jint);
/*
* Class: com_lyndir_masterpassword_impl_MPAlgorithmV0
* Method: _siteState
* Signature: ([B[BLjava/lang/String;JILjava/lang/String;ILjava/lang/String;I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteState
(JNIEnv *, jobject, jbyteArray, jbyteArray, jstring, jlong, jint, jstring, jint, jstring, jint);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,116 @@
//==============================================================================
// 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-marshal-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)? mpw_strndup( *in, len ): NULL;
// Advance past the delimitor.
*in = min( eol, *in + len + 1 );
return token;
}
time_t mpw_mktime(
const char *time) {
// TODO: Support parsing timezone into tm_gmtoff
struct tm tm = { .tm_isdst = -1 };
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 = mpw_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", 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.", fullName, *masterKeyAlgorithm );
return false;
}
}
return true;
}

View 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_MARSHAL_UTIL_H
#define _MPW_MARSHAL_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_MARSHAL_UTIL_H

View 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-marshal.h"
#include "mpw-util.h"
#include "mpw-marshal-util.h"
MPMarshalledUser *mpw_marshal_user(
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) {
MPMarshalledUser *user;
if (!fullName || !masterPassword || !(user = malloc( sizeof( MPMarshalledUser ) )))
return NULL;
*user = (MPMarshalledUser){
.fullName = mpw_strdup( fullName ),
.masterPassword = mpw_strdup( masterPassword ),
.algorithm = algorithmVersion,
.redacted = true,
.avatar = 0,
.defaultType = MPResultTypeDefault,
.lastUsed = 0,
.sites_count = 0,
.sites = NULL,
};
return user;
}
MPMarshalledSite *mpw_marshal_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 = mpw_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 = mpw_strdup( keyword ),
.content = NULL,
.type = MPResultTypeTemplatePhrase,
};
return question;
}
bool mpw_marshal_info_free(
MPMarshalInfo **info) {
if (!info || !*info)
return true;
bool success = true;
success &= mpw_free_strings( &(*info)->fullName, &(*info)->keyID, NULL );
success &= mpw_free( info, sizeof( MPMarshalInfo ) );
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_marshal_write_flat(
char **out, const MPMarshalledUser *user, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!user->fullName || !strlen( user->fullName )) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
return false;
}
if (!user->masterPassword || !strlen( user->masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorMasterPassword, "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 = (MPMarshalError){ MPMarshalErrorInternal, "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 = (MPMarshalError){ MPMarshalErrorInternal, "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 = mpw_strdup( site->content );
if (site->loginType & MPSiteFeatureExportContent && site->loginContent && strlen( site->loginContent ))
loginContent = mpw_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 = (MPMarshalError){ .type = MPMarshalSuccess };
return true;
}
#if MPW_JSON
static bool mpw_marshal_write_json(
char **out, const MPMarshalledUser *user, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!user->fullName || !strlen( user->fullName )) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
return false;
}
if (!user->masterPassword || !strlen( user->masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorMasterPassword, "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 = (MPMarshalError){ MPMarshalErrorInternal, "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 = (MPMarshalError){ MPMarshalErrorInternal, "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 = mpw_strdup( site->content );
if (site->loginType & MPSiteFeatureExportContent && site->loginContent && strlen( site->loginContent ))
loginContent = mpw_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 = (MPMarshalError){ .type = MPMarshalSuccess };
return true;
}
#endif
bool mpw_marshal_write(
char **out, const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error) {
switch (outFormat) {
case MPMarshalFormatNone:
*error = (MPMarshalError){ .type = MPMarshalSuccess };
return false;
case MPMarshalFormatFlat:
return mpw_marshal_write_flat( out, user, error );
#if MPW_JSON
case MPMarshalFormatJSON:
return mpw_marshal_write_json( out, user, error );
#endif
default:
*error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) };
return false;
}
}
static void mpw_marshal_read_flat_info(
const char *in, MPMarshalInfo *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 = mpw_strdup( headerValue );
if (strcmp( headerName, "Key ID" ) == 0)
info->keyID = mpw_strdup( headerValue );
if (strcmp( headerName, "Passwords" ) == 0)
info->redacted = strcmp( headerValue, "VISIBLE" ) != 0;
if (strcmp( headerName, "Date" ) == 0)
info->date = mpw_mktime( headerValue );
mpw_free_strings( &headerName, &headerValue, NULL );
continue;
}
}
}
static MPMarshalledUser *mpw_marshal_read_flat(
const char *in, const char *masterPassword, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!in || !strlen( in )) {
error->type = MPMarshalErrorStructure;
error->description = mpw_str( "No input data." );
return NULL;
}
// Parse import data.
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 = MPMarshalErrorStructure;
error->description = mpw_str( "Invalid header: %s", mpw_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 = mpw_strdup( headerValue );
if (strcmp( headerName, "Avatar" ) == 0)
avatar = (unsigned int)atoi( headerValue );
if (strcmp( headerName, "Key ID" ) == 0)
keyID = mpw_strdup( headerValue );
if (strcmp( headerName, "Algorithm" ) == 0) {
int value = atoi( headerValue );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, 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 = (MPMarshalError){ MPMarshalErrorIllegal, 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 = (MPMarshalError){ MPMarshalErrorMissing, "Missing header: Full Name" };
return NULL;
}
if (positionInLine >= endOfLine)
continue;
if (!user) {
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, algorithm, fullName, masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return NULL;
}
if (keyID && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
*error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Master password doesn't match key ID." };
return NULL;
}
if (!(user = mpw_marshal_user( fullName, masterPassword, algorithm ))) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "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 = mpw_strdup( strtok( typeAndVersion, ":" ) );
str_algorithm = mpw_strdup( strtok( NULL, "" ) );
mpw_free_string( &typeAndVersion );
}
str_counter = mpw_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 = mpw_strdup( strtok( typeAndVersionAndCounter, ":" ) );
str_algorithm = mpw_strdup( strtok( NULL, ":" ) );
str_counter = mpw_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 = (MPMarshalError){ MPMarshalErrorFormat, 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 = (MPMarshalError){ MPMarshalErrorIllegal, 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 = (MPMarshalError){ MPMarshalErrorIllegal, 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 = (MPMarshalError){ MPMarshalErrorIllegal, 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 = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) };
return NULL;
}
MPMarshalledSite *site = mpw_marshal_site(
user, siteName, siteType, siteCounter, siteAlgorithm );
if (!site) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "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 = (MPMarshalError){ MPMarshalErrorInternal, "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 = mpw_strdup( siteContent );
if (siteLoginName && strlen( siteLoginName ))
site->loginContent = mpw_strdup( siteLoginName );
}
}
else {
error->type = MPMarshalErrorMissing;
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 = (MPMarshalError){ .type = MPMarshalSuccess };
return user;
}
#if MPW_JSON
static void mpw_marshal_read_json_info(
const char *in, MPMarshalInfo *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 = mpw_strdup( mpw_get_json_string( json_file, "user.full_name", NULL ) );
info->keyID = mpw_strdup( mpw_get_json_string( json_file, "user.key_id", NULL ) );
json_object_put( json_file );
}
static MPMarshalledUser *mpw_marshal_read_json(
const char *in, const char *masterPassword, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!in || !strlen( in )) {
error->type = MPMarshalErrorStructure;
error->description = mpw_str( "No input data." );
return NULL;
}
// Parse JSON.
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 = (MPMarshalError){ MPMarshalErrorStructure, 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 = (MPMarshalError){ MPMarshalErrorFormat, 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 = (MPMarshalError){ MPMarshalErrorIllegal, 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 = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %u", defaultType ) };
return NULL;
}
time_t lastUsed = mpw_mktime( str_lastUsed );
if (!lastUsed) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user last used: %s", str_lastUsed ) };
return NULL;
}
if (!fullName || !strlen( fullName )) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing value for full name." };
return NULL;
}
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, algorithm, fullName, masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return NULL;
}
if (keyID && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
*error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Master password doesn't match key ID." };
return NULL;
}
if (!(user = mpw_marshal_user( fullName, masterPassword, algorithm ))) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "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 = (MPMarshalError){ MPMarshalErrorIllegal, 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 = (MPMarshalError){ MPMarshalErrorIllegal, 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 = (MPMarshalError){ MPMarshalErrorIllegal, 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 = (MPMarshalError){ MPMarshalErrorIllegal, 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_marshal_site( user, siteName, siteType, siteCounter, siteAlgorithm );
if (!site) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new site." };
return NULL;
}
site->loginType = siteLoginType;
site->url = siteURL? mpw_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 = (MPMarshalError){ MPMarshalErrorInternal, "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 = mpw_strdup( siteContent );
if (siteLoginName && strlen( siteLoginName ))
site->loginContent = mpw_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 = mpw_strdup( answerContent );
}
}
}
json_object_put( json_file );
*error = (MPMarshalError){ .type = MPMarshalSuccess };
return user;
}
#endif
MPMarshalInfo *mpw_marshal_read_info(
const char *in) {
MPMarshalInfo *info = malloc( sizeof( MPMarshalInfo ) );
*info = (MPMarshalInfo){ .format = MPMarshalFormatNone };
if (in && strlen( in )) {
if (in[0] == '#') {
*info = (MPMarshalInfo){ .format = MPMarshalFormatFlat };
mpw_marshal_read_flat_info( in, info );
}
else if (in[0] == '{') {
*info = (MPMarshalInfo){ .format = MPMarshalFormatJSON };
#if MPW_JSON
mpw_marshal_read_json_info( in, info );
#endif
}
}
return info;
}
MPMarshalledUser *mpw_marshal_read(
const char *in, const MPMarshalFormat inFormat, const char *masterPassword, MPMarshalError *error) {
switch (inFormat) {
case MPMarshalFormatNone:
*error = (MPMarshalError){ .type = MPMarshalSuccess };
return false;
case MPMarshalFormatFlat:
return mpw_marshal_read_flat( in, masterPassword, error );
#if MPW_JSON
case MPMarshalFormatJSON:
return mpw_marshal_read_json( in, masterPassword, error );
#endif
default:
*error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported input format: %u", inFormat ) };
return NULL;
}
}
const MPMarshalFormat mpw_formatWithName(
const char *formatName) {
if (!formatName || !strlen( formatName ))
return MPMarshalFormatNone;
// 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( MPMarshalFormatNone ), stdFormatName, strlen( stdFormatName ) ) == 0)
return MPMarshalFormatNone;
if (strncmp( mpw_nameForFormat( MPMarshalFormatFlat ), stdFormatName, strlen( stdFormatName ) ) == 0)
return MPMarshalFormatFlat;
if (strncmp( mpw_nameForFormat( MPMarshalFormatJSON ), stdFormatName, strlen( stdFormatName ) ) == 0)
return MPMarshalFormatJSON;
dbg( "Not a format name: %s", stdFormatName );
return (MPMarshalFormat)ERR;
}
const char *mpw_nameForFormat(
const MPMarshalFormat format) {
switch (format) {
case MPMarshalFormatNone:
return "none";
case MPMarshalFormatFlat:
return "flat";
case MPMarshalFormatJSON:
return "json";
default: {
dbg( "Unknown format: %d", format );
return NULL;
}
}
}
const char *mpw_marshal_format_extension(
const MPMarshalFormat format) {
switch (format) {
case MPMarshalFormatNone:
return NULL;
case MPMarshalFormatFlat:
return "mpsites";
case MPMarshalFormatJSON:
return "mpsites.json";
default: {
dbg( "Unknown format: %d", format );
return NULL;
}
}
}

View 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_MARSHAL_H
#define _MPW_MARSHAL_H
#include <time.h>
#include "mpw-algorithm.h"
//// Types.
typedef mpw_enum( unsigned int, MPMarshalFormat ) {
/** Do not marshal. */
MPMarshalFormatNone,
/** Marshal using the line-based plain-text format. */
MPMarshalFormatFlat,
/** Marshal using the JSON structured format. */
MPMarshalFormatJSON,
#if MPW_JSON
MPMarshalFormatDefault = MPMarshalFormatJSON,
#else
MPMarshalFormatDefault = MPMarshalFormatFlat,
#endif
};
typedef mpw_enum( unsigned int, MPMarshalErrorType ) {
/** The marshalling operation completed successfully. */
MPMarshalSuccess,
/** An error in the structure of the marshall file interrupted marshalling. */
MPMarshalErrorStructure,
/** The marshall file uses an unsupported format version. */
MPMarshalErrorFormat,
/** A required value is missing or not specified. */
MPMarshalErrorMissing,
/** The given master password is not valid. */
MPMarshalErrorMasterPassword,
/** An illegal value was specified. */
MPMarshalErrorIllegal,
/** An internal system error interrupted marshalling. */
MPMarshalErrorInternal,
};
typedef struct MPMarshalError {
MPMarshalErrorType type;
const char *description;
} MPMarshalError;
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 MPMarshalInfo {
MPMarshalFormat format;
MPAlgorithmVersion algorithm;
const char *fullName;
const char *keyID;
bool redacted;
time_t date;
} MPMarshalInfo;
//// Marshalling.
/** Write the user and all associated data out to the given output buffer using the given marshalling format. */
bool mpw_marshal_write(
char **out, const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error);
/** Try to read metadata on the sites in the input buffer. */
MPMarshalInfo *mpw_marshal_read_info(
const char *in);
/** Unmarshall sites in the given input buffer by parsing it using the given marshalling format. */
MPMarshalledUser *mpw_marshal_read(
const char *in, const MPMarshalFormat inFormat, const char *masterPassword, MPMarshalError *error);
//// Utilities.
/** Create a new user object ready for marshalling. */
MPMarshalledUser *mpw_marshal_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_marshal_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(
MPMarshalInfo **info);
bool mpw_marshal_free(
MPMarshalledUser **user);
//// Format.
/**
* @return The purpose represented by the given name.
*/
const MPMarshalFormat mpw_formatWithName(
const char *formatName);
/**
* @return The standard name for the given purpose.
*/
const char *mpw_nameForFormat(
const MPMarshalFormat format);
/**
* @return The file extension that's recommended for files that use the given marshalling format.
*/
const char *mpw_marshal_format_extension(
const MPMarshalFormat format);
#endif // _MPW_MARSHAL_H

View File

@@ -0,0 +1,267 @@
//==============================================================================
// 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 <ctype.h>
#include "mpw-types.h"
#include "mpw-util.h"
const size_t MPMasterKeySize = 64;
const size_t MPSiteKeySize = 256 / 8; // Size of HMAC-SHA-256
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 MPResultTypeTemplatePhrase;
if ('P' == typeName[0])
return MPResultTypeStatefulPersonal;
if ('D' == typeName[0])
return MPResultTypeStatefulDevice;
if ('K' == typeName[0])
return MPResultTypeDeriveKey;
}
// Lower-case typeName to standardize it.
size_t stdTypeNameSize = strlen( typeName );
char stdTypeName[stdTypeNameSize + 1];
for (size_t c = 0; c < stdTypeNameSize; ++c)
stdTypeName[c] = (char)tolower( typeName[c] );
stdTypeName[stdTypeNameSize] = '\0';
// 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;
dbg( "Not a generated type name: %s", stdTypeName );
return (MPResultType)ERR;
}
const char *mpw_nameForType(MPResultType resultType) {
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", resultType );
return NULL;
}
}
}
const char **mpw_templatesForType(MPResultType type, size_t *count) {
if (!(type & MPResultTypeClassTemplate)) {
dbg( "Not a generated type: %d", type );
return NULL;
}
switch (type) {
case MPResultTypeTemplateMaximum:
return mpw_alloc_array( count, const char *,
"anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" );
case MPResultTypeTemplateLong:
return mpw_alloc_array( count, const char *,
"CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" );
case MPResultTypeTemplateMedium:
return mpw_alloc_array( count, const char *,
"CvcnoCvc", "CvcCvcno" );
case MPResultTypeTemplateShort:
return mpw_alloc_array( count, const char *,
"Cvcn" );
case MPResultTypeTemplateBasic:
return mpw_alloc_array( count, const char *,
"aaanaaan", "aannaaan", "aaannaaa" );
case MPResultTypeTemplatePIN:
return mpw_alloc_array( count, const char *,
"nnnn" );
case MPResultTypeTemplateName:
return mpw_alloc_array( count, const char *,
"cvccvcvcv" );
case MPResultTypeTemplatePhrase:
return mpw_alloc_array( count, const char *,
"cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" );
default: {
dbg( "Unknown generated type: %d", type );
return NULL;
}
}
}
const char *mpw_templateForType(MPResultType type, uint8_t templateIndex) {
size_t count = 0;
const char **templates = mpw_templatesForType( type, &count );
char const *template = templates && count? templates[templateIndex % count]: NULL;
free( templates );
return template;
}
const MPKeyPurpose mpw_purposeWithName(const char *purposeName) {
// Lower-case and trim optionally leading "generated" string from typeName to standardize it.
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 (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;
dbg( "Not a purpose name: %s", stdPurposeName );
return (MPKeyPurpose)ERR;
}
const char *mpw_nameForPurpose(MPKeyPurpose purpose) {
switch (purpose) {
case MPKeyPurposeAuthentication:
return "authentication";
case MPKeyPurposeIdentification:
return "identification";
case MPKeyPurposeRecovery:
return "recovery";
default: {
dbg( "Unknown purpose: %d", 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", purpose );
return NULL;
}
}
}
const char *mpw_charactersInClass(char characterClass) {
switch (characterClass) {
case 'V':
return "AEIOU";
case 'C':
return "BCDFGHJKLMNPQRSTVWXYZ";
case 'v':
return "aeiou";
case 'c':
return "bcdfghjklmnpqrstvwxyz";
case 'A':
return "AEIOUBCDFGHJKLMNPQRSTVWXYZ";
case 'a':
return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
case 'n':
return "0123456789";
case 'o':
return "@&%?,=[]_:-+*$#!'^~;()/.";
case 'x':
return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
case ' ':
return " ";
default: {
dbg( "Unknown character class: %c", characterClass );
return NULL;
}
}
}
const char mpw_characterFromClass(char characterClass, uint8_t seedByte) {
const char *classCharacters = mpw_charactersInClass( characterClass );
if (!classCharacters)
return '\0';
return classCharacters[seedByte % strlen( classCharacters )];
}

View File

@@ -0,0 +1,180 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#ifndef _MPW_TYPES_H
#define _MPW_TYPES_H
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef NS_ENUM
#define mpw_enum(_type, _name) NS_ENUM(_type, _name)
#else
#define mpw_enum(_type, _name) _type _name; enum
#endif
#ifndef __unused
#define __unused __attribute__((unused))
#endif
//// Types.
extern const size_t MPMasterKeySize, MPSiteKeySize; /* bytes */
typedef const uint8_t *MPMasterKey, *MPSiteKey;
typedef const char *MPKeyID;
typedef mpw_enum( uint8_t, MPKeyPurpose ) {
/** Generate a key for authentication. */
MPKeyPurposeAuthentication,
/** Generate a name for identification. */
MPKeyPurposeIdentification,
/** Generate a recovery token. */
MPKeyPurposeRecovery,
};
// 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,
};
// 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,
};
// bit 0-3 | MPResultTypeClass | MPSiteFeature
typedef mpw_enum( uint32_t, MPResultType ) {
/** 16: pg^VMAUBk5x3p%HP%i4= */
MPResultTypeTemplateMaximum = 0x0 | MPResultTypeClassTemplate | 0x0,
/** 17: BiroYena8:Kixa */
MPResultTypeTemplateLong = 0x1 | MPResultTypeClassTemplate | 0x0,
/** 18: BirSuj0- */
MPResultTypeTemplateMedium = 0x2 | MPResultTypeClassTemplate | 0x0,
/** 19: Bir8 */
MPResultTypeTemplateShort = 0x3 | MPResultTypeClassTemplate | 0x0,
/** 20: pO98MoD0 */
MPResultTypeTemplateBasic = 0x4 | MPResultTypeClassTemplate | 0x0,
/** 21: 2798 */
MPResultTypeTemplatePIN = 0x5 | MPResultTypeClassTemplate | 0x0,
/** 30: birsujano */
MPResultTypeTemplateName = 0xE | MPResultTypeClassTemplate | 0x0,
/** 31: bir yennoquce fefi */
MPResultTypeTemplatePhrase = 0xF | MPResultTypeClassTemplate | 0x0,
/** 1056: Custom saved password. */
MPResultTypeStatefulPersonal = 0x0 | MPResultTypeClassStateful | MPSiteFeatureExportContent,
/** 2081: Custom saved password that should not be exported from the device. */
MPResultTypeStatefulDevice = 0x1 | MPResultTypeClassStateful | MPSiteFeatureDevicePrivate,
/** 4160: 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,
};
/** These colours are compatible with the original ANSI SGR. */
typedef mpw_enum( uint8_t, MPIdenticonColor ) {
MPIdenticonColorRed = 1,
MPIdenticonColorGreen,
MPIdenticonColorYellow,
MPIdenticonColorBlue,
MPIdenticonColorMagenta,
MPIdenticonColorCyan,
MPIdenticonColorWhite,
MPIdenticonColorFirst = MPIdenticonColorRed,
MPIdenticonColorLast = MPIdenticonColorWhite,
};
typedef struct {
const char *leftArm;
const char *body;
const char *rightArm;
const char *accessory;
MPIdenticonColor color;
} MPIdenticon;
//// Type utilities.
/**
* @return The purpose represented by the given name.
*/
const MPKeyPurpose mpw_purposeWithName(const char *purposeName);
/**
* @return The standard name for the given purpose.
*/
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 password type represented by the given name.
*/
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.
* The amount of elements in the array is stored in count.
* If an unsupported type is given, count will be 0 and will return NULL.
* The array needs to be free'ed, the strings themselves must not be free'ed or modified.
*/
const char **mpw_templatesForType(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(MPResultType type, uint8_t templateIndex);
/**
* @return An internal string that contains all the characters that occur in the given character class.
*/
const char *mpw_charactersInClass(char characterClass);
/**
* @return A character from given character class that encodes the given byte.
*/
const char mpw_characterFromClass(char characterClass, uint8_t seedByte);
#endif // _MPW_TYPES_H

View File

@@ -0,0 +1,498 @@
//==============================================================================
// 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 <ctype.h>
#include <errno.h>
#if MPW_CPERCIVA
#include <scrypt/crypto_scrypt.h>
#include <scrypt/sha256.h>
#elif MPW_SODIUM
#include "sodium.h"
#endif
#define AES_ECB 0
#define AES_CBC 1
#include "aes.h"
#include "mpw-util.h"
#ifdef inf_level
int mpw_verbosity = inf_level;
#endif
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 false;
if (!mpw_realloc( buffer, bufferSize, pushSize )) {
// realloc failed, we can't push. Mark the buffer as broken.
mpw_free( buffer, *bufferSize );
*bufferSize = (size_t)ERR;
return false;
}
uint8_t *bufferOffset = *buffer + *bufferSize - pushSize;
memcpy( bufferOffset, pushBuffer, pushSize );
return true;
}
bool mpw_push_string(uint8_t **buffer, size_t *bufferSize, const char *pushString) {
return pushString && mpw_push_buf( buffer, bufferSize, pushString, strlen( pushString ) );
}
bool mpw_string_push(char **string, const char *pushString) {
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 );
}
bool mpw_string_pushf(char **string, const char *pushFormat, ...) {
va_list args;
va_start( args, pushFormat );
bool success = mpw_string_push( string, mpw_vstr( pushFormat, args ) );
va_end( args );
return success;
}
bool mpw_push_int(uint8_t **buffer, size_t *bufferSize, const uint32_t pushInt) {
uint8_t pushBuf[4 /* 32 / 8 */];
mpw_uint32( pushInt, pushBuf );
return mpw_push_buf( buffer, bufferSize, &pushBuf, sizeof( pushBuf ) );
}
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;
}
void mpw_zero(void *buffer, size_t bufferSize) {
uint8_t *b = buffer;
for (; bufferSize > 0; --bufferSize)
*b++ = 0;
}
bool __mpw_free(void **buffer, const size_t bufferSize) {
if (!buffer || !*buffer)
return false;
mpw_zero( *buffer, bufferSize );
free( *buffer );
*buffer = NULL;
return true;
}
bool __mpw_free_string(char **string) {
return *string && __mpw_free( (void **)string, strlen( *string ) );
}
bool __mpw_free_strings(char **strings, ...) {
bool success = true;
va_list args;
va_start( args, strings );
success &= mpw_free_string( strings );
for (char **string; (string = va_arg( args, char ** ));)
success &= mpw_free_string( string );
va_end( args );
return success;
}
uint8_t const *mpw_kdf_scrypt(const size_t keySize, const uint8_t *secret, const size_t secretSize, const uint8_t *salt, const size_t saltSize,
uint64_t N, uint32_t r, uint32_t p) {
if (!secret || !salt || !secretSize || !saltSize)
return NULL;
uint8_t *key = malloc( keySize );
if (!key)
return NULL;
#if MPW_CPERCIVA
if (crypto_scrypt( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) < 0) {
mpw_free( &key, keySize );
return NULL;
}
#elif MPW_SODIUM
if (crypto_pwhash_scryptsalsa208sha256_ll( secret, secretSize, 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_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 (!key || !keySize || !subkeySize) {
errno = EINVAL;
return NULL;
}
uint8_t *subkey = malloc( subkeySize );
if (!subkey)
return NULL;
#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 ||
(personal && strlen( personal ) > crypto_generichash_blake2b_PERSONALBYTES)) {
errno = EINVAL;
free( subkey );
return NULL;
}
uint8_t saltBuf[crypto_generichash_blake2b_SALTBYTES];
mpw_zero( saltBuf, sizeof saltBuf );
if (id)
mpw_uint64( id, saltBuf );
uint8_t personalBuf[crypto_generichash_blake2b_PERSONALBYTES];
mpw_zero( personalBuf, sizeof personalBuf );
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, message, messageSize ) != 0 ||
crypto_auth_hmacsha256_final( &state, mac ) != 0) {
mpw_free( &mac, crypto_auth_hmacsha256_BYTES );
return NULL;
}
#else
#error No crypto support for mpw_hmac_sha256.
#endif
return mac;
}
// We do our best to not fail on odd buf's, eg. non-padded cipher texts.
static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t keySize, const uint8_t *buf, size_t *bufSize) {
if (!key || keySize < AES_BLOCKLEN || !*bufSize)
return NULL;
// IV = zero
uint8_t iv[AES_BLOCKLEN];
mpw_zero( iv, sizeof iv );
// Add PKCS#7 padding
uint32_t aesSize = ((uint32_t)*bufSize + AES_BLOCKLEN - 1) & -AES_BLOCKLEN; // round up to block size.
if (encrypt && !(*bufSize % AES_BLOCKLEN)) // add pad block if plain text fits block size.
encrypt += AES_BLOCKLEN;
uint8_t aesBuf[aesSize];
memcpy( aesBuf, buf, *bufSize );
memset( aesBuf + *bufSize, aesSize - *bufSize, aesSize - *bufSize );
uint8_t *resultBuf = malloc( aesSize );
if (encrypt)
AES_CBC_encrypt_buffer( resultBuf, aesBuf, aesSize, key, iv );
else
AES_CBC_decrypt_buffer( resultBuf, aesBuf, aesSize, key, iv );
mpw_zero( aesBuf, aesSize );
mpw_zero( iv, AES_BLOCKLEN );
// Truncate PKCS#7 padding
if (encrypt)
*bufSize = aesSize;
else if (*bufSize % AES_BLOCKLEN == 0 && resultBuf[aesSize - 1] < AES_BLOCKLEN)
*bufSize -= resultBuf[aesSize - 1];
return resultBuf;
}
uint8_t const *mpw_aes_encrypt(const uint8_t *key, const size_t keySize, const uint8_t *plainBuf, size_t *bufSize) {
return mpw_aes( true, key, keySize, plainBuf, bufSize );
}
uint8_t const *mpw_aes_decrypt(const uint8_t *key, const size_t keySize, const uint8_t *cipherBuf, size_t *bufSize) {
return mpw_aes( false, key, keySize, cipherBuf, bufSize );
}
#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 mpw_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 );
#elif MPW_SODIUM
uint8_t hash[crypto_hash_sha256_BYTES];
crypto_hash_sha256( hash, buf, length );
#else
#error No crypto support for mpw_id_buf.
#endif
return mpw_hex( hash, sizeof( hash ) / sizeof( uint8_t ) );
}
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) {
// 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;
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) {
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 ) );
}
/**
* @return the amount of bytes used by UTF-8 to encode a single character that starts with the given byte.
*/
static int mpw_utf8_sizeof(unsigned char utf8Byte) {
if (!utf8Byte)
return 0;
if ((utf8Byte & 0x80) == 0)
return 1;
if ((utf8Byte & 0xC0) != 0xC0)
return 0;
if ((utf8Byte & 0xE0) == 0xC0)
return 2;
if ((utf8Byte & 0xF0) == 0xE0)
return 3;
if ((utf8Byte & 0xF8) == 0xF0)
return 4;
return 0;
}
const size_t mpw_utf8_strlen(const char *utf8String) {
size_t charlen = 0;
char *remainingString = (char *)utf8String;
for (int charByteSize; (charByteSize = mpw_utf8_sizeof( (unsigned char)*remainingString )); remainingString += charByteSize)
++charlen;
return charlen;
}
char *mpw_strdup(const char *src) {
if (!src)
return NULL;
size_t len = strlen( src );
char *dst = malloc( len + 1 );
memcpy( dst, src, len );
dst[len] = '\0';
return dst;
}
char *mpw_strndup(const char *src, size_t max) {
if (!src)
return NULL;
size_t len = 0;
for (; len < max && src[len] != '\0'; ++len);
char *dst = malloc( len + 1 );
memcpy( dst, src, len );
dst[len] = '\0';
return dst;
}

View File

@@ -0,0 +1,211 @@
//==============================================================================
// 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_UTIL_H
#define _MPW_UTIL_H
#include <stdio.h>
#include <stdarg.h>
#include "mpw-types.h"
//// Logging.
extern int mpw_verbosity;
#ifndef mpw_log_do
#define mpw_log_do(level, format, ...) \
fprintf( stderr, format "\n", ##__VA_ARGS__ )
#endif
#ifndef mpw_log
#define mpw_log(level, ...) ({ \
if (mpw_verbosity >= level) { \
mpw_log_do( level, ##__VA_ARGS__ ); \
}; })
#endif
#ifndef trc
/** Logging internal state. */
#define trc_level 3
#define trc(...) mpw_log( trc_level, ##__VA_ARGS__ )
/** Logging state and events interesting when investigating issues. */
#define dbg_level 2
#define dbg(...) mpw_log( dbg_level, ##__VA_ARGS__ )
/** User messages. */
#define inf_level 1
#define inf(...) mpw_log( inf_level, ##__VA_ARGS__ )
/** Recoverable issues and user suggestions. */
#define wrn_level 0
#define wrn(...) mpw_log( wrn_level, ##__VA_ARGS__ )
/** Unrecoverable issues. */
#define err_level -1
#define err(...) mpw_log( err_level, ##__VA_ARGS__ )
/** Issues that lead to abortion. */
#define ftl_level -2
#define ftl(...) mpw_log( ftl_level, ##__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__ }; \
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. */
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. */
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. */
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);
void mpw_zero(
void *buffer, size_t bufferSize);
/** 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( (void **)_b, bufferSize ); })
bool __mpw_free(
void **buffer, size_t bufferSize);
/** Free a string after zero'ing its contents, then set the reference to NULL. */
#define mpw_free_string(string) \
({ __typeof__(string) _s = string; const char *__s = *_s; (void)__s; __mpw_free_string( (char **)_s ); })
bool __mpw_free_string(
char **string);
/** Free strings after zero'ing their contents, then set the references to NULL. Terminate the va_list with NULL. */
#define mpw_free_strings(strings, ...) \
({ __typeof__(strings) _s = strings; const char *__s = *_s; (void)__s; __mpw_free_strings( (char **)_s, __VA_ARGS__ ); })
bool __mpw_free_strings(
char **strings, ...);
//// Cryptographic functions.
/** 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 uint8_t *secret, const size_t secretSize, const uint8_t *salt, const size_t saltSize,
uint64_t N, uint32_t r, uint32_t p);
/** 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, 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, 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. */
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);
//// String utilities.
/** @return The amount of display characters in the given UTF-8 string. */
const size_t mpw_utf8_strlen(const char *utf8String);
/** Drop-in for POSIX strdup(3). */
char *mpw_strdup(const char *src);
/** Drop-in for POSIX strndup(3). */
char *mpw_strndup(const char *src, size_t max);
#endif // _MPW_UTIL_H