Introducing the crypto package with utilities to generate random numbers
This commit is contained in:
parent
b745200b53
commit
f5065b215b
8 changed files with 329 additions and 34 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -75,3 +75,5 @@ atlassian-ide-plugin.xml
|
||||||
# Logs
|
# Logs
|
||||||
# ----
|
# ----
|
||||||
/*.log
|
/*.log
|
||||||
|
|
||||||
|
src/main/java/com/luminiasoft/bitshares/mycelium/*
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package com.luminiasoft.bitshares;
|
package com.luminiasoft.bitshares;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.Base58;
|
||||||
|
import org.bitcoinj.core.DumpedPrivateKey;
|
||||||
import org.bitcoinj.core.ECKey;
|
import org.bitcoinj.core.ECKey;
|
||||||
|
import org.bitcoinj.core.NetworkParameters;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
@ -16,15 +19,17 @@ public class BrainKey {
|
||||||
public BrainKey(String words, int sequence) {
|
public BrainKey(String words, int sequence) {
|
||||||
String encoded = String.format("%s %d", words, sequence);
|
String encoded = String.format("%s %d", words, sequence);
|
||||||
try {
|
try {
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA-512");
|
MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
|
||||||
byte[] bytes = md.digest(encoded.getBytes("UTF-8"));
|
|
||||||
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
|
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
|
||||||
byte[] result = sha256.digest(bytes);
|
mPrivateKey = ECKey.fromPrivate(sha256.digest(sha512.digest(encoded.getBytes("UTF-8"))));
|
||||||
mPrivateKey = ECKey.fromPrivate(result);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
System.out.println("NoSuchAlgotithmException. Msg: " + e.getMessage());
|
System.out.println("NoSuchAlgotithmException. Msg: " + e.getMessage());
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
System.out.println("UnsupportedEncodingException. Msg: " + e.getMessage());
|
System.out.println("UnsupportedEncodingException. Msg: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ECKey getPrivateKey(){
|
||||||
|
return this.mPrivateKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,17 @@ import java.io.IOException;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
// Brain key from Nelson's app referencing the bilthon-83 account
|
// Brain key from Nelson's app referencing the bilthon-83 account
|
||||||
public static final String BRAIN_KEY = "PUMPER ISOTOME SERE STAINER CLINGER MOONLIT CHAETA UPBRIM AEDILIC BERTHER NIT SHAP SAID SHADING JUNCOUS CHOUGH";
|
// public static final String BRAIN_KEY = "PUMPER ISOTOME SERE STAINER CLINGER MOONLIT CHAETA UPBRIM AEDILIC BERTHER NIT SHAP SAID SHADING JUNCOUS CHOUGH";
|
||||||
|
|
||||||
// WIF from Nelson's app referencing the bilthon-83 account
|
// WIF from Nelson's app referencing the bilthon-83 account
|
||||||
public static final String WIF = "5J96pne45qWM1WpektoeazN6k9Mt93jQ7LyueRxFfEMTiy6yxjM";
|
// public static final String WIF = "5J96pne45qWM1WpektoeazN6k9Mt93jQ7LyueRxFfEMTiy6yxjM";
|
||||||
|
|
||||||
|
// Brain key from an empty account created by the cli_wallet
|
||||||
|
public static final String BRAIN_KEY = "TWIXT SERMO TRILLI AUDIO PARDED PLUMET BIWA REHUNG MAUDLE VALVULA OUTBURN FEWNESS ALIENER UNTRACE PRICH TROKER";
|
||||||
|
|
||||||
|
// WIF from an emty account created by the cli_wallet
|
||||||
|
public static final String WIF = "5KMzB2GqGhnh7ufhgddmz1eKPHS72uTLeL9hHjSvPb1UywWknF5";
|
||||||
|
|
||||||
// WIF from the cli_wallet instance
|
|
||||||
// public static final String WIF = "5KMzB2GqGhnh7ufhgddmz1eKPHS72uTLeL9hHjSvPb1UywWknF5";
|
|
||||||
public static final String EXTERNAL_SIGNATURE = "1f36c41acb774fcbc9c231b5895ec9701d6872729098d8ea56d78dda72a6b54252694db85d7591de5751b7aea06871da15d63a1028758421607ffc143e53ef3306";
|
public static final String EXTERNAL_SIGNATURE = "1f36c41acb774fcbc9c231b5895ec9701d6872729098d8ea56d78dda72a6b54252694db85d7591de5751b7aea06871da15d63a1028758421607ffc143e53ef3306";
|
||||||
|
|
||||||
// Static block information used for transaction serialization tests
|
// Static block information used for transaction serialization tests
|
||||||
|
|
|
@ -504,6 +504,22 @@ public class Test {
|
||||||
*/
|
*/
|
||||||
public void testBrainKeyOperations(){
|
public void testBrainKeyOperations(){
|
||||||
BrainKey brainKey = new BrainKey(Main.BRAIN_KEY, 0);
|
BrainKey brainKey = new BrainKey(Main.BRAIN_KEY, 0);
|
||||||
|
ECKey key = brainKey.getPrivateKey();
|
||||||
|
String wif = key.getPrivateKeyAsWiF(NetworkParameters.fromID(NetworkParameters.ID_MAINNET));
|
||||||
|
System.out.println("wif compressed: "+wif);
|
||||||
|
String wif2 = key.decompress().getPrivateKeyAsWiF(NetworkParameters.fromID(NetworkParameters.ID_MAINNET));
|
||||||
|
System.out.println("wif decompressed: "+wif2);
|
||||||
|
|
||||||
|
byte[] pubKey1 = key.decompress().getPubKey();
|
||||||
|
System.out.println("decompressed public key: "+Base58.encode(pubKey1));
|
||||||
|
byte[] pubKey2 = key.getPubKey();
|
||||||
|
System.out.println("compressed public key: "+Base58.encode(pubKey2));
|
||||||
|
|
||||||
|
System.out.println("pub key compressed : "+Util.bytesToHex(pubKey1));
|
||||||
|
System.out.println("pub key uncompressed : "+Util.bytesToHex(pubKey2));
|
||||||
|
|
||||||
|
byte[] pubKey3 = key.getPubKeyPoint().getEncoded(true);
|
||||||
|
System.out.println("pub key compressed : "+Base58.encode(pubKey3));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2013, 2014 Megion Research and Development GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Microsoft Reference Source License (MS-RSL)
|
||||||
|
*
|
||||||
|
* This license governs use of the accompanying software. If you use the software, you accept this license.
|
||||||
|
* If you do not accept the license, do not use the software.
|
||||||
|
*
|
||||||
|
* 1. Definitions
|
||||||
|
* The terms "reproduce," "reproduction," and "distribution" have the same meaning here as under U.S. copyright law.
|
||||||
|
* "You" means the licensee of the software.
|
||||||
|
* "Your company" means the company you worked for when you downloaded the software.
|
||||||
|
* "Reference use" means use of the software within your company as a reference, in read only form, for the sole purposes
|
||||||
|
* of debugging your products, maintaining your products, or enhancing the interoperability of your products with the
|
||||||
|
* software, and specifically excludes the right to distribute the software outside of your company.
|
||||||
|
* "Licensed patents" means any Licensor patent claims which read directly on the software as distributed by the Licensor
|
||||||
|
* under this license.
|
||||||
|
*
|
||||||
|
* 2. Grant of Rights
|
||||||
|
* (A) Copyright Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive,
|
||||||
|
* worldwide, royalty-free copyright license to reproduce the software for reference use.
|
||||||
|
* (B) Patent Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive,
|
||||||
|
* worldwide, royalty-free patent license under licensed patents for reference use.
|
||||||
|
*
|
||||||
|
* 3. Limitations
|
||||||
|
* (A) No Trademark License- This license does not grant you any rights to use the Licensor’s name, logo, or trademarks.
|
||||||
|
* (B) If you begin patent litigation against the Licensor over patents that you think may apply to the software
|
||||||
|
* (including a cross-claim or counterclaim in a lawsuit), your license to the software ends automatically.
|
||||||
|
* (C) The software is licensed "as-is." You bear the risk of using it. The Licensor gives no express warranties,
|
||||||
|
* guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot
|
||||||
|
* change. To the extent permitted under your local laws, the Licensor excludes the implied warranties of merchantability,
|
||||||
|
* fitness for a particular purpose and non-infringement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.luminiasoft.bitshares.crypto;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|
||||||
|
public class AndroidRandomSource implements RandomSource {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void nextBytes(byte[] bytes) {
|
||||||
|
// On Android we use /dev/urandom for providing random data
|
||||||
|
File file = new File("/dev/urandom");
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new RuntimeException("Unable to generate random bytes on this Android device");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
FileInputStream stream = new FileInputStream(file);
|
||||||
|
DataInputStream dis = new DataInputStream(stream);
|
||||||
|
dis.readFully(bytes);
|
||||||
|
dis.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Unable to generate random bytes on this Android device", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.luminiasoft.bitshares.crypto;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple interface that can be used to retrieve entropy from any source.
|
||||||
|
*
|
||||||
|
* @author owlstead
|
||||||
|
*/
|
||||||
|
public interface EntropySource {
|
||||||
|
/**
|
||||||
|
* Retrieves the entropy.
|
||||||
|
* The position of the ByteBuffer must be advanced to the limit by any users calling this method.
|
||||||
|
* The values of the bytes between the position and limit should be set to zero by any users calling this method.
|
||||||
|
*
|
||||||
|
* @return entropy within the position and limit of the given buffer
|
||||||
|
*/
|
||||||
|
ByteBuffer provideEntropy();
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2013, 2014 Megion Research & Development GmbH
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.luminiasoft.bitshares.crypto;
|
||||||
|
|
||||||
|
public interface RandomSource {
|
||||||
|
/**
|
||||||
|
* Generates a user specified number of random bytes
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* The array to fill with random bytes
|
||||||
|
*/
|
||||||
|
void nextBytes(byte[] bytes);
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
package com.luminiasoft.bitshares.crypto;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.security.DigestException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A strengthener that can be used to generate and re-seed random number
|
||||||
|
* generators that do not seed themselves appropriately.
|
||||||
|
*
|
||||||
|
* @author owlstead
|
||||||
|
*/
|
||||||
|
public class SecureRandomStrengthener {
|
||||||
|
private static final String DEFAULT_PSEUDO_RANDOM_NUMBER_GENERATOR = "SHA1PRNG";
|
||||||
|
|
||||||
|
private static final EntropySource mTimeEntropySource = new EntropySource() {
|
||||||
|
|
||||||
|
final ByteBuffer timeBuffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE
|
||||||
|
* 2);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer provideEntropy() {
|
||||||
|
this.timeBuffer.clear();
|
||||||
|
this.timeBuffer.putLong(System.currentTimeMillis());
|
||||||
|
this.timeBuffer.putLong(System.nanoTime());
|
||||||
|
this.timeBuffer.flip();
|
||||||
|
return this.timeBuffer;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final String algorithm;
|
||||||
|
private final List<EntropySource> entropySources = new LinkedList<EntropySource>();
|
||||||
|
private final MessageDigest digest;
|
||||||
|
private final ByteBuffer seedBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an instance of a {@link SecureRandomStrengthener} that
|
||||||
|
* generates and re-seeds instances of {@code "SHA1PRNG"}.
|
||||||
|
*
|
||||||
|
* @return the strengthener, never null
|
||||||
|
*/
|
||||||
|
public static SecureRandomStrengthener getInstance() {
|
||||||
|
return new SecureRandomStrengthener(
|
||||||
|
DEFAULT_PSEUDO_RANDOM_NUMBER_GENERATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an instance of a {@link SecureRandomStrengthener} that
|
||||||
|
* generates instances of the given argument. Note that the availability of
|
||||||
|
* the given algorithm arguments in not tested until generation.
|
||||||
|
*
|
||||||
|
* @param algorithm
|
||||||
|
* the algorithm indicating the {@link SecureRandom} instance to
|
||||||
|
* use
|
||||||
|
* @return the strengthener, never null
|
||||||
|
*/
|
||||||
|
public static SecureRandomStrengthener getInstance(final String algorithm) {
|
||||||
|
return new SecureRandomStrengthener(algorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecureRandomStrengthener(final String algorithm) {
|
||||||
|
if (algorithm == null || algorithm.length() == 0) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Please provide a PRNG algorithm string such as SHA1PRNG");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
try {
|
||||||
|
this.digest = MessageDigest.getInstance("SHA1");
|
||||||
|
} catch (final NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"MessageDigest to create seed not available", e);
|
||||||
|
}
|
||||||
|
this.seedBuffer = ByteBuffer.allocate(this.digest.getDigestLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an entropy source, which will be called for each generation and
|
||||||
|
* re-seeding of the given random number generator.
|
||||||
|
*
|
||||||
|
* @param source
|
||||||
|
* the source of entropy
|
||||||
|
*/
|
||||||
|
public void addEntropySource(final EntropySource source) {
|
||||||
|
if (source == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"EntropySource should not be null");
|
||||||
|
}
|
||||||
|
this.entropySources.add(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates and seeds a random number generator of the configured
|
||||||
|
* algorithm. Calls the {@link EntropySource#provideEntropy()} method of all
|
||||||
|
* added sources of entropy.
|
||||||
|
*
|
||||||
|
* @return the random number generator
|
||||||
|
*/
|
||||||
|
public SecureRandom generateAndSeedRandomNumberGenerator() {
|
||||||
|
final SecureRandom secureRandom;
|
||||||
|
try {
|
||||||
|
secureRandom = SecureRandom.getInstance(this.algorithm);
|
||||||
|
} catch (final NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException("PRNG is not available", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
reseed(secureRandom);
|
||||||
|
return secureRandom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-seeds the random number generator. Calls the
|
||||||
|
* {@link EntropySource#provideEntropy()} method of all added sources of
|
||||||
|
* entropy.
|
||||||
|
*
|
||||||
|
* @param secureRandom
|
||||||
|
* the random number generator to re-seed
|
||||||
|
*/
|
||||||
|
public void reseed(final SecureRandom secureRandom) {
|
||||||
|
this.seedBuffer.clear();
|
||||||
|
secureRandom.nextBytes(this.seedBuffer.array());
|
||||||
|
|
||||||
|
for (final EntropySource source : this.entropySources) {
|
||||||
|
final ByteBuffer entropy = source.provideEntropy();
|
||||||
|
if (entropy == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ByteBuffer wipeBuffer = entropy.duplicate();
|
||||||
|
this.digest.update(entropy);
|
||||||
|
wipe(wipeBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.digest.update(mTimeEntropySource.provideEntropy());
|
||||||
|
this.digest.update(this.seedBuffer);
|
||||||
|
this.seedBuffer.clear();
|
||||||
|
// remove data from seedBuffer so it won't be retrievable
|
||||||
|
|
||||||
|
// reuse
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.digest.digest(this.seedBuffer.array(), 0,
|
||||||
|
this.seedBuffer.capacity());
|
||||||
|
} catch (final DigestException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"DigestException should not be thrown", e);
|
||||||
|
}
|
||||||
|
secureRandom.setSeed(this.seedBuffer.array());
|
||||||
|
|
||||||
|
wipe(this.seedBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void wipe(final ByteBuffer buf) {
|
||||||
|
while (buf.hasRemaining()) {
|
||||||
|
buf.put((byte) 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue