Add full kdbx4 read support

Abstract out KeyDerivatino
Add Argon2 and ChaCha20 support
Move to standard gradle plugin
This commit is contained in:
Brian Pellin
2017-04-05 22:03:10 -05:00
parent ea2c0ebd90
commit 4115746258
96 changed files with 4801 additions and 236 deletions

1
app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.externalNativeBuild

View File

@@ -32,12 +32,12 @@
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/flavor1/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/aes" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1Debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/argon2/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/argon2/src/blake2" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/argon2" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/final_key" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/sha" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/final_key/aes" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/final_key/sha" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/flavor1/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/flavor1/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/flavor1/debug" isTestSource="false" generated="true" />
@@ -50,7 +50,8 @@
<sourceFolder url="file://$MODULE_DIR$/src/flavor1Debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1Debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1Debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1Debug/renderscript" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1Debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1Debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1Debug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/flavor1/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/flavor1/debug" isTestSource="true" generated="true" />
@@ -72,7 +73,8 @@
<sourceFolder url="file://$MODULE_DIR$/src/flavor1/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1/renderscript" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/flavor1/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/testFlavor1/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testFlavor1/resources" type="java-test-resource" />
@@ -95,7 +97,8 @@
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/renderscript" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
@@ -110,39 +113,47 @@
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/renderscript" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/renderscript" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/renderscript" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/binaries" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/cmake" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-support" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/objectFiles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/restart-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/sourceFolderJavaResources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
@@ -153,5 +164,6 @@
<orderEntry type="library" exported="" scope="TEST" name="junit4" level="project" />
<orderEntry type="library" exported="" name="prov-1.54.0.0" level="project" />
<orderEntry type="library" exported="" name="core-1.54.0.0" level="project" />
<orderEntry type="library" exported="" name="joda-time-2.9.4" level="project" />
</component>
</module>

View File

