Search in sources :

Example 1 with PreKeyBundle

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);
    }
}
Also used : PreKeyBundle(org.whispersystems.libsignal.state.PreKeyBundle) UnregisteredUserException(org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException) ECPublicKey(org.whispersystems.libsignal.ecc.ECPublicKey) NotFoundException(org.whispersystems.signalservice.api.push.exceptions.NotFoundException) LinkedList(java.util.LinkedList)

Example 2 with PreKeyBundle

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;
}
Also used : IqParser(eu.siacs.conversations.parser.IqParser) Jid(eu.siacs.conversations.xmpp.Jid) Element(eu.siacs.conversations.xml.Element) SessionBuilder(org.whispersystems.libsignal.SessionBuilder) IqPacket(eu.siacs.conversations.xmpp.stanzas.IqPacket) PreKeyBundle(org.whispersystems.libsignal.state.PreKeyBundle) Random(java.util.Random) List(java.util.List) ArrayList(java.util.ArrayList) ImmutableList(com.google.common.collect.ImmutableList)

Example 3 with PreKeyBundle

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());
            }
        }
    });
}
Also used : Account(eu.siacs.conversations.entities.Account) Set(java.util.Set) HashSet(java.util.HashSet) OnIqPacketReceived(eu.siacs.conversations.xmpp.OnIqPacketReceived) Element(eu.siacs.conversations.xml.Element) InvalidKeyException(org.whispersystems.libsignal.InvalidKeyException) IqPacket(eu.siacs.conversations.xmpp.stanzas.IqPacket) PreKeyBundle(org.whispersystems.libsignal.state.PreKeyBundle) SignedPreKeyRecord(org.whispersystems.libsignal.state.SignedPreKeyRecord) PreKeyRecord(org.whispersystems.libsignal.state.PreKeyRecord) InvalidKeyIdException(org.whispersystems.libsignal.InvalidKeyIdException) List(java.util.List) ArrayList(java.util.ArrayList) ImmutableList(com.google.common.collect.ImmutableList) IdentityKeyPair(org.whispersystems.libsignal.IdentityKeyPair) OmemoVerifiedRtpContentMap(eu.siacs.conversations.xmpp.jingle.OmemoVerifiedRtpContentMap) Map(java.util.Map) ImmutableMap(com.google.common.collect.ImmutableMap) HashMap(java.util.HashMap) RtpContentMap(eu.siacs.conversations.xmpp.jingle.RtpContentMap) SignedPreKeyRecord(org.whispersystems.libsignal.state.SignedPreKeyRecord)

Example 4 with PreKeyBundle

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;
}
Also used : PreKeyBundle(org.whispersystems.libsignal.state.PreKeyBundle) ECPublicKey(org.whispersystems.libsignal.ecc.ECPublicKey) ArrayList(java.util.ArrayList)

Example 5 with PreKeyBundle

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);
}
Also used : PreKeyBundle(org.whispersystems.libsignal.state.PreKeyBundle) IdentityKey(org.whispersystems.libsignal.IdentityKey) ECPublicKey(org.whispersystems.libsignal.ecc.ECPublicKey) Element(eu.siacs.conversations.xml.Element)

Aggregations

PreKeyBundle (org.whispersystems.libsignal.state.PreKeyBundle)22 ECPublicKey (org.whispersystems.libsignal.ecc.ECPublicKey)11 IOException (java.io.IOException)10 InvalidKeyException (org.whispersystems.libsignal.InvalidKeyException)10 SessionBuilder (org.whispersystems.libsignal.SessionBuilder)9 SignalProtocolAddress (org.whispersystems.libsignal.SignalProtocolAddress)7 UntrustedIdentityException (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException)7 NotFoundException (org.whispersystems.signalservice.api.push.exceptions.NotFoundException)7 UnregisteredUserException (org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException)7 ArrayList (java.util.ArrayList)5 LinkedList (java.util.LinkedList)4 GroupSessionBuilder (org.whispersystems.libsignal.groups.GroupSessionBuilder)4 SignalGroupSessionBuilder (org.whispersystems.signalservice.api.crypto.SignalGroupSessionBuilder)4 SignalServiceCipher (org.whispersystems.signalservice.api.crypto.SignalServiceCipher)4 SignalSessionBuilder (org.whispersystems.signalservice.api.crypto.SignalSessionBuilder)4 GroupsV2AuthorizationString (org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString)4 GroupNotFoundException (org.whispersystems.signalservice.internal.push.exceptions.GroupNotFoundException)4 Element (eu.siacs.conversations.xml.Element)3 List (java.util.List)3 ImmutableList (com.google.common.collect.ImmutableList)2