Skip to content

Commit 8d86dfe

Browse files
committedJun 2, 2020
Implemented ECEIS.
1 parent 9977995 commit 8d86dfe

File tree

13 files changed

+760
-42
lines changed

13 files changed

+760
-42
lines changed
 

Diff for: ‎build.gradle

+2-3
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,15 @@ dependencies {
1818
implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.62'
1919
implementation group: 'org.bouncycastle', name: 'bcpg-jdk15on', version: '1.62'
2020

21-
implementation group: 'com.github.softwareverde', name: 'java-util', version: 'v2.1.0'
21+
implementation group: 'com.github.softwareverde', name: 'java-util', version: 'v2.1.3'
2222
implementation group: 'com.github.softwareverde', name: 'java-logging', version: 'v2.1.0'
2323

2424
implementation group: 'commons-codec', name: 'commons-codec', version: '1.13'
2525

26-
testCompile group: 'junit', name: 'junit', version: '4.12'
26+
testImplementation group: 'junit', name: 'junit', version: '4.12'
2727
}
2828

2929
test {
3030
maxHeapSize = "4096m"
31-
jvmArgs "-XX:MaxPermSize=4096m"
3231
jvmArgs "-XX:MaxMetaspaceSize=4096m"
3332
}

Diff for: ‎gradle/wrapper/gradle-wrapper.properties

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
#Fri May 15 17:54:33 EDT 2020
2+
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip
13
distributionBase=GRADLE_USER_HOME
24
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip
4-
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists
6+
zipStoreBase=GRADLE_USER_HOME

Diff for: ‎gradlew

+33-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
#!/usr/bin/env sh
22

3+
#
4+
# Copyright 2015 the original author or authors.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# https://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
319
##############################################################################
420
##
521
## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
2844
APP_BASE_NAME=`basename "$0"`
2945

3046
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31-
DEFAULT_JVM_OPTS='"-Xmx64m"'
47+
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
3248

3349
# Use the maximum available, or set MAX_FD != -1 to use that value.
3450
MAX_FD="maximum"
@@ -66,6 +82,7 @@ esac
6682

6783
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
6884

85+
6986
# Determine the Java command to use to start the JVM.
7087
if [ -n "$JAVA_HOME" ] ; then
7188
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@@ -109,10 +126,11 @@ if $darwin; then
109126
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110127
fi
111128

112-
# For Cygwin, switch paths to Windows format before running java
113-
if $cygwin ; then
129+
# For Cygwin or MSYS, switch paths to Windows format before running java
130+
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
114131
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115132
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133+
116134
JAVACMD=`cygpath --unix "$JAVACMD"`
117135

118136
# We build the pattern for arguments to be converted via cygpath
@@ -138,19 +156,19 @@ if $cygwin ; then
138156
else
139157
eval `echo args$i`="\"$arg\""
140158
fi
141-
i=$((i+1))
159+
i=`expr $i + 1`
142160
done
143161
case $i in
144-
(0) set -- ;;
145-
(1) set -- "$args0" ;;
146-
(2) set -- "$args0" "$args1" ;;
147-
(3) set -- "$args0" "$args1" "$args2" ;;
148-
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149-
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150-
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151-
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152-
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153-
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
162+
0) set -- ;;
163+
1) set -- "$args0" ;;
164+
2) set -- "$args0" "$args1" ;;
165+
3) set -- "$args0" "$args1" "$args2" ;;
166+
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167+
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168+
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169+
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170+
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171+
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154172
esac
155173
fi
156174

@@ -159,14 +177,9 @@ save () {
159177
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160178
echo " "
161179
}
162-
APP_ARGS=$(save "$@")
180+
APP_ARGS=`save "$@"`
163181

164182
# Collect all arguments for the java command, following the shell quoting and substitution rules
165183
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166184

167-
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168-
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169-
cd "$(dirname "$0")"
170-
fi
171-
172185
exec "$JAVACMD" "$@"

Diff for: ‎gradlew.bat

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
@rem
2+
@rem Copyright 2015 the original author or authors.
3+
@rem
4+
@rem Licensed under the Apache License, Version 2.0 (the "License");
5+
@rem you may not use this file except in compliance with the License.
6+
@rem You may obtain a copy of the License at
7+
@rem
8+
@rem https://www.apache.org/licenses/LICENSE-2.0
9+
@rem
10+
@rem Unless required by applicable law or agreed to in writing, software
11+
@rem distributed under the License is distributed on an "AS IS" BASIS,
12+
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
@rem See the License for the specific language governing permissions and
14+
@rem limitations under the License.
15+
@rem
16+
117
@if "%DEBUG%" == "" @echo off
218
@rem ##########################################################################
319
@rem
@@ -13,8 +29,11 @@ if "%DIRNAME%" == "" set DIRNAME=.
1329
set APP_BASE_NAME=%~n0
1430
set APP_HOME=%DIRNAME%
1531

32+
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
33+
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34+
1635
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17-
set DEFAULT_JVM_OPTS="-Xmx64m"
36+
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
1837

1938
@rem Find java.exe
2039
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -65,6 +84,7 @@ set CMD_LINE_ARGS=%*
6584

6685
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
6786

