Server POW (work in progress)
This commit is contained in:
parent
73383aa2c2
commit
b0828ec1e5
@ -0,0 +1,84 @@
|
|||||||
|
package ch.dissem.apps.abit.adapter;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ch.dissem.apps.abit.util.PRNGFixes;
|
||||||
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
|
import ch.dissem.bitmessage.entity.PlaintextHolder;
|
||||||
|
import ch.dissem.bitmessage.entity.payload.Broadcast;
|
||||||
|
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||||
|
import ch.dissem.bitmessage.factory.Factory;
|
||||||
|
import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
|
||||||
|
import ch.dissem.bitmessage.security.sc.SpongySecurity;
|
||||||
|
import ch.dissem.bitmessage.utils.UnixTime;
|
||||||
|
|
||||||
|
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SERVER_POW;
|
||||||
|
import static ch.dissem.bitmessage.entity.Plaintext.Status.SENT;
|
||||||
|
import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST;
|
||||||
|
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Basler
|
||||||
|
*/
|
||||||
|
public class AndroidSecurity extends SpongySecurity {
|
||||||
|
private final SharedPreferences preferences;
|
||||||
|
|
||||||
|
public AndroidSecurity(Context ctx) {
|
||||||
|
PRNGFixes.apply();
|
||||||
|
preferences = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes, ProofOfWorkEngine.Callback callback) {
|
||||||
|
if (preferences.getBoolean(PREFERENCE_SERVER_POW, false)) {
|
||||||
|
object.setNonce(new byte[8]);
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
object.write(out);
|
||||||
|
sendAsBroadcast(getContext().getAddressRepo().getIdentities().get(0), out.toByteArray());
|
||||||
|
if (object.getPayload() instanceof PlaintextHolder) {
|
||||||
|
Plaintext plaintext = ((PlaintextHolder) object.getPayload()).getPlaintext();
|
||||||
|
plaintext.setInventoryVector(object.getInventoryVector());
|
||||||
|
plaintext.setStatus(SENT);
|
||||||
|
plaintext.removeLabel(Label.Type.OUTBOX);
|
||||||
|
plaintext.addLabels(getContext().getMessageRepository().getLabels(Label.Type.SENT));
|
||||||
|
getContext().getMessageRepository().save(plaintext);
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.doProofOfWork(object, nonceTrialsPerByte, extraBytes, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendAsBroadcast(BitmessageAddress identity, byte[] data) throws IOException {
|
||||||
|
Plaintext msg = new Plaintext.Builder(BROADCAST)
|
||||||
|
.from(identity)
|
||||||
|
.message(data)
|
||||||
|
.build();
|
||||||
|
Broadcast payload = Factory.getBroadcast(identity, msg);
|
||||||
|
long expires = UnixTime.now(+2 * DAY);
|
||||||
|
final ObjectMessage object = new ObjectMessage.Builder()
|
||||||
|
.stream(identity.getStream())
|
||||||
|
.expiresTime(expires)
|
||||||
|
.payload(payload)
|
||||||
|
.build();
|
||||||
|
object.sign(identity.getPrivateKey());
|
||||||
|
payload.encrypt();
|
||||||
|
object.setNonce(new byte[8]);
|
||||||
|
|
||||||
|
getContext().getInventory().storeObject(object);
|
||||||
|
getContext().getNetworkHandler().offer(object.getInventoryVector());
|
||||||
|
// TODO: offer to the trusted node only?
|
||||||
|
// at least make sure it is offered to the trusted node!
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package ch.dissem.apps.abit.service;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import ch.dissem.apps.abit.adapter.AndroidSecurity;
|
||||||
import ch.dissem.apps.abit.listener.MessageListener;
|
import ch.dissem.apps.abit.listener.MessageListener;
|
||||||
import ch.dissem.apps.abit.repository.AndroidAddressRepository;
|
import ch.dissem.apps.abit.repository.AndroidAddressRepository;
|
||||||
import ch.dissem.apps.abit.repository.AndroidInventory;
|
import ch.dissem.apps.abit.repository.AndroidInventory;
|
||||||
@ -30,7 +31,7 @@ public class Singleton {
|
|||||||
SqlHelper sqlHelper = new SqlHelper(ctx);
|
SqlHelper sqlHelper = new SqlHelper(ctx);
|
||||||
bitmessageContext = new BitmessageContext.Builder()
|
bitmessageContext = new BitmessageContext.Builder()
|
||||||
.proofOfWorkEngine(new ServicePowEngine(ctx))
|
.proofOfWorkEngine(new ServicePowEngine(ctx))
|
||||||
.security(new SpongySecurity())
|
.security(new AndroidSecurity(ctx))
|
||||||
.nodeRegistry(new MemoryNodeRegistry())
|
.nodeRegistry(new MemoryNodeRegistry())
|
||||||
.inventory(new AndroidInventory(sqlHelper))
|
.inventory(new AndroidInventory(sqlHelper))
|
||||||
.addressRepo(new AndroidAddressRepository(sqlHelper))
|
.addressRepo(new AndroidAddressRepository(sqlHelper))
|
||||||
|
@ -20,6 +20,9 @@ import ch.dissem.apps.abit.notification.ErrorNotification;
|
|||||||
import ch.dissem.apps.abit.service.Singleton;
|
import ch.dissem.apps.abit.service.Singleton;
|
||||||
import ch.dissem.bitmessage.BitmessageContext;
|
import ch.dissem.bitmessage.BitmessageContext;
|
||||||
|
|
||||||
|
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SYNC_TIMEOUT;
|
||||||
|
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync Adapter to synchronize with the Bitmessage network - fetches
|
* Sync Adapter to synchronize with the Bitmessage network - fetches
|
||||||
* new objects and then disconnects.
|
* new objects and then disconnects.
|
||||||
@ -48,7 +51,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
|
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||||
|
|
||||||
String trustedNode = preferences.getString("trusted_node", null);
|
String trustedNode = preferences.getString(PREFERENCE_TRUSTED_NODE, null);
|
||||||
if (trustedNode == null) return;
|
if (trustedNode == null) return;
|
||||||
trustedNode = trustedNode.trim();
|
trustedNode = trustedNode.trim();
|
||||||
if (trustedNode.isEmpty()) return;
|
if (trustedNode.isEmpty()) return;
|
||||||
@ -69,7 +72,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
} else {
|
} else {
|
||||||
port = 8444;
|
port = 8444;
|
||||||
}
|
}
|
||||||
long timeoutInSeconds = Long.parseLong(preferences.getString("sync_timeout", "120"));
|
long timeoutInSeconds = Long.parseLong(preferences.getString(PREFERENCE_SYNC_TIMEOUT, "120"));
|
||||||
try {
|
try {
|
||||||
LOG.info("Synchronization started");
|
LOG.info("Synchronization started");
|
||||||
bmc.synchronize(InetAddress.getByName(trustedNode), port, timeoutInSeconds, true);
|
bmc.synchronize(InetAddress.getByName(trustedNode), port, timeoutInSeconds, true);
|
||||||
|
@ -3,9 +3,14 @@ package ch.dissem.apps.abit.util;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by chrigu on 16.11.15.
|
* @author Christian Basler
|
||||||
*/
|
*/
|
||||||
public class Constants {
|
public class Constants {
|
||||||
|
public static final String PREFERENCE_WIFI_ONLY = "wifi_only";
|
||||||
|
public static final String PREFERENCE_TRUSTED_NODE = "trusted_node";
|
||||||
|
public static final String PREFERENCE_SYNC_TIMEOUT = "sync_timeout";
|
||||||
|
public static final String PREFERENCE_SERVER_POW = "server_pow";
|
||||||
|
|
||||||
public static final String BITMESSAGE_URL_SCHEMA = "bitmessage:";
|
public static final String BITMESSAGE_URL_SCHEMA = "bitmessage:";
|
||||||
public static final Pattern BITMESSAGE_ADDRESS_PATTERN = Pattern.compile("\\bBM-[a-zA-Z0-9]+\\b");
|
public static final Pattern BITMESSAGE_ADDRESS_PATTERN = Pattern.compile("\\bBM-[a-zA-Z0-9]+\\b");
|
||||||
}
|
}
|
||||||
|
341
app/src/main/java/ch/dissem/apps/abit/util/PRNGFixes.java
Normal file
341
app/src/main/java/ch/dissem/apps/abit/util/PRNGFixes.java
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
package ch.dissem.apps.abit.util;
|
||||||
|
/*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will Google be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, as long as the origin is not misrepresented.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.SecureRandomSpi;
|
||||||
|
import java.security.Security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixes for the output of the default PRNG having low entropy.
|
||||||
|
* <p/>
|
||||||
|
* The fixes need to be applied via {@link #apply()} before any use of Java
|
||||||
|
* Cryptography Architecture primitives. A good place to invoke them is in the
|
||||||
|
* application's {@code onCreate}.
|
||||||
|
*
|
||||||
|
* @see <a href="http://android-developers.blogspot.ch/2013/08/some-securerandom-thoughts.html">
|
||||||
|
* http://android-developers.blogspot.ch/2013/08/some-securerandom-thoughts.html</a>
|
||||||
|
*/
|
||||||
|
public final class PRNGFixes {
|
||||||
|
|
||||||
|
private static final int VERSION_CODE_JELLY_BEAN = 16;
|
||||||
|
private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
|
||||||
|
private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL =
|
||||||
|
getBuildFingerprintAndDeviceSerial();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hidden constructor to prevent instantiation.
|
||||||
|
*/
|
||||||
|
private PRNGFixes() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies all fixes.
|
||||||
|
*
|
||||||
|
* @throws SecurityException if a fix is needed but could not be applied.
|
||||||
|
*/
|
||||||
|
public static void apply() {
|
||||||
|
applyOpenSSLFix();
|
||||||
|
installLinuxPRNGSecureRandom();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
|
||||||
|
* fix is not needed.
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the fix is needed but could not be applied.
|
||||||
|
*/
|
||||||
|
private static void applyOpenSSLFix() throws SecurityException {
|
||||||
|
if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
|
||||||
|
|| (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
|
||||||
|
// No need to apply the fix
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Mix in the device- and invocation-specific seed.
|
||||||
|
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||||
|
.getMethod("RAND_seed", byte[].class)
|
||||||
|
.invoke(null, generateSeed());
|
||||||
|
|
||||||
|
// Mix output of Linux PRNG into OpenSSL's PRNG
|
||||||
|
int bytesRead = (Integer) Class.forName(
|
||||||
|
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||||
|
.getMethod("RAND_load_file", String.class, long.class)
|
||||||
|
.invoke(null, "/dev/urandom", 1024);
|
||||||
|
if (bytesRead != 1024) {
|
||||||
|
throw new IOException(
|
||||||
|
"Unexpected number of bytes read from Linux PRNG: "
|
||||||
|
+ bytesRead);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SecurityException("Failed to seed OpenSSL PRNG", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
|
||||||
|
* default. Does nothing if the implementation is already the default or if
|
||||||
|
* there is not need to install the implementation.
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the fix is needed but could not be applied.
|
||||||
|
*/
|
||||||
|
private static void installLinuxPRNGSecureRandom()
|
||||||
|
throws SecurityException {
|
||||||
|
if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
|
||||||
|
// No need to apply the fix
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install a Linux PRNG-based SecureRandom implementation as the
|
||||||
|
// default, if not yet installed.
|
||||||
|
Provider[] secureRandomProviders =
|
||||||
|
Security.getProviders("SecureRandom.SHA1PRNG");
|
||||||
|
if ((secureRandomProviders == null)
|
||||||
|
|| (secureRandomProviders.length < 1)
|
||||||
|
|| (!LinuxPRNGSecureRandomProvider.class.equals(
|
||||||
|
secureRandomProviders[0].getClass()))) {
|
||||||
|
Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that new SecureRandom() and
|
||||||
|
// SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
|
||||||
|
// by the Linux PRNG-based SecureRandom implementation.
|
||||||
|
SecureRandom rng1 = new SecureRandom();
|
||||||
|
if (!LinuxPRNGSecureRandomProvider.class.equals(
|
||||||
|
rng1.getProvider().getClass())) {
|
||||||
|
throw new SecurityException(
|
||||||
|
"new SecureRandom() backed by wrong Provider: "
|
||||||
|
+ rng1.getProvider().getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
SecureRandom rng2;
|
||||||
|
try {
|
||||||
|
rng2 = SecureRandom.getInstance("SHA1PRNG");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new SecurityException("SHA1PRNG not available", e);
|
||||||
|
}
|
||||||
|
if (!LinuxPRNGSecureRandomProvider.class.equals(
|
||||||
|
rng2.getProvider().getClass())) {
|
||||||
|
throw new SecurityException(
|
||||||
|
"SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
|
||||||
|
+ " Provider: " + rng2.getProvider().getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code Provider} of {@code SecureRandom} engines which pass through
|
||||||
|
* all requests to the Linux PRNG.
|
||||||
|
*/
|
||||||
|
private static class LinuxPRNGSecureRandomProvider extends Provider {
|
||||||
|
|
||||||
|
public LinuxPRNGSecureRandomProvider() {
|
||||||
|
super("LinuxPRNG",
|
||||||
|
1.0,
|
||||||
|
"A Linux-specific random number provider that uses"
|
||||||
|
+ " /dev/urandom");
|
||||||
|
// Although /dev/urandom is not a SHA-1 PRNG, some apps
|
||||||
|
// explicitly request a SHA1PRNG SecureRandom and we thus need to
|
||||||
|
// prevent them from getting the default implementation whose output
|
||||||
|
// may have low entropy.
|
||||||
|
put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
|
||||||
|
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link SecureRandomSpi} which passes all requests to the Linux PRNG
|
||||||
|
* ({@code /dev/urandom}).
|
||||||
|
*/
|
||||||
|
public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
|
||||||
|
* are passed through to the Linux PRNG (/dev/urandom). Instances of
|
||||||
|
* this class seed themselves by mixing in the current time, PID, UID,
|
||||||
|
* build fingerprint, and hardware serial number (where available) into
|
||||||
|
* Linux PRNG.
|
||||||
|
*
|
||||||
|
* Concurrency: Read requests to the underlying Linux PRNG are
|
||||||
|
* serialized (on sLock) to ensure that multiple threads do not get
|
||||||
|
* duplicated PRNG output.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static final File URANDOM_FILE = new File("/dev/urandom");
|
||||||
|
|
||||||
|
private static final Object sLock = new Object();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Input stream for reading from Linux PRNG or {@code null} if not yet
|
||||||
|
* opened.
|
||||||
|
*
|
||||||
|
* @GuardedBy("sLock")
|
||||||
|
*/
|
||||||
|
private static DataInputStream sUrandomIn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output stream for writing to Linux PRNG or {@code null} if not yet
|
||||||
|
* opened.
|
||||||
|
*
|
||||||
|
* @GuardedBy("sLock")
|
||||||
|
*/
|
||||||
|
private static OutputStream sUrandomOut;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this engine instance has been seeded. This is needed because
|
||||||
|
* each instance needs to seed itself if the client does not explicitly
|
||||||
|
* seed it.
|
||||||
|
*/
|
||||||
|
private boolean mSeeded;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void engineSetSeed(byte[] bytes) {
|
||||||
|
try {
|
||||||
|
OutputStream out;
|
||||||
|
synchronized (sLock) {
|
||||||
|
out = getUrandomOutputStream();
|
||||||
|
}
|
||||||
|
out.write(bytes);
|
||||||
|
out.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// On a small fraction of devices /dev/urandom is not writable.
|
||||||
|
// Log and ignore.
|
||||||
|
Log.w(PRNGFixes.class.getSimpleName(),
|
||||||
|
"Failed to mix seed into " + URANDOM_FILE);
|
||||||
|
} finally {
|
||||||
|
mSeeded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void engineNextBytes(byte[] bytes) {
|
||||||
|
if (!mSeeded) {
|
||||||
|
// Mix in the device- and invocation-specific seed.
|
||||||
|
engineSetSeed(generateSeed());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DataInputStream in;
|
||||||
|
synchronized (sLock) {
|
||||||
|
in = getUrandomInputStream();
|
||||||
|
}
|
||||||
|
synchronized (in) {
|
||||||
|
in.readFully(bytes);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SecurityException(
|
||||||
|
"Failed to read from " + URANDOM_FILE, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] engineGenerateSeed(int size) {
|
||||||
|
byte[] seed = new byte[size];
|
||||||
|
engineNextBytes(seed);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataInputStream getUrandomInputStream() {
|
||||||
|
synchronized (sLock) {
|
||||||
|
if (sUrandomIn == null) {
|
||||||
|
// NOTE: Consider inserting a BufferedInputStream between
|
||||||
|
// DataInputStream and FileInputStream if you need higher
|
||||||
|
// PRNG output performance and can live with future PRNG
|
||||||
|
// output being pulled into this process prematurely.
|
||||||
|
try {
|
||||||
|
sUrandomIn = new DataInputStream(
|
||||||
|
new FileInputStream(URANDOM_FILE));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SecurityException("Failed to open "
|
||||||
|
+ URANDOM_FILE + " for reading", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sUrandomIn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputStream getUrandomOutputStream() throws IOException {
|
||||||
|
synchronized (sLock) {
|
||||||
|
if (sUrandomOut == null) {
|
||||||
|
sUrandomOut = new FileOutputStream(URANDOM_FILE);
|
||||||
|
}
|
||||||
|
return sUrandomOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a device- and invocation-specific seed to be mixed into the
|
||||||
|
* Linux PRNG.
|
||||||
|
*/
|
||||||
|
private static byte[] generateSeed() {
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream seedBufferOut =
|
||||||
|
new DataOutputStream(seedBuffer);
|
||||||
|
seedBufferOut.writeLong(System.currentTimeMillis());
|
||||||
|
seedBufferOut.writeLong(System.nanoTime());
|
||||||
|
seedBufferOut.writeInt(Process.myPid());
|
||||||
|
seedBufferOut.writeInt(Process.myUid());
|
||||||
|
seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
|
||||||
|
seedBufferOut.close();
|
||||||
|
return seedBuffer.toByteArray();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SecurityException("Failed to generate seed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the hardware serial number of this device.
|
||||||
|
*
|
||||||
|
* @return serial number or {@code null} if not available.
|
||||||
|
*/
|
||||||
|
private static String getDeviceSerialNumber() {
|
||||||
|
// We're using the Reflection API because Build.SERIAL is only available
|
||||||
|
// since API Level 9 (Gingerbread, Android 2.3).
|
||||||
|
try {
|
||||||
|
return (String) Build.class.getField("SERIAL").get(null);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] getBuildFingerprintAndDeviceSerial() {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
String fingerprint = Build.FINGERPRINT;
|
||||||
|
if (fingerprint != null) {
|
||||||
|
result.append(fingerprint);
|
||||||
|
}
|
||||||
|
String serial = getDeviceSerialNumber();
|
||||||
|
if (serial != null) {
|
||||||
|
result.append(serial);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return result.toString().getBytes("UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException("UTF-8 encoding not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -49,4 +49,6 @@
|
|||||||
<string name="compose_body_hint">Nachricht schreiben</string>
|
<string name="compose_body_hint">Nachricht schreiben</string>
|
||||||
<string name="contacts_and_subscriptions">Kontakte</string>
|
<string name="contacts_and_subscriptions">Kontakte</string>
|
||||||
<string name="subscribed">Abonniert</string>
|
<string name="subscribed">Abonniert</string>
|
||||||
|
<string name="server_pow">Server POW</string>
|
||||||
|
<string name="server_pow_summary">Der vertrauenswürdige Knoten macht den Proof of Work</string>
|
||||||
</resources>
|
</resources>
|
@ -49,4 +49,6 @@
|
|||||||
<string name="compose_body_hint">Write message</string>
|
<string name="compose_body_hint">Write message</string>
|
||||||
<string name="contacts_and_subscriptions">Contacts</string>
|
<string name="contacts_and_subscriptions">Contacts</string>
|
||||||
<string name="subscribed">Subscribed</string>
|
<string name="subscribed">Subscribed</string>
|
||||||
|
<string name="server_pow">Server POW</string>
|
||||||
|
<string name="server_pow_summary">Trusted node does proof of work</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -24,4 +24,11 @@
|
|||||||
android:key="sync_timeout"
|
android:key="sync_timeout"
|
||||||
android:summary="@string/sync_timeout_summary"
|
android:summary="@string/sync_timeout_summary"
|
||||||
android:title="@string/sync_timeout" />
|
android:title="@string/sync_timeout" />
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="server_pow"
|
||||||
|
android:dependency="@string/trusted_node"
|
||||||
|
android:title="@string/server_pow"
|
||||||
|
android:summary="@string/server_pow_summary"
|
||||||
|
/>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user