use of org.whispersystems.libsignal.state.PreKeyBundle in project libsignal-service-java by signalapp.
the class PushServiceSocket method getPreKeys.
public List<PreKeyBundle> getPreKeys(SignalServiceAddress destination, int deviceIdInteger) throws IOException {
try {
String deviceId = String.valueOf(deviceIdInteger);
if (deviceId.equals("1"))
deviceId = "*";
String path = String.format(PREKEY_DEVICE_PATH, destination.getNumber(), deviceId);
if (destination.getRelay().isPresent()) {
path = path + "?relay=" + destination.getRelay().get();
}
String responseText = makeServiceRequest(path, "GET", null);
PreKeyResponse response = JsonUtil.fromJson(responseText, PreKeyResponse.class);
List<PreKeyBundle> bundles = new LinkedList<>();
for (PreKeyResponseItem device : response.getDevices()) {
ECPublicKey preKey = null;
ECPublicKey signedPreKey = null;
byte[] signedPreKeySignature = null;
int preKeyId = -1;
int signedPreKeyId = -1;
if (device.getSignedPreKey() != null) {
signedPreKey = device.getSignedPreKey().getPublicKey();
signedPreKeyId = device.getSignedPreKey().getKeyId();
signedPreKeySignature = device.getSignedPreKey().getSignature();
}
if (device.getPreKey() != null) {
preKeyId = device.getPreKey().getKeyId();
preKey = device.getPreKey().getPublicKey();
}
bundles.add(new PreKeyBundle(device.getRegistrationId(), device.getDeviceId(), preKeyId, preKey, signedPreKeyId, signedPreKey, signedPreKeySignature, response.getIdentityKey()));
}
return bundles;
} catch (NotFoundException nfe) {
throw new UnregisteredUserException(destination.getNumber(), nfe);
}
}
use of org.whispersystems.libsignal.state.PreKeyBundle in project Conversations by siacs.
the class AxolotlService method buildSessionFromPEP.
private ListenableFuture<XmppAxolotlSession> buildSessionFromPEP(final SignalProtocolAddress address, OnSessionBuildFromPep callback) {
final SettableFuture<XmppAxolotlSession> sessionSettableFuture = SettableFuture.create();
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building new session for " + address.toString());
if (address.equals(getOwnAxolotlAddress())) {
throw new AssertionError("We should NEVER build a session with ourselves. What happened here?!");
}
final Jid jid = Jid.of(address.getName());
final boolean oneOfOurs = jid.asBareJid().equals(account.getJid().asBareJid());
IqPacket bundlesPacket = mXmppConnectionService.getIqGenerator().retrieveBundlesForDevice(jid, address.getDeviceId());
mXmppConnectionService.sendIqPacket(account, bundlesPacket, (account, packet) -> {
if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
fetchStatusMap.put(address, FetchStatus.TIMEOUT);
sessionSettableFuture.setException(new CryptoFailedException("Unable to build session. Timeout"));
} else if (packet.getType() == IqPacket.TYPE.RESULT) {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received preKey IQ packet, processing...");
final IqParser parser = mXmppConnectionService.getIqParser();
final List<PreKeyBundle> preKeyBundleList = parser.preKeys(packet);
final PreKeyBundle bundle = parser.bundle(packet);
if (preKeyBundleList.isEmpty() || bundle == null) {
Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "preKey IQ packet invalid: " + packet);
fetchStatusMap.put(address, FetchStatus.ERROR);
finishBuildingSessionsFromPEP(address);
if (callback != null) {
callback.onSessionBuildFailed();
}
sessionSettableFuture.setException(new CryptoFailedException("Unable to build session. IQ Packet Invalid"));
return;
}
Random random = new Random();
final PreKeyBundle preKey = preKeyBundleList.get(random.nextInt(preKeyBundleList.size()));
if (preKey == null) {
// should never happen
fetchStatusMap.put(address, FetchStatus.ERROR);
finishBuildingSessionsFromPEP(address);
if (callback != null) {
callback.onSessionBuildFailed();
}
sessionSettableFuture.setException(new CryptoFailedException("Unable to build session. No suitable PreKey found"));
return;
}
final PreKeyBundle preKeyBundle = new PreKeyBundle(0, address.getDeviceId(), preKey.getPreKeyId(), preKey.getPreKey(), bundle.getSignedPreKeyId(), bundle.getSignedPreKey(), bundle.getSignedPreKeySignature(), bundle.getIdentityKey());
try {
SessionBuilder builder = new SessionBuilder(axolotlStore, address);
builder.process(preKeyBundle);
XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey());
sessions.put(address, session);
if (Config.X509_VERIFICATION) {
// TODO; maybe inject callback in here too
sessionSettableFuture.setFuture(verifySessionWithPEP(session));
} else {
FingerprintStatus status = getFingerprintTrust(CryptoHelper.bytesToHex(bundle.getIdentityKey().getPublicKey().serialize()));
FetchStatus fetchStatus;
if (status != null && status.isVerified()) {
fetchStatus = FetchStatus.SUCCESS_VERIFIED;
} else if (status != null && status.isTrusted()) {
fetchStatus = FetchStatus.SUCCESS_TRUSTED;
} else {
fetchStatus = FetchStatus.SUCCESS;
}
fetchStatusMap.put(address, fetchStatus);
finishBuildingSessionsFromPEP(address);
if (callback != null) {
callback.onSessionBuildSuccessful();
}
sessionSettableFuture.set(session);
}
} catch (UntrustedIdentityException | InvalidKeyException e) {
Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error building session for " + address + ": " + e.getClass().getName() + ", " + e.getMessage());
fetchStatusMap.put(address, FetchStatus.ERROR);
finishBuildingSessionsFromPEP(address);
if (oneOfOurs && cleanedOwnDeviceIds.add(address.getDeviceId())) {
removeFromDeviceAnnouncement(address.getDeviceId());
}
if (callback != null) {
callback.onSessionBuildFailed();
}
sessionSettableFuture.setException(new CryptoFailedException(e));
}
} else {
fetchStatusMap.put(address, FetchStatus.ERROR);
Element error = packet.findChild("error");
boolean itemNotFound = error != null && error.hasChild("item-not-found");
Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while building session:" + packet.findChild("error"));
finishBuildingSessionsFromPEP(address);
if (oneOfOurs && itemNotFound && cleanedOwnDeviceIds.add(address.getDeviceId())) {
removeFromDeviceAnnouncement(address.getDeviceId());
}
if (callback != null) {
callback.onSessionBuildFailed();
}
sessionSettableFuture.setException(new CryptoFailedException("Unable to build session. IQ Packet Error"));
}
});
return sessionSettableFuture;
}
use of org.whispersystems.libsignal.state.PreKeyBundle in project Conversations by siacs.
the class AxolotlService method publishBundlesIfNeeded.
public void publishBundlesIfNeeded(final boolean announce, final boolean wipe) {
if (pepBroken) {
Log.d(Config.LOGTAG, getLogprefix(account) + "publishBundlesIfNeeded called, but PEP is broken. Ignoring... ");
return;
}
if (account.getXmppConnection().getFeatures().pepPublishOptions()) {
this.changeAccessMode.set(account.isOptionSet(Account.OPTION_REQUIRES_ACCESS_MODE_CHANGE));
} else {
if (account.setOption(Account.OPTION_REQUIRES_ACCESS_MODE_CHANGE, true)) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": server doesn’t support publish-options. setting for later access mode change");
mXmppConnectionService.databaseBackend.updateAccount(account);
}
}
if (this.changeAccessMode.get()) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": server gained publish-options capabilities. changing access model");
}
IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveBundlesForDevice(account.getJid().asBareJid(), getOwnDeviceId());
mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
// ignore timeout. do nothing
return;
}
if (packet.getType() == IqPacket.TYPE.ERROR) {
Element error = packet.findChild("error");
if (error == null || !error.hasChild("item-not-found")) {
pepBroken = true;
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "request for device bundles came back with something other than item-not-found" + packet);
return;
}
}
PreKeyBundle bundle = mXmppConnectionService.getIqParser().bundle(packet);
Map<Integer, ECPublicKey> keys = mXmppConnectionService.getIqParser().preKeyPublics(packet);
boolean flush = false;
if (bundle == null) {
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received invalid bundle:" + packet);
bundle = new PreKeyBundle(-1, -1, -1, null, -1, null, null, null);
flush = true;
}
if (keys == null) {
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received invalid prekeys:" + packet);
}
try {
boolean changed = false;
// Validate IdentityKey
IdentityKeyPair identityKeyPair = axolotlStore.getIdentityKeyPair();
if (flush || !identityKeyPair.getPublicKey().equals(bundle.getIdentityKey())) {
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding own IdentityKey " + identityKeyPair.getPublicKey() + " to PEP.");
changed = true;
}
// Validate signedPreKeyRecord + ID
SignedPreKeyRecord signedPreKeyRecord;
int numSignedPreKeys = axolotlStore.getSignedPreKeysCount();
try {
signedPreKeyRecord = axolotlStore.loadSignedPreKey(bundle.getSignedPreKeyId());
if (flush || !bundle.getSignedPreKey().equals(signedPreKeyRecord.getKeyPair().getPublicKey()) || !Arrays.equals(bundle.getSignedPreKeySignature(), signedPreKeyRecord.getSignature())) {
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP.");
signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1);
axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord);
changed = true;
}
} catch (InvalidKeyIdException e) {
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding new signedPreKey with ID " + (numSignedPreKeys + 1) + " to PEP.");
signedPreKeyRecord = KeyHelper.generateSignedPreKey(identityKeyPair, numSignedPreKeys + 1);
axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord);
changed = true;
}
// Validate PreKeys
Set<PreKeyRecord> preKeyRecords = new HashSet<>();
if (keys != null) {
for (Integer id : keys.keySet()) {
try {
PreKeyRecord preKeyRecord = axolotlStore.loadPreKey(id);
if (preKeyRecord.getKeyPair().getPublicKey().equals(keys.get(id))) {
preKeyRecords.add(preKeyRecord);
}
} catch (InvalidKeyIdException ignored) {
}
}
}
int newKeys = NUM_KEYS_TO_PUBLISH - preKeyRecords.size();
if (newKeys > 0) {
List<PreKeyRecord> newRecords = KeyHelper.generatePreKeys(axolotlStore.getCurrentPreKeyId() + 1, newKeys);
preKeyRecords.addAll(newRecords);
for (PreKeyRecord record : newRecords) {
axolotlStore.storePreKey(record.getId(), record);
}
changed = true;
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Adding " + newKeys + " new preKeys to PEP.");
}
if (changed || changeAccessMode.get()) {
if (account.getPrivateKeyAlias() != null && Config.X509_VERIFICATION) {
mXmppConnectionService.publishDisplayName(account);
publishDeviceVerificationAndBundle(signedPreKeyRecord, preKeyRecords, announce, wipe);
} else {
publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announce, wipe);
}
} else {
Log.d(Config.LOGTAG, getLogprefix(account) + "Bundle " + getOwnDeviceId() + " in PEP was current");
if (wipe) {
wipeOtherPepDevices();
} else if (announce) {
Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId());
publishOwnDeviceIdIfNeeded();
}
}
} catch (InvalidKeyException e) {
Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Failed to publish bundle " + getOwnDeviceId() + ", reason: " + e.getMessage());
}
}
});
}
use of org.whispersystems.libsignal.state.PreKeyBundle in project Conversations by siacs.
the class IqParser method preKeys.
public List<PreKeyBundle> preKeys(final IqPacket preKeys) {
List<PreKeyBundle> bundles = new ArrayList<>();
Map<Integer, ECPublicKey> preKeyPublics = preKeyPublics(preKeys);
if (preKeyPublics != null) {
for (Integer preKeyId : preKeyPublics.keySet()) {
ECPublicKey preKeyPublic = preKeyPublics.get(preKeyId);
bundles.add(new PreKeyBundle(0, 0, preKeyId, preKeyPublic, 0, null, null, null));
}
}
return bundles;
}
use of org.whispersystems.libsignal.state.PreKeyBundle in project Conversations by siacs.
the class IqParser method bundle.
public PreKeyBundle bundle(final IqPacket bundle) {
final Element bundleItem = getItem(bundle);
if (bundleItem == null) {
return null;
}
final Element bundleElement = bundleItem.findChild("bundle");
if (bundleElement == null) {
return null;
}
final ECPublicKey signedPreKeyPublic = signedPreKeyPublic(bundleElement);
final Integer signedPreKeyId = signedPreKeyId(bundleElement);
final byte[] signedPreKeySignature = signedPreKeySignature(bundleElement);
final IdentityKey identityKey = identityKey(bundleElement);
if (signedPreKeyId == null || signedPreKeyPublic == null || identityKey == null || signedPreKeySignature == null || signedPreKeySignature.length == 0) {
return null;
}
return new PreKeyBundle(0, 0, 0, null, signedPreKeyId, signedPreKeyPublic, signedPreKeySignature, identityKey);
}
Aggregations