87+
6888
@rem Execute Gradle
6989
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
7090

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.softwareverde.security.hash.sha512;
2+
3+
import com.softwareverde.constable.Const;
4+
import com.softwareverde.logging.Logger;
5+
import com.softwareverde.security.hash.ImmutableHash;
6+
import com.softwareverde.util.ByteUtil;
7+
import com.softwareverde.util.HexUtil;
8+
9+
public class ImmutableSha512Hash extends ImmutableHash implements Sha512Hash, Const {
10+
public static ImmutableSha512Hash fromHexString(final String hexString) {
11+
if (hexString == null) { return null; }
12+
13+
final byte[] hashBytes = HexUtil.hexStringToByteArray(hexString);
14+
if (hashBytes == null) { return null; }
15+
if (hashBytes.length != BYTE_COUNT) { return null; }
16+
17+
return new ImmutableSha512Hash(hashBytes);
18+
}
19+
20+
public static ImmutableSha512Hash copyOf(final byte[] bytes) {
21+
if (bytes == null) { return null; }
22+
if (bytes.length != BYTE_COUNT) {
23+
Logger.warn("NOTICE: Unable to wrap bytes as hash. Invalid byte count: "+ bytes.length);
24+
return null;
25+
}
26+
return new ImmutableSha512Hash(bytes);
27+
}
28+
29+
protected ImmutableSha512Hash(final byte[] bytes) {
30+
super(new byte[BYTE_COUNT]);
31+
32+
if (bytes.length != BYTE_COUNT) {
33+
throw new RuntimeException("Invalid byte count: " + bytes.length);
34+
}
35+
36+
ByteUtil.setBytes(_bytes, bytes);
37+
}
38+
39+
public ImmutableSha512Hash() {
40+
super(new byte[BYTE_COUNT]);
41+
}
42+
43+
public ImmutableSha512Hash(final Sha512Hash hash) {
44+
super(hash);
45+
}
46+
47+
@Override
48+
public MutableSha512Hash toReversedEndian() {
49+
return MutableSha512Hash.wrap(ByteUtil.reverseEndian(_bytes));
50+
}
51+
52+
@Override
53+
public ImmutableSha512Hash asConst() {
54+
return this;
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.softwareverde.security.hash.sha512;
2+
3+
import com.softwareverde.logging.Logger;
4+
import com.softwareverde.security.hash.MutableHash;
5+
import com.softwareverde.util.ByteUtil;
6+
import com.softwareverde.util.HexUtil;
7+
8+
public class MutableSha512Hash extends MutableHash implements Sha512Hash {
9+
public static MutableSha512Hash fromHexString(final String hexString) {
10+
if (hexString == null) { return null; }
11+
12+
final byte[] hashBytes = HexUtil.hexStringToByteArray(hexString);
13+
return MutableSha512Hash.wrap(hashBytes);
14+
}
15+
16+
public static MutableSha512Hash wrap(final byte[] bytes) {
17+
if (bytes == null) { return null; }
18+
19+
if (bytes.length != BYTE_COUNT) {
20+
Logger.warn("NOTICE: Unable to wrap bytes as hash. Invalid byte count: " + bytes.length);
21+
return null;
22+
}
23+
return new MutableSha512Hash(bytes);
24+
}
25+
26+
public static MutableSha512Hash copyOf(final byte[] bytes) {
27+
if (bytes == null) { return null; }
28+
29+
if (bytes.length != BYTE_COUNT) {
30+
Logger.warn("NOTICE: Unable to wrap bytes as hash. Invalid byte count: " + bytes.length);
31+
return null;
32+
}
33+
return new MutableSha512Hash(ByteUtil.copyBytes(bytes));
34+
}
35+
36+
protected MutableSha512Hash(final byte[] bytes) {
37+
super(bytes);
38+
39+
if (bytes.length != BYTE_COUNT) {
40+
throw new RuntimeException("Invalid byte count: " + bytes.length);
41+
}
42+
}
43+
44+
public MutableSha512Hash() {
45+
super(BYTE_COUNT);
46+
}
47+
48+
public MutableSha512Hash(final Sha512Hash hash) {
49+
super(hash);
50+
}
51+
52+
@Override
53+
public MutableSha512Hash toReversedEndian() {
54+
return MutableSha512Hash.wrap(ByteUtil.reverseEndian(_bytes));
55+
}
56+
57+
public void setBytes(final byte[] bytes) {
58+
if (bytes.length != BYTE_COUNT) {
59+
Logger.warn("NOTICE: Attempted to set hash bytes of incorrect length: " + bytes.length);
60+
return;
61+
}
62+
63+
if (_bytes.length != bytes.length) {
64+
_bytes = new byte[bytes.length];
65+
}
66+
ByteUtil.setBytes(_bytes, bytes);
67+
}
68+
69+
public void setBytes(final Sha512Hash hash) {
70+
ByteUtil.setBytes(_bytes, hash.getBytes());
71+
}
72+
73+
@Override
74+
public ImmutableSha512Hash asConst() {
75+
return new ImmutableSha512Hash(this);
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.softwareverde.security.hash.sha512;
2+
3+
import com.softwareverde.security.hash.Hash;
4+
import com.softwareverde.util.ByteUtil;
5+
6+
import java.util.Comparator;
7+
8+
public interface Sha512Hash extends Hash, Comparable<Sha512Hash> {
9+
Comparator<Sha512Hash> COMPARATOR = new Comparator<Sha512Hash>() {
10+
@Override
11+
public int compare(final Sha512Hash hash0, final Sha512Hash hash1) {
12+
for (int i = 0; i < BYTE_COUNT; ++i) {
13+
final int b0 = ByteUtil.byteToInteger(hash0.getByte(i));
14+
final int b1 = ByteUtil.byteToInteger(hash1.getByte(i));
15+
16+
if (b0 < b1) { return -1; }
17+
if (b0 > b1) { return 1; }
18+
}
19+
return 0;
20+
}
21+
};
22+
23+
static Sha512Hash fromHexString(final String hexString) {
24+
return ImmutableSha512Hash.fromHexString(hexString);
25+
}
26+
27+
static Sha512Hash copyOf(final byte[] bytes) {
28+
return ImmutableSha512Hash.copyOf(bytes);
29+
}
30+
31+
static Sha512Hash wrap(final byte[] bytes) {
32+
return MutableSha512Hash.wrap(bytes);
33+
}
34+
35+
Integer BYTE_COUNT = 64;
36+
ImmutableSha512Hash EMPTY_HASH = new ImmutableSha512Hash();
37+
38+
@Override
39+
Sha512Hash toReversedEndian();
40+
41+
@Override
42+
ImmutableSha512Hash asConst();
43+
44+
@Override
45+
default int compareTo(final Sha512Hash sha512Hash) {
46+
return COMPARATOR.compare(this, sha512Hash);
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
package com.softwareverde.security.secp256k1;
2+
3+
import com.softwareverde.constable.bytearray.ByteArray;
4+
import com.softwareverde.constable.bytearray.MutableByteArray;
5+
import com.softwareverde.logging.Logger;
6+
import com.softwareverde.security.hash.sha512.Sha512Hash;
7+
import com.softwareverde.security.secp256k1.key.PrivateKey;
8+
import com.softwareverde.security.secp256k1.key.PublicKey;
9+
import com.softwareverde.security.util.HashUtil;
10+
import com.softwareverde.util.Util;
11+
import com.softwareverde.util.bytearray.ByteArrayBuilder;
12+
import org.bouncycastle.math.ec.ECCurve;
13+
import org.bouncycastle.math.ec.ECPoint;
14+
15+
import javax.crypto.Cipher;
16+
import javax.crypto.SecretKey;
17+
import javax.crypto.spec.IvParameterSpec;
18+
import javax.crypto.spec.SecretKeySpec;
19+
import java.math.BigInteger;
20+
import java.security.spec.AlgorithmParameterSpec;
21+
22+
public class Ecies {
23+
protected static ByteArray substring(final ByteArray byteArray, final Integer offset) {
24+
final int byteCount = (byteArray.getByteCount() - offset);
25+
return ByteArray.wrap(byteArray.getBytes(offset, byteCount));
26+
}
27+
28+
public static class Aes {
29+
public static final String KEY_ALGORITHM = "AES";
30+
public static final String ENCRYPTION_CIPHER = "AES/CBC/PKCS7Padding";
31+
public static final Integer INITIALIZATION_VECTOR_BYTE_COUNT = 16;
32+
33+
public static ByteArray encrypt(final ByteArray data, final PrivateKey key, final ByteArray initializationVectorBytes) {
34+
try {
35+
final SecretKey secretKey = new SecretKeySpec(key.getBytes(), 0, key.getByteCount(), KEY_ALGORITHM);
36+
37+
final Cipher aesCipher = Cipher.getInstance(ENCRYPTION_CIPHER);
38+
final AlgorithmParameterSpec initializationVector = new IvParameterSpec(initializationVectorBytes.getBytes());
39+
aesCipher.init(Cipher.ENCRYPT_MODE, secretKey, initializationVector);
40+
final byte[] cipherText = aesCipher.doFinal(data.getBytes());
41+
42+
final ByteArrayBuilder byteArrayBuilder = new ByteArrayBuilder();
43+
byteArrayBuilder.appendBytes(initializationVectorBytes);
44+
byteArrayBuilder.appendBytes(cipherText);
45+
return byteArrayBuilder;
46+
}
47+
catch (final Exception exception) {
48+
Logger.error("Unable to encrypt data.", exception);
49+
return null;
50+
}
51+
}
52+
53+
public static ByteArray decrypt(final ByteArray data, final ByteArray key) {
54+
try {
55+
final SecretKey secretKey = new SecretKeySpec(key.getBytes(), 0, key.getByteCount(), KEY_ALGORITHM);
56+
57+
final byte[] initializationVectorBytes = data.getBytes(0, INITIALIZATION_VECTOR_BYTE_COUNT);
58+
final AlgorithmParameterSpec initializationVector = new IvParameterSpec(initializationVectorBytes);
59+
final ByteArray encryptedData = Ecies.substring(data, INITIALIZATION_VECTOR_BYTE_COUNT);
60+
61+
final Cipher aesCipher = Cipher.getInstance(ENCRYPTION_CIPHER);
62+
aesCipher.init(Cipher.DECRYPT_MODE, secretKey, initializationVector);
63+
return ByteArray.wrap(aesCipher.doFinal(encryptedData.getBytes()));
64+
}
65+
catch (final Exception exception) {
66+
Logger.error("Unable to decrypt data.", exception);
67+
return null;
68+
}
69+
}
70+
}
71+
protected final PrivateKey _localPrivateKey;
72+
protected final PublicKey _remotePublicKey;
73+
protected Boolean _includePublicKey = false;
74+
75+
protected Sha512Hash _getK() {
76+
final ECCurve curve = Secp256k1.CURVE_DOMAIN.getCurve();
77+
final PublicKey decompressedPublicKey = _remotePublicKey.decompress();
78+
final ECPoint KB = curve.decodePoint(decompressedPublicKey.getBytes());
79+
final ECPoint P = KB.multiply(new BigInteger(1, _localPrivateKey.getBytes()));
80+
final BigInteger S = P.normalize().getXCoord().toBigInteger();
81+
final MutableByteArray sBytes = new MutableByteArray(32);
82+
sBytes.setBytes(0, S.toByteArray());
83+
84+
return HashUtil.sha512(sBytes);
85+
}
86+
87+
protected PrivateKey _getFirstK() {
88+
final Sha512Hash k = _getK();
89+
return PrivateKey.fromBytes(k.getBytes(0, PrivateKey.KEY_BYTE_COUNT));
90+
}
91+
92+
protected PrivateKey _getLastK() {
93+
final Sha512Hash k = _getK();
94+
return PrivateKey.fromBytes(Ecies.substring(k, PrivateKey.KEY_BYTE_COUNT));
95+
}
96+
97+
public Ecies(final PrivateKey privateKey, final PublicKey recipientPublicKey) {
98+
_localPrivateKey = privateKey;
99+
_remotePublicKey = recipientPublicKey;
100+
}
101+
102+
public void setIncludePublicKey(final Boolean includePublicKey) {
103+
_includePublicKey = includePublicKey;
104+
}
105+
106+
public Boolean getIncludePublicKey() {
107+
return _includePublicKey;
108+
}
109+
110+
public ByteArray encrypt(final ByteArray message) {
111+
return this.encrypt(message, null);
112+
}
113+
114+
public ByteArray encrypt(final ByteArray message, final ByteArray nullableInitializationVector) {
115+
final PublicKey sendersPublicKey;
116+
{
117+
final PublicKey uncompressedPublicKey = _localPrivateKey.getPublicKey();
118+
sendersPublicKey = uncompressedPublicKey.compress();
119+
}
120+
121+
final ByteArray initializationVector;
122+
{
123+
final ByteArray hmac = (nullableInitializationVector != null ? nullableInitializationVector : HashUtil.sha256Hmac(message, _localPrivateKey));
124+
initializationVector = ByteArray.wrap(hmac.getBytes(0, Aes.INITIALIZATION_VECTOR_BYTE_COUNT));
125+
}
126+
127+
final ByteArray c;
128+
{
129+
final PrivateKey kE = _getFirstK();
130+
c = Aes.encrypt(message, kE, initializationVector);
131+
if (c == null) { return null; }
132+
}
133+
134+
final ByteArray d;
135+
{
136+
final ByteArray ct = Ecies.substring(c, Aes.INITIALIZATION_VECTOR_BYTE_COUNT);
137+
final ByteArrayBuilder hmacPreImage = new ByteArrayBuilder();
138+
{
139+
hmacPreImage.appendBytes(initializationVector);
140+
if (_includePublicKey) {
141+
hmacPreImage.appendBytes(sendersPublicKey);
142+
}
143+
hmacPreImage.appendBytes(ct);
144+
}
145+
146+
final PrivateKey kM = _getLastK();
147+
d = HashUtil.sha256Hmac(hmacPreImage, kM);
148+
}
149+
150+
final ByteArrayBuilder result = new ByteArrayBuilder();
151+
{
152+
if (_includePublicKey) {
153+
result.appendBytes(sendersPublicKey);
154+
}
155+
result.appendBytes(c);
156+
result.appendBytes(d);
157+
}
158+
159+
return result;
160+
}
161+
162+
public ByteArray decrypt(final ByteArray data) {
163+
final int publicKeyByteCount;
164+
final PublicKey sendersPublicKey;
165+
if (_includePublicKey) {
166+
final ByteArray publicKeyBytes;
167+
if (Util.areEqual(PublicKey.UNCOMPRESSED_FIRST_BYTE, data.getByte(0))) {
168+
publicKeyBytes = ByteArray.wrap(data.getBytes(0, PublicKey.UNCOMPRESSED_BYTE_COUNT));
169+
publicKeyByteCount = PublicKey.UNCOMPRESSED_BYTE_COUNT;
170+
}
171+
else {
172+
publicKeyBytes = ByteArray.wrap(data.getBytes(0, PublicKey.COMPRESSED_BYTE_COUNT));
173+
publicKeyByteCount = PublicKey.COMPRESSED_BYTE_COUNT;
174+
}
175+
sendersPublicKey = PublicKey.fromBytes(publicKeyBytes).compress();
176+
if ((sendersPublicKey == null) || (! sendersPublicKey.isValid())) { return null; }
177+
178+
final PublicKey publicKey = _localPrivateKey.getPublicKey();
179+
if ( (! Util.areEqual(publicKey.compress(), sendersPublicKey)) && (! Util.areEqual(_remotePublicKey.compress(), sendersPublicKey)) ) {
180+
return null;
181+
}
182+
}
183+
else {
184+
publicKeyByteCount = 0;
185+
sendersPublicKey = null; // _remotePublicKey;
186+
}
187+
188+
final int hmacByteCount = 32;
189+
190+
final ByteArray c;
191+
{
192+
final int cByteCount = (data.getByteCount() - publicKeyByteCount - hmacByteCount);
193+
c = ByteArray.wrap(data.getBytes(publicKeyByteCount, cByteCount));
194+
}
195+
196+
final ByteArray d;
197+
{
198+
final int dOffset = (data.getByteCount() - hmacByteCount);
199+
d = Ecies.substring(data, dOffset);
200+
}
201+
202+
final ByteArrayBuilder hmacPreImage = new ByteArrayBuilder();
203+
{
204+
final ByteArray initializationVector = ByteArray.wrap(c.getBytes(0, Aes.INITIALIZATION_VECTOR_BYTE_COUNT));
205+
final ByteArray ct = Ecies.substring(c, Aes.INITIALIZATION_VECTOR_BYTE_COUNT);
206+
207+
hmacPreImage.appendBytes(initializationVector);
208+
if (_includePublicKey) {
209+
hmacPreImage.appendBytes(sendersPublicKey);
210+
}
211+
hmacPreImage.appendBytes(ct);
212+
}
213+
214+
final ByteArray d2;
215+
{
216+
final PrivateKey kM = _getLastK();
217+
d2 = HashUtil.sha256Hmac(hmacPreImage, kM);
218+
}
219+
220+
final boolean checksumMatches = Util.areEqual(d, d2);
221+
if (! checksumMatches) { return null; }
222+
223+
final PrivateKey kE = _getFirstK();
224+
return Aes.decrypt(c, kE);
225+
}
226+
}

Diff for: ‎src/main/java/com/softwareverde/security/secp256k1/key/PublicKey.java

+14-7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
import java.math.BigInteger;
1616

1717
public class PublicKey extends ImmutableByteArray implements Const {
18+
public static final byte UNCOMPRESSED_FIRST_BYTE = (byte) 0x04;
19+
public static final byte COMPRESSED_FIRST_BYTE_0 = (byte) 0x02;
20+
public static final byte COMPRESSED_FIRST_BYTE_1 = (byte) 0x03;
21+
22+
public static final Integer COMPRESSED_BYTE_COUNT = 33;
23+
public static final Integer UNCOMPRESSED_BYTE_COUNT = 65;
24+
1825
public static PublicKey fromBytes(final ByteArray bytes) {
1926
if (bytes == null) { return null; }
2027

@@ -111,17 +118,17 @@ public static PublicKey fromSignature(final Signature signature, final Sha256Has
111118
}
112119

113120
protected Boolean _isCompressed() {
114-
if (_bytes.length != 33) { return false; }
121+
if (_bytes.length != COMPRESSED_BYTE_COUNT) { return false; }
115122

116123
final byte firstByte = _bytes[0];
117-
return ( (firstByte == (byte) 0x02) || (firstByte == (byte) 0x03) );
124+
return ( (firstByte == COMPRESSED_FIRST_BYTE_0) || (firstByte == COMPRESSED_FIRST_BYTE_1) );
118125
}
119126

120127
protected Boolean _isDecompressed() {
121-
if (_bytes.length != 65) { return false; }
128+
if (_bytes.length != UNCOMPRESSED_BYTE_COUNT) { return false; }
122129

123130
final byte firstByte = _bytes[0];
124-
return (firstByte == (byte) 0x04);
131+
return (firstByte == UNCOMPRESSED_FIRST_BYTE);
125132
}
126133

127134
protected PublicKey(final byte[] bytes) {
@@ -155,9 +162,9 @@ public PublicKey compress() {
155162
if (_bytes.length == 0) { return this; } // NOP
156163
if (_isCompressed()) { return this; }
157164

158-
final Integer coordinateByteCount = ((_bytes.length - 1) / 2);
165+
final int coordinateByteCount = ((_bytes.length - 1) / 2);
159166

160-
final Integer prefixByteCount = 1;
167+
final int prefixByteCount = 1;
161168
// final byte prefix = _bytes[0];
162169
final byte[] publicKeyPointX = new byte[coordinateByteCount];
163170
final byte[] publicKeyPointY = new byte[coordinateByteCount];
@@ -167,7 +174,7 @@ public PublicKey compress() {
167174
publicKeyPointY[i] = _bytes[prefixByteCount + coordinateByteCount + i];
168175
}
169176
}
170-
final Boolean yCoordinateIsEven = ((publicKeyPointY[coordinateByteCount - 1] & 0xFF) % 2 == 0);
177+
final boolean yCoordinateIsEven = ((publicKeyPointY[coordinateByteCount - 1] & 0xFF) % 2 == 0);
171178
final byte compressedPublicKeyPrefix = (yCoordinateIsEven ? (byte) 0x02 : (byte) 0x03);
172179
final byte[] compressedPublicKeyPoint = new byte[coordinateByteCount + prefixByteCount];
173180
{

Diff for: ‎src/main/java/com/softwareverde/security/secp256k1/signature/Secp256k1Signature.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ protected static byte[] _toDerEncodedInteger(final ByteArray byteArray) {
4141
return bytes;
4242
}
4343

44-
final Integer skippedByteCount;
44+
final int skippedByteCount;
4545
{
4646
int firstNonZeroIndex = 0;
4747
for (int i = 0; i < byteArray.getByteCount(); ++i) {

Diff for: ‎src/main/java/com/softwareverde/security/util/HashUtil.java

+61-1
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,24 @@
55
import com.softwareverde.security.hash.murmur.MurmurHashUtil;
66
import com.softwareverde.security.hash.ripemd160.MutableRipemd160Hash;
77
import com.softwareverde.security.hash.sha256.MutableSha256Hash;
8+
import com.softwareverde.security.hash.sha256.Sha256Hash;
9+
import com.softwareverde.security.hash.sha512.MutableSha512Hash;
10+
import com.softwareverde.security.secp256k1.key.PrivateKey;
811
import org.bouncycastle.crypto.digests.RIPEMD160Digest;
12+
import org.bouncycastle.crypto.digests.SHA256Digest;
13+
import org.bouncycastle.crypto.macs.HMac;
14+
import org.bouncycastle.crypto.params.KeyParameter;
15+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
916

1017
import java.security.MessageDigest;
1118
import java.security.NoSuchAlgorithmException;
19+
import java.security.Security;
1220

1321
public class HashUtil {
22+
static {
23+
Security.addProvider(new BouncyCastleProvider());
24+
}
25+
1426
protected HashUtil() { }
1527

1628
public static byte[] md5(final byte[] data) {
@@ -41,7 +53,13 @@ public static ByteArray sha1(final ByteArray data) {
4153
return MutableByteArray.wrap(HashUtil.sha1(data.getBytes()));
4254
}
4355

44-
public static byte[] sha256(final byte[] data) {
56+
/**
57+
* Uses Java's native implementation of SHA256.
58+
* This implementation is faster than BouncyCastle's implementation,
59+
* however, it has poor performance when parallelized across multiple
60+
* threads since it has an internal lock.
61+
*/
62+
public static byte[] sha256_jvm(final byte[] data) {
4563
try {
4664
final MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
4765
return messageDigest.digest(data);
@@ -51,6 +69,26 @@ public static byte[] sha256(final byte[] data) {
5169
}
5270
}
5371

72+
73+
/**
74+
* Uses BouncyCastle's implementation of SHA256.
75+
* This implementation is slower than Java's native implementation,
76+
* however, it may be parallelized whereas Java's native implementation
77+
* has an internal lock.
78+
*/
79+
public static byte[] sha256_bc(final byte[] data) {
80+
final SHA256Digest messageDigest = new SHA256Digest();
81+
messageDigest.update(data, 0, data.length);
82+
83+
final byte[] hashedBytes = new byte[Sha256Hash.BYTE_COUNT];
84+
messageDigest.doFinal(hashedBytes, 0);
85+
return hashedBytes;
86+
}
87+
88+
public static byte[] sha256(final byte[] data) {
89+
return HashUtil.sha256_jvm(data);
90+
}
91+
5492
public static MutableSha256Hash sha256(final ByteArray data) {
5593
return MutableSha256Hash.wrap(HashUtil.sha256(data.getBytes()));
5694
}
@@ -70,6 +108,28 @@ public static MutableSha256Hash doubleSha256(final ByteArray data) {
70108
return MutableSha256Hash.wrap(HashUtil.doubleSha256(data.getBytes()));
71109
}
72110

111+
public static ByteArray sha256Hmac(final ByteArray data, final PrivateKey privateKey) {
112+
final HMac hmac = new HMac(new SHA256Digest());
113+
114+
hmac.init(new KeyParameter(privateKey.getBytes()));
115+
final MutableByteArray hmacResult = new MutableByteArray(hmac.getMacSize());
116+
117+
hmac.update(data.getBytes(), 0, data.getByteCount());
118+
hmac.doFinal(hmacResult.unwrap(), 0);
119+
120+
return hmacResult;
121+
}
122+
123+
public static MutableSha512Hash sha512(final ByteArray data) {
124+
try {
125+
final MessageDigest messageDigest = MessageDigest.getInstance("SHA-512");
126+
return MutableSha512Hash.wrap(messageDigest.digest(data.getBytes()));
127+
}
128+
catch (final NoSuchAlgorithmException exception) {
129+
throw new RuntimeException(exception);
130+
}
131+
}
132+
73133
public static byte[] ripemd160(final byte[] data) {
74134
final RIPEMD160Digest ripemd160Digest = new RIPEMD160Digest();
75135
ripemd160Digest.update(data, 0, data.length);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.softwareverde.security.secp256k1;
2+
3+
import com.softwareverde.constable.bytearray.ByteArray;
4+
import com.softwareverde.security.secp256k1.key.PrivateKey;
5+
import com.softwareverde.security.secp256k1.key.PublicKey;
6+
import com.softwareverde.util.StringUtil;
7+
import org.junit.Assert;
8+
import org.junit.Test;
9+
10+
public class EciesTests {
11+
@Test
12+
public void should_encrypt_and_decrypt_data() {
13+
// Setup
14+
final PrivateKey sendersPrivateKey = PrivateKey.createNewKey();
15+
final PublicKey sendersPublicKey = sendersPrivateKey.getPublicKey();
16+
17+
final PrivateKey recipientPrivateKey = PrivateKey.createNewKey();
18+
final PublicKey recipientPublicKey = recipientPrivateKey.getPublicKey();
19+
20+
final String message = "Mary had a little lamb.";
21+
final ByteArray payload = ByteArray.wrap(StringUtil.stringToBytes(message));
22+
23+
final Ecies ecies = new Ecies(sendersPrivateKey, recipientPublicKey);
24+
final Ecies recipientEcies = new Ecies(recipientPrivateKey, sendersPublicKey);
25+
26+
// Action
27+
final ByteArray encryptedPayload = ecies.encrypt(payload);
28+
final ByteArray decryptedPayload = recipientEcies.decrypt(encryptedPayload);
29+
30+
// Assert
31+
Assert.assertEquals(payload, decryptedPayload);
32+
}
33+
34+
@Test
35+
public void bitcore_test() {
36+
// Setup
37+
final PrivateKey alicePrivateKey = PrivateKey.fromHexString("77E06ABC52BF065CB5164C5DECA839D0276911991A2730BE4D8D0A0307DE7CEB");
38+
final PrivateKey bobPrivateKey = PrivateKey.fromHexString("2B57C7C5E408CE927EEF5E2EFB49CFDADDE77961D342DAA72284BB3D6590862D");
39+
Assert.assertNotNull(alicePrivateKey);
40+
Assert.assertNotNull(bobPrivateKey);
41+
42+
final ByteArray message = ByteArray.wrap(StringUtil.stringToBytes("attack at dawn"));
43+
// 0339E504D6492B082DA96E11E8F039796B06CD4855C101E2492A6F10F3E056A9E712C732611C6917AB5C57A1926973BC44A1586E94A783F81D05CE72518D9B0A80E2E13C7FF7D1306583F9CC7A48DEF5B37FBF2D5F294F128472A6E9C78DEDE5F5
44+
final ByteArray expectedEncryptedValue = ByteArray.fromHexString("12C732611C6917AB5C57A1926973BC44A1586E94A783F81D05CE72518D9B0A80E2E13C7FF7D1306583F9CC7A48DEF5B37FBF2D5F294F128472A6E9C78DEDE5F5");
45+
46+
final Ecies senderEcies = new Ecies(alicePrivateKey, bobPrivateKey.getPublicKey().compress());
47+
senderEcies.setIncludePublicKey(false);
48+
49+
final Ecies receiverEcies = new Ecies(bobPrivateKey, alicePrivateKey.getPublicKey());
50+
receiverEcies.setIncludePublicKey(false);
51+
52+
// Action
53+
final ByteArray encryptedValue = senderEcies.encrypt(message);
54+
final ByteArray decryptedValue = receiverEcies.decrypt(encryptedValue);
55+
56+
// Assert
57+
Assert.assertNotNull(encryptedValue);
58+
Assert.assertEquals(expectedEncryptedValue, encryptedValue);
59+
60+
Assert.assertNotNull(decryptedValue);
61+
Assert.assertEquals(message, decryptedValue);
62+
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package com.softwareverde.security.util;
22

3-
import com.softwareverde.constable.bytearray.ByteArray;
4-
import com.softwareverde.constable.bytearray.MutableByteArray;
5-
import com.softwareverde.security.hash.sha256.Sha256Hash;
6-
import org.junit.Assert;
7-
import org.junit.Test;
3+
import com.softwareverde.constable.bytearray.*;
4+
import com.softwareverde.security.hash.sha256.*;
5+
import com.softwareverde.security.hash.sha512.Sha512Hash;
6+
import com.softwareverde.util.*;
7+
import com.softwareverde.util.timer.*;
8+
import org.junit.*;
89

910
public class HashUtilTests {
1011
@Test
11-
public void should_hash_sha256_in_place() {
12+
public void should_hash_sha256() {
1213
// Setup
1314
final byte[] preImage = new byte[]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
1415
final ByteArray expectedBytes = MutableByteArray.wrap(HashUtil.sha256(HashUtil.sha256(preImage)));
@@ -19,4 +20,150 @@ public void should_hash_sha256_in_place() {
1920
// Assert
2021
Assert.assertEquals(expectedBytes, doubleSha256Hash);
2122
}
22-
}
23+
24+
@Test
25+
public void should_hash_sha256_string() {
26+
// Setup
27+
final byte[] preImage = StringUtil.stringToBytes("Mary had a little lamb.");
28+
final ByteArray expectedBytes = ByteArray.fromHexString("D2FC16A1F51A653AA01964EF9C923336E10653FEC195F493458B3B21890E1B97");
29+
30+
// Action
31+
final Sha256Hash sha256Hash = HashUtil.sha256(MutableByteArray.wrap(preImage));
32+
33+
// Assert
34+
Assert.assertEquals(expectedBytes, sha256Hash);
35+
}
36+
37+
static class HasherThread extends Thread {
38+
protected final NanoTimer _timer;
39+
protected byte[] _preImage;
40+
41+
public static HasherThread newInstance(final byte[] bytes, final boolean useBc) {
42+
final NanoTimer timer = new NanoTimer();
43+
final byte[] preImage = ByteUtil.copyBytes(bytes);
44+
final Runnable runnable = new Runnable() {
45+
@Override
46+
public void run() {
47+
byte[] bytes = preImage;
48+
timer.start();
49+
for (int i = 0; i < 10000000; ++i) {
50+
if (useBc) {
51+
bytes = HashUtil.sha256_bc(bytes);
52+
}
53+
else {
54+
bytes = HashUtil.sha256_jvm(bytes);
55+
}
56+
}
57+
timer.stop();
58+
}
59+
};
60+
61+
return new HasherThread(preImage, runnable, timer);
62+
}
63+
64+
protected HasherThread(final byte[] preImage, final Runnable runnable, final NanoTimer timer) {
65+
super(runnable);
66+
_timer = timer;
67+
_preImage = preImage;
68+
}
69+
70+
public Long getMillisecondsElapsed() {
71+
return _timer.getMillisecondsElapsed().longValue();
72+
}
73+
}
74+
75+
@Test
76+
public void time_sha256_jvm_vs_bc() throws Exception {
77+
byte[] preImage = new byte[]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
78+
79+
final NanoTimer jvmNanoTimer = new NanoTimer();
80+
jvmNanoTimer.start();
81+
for (int i = 0; i < 10000000; ++i) {
82+
preImage = HashUtil.sha256_jvm(preImage);
83+
}
84+
jvmNanoTimer.stop();
85+
86+
final NanoTimer bcNanoTimer = new NanoTimer();
87+
bcNanoTimer.start();
88+
for (int i = 0; i < 10000000; ++i) {
89+
preImage = HashUtil.sha256_bc(preImage);
90+
}
91+
bcNanoTimer.stop();
92+
93+
System.out.println("JVM: " + jvmNanoTimer.getMillisecondsElapsed());
94+
System.out.println(" BC: " + bcNanoTimer.getMillisecondsElapsed());
95+
96+
final HasherThread[] threads = new HasherThread[2];
97+
for (int i = 0; i < threads.length; ++i) {
98+
threads[i] = HasherThread.newInstance(preImage, true);
99+
}
100+
101+
final NanoTimer threadedBcTimer = new NanoTimer();
102+
threadedBcTimer.start();
103+
for (int i = 0; i < threads.length; ++i) {
104+
threads[i].start();
105+
}
106+
for (int i = 0; i < threads.length; ++i) {
107+
threads[i].join();
108+
}
109+
threadedBcTimer.stop();
110+
111+
for (int i = 0; i < threads.length; ++i) {
112+
threads[i] = HasherThread.newInstance(preImage, false);
113+
}
114+
115+
final NanoTimer threadedJvmTimer = new NanoTimer();
116+
threadedJvmTimer.start();
117+
for (int i = 0; i < threads.length; ++i) {
118+
threads[i].start();
119+
}
120+
for (int i = 0; i < threads.length; ++i) {
121+
threads[i].join();
122+
}
123+
threadedJvmTimer.stop();
124+
125+
System.out.println("Thread JVM: " + threadedJvmTimer.getMillisecondsElapsed());
126+
System.out.println("Thread BC: " + threadedBcTimer.getMillisecondsElapsed());
127+
}
128+
129+
@Test
130+
public void should_hash_sha256_with_threaded_implementation_when_in_use_by_other_thread() {
131+
// Setup
132+
final byte[] preImage = new byte[]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
133+
final ByteArray expectedBytes = MutableByteArray.wrap(HashUtil.sha256(HashUtil.sha256(preImage)));
134+
135+
// Action
136+
final Sha256Hash doubleSha256Hash = HashUtil.doubleSha256(MutableByteArray.wrap(preImage));
137+
138+
// Assert
139+
Assert.assertEquals(expectedBytes, doubleSha256Hash);
140+
}
141+
142+
@Test
143+
public void should_hash_sha512() {
144+
// Setup
145+
final byte[] preImage = new byte[]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
146+
final ByteArray expectedBytes = ByteArray.fromHexString("0F89EE1FCB7B0A4F7809D1267A029719004C5A5E5EC323A7C3523A20974F9A3F202F56FADBA4CD9E8D654AB9F2E96DC5C795EA176FA20EDE8D854C342F903533");
147+
148+
// Action
149+
final Sha512Hash sha512Hash = HashUtil.sha512(MutableByteArray.wrap(preImage));
150+
System.out.println(sha512Hash);
151+
152+
// Assert
153+
Assert.assertEquals(expectedBytes, sha512Hash);
154+
}
155+
156+
@Test
157+
public void should_hash_sha512_string() {
158+
// Setup
159+
final byte[] preImage = StringUtil.stringToBytes("Mary had a little lamb.");
160+
final ByteArray expectedBytes = ByteArray.fromHexString("35154F15409907FD431E20ABAD63EFB35F7992C71A847E70DB0AA95D13D51279D489C6A4BFBFA57F03E114BAC5808A8AFF1C666818C5FAF8225CE4C27D22BFC3");
161+
162+
// Action
163+
final Sha512Hash sha512Hash = HashUtil.sha512(MutableByteArray.wrap(preImage));
164+
System.out.println(sha512Hash);
165+
166+
// Assert
167+
Assert.assertEquals(expectedBytes, sha512Hash);
168+
}
169+
}

0 commit comments

Comments
 (0)
Please sign in to comment.