mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Add full kdbx4 read support
Abstract out KeyDerivatino Add Argon2 and ChaCha20 support Move to standard gradle plugin
This commit is contained in:
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.externalNativeBuild
|
||||
54
app/app.iml
54
app/app.iml
@@ -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>
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
33
app/src/main/java/com/keepassdroid/utils/DateUtil.java
Normal file
33
app/src/main/java/com/keepassdroid/utils/DateUtil.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# Recursively sources all Android.mk files in subdirs:
|
||||
include $(call all-subdir-makefiles)
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
APP_MODULES := aes sha final-key
|
||||
APP_OPTIM := release
|
||||
APP_ABI := all
|
||||
4
app/src/main/jni/CMakeLists.txt
Normal file
4
app/src/main/jni/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
add_subdirectory(final_key)
|
||||
add_subdirectory(argon2)
|
||||
@@ -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)
|
||||
17
app/src/main/jni/argon2/CMakeLists.txt
Normal file
17
app/src/main/jni/argon2/CMakeLists.txt
Normal 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
|
||||
)
|
||||
193
app/src/main/jni/argon2/argon2_jni.c
Normal file
193
app/src/main/jni/argon2/argon2_jni.c
Normal 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;
|
||||
}
|
||||
434
app/src/main/jni/argon2/include/argon2.h
Normal file
434
app/src/main/jni/argon2/include/argon2.h
Normal 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
|
||||
22
app/src/main/jni/argon2/src/CMakeLists.txt
Normal file
22
app/src/main/jni/argon2/src/CMakeLists.txt
Normal 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})
|
||||
436
app/src/main/jni/argon2/src/argon2.c
Normal file
436
app/src/main/jni/argon2/src/argon2.c
Normal 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;
|
||||
}
|
||||
156
app/src/main/jni/argon2/src/blake2/blake2-impl.h
Normal file
156
app/src/main/jni/argon2/src/blake2/blake2-impl.h
Normal 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
|
||||
91
app/src/main/jni/argon2/src/blake2/blake2.h
Normal file
91
app/src/main/jni/argon2/src/blake2/blake2.h
Normal 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
|
||||
390
app/src/main/jni/argon2/src/blake2/blake2b.c
Normal file
390
app/src/main/jni/argon2/src/blake2/blake2b.c
Normal 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 */
|
||||
56
app/src/main/jni/argon2/src/blake2/blamka-round-ref.h
Normal file
56
app/src/main/jni/argon2/src/blake2/blamka-round-ref.h
Normal 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
|
||||
605
app/src/main/jni/argon2/src/core.c
Normal file
605
app/src/main/jni/argon2/src/core.c
Normal 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;
|
||||
}
|
||||
234
app/src/main/jni/argon2/src/core.h
Normal file
234
app/src/main/jni/argon2/src/core.h
Normal 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
|
||||
450
app/src/main/jni/argon2/src/encoding.c
Normal file
450
app/src/main/jni/argon2/src/encoding.c
Normal 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;
|
||||
}
|
||||
|
||||
57
app/src/main/jni/argon2/src/encoding.h
Normal file
57
app/src/main/jni/argon2/src/encoding.h
Normal 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
|
||||
186
app/src/main/jni/argon2/src/opt.c
Normal file
186
app/src/main/jni/argon2/src/opt.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
app/src/main/jni/argon2/src/opt.h
Normal file
35
app/src/main/jni/argon2/src/opt.h
Normal 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 */
|
||||
185
app/src/main/jni/argon2/src/ref.c
Normal file
185
app/src/main/jni/argon2/src/ref.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
app/src/main/jni/argon2/src/ref.h
Normal file
35
app/src/main/jni/argon2/src/ref.h
Normal 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 */
|
||||
53
app/src/main/jni/argon2/src/thread.c
Normal file
53
app/src/main/jni/argon2/src/thread.c
Normal 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
|
||||
}
|
||||
63
app/src/main/jni/argon2/src/thread.h
Normal file
63
app/src/main/jni/argon2/src/thread.h
Normal 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
|
||||
@@ -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)
|
||||
22
app/src/main/jni/final_key/CMakeLists.txt
Normal file
22
app/src/main/jni/final_key/CMakeLists.txt
Normal 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})
|
||||
@@ -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...
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user