private CryptoBox(Builder builder) { initializationVector = builder.initializationVector; curveType = builder.curveType; R = Security.createPoint(builder.xComponent, builder.yComponent); encrypted = builder.encrypted; mac = builder.mac; }
public PrivateKey(boolean shorter, long stream, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { byte[] privSK; byte[] pubSK; byte[] privEK; byte[] pubEK; byte[] ripe; do { privSK = Security.randomBytes(PRIVATE_KEY_SIZE); privEK = Security.randomBytes(PRIVATE_KEY_SIZE); pubSK = Security.createPublicKey(privSK).getEncoded(false); pubEK = Security.createPublicKey(privEK).getEncoded(false); ripe = Pubkey.getRipe(pubSK, pubEK); } while (ripe[0] != 0 || (shorter && ripe[1] != 0)); this.privateSigningKey = privSK; this.privateEncryptionKey = privEK; this.pubkey = Security.createPubkey(Pubkey.LATEST_VERSION, stream, privateSigningKey, privateEncryptionKey, nonceTrialsPerByte, extraBytes, features); }
/** * @param object to be checked * @param nonceTrialsPerByte difficulty * @param extraBytes bytes to add to the object size * @throws InsufficientProofOfWorkException if proof of work doesn't check out (makes it more difficult to send small messages) */ public static void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) throws IOException { byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); byte[] value = Security.doubleSha512(object.getNonce(), getInitialHash(object)); if (Bytes.lt(target, value, 8)) { throw new InsufficientProofOfWorkException(target, value); } }
public PrivateKey(long version, long stream, String passphrase, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { try { // FIXME: this is most definitely wrong this.privateSigningKey = Bytes.truncate(Security.sha512(passphrase.getBytes("UTF-8"), new byte[]{0}), 32); this.privateEncryptionKey = Bytes.truncate(Security.sha512(passphrase.getBytes("UTF-8"), new byte[]{1}), 32); this.pubkey = Security.createPubkey(version, stream, privateSigningKey, privateEncryptionKey, nonceTrialsPerByte, extraBytes, features); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } }
public CryptoBox(Streamable data, ECPoint K) throws IOException { curveType = 0x02CA; // 1. The destination public key is called K. // 2. Generate 16 random bytes using a secure random number generator. Call them IV. initializationVector = Security.randomBytes(16); // 3. Generate a new random EC key pair with private key called r and public key called R. byte[] r = Security.randomBytes(PRIVATE_KEY_SIZE); R = Security.createPublicKey(r); // 4. Do an EC point multiply with public key K and private key r. This gives you public key P. ECPoint P = K.multiply(Security.keyToBigInt(r)).normalize(); byte[] X = P.getXCoord().getEncoded(); // 5. Use the X component of public key P and calculate the SHA512 hash H. byte[] H = Security.sha512(X); // 6. The first 32 bytes of H are called key_e and the last 32 bytes are called key_m. byte[] key_e = Arrays.copyOfRange(H, 0, 32); byte[] key_m = Arrays.copyOfRange(H, 32, 64); // 7. Pad the input text to a multiple of 16 bytes, in accordance to PKCS7. // 8. Encrypt the data with AES-256-CBC, using IV as initialization vector, key_e as encryption key and the padded input text as payload. Call the output cipher text. encrypted = crypt(true, Encode.bytes(data), key_e); // 9. Calculate a 32 byte MAC with HMACSHA256, using key_m as salt and IV + R + cipher text as data. Call the output MAC. mac = calculateMac(key_m); // The resulting data is: IV + R + cipher text + MAC }
BitmessageAddress(long version, long stream, byte[] ripe) { try { this.version = version; this.stream = stream; this.ripe = ripe; ByteArrayOutputStream os = new ByteArrayOutputStream(); Encode.varInt(version, os); Encode.varInt(stream, os); if (version < 4) { byte[] checksum = Security.sha512(os.toByteArray(), ripe); this.tag = null; this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } else { // for tag and decryption key, the checksum has to be created with 0x00 padding byte[] checksum = Security.doubleSha512(os.toByteArray(), ripe); this.tag = Arrays.copyOfRange(checksum, 32, 64); this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } // but for the address and its checksum they need to be stripped int offset = Bytes.numberOfLeadingZeros(ripe); os.write(ripe, offset, ripe.length - offset); byte[] checksum = Security.doubleSha512(os.toByteArray()); os.write(checksum, 0, 4); this.address = "BM-" + Base58.encode(os.toByteArray()); } catch (IOException e) { throw new RuntimeException(e); } }
/** * Calculates the proof of work. This might take a long time, depending on the hardware, message size and time to * live. * * @param object to do the proof of work for * @param worker doing the actual proof of work * @param nonceTrialsPerByte difficulty * @param extraBytes bytes to add to the object size (makes it more difficult to send small messages) */ public static void doProofOfWork(ObjectMessage object, ProofOfWorkEngine worker, long nonceTrialsPerByte, long extraBytes) { try { if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000; if (extraBytes < 1000) extraBytes = 1000; byte[] initialHash = getInitialHash(object); byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); byte[] nonce = worker.calculateNonce(initialHash, target); object.setNonce(nonce); } catch (IOException e) { throw new RuntimeException(e); } }
public void sendPubkey(BitmessageAddress identity, long targetStream) { try { long expires = UnixTime.now(+28 * DAY); LOG.info("Expires at " + expires); ObjectMessage response = new ObjectMessage.Builder() .stream(targetStream) .expiresTime(expires) .payload(identity.getPubkey()) .build(); response.sign(identity.getPrivateKey()); response.encrypt(Security.createPublicKey(identity.getPublicDecryptionKey()).getEncoded(false)); Security.doProofOfWork(response, proofOfWorkEngine, networkNonceTrialsPerByte, networkExtraBytes); if (response.isSigned()) { response.sign(identity.getPrivateKey()); } if (response instanceof Encrypted) { response.encrypt(Security.createPublicKey(identity.getPublicDecryptionKey()).getEncoded(false)); } inventory.storeObject(response); networkHandler.offer(response.getInventoryVector()); // TODO: save that the pubkey was just sent, and on which stream! } catch (IOException e) { throw new RuntimeException(e); } }
public void requestPubkey(BitmessageAddress contact) { long expires = UnixTime.now(+2 * DAY); LOG.info("Expires at " + expires); ObjectMessage response = new ObjectMessage.Builder() .stream(contact.getStream()) .expiresTime(expires) .payload(new GetPubkey(contact)) .build(); Security.doProofOfWork(response, proofOfWorkEngine, networkNonceTrialsPerByte, networkExtraBytes); inventory.storeObject(response); networkHandler.offer(response.getInventoryVector()); }
/** * A helper method to calculate SHA-512 hashes. Please note that a new {@link MessageDigest} object is created at * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in * success on the same thread. * * @param data to get hashed * @return SHA-512 hash of data */ public static byte[] sha512(byte[]... data) { return hash("SHA-512", data); }
/** * Create a new public key fom given private keys. * * @param version of the public key / address * @param stream of the address * @param privateSigningKey private key used for signing * @param privateEncryptionKey private key used for encryption * @param nonceTrialsPerByte proof of work difficulty * @param extraBytes bytes to add for the proof of work (make it harder for small messages) * @param features of the address * @return a public key object */ public static Pubkey createPubkey(long version, long stream, byte[] privateSigningKey, byte[] privateEncryptionKey, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { return Factory.createPubkey(version, stream, createPublicKey(privateSigningKey).getEncoded(false), createPublicKey(privateEncryptionKey).getEncoded(false), nonceTrialsPerByte, extraBytes, features); }
public static byte[] calculateTag(long version, long stream, byte[] ripe) { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); Encode.varInt(version, out); Encode.varInt(stream, out); out.write(ripe); return Arrays.copyOfRange(Security.doubleSha512(out.toByteArray()), 32, 64); } catch (IOException e) { throw new RuntimeException(e); } }
public boolean isSignatureValid(Pubkey pubkey) throws IOException { if (isEncrypted()) throw new IllegalStateException("Payload must be decrypted first"); return Security.isSignatureValid(getBytesToSign(), payload.getSignature(), pubkey); }
public void sign(PrivateKey key) { if (payload.isSigned()) { payload.setSignature(Security.getSignature(getBytesToSign(), key)); } }
public BitmessageAddress(String address) { try { this.address = address; byte[] bytes = Base58.decode(address.substring(3)); ByteArrayInputStream in = new ByteArrayInputStream(bytes); AccessCounter counter = new AccessCounter(); this.version = varInt(in, counter); this.stream = varInt(in, counter); this.ripe = Bytes.expand(bytes(in, bytes.length - counter.length() - 4), 20); // test checksum byte[] checksum = Security.doubleSha512(bytes, bytes.length - 4); byte[] expectedChecksum = bytes(in, 4); for (int i = 0; i < 4; i++) { if (expectedChecksum[i] != checksum[i]) throw new IllegalArgumentException("Checksum of address failed"); } if (version < 4) { checksum = Security.sha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); this.tag = null; this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } else { checksum = Security.doubleSha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); this.tag = Arrays.copyOfRange(checksum, 32, 64); this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } } catch (IOException e) { throw new RuntimeException(e); } }
private void send(long stream, ObjectPayload payload, long timeToLive) { long expires = UnixTime.now(+timeToLive); LOG.info("Expires at " + expires); ObjectMessage object = new ObjectMessage.Builder() .stream(stream) .expiresTime(expires) .payload(payload) .build(); Security.doProofOfWork(object, ctx.getProofOfWorkEngine(), ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); ctx.getInventory().storeObject(object); ctx.getNetworkHandler().offer(object.getInventoryVector()); }
/** * A helper method to calculate SHA-1 hashes. Supplying multiple byte arrays has the same result as a * concatenation of all arrays, but might perform better. * <p> * Please note that a new {@link MessageDigest} object is created at * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short * order on the same thread. * </p> * * @param data to get hashed * @return SHA hash of data */ public static byte[] sha1(byte[]... data) { return hash("SHA-1", data); }
public void encrypt() throws IOException { encrypt(Security.createPublicKey(plaintext.getFrom().getPublicDecryptionKey()).getEncoded(false)); }
public InventoryVector getInventoryVector() { return new InventoryVector(Bytes.truncate(Security.doubleSha512(nonce, getPayloadBytesWithoutNonce()), 32)); }
public void send(BitmessageAddress from, BitmessageAddress to, ObjectPayload payload, long timeToLive, long nonceTrialsPerByte, long extraBytes) { try { if (to == null) to = from; long expires = UnixTime.now(+timeToLive); LOG.info("Expires at " + expires); ObjectMessage object = new ObjectMessage.Builder() .stream(to.getStream()) .expiresTime(expires) .payload(payload) .build(); if (object.isSigned()) { object.sign(from.getPrivateKey()); } if (payload instanceof Broadcast) { ((Broadcast) payload).encrypt(); } else if (payload instanceof Encrypted) { object.encrypt(to.getPubkey()); } Security.doProofOfWork(object, proofOfWorkEngine, nonceTrialsPerByte, extraBytes); if (payload instanceof PlaintextHolder) { Plaintext plaintext = ((PlaintextHolder) payload).getPlaintext(); plaintext.setInventoryVector(object.getInventoryVector()); messageRepository.save(plaintext); } inventory.storeObject(object); networkHandler.offer(object.getInventoryVector()); } catch (IOException e) { throw new RuntimeException(e); } }