@@ -1,79 +1,57 @@
apply plugin: 'com.android.model.application'
apply plugin: 'com.android.application'
model {
android {
compileSdkVersion = 22
buildToolsVersion = "25.0.0"
android {
compileSdkVersion = 22
buildToolsVersion = "25.0.0"
defaultConfig.with {
applicationId = "com.android.keepass"
minSdkVersion.apiLevel = 3
targetSdkVersion.apiLevel = 12
versionCode = 154
versionName = "2.0.6.4"
defaultConfig {
applicationId = "com.android.keepass"
minSdkVersion = 3
targetSdkVersion = 12
versionCode = 154
versionName = "2.0.6.4"
testApplicationId = "com.keepassdroid.tests"
testInstrumentationRunner = "android.test.InstrumentationTestRunner"
testApplicationId = "com.keepassdroid.tests"
testInstrumentationRunner = "android.test.InstrumentationTestRunner"
buildConfigFields.with {
create() {
type = "int"
name = "VALUE"
value = "1"
}
/*
buildConfigFields.with {
create() {
type = "int"
name = "VALUE"
value = "1"
}
}
*/
}
externalNativeBuild {
cmake {
path "src/main/jni/CMakeLists.txt"
}
}
android.buildTypes {
buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file('proguard-rules.txt'))
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
android.productFlavors {
create("flavor1") {
productFlavors {
flavor1 {
applicationId = "com.android.keepass"
}
}
android.sources {
main {
jni {
source {
srcDirs = [
"src/main/jni/aes",
"src/main/jni/final_key",
"src/main/jni/sha"
]
exclude "**/aesxam.c"
exclude "**/rfc3686.c"
exclude "**/tablegen.c"
exclude "**/pwd2key.c"
exclude "**/sha1b.c"
exclude "**/sha2b.c"
exclude "**/shasum.c"
}
}
}
}
android.ndk {
moduleName = "final-key"
ldLibs.add("log")
CFlags.add("-I${file("src/main/jni/aes")}".toString())
CFlags.add("-I${file("src/main/jni/sha")}".toString())
CFlags.add("-DUSE_SHA256")
}
}
dependencies {
androidTestCompile files('libs/junit4.jar')
// Set this dependency if you want to use Hamcrest matching
//androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
compile 'com.madgag.spongycastle:core:1.54.0.0'
compile 'com.madgag.spongycastle:prov:1.54.0.0'
compile 'joda-time:joda-time:2.9.4'
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright 2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
* KeePassDroid 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 2 of the License, or
* (at your option) any later version.
*
* KeePassDroid 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 should have received a copy of the GNU General Public License
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.collections;
import com.keepassdroid.stream.LEDataInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class VariantDictionary {
private static final int VdVersion = 0x0100;
private static final int VdmCritical = 0xFF00;
private static final int VdmInfo = 0x00FF;
private Map<String, Object> dict = new HashMap<String, Object>();
private enum VdType {
None(0x00),
UInt32(0x04),
UInt64(0x05),
Bool(0x08),
Int32(0x0C),
Int64(0x0D),
String(0x18),
ByteArray(0x42);
private final byte value;
VdType(int value) {
this.value = (byte) value;
}
boolean equals(byte type) {
return type == value;
}
byte getValue() {
return value;
}
}
public void setUInt32(String name, long value) {
dict.put(name, value);
}
public long getUInt32(String name) { return (long)dict.get(name); }
public void setUInt64(String name, long value) {
dict.put(name, value);
}
public long getUInt64(String name) { return (long)dict.get(name); }
public void setBool(String name, boolean value) {
dict.put(name, value);
}
public void setInt32(String name, int value) {
dict.put(name, value);
}
public void setInt64(String name, long value) {
dict.put(name, value);
}
public void setString(String name, String value) {
dict.put(name, value);
}
public void setByteArray(String name, byte[] value) {
dict.put(name, value);
}
public byte[] getByteArray(String name) { return (byte[])dict.get(name); }
public static VariantDictionary deserialize(LEDataInputStream lis) throws IOException {
VariantDictionary d = new VariantDictionary();
int version = lis.readUShort();
if ((version & VdmCritical) > (VdVersion & VdmCritical)) {
throw new IOException("Invalid format");
}
while(true) {
int type = lis.read();
if (type < 0) {
throw new IOException(("Invalid format"));
}
byte bType = (byte)type;
if (VdType.None.equals(bType)) {
break;
}
int nameLen = lis.readInt();
byte[] nameBuf = lis.readBytes(nameLen);
if (nameLen != nameBuf.length) {
throw new IOException("Invalid format");
}
String name = new String(nameBuf, "UTF-8");
int valueLen = lis.readInt();
byte[] valueBuf = lis.readBytes(valueLen);
if (valueLen != valueBuf.length) {
throw new IOException("Invalid format");
}
if (VdType.UInt32.equals(bType)) {
if (valueLen == 4) {
d.setUInt32(name, LEDataInputStream.readUInt(valueBuf, 0));
}
}
else if (VdType.UInt64.equals(bType)) {
if (valueLen == 8) {
d.setUInt64(name, LEDataInputStream.readLong(valueBuf, 0));
}
}
else if (VdType.Bool.equals(bType)) {
if (valueLen == 1) {
d.setBool(name, valueBuf[0] != 0);
}
}
else if (VdType.Int32.equals(bType)) {
if (valueLen == 4) {
d.setInt32(name, LEDataInputStream.readInt(valueBuf, 0));
}
}
else if (VdType.Int64.equals(bType)) {
if (valueLen == 8) {
d.setInt64(name, LEDataInputStream.readLong(valueBuf, 0));
}
}
else if (VdType.String.equals(bType)) {
d.setString(name, new String(valueBuf, "UTF-8"));
}
else if (VdType.ByteArray.equals(bType)) {
d.setByteArray(name, valueBuf);
}
else {
assert(false);
}
}
return d;
}
public void copyTo(VariantDictionary d) {
for (Map.Entry<String, Object> entry : d.dict.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
dict.put(key, value);
}
}
}

View File

@@ -82,6 +82,10 @@ public class CryptoUtil {
return hashGen("SHA-256", data, offset, count);
}
public static byte[] hashSha512(byte[] data) {
return hashSha512(data, 0, data.length);
}
public static byte[] hashSha512(byte[] data, int offset, int count) {
return hashGen("SHA-512", data, offset, count);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2009 Brian Pellin.
* Copyright 2009-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -31,6 +31,7 @@ public class NativeLib {
if ( ! isLoaded ) {
try {
System.loadLibrary("final-key");
System.loadLibrary("argon2");
} catch ( UnsatisfiedLinkError e) {
return false;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2009 Brian Pellin.
* Copyright 2009-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -19,10 +19,8 @@
*/
package com.keepassdroid.crypto;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.spongycastle.crypto.StreamCipher;
import org.spongycastle.crypto.engines.ChaChaEngine;
import org.spongycastle.crypto.engines.Salsa20Engine;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.crypto.params.ParametersWithIV;
@@ -33,7 +31,8 @@ public class PwStreamCipherFactory {
public static StreamCipher getInstance(CrsAlgorithm alg, byte[] key) {
if ( alg == CrsAlgorithm.Salsa20 ) {
return getSalsa20(key);
} else if (alg == CrsAlgorithm.ChaCha20) {
return getChaCha20(key);
} else {
return null;
}
@@ -42,23 +41,34 @@ public class PwStreamCipherFactory {
private static final byte[] SALSA_IV = new byte[]{ (byte)0xE8, 0x30, 0x09, 0x4B,
(byte)0x97, 0x20, 0x5D, 0x2A };
private static StreamCipher getSalsa20(byte[] key) {
// Build stream cipher key
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("SHA 256 not supported");
}
byte[] key32 = md.digest(key);
byte[] key32 = CryptoUtil.hashSha256(key);
KeyParameter keyParam = new KeyParameter(key32);
ParametersWithIV ivParam = new ParametersWithIV(keyParam, SALSA_IV);
StreamCipher cipher = new Salsa20Engine();
cipher.init(true, ivParam);
return cipher;
}
private static StreamCipher getChaCha20(byte[] key) {
// Build stream cipher key
byte[] hash = CryptoUtil.hashSha512(key);
byte[] key32 = new byte[32];
byte[] iv = new byte[12];
System.arraycopy(hash, 0, key32, 0, 32);
System.arraycopy(hash, 32, iv, 0, 12);
KeyParameter keyParam = new KeyParameter(key32);
ParametersWithIV ivParam = new ParametersWithIV(keyParam, SALSA_IV);
StreamCipher cipher = new ChaChaEngine();
cipher.init(true, ivParam);
return cipher;
}

View File

@@ -36,7 +36,7 @@ import javax.crypto.spec.SecretKeySpec;
public class AesEngine extends CipherEngine {
public static final UUID CIPHER_UUID = Types.bytestoUUID(
new byte[]{(byte) 0x31, (byte) 0xC1, (byte) 0xF2, (byte) 0xE6, (byte) 0xBF, (byte) 0x71, (byte) 0x43, (byte) 0x50,
(byte) 0xBE, (byte) 0x58, (byte) 0x05, (byte) 0x21, (byte) 0x6A, (byte) 0xFC, 0x5A, (byte) 0xFF
(byte) 0xBE, (byte) 0x58, (byte) 0x05, (byte) 0x21, (byte) 0x6A, (byte) 0xFC, (byte)0x5A, (byte) 0xFF
});
@Override

View File

@@ -32,7 +32,7 @@ import javax.crypto.spec.SecretKeySpec;
public class AndroidFinalKey extends FinalKey {
@Override
public byte[] transformMasterKey(byte[] pKeySeed, byte[] pKey, int rounds) throws IOException {
public byte[] transformMasterKey(byte[] pKeySeed, byte[] pKey, long rounds) throws IOException {
Cipher cipher;
try {
cipher = Cipher.getInstance("AES/ECB/NoPadding");

View File

@@ -22,5 +22,5 @@ package com.keepassdroid.crypto.finalkey;
import java.io.IOException;
public abstract class FinalKey {
public abstract byte[] transformMasterKey(byte[] seed, byte[] key, int rounds) throws IOException;
public abstract byte[] transformMasterKey(byte[] seed, byte[] key, long rounds) throws IOException;
}

View File

@@ -31,14 +31,14 @@ public class NativeFinalKey extends FinalKey {
}
@Override
public byte[] transformMasterKey(byte[] seed, byte[] key, int rounds) throws IOException {
public byte[] transformMasterKey(byte[] seed, byte[] key, long rounds) throws IOException {
NativeLib.init();
return nTransformMasterKey(seed, key, rounds);
}
private static native byte[] nTransformMasterKey(byte[] seed, byte[] key, int rounds);
private static native byte[] nTransformMasterKey(byte[] seed, byte[] key, long rounds);
// For testing
/*

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
* KeePassDroid 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 2 of the License, or
* (at your option) any later version.
*
* KeePassDroid 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 should have received a copy of the GNU General Public License
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.crypto.keyDerivation;
import com.keepassdroid.crypto.CryptoUtil;
import com.keepassdroid.crypto.finalkey.FinalKey;
import com.keepassdroid.crypto.finalkey.FinalKeyFactory;
import com.keepassdroid.database.PwDatabaseV4;
import com.keepassdroid.utils.Types;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.UUID;
public class AesKdf extends KdfEngine {
public static final UUID CIPHER_UUID = Types.bytestoUUID(
new byte[]{(byte) 0xC9, (byte) 0xD9, (byte) 0xF3, (byte) 0x9A, (byte) 0x62, (byte) 0x8A, (byte) 0x44, (byte) 0x60,
(byte) 0xBF, (byte) 0x74, (byte) 0x0D, (byte) 0x08, (byte)0xC1, (byte) 0x8A, (byte) 0x4F, (byte) 0xEA
});
public static final String ParamRounds = "R";
public static final String ParamSeed = "S";
public AesKdf() {
uuid = CIPHER_UUID;
}
@Override
public KdfParameters getDefaultParameters() {
KdfParameters p = super.getDefaultParameters();
p.setUInt32(ParamRounds, PwDatabaseV4.DEFAULT_ROUNDS);
return p;
}
@Override
public byte[] transform(byte[] masterKey, KdfParameters p) throws IOException {
long rounds = p.getUInt64(ParamRounds);
byte[] seed = p.getByteArray(ParamSeed);
if (masterKey.length != 32) {
masterKey = CryptoUtil.hashSha256(masterKey);
}
if (seed.length != 32) {
seed = CryptoUtil.hashSha256(seed);
}
FinalKey key = FinalKeyFactory.createFinalKey();
return key.transformMasterKey(seed, masterKey, rounds);
}
@Override
public void randomize(KdfParameters p) {
SecureRandom random = new SecureRandom();
byte[] seed = new byte[32];
random.nextBytes(seed);
p.setByteArray(ParamSeed, seed);
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
* KeePassDroid 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 2 of the License, or
* (at your option) any later version.
*
* KeePassDroid 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 should have received a copy of the GNU General Public License
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.crypto.keyDerivation;
import com.keepassdroid.utils.Types;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.UUID;
public class Argon2Kdf extends KdfEngine {
public static final UUID CIPHER_UUID = Types.bytestoUUID(
new byte[]{(byte) 0xEF, (byte) 0x63, (byte) 0x6D, (byte) 0xDF, (byte) 0x8C, (byte) 0x29, (byte) 0x44, (byte) 0x4B,
(byte) 0x91, (byte) 0xF7, (byte) 0xA9, (byte) 0xA4, (byte)0x03, (byte) 0xE3, (byte) 0x0A, (byte) 0x0C
});
public static final String ParamSalt = "S"; // byte[]
public static final String ParamParallelism = "P"; // UInt32
public static final String ParamMemory = "M"; // UInt64
public static final String ParamIterations = "I"; // UInt64
public static final String ParamVersion = "V"; // UInt32
public static final String ParamSecretKey = "K"; // byte[]
public static final String ParamAssocData = "A"; // byte[]
public static final long MinVersion = 0x10;
public static final long MaxVersion = 0x13;
private static final int MinSalt = 8;
private static final int MaxSalt = Integer.MAX_VALUE;
private static final long MinIterations = 1;
private static final long MaxIterations = 4294967295L;
private static final long MinMemory = 1024 * 8;
private static final long MaxMemory = Integer.MAX_VALUE;
private static final int MinParallelism = 1;
private static final int MaxParallelism = (1 << 24) - 1;
private static final long DefaultIterations = 2;
private static final long DefaultMemory = 1024 * 1024;
private static final long DefaultParallelism = 2;
public Argon2Kdf() {
uuid = CIPHER_UUID;
}
@Override
public KdfParameters getDefaultParameters() {
KdfParameters p = super.getDefaultParameters();
p.setUInt32(ParamVersion, MaxVersion);
p.setUInt64(ParamMemory, DefaultMemory);
p.setUInt32(ParamParallelism, DefaultParallelism);
return p;
}
@Override
public byte[] transform(byte[] masterKey, KdfParameters p) throws IOException {
byte[] salt = p.getByteArray(ParamSalt);
int parallelism = (int)p.getUInt32(ParamParallelism);
long memory = p.getUInt64(ParamMemory);
long iterations = p.getUInt64(ParamIterations);
long version = p.getUInt32(ParamVersion);
byte[] secretKey = p.getByteArray(ParamSecretKey);
byte[] assocData = p.getByteArray(ParamAssocData);
return Argon2Native.transformKey(masterKey, salt, parallelism, memory, iterations,
secretKey, assocData, version);
}
@Override
public void randomize(KdfParameters p) {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[32];
random.nextBytes(salt);
p.setByteArray(ParamSalt, salt);
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
* KeePassDroid 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 2 of the License, or
* (at your option) any later version.
*
* KeePassDroid 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 should have received a copy of the GNU General Public License
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.crypto.keyDerivation;
import com.keepassdroid.crypto.NativeLib;
import java.io.IOException;
public class Argon2Native {
public static byte[] transformKey(byte[] password, byte[] salt, int parallelism,
long memory, long iterations, byte[] secretKey,
byte[] associatedData, long version) throws IOException {
NativeLib.init();
return nTransformMasterKey(password, salt, parallelism, memory, iterations, secretKey, associatedData, version);
}
private static native byte[] nTransformMasterKey(byte[] password, byte[] salt, int parallelism,
long memory, long iterations, byte[] secretKey,
byte[] associatedData, long version) throws IOException;
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
* KeePassDroid 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 2 of the License, or
* (at your option) any later version.
*
* KeePassDroid 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 should have received a copy of the GNU General Public License
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.crypto.keyDerivation;
import java.io.IOException;
import java.util.UUID;
public abstract class KdfEngine {
public UUID uuid;
public KdfParameters getDefaultParameters() {
return new KdfParameters(uuid);
}
public abstract byte[] transform(byte[] masterKey, KdfParameters p) throws IOException;
public abstract void randomize(KdfParameters p);
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
* KeePassDroid 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 2 of the License, or
* (at your option) any later version.
*
* KeePassDroid 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 should have received a copy of the GNU General Public License
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.crypto.keyDerivation;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class KdfFactory {
public static List<KdfEngine> kdfList = new ArrayList<KdfEngine>();
static {
kdfList.add(new AesKdf());
kdfList.add(new Argon2Kdf());
}
public static KdfParameters getDefaultParameters() {
return kdfList.get(0).getDefaultParameters();
}
public static KdfEngine get(UUID uuid) {
for (KdfEngine engine: kdfList) {
if (engine.uuid.equals(uuid)) {
return engine;
}
}
return null;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
* KeePassDroid 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 2 of the License, or
* (at your option) any later version.
*
* KeePassDroid 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 should have received a copy of the GNU General Public License
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.crypto.keyDerivation;
import com.keepassdroid.collections.VariantDictionary;
import com.keepassdroid.stream.LEDataInputStream;
import com.keepassdroid.utils.Types;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.UUID;
public class KdfParameters extends VariantDictionary {
public UUID kdfUUID;
private static final String ParamUUID = "$UUID";
public KdfParameters(UUID uuid) {
kdfUUID = uuid;
}
public static KdfParameters deserialize(byte[] data) throws IOException {
ByteArrayInputStream bis = new ByteArrayInputStream(data);
LEDataInputStream lis = new LEDataInputStream(bis);
VariantDictionary d = VariantDictionary.deserialize(lis);
if (d == null) {
assert(false);
return null;
}
UUID uuid = Types.bytestoUUID(d.getByteArray(ParamUUID));
if (uuid == null) {
assert(false);
return null;
}
KdfParameters kdfP = new KdfParameters(uuid);
kdfP.copyTo(d);
return kdfP;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 Brian Pellin.
* Copyright 2013-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -27,7 +27,7 @@ import java.util.Set;
import com.keepassdroid.database.security.ProtectedBinary;
public class BinaryPool {
private HashMap<String, ProtectedBinary> pool = new HashMap<String, ProtectedBinary>();
private HashMap<Integer, ProtectedBinary> pool = new HashMap<Integer, ProtectedBinary>();
public BinaryPool() {
@@ -37,15 +37,16 @@ public class BinaryPool {
build(rootGroup);
}
public ProtectedBinary get(String key) {
public ProtectedBinary get(int key) {
return pool.get(key);
}
public ProtectedBinary put(String key, ProtectedBinary value) {
public ProtectedBinary put(int key, ProtectedBinary value) {
return pool.put(key, value);
}
public Set<Entry<String, ProtectedBinary>> entrySet() {
public Set<Entry<Integer, ProtectedBinary>> entrySet() {
return pool.entrySet();
}
@@ -74,17 +75,17 @@ public class BinaryPool {
private void poolAdd(ProtectedBinary pb) {
assert(pb != null);
if (poolFind(pb) != null) return;
if (poolFind(pb) != -1) return;
pool.put(String.valueOf(pool.size()), pb);
pool.put(pool.size(), pb);
}
public String poolFind(ProtectedBinary pb) {
for (Entry<String, ProtectedBinary> pair : pool.entrySet()) {
public int poolFind(ProtectedBinary pb) {
for (Entry<Integer, ProtectedBinary> pair : pool.entrySet()) {
if (pair.getValue().equals(pb)) return pair.getKey();
}
return null;
return -1;
}
private void build(PwGroupV4 rootGroup) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010 Brian Pellin.
* Copyright 2010-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -23,9 +23,10 @@ public enum CrsAlgorithm {
Null(0),
ArcFourVariant(1),
Salsa20(2);
public static final int count = 3;
Salsa20(2),
ChaCha20(3);
public static final int count = 4;
public final int id;
private CrsAlgorithm(int num) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 Brian Pellin.
* Copyright 2010-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -45,11 +45,16 @@ import org.w3c.dom.Text;
import android.webkit.URLUtil;
import biz.source_code.base64Coder.Base64Coder;
import com.keepassdroid.collections.VariantDictionary;
import com.keepassdroid.crypto.CipherFactory;
import com.keepassdroid.crypto.CryptoUtil;
import com.keepassdroid.crypto.PwStreamCipherFactory;
import com.keepassdroid.crypto.engine.AesEngine;
import com.keepassdroid.crypto.engine.CipherEngine;
import com.keepassdroid.crypto.keyDerivation.AesKdf;
import com.keepassdroid.crypto.keyDerivation.KdfEngine;
import com.keepassdroid.crypto.keyDerivation.KdfFactory;
import com.keepassdroid.crypto.keyDerivation.KdfParameters;
import com.keepassdroid.database.exception.InvalidKeyFileException;
import com.keepassdroid.utils.EmptyUtils;
@@ -58,6 +63,7 @@ public class PwDatabaseV4 extends PwDatabase {
public static final Date DEFAULT_NOW = new Date();
public static final UUID UUID_ZERO = new UUID(0,0);
public static final int DEFAULT_ROUNDS = 6000;
private static final int DEFAULT_HISTORY_MAX_ITEMS = 10; // -1 unlimited
private static final long DEFAULT_HISTORY_MAX_SIZE = 6 * 1024 * 1024; // -1 unlimited
private static final String RECYCLEBIN_NAME = "RecycleBin";
@@ -68,6 +74,7 @@ public class PwDatabaseV4 extends PwDatabase {
public PwCompressionAlgorithm compressionAlgorithm = PwCompressionAlgorithm.Gzip;
public long numKeyEncRounds = 6000;
public Date nameChanged = DEFAULT_NOW;
public Date settingsChanged = DEFAULT_NOW;
public String description = "";
public Date descriptionChanged = DEFAULT_NOW;
public String defaultUserName = "";
@@ -76,6 +83,7 @@ public class PwDatabaseV4 extends PwDatabase {
public Date keyLastChanged = DEFAULT_NOW;
public long keyChangeRecDays = -1;
public long keyChangeForceDays = 1;
public boolean keyChangeForceOnce = false;
public long maintenanceHistoryDays = 365;
public String color = "";
@@ -92,6 +100,8 @@ public class PwDatabaseV4 extends PwDatabase {
public List<PwDeletedObject> deletedObjects = new ArrayList<PwDeletedObject>();
public List<PwIconCustom> customIcons = new ArrayList<PwIconCustom>();
public Map<String, String> customData = new HashMap<String, String>();
public KdfParameters kdfParameters = KdfFactory.getDefaultParameters();
public VariantDictionary publicCustomData;
public String localizedAppName = "KeePassDroid";
@@ -148,6 +158,34 @@ public class PwDatabaseV4 extends PwDatabase {
byte[] transformedMasterKey = transformMasterKey(masterSeed2, masterKey, numRounds);
byte[] cmpKey = new byte[65];
System.arraycopy(masterSeed, 0, cmpKey, 0, 32);
System.arraycopy(transformedMasterKey, 0, cmpKey, 32, 32);
finalKey = CryptoUtil.resizeKey(cmpKey, 0, 64, dataEngine.keyLength());
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-512");
cmpKey[64] = 1;
hmacKey = md.digest(cmpKey);
} catch (NoSuchAlgorithmException e) {
throw new IOException("No SHA-512 implementation");
} finally {
Arrays.fill(cmpKey, (byte)0);
}
}
public void makeFinalKey(byte[] masterSeed, KdfParameters kdfP) throws IOException {
KdfEngine kdfEngine = KdfFactory.get(kdfP.kdfUUID);
if (kdfEngine == null) {
throw new IOException("Unknown key derivation function");
}
byte[] transformedMasterKey = kdfEngine.transform(masterKey, kdfP);
if (transformedMasterKey.length != 32) {
transformedMasterKey = CryptoUtil.hashSha256(transformedMasterKey);
}
byte[] cmpKey = new byte[65];
System.arraycopy(masterSeed, 0, cmpKey, 0, 32);
System.arraycopy(transformedMasterKey, 0, cmpKey, 32, 32);
@@ -421,5 +459,4 @@ public class PwDatabaseV4 extends PwDatabase {
return filename.substring(0, lastExtDot);
}
}

View File

@@ -41,6 +41,7 @@ public class PwDatabaseV4XML {
public static final String ElemGenerator = "Generator";
public static final String ElemHeaderHash = "HeaderHash";
public static final String ElemSettingsChanged = "SettingsChanged";
public static final String ElemDbName = "DatabaseName";
public static final String ElemDbNameChanged = "DatabaseNameChanged";
public static final String ElemDbDesc = "DatabaseDescription";
@@ -52,6 +53,7 @@ public class PwDatabaseV4XML {
public static final String ElemDbKeyChanged = "MasterKeyChanged";
public static final String ElemDbKeyChangeRec = "MasterKeyChangeRec";
public static final String ElemDbKeyChangeForce = "MasterKeyChangeForce";
public static final String ElemDbKeyChangeForceOnce = "MasterKeyChangeForceOnce";
public static final String ElemRecycleBinEnabled = "RecycleBinEnabled";
public static final String ElemRecycleBinUuid = "RecycleBinUUID";
public static final String ElemRecycleBinChanged = "RecycleBinChanged";

View File

@@ -26,9 +26,6 @@ public abstract class PwDbHeader {
/** Seed that gets hashed with the userkey to form the final key */
public byte masterSeed[];
/** Used for the dwKeyEncRounds AES transformations */
public byte transformSeed[] = new byte[32];
/** IV used for content encryption */
public byte encryptionIV[] = new byte[16];

View File

@@ -65,7 +65,8 @@ public class PwDbHeaderV3 extends PwDbHeader {
/** Size of byte buffer needed to hold this struct. */
public static final int BUF_SIZE = 124;
/** Used for the dwKeyEncRounds AES transformations */
public byte transformSeed[] = new byte[32];
public int signature1; // = PWM_DBSIG_1
public int signature2; // = DBSIG_2

View File

@@ -23,11 +23,12 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.DigestInputStream;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import com.keepassdroid.database.exception.InvalidAlgorithmException;
import com.keepassdroid.database.exception.InvalidDBException;
import com.keepassdroid.crypto.keyDerivation.AesKdf;
import com.keepassdroid.crypto.keyDerivation.KdfParameters;
import com.keepassdroid.database.exception.InvalidDBVersionException;
import com.keepassdroid.stream.CopyInputStream;
import com.keepassdroid.stream.HmacBlockStream;
@@ -35,6 +36,7 @@ import com.keepassdroid.stream.LEDataInputStream;
import com.keepassdroid.utils.Types;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class PwDbHeaderV4 extends PwDbHeader {
public static final int DBSIG_PRE2 = 0xB54BFB66;
@@ -42,7 +44,7 @@ public class PwDbHeaderV4 extends PwDbHeader {
private static final int FILE_VERSION_CRITICAL_MASK = 0xFFFF0000;
public static final int FILE_VERSION_32_3 = 0x00030001;
public static final int FILE_VERSION_32_4 = 0x00040001;
public static final int FILE_VERSION_32_4 = 0x00040000;
public static final int FILE_VERSION_32 = FILE_VERSION_32_4;
public class PwDbHeaderV4Fields {
@@ -54,12 +56,26 @@ public class PwDbHeaderV4 extends PwDbHeader {
public static final byte TransformSeed = 5;
public static final byte TransformRounds = 6;
public static final byte EncryptionIV = 7;
public static final byte ProtectedStreamKey = 8;
public static final byte InnerRandomstreamKey = 8;
public static final byte StreamStartBytes = 9;
public static final byte InnerRandomStreamID = 10;
public static final byte KdfParameters = 11;
public static final byte PublicCustomData = 12;
}
public class PwDbInnerHeaderV4Fields {
public static final byte EndOfHeader = 0;
public static final byte InnerRandomStreamID = 1;
public static final byte InnerRandomstreamKey = 2;
public static final byte Binary = 3;
}
public class KdbxBinaryFlags {
public static final byte None = 0;
public static final byte Protected = 1;
}
public class HeaderAndHash {
public byte[] header;
public byte[] hash;
@@ -124,7 +140,12 @@ public class PwDbHeaderV4 extends PwDbHeader {
private boolean readHeaderField(LEDataInputStream dis) throws IOException {
byte fieldID = (byte) dis.read();
int fieldSize = dis.readUShort();
int fieldSize;
if (version < FILE_VERSION_32_4) {
fieldSize = dis.readUShort();
} else {
fieldSize = dis.readInt();
}
byte[] fieldData = null;
if ( fieldSize > 0 ) {
@@ -153,18 +174,30 @@ public class PwDbHeaderV4 extends PwDbHeader {
break;
case PwDbHeaderV4Fields.TransformSeed:
transformSeed = fieldData;
assert(version < PwDbHeaderV4.FILE_VERSION_32_4);
AesKdf kdfS = new AesKdf();
if (!db.kdfParameters.kdfUUID.equals(kdfS.uuid)) {
db.kdfParameters = kdfS.getDefaultParameters();
}
db.kdfParameters.setByteArray(AesKdf.ParamSeed, fieldData);
break;
case PwDbHeaderV4Fields.TransformRounds:
setTransformRounds(fieldData);
assert(version < PwDbHeaderV4.FILE_VERSION_32_4);
AesKdf kdfR = new AesKdf();
if (!db.kdfParameters.kdfUUID.equals(kdfR.uuid)) {
db.kdfParameters = kdfR.getDefaultParameters();
}
db.kdfParameters.setUInt64(AesKdf.ParamRounds, LEDataInputStream.readLong(fieldData, 0));
break;
case PwDbHeaderV4Fields.EncryptionIV:
encryptionIV = fieldData;
break;
case PwDbHeaderV4Fields.ProtectedStreamKey:
case PwDbHeaderV4Fields.InnerRandomstreamKey:
assert(version < PwDbHeaderV4.FILE_VERSION_32_4);
protectedStreamKey = fieldData;
break;
@@ -173,11 +206,18 @@ public class PwDbHeaderV4 extends PwDbHeader {
break;
case PwDbHeaderV4Fields.InnerRandomStreamID:
assert(version < PwDbHeaderV4.FILE_VERSION_32_4);
setRandomStreamID(fieldData);
break;
case PwDbHeaderV4Fields.KdfParameters:
db.kdfParameters = KdfParameters.deserialize(fieldData);
break;
case PwDbHeaderV4Fields.PublicCustomData:
db.publicCustomData = KdfParameters.deserialize(fieldData);
default:
throw new IOException("Invalid header type.");
throw new IOException("Invalid header type: " + fieldID);
}
@@ -222,7 +262,7 @@ public class PwDbHeaderV4 extends PwDbHeader {
}
private void setRandomStreamID(byte[] streamID) throws IOException {
public void setRandomStreamID(byte[] streamID) throws IOException {
if ( streamID == null || streamID.length != 4 ) {
throw new IOException("Invalid stream id.");
}
@@ -244,7 +284,7 @@ public class PwDbHeaderV4 extends PwDbHeader {
*/
private boolean validVersion(long version) {
return ! ((version & FILE_VERSION_CRITICAL_MASK) > (FILE_VERSION_32_3 & FILE_VERSION_CRITICAL_MASK));
return ! ((version & FILE_VERSION_CRITICAL_MASK) > (FILE_VERSION_32 & FILE_VERSION_CRITICAL_MASK));
}
@@ -259,11 +299,24 @@ public class PwDbHeaderV4 extends PwDbHeader {
Mac hmac;
try {
hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec signingKey = new SecretKeySpec(blockKey, "HmacSHA256");
hmac.init(signingKey);
} catch (NoSuchAlgorithmException e) {
throw new IOException("No HmacAlogirthm");
} catch (InvalidKeyException e) {
throw new IOException("Invalid Hmac Key");
}
return hmac.doFinal(header);
}
public void setMinimumVersion() {
}
public byte[] getTransformSeed() {
assert(version < FILE_VERSION_32_4);
return db.kdfParameters.getByteArray(AesKdf.ParamSeed);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2014 Brian Pellin.
* Copyright 2010-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
@@ -59,6 +60,7 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger {
public String url = "";
public String additional = "";
public String tags = "";
public Map<String, String> customData = new HashMap<String, String>();
public class AutoType implements Cloneable {
private static final long OBF_OPT_NONE = 0;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2013 Brian Pellin.
* Copyright 2010-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -20,7 +20,9 @@
package com.keepassdroid.database;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class PwGroupV4 extends PwGroup implements ITimeLogger {
@@ -44,6 +46,7 @@ public class PwGroupV4 extends PwGroup implements ITimeLogger {
private Date expireDate = PwDatabaseV4.DEFAULT_NOW;
private boolean expires = false;
private long usageCount = 0;
public Map<String, String> customData = new HashMap<String, String>();
public PwGroupV4() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2009-2013 Brian Pellin.
* Copyright 2009-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -29,8 +29,10 @@ import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Stack;
import java.util.TimeZone;
import java.util.UUID;
import java.util.zip.GZIPInputStream;
@@ -38,6 +40,7 @@ import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import org.spongycastle.crypto.StreamCipher;
import org.spongycastle.util.encoders.Base64Encoder;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
@@ -49,6 +52,7 @@ import com.keepassdroid.crypto.CipherFactory;
import com.keepassdroid.crypto.PwStreamCipherFactory;
import com.keepassdroid.crypto.engine.CipherEngine;
import com.keepassdroid.database.BinaryPool;
import com.keepassdroid.database.CrsAlgorithm;
import com.keepassdroid.database.ITimeLogger;
import com.keepassdroid.database.PwCompressionAlgorithm;
import com.keepassdroid.database.PwDatabaseV4;
@@ -67,6 +71,7 @@ import com.keepassdroid.stream.BetterCipherInputStream;
import com.keepassdroid.stream.HashedBlockInputStream;
import com.keepassdroid.stream.HmacBlockInputStream;
import com.keepassdroid.stream.LEDataInputStream;
import com.keepassdroid.utils.DateUtil;
import com.keepassdroid.utils.EmptyUtils;
import com.keepassdroid.utils.MemUtil;
import com.keepassdroid.utils.Types;
@@ -79,6 +84,12 @@ public class ImporterV4 extends Importer {
private byte[] hashOfHeader = null;
private byte[] pbHeader = null;
private long version;
Calendar utcCal;
public ImporterV4() {
utcCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
}
protected PwDatabaseV4 createDB() {
return new PwDatabaseV4();
@@ -102,34 +113,34 @@ public class ImporterV4 extends Importer {
PwDbHeaderV4 header = new PwDbHeaderV4(db);
PwDbHeaderV4.HeaderAndHash hh = header.loadFromFile(inStream);
version = header.version;
hashOfHeader = hh.hash;
pbHeader = hh.header;
db.setMasterKey(password, keyInputStream);
db.makeFinalKey(header.masterSeed, header.transformSeed, (int)db.numKeyEncRounds);
db.makeFinalKey(header.masterSeed, db.kdfParameters);
CipherEngine engine;
Cipher cipher;
try {
engine = CipherFactory.getInstance(db.dataCipher);
db.dataEngine = engine;
cipher = engine.getCipher(Cipher.DECRYPT_MODE, db.finalKey, header.encryptionIV);
} catch (NoSuchAlgorithmException e) {
throw new IOException("Invalid algorithm.");
} catch (NoSuchPaddingException e) {
throw new IOException("Invalid algorithm.");
} catch (InvalidKeyException e) {
throw new IOException("Invalid algorithm.");
} catch (InvalidAlgorithmParameterException e) {
throw new IOException("Invalid algorithm.");
}
InputStream isPlain;
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
if (version < PwDbHeaderV4.FILE_VERSION_32_4) {
// Attach decryptor
CipherEngine engine;
Cipher cipher;
try {
engine = CipherFactory.getInstance(db.dataCipher);
db.dataEngine = engine;
cipher = engine.getCipher(Cipher.DECRYPT_MODE, db.finalKey, header.encryptionIV);
} catch (NoSuchAlgorithmException e) {
throw new IOException("Invalid algorithm.");
} catch (NoSuchPaddingException e) {
throw new IOException("Invalid algorithm.");
} catch (InvalidKeyException e) {
throw new IOException("Invalid algorithm.");
} catch (InvalidAlgorithmParameterException e) {
throw new IOException("Invalid algorithm.");
}
InputStream decrypted = new BetterCipherInputStream(inStream, cipher, 50 * 1024);
InputStream decrypted = AttachCipherStream(inStream, cipher);
LEDataInputStream dataDecrypted = new LEDataInputStream(decrypted);
byte[] storedStartBytes = null;
try {
@@ -167,8 +178,7 @@ public class ImporterV4 extends Importer {
HmacBlockInputStream hmIs = new HmacBlockInputStream(isData, true, hmacKey);
isPlain = null;
isPlain = AttachCipherStream(hmIs, cipher);
}
InputStream isXml;
@@ -177,6 +187,10 @@ public class ImporterV4 extends Importer {
} else {
isXml = isPlain;
}
if (version >= header.FILE_VERSION_32_4) {
LoadInnerHeader(isXml, header);
}
if ( header.protectedStreamKey == null ) {
assert(false);
@@ -195,7 +209,64 @@ public class ImporterV4 extends Importer {
}
private InputStream AttachCipherStream(InputStream is, Cipher cipher) {
return new BetterCipherInputStream(is, cipher, 50 * 1024);
}
private void LoadInnerHeader(InputStream is, PwDbHeaderV4 header) throws IOException {
LEDataInputStream lis = new LEDataInputStream(is);
while(true) {
if (!ReadInnerHeader(lis, header)) break;
}
}
private boolean ReadInnerHeader(LEDataInputStream lis, PwDbHeaderV4 header) throws IOException {
byte fieldId = (byte)lis.read();
int size = lis.readInt();
if (size < 0) throw new IOException("Corrupted file");
byte[] data = new byte[0];
if (size > 0) {
data = lis.readBytes(size);
}
boolean result = true;
switch(fieldId) {
case PwDbHeaderV4.PwDbInnerHeaderV4Fields.EndOfHeader:
result = false;
break;
case PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomStreamID:
header.setRandomStreamID(data);
break;
case PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomstreamKey:
header.protectedStreamKey = data;
break;
case PwDbHeaderV4.PwDbInnerHeaderV4Fields.Binary:
if (data.length < 1) throw new IOException("Invalid binary format");
byte flag = data[0];
boolean prot = (flag & PwDbHeaderV4.KdbxBinaryFlags.Protected) !=
PwDbHeaderV4.KdbxBinaryFlags.None;
byte[] bin = new byte[data.length - 1];
System.arraycopy(data, 1, bin, 0, data.length-1);
ProtectedBinary pb = new ProtectedBinary(prot, bin);
if (prot) {
Arrays.fill(data, (byte)0);
}
break;
default:
assert(false);
break;
}
return result;
}
private enum KdbContext {
Null,
KeePassFile,
@@ -210,6 +281,8 @@ public class ImporterV4 extends Importer {
DeletedObject,
Group,
GroupTimes,
GroupCustomData,
GroupCustomDataItem,
Entry,
EntryTimes,
EntryString,
@@ -217,10 +290,11 @@ public class ImporterV4 extends Importer {
EntryAutoType,
EntryAutoTypeItem,
EntryHistory,
EntryCustomData,
EntryCustomDataItem,
Binaries
}
private static final long DEFAULT_HISTORY_DAYS = 365;
private boolean readNextNode = true;
@@ -240,7 +314,11 @@ public class ImporterV4 extends Importer {
private byte[] customIconData;
private String customDataKey = null;
private String customDataValue = null;
private String groupCustomDataKey = null;
private String groupCustomDataValue = null;
private String entryCustomDataKey = null;
private String entryCustomDataValue = null;
private void ReadXmlStreamed(InputStream readerStream) throws IOException, InvalidDBException {
try {
@@ -284,7 +362,7 @@ public class ImporterV4 extends Importer {
case XmlPullParser.END_TAG:
ctx = EndXmlElement(ctx, xpp);
break;
default:
assert(false);
break;
@@ -329,6 +407,8 @@ public class ImporterV4 extends Importer {
throw new InvalidDBException();
}
}
} else if (name.equalsIgnoreCase(ElemSettingsChanged)) {
db.settingsChanged = ReadTime(xpp);
} else if ( name.equalsIgnoreCase(ElemDbName) ) {
db.name = ReadString(xpp);
} else if ( name.equalsIgnoreCase(ElemDbNameChanged) ) {
@@ -352,6 +432,8 @@ public class ImporterV4 extends Importer {
db.keyChangeRecDays = ReadLong(xpp, -1);
} else if ( name.equalsIgnoreCase(ElemDbKeyChangeForce) ) {
db.keyChangeForceDays = ReadLong(xpp, -1);
} else if ( name.equalsIgnoreCase(ElemDbKeyChangeForceOnce) ) {
db.keyChangeForceOnce = ReadBool(xpp, false);
} else if ( name.equalsIgnoreCase(ElemMemoryProt) ) {
return SwitchContext(ctx, KdbContext.MemoryProtection, xpp);
} else if ( name.equalsIgnoreCase(ElemCustomIcons) ) {
@@ -429,7 +511,8 @@ public class ImporterV4 extends Importer {
String key = xpp.getAttributeValue(null, AttrId);
if ( key != null ) {
ProtectedBinary pbData = ReadProtectedBinary(xpp);
binPool.put(key, pbData);
int id = Integer.parseInt(key);
binPool.put(id, pbData);
} else {
ReadUnknown(xpp);
}
@@ -497,6 +580,8 @@ public class ImporterV4 extends Importer {
ctxGroup.enableSearching = StringToBoolean(ReadString(xpp));
} else if ( name.equalsIgnoreCase(ElemLastTopVisibleEntry) ) {
ctxGroup.lastTopVisibleEntry = ReadUuid(xpp);
} else if ( name.equalsIgnoreCase(ElemCustomData) ) {
return SwitchContext(ctx, KdbContext.GroupCustomData, xpp);
} else if ( name.equalsIgnoreCase(ElemGroup) ) {
ctxGroup = new PwGroupV4();
ctxGroups.peek().AddGroup(ctxGroup, true);
@@ -513,6 +598,23 @@ public class ImporterV4 extends Importer {
ReadUnknown(xpp);
}
break;
case GroupCustomData:
if (name.equalsIgnoreCase(ElemStringDictExItem)) {
return SwitchContext(ctx, KdbContext.GroupCustomDataItem, xpp);
} else {
ReadUnknown(xpp);
}
break;
case GroupCustomDataItem:
if (name.equalsIgnoreCase(ElemKey)) {
groupCustomDataKey = ReadString(xpp);
} else if (name.equalsIgnoreCase(ElemValue)) {
groupCustomDataValue = ReadString(xpp);
} else {
ReadUnknown(xpp);
}
break;
case Entry:
if ( name.equalsIgnoreCase(ElemUuid) ) {
@@ -537,6 +639,8 @@ public class ImporterV4 extends Importer {
return SwitchContext(ctx, KdbContext.EntryBinary, xpp);
} else if ( name.equalsIgnoreCase(ElemAutoType) ) {
return SwitchContext(ctx, KdbContext.EntryAutoType, xpp);
} else if ( name.equalsIgnoreCase(ElemCustomData)) {
return SwitchContext(ctx, KdbContext.EntryCustomData, xpp);
} else if ( name.equalsIgnoreCase(ElemHistory) ) {
assert(!entryInHistory);
@@ -550,7 +654,23 @@ public class ImporterV4 extends Importer {
ReadUnknown(xpp);
}
break;
case EntryCustomData:
if (name.equalsIgnoreCase(ElemStringDictExItem)) {
return SwitchContext(ctx, KdbContext.EntryCustomDataItem, xpp);
} else {
ReadUnknown(xpp);
}
break;
case EntryCustomDataItem:
if (name.equalsIgnoreCase(ElemKey)) {
entryCustomDataKey = ReadString(xpp);
} else if (name.equalsIgnoreCase(ElemValue)) {
entryCustomDataValue = ReadString(xpp);
} else {
ReadUnknown(xpp);
}
break;
case GroupTimes:
case EntryTimes:
ITimeLogger tl;
@@ -716,6 +836,20 @@ public class ImporterV4 extends Importer {
}
} else if ( ctx == KdbContext.GroupTimes && name.equalsIgnoreCase(ElemTimes) ) {
return KdbContext.Group;
} else if ( ctx == KdbContext.GroupCustomData && name.equalsIgnoreCase(ElemCustomData) ) {
return KdbContext.Group;
} else if ( ctx == KdbContext.GroupCustomDataItem && name.equalsIgnoreCase(ElemStringDictExItem)) {
if (groupCustomDataKey != null && groupCustomDataValue != null) {
ctxGroup.customData.put(groupCustomDataKey, groupCustomDataKey);
} else {
assert(false);
}
groupCustomDataKey = null;
groupCustomDataValue = null;
return KdbContext.GroupCustomData;
} else if ( ctx == KdbContext.Entry && name.equalsIgnoreCase(ElemEntry) ) {
if ( ctxEntry.uuid == null || ctxEntry.uuid.equals(PwDatabaseV4.UUID_ZERO) ) {
ctxEntry.uuid = UUID.randomUUID();
@@ -747,8 +881,21 @@ public class ImporterV4 extends Importer {
ctxEntry.autoType.put(ctxATName, ctxATSeq);
ctxATName = null;
ctxATSeq = null;
return KdbContext.EntryAutoType;
} else if ( ctx == KdbContext.EntryCustomData && name.equalsIgnoreCase(ElemCustomData)) {
return KdbContext.Entry;
} else if ( ctx == KdbContext.EntryCustomDataItem && name.equalsIgnoreCase(ElemStringDictExItem)) {
if (entryCustomDataKey != null && entryCustomDataValue != null) {
ctxEntry.customData.put(entryCustomDataKey, entryCustomDataValue);
} else {
assert(false);
}
entryCustomDataKey = null;
entryCustomDataValue = null;
return KdbContext.EntryCustomData;
} else if ( ctx == KdbContext.EntryHistory && name.equalsIgnoreCase(ElemHistory) ) {
entryInHistory = false;
return KdbContext.Entry;
@@ -770,16 +917,30 @@ public class ImporterV4 extends Importer {
private Date ReadTime(XmlPullParser xpp) throws IOException, XmlPullParserException {
String sDate = ReadString(xpp);
Date utcDate = null;
try {
utcDate = PwDatabaseV4XML.dateFormat.parse(sDate);
} catch (ParseException e) {
// Catch with null test below
}
if (utcDate == null) {
utcDate = new Date(0L);
if (version >= PwDbHeaderV4.FILE_VERSION_32_4) {
byte[] buf = Base64Coder.decode(sDate);
if (buf.length != 8) {
byte[] buf8 = new byte[8];
System.arraycopy(buf, 0, buf8, 0, buf.length);
buf = buf8;
}
long seconds = LEDataInputStream.readLong(buf, 0);
utcDate = DateUtil.convertKDBX4Time(seconds);
} else {
try {
utcDate = PwDatabaseV4XML.dateFormat.parse(sDate);
} catch (ParseException e) {
// Catch with null test below
}
if (utcDate == null) {
utcDate = new Date(0L);
}
}
return utcDate;
@@ -899,8 +1060,9 @@ public class ImporterV4 extends Importer {
String ref = xpp.getAttributeValue(null, AttrRef);
if (ref != null) {
xpp.next(); // Consume end tag
return binPool.get(ref);
int id = Integer.parseInt(ref);
return binPool.get(id);
}
boolean compressed = false;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2013 Brian Pellin.
* Copyright 2012-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -59,15 +59,16 @@ public class PwDbHeaderOutputV4 extends PwDbHeaderOutput {
public void output() throws IOException {
los.writeUInt(PwDbHeader.PWM_DBSIG_1);
los.writeUInt(PwDbHeaderV4.DBSIG_2);
los.writeUInt(PwDbHeaderV4.FILE_VERSION_32_3);
los.writeUInt(header.version);
writeHeaderField(PwDbHeaderV4Fields.CipherID, Types.UUIDtoBytes(db.dataCipher));
writeHeaderField(PwDbHeaderV4Fields.CompressionFlags, LEDataOutputStream.writeIntBuf(db.compressionAlgorithm.id));
writeHeaderField(PwDbHeaderV4Fields.MasterSeed, header.masterSeed);
writeHeaderField(PwDbHeaderV4Fields.TransformSeed, header.transformSeed);
writeHeaderField(PwDbHeaderV4Fields.TransformSeed, header.getTransformSeed());
writeHeaderField(PwDbHeaderV4Fields.TransformRounds, LEDataOutputStream.writeLongBuf(db.numKeyEncRounds));
writeHeaderField(PwDbHeaderV4Fields.EncryptionIV, header.encryptionIV);
writeHeaderField(PwDbHeaderV4Fields.ProtectedStreamKey, header.protectedStreamKey);
writeHeaderField(PwDbHeaderV4Fields.InnerRandomstreamKey, header.protectedStreamKey);
writeHeaderField(PwDbHeaderV4Fields.StreamStartBytes, header.streamStartBytes);
writeHeaderField(PwDbHeaderV4Fields.InnerRandomStreamID, LEDataOutputStream.writeIntBuf(header.innerRandomStream.id));
writeHeaderField(PwDbHeaderV4Fields.EndOfHeader, EndHeaderValue);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2013 Brian Pellin.
* Copyright 2010-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -56,8 +56,7 @@ public abstract class PwDbOutput {
}
random.nextBytes(header.encryptionIV);
random.nextBytes(header.masterSeed);
random.nextBytes(header.transformSeed);
return random;
}

View File

@@ -1,5 +1,5 @@
/*
` * Copyright 2009-2014 Brian Pellin.
` * Copyright 2009-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -62,7 +62,8 @@ public class PwDbV3Output extends PwDbOutput {
public byte[] getFinalKey(PwDbHeader header) throws PwDbOutputException {
try {
mPM.makeFinalKey(header.masterSeed, header.transformSeed, mPM.numKeyEncRounds);
PwDbHeaderV3 h3 = (PwDbHeaderV3) header;
mPM.makeFinalKey(h3.masterSeed, h3.transformSeed, mPM.numKeyEncRounds);
return mPM.finalKey;
} catch (IOException e) {
throw new PwDbOutputException("Key creation failed: " + e.getMessage());
@@ -112,6 +113,16 @@ public class PwDbV3Output extends PwDbOutput {
sortGroupsForOutput();
}
@Override
protected SecureRandom setIVs(PwDbHeader header) throws PwDbOutputException {
SecureRandom random = super.setIVs(header);
PwDbHeaderV3 h3 = (PwDbHeaderV3) header;
random.nextBytes(h3.transformSeed);
return random;
}
public PwDbHeaderV3 outputHeader(OutputStream os) throws PwDbOutputException {
// Build header
PwDbHeaderV3 header = new PwDbHeaderV3();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2016 Brian Pellin.
* Copyright 2013-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -44,6 +44,8 @@ import biz.source_code.base64Coder.Base64Coder;
import com.keepassdroid.crypto.CipherFactory;
import com.keepassdroid.crypto.PwStreamCipherFactory;
import com.keepassdroid.crypto.engine.CipherEngine;
import com.keepassdroid.crypto.keyDerivation.KdfEngine;
import com.keepassdroid.crypto.keyDerivation.KdfFactory;
import com.keepassdroid.database.BinaryPool;
import com.keepassdroid.database.CrsAlgorithm;
import com.keepassdroid.database.EntryHandler;
@@ -88,7 +90,9 @@ public class PwDbV4Output extends PwDbOutput {
@Override
public void output() throws PwDbOutputException {
header = (PwDbHeaderV4 ) outputHeader(mOS);
header = (PwDbHeaderV4) outputHeader(mOS);
CipherOutputStream cos = attachStreamEncryptor(header, mOS);
@@ -103,6 +107,7 @@ public class PwDbV4Output extends PwDbOutput {
} else {
compressed = hashed;
}
outputDatabase(compressed);
compressed.close();
@@ -248,7 +253,7 @@ public class PwDbV4Output extends PwDbOutput {
Cipher cipher;
CipherEngine engine;
try {
mPM.makeFinalKey(header.masterSeed, header.transformSeed, (int)mPM.numKeyEncRounds);
mPM.makeFinalKey(header.masterSeed, header.getTransformSeed(), (int)mPM.numKeyEncRounds);
engine = CipherFactory.getInstance(mPM.dataCipher);
cipher = engine.getCipher(Cipher.ENCRYPT_MODE, mPM.finalKey, header.encryptionIV);
@@ -267,9 +272,12 @@ public class PwDbV4Output extends PwDbOutput {
PwDbHeaderV4 h = (PwDbHeaderV4) header;
random.nextBytes(h.masterSeed);
random.nextBytes(h.transformSeed);
random.nextBytes(h.encryptionIV);
UUID kdfUUID = mPM.kdfParameters.kdfUUID;
KdfEngine kdf = KdfFactory.get(kdfUUID);
kdf.randomize(mPM.kdfParameters);
random.nextBytes(h.protectedStreamKey);
h.innerRandomStream = CrsAlgorithm.Salsa20;
randomStream = PwStreamCipherFactory.getInstance(h.innerRandomStream, h.protectedStreamKey);
@@ -366,7 +374,8 @@ public class PwDbV4Output extends PwDbOutput {
xml.startTag(null, ElemValue);
String strRef = null;
if (allowRef) {
strRef = binPool.poolFind(value);
int ref = binPool.poolFind(value);
strRef = Integer.toString(ref);
}
if (strRef != null) {
@@ -656,9 +665,9 @@ public class PwDbV4Output extends PwDbOutput {
private void writeBinPool() throws IllegalArgumentException, IllegalStateException, IOException {
xml.startTag(null, ElemBinaries);
for (Entry<String, ProtectedBinary> pair : binPool.entrySet()) {
for (Entry<Integer, ProtectedBinary> pair : binPool.entrySet()) {
xml.startTag(null, ElemBinary);
xml.attribute(null, AttrId, pair.getKey());
xml.attribute(null, AttrId, Integer.toString(pair.getKey()));
subWriteValue(pair.getValue());

View File

@@ -23,10 +23,12 @@ import com.keepassdroid.utils.Types;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class HmacBlockInputStream extends InputStream {
private LEDataInputStream baseStream;
@@ -43,7 +45,7 @@ public class HmacBlockInputStream extends InputStream {
this.baseStream = new LEDataInputStream(baseStream);
this.verify = verify;
this.key = key;
buffer = null;
buffer = new byte[0];
}
@Override
@@ -66,7 +68,12 @@ public class HmacBlockInputStream extends InputStream {
while (remaining > 0) {
if (bufferPos == buffer.length) {
if (!readSafeBlock()) {
return byteCount - remaining;
int read = byteCount - remaining;
if (read <= 0) {
return -1;
} else {
return byteCount - remaining;
}
}
}
@@ -108,12 +115,16 @@ public class HmacBlockInputStream extends InputStream {
if (verify) {
byte[] cmpHmac;
byte[] pbBlockKey = HmacBlockStream.GetHmacKey64(key, blockIndex);
byte[] blockKey = HmacBlockStream.GetHmacKey64(key, blockIndex);
Mac hmac;
try {
hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec signingKey = new SecretKeySpec(blockKey, "HmacSHA256");
hmac.init(signingKey);
} catch (NoSuchAlgorithmException e) {
throw new IOException("Invalid Hmac");
} catch (InvalidKeyException e) {
throw new IOException("Invalid Hmac");
}
hmac.update(pbBlockIndex);
@@ -124,7 +135,7 @@ public class HmacBlockInputStream extends InputStream {
}
cmpHmac = hmac.doFinal();
Arrays.fill(pbBlockKey, (byte)0);
Arrays.fill(blockKey, (byte)0);
if (!Arrays.equals(cmpHmac, storedHmac)) {
throw new IOException("Invalid Hmac");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2011 Brian Pellin.
* Copyright 2010-2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
@@ -19,8 +19,11 @@
*/
package com.keepassdroid.stream;
import com.keepassdroid.utils.Types;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
/** Little endian version of the DataInputStream
@@ -181,4 +184,10 @@ public class LEDataInputStream extends InputStream {
+ ((buf[offset + 3] & 0xFF) << 24);
}
public UUID readUUID() throws IOException {
byte[] buf = readBytes(16);
return Types.bytestoUUID(buf);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
* KeePassDroid 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 2 of the License, or
* (at your option) any later version.
*
* KeePassDroid 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 should have received a copy of the GNU General Public License
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.utils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import java.util.Date;
public class DateUtil {
private static final DateTime dotNetEpoch = new DateTime(1, 1, 1, 0, 0, 0, DateTimeZone.UTC);
public static Date convertKDBX4Time(long seconds) {
return dotNetEpoch.plus(seconds).toDate();
}
}

View File

@@ -1,3 +0,0 @@
# Recursively sources all Android.mk files in subdirs:
include $(call all-subdir-makefiles)

View File

@@ -1,3 +0,0 @@
APP_MODULES := aes sha final-key
APP_OPTIM := release
APP_ABI := all

View File

@@ -0,0 +1,4 @@
cmake_minimum_required(VERSION 3.4.1)
add_subdirectory(final_key)
add_subdirectory(argon2)

View File

@@ -1,13 +0,0 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := aes
LOCAL_SRC_FILES := \
aescrypt.c \
aeskey.c \
aes_modes.c \
aestab.c
include $(BUILD_STATIC_LIBRARY)

View File

@@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c89 -pthread")
include_directories(include/)
include_directories(src/)
add_library(
argon2 SHARED
src/argon2.c
src/core.c
src/encoding.c
src/ref.c
src/thread.c
src/blake2/blake2b.c
argon2_jni.c
)

View File

@@ -0,0 +1,193 @@
/*
* Copyright 2017 Brian Pellin.
*
* This file is part of KeePassDroid.
*
* KeePassDroid 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 2 of the License, or
* (at your option) any later version.
*
* KeePassDroid 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 should have received a copy of the GNU General Public License
* along with KeePassDroid. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
#include "argon2.h"
#include "core.h"
static JavaVM *cached_vm;
static jclass bad_arg, io, no_mem;
JNIEXPORT jint JNICALL JNI_OnLoad( JavaVM *vm, void *reserved ) {
JNIEnv *env;
jclass cls;
cached_vm = vm;
if((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6))
return JNI_ERR;
cls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
if( cls == NULL )
return JNI_ERR;
bad_arg = (*env)->NewGlobalRef(env, cls);
if( bad_arg == NULL )
return JNI_ERR;
cls = (*env)->FindClass(env, "java/io/IOException");
if( cls == NULL )
return JNI_ERR;
io = (*env)->NewGlobalRef(env, cls);
if( io == NULL )
return JNI_ERR;
cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
if( cls == NULL )
return JNI_ERR;
no_mem = (*env)->NewGlobalRef(env, cls);
if( no_mem == NULL )
return JNI_ERR;
/*
cls = (*env)->FindClass(env, "javax/crypto/BadPaddingException");
if( cls == NULL )
return JNI_ERR;
bad_padding = (*env)->NewGlobalRef(env, cls);
cls = (*env)->FindClass(env, "javax/crypto/ShortBufferException");
if( cls == NULL )
return JNI_ERR;
short_buf = (*env)->NewGlobalRef(env, cls);
cls = (*env)->FindClass(env, "javax/crypto/IllegalBlockSizeException");
if( cls == NULL )
return JNI_ERR;
block_size = (*env)->NewGlobalRef(env, cls);
aes_init();
*/
return JNI_VERSION_1_6;
}
// called on garbage collection
JNIEXPORT void JNICALL JNI_OnUnload( JavaVM *vm, void *reserved ) {
JNIEnv *env;
if((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6)) {
return;
}
(*env)->DeleteGlobalRef(env, bad_arg);
(*env)->DeleteGlobalRef(env, io);
(*env)->DeleteGlobalRef(env, no_mem);
/*
(*env)->DeleteGlobalRef(env, bad_padding);
(*env)->DeleteGlobalRef(env, short_buf);
(*env)->DeleteGlobalRef(env, block_size);
*/
return;
}
uint32_t getJNIArray(JNIEnv *env, jbyteArray array, uint8_t **output) {
if (array == NULL) {
*output = NULL;
return 0;
}
uint32_t len = (*env)->GetArrayLength(env, array);
uint8_t *buf = (uint8_t *)malloc(len);
(*env)->GetByteArrayRegion(env, array, 0, len, (jbyte*) buf);
*output = buf;
return len;
}
void throwExceptionF(JNIEnv *env, jclass exception, const char *format, ...) {
char message[512];
va_list args;
va_start(args, format);
snprintf(message, 512, format, args);
va_end(args);
(*env)->ThrowNew(env, exception, message);
}
#define ARGON2_HASHLEN 32
#define NB_BLOCKSIZE 1024
JNIEXPORT jbyteArray
JNICALL Java_com_keepassdroid_crypto_keyDerivation_Argon2Native_nTransformMasterKey(JNIEnv *env,
jobject this, jbyteArray password, jbyteArray salt, jint parallelism, jlong memory,
jlong iterations, jbyteArray secretKey, jbyteArray associatedData, jlong version) {
argon2_context context;
uint8_t *out;
out = (uint8_t *) malloc(ARGON2_HASHLEN);
if (out == NULL) {
throwExceptionF(env, no_mem, "Not enough memory for output hash array");
return NULL;
}
uint8_t *passwordBuf;
uint32_t passwordLen = getJNIArray(env, password, &passwordBuf);
uint8_t *saltBuf;
uint32_t saltLen = getJNIArray(env, salt, &saltBuf);
uint8_t *secretBuf;
uint32_t secretLen = getJNIArray(env, secretKey, &secretBuf);
uint8_t *adBuf;
uint32_t adLen = getJNIArray(env, associatedData, &adBuf);
context.out = (uint8_t *) out;
context.outlen = ARGON2_HASHLEN;
context.pwd = passwordBuf;
context.pwdlen = passwordLen;
context.salt = saltBuf;
context.saltlen = saltLen;
context.secret = secretBuf;
context.secretlen = secretLen;
context.ad = adBuf;
context.adlen = adLen;
context.t_cost = (uint32_t) iterations;
context.m_cost = ((uint32_t) memory) / NB_BLOCKSIZE;
context.lanes = (uint32_t) parallelism;
context.threads = (uint32_t) parallelism;
context.allocate_cbk = NULL;
context.free_cbk = NULL;
context.flags = ARGON2_DEFAULT_FLAGS;
context.version = (uint32_t) version;
int argonResult = argon2_ctx(&context, Argon2_d);
jbyteArray result;
if (argonResult != ARGON2_OK) {
throwExceptionF(env, io, "Hash failed with code=%d", argonResult);
result = NULL;
} else {
result = (*env)->NewByteArray(env, ARGON2_HASHLEN);
(*env)->SetByteArrayRegion(env, result, 0, ARGON2_HASHLEN, (jbyte *) out);
}
clear_internal_memory(out, ARGON2_HASHLEN);
free(out);
if (passwordBuf != NULL) { free(passwordBuf); }
if (saltBuf != NULL) { free(saltBuf); }
if (secretBuf != NULL) { free(secretBuf); }
if (adBuf != NULL) { free(adBuf); }
return result;
}

View File

@@ -0,0 +1,434 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#ifndef ARGON2_H
#define ARGON2_H
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
#if defined(__cplusplus)
extern "C" {
#endif
/* Symbols visibility control */
#ifdef A2_VISCTL
#define ARGON2_PUBLIC __attribute__((visibility("default")))
#elif _MSC_VER
#define ARGON2_PUBLIC __declspec(dllexport)
#else
#define ARGON2_PUBLIC
#endif
/*
* Argon2 input parameter restrictions
*/
/* Minimum and maximum number of lanes (degree of parallelism) */
#define ARGON2_MIN_LANES UINT32_C(1)
#define ARGON2_MAX_LANES UINT32_C(0xFFFFFF)
/* Minimum and maximum number of threads */
#define ARGON2_MIN_THREADS UINT32_C(1)
#define ARGON2_MAX_THREADS UINT32_C(0xFFFFFF)
/* Number of synchronization points between lanes per pass */
#define ARGON2_SYNC_POINTS UINT32_C(4)
/* Minimum and maximum digest size in bytes */
#define ARGON2_MIN_OUTLEN UINT32_C(4)
#define ARGON2_MAX_OUTLEN UINT32_C(0xFFFFFFFF)
/* Minimum and maximum number of memory blocks (each of BLOCK_SIZE bytes) */
#define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS) /* 2 blocks per slice */
#define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b))
/* Max memory size is addressing-space/2, topping at 2^32 blocks (4 TB) */
#define ARGON2_MAX_MEMORY_BITS \
ARGON2_MIN(UINT32_C(32), (sizeof(void *) * CHAR_BIT - 10 - 1))
#define ARGON2_MAX_MEMORY \
ARGON2_MIN(UINT32_C(0xFFFFFFFF), UINT64_C(1) << ARGON2_MAX_MEMORY_BITS)
/* Minimum and maximum number of passes */
#define ARGON2_MIN_TIME UINT32_C(1)
#define ARGON2_MAX_TIME UINT32_C(0xFFFFFFFF)
/* Minimum and maximum password length in bytes */
#define ARGON2_MIN_PWD_LENGTH UINT32_C(0)
#define ARGON2_MAX_PWD_LENGTH UINT32_C(0xFFFFFFFF)
/* Minimum and maximum associated data length in bytes */
#define ARGON2_MIN_AD_LENGTH UINT32_C(0)
#define ARGON2_MAX_AD_LENGTH UINT32_C(0xFFFFFFFF)
/* Minimum and maximum salt length in bytes */
#define ARGON2_MIN_SALT_LENGTH UINT32_C(8)
#define ARGON2_MAX_SALT_LENGTH UINT32_C(0xFFFFFFFF)
/* Minimum and maximum key length in bytes */
#define ARGON2_MIN_SECRET UINT32_C(0)
#define ARGON2_MAX_SECRET UINT32_C(0xFFFFFFFF)
/* Flags to determine which fields are securely wiped (default = no wipe). */
#define ARGON2_DEFAULT_FLAGS UINT32_C(0)
#define ARGON2_FLAG_CLEAR_PASSWORD (UINT32_C(1) << 0)
#define ARGON2_FLAG_CLEAR_SECRET (UINT32_C(1) << 1)
/* Global flag to determine if we are wiping internal memory buffers. This flag
* is defined in core.c and deafults to 1 (wipe internal memory). */
extern int FLAG_clear_internal_memory;
/* Error codes */
typedef enum Argon2_ErrorCodes {
ARGON2_OK = 0,
ARGON2_OUTPUT_PTR_NULL = -1,
ARGON2_OUTPUT_TOO_SHORT = -2,
ARGON2_OUTPUT_TOO_LONG = -3,
ARGON2_PWD_TOO_SHORT = -4,
ARGON2_PWD_TOO_LONG = -5,
ARGON2_SALT_TOO_SHORT = -6,
ARGON2_SALT_TOO_LONG = -7,
ARGON2_AD_TOO_SHORT = -8,
ARGON2_AD_TOO_LONG = -9,
ARGON2_SECRET_TOO_SHORT = -10,
ARGON2_SECRET_TOO_LONG = -11,
ARGON2_TIME_TOO_SMALL = -12,
ARGON2_TIME_TOO_LARGE = -13,
ARGON2_MEMORY_TOO_LITTLE = -14,
ARGON2_MEMORY_TOO_MUCH = -15,
ARGON2_LANES_TOO_FEW = -16,
ARGON2_LANES_TOO_MANY = -17,
ARGON2_PWD_PTR_MISMATCH = -18, /* NULL ptr with non-zero length */
ARGON2_SALT_PTR_MISMATCH = -19, /* NULL ptr with non-zero length */
ARGON2_SECRET_PTR_MISMATCH = -20, /* NULL ptr with non-zero length */
ARGON2_AD_PTR_MISMATCH = -21, /* NULL ptr with non-zero length */
ARGON2_MEMORY_ALLOCATION_ERROR = -22,
ARGON2_FREE_MEMORY_CBK_NULL = -23,
ARGON2_ALLOCATE_MEMORY_CBK_NULL = -24,
ARGON2_INCORRECT_PARAMETER = -25,
ARGON2_INCORRECT_TYPE = -26,
ARGON2_OUT_PTR_MISMATCH = -27,
ARGON2_THREADS_TOO_FEW = -28,
ARGON2_THREADS_TOO_MANY = -29,
ARGON2_MISSING_ARGS = -30,
ARGON2_ENCODING_FAIL = -31,
ARGON2_DECODING_FAIL = -32,
ARGON2_THREAD_FAIL = -33,
ARGON2_DECODING_LENGTH_FAIL = -34,
ARGON2_VERIFY_MISMATCH = -35
} argon2_error_codes;
/* Memory allocator types --- for external allocation */
typedef int (*allocate_fptr)(uint8_t **memory, size_t bytes_to_allocate);
typedef void (*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate);
/* Argon2 external data structures */
/*
*****
* Context: structure to hold Argon2 inputs:
* output array and its length,
* password and its length,
* salt and its length,
* secret and its length,
* associated data and its length,
* number of passes, amount of used memory (in KBytes, can be rounded up a bit)
* number of parallel threads that will be run.
* All the parameters above affect the output hash value.
* Additionally, two function pointers can be provided to allocate and
* deallocate the memory (if NULL, memory will be allocated internally).
* Also, three flags indicate whether to erase password, secret as soon as they
* are pre-hashed (and thus not needed anymore), and the entire memory
*****
* Simplest situation: you have output array out[8], password is stored in
* pwd[32], salt is stored in salt[16], you do not have keys nor associated
* data. You need to spend 1 GB of RAM and you run 5 passes of Argon2d with
* 4 parallel lanes.
* You want to erase the password, but you're OK with last pass not being
* erased. You want to use the default memory allocator.
* Then you initialize:
Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false)
*/
typedef struct Argon2_Context {
uint8_t *out; /* output array */
uint32_t outlen; /* digest length */
uint8_t *pwd; /* password array */
uint32_t pwdlen; /* password length */
uint8_t *salt; /* salt array */
uint32_t saltlen; /* salt length */
uint8_t *secret; /* key array */
uint32_t secretlen; /* key length */
uint8_t *ad; /* associated data array */
uint32_t adlen; /* associated data length */
uint32_t t_cost; /* number of passes */
uint32_t m_cost; /* amount of memory requested (KB) */
uint32_t lanes; /* number of lanes */
uint32_t threads; /* maximum number of threads */
uint32_t version; /* version number */
allocate_fptr allocate_cbk; /* pointer to memory allocator */
deallocate_fptr free_cbk; /* pointer to memory deallocator */
uint32_t flags; /* array of bool options */
} argon2_context;
/* Argon2 primitive type */
typedef enum Argon2_type {
Argon2_d = 0,
Argon2_i = 1,
Argon2_id = 2
} argon2_type;
/* Version of the algorithm */
typedef enum Argon2_version {
ARGON2_VERSION_10 = 0x10,
ARGON2_VERSION_13 = 0x13,
ARGON2_VERSION_NUMBER = ARGON2_VERSION_13
} argon2_version;
/*
* Function that gives the string representation of an argon2_type.
* @param type The argon2_type that we want the string for
* @param uppercase Whether the string should have the first letter uppercase
* @return NULL if invalid type, otherwise the string representation.
*/
ARGON2_PUBLIC const char *argon2_type2string(argon2_type type, int uppercase);
/*
* Function that performs memory-hard hashing with certain degree of parallelism
* @param context Pointer to the Argon2 internal structure
* @return Error code if smth is wrong, ARGON2_OK otherwise
*/
ARGON2_PUBLIC int argon2_ctx(argon2_context *context, argon2_type type);
/**
* Hashes a password with Argon2i, producing an encoded hash
* @param t_cost Number of iterations
* @param m_cost Sets memory usage to m_cost kibibytes
* @param parallelism Number of threads and compute lanes
* @param pwd Pointer to password
* @param pwdlen Password size in bytes
* @param salt Pointer to salt
* @param saltlen Salt size in bytes
* @param hashlen Desired length of the hash in bytes
* @param encoded Buffer where to write the encoded hash
* @param encodedlen Size of the buffer (thus max size of the encoded hash)
* @pre Different parallelism levels will give different results
* @pre Returns ARGON2_OK if successful
*/
ARGON2_PUBLIC int argon2i_hash_encoded(const uint32_t t_cost,
const uint32_t m_cost,
const uint32_t parallelism,
const void *pwd, const size_t pwdlen,
const void *salt, const size_t saltlen,
const size_t hashlen, char *encoded,
const size_t encodedlen);
/**
* Hashes a password with Argon2i, producing a raw hash by allocating memory at
* @hash
* @param t_cost Number of iterations
* @param m_cost Sets memory usage to m_cost kibibytes
* @param parallelism Number of threads and compute lanes
* @param pwd Pointer to password
* @param pwdlen Password size in bytes
* @param salt Pointer to salt
* @param saltlen Salt size in bytes
* @param hash Buffer where to write the raw hash - updated by the function
* @param hashlen Desired length of the hash in bytes
* @pre Different parallelism levels will give different results
* @pre Returns ARGON2_OK if successful
*/
ARGON2_PUBLIC int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
const uint32_t parallelism, const void *pwd,
const size_t pwdlen, const void *salt,
const size_t saltlen, void *hash,
const size_t hashlen);
ARGON2_PUBLIC int argon2d_hash_encoded(const uint32_t t_cost,
const uint32_t m_cost,
const uint32_t parallelism,
const void *pwd, const size_t pwdlen,
const void *salt, const size_t saltlen,
const size_t hashlen, char *encoded,
const size_t encodedlen);
ARGON2_PUBLIC int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
const uint32_t parallelism, const void *pwd,
const size_t pwdlen, const void *salt,
const size_t saltlen, void *hash,
const size_t hashlen);
ARGON2_PUBLIC int argon2id_hash_encoded(const uint32_t t_cost,
const uint32_t m_cost,
const uint32_t parallelism,
const void *pwd, const size_t pwdlen,
const void *salt, const size_t saltlen,
const size_t hashlen, char *encoded,
const size_t encodedlen);
ARGON2_PUBLIC int argon2id_hash_raw(const uint32_t t_cost,
const uint32_t m_cost,
const uint32_t parallelism, const void *pwd,
const size_t pwdlen, const void *salt,
const size_t saltlen, void *hash,
const size_t hashlen);
/* generic function underlying the above ones */
ARGON2_PUBLIC int argon2_hash(const uint32_t t_cost, const uint32_t m_cost,
const uint32_t parallelism, const void *pwd,
const size_t pwdlen, const void *salt,
const size_t saltlen, void *hash,
const size_t hashlen, char *encoded,
const size_t encodedlen, argon2_type type,
const uint32_t version);
/**
* Verifies a password against an encoded string
* Encoded string is restricted as in validate_inputs()
* @param encoded String encoding parameters, salt, hash
* @param pwd Pointer to password
* @pre Returns ARGON2_OK if successful
*/
ARGON2_PUBLIC int argon2i_verify(const char *encoded, const void *pwd,
const size_t pwdlen);
ARGON2_PUBLIC int argon2d_verify(const char *encoded, const void *pwd,
const size_t pwdlen);
ARGON2_PUBLIC int argon2id_verify(const char *encoded, const void *pwd,
const size_t pwdlen);
/* generic function underlying the above ones */
ARGON2_PUBLIC int argon2_verify(const char *encoded, const void *pwd,
const size_t pwdlen, argon2_type type);
/**
* Argon2d: Version of Argon2 that picks memory blocks depending
* on the password and salt. Only for side-channel-free
* environment!!
*****
* @param context Pointer to current Argon2 context
* @return Zero if successful, a non zero error code otherwise
*/
ARGON2_PUBLIC int argon2d_ctx(argon2_context *context);
/**
* Argon2i: Version of Argon2 that picks memory blocks
* independent on the password and salt. Good for side-channels,
* but worse w.r.t. tradeoff attacks if only one pass is used.
*****
* @param context Pointer to current Argon2 context
* @return Zero if successful, a non zero error code otherwise
*/
ARGON2_PUBLIC int argon2i_ctx(argon2_context *context);
/**
* Argon2id: Version of Argon2 where the first half-pass over memory is
* password-independent, the rest are password-dependent (on the password and
* salt). OK against side channels (they reduce to 1/2-pass Argon2i), and
* better with w.r.t. tradeoff attacks (similar to Argon2d).
*****
* @param context Pointer to current Argon2 context
* @return Zero if successful, a non zero error code otherwise
*/
ARGON2_PUBLIC int argon2id_ctx(argon2_context *context);
/**
* Verify if a given password is correct for Argon2d hashing
* @param context Pointer to current Argon2 context
* @param hash The password hash to verify. The length of the hash is
* specified by the context outlen member
* @return Zero if successful, a non zero error code otherwise
*/
ARGON2_PUBLIC int argon2d_verify_ctx(argon2_context *context, const char *hash);
/**
* Verify if a given password is correct for Argon2i hashing
* @param context Pointer to current Argon2 context
* @param hash The password hash to verify. The length of the hash is
* specified by the context outlen member
* @return Zero if successful, a non zero error code otherwise
*/
ARGON2_PUBLIC int argon2i_verify_ctx(argon2_context *context, const char *hash);
/**
* Verify if a given password is correct for Argon2id hashing
* @param context Pointer to current Argon2 context
* @param hash The password hash to verify. The length of the hash is
* specified by the context outlen member
* @return Zero if successful, a non zero error code otherwise
*/
ARGON2_PUBLIC int argon2id_verify_ctx(argon2_context *context,
const char *hash);
/* generic function underlying the above ones */
ARGON2_PUBLIC int argon2_verify_ctx(argon2_context *context, const char *hash,
argon2_type type);
/**
* Get the associated error message for given error code
* @return The error message associated with the given error code
*/
ARGON2_PUBLIC const char *argon2_error_message(int error_code);
/**
* Returns the encoded hash length for the given input parameters
* @param t_cost Number of iterations
* @param m_cost Memory usage in kibibytes
* @param parallelism Number of threads; used to compute lanes
* @param saltlen Salt size in bytes
* @param hashlen Hash size in bytes
* @return The encoded hash length in bytes
*/
ARGON2_PUBLIC size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost,
uint32_t parallelism, uint32_t saltlen,
uint32_t hashlen, argon2_type type);
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_SHA256")
include_directories(aes/)
include_directories(sha/)
add_library(
final-key SHARED
kpd_jni.c
aes/aescrypt.c
aes/aeskey.c
aes/aes_modes.c
aes/aestab.c
sha/hmac.c
sha/sha1.c
sha/sha2.c
)
find_library(log-lib log)
target_link_libraries(final-key ${log-lib})

View File

@@ -0,0 +1,436 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "argon2.h"
#include "encoding.h"
#include "core.h"
const char *argon2_type2string(argon2_type type, int uppercase) {
switch (type) {
case Argon2_d:
return uppercase ? "Argon2d" : "argon2d";
case Argon2_i:
return uppercase ? "Argon2i" : "argon2i";
case Argon2_id:
return uppercase ? "Argon2id" : "argon2id";
}
return NULL;
}
int argon2_ctx(argon2_context *context, argon2_type type) {
/* 1. Validate all inputs */
int result = validate_inputs(context);
uint32_t memory_blocks, segment_length;
argon2_instance_t instance;
if (ARGON2_OK != result) {
return result;
}
if (Argon2_d != type && Argon2_i != type && Argon2_id != type) {
return ARGON2_INCORRECT_TYPE;
}
/* 2. Align memory size */
/* Minimum memory_blocks = 8L blocks, where L is the number of lanes */
memory_blocks = context->m_cost;
if (memory_blocks < 2 * ARGON2_SYNC_POINTS * context->lanes) {
memory_blocks = 2 * ARGON2_SYNC_POINTS * context->lanes;
}
segment_length = memory_blocks / (context->lanes * ARGON2_SYNC_POINTS);
/* Ensure that all segments have equal length */
memory_blocks = segment_length * (context->lanes * ARGON2_SYNC_POINTS);
instance.version = context->version;
instance.memory = NULL;
instance.passes = context->t_cost;
instance.memory_blocks = memory_blocks;
instance.segment_length = segment_length;
instance.lane_length = segment_length * ARGON2_SYNC_POINTS;
instance.lanes = context->lanes;
instance.threads = context->threads;
instance.type = type;
/* 3. Initialization: Hashing inputs, allocating memory, filling first
* blocks
*/
result = initialize(&instance, context);
if (ARGON2_OK != result) {
return result;
}
/* 4. Filling memory */
result = fill_memory_blocks(&instance);
if (ARGON2_OK != result) {
return result;
}
/* 5. Finalization */
finalize(context, &instance);
return ARGON2_OK;
}
int argon2_hash(const uint32_t t_cost, const uint32_t m_cost,
const uint32_t parallelism, const void *pwd,
const size_t pwdlen, const void *salt, const size_t saltlen,
void *hash, const size_t hashlen, char *encoded,
const size_t encodedlen, argon2_type type,
const uint32_t version){
argon2_context context;
int result;
uint8_t *out;
if (hashlen > ARGON2_MAX_OUTLEN) {
return ARGON2_OUTPUT_TOO_LONG;
}
if (hashlen < ARGON2_MIN_OUTLEN) {
return ARGON2_OUTPUT_TOO_SHORT;
}
out = malloc(hashlen);
if (!out) {
return ARGON2_MEMORY_ALLOCATION_ERROR;
}
context.out = (uint8_t *)out;
context.outlen = (uint32_t)hashlen;
context.pwd = CONST_CAST(uint8_t *)pwd;
context.pwdlen = (uint32_t)pwdlen;
context.salt = CONST_CAST(uint8_t *)salt;
context.saltlen = (uint32_t)saltlen;
context.secret = NULL;
context.secretlen = 0;
context.ad = NULL;
context.adlen = 0;
context.t_cost = t_cost;
context.m_cost = m_cost;
context.lanes = parallelism;
context.threads = parallelism;
context.allocate_cbk = NULL;
context.free_cbk = NULL;
context.flags = ARGON2_DEFAULT_FLAGS;
context.version = version;
result = argon2_ctx(&context, type);
if (result != ARGON2_OK) {
clear_internal_memory(out, hashlen);
free(out);
return result;
}
/* if raw hash requested, write it */
if (hash) {
memcpy(hash, out, hashlen);
}
/* if encoding requested, write it */
if (encoded && encodedlen) {
if (encode_string(encoded, encodedlen, &context, type) != ARGON2_OK) {
clear_internal_memory(out, hashlen); /* wipe buffers if error */
clear_internal_memory(encoded, encodedlen);
free(out);
return ARGON2_ENCODING_FAIL;
}
}
clear_internal_memory(out, hashlen);
free(out);
return ARGON2_OK;
}
int argon2i_hash_encoded(const uint32_t t_cost, const uint32_t m_cost,
const uint32_t parallelism, const void *pwd,
const size_t pwdlen, const void *salt,
const size_t saltlen, const size_t hashlen,
char *encoded, const size_t encodedlen) {
return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
NULL, hashlen, encoded, encodedlen, Argon2_i,
ARGON2_VERSION_NUMBER);
}
int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
const uint32_t parallelism, const void *pwd,
const size_t pwdlen, const void *salt,
const size_t saltlen, void *hash, const size_t hashlen) {
return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
hash, hashlen, NULL, 0, Argon2_i, ARGON2_VERSION_NUMBER);
}
int argon2d_hash_encoded(const uint32_t t_cost, const uint32_t m_cost,
const uint32_t parallelism, const void *pwd,
const size_t pwdlen, const void *salt,
const size_t saltlen, const size_t hashlen,
char *encoded, const size_t encodedlen) {
return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
NULL, hashlen, encoded, encodedlen, Argon2_d,
ARGON2_VERSION_NUMBER);
}
int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
const uint32_t parallelism, const void *pwd,
const size_t pwdlen, const void *salt,
const size_t saltlen, void *hash, const size_t hashlen) {
return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
hash, hashlen, NULL, 0, Argon2_d, ARGON2_VERSION_NUMBER);
}
int argon2id_hash_encoded(const uint32_t t_cost, const uint32_t m_cost,
const uint32_t parallelism, const void *pwd,
const size_t pwdlen, const void *salt,
const size_t saltlen, const size_t hashlen,
char *encoded, const size_t encodedlen) {
return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
NULL, hashlen, encoded, encodedlen, Argon2_id,
ARGON2_VERSION_NUMBER);
}
int argon2id_hash_raw(const uint32_t t_cost, const uint32_t m_cost,
const uint32_t parallelism, const void *pwd,
const size_t pwdlen, const void *salt,
const size_t saltlen, void *hash, const size_t hashlen) {
return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen,
hash, hashlen, NULL, 0, Argon2_id,
ARGON2_VERSION_NUMBER);
}
static int argon2_compare(const uint8_t *b1, const uint8_t *b2, size_t len) {
size_t i;
uint8_t d = 0U;
for (i = 0U; i < len; i++) {
d |= b1[i] ^ b2[i];
}
return (int)((1 & ((d - 1) >> 8)) - 1);
}
int argon2_verify(const char *encoded, const void *pwd, const size_t pwdlen,
argon2_type type) {
argon2_context ctx;
uint8_t *desired_result = NULL;
int ret = ARGON2_OK;
size_t encoded_len;
uint32_t max_field_len;
if (encoded == NULL) {
return ARGON2_DECODING_FAIL;
}
encoded_len = strlen(encoded);
if (encoded_len > UINT32_MAX) {
return ARGON2_DECODING_FAIL;
}
/* No field can be longer than the encoded length */
max_field_len = (uint32_t)encoded_len;
ctx.saltlen = max_field_len;
ctx.outlen = max_field_len;
ctx.salt = malloc(ctx.saltlen);
ctx.out = malloc(ctx.outlen);
if (!ctx.salt || !ctx.out) {
ret = ARGON2_MEMORY_ALLOCATION_ERROR;
goto fail;
}
ctx.pwd = (uint8_t *)pwd;
ctx.pwdlen = pwdlen;
ret = decode_string(&ctx, encoded, type);
if (ret != ARGON2_OK) {
goto fail;
}
/* Set aside the desired result, and get a new buffer. */
desired_result = ctx.out;
ctx.out = malloc(ctx.outlen);
if (!ctx.out) {
ret = ARGON2_MEMORY_ALLOCATION_ERROR;
goto fail;
}
ret = argon2_verify_ctx(&ctx, (char *)desired_result, type);
if (ret != ARGON2_OK) {
goto fail;
}
fail:
free(ctx.salt);
free(ctx.out);
free(desired_result);
return ret;
}
int argon2i_verify(const char *encoded, const void *pwd, const size_t pwdlen) {
return argon2_verify(encoded, pwd, pwdlen, Argon2_i);
}
int argon2d_verify(const char *encoded, const void *pwd, const size_t pwdlen) {
return argon2_verify(encoded, pwd, pwdlen, Argon2_d);
}
int argon2id_verify(const char *encoded, const void *pwd, const size_t pwdlen) {
return argon2_verify(encoded, pwd, pwdlen, Argon2_id);
}
int argon2d_ctx(argon2_context *context) {
return argon2_ctx(context, Argon2_d);
}
int argon2i_ctx(argon2_context *context) {
return argon2_ctx(context, Argon2_i);
}
int argon2id_ctx(argon2_context *context) {
return argon2_ctx(context, Argon2_id);
}
int argon2_verify_ctx(argon2_context *context, const char *hash,
argon2_type type) {
int ret = argon2_ctx(context, type);
if (ret != ARGON2_OK) {
return ret;
}
if (argon2_compare((uint8_t *)hash, context->out, context->outlen)) {
return ARGON2_VERIFY_MISMATCH;
}
return ARGON2_OK;
}
int argon2d_verify_ctx(argon2_context *context, const char *hash) {
return argon2_verify_ctx(context, hash, Argon2_d);
}
int argon2i_verify_ctx(argon2_context *context, const char *hash) {
return argon2_verify_ctx(context, hash, Argon2_i);
}
int argon2id_verify_ctx(argon2_context *context, const char *hash) {
return argon2_verify_ctx(context, hash, Argon2_id);
}
const char *argon2_error_message(int error_code) {
switch (error_code) {
case ARGON2_OK:
return "OK";
case ARGON2_OUTPUT_PTR_NULL:
return "Output pointer is NULL";
case ARGON2_OUTPUT_TOO_SHORT:
return "Output is too short";
case ARGON2_OUTPUT_TOO_LONG:
return "Output is too long";
case ARGON2_PWD_TOO_SHORT:
return "Password is too short";
case ARGON2_PWD_TOO_LONG:
return "Password is too long";
case ARGON2_SALT_TOO_SHORT:
return "Salt is too short";
case ARGON2_SALT_TOO_LONG:
return "Salt is too long";
case ARGON2_AD_TOO_SHORT:
return "Associated data is too short";
case ARGON2_AD_TOO_LONG:
return "Associated data is too long";
case ARGON2_SECRET_TOO_SHORT:
return "Secret is too short";
case ARGON2_SECRET_TOO_LONG:
return "Secret is too long";
case ARGON2_TIME_TOO_SMALL:
return "Time cost is too small";
case ARGON2_TIME_TOO_LARGE:
return "Time cost is too large";
case ARGON2_MEMORY_TOO_LITTLE:
return "Memory cost is too small";
case ARGON2_MEMORY_TOO_MUCH:
return "Memory cost is too large";
case ARGON2_LANES_TOO_FEW:
return "Too few lanes";
case ARGON2_LANES_TOO_MANY:
return "Too many lanes";
case ARGON2_PWD_PTR_MISMATCH:
return "Password pointer is NULL, but password length is not 0";
case ARGON2_SALT_PTR_MISMATCH:
return "Salt pointer is NULL, but salt length is not 0";
case ARGON2_SECRET_PTR_MISMATCH:
return "Secret pointer is NULL, but secret length is not 0";
case ARGON2_AD_PTR_MISMATCH:
return "Associated data pointer is NULL, but ad length is not 0";
case ARGON2_MEMORY_ALLOCATION_ERROR:
return "Memory allocation error";
case ARGON2_FREE_MEMORY_CBK_NULL:
return "The free memory callback is NULL";
case ARGON2_ALLOCATE_MEMORY_CBK_NULL:
return "The allocate memory callback is NULL";
case ARGON2_INCORRECT_PARAMETER:
return "Argon2_Context context is NULL";
case ARGON2_INCORRECT_TYPE:
return "There is no such version of Argon2";
case ARGON2_OUT_PTR_MISMATCH:
return "Output pointer mismatch";
case ARGON2_THREADS_TOO_FEW:
return "Not enough threads";
case ARGON2_THREADS_TOO_MANY:
return "Too many threads";
case ARGON2_MISSING_ARGS:
return "Missing arguments";
case ARGON2_ENCODING_FAIL:
return "Encoding failed";
case ARGON2_DECODING_FAIL:
return "Decoding failed";
case ARGON2_THREAD_FAIL:
return "Threading failure";
case ARGON2_DECODING_LENGTH_FAIL:
return "Some of encoded parameters are too long or too short";
case ARGON2_VERIFY_MISMATCH:
return "The password does not match the supplied hash";
default:
return "Unknown error code";
}
}
size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, uint32_t parallelism,
uint32_t saltlen, uint32_t hashlen, argon2_type type) {
return strlen("$$v=$m=,t=,p=$$") + strlen(argon2_type2string(type, 0)) +
numlen(t_cost) + numlen(m_cost) + numlen(parallelism) +
b64len(saltlen) + b64len(hashlen) + numlen(ARGON2_VERSION_NUMBER) + 1;
}

View File

@@ -0,0 +1,156 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#ifndef PORTABLE_BLAKE2_IMPL_H
#define PORTABLE_BLAKE2_IMPL_H
#include <stdint.h>
#include <string.h>
#if defined(_MSC_VER)
#define BLAKE2_INLINE __inline
#elif defined(__GNUC__) || defined(__clang__)
#define BLAKE2_INLINE __inline__
#else
#define BLAKE2_INLINE
#endif
/* Argon2 Team - Begin Code */
/*
Not an exhaustive list, but should cover the majority of modern platforms
Additionally, the code will always be correct---this is only a performance
tweak.
*/
#if (defined(__BYTE_ORDER__) && \
(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \
defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \
defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \
defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \
defined(_M_ARM)
#define NATIVE_LITTLE_ENDIAN
#endif
/* Argon2 Team - End Code */
static BLAKE2_INLINE uint32_t load32(const void *src) {
#if defined(NATIVE_LITTLE_ENDIAN)
uint32_t w;
memcpy(&w, src, sizeof w);
return w;
#else
const uint8_t *p = (const uint8_t *)src;
uint32_t w = *p++;
w |= (uint32_t)(*p++) << 8;
w |= (uint32_t)(*p++) << 16;
w |= (uint32_t)(*p++) << 24;
return w;
#endif
}
static BLAKE2_INLINE uint64_t load64(const void *src) {
#if defined(NATIVE_LITTLE_ENDIAN)
uint64_t w;
memcpy(&w, src, sizeof w);
return w;
#else
const uint8_t *p = (const uint8_t *)src;
uint64_t w = *p++;
w |= (uint64_t)(*p++) << 8;
w |= (uint64_t)(*p++) << 16;
w |= (uint64_t)(*p++) << 24;
w |= (uint64_t)(*p++) << 32;
w |= (uint64_t)(*p++) << 40;
w |= (uint64_t)(*p++) << 48;
w |= (uint64_t)(*p++) << 56;
return w;
#endif
}
static BLAKE2_INLINE void store32(void *dst, uint32_t w) {
#if defined(NATIVE_LITTLE_ENDIAN)
memcpy(dst, &w, sizeof w);
#else
uint8_t *p = (uint8_t *)dst;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
#endif
}
static BLAKE2_INLINE void store64(void *dst, uint64_t w) {
#if defined(NATIVE_LITTLE_ENDIAN)
memcpy(dst, &w, sizeof w);
#else
uint8_t *p = (uint8_t *)dst;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
#endif
}
static BLAKE2_INLINE uint64_t load48(const void *src) {
const uint8_t *p = (const uint8_t *)src;
uint64_t w = *p++;
w |= (uint64_t)(*p++) << 8;
w |= (uint64_t)(*p++) << 16;
w |= (uint64_t)(*p++) << 24;
w |= (uint64_t)(*p++) << 32;
w |= (uint64_t)(*p++) << 40;
return w;
}
static BLAKE2_INLINE void store48(void *dst, uint64_t w) {
uint8_t *p = (uint8_t *)dst;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
w >>= 8;
*p++ = (uint8_t)w;
}
static BLAKE2_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) {
return (w >> c) | (w << (32 - c));
}
static BLAKE2_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) {
return (w >> c) | (w << (64 - c));
}
void clear_internal_memory(void *v, size_t n);
#endif

View File

@@ -0,0 +1,91 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#ifndef PORTABLE_BLAKE2_H
#define PORTABLE_BLAKE2_H
#include <stddef.h>
#include <stdint.h>
#include <limits.h>
#if defined(__cplusplus)
extern "C" {
#endif
enum blake2b_constant {
BLAKE2B_BLOCKBYTES = 128,
BLAKE2B_OUTBYTES = 64,
BLAKE2B_KEYBYTES = 64,
BLAKE2B_SALTBYTES = 16,
BLAKE2B_PERSONALBYTES = 16
};
#pragma pack(push, 1)
typedef struct __blake2b_param {
uint8_t digest_length; /* 1 */
uint8_t key_length; /* 2 */
uint8_t fanout; /* 3 */
uint8_t depth; /* 4 */
uint32_t leaf_length; /* 8 */
uint64_t node_offset; /* 16 */
uint8_t node_depth; /* 17 */
uint8_t inner_length; /* 18 */
uint8_t reserved[14]; /* 32 */
uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */
uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */
} blake2b_param;
#pragma pack(pop)
typedef struct __blake2b_state {
uint64_t h[8];
uint64_t t[2];
uint64_t f[2];
uint8_t buf[BLAKE2B_BLOCKBYTES];
unsigned buflen;
unsigned outlen;
uint8_t last_node;
} blake2b_state;
/* Ensure param structs have not been wrongly padded */
/* Poor man's static_assert */
enum {
blake2_size_check_0 = 1 / !!(CHAR_BIT == 8),
blake2_size_check_2 =
1 / !!(sizeof(blake2b_param) == sizeof(uint64_t) * CHAR_BIT)
};
/* Streaming API */
int blake2b_init(blake2b_state *S, size_t outlen);
int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key,
size_t keylen);
int blake2b_init_param(blake2b_state *S, const blake2b_param *P);
int blake2b_update(blake2b_state *S, const void *in, size_t inlen);
int blake2b_final(blake2b_state *S, void *out, size_t outlen);
/* Simple API */
int blake2b(void *out, size_t outlen, const void *in, size_t inlen,
const void *key, size_t keylen);
/* Argon2 Team - Begin Code */
int blake2b_long(void *out, size_t outlen, const void *in, size_t inlen);
/* Argon2 Team - End Code */
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,390 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "blake2.h"
#include "blake2-impl.h"
static const uint64_t blake2b_IV[8] = {
UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b),
UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1),
UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f),
UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179)};
static const unsigned int blake2b_sigma[12][16] = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
{7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
{9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
{2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
{13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
{6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
{10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
};
static BLAKE2_INLINE void blake2b_set_lastnode(blake2b_state *S) {
S->f[1] = (uint64_t)-1;
}
static BLAKE2_INLINE void blake2b_set_lastblock(blake2b_state *S) {
if (S->last_node) {
blake2b_set_lastnode(S);
}
S->f[0] = (uint64_t)-1;
}
static BLAKE2_INLINE void blake2b_increment_counter(blake2b_state *S,
uint64_t inc) {
S->t[0] += inc;
S->t[1] += (S->t[0] < inc);
}
static BLAKE2_INLINE void blake2b_invalidate_state(blake2b_state *S) {
clear_internal_memory(S, sizeof(*S)); /* wipe */
blake2b_set_lastblock(S); /* invalidate for further use */
}
static BLAKE2_INLINE void blake2b_init0(blake2b_state *S) {
memset(S, 0, sizeof(*S));
memcpy(S->h, blake2b_IV, sizeof(S->h));
}
int blake2b_init_param(blake2b_state *S, const blake2b_param *P) {
const unsigned char *p = (const unsigned char *)P;
unsigned int i;
if (NULL == P || NULL == S) {
return -1;
}
blake2b_init0(S);
/* IV XOR Parameter Block */
for (i = 0; i < 8; ++i) {
S->h[i] ^= load64(&p[i * sizeof(S->h[i])]);
}
S->outlen = P->digest_length;
return 0;
}
/* Sequential blake2b initialization */
int blake2b_init(blake2b_state *S, size_t outlen) {
blake2b_param P;
if (S == NULL) {
return -1;
}
if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) {
blake2b_invalidate_state(S);
return -1;
}
/* Setup Parameter Block for unkeyed BLAKE2 */
P.digest_length = (uint8_t)outlen;
P.key_length = 0;
P.fanout = 1;
P.depth = 1;
P.leaf_length = 0;
P.node_offset = 0;
P.node_depth = 0;
P.inner_length = 0;
memset(P.reserved, 0, sizeof(P.reserved));
memset(P.salt, 0, sizeof(P.salt));
memset(P.personal, 0, sizeof(P.personal));
return blake2b_init_param(S, &P);
}
int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key,
size_t keylen) {
blake2b_param P;
if (S == NULL) {
return -1;
}
if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) {
blake2b_invalidate_state(S);
return -1;
}
if ((key == 0) || (keylen == 0) || (keylen > BLAKE2B_KEYBYTES)) {
blake2b_invalidate_state(S);
return -1;
}
/* Setup Parameter Block for keyed BLAKE2 */
P.digest_length = (uint8_t)outlen;
P.key_length = (uint8_t)keylen;
P.fanout = 1;
P.depth = 1;
P.leaf_length = 0;
P.node_offset = 0;
P.node_depth = 0;
P.inner_length = 0;
memset(P.reserved, 0, sizeof(P.reserved));
memset(P.salt, 0, sizeof(P.salt));
memset(P.personal, 0, sizeof(P.personal));
if (blake2b_init_param(S, &P) < 0) {
blake2b_invalidate_state(S);
return -1;
}
{
uint8_t block[BLAKE2B_BLOCKBYTES];
memset(block, 0, BLAKE2B_BLOCKBYTES);
memcpy(block, key, keylen);
blake2b_update(S, block, BLAKE2B_BLOCKBYTES);
/* Burn the key from stack */
clear_internal_memory(block, BLAKE2B_BLOCKBYTES);
}
return 0;
}
static void blake2b_compress(blake2b_state *S, const uint8_t *block) {
uint64_t m[16];
uint64_t v[16];
unsigned int i, r;
for (i = 0; i < 16; ++i) {
m[i] = load64(block + i * sizeof(m[i]));
}
for (i = 0; i < 8; ++i) {
v[i] = S->h[i];
}
v[8] = blake2b_IV[0];
v[9] = blake2b_IV[1];
v[10] = blake2b_IV[2];
v[11] = blake2b_IV[3];
v[12] = blake2b_IV[4] ^ S->t[0];
v[13] = blake2b_IV[5] ^ S->t[1];
v[14] = blake2b_IV[6] ^ S->f[0];
v[15] = blake2b_IV[7] ^ S->f[1];
#define G(r, i, a, b, c, d) \
do { \
a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \
d = rotr64(d ^ a, 32); \
c = c + d; \
b = rotr64(b ^ c, 24); \
a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \
d = rotr64(d ^ a, 16); \
c = c + d; \
b = rotr64(b ^ c, 63); \
} while ((void)0, 0)
#define ROUND(r) \
do { \
G(r, 0, v[0], v[4], v[8], v[12]); \
G(r, 1, v[1], v[5], v[9], v[13]); \
G(r, 2, v[2], v[6], v[10], v[14]); \
G(r, 3, v[3], v[7], v[11], v[15]); \
G(r, 4, v[0], v[5], v[10], v[15]); \
G(r, 5, v[1], v[6], v[11], v[12]); \
G(r, 6, v[2], v[7], v[8], v[13]); \
G(r, 7, v[3], v[4], v[9], v[14]); \
} while ((void)0, 0)
for (r = 0; r < 12; ++r) {
ROUND(r);
}
for (i = 0; i < 8; ++i) {
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
}
#undef G
#undef ROUND
}
int blake2b_update(blake2b_state *S, const void *in, size_t inlen) {
const uint8_t *pin = (const uint8_t *)in;
if (inlen == 0) {
return 0;
}
/* Sanity check */
if (S == NULL || in == NULL) {
return -1;
}
/* Is this a reused state? */
if (S->f[0] != 0) {
return -1;
}
if (S->buflen + inlen > BLAKE2B_BLOCKBYTES) {
/* Complete current block */
size_t left = S->buflen;
size_t fill = BLAKE2B_BLOCKBYTES - left;
memcpy(&S->buf[left], pin, fill);
blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
blake2b_compress(S, S->buf);
S->buflen = 0;
inlen -= fill;
pin += fill;
/* Avoid buffer copies when possible */
while (inlen > BLAKE2B_BLOCKBYTES) {
blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
blake2b_compress(S, pin);
inlen -= BLAKE2B_BLOCKBYTES;
pin += BLAKE2B_BLOCKBYTES;
}
}
memcpy(&S->buf[S->buflen], pin, inlen);
S->buflen += (unsigned int)inlen;
return 0;
}
int blake2b_final(blake2b_state *S, void *out, size_t outlen) {
uint8_t buffer[BLAKE2B_OUTBYTES] = {0};
unsigned int i;
/* Sanity checks */
if (S == NULL || out == NULL || outlen < S->outlen) {
return -1;
}
/* Is this a reused state? */
if (S->f[0] != 0) {
return -1;
}
blake2b_increment_counter(S, S->buflen);
blake2b_set_lastblock(S);
memset(&S->buf[S->buflen], 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */
blake2b_compress(S, S->buf);
for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */
store64(buffer + sizeof(S->h[i]) * i, S->h[i]);
}
memcpy(out, buffer, S->outlen);
clear_internal_memory(buffer, sizeof(buffer));
clear_internal_memory(S->buf, sizeof(S->buf));
clear_internal_memory(S->h, sizeof(S->h));
return 0;
}
int blake2b(void *out, size_t outlen, const void *in, size_t inlen,
const void *key, size_t keylen) {
blake2b_state S;
int ret = -1;
/* Verify parameters */
if (NULL == in && inlen > 0) {
goto fail;
}
if (NULL == out || outlen == 0 || outlen > BLAKE2B_OUTBYTES) {
goto fail;
}
if ((NULL == key && keylen > 0) || keylen > BLAKE2B_KEYBYTES) {
goto fail;
}
if (keylen > 0) {
if (blake2b_init_key(&S, outlen, key, keylen) < 0) {
goto fail;
}
} else {
if (blake2b_init(&S, outlen) < 0) {
goto fail;
}
}
if (blake2b_update(&S, in, inlen) < 0) {
goto fail;
}
ret = blake2b_final(&S, out, outlen);
fail:
clear_internal_memory(&S, sizeof(S));
return ret;
}
/* Argon2 Team - Begin Code */
int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) {
uint8_t *out = (uint8_t *)pout;
blake2b_state blake_state;
uint8_t outlen_bytes[sizeof(uint32_t)] = {0};
int ret = -1;
if (outlen > UINT32_MAX) {
goto fail;
}
/* Ensure little-endian byte order! */
store32(outlen_bytes, (uint32_t)outlen);
#define TRY(statement) \
do { \
ret = statement; \
if (ret < 0) { \
goto fail; \
} \
} while ((void)0, 0)
if (outlen <= BLAKE2B_OUTBYTES) {
TRY(blake2b_init(&blake_state, outlen));
TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes)));
TRY(blake2b_update(&blake_state, in, inlen));
TRY(blake2b_final(&blake_state, out, outlen));
} else {
uint32_t toproduce;
uint8_t out_buffer[BLAKE2B_OUTBYTES];
uint8_t in_buffer[BLAKE2B_OUTBYTES];
TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES));
TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes)));
TRY(blake2b_update(&blake_state, in, inlen));
TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES));
memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2);
out += BLAKE2B_OUTBYTES / 2;
toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2;
while (toproduce > BLAKE2B_OUTBYTES) {
memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES);
TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer,
BLAKE2B_OUTBYTES, NULL, 0));
memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2);
out += BLAKE2B_OUTBYTES / 2;
toproduce -= BLAKE2B_OUTBYTES / 2;
}
memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES);
TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL,
0));
memcpy(out, out_buffer, toproduce);
}
fail:
clear_internal_memory(&blake_state, sizeof(blake_state));
return ret;
#undef TRY
}
/* Argon2 Team - End Code */

View File

@@ -0,0 +1,56 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#ifndef BLAKE_ROUND_MKA_H
#define BLAKE_ROUND_MKA_H
#include "blake2.h"
#include "blake2-impl.h"
/*designed by the Lyra PHC team */
static BLAKE2_INLINE uint64_t fBlaMka(uint64_t x, uint64_t y) {
const uint64_t m = UINT64_C(0xFFFFFFFF);
const uint64_t xy = (x & m) * (y & m);
return x + y + 2 * xy;
}
#define G(a, b, c, d) \
do { \
a = fBlaMka(a, b); \
d = rotr64(d ^ a, 32); \
c = fBlaMka(c, d); \
b = rotr64(b ^ c, 24); \
a = fBlaMka(a, b); \
d = rotr64(d ^ a, 16); \
c = fBlaMka(c, d); \
b = rotr64(b ^ c, 63); \
} while ((void)0, 0)
#define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \
v12, v13, v14, v15) \
do { \
G(v0, v4, v8, v12); \
G(v1, v5, v9, v13); \
G(v2, v6, v10, v14); \
G(v3, v7, v11, v15); \
G(v0, v5, v10, v15); \
G(v1, v6, v11, v12); \
G(v2, v7, v8, v13); \
G(v3, v4, v9, v14); \
} while ((void)0, 0)
#endif

View File

@@ -0,0 +1,605 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
/*For memory wiping*/
#ifdef _MSC_VER
#include <windows.h>
#include <winbase.h> /* For SecureZeroMemory */
#endif
#if defined __STDC_LIB_EXT1__
#define __STDC_WANT_LIB_EXT1__ 1
#endif
#define VC_GE_2005(version) (version >= 1400)
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "core.h"
#include "thread.h"
#include "blake2/blake2.h"
#include "blake2/blake2-impl.h"
#ifdef GENKAT
#include "genkat.h"
#endif
#if defined(__clang__)
#if __has_attribute(optnone)
#define NOT_OPTIMIZED __attribute__((optnone))
#endif
#elif defined(__GNUC__)
#define GCC_VERSION \
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= 40400
#define NOT_OPTIMIZED __attribute__((optimize("O0")))
#endif
#endif
#ifndef NOT_OPTIMIZED
#define NOT_OPTIMIZED
#endif
/***************Instance and Position constructors**********/
void init_block_value(block *b, uint8_t in) { memset(b->v, in, sizeof(b->v)); }
void copy_block(block *dst, const block *src) {
memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_QWORDS_IN_BLOCK);
}
void xor_block(block *dst, const block *src) {
int i;
for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
dst->v[i] ^= src->v[i];
}
}
static void load_block(block *dst, const void *input) {
unsigned i;
for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
dst->v[i] = load64((const uint8_t *)input + i * sizeof(dst->v[i]));
}
}
static void store_block(void *output, const block *src) {
unsigned i;
for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) {
store64((uint8_t *)output + i * sizeof(src->v[i]), src->v[i]);
}
}
/***************Memory functions*****************/
int allocate_memory(const argon2_context *context, uint8_t **memory,
size_t num, size_t size) {
size_t memory_size = num*size;
if (memory == NULL) {
return ARGON2_MEMORY_ALLOCATION_ERROR;
}
/* 1. Check for multiplication overflow */
if (size != 0 && memory_size / size != num) {
return ARGON2_MEMORY_ALLOCATION_ERROR;
}
/* 2. Try to allocate with appropriate allocator */
if (context->allocate_cbk) {
(context->allocate_cbk)(memory, memory_size);
} else {
*memory = malloc(memory_size);
}
if (*memory == NULL) {
return ARGON2_MEMORY_ALLOCATION_ERROR;
}
return ARGON2_OK;
}
void free_memory(const argon2_context *context, uint8_t *memory,
size_t num, size_t size) {
size_t memory_size = num*size;
clear_internal_memory(memory, memory_size);
if (context->free_cbk) {
(context->free_cbk)(memory, memory_size);
} else {
free(memory);
}
}
void NOT_OPTIMIZED secure_wipe_memory(void *v, size_t n) {
#if defined(_MSC_VER) && VC_GE_2005(_MSC_VER)
SecureZeroMemory(v, n);
#elif defined memset_s
memset_s(v, n, 0, n);
#elif defined(__OpenBSD__)
explicit_bzero(v, n);
#else
static void *(*const volatile memset_sec)(void *, int, size_t) = &memset;
memset_sec(v, 0, n);
#endif
}
/* Memory clear flag defaults to true. */
int FLAG_clear_internal_memory = 1;
void clear_internal_memory(void *v, size_t n) {
if (FLAG_clear_internal_memory && v) {
secure_wipe_memory(v, n);
}
}
void finalize(const argon2_context *context, argon2_instance_t *instance) {
if (context != NULL && instance != NULL) {
block blockhash;
uint32_t l;
copy_block(&blockhash, instance->memory + instance->lane_length - 1);
/* XOR the last blocks */
for (l = 1; l < instance->lanes; ++l) {
uint32_t last_block_in_lane =
l * instance->lane_length + (instance->lane_length - 1);
xor_block(&blockhash, instance->memory + last_block_in_lane);
}
/* Hash the result */
{
uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
store_block(blockhash_bytes, &blockhash);
blake2b_long(context->out, context->outlen, blockhash_bytes,
ARGON2_BLOCK_SIZE);
/* clear blockhash and blockhash_bytes */
clear_internal_memory(blockhash.v, ARGON2_BLOCK_SIZE);
clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE);
}
#ifdef GENKAT
print_tag(context->out, context->outlen);
#endif
free_memory(context, (uint8_t *)instance->memory,
instance->memory_blocks, sizeof(block));
}
}
uint32_t index_alpha(const argon2_instance_t *instance,
const argon2_position_t *position, uint32_t pseudo_rand,
int same_lane) {
/*
* Pass 0:
* This lane : all already finished segments plus already constructed
* blocks in this segment
* Other lanes : all already finished segments
* Pass 1+:
* This lane : (SYNC_POINTS - 1) last segments plus already constructed
* blocks in this segment
* Other lanes : (SYNC_POINTS - 1) last segments
*/
uint32_t reference_area_size;
uint64_t relative_position;
uint32_t start_position, absolute_position;
if (0 == position->pass) {
/* First pass */
if (0 == position->slice) {
/* First slice */
reference_area_size =
position->index - 1; /* all but the previous */
} else {
if (same_lane) {
/* The same lane => add current segment */
reference_area_size =
position->slice * instance->segment_length +
position->index - 1;
} else {
reference_area_size =
position->slice * instance->segment_length +
((position->index == 0) ? (-1) : 0);
}
}
} else {
/* Second pass */
if (same_lane) {
reference_area_size = instance->lane_length -
instance->segment_length + position->index -
1;
} else {
reference_area_size = instance->lane_length -
instance->segment_length +
((position->index == 0) ? (-1) : 0);
}
}
/* 1.2.4. Mapping pseudo_rand to 0..<reference_area_size-1> and produce
* relative position */
relative_position = pseudo_rand;
relative_position = relative_position * relative_position >> 32;
relative_position = reference_area_size - 1 -
(reference_area_size * relative_position >> 32);
/* 1.2.5 Computing starting position */
start_position = 0;
if (0 != position->pass) {
start_position = (position->slice == ARGON2_SYNC_POINTS - 1)
? 0
: (position->slice + 1) * instance->segment_length;
}
/* 1.2.6. Computing absolute position */
absolute_position = (start_position + relative_position) %
instance->lane_length; /* absolute position */
return absolute_position;
}
#ifdef _WIN32
static unsigned __stdcall fill_segment_thr(void *thread_data)
#else
static void *fill_segment_thr(void *thread_data)
#endif
{
argon2_thread_data *my_data = thread_data;
fill_segment(my_data->instance_ptr, my_data->pos);
argon2_thread_exit();
return 0;
}
int fill_memory_blocks(argon2_instance_t *instance) {
uint32_t r, s;
argon2_thread_handle_t *thread = NULL;
argon2_thread_data *thr_data = NULL;
int rc = ARGON2_OK;
if (instance == NULL || instance->lanes == 0) {
rc = ARGON2_THREAD_FAIL;
goto fail;
}
/* 1. Allocating space for threads */
thread = calloc(instance->lanes, sizeof(argon2_thread_handle_t));
if (thread == NULL) {
rc = ARGON2_MEMORY_ALLOCATION_ERROR;
goto fail;
}
thr_data = calloc(instance->lanes, sizeof(argon2_thread_data));
if (thr_data == NULL) {
rc = ARGON2_MEMORY_ALLOCATION_ERROR;
goto fail;
}
for (r = 0; r < instance->passes; ++r) {
for (s = 0; s < ARGON2_SYNC_POINTS; ++s) {
uint32_t l;
/* 2. Calling threads */
for (l = 0; l < instance->lanes; ++l) {
argon2_position_t position;
/* 2.1 Join a thread if limit is exceeded */
if (l >= instance->threads) {
if (argon2_thread_join(thread[l - instance->threads])) {
rc = ARGON2_THREAD_FAIL;
goto fail;
}
}
/* 2.2 Create thread */
position.pass = r;
position.lane = l;
position.slice = (uint8_t)s;
position.index = 0;
thr_data[l].instance_ptr =
instance; /* preparing the thread input */
memcpy(&(thr_data[l].pos), &position,
sizeof(argon2_position_t));
if (argon2_thread_create(&thread[l], &fill_segment_thr,
(void *)&thr_data[l])) {
rc = ARGON2_THREAD_FAIL;
goto fail;
}
/* fill_segment(instance, position); */
/*Non-thread equivalent of the lines above */
}
/* 3. Joining remaining threads */
for (l = instance->lanes - instance->threads; l < instance->lanes;
++l) {
if (argon2_thread_join(thread[l])) {
rc = ARGON2_THREAD_FAIL;
goto fail;
}
}
}
#ifdef GENKAT
internal_kat(instance, r); /* Print all memory blocks */
#endif
}
fail:
if (thread != NULL) {
free(thread);
}
if (thr_data != NULL) {
free(thr_data);
}
return rc;
}
int validate_inputs(const argon2_context *context) {
if (NULL == context) {
return ARGON2_INCORRECT_PARAMETER;
}
if (NULL == context->out) {
return ARGON2_OUTPUT_PTR_NULL;
}
/* Validate output length */
if (ARGON2_MIN_OUTLEN > context->outlen) {
return ARGON2_OUTPUT_TOO_SHORT;
}
if (ARGON2_MAX_OUTLEN < context->outlen) {
return ARGON2_OUTPUT_TOO_LONG;
}
/* Validate password (required param) */
if (NULL == context->pwd) {
if (0 != context->pwdlen) {
return ARGON2_PWD_PTR_MISMATCH;
}
}
if (ARGON2_MIN_PWD_LENGTH > context->pwdlen) {
return ARGON2_PWD_TOO_SHORT;
}
if (ARGON2_MAX_PWD_LENGTH < context->pwdlen) {
return ARGON2_PWD_TOO_LONG;
}
/* Validate salt (required param) */
if (NULL == context->salt) {
if (0 != context->saltlen) {
return ARGON2_SALT_PTR_MISMATCH;
}
}
if (ARGON2_MIN_SALT_LENGTH > context->saltlen) {
return ARGON2_SALT_TOO_SHORT;
}
if (ARGON2_MAX_SALT_LENGTH < context->saltlen) {
return ARGON2_SALT_TOO_LONG;
}
/* Validate secret (optional param) */
if (NULL == context->secret) {
if (0 != context->secretlen) {
return ARGON2_SECRET_PTR_MISMATCH;
}
} else {
if (ARGON2_MIN_SECRET > context->secretlen) {
return ARGON2_SECRET_TOO_SHORT;
}
if (ARGON2_MAX_SECRET < context->secretlen) {
return ARGON2_SECRET_TOO_LONG;
}
}
/* Validate associated data (optional param) */
if (NULL == context->ad) {
if (0 != context->adlen) {
return ARGON2_AD_PTR_MISMATCH;
}
} else {
if (ARGON2_MIN_AD_LENGTH > context->adlen) {
return ARGON2_AD_TOO_SHORT;
}
if (ARGON2_MAX_AD_LENGTH < context->adlen) {
return ARGON2_AD_TOO_LONG;
}
}
/* Validate memory cost */
if (ARGON2_MIN_MEMORY > context->m_cost) {
return ARGON2_MEMORY_TOO_LITTLE;
}
if (ARGON2_MAX_MEMORY < context->m_cost) {
return ARGON2_MEMORY_TOO_MUCH;
}
if (context->m_cost < 8 * context->lanes) {
return ARGON2_MEMORY_TOO_LITTLE;
}
/* Validate time cost */
if (ARGON2_MIN_TIME > context->t_cost) {
return ARGON2_TIME_TOO_SMALL;
}
if (ARGON2_MAX_TIME < context->t_cost) {
return ARGON2_TIME_TOO_LARGE;
}
/* Validate lanes */
if (ARGON2_MIN_LANES > context->lanes) {
return ARGON2_LANES_TOO_FEW;
}
if (ARGON2_MAX_LANES < context->lanes) {
return ARGON2_LANES_TOO_MANY;
}
/* Validate threads */
if (ARGON2_MIN_THREADS > context->threads) {
return ARGON2_THREADS_TOO_FEW;
}
if (ARGON2_MAX_THREADS < context->threads) {
return ARGON2_THREADS_TOO_MANY;
}
if (NULL != context->allocate_cbk && NULL == context->free_cbk) {
return ARGON2_FREE_MEMORY_CBK_NULL;
}
if (NULL == context->allocate_cbk && NULL != context->free_cbk) {
return ARGON2_ALLOCATE_MEMORY_CBK_NULL;
}
return ARGON2_OK;
}
void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) {
uint32_t l;
/* Make the first and second block in each lane as G(H0||i||0) or
G(H0||i||1) */
uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
for (l = 0; l < instance->lanes; ++l) {
store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0);
store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l);
blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash,
ARGON2_PREHASH_SEED_LENGTH);
load_block(&instance->memory[l * instance->lane_length + 0],
blockhash_bytes);
store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1);
blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash,
ARGON2_PREHASH_SEED_LENGTH);
load_block(&instance->memory[l * instance->lane_length + 1],
blockhash_bytes);
}
clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE);
}
void initial_hash(uint8_t *blockhash, argon2_context *context,
argon2_type type) {
blake2b_state BlakeHash;
uint8_t value[sizeof(uint32_t)];
if (NULL == context || NULL == blockhash) {
return;
}
blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH);
store32(&value, context->lanes);
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
store32(&value, context->outlen);
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
store32(&value, context->m_cost);
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
store32(&value, context->t_cost);
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
store32(&value, context->version);
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
store32(&value, (uint32_t)type);
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
store32(&value, context->pwdlen);
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
if (context->pwd != NULL) {
blake2b_update(&BlakeHash, (const uint8_t *)context->pwd,
context->pwdlen);
if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) {
secure_wipe_memory(context->pwd, context->pwdlen);
context->pwdlen = 0;
}
}
store32(&value, context->saltlen);
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
if (context->salt != NULL) {
blake2b_update(&BlakeHash, (const uint8_t *)context->salt,
context->saltlen);
}
store32(&value, context->secretlen);
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
if (context->secret != NULL) {
blake2b_update(&BlakeHash, (const uint8_t *)context->secret,
context->secretlen);
if (context->flags & ARGON2_FLAG_CLEAR_SECRET) {
secure_wipe_memory(context->secret, context->secretlen);
context->secretlen = 0;
}
}
store32(&value, context->adlen);
blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
if (context->ad != NULL) {
blake2b_update(&BlakeHash, (const uint8_t *)context->ad,
context->adlen);
}
blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH);
}
int initialize(argon2_instance_t *instance, argon2_context *context) {
uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH];
int result = ARGON2_OK;
if (instance == NULL || context == NULL)
return ARGON2_INCORRECT_PARAMETER;
instance->context_ptr = context;
/* 1. Memory allocation */
result = allocate_memory(context, (uint8_t **)&(instance->memory),
instance->memory_blocks, sizeof(block));
if (result != ARGON2_OK) {
return result;
}
/* 2. Initial hashing */
/* H_0 + 8 extra bytes to produce the first blocks */
/* uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */
/* Hashing all inputs */
initial_hash(blockhash, context, instance->type);
/* Zeroing 8 extra bytes */
clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH,
ARGON2_PREHASH_SEED_LENGTH -
ARGON2_PREHASH_DIGEST_LENGTH);
#ifdef GENKAT
initial_kat(blockhash, context, instance->type);
#endif
/* 3. Creating first blocks, we always have at least two blocks in a slice
*/
fill_first_blocks(blockhash, instance);
/* Clearing the hash */
clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH);
return ARGON2_OK;
}

View File

@@ -0,0 +1,234 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#ifndef ARGON2_CORE_H
#define ARGON2_CORE_H
#include "argon2.h"
#if defined(_MSC_VER)
#define ALIGN(n) __declspec(align(16))
#elif defined(__GNUC__) || defined(__clang)
#define ALIGN(x) __attribute__((__aligned__(x)))
#else
#define ALIGN(x)
#endif
#define CONST_CAST(x) (x)(uintptr_t)
/**********************Argon2 internal constants*******************************/
enum argon2_core_constants {
/* Memory block size in bytes */
ARGON2_BLOCK_SIZE = 1024,
ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8,
ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16,
/* Number of pseudo-random values generated by one call to Blake in Argon2i
to
generate reference block positions */
ARGON2_ADDRESSES_IN_BLOCK = 128,
/* Pre-hashing digest length and its extension*/
ARGON2_PREHASH_DIGEST_LENGTH = 64,
ARGON2_PREHASH_SEED_LENGTH = 72
};
/*************************Argon2 internal data types***********************/
/*
* Structure for the (1KB) memory block implemented as 128 64-bit words.
* Memory blocks can be copied, XORed. Internal words can be accessed by [] (no
* bounds checking).
*/
typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block;
/*****************Functions that work with the block******************/
/* Initialize each byte of the block with @in */
void init_block_value(block *b, uint8_t in);
/* Copy block @src to block @dst */
void copy_block(block *dst, const block *src);
/* XOR @src onto @dst bytewise */
void xor_block(block *dst, const block *src);
/*
* Argon2 instance: memory pointer, number of passes, amount of memory, type,
* and derived values.
* Used to evaluate the number and location of blocks to construct in each
* thread
*/
typedef struct Argon2_instance_t {
block *memory; /* Memory pointer */
uint32_t version;
uint32_t passes; /* Number of passes */
uint32_t memory_blocks; /* Number of blocks in memory */
uint32_t segment_length;
uint32_t lane_length;
uint32_t lanes;
uint32_t threads;
argon2_type type;
int print_internals; /* whether to print the memory blocks */
argon2_context *context_ptr; /* points back to original context */
} argon2_instance_t;
/*
* Argon2 position: where we construct the block right now. Used to distribute
* work between threads.
*/
typedef struct Argon2_position_t {
uint32_t pass;
uint32_t lane;
uint8_t slice;
uint32_t index;
} argon2_position_t;
/*Struct that holds the inputs for thread handling FillSegment*/
typedef struct Argon2_thread_data {
argon2_instance_t *instance_ptr;
argon2_position_t pos;
} argon2_thread_data;
/*************************Argon2 core functions********************************/
/* Allocates memory to the given pointer, uses the appropriate allocator as
* specified in the context. Total allocated memory is num*size.
* @param context argon2_context which specifies the allocator
* @param memory pointer to the pointer to the memory
* @param size the size in bytes for each element to be allocated
* @param num the number of elements to be allocated
* @return ARGON2_OK if @memory is a valid pointer and memory is allocated
*/
int allocate_memory(const argon2_context *context, uint8_t **memory,
size_t num, size_t size);
/*
* Frees memory at the given pointer, uses the appropriate deallocator as
* specified in the context. Also cleans the memory using clear_internal_memory.
* @param context argon2_context which specifies the deallocator
* @param memory pointer to buffer to be freed
* @param size the size in bytes for each element to be deallocated
* @param num the number of elements to be deallocated
*/
void free_memory(const argon2_context *context, uint8_t *memory,
size_t num, size_t size);
/* Function that securely cleans the memory. This ignores any flags set
* regarding clearing memory. Usually one just calls clear_internal_memory.
* @param mem Pointer to the memory
* @param s Memory size in bytes
*/
void secure_wipe_memory(void *v, size_t n);
/* Function that securely clears the memory if FLAG_clear_internal_memory is
* set. If the flag isn't set, this function does nothing.
* @param mem Pointer to the memory
* @param s Memory size in bytes
*/
void clear_internal_memory(void *v, size_t n);
/*
* Computes absolute position of reference block in the lane following a skewed
* distribution and using a pseudo-random value as input
* @param instance Pointer to the current instance
* @param position Pointer to the current position
* @param pseudo_rand 32-bit pseudo-random value used to determine the position
* @param same_lane Indicates if the block will be taken from the current lane.
* If so we can reference the current segment
* @pre All pointers must be valid
*/
uint32_t index_alpha(const argon2_instance_t *instance,
const argon2_position_t *position, uint32_t pseudo_rand,
int same_lane);
/*
* Function that validates all inputs against predefined restrictions and return
* an error code
* @param context Pointer to current Argon2 context
* @return ARGON2_OK if everything is all right, otherwise one of error codes
* (all defined in <argon2.h>
*/
int validate_inputs(const argon2_context *context);
/*
* Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears
* password and secret if needed
* @param context Pointer to the Argon2 internal structure containing memory
* pointer, and parameters for time and space requirements.
* @param blockhash Buffer for pre-hashing digest
* @param type Argon2 type
* @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes
* allocated
*/
void initial_hash(uint8_t *blockhash, argon2_context *context,
argon2_type type);
/*
* Function creates first 2 blocks per lane
* @param instance Pointer to the current instance
* @param blockhash Pointer to the pre-hashing digest
* @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values
*/
void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance);
/*
* Function allocates memory, hashes the inputs with Blake, and creates first
* two blocks. Returns the pointer to the main memory with 2 blocks per lane
* initialized
* @param context Pointer to the Argon2 internal structure containing memory
* pointer, and parameters for time and space requirements.
* @param instance Current Argon2 instance
* @return Zero if successful, -1 if memory failed to allocate. @context->state
* will be modified if successful.
*/
int initialize(argon2_instance_t *instance, argon2_context *context);
/*
* XORing the last block of each lane, hashing it, making the tag. Deallocates
* the memory.
* @param context Pointer to current Argon2 context (use only the out parameters
* from it)
* @param instance Pointer to current instance of Argon2
* @pre instance->state must point to necessary amount of memory
* @pre context->out must point to outlen bytes of memory
* @pre if context->free_cbk is not NULL, it should point to a function that
* deallocates memory
*/
void finalize(const argon2_context *context, argon2_instance_t *instance);
/*
* Function that fills the segment using previous segments also from other
* threads
* @param context current context
* @param instance Pointer to the current instance
* @param position Current position
* @pre all block pointers must be valid
*/
void fill_segment(const argon2_instance_t *instance,
argon2_position_t position);
/*
* Function that fills the entire memory t_cost times based on the first two
* blocks in each lane
* @param instance Pointer to the current instance
* @return ARGON2_OK if successful, @context->state
*/
int fill_memory_blocks(argon2_instance_t *instance);
#endif

View File

@@ -0,0 +1,450 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "encoding.h"
#include "core.h"
/*
* Example code for a decoder and encoder of "hash strings", with Argon2
* parameters.
*
* This code comprises three sections:
*
* -- The first section contains generic Base64 encoding and decoding
* functions. It is conceptually applicable to any hash function
* implementation that uses Base64 to encode and decode parameters,
* salts and outputs. It could be made into a library, provided that
* the relevant functions are made public (non-static) and be given
* reasonable names to avoid collisions with other functions.
*
* -- The second section is specific to Argon2. It encodes and decodes
* the parameters, salts and outputs. It does not compute the hash
* itself.
*
* The code was originally written by Thomas Pornin <pornin@bolet.org>,
* to whom comments and remarks may be sent. It is released under what
* should amount to Public Domain or its closest equivalent; the
* following mantra is supposed to incarnate that fact with all the
* proper legal rituals:
*
* ---------------------------------------------------------------------
* This file is provided under the terms of Creative Commons CC0 1.0
* Public Domain Dedication. To the extent possible under law, the
* author (Thomas Pornin) has waived all copyright and related or
* neighboring rights to this file. This work is published from: Canada.
* ---------------------------------------------------------------------
*
* Copyright (c) 2015 Thomas Pornin
*/
/* ==================================================================== */
/*
* Common code; could be shared between different hash functions.
*
* Note: the Base64 functions below assume that uppercase letters (resp.
* lowercase letters) have consecutive numerical codes, that fit on 8
* bits. All modern systems use ASCII-compatible charsets, where these
* properties are true. If you are stuck with a dinosaur of a system
* that still defaults to EBCDIC then you already have much bigger
* interoperability issues to deal with.
*/
/*
* Some macros for constant-time comparisons. These work over values in
* the 0..255 range. Returned value is 0x00 on "false", 0xFF on "true".
*/
#define EQ(x, y) ((((0U - ((unsigned)(x) ^ (unsigned)(y))) >> 8) & 0xFF) ^ 0xFF)
#define GT(x, y) ((((unsigned)(y) - (unsigned)(x)) >> 8) & 0xFF)
#define GE(x, y) (GT(y, x) ^ 0xFF)
#define LT(x, y) GT(y, x)
#define LE(x, y) GE(y, x)
/*
* Convert value x (0..63) to corresponding Base64 character.
*/
static int b64_byte_to_char(unsigned x) {
return (LT(x, 26) & (x + 'A')) |
(GE(x, 26) & LT(x, 52) & (x + ('a' - 26))) |
(GE(x, 52) & LT(x, 62) & (x + ('0' - 52))) | (EQ(x, 62) & '+') |
(EQ(x, 63) & '/');
}
/*
* Convert character c to the corresponding 6-bit value. If character c
* is not a Base64 character, then 0xFF (255) is returned.
*/
static unsigned b64_char_to_byte(int c) {
unsigned x;
x = (GE(c, 'A') & LE(c, 'Z') & (c - 'A')) |
(GE(c, 'a') & LE(c, 'z') & (c - ('a' - 26))) |
(GE(c, '0') & LE(c, '9') & (c - ('0' - 52))) | (EQ(c, '+') & 62) |
(EQ(c, '/') & 63);
return x | (EQ(x, 0) & (EQ(c, 'A') ^ 0xFF));
}
/*
* Convert some bytes to Base64. 'dst_len' is the length (in characters)
* of the output buffer 'dst'; if that buffer is not large enough to
* receive the result (including the terminating 0), then (size_t)-1
* is returned. Otherwise, the zero-terminated Base64 string is written
* in the buffer, and the output length (counted WITHOUT the terminating
* zero) is returned.
*/
static size_t to_base64(char *dst, size_t dst_len, const void *src,
size_t src_len) {
size_t olen;
const unsigned char *buf;
unsigned acc, acc_len;
olen = (src_len / 3) << 2;
switch (src_len % 3) {
case 2:
olen++;
/* fall through */
case 1:
olen += 2;
break;
}
if (dst_len <= olen) {
return (size_t)-1;
}
acc = 0;
acc_len = 0;
buf = (const unsigned char *)src;
while (src_len-- > 0) {
acc = (acc << 8) + (*buf++);
acc_len += 8;
while (acc_len >= 6) {
acc_len -= 6;
*dst++ = (char)b64_byte_to_char((acc >> acc_len) & 0x3F);
}
}
if (acc_len > 0) {
*dst++ = (char)b64_byte_to_char((acc << (6 - acc_len)) & 0x3F);
}
*dst++ = 0;
return olen;
}
/*
* Decode Base64 chars into bytes. The '*dst_len' value must initially
* contain the length of the output buffer '*dst'; when the decoding
* ends, the actual number of decoded bytes is written back in
* '*dst_len'.
*
* Decoding stops when a non-Base64 character is encountered, or when
* the output buffer capacity is exceeded. If an error occurred (output
* buffer is too small, invalid last characters leading to unprocessed
* buffered bits), then NULL is returned; otherwise, the returned value
* points to the first non-Base64 character in the source stream, which
* may be the terminating zero.
*/
static const char *from_base64(void *dst, size_t *dst_len, const char *src) {
size_t len;
unsigned char *buf;
unsigned acc, acc_len;
buf = (unsigned char *)dst;
len = 0;
acc = 0;
acc_len = 0;
for (;;) {
unsigned d;
d = b64_char_to_byte(*src);
if (d == 0xFF) {
break;
}
src++;
acc = (acc << 6) + d;
acc_len += 6;
if (acc_len >= 8) {
acc_len -= 8;
if ((len++) >= *dst_len) {
return NULL;
}
*buf++ = (acc >> acc_len) & 0xFF;
}
}
/*
* If the input length is equal to 1 modulo 4 (which is
* invalid), then there will remain 6 unprocessed bits;
* otherwise, only 0, 2 or 4 bits are buffered. The buffered
* bits must also all be zero.
*/
if (acc_len > 4 || (acc & (((unsigned)1 << acc_len) - 1)) != 0) {
return NULL;
}
*dst_len = len;
return src;
}
/*
* Decode decimal integer from 'str'; the value is written in '*v'.
* Returned value is a pointer to the next non-decimal character in the
* string. If there is no digit at all, or the value encoding is not
* minimal (extra leading zeros), or the value does not fit in an
* 'unsigned long', then NULL is returned.
*/
static const char *decode_decimal(const char *str, unsigned long *v) {
const char *orig;
unsigned long acc;
acc = 0;
for (orig = str;; str++) {
int c;
c = *str;
if (c < '0' || c > '9') {
break;
}
c -= '0';
if (acc > (ULONG_MAX / 10)) {
return NULL;
}
acc *= 10;
if ((unsigned long)c > (ULONG_MAX - acc)) {
return NULL;
}
acc += (unsigned long)c;
}
if (str == orig || (*orig == '0' && str != (orig + 1))) {
return NULL;
}
*v = acc;
return str;
}
/* ==================================================================== */
/*
* Code specific to Argon2.
*
* The code below applies the following format:
*
* $argon2<T>[$v=<num>]$m=<num>,t=<num>,p=<num>$<bin>$<bin>
*
* where <T> is either 'd', 'id', or 'i', <num> is a decimal integer (positive,
* fits in an 'unsigned long'), and <bin> is Base64-encoded data (no '=' padding
* characters, no newline or whitespace).
*
* The last two binary chunks (encoded in Base64) are, in that order,
* the salt and the output. Both are required. The binary salt length and the
* output length must be in the allowed ranges defined in argon2.h.
*
* The ctx struct must contain buffers large enough to hold the salt and pwd
* when it is fed into decode_string.
*/
int decode_string(argon2_context *ctx, const char *str, argon2_type type) {
/* check for prefix */
#define CC(prefix) \
do { \
size_t cc_len = strlen(prefix); \
if (strncmp(str, prefix, cc_len) != 0) { \
return ARGON2_DECODING_FAIL; \
} \
str += cc_len; \
} while ((void)0, 0)
/* optional prefix checking with supplied code */
#define CC_opt(prefix, code) \
do { \
size_t cc_len = strlen(prefix); \
if (strncmp(str, prefix, cc_len) == 0) { \
str += cc_len; \
{ code; } \
} \
} while ((void)0, 0)
/* Decoding prefix into decimal */
#define DECIMAL(x) \
do { \
unsigned long dec_x; \
str = decode_decimal(str, &dec_x); \
if (str == NULL) { \
return ARGON2_DECODING_FAIL; \
} \
(x) = dec_x; \
} while ((void)0, 0)
/* Decoding base64 into a binary buffer */
#define BIN(buf, max_len, len) \
do { \
size_t bin_len = (max_len); \
str = from_base64(buf, &bin_len, str); \
if (str == NULL || bin_len > UINT32_MAX) { \
return ARGON2_DECODING_FAIL; \
} \
(len) = (uint32_t)bin_len; \
} while ((void)0, 0)
size_t maxsaltlen = ctx->saltlen;
size_t maxoutlen = ctx->outlen;
int validation_result;
const char* type_string;
/* We should start with the argon2_type we are using */
type_string = argon2_type2string(type, 0);
if (!type_string) {
return ARGON2_INCORRECT_TYPE;
}
CC("$");
CC(type_string);
/* Reading the version number if the default is suppressed */
ctx->version = ARGON2_VERSION_10;
CC_opt("$v=", DECIMAL(ctx->version));
CC("$m=");
DECIMAL(ctx->m_cost);
CC(",t=");
DECIMAL(ctx->t_cost);
CC(",p=");
DECIMAL(ctx->lanes);
ctx->threads = ctx->lanes;
CC("$");
BIN(ctx->salt, maxsaltlen, ctx->saltlen);
CC("$");
BIN(ctx->out, maxoutlen, ctx->outlen);
/* The rest of the fields get the default values */
ctx->secret = NULL;
ctx->secretlen = 0;
ctx->ad = NULL;
ctx->adlen = 0;
ctx->allocate_cbk = NULL;
ctx->free_cbk = NULL;
ctx->flags = ARGON2_DEFAULT_FLAGS;
/* On return, must have valid context */
validation_result = validate_inputs(ctx);
if (validation_result != ARGON2_OK) {
return validation_result;
}
/* Can't have any additional characters */
if (*str == 0) {
return ARGON2_OK;
} else {
return ARGON2_DECODING_FAIL;
}
#undef CC
#undef CC_opt
#undef DECIMAL
#undef BIN
}
int encode_string(char *dst, size_t dst_len, argon2_context *ctx,
argon2_type type) {
#define SS(str) \
do { \
size_t pp_len = strlen(str); \
if (pp_len >= dst_len) { \
return ARGON2_ENCODING_FAIL; \
} \
memcpy(dst, str, pp_len + 1); \
dst += pp_len; \
dst_len -= pp_len; \
} while ((void)0, 0)
#define SX(x) \
do { \
char tmp[30]; \
sprintf(tmp, "%lu", (unsigned long)(x)); \
SS(tmp); \
} while ((void)0, 0)
#define SB(buf, len) \
do { \
size_t sb_len = to_base64(dst, dst_len, buf, len); \
if (sb_len == (size_t)-1) { \
return ARGON2_ENCODING_FAIL; \
} \
dst += sb_len; \
dst_len -= sb_len; \
} while ((void)0, 0)
const char* type_string = argon2_type2string(type, 0);
int validation_result = validate_inputs(ctx);
if (!type_string) {
return ARGON2_ENCODING_FAIL;
}
if (validation_result != ARGON2_OK) {
return validation_result;
}
SS("$");
SS(type_string);
SS("$v=");
SX(ctx->version);
SS("$m=");
SX(ctx->m_cost);
SS(",t=");
SX(ctx->t_cost);
SS(",p=");
SX(ctx->lanes);
SS("$");
SB(ctx->salt, ctx->saltlen);
SS("$");
SB(ctx->out, ctx->outlen);
return ARGON2_OK;
#undef SS
#undef SX
#undef SB
}
size_t b64len(uint32_t len) {
size_t olen = ((size_t)len / 3) << 2;
switch (len % 3) {
case 2:
olen++;
/* fall through */
case 1:
olen += 2;
break;
}
return olen;
}
size_t numlen(uint32_t num) {
size_t len = 1;
while (num >= 10) {
++len;
num = num / 10;
}
return len;
}

View File

@@ -0,0 +1,57 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#ifndef ENCODING_H
#define ENCODING_H
#include "argon2.h"
#define ARGON2_MAX_DECODED_LANES UINT32_C(255)
#define ARGON2_MIN_DECODED_SALT_LEN UINT32_C(8)
#define ARGON2_MIN_DECODED_OUT_LEN UINT32_C(12)
/*
* encode an Argon2 hash string into the provided buffer. 'dst_len'
* contains the size, in characters, of the 'dst' buffer; if 'dst_len'
* is less than the number of required characters (including the
* terminating 0), then this function returns ARGON2_ENCODING_ERROR.
*
* on success, ARGON2_OK is returned.
*/
int encode_string(char *dst, size_t dst_len, argon2_context *ctx,
argon2_type type);
/*
* Decodes an Argon2 hash string into the provided structure 'ctx'.
* The only fields that must be set prior to this call are ctx.saltlen and
* ctx.outlen (which must be the maximal salt and out length values that are
* allowed), ctx.salt and ctx.out (which must be buffers of the specified
* length), and ctx.pwd and ctx.pwdlen which must hold a valid password.
*
* Invalid input string causes an error. On success, the ctx is valid and all
* fields have been initialized.
*
* Returned value is ARGON2_OK on success, other ARGON2_ codes on error.
*/
int decode_string(argon2_context *ctx, const char *str, argon2_type type);
/* Returns the length of the encoded byte stream with length len */
size_t b64len(uint32_t len);
/* Returns the length of the encoded number num */
size_t numlen(uint32_t num);
#endif

View File

@@ -0,0 +1,186 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "argon2.h"
#include "opt.h"
#include "blake2/blake2.h"
#include "blake2/blamka-round-opt.h"
void fill_block(__m128i *state, const block *ref_block, block *next_block,
int with_xor) {
__m128i block_XY[ARGON2_OWORDS_IN_BLOCK];
unsigned int i;
if (with_xor) {
for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) {
state[i] = _mm_xor_si128(
state[i], _mm_loadu_si128((const __m128i *)ref_block->v + i));
block_XY[i] = _mm_xor_si128(
state[i], _mm_loadu_si128((const __m128i *)next_block->v + i));
}
} else {
for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) {
block_XY[i] = state[i] = _mm_xor_si128(
state[i], _mm_loadu_si128((const __m128i *)ref_block->v + i));
}
}
for (i = 0; i < 8; ++i) {
BLAKE2_ROUND(state[8 * i + 0], state[8 * i + 1], state[8 * i + 2],
state[8 * i + 3], state[8 * i + 4], state[8 * i + 5],
state[8 * i + 6], state[8 * i + 7]);
}
for (i = 0; i < 8; ++i) {
BLAKE2_ROUND(state[8 * 0 + i], state[8 * 1 + i], state[8 * 2 + i],
state[8 * 3 + i], state[8 * 4 + i], state[8 * 5 + i],
state[8 * 6 + i], state[8 * 7 + i]);
}
for (i = 0; i < ARGON2_OWORDS_IN_BLOCK; i++) {
state[i] = _mm_xor_si128(state[i], block_XY[i]);
_mm_storeu_si128((__m128i *)next_block->v + i, state[i]);
}
}
static void next_addresses(block *address_block, block *input_block) {
/*Temporary zero-initialized blocks*/
__m128i zero_block[ARGON2_OWORDS_IN_BLOCK];
__m128i zero2_block[ARGON2_OWORDS_IN_BLOCK];
memset(zero_block, 0, sizeof(zero_block));
memset(zero2_block, 0, sizeof(zero2_block));
/*Increasing index counter*/
input_block->v[6]++;
/*First iteration of G*/
fill_block(zero_block, input_block, address_block, 0);
/*Second iteration of G*/
fill_block(zero2_block, address_block, address_block, 0);
}
void fill_segment(const argon2_instance_t *instance,
argon2_position_t position) {
block *ref_block = NULL, *curr_block = NULL;
block address_block, input_block;
uint64_t pseudo_rand, ref_index, ref_lane;
uint32_t prev_offset, curr_offset;
uint32_t starting_index, i;
__m128i state[64];
int data_independent_addressing;
if (instance == NULL) {
return;
}
data_independent_addressing =
(instance->type == Argon2_i) ||
(instance->type == Argon2_id && (position.pass == 0) &&
(position.slice < ARGON2_SYNC_POINTS / 2));
if (data_independent_addressing) {
init_block_value(&input_block, 0);
input_block.v[0] = position.pass;
input_block.v[1] = position.lane;
input_block.v[2] = position.slice;
input_block.v[3] = instance->memory_blocks;
input_block.v[4] = instance->passes;
input_block.v[5] = instance->type;
}
starting_index = 0;
if ((0 == position.pass) && (0 == position.slice)) {
starting_index = 2; /* we have already generated the first two blocks */
/* Don't forget to generate the first block of addresses: */
if (data_independent_addressing) {
next_addresses(&address_block, &input_block);
}
}
/* Offset of the current block */
curr_offset = position.lane * instance->lane_length +
position.slice * instance->segment_length + starting_index;
if (0 == curr_offset % instance->lane_length) {
/* Last block in this lane */
prev_offset = curr_offset + instance->lane_length - 1;
} else {
/* Previous block */
prev_offset = curr_offset - 1;
}
memcpy(state, ((instance->memory + prev_offset)->v), ARGON2_BLOCK_SIZE);
for (i = starting_index; i < instance->segment_length;
++i, ++curr_offset, ++prev_offset) {
/*1.1 Rotating prev_offset if needed */
if (curr_offset % instance->lane_length == 1) {
prev_offset = curr_offset - 1;
}
/* 1.2 Computing the index of the reference block */
/* 1.2.1 Taking pseudo-random value from the previous block */
if (data_independent_addressing) {
if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) {
next_addresses(&address_block, &input_block);
}
pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK];
} else {
pseudo_rand = instance->memory[prev_offset].v[0];
}
/* 1.2.2 Computing the lane of the reference block */
ref_lane = ((pseudo_rand >> 32)) % instance->lanes;
if ((position.pass == 0) && (position.slice == 0)) {
/* Can not reference other lanes yet */
ref_lane = position.lane;
}
/* 1.2.3 Computing the number of possible reference block within the
* lane.
*/
position.index = i;
ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF,
ref_lane == position.lane);
/* 2 Creating a new block */
ref_block =
instance->memory + instance->lane_length * ref_lane + ref_index;
curr_block = instance->memory + curr_offset;
if (ARGON2_VERSION_10 == instance->version) {
/* version 1.2.1 and earlier: overwrite, not XOR */
fill_block(state, ref_block, curr_block, 0);
} else {
if(0 == position.pass) {
fill_block(state, ref_block, curr_block, 0);
} else {
fill_block(state, ref_block, curr_block, 1);
}
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#ifndef ARGON2_OPT_H
#define ARGON2_OPT_H
#include "core.h"
#include <emmintrin.h>
/*
* Function fills a new memory block and optionally XORs the old block over the new one.
* Memory must be initialized.
* @param state Pointer to the just produced block. Content will be updated(!)
* @param ref_block Pointer to the reference block
* @param next_block Pointer to the block to be XORed over. May coincide with @ref_block
* @param with_xor Whether to XOR into the new block (1) or just overwrite (0)
* @pre all block pointers must be valid
*/
void fill_block(__m128i *s, const block *ref_block, block *next_block, int with_xor);
#endif /* ARGON2_OPT_H */

View File

@@ -0,0 +1,185 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "argon2.h"
#include "ref.h"
#include "blake2/blamka-round-ref.h"
#include "blake2/blake2-impl.h"
#include "blake2/blake2.h"
void fill_block(const block *prev_block, const block *ref_block,
block *next_block, int with_xor) {
block blockR, block_tmp;
unsigned i;
copy_block(&blockR, ref_block);
xor_block(&blockR, prev_block);
copy_block(&block_tmp, &blockR);
/* Now blockR = ref_block + prev_block and block_tmp = ref_block + prev_block */
if (with_xor) {
/* Saving the next block contents for XOR over: */
xor_block(&block_tmp, next_block);
/* Now blockR = ref_block + prev_block and
block_tmp = ref_block + prev_block + next_block */
}
/* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
(16,17,..31)... finally (112,113,...127) */
for (i = 0; i < 8; ++i) {
BLAKE2_ROUND_NOMSG(
blockR.v[16 * i], blockR.v[16 * i + 1], blockR.v[16 * i + 2],
blockR.v[16 * i + 3], blockR.v[16 * i + 4], blockR.v[16 * i + 5],
blockR.v[16 * i + 6], blockR.v[16 * i + 7], blockR.v[16 * i + 8],
blockR.v[16 * i + 9], blockR.v[16 * i + 10], blockR.v[16 * i + 11],
blockR.v[16 * i + 12], blockR.v[16 * i + 13], blockR.v[16 * i + 14],
blockR.v[16 * i + 15]);
}
/* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then
(2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */
for (i = 0; i < 8; i++) {
BLAKE2_ROUND_NOMSG(
blockR.v[2 * i], blockR.v[2 * i + 1], blockR.v[2 * i + 16],
blockR.v[2 * i + 17], blockR.v[2 * i + 32], blockR.v[2 * i + 33],
blockR.v[2 * i + 48], blockR.v[2 * i + 49], blockR.v[2 * i + 64],
blockR.v[2 * i + 65], blockR.v[2 * i + 80], blockR.v[2 * i + 81],
blockR.v[2 * i + 96], blockR.v[2 * i + 97], blockR.v[2 * i + 112],
blockR.v[2 * i + 113]);
}
copy_block(next_block, &block_tmp);
xor_block(next_block, &blockR);
}
static void next_addresses(block *address_block, block *input_block,
const block *zero_block) {
input_block->v[6]++;
fill_block(zero_block, input_block, address_block, 0);
fill_block(zero_block, address_block, address_block, 0);
}
void fill_segment(const argon2_instance_t *instance,
argon2_position_t position) {
block *ref_block = NULL, *curr_block = NULL;
block address_block, input_block, zero_block;
uint64_t pseudo_rand, ref_index, ref_lane;
uint32_t prev_offset, curr_offset;
uint32_t starting_index;
uint32_t i;
int data_independent_addressing;
if (instance == NULL) {
return;
}
data_independent_addressing =
(instance->type == Argon2_i) ||
(instance->type == Argon2_id && (position.pass == 0) &&
(position.slice < ARGON2_SYNC_POINTS / 2));
if (data_independent_addressing) {
init_block_value(&zero_block, 0);
init_block_value(&input_block, 0);
input_block.v[0] = position.pass;
input_block.v[1] = position.lane;
input_block.v[2] = position.slice;
input_block.v[3] = instance->memory_blocks;
input_block.v[4] = instance->passes;
input_block.v[5] = instance->type;
}
starting_index = 0;
if ((0 == position.pass) && (0 == position.slice)) {
starting_index = 2; /* we have already generated the first two blocks */
/* Don't forget to generate the first block of addresses: */
if (data_independent_addressing) {
next_addresses(&address_block, &input_block, &zero_block);
}
}
/* Offset of the current block */
curr_offset = position.lane * instance->lane_length +
position.slice * instance->segment_length + starting_index;
if (0 == curr_offset % instance->lane_length) {
/* Last block in this lane */
prev_offset = curr_offset + instance->lane_length - 1;
} else {
/* Previous block */
prev_offset = curr_offset - 1;
}
for (i = starting_index; i < instance->segment_length;
++i, ++curr_offset, ++prev_offset) {
/*1.1 Rotating prev_offset if needed */
if (curr_offset % instance->lane_length == 1) {
prev_offset = curr_offset - 1;
}
/* 1.2 Computing the index of the reference block */
/* 1.2.1 Taking pseudo-random value from the previous block */
if (data_independent_addressing) {
if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) {
next_addresses(&address_block, &input_block, &zero_block);
}
pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK];
} else {
pseudo_rand = instance->memory[prev_offset].v[0];
}
/* 1.2.2 Computing the lane of the reference block */
ref_lane = ((pseudo_rand >> 32)) % instance->lanes;
if ((position.pass == 0) && (position.slice == 0)) {
/* Can not reference other lanes yet */
ref_lane = position.lane;
}
/* 1.2.3 Computing the number of possible reference block within the
* lane.
*/
position.index = i;
ref_index = index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF,
ref_lane == position.lane);
/* 2 Creating a new block */
ref_block =
instance->memory + instance->lane_length * ref_lane + ref_index;
curr_block = instance->memory + curr_offset;
if (ARGON2_VERSION_10 == instance->version) {
/* version 1.2.1 and earlier: overwrite, not XOR */
fill_block(instance->memory + prev_offset, ref_block, curr_block, 0);
} else {
if(0 == position.pass) {
fill_block(instance->memory + prev_offset, ref_block,
curr_block, 0);
} else {
fill_block(instance->memory + prev_offset, ref_block,
curr_block, 1);
}
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#ifndef ARGON2_REF_H
#define ARGON2_REF_H
#include "core.h"
/*
* Function fills a new memory block and optionally XORs the old block over the new one.
* @next_block must be initialized.
* @param prev_block Pointer to the previous block
* @param ref_block Pointer to the reference block
* @param next_block Pointer to the block to be constructed
* @param with_xor Whether to XOR into the new block (1) or just overwrite (0)
* @pre all block pointers must be valid
*/
void fill_block(const block *prev_block, const block *ref_block,
block *next_block, int with_xor);
#endif /* ARGON2_REF_H */

View File

@@ -0,0 +1,53 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#include "thread.h"
#if defined(_WIN32)
#include <windows.h>
#endif
int argon2_thread_create(argon2_thread_handle_t *handle,
argon2_thread_func_t func, void *args) {
if (NULL == handle || func == NULL) {
return -1;
}
#if defined(_WIN32)
*handle = _beginthreadex(NULL, 0, func, args, 0, NULL);
return *handle != 0 ? 0 : -1;
#else
return pthread_create(handle, NULL, func, args);
#endif
}
int argon2_thread_join(argon2_thread_handle_t handle) {
#if defined(_WIN32)
if (WaitForSingleObject((HANDLE)handle, INFINITE) == WAIT_OBJECT_0) {
return CloseHandle((HANDLE)handle) != 0 ? 0 : -1;
}
return -1;
#else
return pthread_join(handle, NULL);
#endif
}
void argon2_thread_exit(void) {
#if defined(_WIN32)
_endthreadex(0);
#else
pthread_exit(NULL);
#endif
}

View File

@@ -0,0 +1,63 @@
/*
* Argon2 reference source code package - reference C implementations
*
* Copyright 2015
* Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
*
* You may use this work under the terms of a Creative Commons CC0 1.0
* License/Waiver or the Apache Public License 2.0, at your option. The terms of
* these licenses can be found at:
*
* - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
* - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of both of these licenses along with this
* software. If not, they may be obtained at the above URLs.
*/
#ifndef ARGON2_THREAD_H
#define ARGON2_THREAD_H
/*
Here we implement an abstraction layer for the simpĺe requirements
of the Argon2 code. We only require 3 primitives---thread creation,
joining, and termination---so full emulation of the pthreads API
is unwarranted. Currently we wrap pthreads and Win32 threads.
The API defines 2 types: the function pointer type,
argon2_thread_func_t,
and the type of the thread handle---argon2_thread_handle_t.
*/
#if defined(_WIN32)
#include <process.h>
typedef unsigned(__stdcall *argon2_thread_func_t)(void *);
typedef uintptr_t argon2_thread_handle_t;
#else
#include <pthread.h>
typedef void *(*argon2_thread_func_t)(void *);
typedef pthread_t argon2_thread_handle_t;
#endif
/* Creates a thread
* @param handle pointer to a thread handle, which is the output of this
* function. Must not be NULL.
* @param func A function pointer for the thread's entry point. Must not be
* NULL.
* @param args Pointer that is passed as an argument to @func. May be NULL.
* @return 0 if @handle and @func are valid pointers and a thread is successfuly
* created.
*/
int argon2_thread_create(argon2_thread_handle_t *handle,
argon2_thread_func_t func, void *args);
/* Waits for a thread to terminate
* @param handle Handle to a thread created with argon2_thread_create.
* @return 0 if @handle is a valid handle, and joining completed successfully.
*/
int argon2_thread_join(argon2_thread_handle_t handle);
/* Terminate the current thread. Must be run inside a thread created by
* argon2_thread_create.
*/
void argon2_thread_exit(void);
#endif

View File

@@ -1,16 +0,0 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := final-key
LOCAL_SRC_FILES := \
kpd_jni.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../sha $(LOCAL_PATH)/../aes
LOCAL_STATIC_LIBRARIES := aes sha
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)

View File

@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_SHA256")
include_directories(aes/)
include_directories(sha/)
add_library(
final-key SHARED
kpd_jni.c
aes/aescrypt.c
aes/aeskey.c
aes/aes_modes.c
aes/aestab.c
sha/hmac.c
sha/sha1.c
sha/sha2.c
)
find_library(log-lib log)
target_link_libraries(final-key ${log-lib})

View File

@@ -375,7 +375,8 @@ JNIEXPORT jint JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nGetCache
#define MASTER_KEY_SIZE 32
typedef struct _master_key {
uint32_t rounds, done[2];
uint64_t rounds;
uint32_t done[2];
pthread_mutex_t lock1, lock2; // these lock the two halves of the key material
uint8_t c_seed[MASTER_KEY_SIZE] __attribute__ ((aligned (16)));
uint8_t key1[MASTER_KEY_SIZE] __attribute__ ((aligned (16)));
@@ -437,7 +438,7 @@ void *generate_key_material(void *arg) {
return (void *)flip;
}
JNIEXPORT jbyteArray JNICALL Java_com_keepassdroid_crypto_finalkey_NativeFinalKey_nTransformMasterKey(JNIEnv *env, jobject this, jbyteArray seed, jbyteArray key, jint rounds) {
JNIEXPORT jbyteArray JNICALL Java_com_keepassdroid_crypto_finalkey_NativeFinalKey_nTransformMasterKey(JNIEnv *env, jobject this, jbyteArray seed, jbyteArray key, jlong rounds) {
master_key mk;
uint32_t flip;
pthread_t t1, t2;
@@ -459,7 +460,7 @@ JNIEXPORT jbyteArray JNICALL Java_com_keepassdroid_crypto_finalkey_NativeFinalKe
(*env)->ThrowNew(env, bad_arg, "TransformMasterKey: illegal number of encryption rounds");
return NULL;
}
mk.rounds = (uint32_t)rounds;
mk.rounds = (uint64_t)rounds;
mk.done[0] = mk.done[1] = 0;
if( pthread_mutex_init(&mk.lock1, NULL) != 0 ) {
(*env)->ThrowNew(env, bad_arg, "TransformMasterKey: failed to initialize the mutex for thread 1"); // FIXME: get a better exception class for this...

View File

@@ -4,7 +4,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle-experimental:0.9.0'
classpath 'com.android.tools.build:gradle:2.3.0'
}
}