private CryptoBox(Builder builder) { initializationVector = builder.initializationVector; curveType = builder.curveType; R = cryptography().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 = cryptography().randomBytes(PRIVATE_KEY_SIZE); privEK = cryptography().randomBytes(PRIVATE_KEY_SIZE); pubSK = cryptography().createPublicKey(privSK); pubEK = cryptography().createPublicKey(privEK); ripe = Pubkey.getRipe(pubSK, pubEK); } while (ripe[0] != 0 || (shorter && ripe[1] != 0)); this.privateSigningKey = privSK; this.privateEncryptionKey = privEK; this.pubkey = cryptography().createPubkey(Pubkey.LATEST_VERSION, stream, privateSigningKey, privateEncryptionKey, nonceTrialsPerByte, extraBytes, features); }
public CryptoBox(byte[] data, byte[] 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 = cryptography().randomBytes(16); // 3. Generate a new random EC key pair with private key called r and public key called R. byte[] r = cryptography().randomBytes(PRIVATE_KEY_SIZE); R = cryptography().createPublicKey(r); // 4. Do an EC point multiply with public key K and private key r. This gives you public key P. byte[] P = cryptography().multiply(K, r); byte[] X = Points.getX(P); // 5. Use the X component of public key P and calculate the SHA512 hash H. byte[] H = cryptography().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 = cryptography().crypt(true, data, key_e, initializationVector); // 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 }
Builder generate() { long signingKeyNonce = nextNonce; long encryptionKeyNonce = nextNonce + 1; byte[] ripe; do { privEK = Bytes.truncate(cryptography().sha512(seed, Encode.varInt(encryptionKeyNonce)), 32); privSK = Bytes.truncate(cryptography().sha512(seed, Encode.varInt(signingKeyNonce)), 32); pubSK = cryptography().createPublicKey(privSK); pubEK = cryptography().createPublicKey(privEK); ripe = cryptography().ripemd160(cryptography().sha512(pubSK, pubEK)); signingKeyNonce += 2; encryptionKeyNonce += 2; } while (ripe[0] != 0 || (shorter && ripe[1] != 0)); nextNonce = signingKeyNonce; return this; } }
/** * @param k a private key, typically should be 32 bytes long * @return an InputStream yielding the decrypted data * @throws DecryptionFailedException if the payload can't be decrypted using this private key * @see <a href='https://bitmessage.org/wiki/Encryption#Decryption'>https://bitmessage.org/wiki/Encryption#Decryption</a> */ public InputStream decrypt(byte[] k) throws DecryptionFailedException { // 1. The private key used to decrypt is called k. // 2. Do an EC point multiply with private key k and public key R. This gives you public key P. byte[] P = cryptography().multiply(R, k); // 3. Use the X component of public key P and calculate the SHA512 hash H. byte[] H = cryptography().sha512(Arrays.copyOfRange(P, 1, 33)); // 4. 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); // 5. Calculate MAC' with HMACSHA256, using key_m as salt and IV + R + cipher text as data. // 6. Compare MAC with MAC'. If not equal, decryption will fail. if (!Arrays.equals(mac, calculateMac(key_m))) { throw new DecryptionFailedException(); } // 7. Decrypt the cipher text with AES-256-CBC, using IV as initialization vector, key_e as decryption key // and the cipher text as payload. The output is the padded input text. return new ByteArrayInputStream(cryptography().crypt(false, encrypted, key_e, initializationVector)); }
public void doProofOfWork(BitmessageAddress recipient, ObjectMessage object) { Pubkey pubkey = recipient == null ? null : recipient.getPubkey(); long nonceTrialsPerByte = pubkey == null ? NETWORK_NONCE_TRIALS_PER_BYTE : pubkey.getNonceTrialsPerByte(); long extraBytes = pubkey == null ? NETWORK_EXTRA_BYTES : pubkey.getExtraBytes(); powRepo.putObject(object, nonceTrialsPerByte, extraBytes); if (object.getPayload() instanceof PlaintextHolder) { Plaintext plaintext = ((PlaintextHolder) object.getPayload()).getPlaintext(); plaintext.setInitialHash(cryptography.getInitialHash(object)); messageRepo.save(plaintext); } cryptography.doProofOfWork(object, nonceTrialsPerByte, extraBytes, this); }
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 = cryptography().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 = cryptography().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 = cryptography().doubleSha512(os.toByteArray()); os.write(checksum, 0, 4); this.address = "BM-" + Base58.encode(os.toByteArray()); } catch (IOException e) { throw new ApplicationException(e); } }
@Override public void putObject(Item item) { try ( Connection connection = config.getConnection(); PreparedStatement ps = connection.prepareStatement("INSERT INTO POW (initial_hash, data, version, " + "nonce_trials_per_byte, extra_bytes, expiration_time, message_id) " + "VALUES (?, ?, ?, ?, ?, ?, ?)") ) { ps.setBytes(1, cryptography().getInitialHash(item.object)); writeBlob(ps, 2, item.object); ps.setLong(3, item.object.getVersion()); ps.setLong(4, item.nonceTrialsPerByte); ps.setLong(5, item.extraBytes); if (item.message == null) { ps.setObject(6, null); ps.setObject(7, null); } else { ps.setLong(6, item.expirationTime); ps.setLong(7, (Long) item.message.getId()); } ps.executeUpdate(); } catch (IOException | SQLException e) { LOG.debug("Error storing object of type " + item.object.getPayload().getClass().getSimpleName(), e); throw new ApplicationException(e); } }
public void encrypt() throws IOException { encrypt(cryptography().createPublicKey(plaintext.getFrom().getPublicDecryptionKey())); }
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(cryptography().doubleSha512(out.toByteArray()), 32, 64); } catch (IOException e) { throw new ApplicationException(e); } }
@Override public void run() { LOG.info("Doing POW for " + items.size() + " tasks."); for (byte[] initialHash : items) { Item item = powRepo.getItem(initialHash); cryptography.doProofOfWork(item.object, item.nonceTrialsPerByte, item.extraBytes, ProofOfWorkService.this); } } }, delayInMilliseconds);
private String exportSecret(byte[] privateKey) { if (privateKey.length != PRIVATE_KEY_SIZE) { throw new IllegalArgumentException("Private key of length 32 expected, but was " + privateKey.length); } byte[] result = new byte[37]; result[0] = (byte) 0x80; System.arraycopy(privateKey, 0, result, 1, PRIVATE_KEY_SIZE); byte[] hash = cryptography().doubleSha256(result, PRIVATE_KEY_SIZE + 1); System.arraycopy(hash, 0, result, PRIVATE_KEY_SIZE + 1, 4); return Base58.encode(result); }
public void sign(PrivateKey key) { if (payload.isSigned()) { payload.setSignature(cryptography().getSignature(getBytesToSign(), key)); } }
private void receiveMessage(ObjectMessage objectMessage) { requestedObjects.remove(objectMessage.getInventoryVector()); if (ctx.getInventory().contains(objectMessage)) { LOG.trace("Received object " + objectMessage.getInventoryVector() + " - already in inventory"); return; } try { listener.receive(objectMessage); cryptography().checkProofOfWork(objectMessage, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES); ctx.getInventory().storeObject(objectMessage); // offer object to some random nodes so it gets distributed throughout the network: ctx.getNetworkHandler().offer(objectMessage.getInventoryVector()); lastObjectTime = UnixTime.now(); } catch (InsufficientProofOfWorkException e) { LOG.warn(e.getMessage()); // DebugUtils.saveToFile(objectMessage); // this line must not be committed active } catch (IOException e) { LOG.error("Stream " + objectMessage.getStream() + ", object type " + objectMessage.getType() + ": " + e.getMessage(), e); } finally { if (commonRequestedObjects.remove(objectMessage.getInventoryVector()) == null) { LOG.debug("Received object that wasn't requested."); } } }
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 = cryptography().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 = cryptography().sha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); this.tag = null; this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } else { checksum = cryptography().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 ApplicationException(e); } }
public static BitmessageAddress createIdentityFromPrivateKey(String address, byte[] privateSigningKey, byte[] privateEncryptionKey, long nonceTrialsPerByte, long extraBytes, int behaviourBitfield) { BitmessageAddress temp = new BitmessageAddress(address); PrivateKey privateKey = new PrivateKey(privateSigningKey, privateEncryptionKey, createPubkey(temp.getVersion(), temp.getStream(), cryptography().createPublicKey(privateSigningKey), cryptography().createPublicKey(privateEncryptionKey), nonceTrialsPerByte, extraBytes, behaviourBitfield)); BitmessageAddress result = new BitmessageAddress(privateKey); if (!result.getAddress().equals(address)) { throw new IllegalArgumentException("Address not matching private key. Address: " + address + "; Address derived from private key: " + result.getAddress()); } return result; }
public InventoryVector getInventoryVector() { return new InventoryVector( Bytes.truncate(cryptography().doubleSha512(nonce, getPayloadBytesWithoutNonce()), 32) ); }
public void doProofOfWorkWithAck(Plaintext plaintext, long expirationTime) { final ObjectMessage ack = plaintext.getAckMessage(); messageRepo.save(plaintext); Item item = new Item(ack, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES, expirationTime, plaintext); powRepo.putObject(item); cryptography.doProofOfWork(ack, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES, this); }
private byte[] getSecret(String walletImportFormat) throws IOException { byte[] bytes = Base58.decode(walletImportFormat); if (bytes[0] != WIF_FIRST_BYTE) throw new IOException("Unknown format: 0x80 expected as first byte, but secret " + walletImportFormat + " was " + bytes[0]); if (bytes.length != WIF_SECRET_LENGTH) throw new IOException("Unknown format: " + WIF_SECRET_LENGTH + " bytes expected, but secret " + walletImportFormat + " was " + bytes.length + " long"); byte[] hash = cryptography().doubleSha256(bytes, 33); for (int i = 0; i < 4; i++) { if (hash[i] != bytes[33 + i]) throw new IOException("Hash check failed for secret " + walletImportFormat); } return Arrays.copyOfRange(bytes, 1, 33); }
public void sendPubkey(final BitmessageAddress identity, final long targetStream) { try { long expires = UnixTime.now(TTL.pubkey()); LOG.info("Expires at " + expires); final ObjectMessage response = new ObjectMessage.Builder() .stream(targetStream) .expiresTime(expires) .payload(identity.getPubkey()) .build(); response.sign(identity.getPrivateKey()); response.encrypt(cryptography.createPublicKey(identity.getPublicDecryptionKey())); // TODO: remember that the pubkey is just about to be sent, and on which stream! proofOfWorkService.doProofOfWork(response); } catch (IOException e) { throw new ApplicationException(e); } }