diff --git a/.gitignore b/.gitignore index 98f4c142..a37bf85c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,10 @@ bin/ gen/ out/ +.cxx/ +ostsdk/src/main/jniLibs/ +app/src/obj/ +ostsdk/src/obj/ # Gradle files .gradle/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 631f6fd8..4fef5c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # OST Wallet SDK Changelog +## Version 2.3.8 +### Changes: +* Reduced recovery key generation time substantially by leveraging on NDK. + +### Bug Fix: +* In OstWalletSDK UI workflows progress bar crashes in background. + ## Version 2.3.7 ### Bug Fix: * Inaccurate error is thrown when application runs out of memory during recover device workflow. diff --git a/README.md b/README.md index d55cfefc..99186fd4 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ compileOptions { ``` dependencies { - implementation 'com.ost:ost-wallet-sdk-android:2.3.7' + implementation 'com.ost:ost-wallet-sdk-android:2.3.8' ... ... ... diff --git a/Samples/customloader/src/OstMockLoaderFragment.java b/Samples/customloader/src/OstMockLoaderFragment.java index 747d6e15..4c3854d5 100644 --- a/Samples/customloader/src/OstMockLoaderFragment.java +++ b/Samples/customloader/src/OstMockLoaderFragment.java @@ -3,15 +3,15 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.DialogFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; import com.ost.ostwallet.R; import com.ost.walletsdk.ui.loader.OstLoaderFragment; diff --git a/Samples/customloader/src/OstMockLoaderManager.java b/Samples/customloader/src/OstMockLoaderManager.java index dc385824..b4deb69c 100644 --- a/Samples/customloader/src/OstMockLoaderManager.java +++ b/Samples/customloader/src/OstMockLoaderManager.java @@ -22,6 +22,12 @@ public OstLoaderFragment getLoader(OstWorkflowContext.WORKFLOW_TYPE workflowType @Override public boolean waitForFinalization(OstWorkflowContext.WORKFLOW_TYPE workflowType) { + if (OstWorkflowContext.WORKFLOW_TYPE.ACTIVATE_USER.equals(workflowType)) { + return false; + } + if (OstWorkflowContext.WORKFLOW_TYPE.EXECUTE_TRANSACTION.equals(workflowType)) { + return false; + } return true; } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 5fa958a2..4ebe9932 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ buildscript { maven { url 'https://maven.fabric.io/public' } } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.5.0' classpath 'io.fabric.tools:gradle:1.+' // NOTE: Do not place your application dependencies here; they belong diff --git a/documentation/OstWalletUI.md b/documentation/OstWalletUI.md index ecdb80f3..30eab877 100644 --- a/documentation/OstWalletUI.md +++ b/documentation/OstWalletUI.md @@ -50,6 +50,13 @@ try { OstWalletUI.setThemeConfig(themeConfig) ``` +### Get Theme Config +Get currently applied theme config from sdk. + +```java +OstWalletUI.getThemeConfig() +``` + ### Set Content Config Content for OstWalletUI can be initialized by calling `setContentConfig` API. diff --git a/documentation/ThemeConfig.md b/documentation/ThemeConfig.md index 679600c9..a23fb509 100644 --- a/documentation/ThemeConfig.md +++ b/documentation/ThemeConfig.md @@ -120,6 +120,7 @@ The following UI components properties supported by navigation bar: | ---------------------- | :-----------------------------: | | bar logo | nav_bar_logo_image.asset_name | | bar tint color | navigation_bar.tint_color | +| bar title color | navigation_bar_header.tint_color | | close icon tint color | icons.close.tint_color | | close icon tint color | icons.back.tint_color | @@ -132,6 +133,36 @@ The following UI components properties supported by navigation bar: | empty_color | hex value(String) | | filled_color | hex value(String) | +### Cell Separator + + The following UI components properties supported by cell separator: + +| Configuration Keys | Type | +| -------------------- | :---------------- | +| color | hex value(String) | + + ### Link + + The following UI components properties supported by link: + +| Configuration Keys | Type | +| -------------------- | :---------------- | +| size | number | +| color | hex value(String) | +| system_font_weight | string | +| alignment | string | + + ### status + +The following UI components properties supported by status: + +| Configuration Keys | Type | +| -------------------- | :---------------- | +| size | number | +| color | hex value(String) | +| system_font_weight | string | +| alignment | string | + ## UI Components ![copy-framework-file](images/NavBar.png) @@ -140,4 +171,4 @@ The following UI components properties supported by navigation bar: ![copy-framework-file](images/Card.png) -![copy-framework-file](images/TextField.png) \ No newline at end of file +![copy-framework-file](images/TextField.png) diff --git a/documentation/images/TextField.png b/documentation/images/TextField.png new file mode 100644 index 00000000..4d89a911 Binary files /dev/null and b/documentation/images/TextField.png differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7550890f..c579a232 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,16 +1,6 @@ -# -# Copyright 2019 OST.com Inc -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -#Mon Feb 25 12:36:41 IST 2019 +#Tue Dec 17 03:00:32 IST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/ostsdk/build.gradle b/ostsdk/build.gradle index 8e1b9985..255eeae1 100644 --- a/ostsdk/build.gradle +++ b/ostsdk/build.gradle @@ -1,3 +1,5 @@ +import org.apache.tools.ant.taskdefs.condition.Os + /* * Copyright 2019 OST.com Inc * @@ -57,6 +59,36 @@ android { lintOptions { abortOnError false } + + task ndkBuild(type: Exec) { + def rootDir = project.rootDir + def localProperties = new File(rootDir, "local.properties") + Properties properties = new Properties() + localProperties.withInputStream { instr -> + properties.load(instr) + } + def ndkDir = properties.getProperty('ndk.dir') + + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + commandLine ndkDir + '\\ndk-build.cmd', + 'NDK_LIBS_OUT=main/jniLibs', + 'NDK_PROJECT_PATH=' + rootDir + '\\ostsdk\\src', + 'APP_BUILD_SCRIPT=jni/Android.mk', + '-C', + file('src').absolutePath + } else { + commandLine ndkDir + '/ndk-build', + 'NDK_LIBS_OUT=main/jniLibs', + 'NDK_PROJECT_PATH=' + rootDir + '/ostsdk/src', + 'APP_BUILD_SCRIPT=jni/Android.mk', + '-C', + file('src').absolutePath + } + } + + tasks.withType(JavaCompile) { + compileTask -> compileTask.dependsOn ndkBuild + } } dependencies { diff --git a/ostsdk/gradle.properties b/ostsdk/gradle.properties index 78d1a1a6..fa8adff0 100644 --- a/ostsdk/gradle.properties +++ b/ostsdk/gradle.properties @@ -22,9 +22,9 @@ # org.gradle.parallel=true #Increase version when publishing. -VERSION_NAME=2.3.7 +VERSION_NAME=2.3.8 #Increase version code when publishing. -VERSION_CODE=49 +VERSION_CODE=53 #Everything else. GROUP=com.ost diff --git a/ostsdk/src/jni/Android.mk b/ostsdk/src/jni/Android.mk new file mode 100644 index 00000000..e543ab46 --- /dev/null +++ b/ostsdk/src/jni/Android.mk @@ -0,0 +1,16 @@ + +LOCAL_MODULE := libscrypt +LOCAL_MODULE_FILENAME := libscrypt +LOCAL_PATH := $(NDK_PROJECT_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/scrypt/c/*.c) +LOCAL_C_INCLUDES := $(LOCAL_PATH)/scrypt/include/ + +LOCAL_CFLAGS := -std=c99 -Wall -O2 + +LOCAL_LDFLAGS := -shared +LOCAL_CFLAGS += -DHAVE_CONFIG_H -I $(LOCAL_PATH)/scrypt/include +CC := arm-linux-androideabi-gcc +LOCAL_CFLAGS += --sysroot=$(SYSROOT) + +include $(BUILD_SHARED_LIBRARY) \ No newline at end of file diff --git a/ostsdk/src/main/assets/ost-theme-config.json b/ostsdk/src/main/assets/ost-theme-config.json index 255bd3c4..1f1e4a1b 100644 --- a/ostsdk/src/main/assets/ost-theme-config.json +++ b/ostsdk/src/main/assets/ost-theme-config.json @@ -1,5 +1,4 @@ { - "nav_bar_logo_image": { "asset_name": "ost_nav_bar_logo" }, @@ -68,6 +67,28 @@ "system_font_weight": "medium" }, + "navigation_bar": { + "tint_color": "#ffffff" + }, + + "navigation_bar_header": { + "tint_color": "#438bad" + }, + + "icons": { + "close": { + "tint_color": "#438bad" + }, + "back":{ + "tint_color": "#438bad" + } + }, + + "pin_input": { + "empty_color": "#c7c7cc", + "filled_color": "#438bad" + }, + "edit_text": { "size": 15, "color": "#101010", @@ -77,25 +98,26 @@ "placeholder": { "size": 15, "color": "#888888", + "alignment": "left", "system_font_weight": "regular" } }, - "navigation_bar": { - "tint_color": "#ffffff" + "cell_separator": { + "color": "#DBDBDB" }, - "icons": { - "close": { - "tint_color": "#438bad" - }, - "back":{ - "tint_color": "#438bad" - } + "link": { + "size": 15, + "color": "#007aff", + "system_font_weight": "medium", + "alignment": "left" }, - "pin_input": { - "empty_color": "#c7c7cc", - "filled_color": "#438bad" + "status": { + "size": 15, + "color": "#0F9D58", + "system_font_weight": "regular", + "alignment": "left" } } \ No newline at end of file diff --git a/ostsdk/src/main/java/com/ost/walletsdk/codec/Base64.java b/ostsdk/src/main/java/com/ost/walletsdk/codec/Base64.java new file mode 100644 index 00000000..33d7791d --- /dev/null +++ b/ostsdk/src/main/java/com/ost/walletsdk/codec/Base64.java @@ -0,0 +1,157 @@ +// Copyright (C) 2011 - Will Glozer. All rights reserved. + +package com.ost.walletsdk.codec; + +import java.util.Arrays; + +/** + * High-performance base64 codec based on the algorithm used in Mikael Grev's MiG Base64. + * This implementation is designed to handle base64 without line splitting and with + * optional padding. Alternative character tables may be supplied to the {@code encode} + * and {@code decode} methods to implement modified base64 schemes. + * + * Decoding assumes correct input, the caller is responsible for ensuring that the input + * contains no invalid characters. + * + * @author Will Glozer + */ +public class Base64 { + private static final char[] encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); + private static final int[] decode = new int[128]; + private static final char pad = '='; + + static { + Arrays.fill(decode, -1); + for (int i = 0; i < encode.length; i++) { + decode[encode[i]] = i; + } + decode[pad] = 0; + } + + /** + * Decode base64 chars to bytes. + * + * @param chars Chars to encode. + * + * @return Decoded bytes. + */ + public static byte[] decode(char[] chars) { + return decode(chars, decode, pad); + } + + /** + * Encode bytes to base64 chars, with padding. + * + * @param bytes Bytes to encode. + * + * @return Encoded chars. + */ + public static char[] encode(byte[] bytes) { + return encode(bytes, encode, pad); + } + + /** + * Encode bytes to base64 chars, with optional padding. + * + * @param bytes Bytes to encode. + * @param padded Add padding to output. + * + * @return Encoded chars. + */ + public static char[] encode(byte[] bytes, boolean padded) { + return encode(bytes, encode, padded ? pad : 0); + } + + /** + * Decode base64 chars to bytes using the supplied decode table and padding + * character. + * + * @param src Base64 encoded data. + * @param table Decode table. + * @param pad Padding character. + * + * @return Decoded bytes. + */ + public static byte[] decode(char[] src, int[] table, char pad) { + int len = src.length; + + if (len == 0) return new byte[0]; + + int padCount = (src[len - 1] == pad ? (src[len - 2] == pad ? 2 : 1) : 0); + int bytes = (len * 6 >> 3) - padCount; + int blocks = (bytes / 3) * 3; + + byte[] dst = new byte[bytes]; + int si = 0, di = 0; + + while (di < blocks) { + int n = table[src[si++]] << 18 | table[src[si++]] << 12 | table[src[si++]] << 6 | table[src[si++]]; + dst[di++] = (byte) (n >> 16); + dst[di++] = (byte) (n >> 8); + dst[di++] = (byte) n; + } + + if (di < bytes) { + int n = 0; + switch (len - si) { + case 4: n |= table[src[si+3]]; + case 3: n |= table[src[si+2]] << 6; + case 2: n |= table[src[si+1]] << 12; + case 1: n |= table[src[si]] << 18; + } + for (int r = 16; di < bytes; r -= 8) { + dst[di++] = (byte) (n >> r); + } + } + + return dst; + } + + /** + * Encode bytes to base64 chars using the supplied encode table and with + * optional padding. + * + * @param src Bytes to encode. + * @param table Encoding table. + * @param pad Padding character, or 0 for no padding. + * + * @return Encoded chars. + */ + public static char[] encode(byte[] src, char[] table, char pad) { + int len = src.length; + + if (len == 0) return new char[0]; + + int blocks = (len / 3) * 3; + int chars = ((len - 1) / 3 + 1) << 2; + int tail = len - blocks; + if (pad == 0 && tail > 0) chars -= 3 - tail; + + char[] dst = new char[chars]; + int si = 0, di = 0; + + while (si < blocks) { + int n = (src[si++] & 0xff) << 16 | (src[si++] & 0xff) << 8 | (src[si++] & 0xff); + dst[di++] = table[(n >>> 18) & 0x3f]; + dst[di++] = table[(n >>> 12) & 0x3f]; + dst[di++] = table[(n >>> 6) & 0x3f]; + dst[di++] = table[n & 0x3f]; + } + + if (tail > 0) { + int n = (src[si] & 0xff) << 10; + if (tail == 2) n |= (src[++si] & 0xff) << 2; + + dst[di++] = table[(n >>> 12) & 0x3f]; + dst[di++] = table[(n >>> 6) & 0x3f]; + if (tail == 2) dst[di++] = table[n & 0x3f]; + + if (pad != 0) { + if (tail == 1) dst[di++] = pad; + dst[di] = pad; + } + } + + return dst; + } +} diff --git a/ostsdk/src/main/java/com/ost/walletsdk/crypto/PBKDF.java b/ostsdk/src/main/java/com/ost/walletsdk/crypto/PBKDF.java new file mode 100644 index 00000000..41dc92a7 --- /dev/null +++ b/ostsdk/src/main/java/com/ost/walletsdk/crypto/PBKDF.java @@ -0,0 +1,87 @@ +// Copyright (C) 2011 - Will Glozer. All rights reserved. + +package com.ost.walletsdk.crypto; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.security.GeneralSecurityException; +import static java.lang.System.arraycopy; + +/** + * An implementation of the Password-Based Key Derivation Function as specified + * in RFC 2898. + * + * @author Will Glozer + */ +public class PBKDF { + /** + * Implementation of PBKDF2 (RFC2898). + * + * @param alg HMAC algorithm to use. + * @param P Password. + * @param S Salt. + * @param c Iteration count. + * @param dkLen Intended length, in octets, of the derived key. + * + * @return The derived key. + * + * @throws GeneralSecurityException + */ + public static byte[] pbkdf2(String alg, byte[] P, byte[] S, int c, int dkLen) throws GeneralSecurityException { + Mac mac = Mac.getInstance(alg); + mac.init(new SecretKeySpec(P, alg)); + byte[] DK = new byte[dkLen]; + pbkdf2(mac, S, c, DK, dkLen); + return DK; + } + + /** + * Implementation of PBKDF2 (RFC2898). + * + * @param mac Pre-initialized {@link Mac} instance to use. + * @param S Salt. + * @param c Iteration count. + * @param DK Byte array that derived key will be placed in. + * @param dkLen Intended length, in octets, of the derived key. + * + * @throws GeneralSecurityException + */ + public static void pbkdf2(Mac mac, byte[] S, int c, byte[] DK, int dkLen) throws GeneralSecurityException { + int hLen = mac.getMacLength(); + + if (dkLen > (Math.pow(2, 32) - 1) * hLen) { + throw new GeneralSecurityException("Requested key length too long"); + } + + byte[] U = new byte[hLen]; + byte[] T = new byte[hLen]; + byte[] block1 = new byte[S.length + 4]; + + int l = (int) Math.ceil((double) dkLen / hLen); + int r = dkLen - (l - 1) * hLen; + + arraycopy(S, 0, block1, 0, S.length); + + for (int i = 1; i <= l; i++) { + block1[S.length + 0] = (byte) (i >> 24 & 0xff); + block1[S.length + 1] = (byte) (i >> 16 & 0xff); + block1[S.length + 2] = (byte) (i >> 8 & 0xff); + block1[S.length + 3] = (byte) (i >> 0 & 0xff); + + mac.update(block1); + mac.doFinal(U, 0); + arraycopy(U, 0, T, 0, hLen); + + for (int j = 1; j < c; j++) { + mac.update(U); + mac.doFinal(U, 0); + + for (int k = 0; k < hLen; k++) { + T[k] ^= U[k]; + } + } + + arraycopy(T, 0, DK, (i - 1) * hLen, (i == l ? r : hLen)); + } + } +} diff --git a/ostsdk/src/main/java/com/ost/walletsdk/crypto/SCrypt.java b/ostsdk/src/main/java/com/ost/walletsdk/crypto/SCrypt.java new file mode 100644 index 00000000..579fd8aa --- /dev/null +++ b/ostsdk/src/main/java/com/ost/walletsdk/crypto/SCrypt.java @@ -0,0 +1,222 @@ +// Copyright (C) 2011 - Will Glozer. All rights reserved. + +package com.ost.walletsdk.crypto; + +import android.util.Log; + +import com.ost.walletsdk.jni.*; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.security.GeneralSecurityException; + +import static java.lang.Integer.MAX_VALUE; +import static java.lang.System.arraycopy; + +/** + * An implementation of the scrypt + * key derivation function. This class will attempt to load a native library + * containing the optimized C implementation from + * http://www.tarsnap.com/scrypt.html and + * fall back to the pure Java version if that fails. + * + * @author Will Glozer + */ +public class SCrypt { + private static final boolean native_library_loaded; + + static { + LibraryLoader loader = LibraryLoaders.loader(); + native_library_loaded = loader.load("scrypt", true); + } + + /** + * Implementation of the scrypt KDF. + * Calls the native implementation {@link #scryptN} when the native library was successfully + * loaded, otherwise calls {@link #scryptJ}. + * + * @param passwd Password. + * @param salt Salt. + * @param N CPU cost parameter. + * @param r Memory cost parameter. + * @param p Parallelization parameter. + * @param dkLen Intended length of the derived key. + * + * @return The derived key. + * + * @throws GeneralSecurityException when HMAC_SHA256 is not available. + */ + public static byte[] scrypt(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen) throws GeneralSecurityException { + if ( native_library_loaded ) { + Log.i("OstSCrypt", "Using Native SCrypt method :D"); + return scryptN(passwd, salt, N, r, p, dkLen); + } else { + Log.i("OstSCrypt", "Using Java SCrypt method :("); + return scryptJ(passwd, salt, N, r, p, dkLen); + } + } + + /** + * Native C implementation of the scrypt KDF using + * the code from http://www.tarsnap.com/scrypt.html. + * + * @param passwd Password. + * @param salt Salt. + * @param N CPU cost parameter. + * @param r Memory cost parameter. + * @param p Parallelization parameter. + * @param dkLen Intended length of the derived key. + * + * @return The derived key. + */ + public static native byte[] scryptN(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen); + + /** + * Pure Java implementation of the scrypt KDF. + * + * @param passwd Password. + * @param salt Salt. + * @param N CPU cost parameter. + * @param r Memory cost parameter. + * @param p Parallelization parameter. + * @param dkLen Intended length of the derived key. + * + * @return The derived key. + * + * @throws GeneralSecurityException when HMAC_SHA256 is not available. + */ + public static byte[] scryptJ(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen) throws GeneralSecurityException { + if (N < 2 || (N & (N - 1)) != 0) throw new IllegalArgumentException("N must be a power of 2 greater than 1"); + + if (N > MAX_VALUE / 128 / r) throw new IllegalArgumentException("Parameter N is too large"); + if (r > MAX_VALUE / 128 / p) throw new IllegalArgumentException("Parameter r is too large"); + + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(passwd, "HmacSHA256")); + + byte[] DK = new byte[dkLen]; + + byte[] B = new byte[128 * r * p]; + byte[] XY = new byte[256 * r]; + byte[] V = new byte[128 * r * N]; + int i; + + PBKDF.pbkdf2(mac, salt, 1, B, p * 128 * r); + + for (i = 0; i < p; i++) { + smix(B, i * 128 * r, r, N, V, XY); + } + + PBKDF.pbkdf2(mac, B, 1, DK, dkLen); + + return DK; + } + + public static void smix(byte[] B, int Bi, int r, int N, byte[] V, byte[] XY) { + int Xi = 0; + int Yi = 128 * r; + int i; + + arraycopy(B, Bi, XY, Xi, 128 * r); + + for (i = 0; i < N; i++) { + arraycopy(XY, Xi, V, i * (128 * r), 128 * r); + blockmix_salsa8(XY, Xi, Yi, r); + } + + for (i = 0; i < N; i++) { + int j = integerify(XY, Xi, r) & (N - 1); + blockxor(V, j * (128 * r), XY, Xi, 128 * r); + blockmix_salsa8(XY, Xi, Yi, r); + } + + arraycopy(XY, Xi, B, Bi, 128 * r); + } + + public static void blockmix_salsa8(byte[] BY, int Bi, int Yi, int r) { + byte[] X = new byte[64]; + int i; + + arraycopy(BY, Bi + (2 * r - 1) * 64, X, 0, 64); + + for (i = 0; i < 2 * r; i++) { + blockxor(BY, i * 64, X, 0, 64); + salsa20_8(X); + arraycopy(X, 0, BY, Yi + (i * 64), 64); + } + + for (i = 0; i < r; i++) { + arraycopy(BY, Yi + (i * 2) * 64, BY, Bi + (i * 64), 64); + } + + for (i = 0; i < r; i++) { + arraycopy(BY, Yi + (i * 2 + 1) * 64, BY, Bi + (i + r) * 64, 64); + } + } + + public static int R(int a, int b) { + return (a << b) | (a >>> (32 - b)); + } + + public static void salsa20_8(byte[] B) { + int[] B32 = new int[16]; + int[] x = new int[16]; + int i; + + for (i = 0; i < 16; i++) { + B32[i] = (B[i * 4 + 0] & 0xff) << 0; + B32[i] |= (B[i * 4 + 1] & 0xff) << 8; + B32[i] |= (B[i * 4 + 2] & 0xff) << 16; + B32[i] |= (B[i * 4 + 3] & 0xff) << 24; + } + + arraycopy(B32, 0, x, 0, 16); + + for (i = 8; i > 0; i -= 2) { + x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); + x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); + x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); + x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); + x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); + x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); + x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); + x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); + x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); + x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); + x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); + x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); + x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); + x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); + x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); + x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); + } + + for (i = 0; i < 16; ++i) B32[i] = x[i] + B32[i]; + + for (i = 0; i < 16; i++) { + B[i * 4 + 0] = (byte) (B32[i] >> 0 & 0xff); + B[i * 4 + 1] = (byte) (B32[i] >> 8 & 0xff); + B[i * 4 + 2] = (byte) (B32[i] >> 16 & 0xff); + B[i * 4 + 3] = (byte) (B32[i] >> 24 & 0xff); + } + } + + public static void blockxor(byte[] S, int Si, byte[] D, int Di, int len) { + for (int i = 0; i < len; i++) { + D[Di + i] ^= S[Si + i]; + } + } + + public static int integerify(byte[] B, int Bi, int r) { + int n; + + Bi += (2 * r - 1) * 64; + + n = (B[Bi + 0] & 0xff) << 0; + n |= (B[Bi + 1] & 0xff) << 8; + n |= (B[Bi + 2] & 0xff) << 16; + n |= (B[Bi + 3] & 0xff) << 24; + + return n; + } +} diff --git a/ostsdk/src/main/java/com/ost/walletsdk/crypto/SCryptUtil.java b/ostsdk/src/main/java/com/ost/walletsdk/crypto/SCryptUtil.java new file mode 100644 index 00000000..8cdb50c3 --- /dev/null +++ b/ostsdk/src/main/java/com/ost/walletsdk/crypto/SCryptUtil.java @@ -0,0 +1,112 @@ +// Copyright (C) 2011 - Will Glozer. All rights reserved. + +package com.ost.walletsdk.crypto; + +import java.io.UnsupportedEncodingException; +import java.security.GeneralSecurityException; +import java.security.SecureRandom; + +import static com.ost.walletsdk.codec.Base64.*; + +/** + * Simple {@link SCrypt} interface for hashing passwords using the + * scrypt key derivation function + * and comparing a plain text password to a hashed one. The hashed output is an + * extended implementation of the Modular Crypt Format that also includes the scrypt + * algorithm parameters. + * + * Format: $s0$PARAMS$SALT$KEY. + * + *
+ *
PARAMS
32-bit hex integer containing log2(N) (16 bits), r (8 bits), and p (8 bits)
+ *
SALT
base64-encoded salt
+ *
KEY
base64-encoded derived key
+ *
+ * + * s0 identifies version 0 of the scrypt format, using a 128-bit salt and 256-bit derived key. + * + * @author Will Glozer + */ +public class SCryptUtil { + /** + * Hash the supplied plaintext password and generate output in the format described + * in {@link SCryptUtil}. + * + * @param passwd Password. + * @param N CPU cost parameter. + * @param r Memory cost parameter. + * @param p Parallelization parameter. + * + * @return The hashed password. + */ + public static String scrypt(String passwd, int N, int r, int p) { + try { + byte[] salt = new byte[16]; + SecureRandom.getInstance("SHA1PRNG").nextBytes(salt); + + byte[] derived = SCrypt.scrypt(passwd.getBytes("UTF-8"), salt, N, r, p, 32); + + String params = Long.toString(log2(N) << 16L | r << 8 | p, 16); + + StringBuilder sb = new StringBuilder((salt.length + derived.length) * 2); + sb.append("$s0$").append(params).append('$'); + sb.append(encode(salt)).append('$'); + sb.append(encode(derived)); + + return sb.toString(); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("JVM doesn't support UTF-8?"); + } catch (GeneralSecurityException e) { + throw new IllegalStateException("JVM doesn't support SHA1PRNG or HMAC_SHA256?"); + } + } + + /** + * Compare the supplied plaintext password to a hashed password. + * + * @param passwd Plaintext password. + * @param hashed scrypt hashed password. + * + * @return true if passwd matches hashed value. + */ + public static boolean check(String passwd, String hashed) { + try { + String[] parts = hashed.split("\\$"); + + if (parts.length != 5 || !parts[1].equals("s0")) { + throw new IllegalArgumentException("Invalid hashed value"); + } + + long params = Long.parseLong(parts[2], 16); + byte[] salt = decode(parts[3].toCharArray()); + byte[] derived0 = decode(parts[4].toCharArray()); + + int N = (int) Math.pow(2, params >> 16 & 0xffff); + int r = (int) params >> 8 & 0xff; + int p = (int) params & 0xff; + + byte[] derived1 = SCrypt.scrypt(passwd.getBytes("UTF-8"), salt, N, r, p, 32); + + if (derived0.length != derived1.length) return false; + + int result = 0; + for (int i = 0; i < derived0.length; i++) { + result |= derived0[i] ^ derived1[i]; + } + return result == 0; + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("JVM doesn't support UTF-8?"); + } catch (GeneralSecurityException e) { + throw new IllegalStateException("JVM doesn't support SHA1PRNG or HMAC_SHA256?"); + } + } + + private static int log2(int n) { + int log = 0; + if ((n & 0xffff0000 ) != 0) { n >>>= 16; log = 16; } + if (n >= 256) { n >>>= 8; log += 8; } + if (n >= 16 ) { n >>>= 4; log += 4; } + if (n >= 4 ) { n >>>= 2; log += 2; } + return log + (n >>> 1); + } +} diff --git a/ostsdk/src/main/java/com/ost/walletsdk/ecKeyInteracts/InternalKeyManager.java b/ostsdk/src/main/java/com/ost/walletsdk/ecKeyInteracts/InternalKeyManager.java index fecbe8c1..ca26f55b 100644 --- a/ostsdk/src/main/java/com/ost/walletsdk/ecKeyInteracts/InternalKeyManager.java +++ b/ostsdk/src/main/java/com/ost/walletsdk/ecKeyInteracts/InternalKeyManager.java @@ -13,6 +13,7 @@ import android.text.TextUtils; import android.util.Log; +import com.ost.walletsdk.crypto.SCrypt; import com.ost.walletsdk.OstConfigs; import com.ost.walletsdk.OstSdk; import com.ost.walletsdk.ecKeyInteracts.impls.OstAndroidSecureStorage; @@ -30,7 +31,8 @@ import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; import org.bouncycastle.crypto.params.KeyParameter; -import com.ost.walletsdk.utils.scrypt.SCrypt; + + import org.web3j.crypto.Bip32ECKeyPair; import org.web3j.crypto.Credentials; import org.web3j.crypto.ECKeyPair; @@ -165,17 +167,48 @@ String signBytesWithApiSigner(byte[] dataToSign) { OstSecureKey osk = null; byte[] key = null; + byte[] dataToDecrypt = null; ECKeyPair ecKeyPair; try { osk = metaRepository.getByKey(apiKeyId); - key = OstAndroidSecureStorage.getInstance(OstSdk.getContext(), mUserId).decrypt(osk.getData()); + dataToDecrypt = osk.getData(); + + key = OstAndroidSecureStorage.getInstance(OstSdk.getContext(), mUserId).decrypt( dataToDecrypt ); + ecKeyPair = ECKeyPair.create(key); //Sign the data Sign.SignatureData signatureData = Sign.signPrefixedMessage(dataToSign, ecKeyPair); return signatureDataToString(signatureData); - } catch (Exception ex) { - Log.e(TAG, "m_s_ikm_sbwps: Unexpected Exception", ex); - return null; + } catch (Throwable th) { + OstError ostError = OstError.SdkError("m_s_ikm_sbwps_1", th); + + ostError.addErrorInfo("m_s_ikm_sbwps_1.apiKeyAddress", apiKeyAddress); + ostError.addErrorInfo("m_s_ikm_sbwps_1.deviceKeyAddress", mKeyMetaStruct.getDeviceAddress() ); + ostError.addErrorInfo("m_s_ikm_sbwps_1.apiKeyId", apiKeyId); + ostError.addErrorInfo("m_s_ikm_sbwps_1.mUserId", mUserId); + ostError.addErrorInfo("m_s_ikm_sbwps_1.isKeyPairGenerated", String.valueOf(OstAndroidSecureStorage.isKeyPairGenerated)); + + String isKeyNull = "true"; + String keyLength = "0"; + if ( null != key ) { + isKeyNull = "false"; + keyLength = String.valueOf( key.length ); + } + ostError.addErrorInfo("m_s_ikm_sbwps_1.isKeyNull", isKeyNull ); + ostError.addErrorInfo("m_s_ikm_sbwps_1.keyLength", keyLength ); + + + String isDataToDecryptNull = "true"; + String dataToDecryptLength = "0"; + if ( null != dataToDecrypt ) { + isDataToDecryptNull = "false"; + dataToDecryptLength = String.valueOf( dataToDecrypt.length ); + } + ostError.addErrorInfo("m_s_ikm_sbwps_1.isDataToDecryptNull", isDataToDecryptNull ); + ostError.addErrorInfo("m_s_ikm_sbwps_1.dataToDecryptLength", dataToDecryptLength ); + + Log.e(TAG, "m_s_ikm_sbwps_1: Unexpected Exception", th); + throw ostError; } finally { clearBytes(key); ecKeyPair = null; @@ -666,8 +699,27 @@ private ECKeyPair createRecoveryKey(UserPassphrase userPassphrase, byte[] salt) } catch (OutOfMemoryError error) { throw new OstError("c_ikm_crk_2", ErrorCode.OUT_OF_MEMORY_ERROR); } catch (Throwable th) { - //Suppress Error. - throw new OstError("c_ikm_crk_1", ErrorCode.RECOVERY_KEY_GENERATION_FAILED); + OstError ostError = OstError.SdkError("c_ikm_crk_1", th); + + String passphraseLength = "null"; + if (null != passphrase) { + passphraseLength = String.valueOf(passphrase.length); + } + ostError.addErrorInfo("c_ikm_crk_1.passphrase.length", passphraseLength); + + String seedLength = "null"; + if (null != seed) { + seedLength = String.valueOf(seed.length); + } + ostError.addErrorInfo("c_ikm_crk_1.seed.length", seedLength ); + + String saltLength = "null"; + if (null != salt) { + saltLength = String.valueOf(salt.length); + } + ostError.addErrorInfo("c_ikm_crk_1.salt.length", saltLength ); + + throw ostError; } finally { //Clear the seed. clearBytes(seed); @@ -712,32 +764,63 @@ String signDataWithRecoveryKey(String hexStringToSign, UserPassphrase passphrase } ECKeyPair ecKeyPair = null; + String recoveryOwnerAddress = null; + String expectedRecoveryOwnerAddress = null; + Sign.SignatureData signatureData = null; try { OstUser ostUser = OstUser.getById(mUserId); - String recoveryOwnerAddress = ostUser.getRecoveryOwnerAddress(); + recoveryOwnerAddress = ostUser.getRecoveryOwnerAddress(); // Generate ecKeyPair. ecKeyPair = createRecoveryKey(passphrase, salt); // Validate recoveryOwnerAddress. - String expectedRecoveryOwnerAddress = Credentials.create(ecKeyPair).getAddress(); + expectedRecoveryOwnerAddress = Credentials.create(ecKeyPair).getAddress(); if ( !expectedRecoveryOwnerAddress.equalsIgnoreCase(recoveryOwnerAddress) ) { // Note that user passphrase is invalid. userPassphraseInvalidated(); - //Do not throw. Just return null. - return null; + throw new OstError("m_s_ikm_sdwrk_2", ErrorCode.INVALID_USER_PASSPHRASE); } // Note that user passphrase is valid. userPassphraseValidated(); // Sign the data. - Sign.SignatureData signatureData = Sign.signMessage(Numeric.hexStringToByteArray(hexStringToSign), ecKeyPair, false); + signatureData = Sign.signMessage(Numeric.hexStringToByteArray(hexStringToSign), ecKeyPair, false); return Numeric.toHexString(signatureData.getR()) + Numeric.cleanHexPrefix(Numeric.toHexString(signatureData.getS())) + String.format("%02x", (signatureData.getV())); - } catch (OstError ostError) { - throw ostError; } catch (Throwable th) { - //Supress it. - throw new OstError("c_ikm_sdwrk_1", ErrorCode.FAILED_TO_SIGN_DATA); + OstError ostError = OstError.SdkError("m_s_ikm_sdwrk_1", th); + + if (null == recoveryOwnerAddress) { + recoveryOwnerAddress = "null"; + } + ostError.addErrorInfo("m_s_ikm_sdwrk_1.recoveryOwnerAddress", recoveryOwnerAddress); + + if (null == expectedRecoveryOwnerAddress) { + expectedRecoveryOwnerAddress = "null"; + } + ostError.addErrorInfo("m_s_ikm_sdwrk_1.generatedRecoveryOwnerAddress", expectedRecoveryOwnerAddress); + + String saltLength = "null"; + if (null != salt){ + saltLength = String.valueOf(salt.length); + } + ostError.addErrorInfo("m_s_ikm_sdwrk_1.salt.length", saltLength); + + String signatureString = "null"; + if (null != signatureData) { + String rLength = "null"; + if (null != signatureData.getR()) { + rLength = String.valueOf(signatureData.getR().length); + } + String sLength = "null"; + if (null != signatureData.getS()) { + sLength = String.valueOf(signatureData.getS().length); + } + signatureString = String.format("R.%s S.%s", rLength, sLength); + } + ostError.addErrorInfo("m_s_ikm_sdwrk_1.signatureData.length", signatureString); + + throw ostError; } finally { if ( null == ecKeyPair ) { clearBytes(salt); diff --git a/ostsdk/src/main/java/com/ost/walletsdk/ecKeyInteracts/impls/OstAndroidSecureStorage.java b/ostsdk/src/main/java/com/ost/walletsdk/ecKeyInteracts/impls/OstAndroidSecureStorage.java index 520827b6..0a9ea3d2 100644 --- a/ostsdk/src/main/java/com/ost/walletsdk/ecKeyInteracts/impls/OstAndroidSecureStorage.java +++ b/ostsdk/src/main/java/com/ost/walletsdk/ecKeyInteracts/impls/OstAndroidSecureStorage.java @@ -20,6 +20,7 @@ import com.ost.walletsdk.annotations.NonNull; import com.ost.walletsdk.ecKeyInteracts.OstSecureStorage; +import com.ost.walletsdk.workflows.errors.OstError; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; @@ -47,6 +48,7 @@ public class OstAndroidSecureStorage implements OstSecureStorage { private static final String RSA = "RSA"; private final Context mContext; private String mKeyAlias; + public static boolean isKeyPairGenerated = false; private KeyStore mKeyStore; @@ -57,11 +59,19 @@ private OstAndroidSecureStorage(@NonNull Context context, @NonNull String keyAli mKeyStore = KeyStore.getInstance(ANDROID_KEY_STORE); mKeyStore.load(null); - if (null == getKey()) { + KeyPair keyPair = null; + try { + keyPair = getKey(); + } catch (OstError err) { + //This is expected. Ignore it. + } + + if (null == keyPair) { generateKey(); } } catch (Exception ex) { Log.e(TAG, "Exception faced while build object" + ex.getMessage(), ex.getCause()); + throw OstError.SdkError("oass_constructor_1", ex); } } @@ -87,10 +97,11 @@ public byte[] decrypt(byte[] data) { Cipher cipher = Cipher.getInstance(TRANSFORMATION_ASYMMETRIC); cipher.init(Cipher.DECRYPT_MODE, Objects.requireNonNull(getKey()).getPrivate()); return cipher.doFinal(data); - } catch (Exception ex) { - Log.e(TAG, "Exception faced while decryption " + ex.getMessage(), ex.getCause()); + } catch (Throwable th) { + OstError ostError = OstError.SdkError("oass_d_1", th); + Log.e(TAG, "Exception faced while decryption " + th.getMessage(), th.getCause()); + throw ostError; } - return null; } private void generateKey() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { @@ -103,7 +114,7 @@ private void generateKey() throws NoSuchProviderException, NoSuchAlgorithmExcept } keyPairGenerator.initialize(algorithmParameterSpec); keyPairGenerator.genKeyPair(); - + isKeyPairGenerated = true; } @RequiresApi(Build.VERSION_CODES.M) @@ -132,17 +143,33 @@ private KeyPairGeneratorSpec initGeneratorWithKeyPairGeneratorSpec() { } private KeyPair getKey() { + PrivateKey privateKey = null; + Certificate keyStoreCertificate = null; + PublicKey publicKey = null; try { - PrivateKey privateKey = (PrivateKey) mKeyStore.getKey(mKeyAlias, null); - Certificate keyStoreCertificate = mKeyStore.getCertificate(mKeyAlias); - PublicKey publicKey = (null == keyStoreCertificate ? null : keyStoreCertificate.getPublicKey()); - if (null == privateKey || null == publicKey) { - return null; + privateKey = (PrivateKey) mKeyStore.getKey(mKeyAlias, null); + keyStoreCertificate = mKeyStore.getCertificate(mKeyAlias); + publicKey = (null == keyStoreCertificate ? null : keyStoreCertificate.getPublicKey()); + + if (null == privateKey || null == keyStoreCertificate || null == publicKey) { + throw new Error("Failed to get private key from key store"); } return new KeyPair(publicKey, privateKey); - } catch (Exception ex) { + } catch (Throwable th) { + OstError ostError = OstError.SdkError("oass_kp_gk_1", th); + ostError.addErrorInfo("oass_kp_gk_1.key_alias", mKeyAlias); + + String isPrivateKeyNull = null == privateKey? "true": "false"; + ostError.addErrorInfo("oass_kp_gk_1.isPrivateKeyNull", isPrivateKeyNull); + + String isPublicKeyNull = null == publicKey? "true": "false"; + ostError.addErrorInfo("oass_kp_gk_1.isPublicKeyNull", isPublicKeyNull); + + String isKeyStoreCertificateNull = null == keyStoreCertificate? "true": "false"; + ostError.addErrorInfo("oass_kp_gk_1.isKeyStoreCertificateNull", isKeyStoreCertificateNull); + Log.d(TAG, "Exception faced in getId "); + throw ostError; } - return null; } } \ No newline at end of file diff --git a/ostsdk/src/main/java/com/ost/walletsdk/jni/JarLibraryLoader.java b/ostsdk/src/main/java/com/ost/walletsdk/jni/JarLibraryLoader.java new file mode 100644 index 00000000..fecb8f7d --- /dev/null +++ b/ostsdk/src/main/java/com/ost/walletsdk/jni/JarLibraryLoader.java @@ -0,0 +1,145 @@ +// Copyright (C) 2011 - Will Glozer. All rights reserved. + +package com.ost.walletsdk.jni; + +import java.io.*; +import java.security.CodeSource; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * A native library loader that will extract and load a shared library contained in a jar. + * This loader will attempt to detect the {@link Platform platform} (CPU architecture and OS) + * it is running on and load the appropriate shared library. + * + * Given a library path and name this loader looks for a native library with path + * [libraryPath]/[arch]/[os]/lib[name].[ext] + * + * @author Will Glozer + */ +public class JarLibraryLoader implements LibraryLoader { + private final CodeSource codeSource; + private final String libraryPath; + + /** + * Initialize a new instance that looks for shared libraries located in the same jar + * as this class and with a path starting with {@code lib}. + */ + public JarLibraryLoader() { + this(JarLibraryLoader.class.getProtectionDomain().getCodeSource(), "lib"); + } + + /** + * Initialize a new instance that looks for shared libraries located in the specified + * directory of the supplied code source. + * + * @param codeSource Code source containing shared libraries. + * @param libraryPath Path prefix of shared libraries. + */ + public JarLibraryLoader(CodeSource codeSource, String libraryPath) { + this.codeSource = codeSource; + this.libraryPath = libraryPath; + } + + /** + * Load a shared library, and optionally verify the jar signatures. + * + * @param name Name of the library to load. + * @param verify Verify the jar file if signed. + * + * @return true if the library was successfully loaded. + */ + public boolean load(String name, boolean verify) { + boolean loaded = false; + + try { + Platform platform = Platform.detect(); + JarFile jar = new JarFile(codeSource.getLocation().getPath(), verify); + try { + for (String path : libCandidates(platform, name)) { + JarEntry entry = jar.getJarEntry(path); + if (entry == null) continue; + + File lib = extract(name, jar.getInputStream(entry)); + System.load(lib.getAbsolutePath()); + lib.delete(); + + loaded = true; + break; + } + } finally { + jar.close(); + } + } catch (Throwable e) { + loaded = false; + } + + return loaded; + } + + /** + * Extract a jar entry to a temp file. + * + * @param name Name prefix for temp file. + * @param is Jar entry input stream. + * + * @return A temporary file. + * + * @throws IOException when an IO error occurs. + */ + private static File extract(String name, InputStream is) throws IOException { + byte[] buf = new byte[4096]; + int len; + + File lib = File.createTempFile(name, "lib"); + FileOutputStream os = new FileOutputStream(lib); + + try { + while ((len = is.read(buf)) > 0) { + os.write(buf, 0, len); + } + } catch (IOException e) { + lib.delete(); + throw e; + } finally { + os.close(); + is.close(); + } + + return lib; + } + + /** + * Generate a list of candidate libraries for the supplied library name and suitable + * for the current platform. + * + * @param platform Current platform. + * @param name Library name. + * + * @return List of potential library names. + */ + private List libCandidates(Platform platform, String name) { + List candidates = new ArrayList(); + StringBuilder sb = new StringBuilder(); + + sb.append(libraryPath).append("/"); + sb.append(platform.arch).append("/"); + sb.append(platform.os).append("/"); + sb.append("lib").append(name); + + switch (platform.os) { + case darwin: + candidates.add(sb + ".dylib"); + candidates.add(sb + ".jnilib"); + break; + case linux: + case freebsd: + candidates.add(sb + ".so"); + break; + } + + return candidates; + } +} diff --git a/ostsdk/src/main/java/com/ost/walletsdk/jni/LibraryLoader.java b/ostsdk/src/main/java/com/ost/walletsdk/jni/LibraryLoader.java new file mode 100644 index 00000000..cd60feec --- /dev/null +++ b/ostsdk/src/main/java/com/ost/walletsdk/jni/LibraryLoader.java @@ -0,0 +1,21 @@ +// Copyright (C) 2011 - Will Glozer. All rights reserved. + +package com.ost.walletsdk.jni; + +/** + * A {@code LibraryLoader} attempts to load the appropriate native library + * for the current platform. + * + * @author Will Glozer + */ +public interface LibraryLoader { + /** + * Load a native library, and optionally verify any signatures. + * + * @param name Name of the library to load. + * @param verify Verify signatures if signed. + * + * @return true if the library was successfully loaded. + */ + boolean load(String name, boolean verify); +} diff --git a/ostsdk/src/main/java/com/ost/walletsdk/jni/LibraryLoaders.java b/ostsdk/src/main/java/com/ost/walletsdk/jni/LibraryLoaders.java new file mode 100644 index 00000000..dfe08709 --- /dev/null +++ b/ostsdk/src/main/java/com/ost/walletsdk/jni/LibraryLoaders.java @@ -0,0 +1,34 @@ +// Copyright (C) 2011 - Will Glozer. All rights reserved. + +package com.ost.walletsdk.jni; + +/** + * {@code LibraryLoaders} will create the appropriate {@link LibraryLoader} for + * the VM it is running on. + * + * The system property {@code com.ost.walletsdk.jni.loader} may be used to override + * loader auto-detection, or to disable loading native libraries entirely via use + * of the nil loader. + * + * @author Will Glozer + */ +public class LibraryLoaders { + /** + * Create a new {@link LibraryLoader} for the current VM. + * + * @return the loader. + */ + public static LibraryLoader loader() { + String type = System.getProperty("com.ost.walletsdk.jni.loader"); + + if (type != null) { + if (type.equals("sys")) return new SysLibraryLoader(); + if (type.equals("nil")) return new NilLibraryLoader(); + if (type.equals("jar")) return new JarLibraryLoader(); + throw new IllegalStateException("Illegal value for com.ost.walletsdk.jni.loader: " + type); + } + + String vmSpec = System.getProperty("java.vm.specification.name"); + return vmSpec.startsWith("Java") ? new JarLibraryLoader() : new SysLibraryLoader(); + } +} diff --git a/ostsdk/src/main/java/com/ost/walletsdk/jni/NilLibraryLoader.java b/ostsdk/src/main/java/com/ost/walletsdk/jni/NilLibraryLoader.java new file mode 100644 index 00000000..81b0e8bb --- /dev/null +++ b/ostsdk/src/main/java/com/ost/walletsdk/jni/NilLibraryLoader.java @@ -0,0 +1,22 @@ +// Copyright (C) 2013 - Will Glozer. All rights reserved. + +package com.ost.walletsdk.jni; + +/** + * A native library loader that refuses to load libraries. + * + * @author Will Glozer + */ +public class NilLibraryLoader implements LibraryLoader { + /** + * Don't load a shared library. + * + * @param name Name of the library to load. + * @param verify Ignored, no verification is done. + * + * @return false. + */ + public boolean load(String name, boolean verify) { + return false; + } +} diff --git a/ostsdk/src/main/java/com/ost/walletsdk/jni/Platform.java b/ostsdk/src/main/java/com/ost/walletsdk/jni/Platform.java new file mode 100644 index 00000000..f2bfc9fc --- /dev/null +++ b/ostsdk/src/main/java/com/ost/walletsdk/jni/Platform.java @@ -0,0 +1,73 @@ +// Copyright (C) 2011 - Will Glozer. All rights reserved. + +package com.ost.walletsdk.jni; + +import java.util.regex.Pattern; + +import static java.lang.System.getProperty; +import static java.util.regex.Pattern.CASE_INSENSITIVE; + +/** + * A platform is a unique combination of CPU architecture and operating system. This class + * attempts to determine the platform it is executing on by examining and normalizing the + * os.arch and os.name system properties. + * + * @author Will Glozer + */ +public class Platform { + public enum Arch { + x86 ("x86|i386"), + x86_64("x86_64|amd64"); + + Pattern pattern; + + Arch(String pattern) { + this.pattern = Pattern.compile("\\A" + pattern + "\\Z", CASE_INSENSITIVE); + } + } + + public enum OS { + darwin ("darwin|mac os x"), + freebsd("freebsd"), + linux ("linux"); + + Pattern pattern; + + OS(String pattern) { + this.pattern = Pattern.compile("\\A" + pattern + "\\Z", CASE_INSENSITIVE); + } + } + + public final Arch arch; + public final OS os; + + private Platform(Arch arch, OS os) { + this.arch = arch; + this.os = os; + } + + /** + * Attempt to detect the current platform. + * + * @return The current platform. + * + * @throws UnsupportedPlatformException if the platform cannot be detected. + */ + public static Platform detect() throws UnsupportedPlatformException { + String osArch = getProperty("os.arch"); + String osName = getProperty("os.name"); + + for (Arch arch : Arch.values()) { + if (arch.pattern.matcher(osArch).matches()) { + for (OS os : OS.values()) { + if (os.pattern.matcher(osName).matches()) { + return new Platform(arch, os); + } + } + } + } + + String msg = String.format("Unsupported platform %s %s", osArch, osName); + throw new UnsupportedPlatformException(msg); + } +} diff --git a/ostsdk/src/main/java/com/ost/walletsdk/jni/SysLibraryLoader.java b/ostsdk/src/main/java/com/ost/walletsdk/jni/SysLibraryLoader.java new file mode 100644 index 00000000..7f911e21 --- /dev/null +++ b/ostsdk/src/main/java/com/ost/walletsdk/jni/SysLibraryLoader.java @@ -0,0 +1,32 @@ +// Copyright (C) 2011 - Will Glozer. All rights reserved. + +package com.ost.walletsdk.jni; + +/** + * A native library loader that simply invokes {@link System#loadLibrary}. The shared + * library path and filename are platform specific. + * + * @author Will Glozer + */ +public class SysLibraryLoader implements LibraryLoader { + /** + * Load a shared library. + * + * @param name Name of the library to load. + * @param verify Ignored, no verification is done. + * + * @return true if the library was successfully loaded. + */ + public boolean load(String name, boolean verify) { + boolean loaded; + + try { + System.loadLibrary(name); + loaded = true; + } catch (Throwable e) { + loaded = false; + } + + return loaded; + } +} diff --git a/ostsdk/src/main/java/com/ost/walletsdk/jni/UnsupportedPlatformException.java b/ostsdk/src/main/java/com/ost/walletsdk/jni/UnsupportedPlatformException.java new file mode 100644 index 00000000..a45a9b9d --- /dev/null +++ b/ostsdk/src/main/java/com/ost/walletsdk/jni/UnsupportedPlatformException.java @@ -0,0 +1,14 @@ +// Copyright (C) 2011 - Will Glozer. All rights reserved. + +package com.ost.walletsdk.jni; + +/** + * Exception thrown when the current platform cannot be detected. + * + * @author Will Glozer + */ +public class UnsupportedPlatformException extends RuntimeException { + public UnsupportedPlatformException(String s) { + super(s); + } +} diff --git a/ostsdk/src/main/java/com/ost/walletsdk/network/OstJsonApi.java b/ostsdk/src/main/java/com/ost/walletsdk/network/OstJsonApi.java index 632422b9..ec146515 100644 --- a/ostsdk/src/main/java/com/ost/walletsdk/network/OstJsonApi.java +++ b/ostsdk/src/main/java/com/ost/walletsdk/network/OstJsonApi.java @@ -61,7 +61,7 @@ private static void execGetBalance(@NonNull String userId, @NonNull OstJsonApiCa if ( err instanceof OstError ) { error = (OstError) err; } else { - error = new OstError("ojsonapi_egb_2", ErrorCode.SDK_ERROR); + error = OstError.SdkError("ojsonapi_egb_1", err); } sendErrorCallback(callback, error, response); } @@ -98,7 +98,7 @@ private static void execGetPricePoints(@NonNull String userId, @NonNull OstJsonA if ( err instanceof OstError ) { error = (OstError) err; } else { - error = new OstError("ojsonapi_egb_2", ErrorCode.SDK_ERROR); + error = OstError.SdkError("ojsonapi_egpp_1", err); } sendErrorCallback(callback, error, response); } @@ -160,8 +160,7 @@ private static void execGetBalanceWithPricePoints(@NonNull String userId, @NonNu if ( err instanceof OstError ) { error = (OstError) err; } else { - - error = new OstError("ojsonapi_egbwpp_4", ErrorCode.SDK_ERROR); + error = OstError.SdkError("ojsonapi_egbpp_1", err); } sendErrorCallback(callback, error, response); } @@ -199,7 +198,7 @@ private static void execGetTransactions(@NonNull String userId, Map +#include + +#include +#include +#include +#include + +#include "sha256.h" +#include "sysendian.h" + +#include "crypto_scrypt.h" + +static void blkcpy(void *, void *, size_t); +static void blkxor(void *, void *, size_t); +static void salsa20_8(uint32_t[16]); +static void blockmix_salsa8(uint32_t *, uint32_t *, uint32_t *, size_t); +static uint64_t integerify(void *, size_t); +static void smix(uint8_t *, size_t, uint64_t, uint32_t *, uint32_t *); + +static void +blkcpy(void * dest, void * src, size_t len) +{ + size_t * D = dest; + size_t * S = src; + size_t L = len / sizeof(size_t); + size_t i; + + for (i = 0; i < L; i++) + D[i] = S[i]; +} + +static void +blkxor(void * dest, void * src, size_t len) +{ + size_t * D = dest; + size_t * S = src; + size_t L = len / sizeof(size_t); + size_t i; + + for (i = 0; i < L; i++) + D[i] ^= S[i]; +} + +/** + * salsa20_8(B): + * Apply the salsa20/8 core to the provided block. + */ +static void +salsa20_8(uint32_t B[16]) +{ + uint32_t x[16]; + size_t i; + + blkcpy(x, B, 64); + for (i = 0; i < 8; i += 2) { +#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) + /* Operate on columns. */ + x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); + x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); + + x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); + x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); + + x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); + x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); + + x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); + x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); + + /* Operate on rows. */ + x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); + x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); + + x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); + x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); + + x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); + x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); + + x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); + x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); +#undef R + } + for (i = 0; i < 16; i++) + B[i] += x[i]; +} + +/** + * blockmix_salsa8(Bin, Bout, X, r): + * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r + * bytes in length; the output Bout must also be the same size. The + * temporary space X must be 64 bytes. + */ +static void +blockmix_salsa8(uint32_t * Bin, uint32_t * Bout, uint32_t * X, size_t r) +{ + size_t i; + + /* 1: X <-- B_{2r - 1} */ + blkcpy(X, &Bin[(2 * r - 1) * 16], 64); + + /* 2: for i = 0 to 2r - 1 do */ + for (i = 0; i < 2 * r; i += 2) { + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &Bin[i * 16], 64); + salsa20_8(X); + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + blkcpy(&Bout[i * 8], X, 64); + + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &Bin[i * 16 + 16], 64); + salsa20_8(X); + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + blkcpy(&Bout[i * 8 + r * 16], X, 64); + } +} + +/** + * integerify(B, r): + * Return the result of parsing B_{2r-1} as a little-endian integer. + */ +static uint64_t +integerify(void * B, size_t r) +{ + uint32_t * X = (void *)((uintptr_t)(B) + (2 * r - 1) * 64); + + return (((uint64_t)(X[1]) << 32) + X[0]); +} + +/** + * smix(B, r, N, V, XY): + * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; + * the temporary storage V must be 128rN bytes in length; the temporary + * storage XY must be 256r + 64 bytes in length. The value N must be a + * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a + * multiple of 64 bytes. + */ +static void +smix(uint8_t * B, size_t r, uint64_t N, uint32_t * V, uint32_t * XY) +{ + uint32_t * X = XY; + uint32_t * Y = &XY[32 * r]; + uint32_t * Z = &XY[64 * r]; + uint64_t i; + uint64_t j; + size_t k; + + /* 1: X <-- B */ + for (k = 0; k < 32 * r; k++) + X[k] = le32dec(&B[4 * k]); + + /* 2: for i = 0 to N - 1 do */ + for (i = 0; i < N; i += 2) { + /* 3: V_i <-- X */ + blkcpy(&V[i * (32 * r)], X, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(X, Y, Z, r); + + /* 3: V_i <-- X */ + blkcpy(&V[(i + 1) * (32 * r)], Y, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(Y, X, Z, r); + } + + /* 6: for i = 0 to N - 1 do */ + for (i = 0; i < N; i += 2) { + /* 7: j <-- Integerify(X) mod N */ + j = integerify(X, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(X, &V[j * (32 * r)], 128 * r); + blockmix_salsa8(X, Y, Z, r); + + /* 7: j <-- Integerify(X) mod N */ + j = integerify(Y, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(Y, &V[j * (32 * r)], 128 * r); + blockmix_salsa8(Y, X, Z, r); + } + + /* 10: B' <-- X */ + for (k = 0; k < 32 * r; k++) + le32enc(&B[4 * k], X[k]); +} + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2 greater than 1. + * + * Return 0 on success; or -1 on error. + */ +int +crypto_scrypt(const uint8_t * passwd, size_t passwdlen, + const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p, + uint8_t * buf, size_t buflen) +{ + void * B0, * V0, * XY0; + uint8_t * B; + uint32_t * V; + uint32_t * XY; + uint32_t i; + + /* Sanity-check parameters. */ +#if SIZE_MAX > UINT32_MAX + if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { + errno = EFBIG; + goto err0; + } +#endif + if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { + errno = EFBIG; + goto err0; + } + if (((N & (N - 1)) != 0) || (N < 2)) { + errno = EINVAL; + goto err0; + } + if ((r > SIZE_MAX / 128 / p) || +#if SIZE_MAX / 256 <= UINT32_MAX + (r > SIZE_MAX / 256) || +#endif + (N > SIZE_MAX / 128 / r)) { + errno = ENOMEM; + goto err0; + } + + /* Allocate memory. */ +#ifdef HAVE_POSIX_MEMALIGN + if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0) + goto err0; + B = (uint8_t *)(B0); + if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0) + goto err1; + XY = (uint32_t *)(XY0); +#ifndef MAP_ANON + if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0) + goto err2; + V = (uint32_t *)(V0); +#endif +#else + if ((B0 = malloc(128 * r * p + 63)) == NULL) + goto err0; + B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63)); + if ((XY0 = malloc(256 * r + 64 + 63)) == NULL) + goto err1; + XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63)); +#ifndef MAP_ANON + if ((V0 = malloc(128 * r * N + 63)) == NULL) + goto err2; + V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63)); +#endif +#endif +#ifdef MAP_ANON + if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE, +#ifdef MAP_NOCORE + MAP_ANON | MAP_PRIVATE | MAP_NOCORE, +#else + MAP_ANON | MAP_PRIVATE, +#endif + -1, 0)) == MAP_FAILED) + goto err2; + V = (uint32_t *)(V0); +#endif + + /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ + PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); + + /* 2: for i = 0 to p - 1 do */ + for (i = 0; i < p; i++) { + /* 3: B_i <-- MF(B_i, N) */ + smix(&B[i * 128 * r], r, N, V, XY); + } + + /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ + PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); + + /* Free memory. */ +#ifdef MAP_ANON + if (munmap(V0, 128 * r * N)) + goto err2; +#else + free(V0); +#endif + free(XY0); + free(B0); + + /* Success! */ + return (0); + +err2: + free(XY0); +err1: + free(B0); +err0: + /* Failure! */ + return (-1); +} diff --git a/ostsdk/src/scrypt/c/scrypt_jni.c b/ostsdk/src/scrypt/c/scrypt_jni.c new file mode 100644 index 00000000..da1c2b4d --- /dev/null +++ b/ostsdk/src/scrypt/c/scrypt_jni.c @@ -0,0 +1,70 @@ +// Copyright (C) 2011 - Will Glozer. All rights reserved. + +#include +#include +#include + +#include +#include "crypto_scrypt.h" + +jbyteArray JNICALL scryptN(JNIEnv *env, jclass cls, jbyteArray passwd, jbyteArray salt, + jint N, jint r, jint p, jint dkLen) +{ + jint Plen = (*env)->GetArrayLength(env, passwd); + jint Slen = (*env)->GetArrayLength(env, salt); + jbyte *P = (*env)->GetByteArrayElements(env, passwd, NULL); + jbyte *S = (*env)->GetByteArrayElements(env, salt, NULL); + uint8_t *buf = malloc(sizeof(uint8_t) * dkLen); + jbyteArray DK = NULL; + + if (P == NULL || S == NULL || buf == NULL) goto cleanup; + + if (crypto_scrypt((uint8_t *) P, Plen, (uint8_t *) S, Slen, N, r, p, buf, dkLen)) { + jclass e = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); + char *msg; + switch (errno) { + case EINVAL: + e = (*env)->FindClass(env, "java/lang/IllegalArgumentException"); + msg = "N must be a power of 2 greater than 1"; + break; + case EFBIG: + case ENOMEM: + msg = "Insufficient memory available"; + break; + default: + msg = "Memory allocation failed"; + } + (*env)->ThrowNew(env, e, msg); + goto cleanup; + } + + DK = (*env)->NewByteArray(env, dkLen); + if (DK == NULL) goto cleanup; + + (*env)->SetByteArrayRegion(env, DK, 0, dkLen, (jbyte *) buf); + + cleanup: + + if (P) (*env)->ReleaseByteArrayElements(env, passwd, P, JNI_ABORT); + if (S) (*env)->ReleaseByteArrayElements(env, salt, S, JNI_ABORT); + if (buf) free(buf); + + return DK; +} + +static const JNINativeMethod methods[] = { + { "scryptN", "([B[BIIII)[B", (void *) scryptN } +}; + +jint JNI_OnLoad(JavaVM *vm, void *reserved) { + JNIEnv *env; + + if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) { + return -1; + } + + jclass cls = (*env)->FindClass(env, "com/ost/walletsdk/crypto/SCrypt"); + int r = (*env)->RegisterNatives(env, cls, methods, 1); + + return (r == JNI_OK) ? JNI_VERSION_1_6 : -1; +} diff --git a/ostsdk/src/scrypt/c/sha256.c b/ostsdk/src/scrypt/c/sha256.c new file mode 100644 index 00000000..01a9f398 --- /dev/null +++ b/ostsdk/src/scrypt/c/sha256.c @@ -0,0 +1,412 @@ +/*- + * Copyright 2005,2007,2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "scrypt_platform.h" + +#include + +#include +#include + +#include "sysendian.h" + +#include "sha256.h" + +/* + * Encode a length len/4 vector of (uint32_t) into a length len vector of + * (unsigned char) in big-endian form. Assumes len is a multiple of 4. + */ +static void +be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + be32enc(dst + i * 4, src[i]); +} + +/* + * Decode a big-endian length len vector of (unsigned char) into a length + * len/4 vector of (uint32_t). Assumes len is a multiple of 4. + */ +static void +be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + dst[i] = be32dec(src + i * 4); +} + +/* Elementary functions used by SHA256 */ +#define Ch(x, y, z) ((x & (y ^ z)) ^ z) +#define Maj(x, y, z) ((x & (y | z)) | (y & z)) +#define SHR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << (32 - n))) +#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +/* SHA256 round function */ +#define RND(a, b, c, d, e, f, g, h, k) \ + t0 = h + S1(e) + Ch(e, f, g) + k; \ + t1 = S0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + +/* Adjusted round function for rotating state */ +#define RNDr(S, W, i, k) \ + RND(S[(64 - i) % 8], S[(65 - i) % 8], \ + S[(66 - i) % 8], S[(67 - i) % 8], \ + S[(68 - i) % 8], S[(69 - i) % 8], \ + S[(70 - i) % 8], S[(71 - i) % 8], \ + W[i] + k) + +/* + * SHA256 block compression function. The 256-bit state is transformed via + * the 512-bit input block to produce a new state. + */ +static void +SHA256_Transform(uint32_t * state, const unsigned char block[64]) +{ + uint32_t W[64]; + uint32_t S[8]; + uint32_t t0, t1; + int i; + + /* 1. Prepare message schedule W. */ + be32dec_vect(W, block, 64); + for (i = 16; i < 64; i++) + W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16]; + + /* 2. Initialize working variables. */ + memcpy(S, state, 32); + + /* 3. Mix. */ + RNDr(S, W, 0, 0x428a2f98); + RNDr(S, W, 1, 0x71374491); + RNDr(S, W, 2, 0xb5c0fbcf); + RNDr(S, W, 3, 0xe9b5dba5); + RNDr(S, W, 4, 0x3956c25b); + RNDr(S, W, 5, 0x59f111f1); + RNDr(S, W, 6, 0x923f82a4); + RNDr(S, W, 7, 0xab1c5ed5); + RNDr(S, W, 8, 0xd807aa98); + RNDr(S, W, 9, 0x12835b01); + RNDr(S, W, 10, 0x243185be); + RNDr(S, W, 11, 0x550c7dc3); + RNDr(S, W, 12, 0x72be5d74); + RNDr(S, W, 13, 0x80deb1fe); + RNDr(S, W, 14, 0x9bdc06a7); + RNDr(S, W, 15, 0xc19bf174); + RNDr(S, W, 16, 0xe49b69c1); + RNDr(S, W, 17, 0xefbe4786); + RNDr(S, W, 18, 0x0fc19dc6); + RNDr(S, W, 19, 0x240ca1cc); + RNDr(S, W, 20, 0x2de92c6f); + RNDr(S, W, 21, 0x4a7484aa); + RNDr(S, W, 22, 0x5cb0a9dc); + RNDr(S, W, 23, 0x76f988da); + RNDr(S, W, 24, 0x983e5152); + RNDr(S, W, 25, 0xa831c66d); + RNDr(S, W, 26, 0xb00327c8); + RNDr(S, W, 27, 0xbf597fc7); + RNDr(S, W, 28, 0xc6e00bf3); + RNDr(S, W, 29, 0xd5a79147); + RNDr(S, W, 30, 0x06ca6351); + RNDr(S, W, 31, 0x14292967); + RNDr(S, W, 32, 0x27b70a85); + RNDr(S, W, 33, 0x2e1b2138); + RNDr(S, W, 34, 0x4d2c6dfc); + RNDr(S, W, 35, 0x53380d13); + RNDr(S, W, 36, 0x650a7354); + RNDr(S, W, 37, 0x766a0abb); + RNDr(S, W, 38, 0x81c2c92e); + RNDr(S, W, 39, 0x92722c85); + RNDr(S, W, 40, 0xa2bfe8a1); + RNDr(S, W, 41, 0xa81a664b); + RNDr(S, W, 42, 0xc24b8b70); + RNDr(S, W, 43, 0xc76c51a3); + RNDr(S, W, 44, 0xd192e819); + RNDr(S, W, 45, 0xd6990624); + RNDr(S, W, 46, 0xf40e3585); + RNDr(S, W, 47, 0x106aa070); + RNDr(S, W, 48, 0x19a4c116); + RNDr(S, W, 49, 0x1e376c08); + RNDr(S, W, 50, 0x2748774c); + RNDr(S, W, 51, 0x34b0bcb5); + RNDr(S, W, 52, 0x391c0cb3); + RNDr(S, W, 53, 0x4ed8aa4a); + RNDr(S, W, 54, 0x5b9cca4f); + RNDr(S, W, 55, 0x682e6ff3); + RNDr(S, W, 56, 0x748f82ee); + RNDr(S, W, 57, 0x78a5636f); + RNDr(S, W, 58, 0x84c87814); + RNDr(S, W, 59, 0x8cc70208); + RNDr(S, W, 60, 0x90befffa); + RNDr(S, W, 61, 0xa4506ceb); + RNDr(S, W, 62, 0xbef9a3f7); + RNDr(S, W, 63, 0xc67178f2); + + /* 4. Mix local working variables into global state. */ + for (i = 0; i < 8; i++) + state[i] += S[i]; + + /* Clean the stack. */ + memset(W, 0, 256); + memset(S, 0, 32); + t0 = t1 = 0; +} + +static unsigned char PAD[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* Add padding and terminating bit-count. */ +static void +SHA256_Pad(SHA256_CTX * ctx) +{ + unsigned char len[8]; + uint32_t r, plen; + + /* + * Convert length to a vector of bytes -- we do this now rather + * than later because the length will change after we pad. + */ + be32enc_vect(len, ctx->count, 8); + + /* Add 1--64 bytes so that the resulting length is 56 mod 64. */ + r = (ctx->count[1] >> 3) & 0x3f; + plen = (r < 56) ? (56 - r) : (120 - r); + SHA256_Update(ctx, PAD, (size_t)plen); + + /* Add the terminating bit-count. */ + SHA256_Update(ctx, len, 8); +} + +/* SHA-256 initialization. Begins a SHA-256 operation. */ +void +SHA256_Init(SHA256_CTX * ctx) +{ + + /* Zero bits processed so far. */ + ctx->count[0] = ctx->count[1] = 0; + + /* Magic initialization constants. */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; +} + +/* Add bytes into the hash. */ +void +SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len) +{ + uint32_t bitlen[2]; + uint32_t r; + const unsigned char *src = in; + + /* Number of bytes left in the buffer from previous updates. */ + r = (ctx->count[1] >> 3) & 0x3f; + + /* Convert the length into a number of bits. */ + bitlen[1] = ((uint32_t)len) << 3; + bitlen[0] = (uint32_t)(len >> 29); + + /* Update number of bits. */ + if ((ctx->count[1] += bitlen[1]) < bitlen[1]) + ctx->count[0]++; + ctx->count[0] += bitlen[0]; + + /* Handle the case where we don't need to perform any transforms. */ + if (len < 64 - r) { + memcpy(&ctx->buf[r], src, len); + return; + } + + /* Finish the current block. */ + memcpy(&ctx->buf[r], src, 64 - r); + SHA256_Transform(ctx->state, ctx->buf); + src += 64 - r; + len -= 64 - r; + + /* Perform complete blocks. */ + while (len >= 64) { + SHA256_Transform(ctx->state, src); + src += 64; + len -= 64; + } + + /* Copy left over data into buffer. */ + memcpy(ctx->buf, src, len); +} + +/* + * SHA-256 finalization. Pads the input data, exports the hash value, + * and clears the context state. + */ +void +SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) +{ + + /* Add padding. */ + SHA256_Pad(ctx); + + /* Write the hash. */ + be32enc_vect(digest, ctx->state, 32); + + /* Clear the context state. */ + memset((void *)ctx, 0, sizeof(*ctx)); +} + +/* Initialize an HMAC-SHA256 operation with the given key. */ +void +HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen) +{ + unsigned char pad[64]; + unsigned char khash[32]; + const unsigned char * K = _K; + size_t i; + + /* If Klen > 64, the key is really SHA256(K). */ + if (Klen > 64) { + SHA256_Init(&ctx->ictx); + SHA256_Update(&ctx->ictx, K, Klen); + SHA256_Final(khash, &ctx->ictx); + K = khash; + Klen = 32; + } + + /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */ + SHA256_Init(&ctx->ictx); + memset(pad, 0x36, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + SHA256_Update(&ctx->ictx, pad, 64); + + /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */ + SHA256_Init(&ctx->octx); + memset(pad, 0x5c, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + SHA256_Update(&ctx->octx, pad, 64); + + /* Clean the stack. */ + memset(khash, 0, 32); +} + +/* Add bytes to the HMAC-SHA256 operation. */ +void +HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void *in, size_t len) +{ + + /* Feed data to the inner SHA256 operation. */ + SHA256_Update(&ctx->ictx, in, len); +} + +/* Finish an HMAC-SHA256 operation. */ +void +HMAC_SHA256_Final(unsigned char digest[32], HMAC_SHA256_CTX * ctx) +{ + unsigned char ihash[32]; + + /* Finish the inner SHA256 operation. */ + SHA256_Final(ihash, &ctx->ictx); + + /* Feed the inner hash to the outer SHA256 operation. */ + SHA256_Update(&ctx->octx, ihash, 32); + + /* Finish the outer SHA256 operation. */ + SHA256_Final(digest, &ctx->octx); + + /* Clean the stack. */ + memset(ihash, 0, 32); +} + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void +PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, + size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen) +{ + HMAC_SHA256_CTX PShctx, hctx; + size_t i; + uint8_t ivec[4]; + uint8_t U[32]; + uint8_t T[32]; + uint64_t j; + int k; + size_t clen; + + /* Compute HMAC state after processing P and S. */ + HMAC_SHA256_Init(&PShctx, passwd, passwdlen); + HMAC_SHA256_Update(&PShctx, salt, saltlen); + + /* Iterate through the blocks. */ + for (i = 0; i * 32 < dkLen; i++) { + /* Generate INT(i + 1). */ + be32enc(ivec, (uint32_t)(i + 1)); + + /* Compute U_1 = PRF(P, S || INT(i)). */ + memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX)); + HMAC_SHA256_Update(&hctx, ivec, 4); + HMAC_SHA256_Final(U, &hctx); + + /* T_i = U_1 ... */ + memcpy(T, U, 32); + + for (j = 2; j <= c; j++) { + /* Compute U_j. */ + HMAC_SHA256_Init(&hctx, passwd, passwdlen); + HMAC_SHA256_Update(&hctx, U, 32); + HMAC_SHA256_Final(U, &hctx); + + /* ... xor U_j ... */ + for (k = 0; k < 32; k++) + T[k] ^= U[k]; + } + + /* Copy as many bytes as necessary into buf. */ + clen = dkLen - i * 32; + if (clen > 32) + clen = 32; + memcpy(&buf[i * 32], T, clen); + } + + /* Clean PShctx, since we never called _Final on it. */ + memset(&PShctx, 0, sizeof(HMAC_SHA256_CTX)); +} diff --git a/ostsdk/src/scrypt/include/config.h b/ostsdk/src/scrypt/include/config.h new file mode 100644 index 00000000..1f0dc1e1 --- /dev/null +++ b/ostsdk/src/scrypt/include/config.h @@ -0,0 +1,10 @@ +#define HAVE_DECL_BE64ENC 0 +#define HAVE_MMAP 1 + +#ifndef __ANDROID__ +#define HAVE_POSIX_MEMALIGN 1 +#endif + +#ifdef __ANDROID__ +#include +#endif diff --git a/ostsdk/src/scrypt/include/crypto_scrypt.h b/ostsdk/src/scrypt/include/crypto_scrypt.h new file mode 100644 index 00000000..f72e1f4b --- /dev/null +++ b/ostsdk/src/scrypt/include/crypto_scrypt.h @@ -0,0 +1,46 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ +#ifndef _CRYPTO_SCRYPT_H_ +#define _CRYPTO_SCRYPT_H_ + +#include + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2 greater than 1. + * + * Return 0 on success; or -1 on error. + */ +int crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t, + uint32_t, uint32_t, uint8_t *, size_t); + +#endif /* !_CRYPTO_SCRYPT_H_ */ diff --git a/ostsdk/src/scrypt/include/scrypt_platform.h b/ostsdk/src/scrypt/include/scrypt_platform.h new file mode 100644 index 00000000..5cec2363 --- /dev/null +++ b/ostsdk/src/scrypt/include/scrypt_platform.h @@ -0,0 +1,12 @@ +#ifndef _SCRYPT_PLATFORM_H_ +#define _SCRYPT_PLATFORM_H_ + +#if defined(CONFIG_H_FILE) +#include CONFIG_H_FILE +#elif defined(HAVE_CONFIG_H) +#include "config.h" +#else +#error Need either CONFIG_H_FILE or HAVE_CONFIG_H defined. +#endif + +#endif /* !_SCRYPT_PLATFORM_H_ */ diff --git a/ostsdk/src/scrypt/include/sha256.h b/ostsdk/src/scrypt/include/sha256.h new file mode 100644 index 00000000..289a5232 --- /dev/null +++ b/ostsdk/src/scrypt/include/sha256.h @@ -0,0 +1,62 @@ +/*- + * Copyright 2005,2007,2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/libmd/sha256.h,v 1.2 2006/01/17 15:35:56 phk Exp $ + */ + +#ifndef _SHA256_H_ +#define _SHA256_H_ + +#include + +#include + +typedef struct SHA256Context { + uint32_t state[8]; + uint32_t count[2]; + unsigned char buf[64]; +} SHA256_CTX; + +typedef struct HMAC_SHA256Context { + SHA256_CTX ictx; + SHA256_CTX octx; +} HMAC_SHA256_CTX; + +void SHA256_Init(SHA256_CTX *); +void SHA256_Update(SHA256_CTX *, const void *, size_t); +void SHA256_Final(unsigned char [32], SHA256_CTX *); +void HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t); +void HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t); +void HMAC_SHA256_Final(unsigned char [32], HMAC_SHA256_CTX *); + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t, + uint64_t, uint8_t *, size_t); + +#endif /* !_SHA256_H_ */ diff --git a/ostsdk/src/scrypt/include/sysendian.h b/ostsdk/src/scrypt/include/sysendian.h new file mode 100644 index 00000000..62ef31a4 --- /dev/null +++ b/ostsdk/src/scrypt/include/sysendian.h @@ -0,0 +1,140 @@ +/*- + * Copyright 2007-2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ +#ifndef _SYSENDIAN_H_ +#define _SYSENDIAN_H_ + +#include "scrypt_platform.h" + +/* If we don't have be64enc, the we have isn't usable. */ +#if !HAVE_DECL_BE64ENC +#undef HAVE_SYS_ENDIAN_H +#endif + +#ifdef HAVE_SYS_ENDIAN_H + +#include + +#else + +#include + +static inline uint32_t +be32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) + + ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24)); +} + +static inline void +be32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[3] = x & 0xff; + p[2] = (x >> 8) & 0xff; + p[1] = (x >> 16) & 0xff; + p[0] = (x >> 24) & 0xff; +} + +static inline uint64_t +be64dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) + + ((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) + + ((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) + + ((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56)); +} + +static inline void +be64enc(void *pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[7] = x & 0xff; + p[6] = (x >> 8) & 0xff; + p[5] = (x >> 16) & 0xff; + p[4] = (x >> 24) & 0xff; + p[3] = (x >> 32) & 0xff; + p[2] = (x >> 40) & 0xff; + p[1] = (x >> 48) & 0xff; + p[0] = (x >> 56) & 0xff; +} + +static inline uint32_t +le32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) + + ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24)); +} + +static inline void +le32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; +} + +static inline uint64_t +le64dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) + + ((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) + + ((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) + + ((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56)); +} + +static inline void +le64enc(void *pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; + p[4] = (x >> 32) & 0xff; + p[5] = (x >> 40) & 0xff; + p[6] = (x >> 48) & 0xff; + p[7] = (x >> 56) & 0xff; +} +#endif /* !HAVE_SYS_ENDIAN_H */ + +#endif /* !_SYSENDIAN_H_ */