Search in sources :

Example 1 with UntrustedOmemoIdentityException

use of org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException in project Smack by igniterealtime.

the class OmemoRatchet method retrieveMessageKeyAndAuthTag.

/**
 * Try to decrypt the transported message key using the double ratchet session.
 *
 * @param element omemoElement
 * @return tuple of cipher generated from the unpacked message key and the auth-tag
 *
 * @throws CryptoFailedException if decryption using the double ratchet fails
 * @throws NoRawSessionException if we have no session, but the element was NOT a PreKeyMessage
 * @throws IOException if an I/O error occurred.
 */
CipherAndAuthTag retrieveMessageKeyAndAuthTag(OmemoDevice sender, OmemoElement element) throws CryptoFailedException, NoRawSessionException, IOException {
    int keyId = omemoManager.getDeviceId();
    byte[] unpackedKey = null;
    List<CryptoFailedException> decryptExceptions = new ArrayList<>();
    List<OmemoKeyElement> keys = element.getHeader().getKeys();
    boolean preKey = false;
    // Find key with our ID.
    for (OmemoKeyElement k : keys) {
        if (k.getId() == keyId) {
            try {
                unpackedKey = doubleRatchetDecrypt(sender, k.getData());
                preKey = k.isPreKey();
                break;
            } catch (CryptoFailedException e) {
                // There might be multiple keys with our id, but we can only decrypt one.
                // So we can't throw the exception, when decrypting the first duplicate which is not for us.
                decryptExceptions.add(e);
            } catch (CorruptedOmemoKeyException e) {
                decryptExceptions.add(new CryptoFailedException(e));
            } catch (UntrustedOmemoIdentityException e) {
                LOGGER.log(Level.WARNING, "Received message from " + sender + " contained unknown identityKey. Ignore message.", e);
            }
        }
    }
    if (unpackedKey == null) {
        if (!decryptExceptions.isEmpty()) {
            throw MultipleCryptoFailedException.from(decryptExceptions);
        }
        throw new CryptoFailedException("Transported key could not be decrypted, since no suitable message key " + "was provided. Provides keys: " + keys);
    }
    // Split in AES auth-tag and key
    byte[] messageKey = new byte[16];
    byte[] authTag = null;
    if (unpackedKey.length == 32) {
        authTag = new byte[16];
        // copy key part into messageKey
        System.arraycopy(unpackedKey, 0, messageKey, 0, 16);
        // copy tag part into authTag
        System.arraycopy(unpackedKey, 16, authTag, 0, 16);
    } else if (element.isKeyTransportElement() && unpackedKey.length == 16) {
        messageKey = unpackedKey;
    } else {
        throw new CryptoFailedException("MessageKey has wrong length: " + unpackedKey.length + ". Probably legacy auth tag format.");
    }
    return new CipherAndAuthTag(messageKey, element.getHeader().getIv(), authTag, preKey);
}
Also used : MultipleCryptoFailedException(org.jivesoftware.smackx.omemo.exceptions.MultipleCryptoFailedException) CryptoFailedException(org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException) OmemoKeyElement(org.jivesoftware.smackx.omemo.element.OmemoKeyElement) ArrayList(java.util.ArrayList) CipherAndAuthTag(org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag) UntrustedOmemoIdentityException(org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException) CorruptedOmemoKeyException(org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException)

Example 2 with UntrustedOmemoIdentityException

use of org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException in project Smack by igniterealtime.

the class OmemoMessageBuilder method addRecipient.

/**
 * Add a new recipient device to the message.
 *
 * @param contactsDevice device of the recipient
 *
 * @throws NoIdentityKeyException if we have no identityKey of that device. Can be fixed by fetching and
 *                                processing the devices bundle.
 * @throws CorruptedOmemoKeyException if the identityKey of that device is corrupted.
 * @throws UndecidedOmemoIdentityException if the user hasn't yet decided whether to trust that device or not.
 * @throws UntrustedOmemoIdentityException if the user has decided not to trust that device.
 * @throws IOException if an I/O error occurred.
 */
public void addRecipient(OmemoDevice contactsDevice) throws NoIdentityKeyException, CorruptedOmemoKeyException, UndecidedOmemoIdentityException, UntrustedOmemoIdentityException, IOException {
    OmemoFingerprint fingerprint;
    fingerprint = OmemoService.getInstance().getOmemoStoreBackend().getFingerprint(userDevice, contactsDevice);
    switch(trustCallback.getTrust(contactsDevice, fingerprint)) {
        case undecided:
            throw new UndecidedOmemoIdentityException(contactsDevice);
        case trusted:
            CiphertextTuple encryptedKey = ratchet.doubleRatchetEncrypt(contactsDevice, messageKey);
            keys.add(new OmemoKeyElement(encryptedKey.getCiphertext(), contactsDevice.getDeviceId(), encryptedKey.isPreKeyMessage()));
            break;
        case untrusted:
            throw new UntrustedOmemoIdentityException(contactsDevice, fingerprint);
    }
}
Also used : UndecidedOmemoIdentityException(org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException) OmemoKeyElement(org.jivesoftware.smackx.omemo.element.OmemoKeyElement) OmemoFingerprint(org.jivesoftware.smackx.omemo.trust.OmemoFingerprint) UntrustedOmemoIdentityException(org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException) CiphertextTuple(org.jivesoftware.smackx.omemo.internal.CiphertextTuple)

Example 3 with UntrustedOmemoIdentityException

use of org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException in project Smack by igniterealtime.

the class OmemoService method createRatchetUpdateElement.

/**
 * Create an empty OMEMO message, which is used to forward the ratchet of the recipient.
 * This message type is typically used to create stable sessions.
 * Note that trust decisions are ignored for the creation of this message.
 *
 * @param managerGuard Logged in OmemoManager
 * @param contactsDevice OmemoDevice of the contact
 * @return ratchet update message
 *
 * @throws NoSuchAlgorithmException if AES algorithms are not supported on this system.
 * @throws InterruptedException if the calling thread was interrupted.
 * @throws SmackException.NoResponseException if there was no response from the remote entity.
 * @throws CorruptedOmemoKeyException if our IdentityKeyPair is corrupted.
 * @throws SmackException.NotConnectedException if the XMPP connection is not connected.
 * @throws CannotEstablishOmemoSessionException if session negotiation fails.
 * @throws IOException if an I/O error occurred.
 */
OmemoElement createRatchetUpdateElement(OmemoManager.LoggedInOmemoManager managerGuard, OmemoDevice contactsDevice) throws InterruptedException, SmackException.NoResponseException, CorruptedOmemoKeyException, SmackException.NotConnectedException, CannotEstablishOmemoSessionException, NoSuchAlgorithmException, CryptoFailedException, IOException {
    OmemoManager manager = managerGuard.get();
    OmemoDevice userDevice = manager.getOwnDevice();
    if (contactsDevice.equals(userDevice)) {
        throw new IllegalArgumentException("\"Thou shall not update thy own ratchet!\" - William Shakespeare");
    }
    // Establish session if necessary
    if (!hasSession(userDevice, contactsDevice)) {
        buildFreshSessionWithDevice(manager.getConnection(), userDevice, contactsDevice);
    }
    // Generate fresh AES key and IV
    byte[] messageKey = OmemoMessageBuilder.generateKey(KEYTYPE, KEYLENGTH);
    byte[] iv = OmemoMessageBuilder.generateIv();
    // Create message builder
    OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> builder;
    try {
        builder = new OmemoMessageBuilder<>(userDevice, gullibleTrustCallback, getOmemoRatchet(manager), messageKey, iv, null);
    } catch (InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException e) {
        throw new CryptoFailedException(e);
    }
    // Add recipient
    try {
        builder.addRecipient(contactsDevice);
    } catch (UndecidedOmemoIdentityException | UntrustedOmemoIdentityException e) {
        throw new AssertionError("Gullible Trust Callback reported undecided or untrusted device, " + "even though it MUST NOT do that.");
    } catch (NoIdentityKeyException e) {
        throw new AssertionError("We MUST have an identityKey for " + contactsDevice + " since we built a session." + e);
    }
    return builder.finish();
}
Also used : IllegalBlockSizeException(javax.crypto.IllegalBlockSizeException) BadPaddingException(javax.crypto.BadPaddingException) NoIdentityKeyException(org.jivesoftware.smackx.omemo.exceptions.NoIdentityKeyException) CryptoFailedException(org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException) UndecidedOmemoIdentityException(org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException) InvalidAlgorithmParameterException(java.security.InvalidAlgorithmParameterException) OmemoDevice(org.jivesoftware.smackx.omemo.internal.OmemoDevice) NoSuchPaddingException(javax.crypto.NoSuchPaddingException) InvalidKeyException(java.security.InvalidKeyException) UntrustedOmemoIdentityException(org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException)

Example 4 with UntrustedOmemoIdentityException

use of org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException in project Smack by igniterealtime.

the class OmemoService method encrypt.

/**
 * Encrypt a message with a messageKey and an IV and create an OmemoMessage from it.
 *
 * @param managerGuard authenticated OmemoManager
 * @param contactsDevices set of recipient OmemoDevices
 * @param messageKey AES key to encrypt the message
 * @param iv iv to be used with the messageKey
 * @return OmemoMessage object which contains the OmemoElement and some information.
 *
 * @throws SmackException.NotConnectedException if the XMPP connection is not connected.
 * @throws InterruptedException if the calling thread was interrupted.
 * @throws SmackException.NoResponseException if there was no response from the remote entity.
 * @throws UndecidedOmemoIdentityException if the list of recipient devices contains undecided devices
 * @throws CryptoFailedException if we are lacking some crypto primitives
 * @throws IOException if an I/O error occurred.
 */
private OmemoMessage.Sent encrypt(OmemoManager.LoggedInOmemoManager managerGuard, Set<OmemoDevice> contactsDevices, byte[] messageKey, byte[] iv, String message) throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, UndecidedOmemoIdentityException, CryptoFailedException, IOException {
    OmemoManager manager = managerGuard.get();
    OmemoDevice userDevice = manager.getOwnDevice();
    // Do not encrypt for our own device.
    removeOurDevice(userDevice, contactsDevices);
    buildMissingSessionsWithDevices(manager.getConnection(), userDevice, contactsDevices);
    Set<OmemoDevice> undecidedDevices = getUndecidedDevices(userDevice, manager.getTrustCallback(), contactsDevices);
    if (!undecidedDevices.isEmpty()) {
        throw new UndecidedOmemoIdentityException(undecidedDevices);
    }
    // Keep track of skipped devices
    HashMap<OmemoDevice, Throwable> skippedRecipients = new HashMap<>();
    OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> builder;
    try {
        builder = new OmemoMessageBuilder<>(userDevice, manager.getTrustCallback(), getOmemoRatchet(managerGuard.get()), messageKey, iv, message);
    } catch (BadPaddingException | IllegalBlockSizeException | NoSuchPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | NoSuchAlgorithmException e) {
        throw new CryptoFailedException(e);
    }
    for (OmemoDevice contactsDevice : contactsDevices) {
        // Build missing sessions
        if (!hasSession(userDevice, contactsDevice)) {
            try {
                buildFreshSessionWithDevice(manager.getConnection(), userDevice, contactsDevice);
            } catch (CorruptedOmemoKeyException | CannotEstablishOmemoSessionException e) {
                LOGGER.log(Level.WARNING, "Could not build session with " + contactsDevice + ".", e);
                skippedRecipients.put(contactsDevice, e);
                continue;
            }
        }
        int messageCounter = omemoStore.loadOmemoMessageCounter(userDevice, contactsDevice);
        // Ignore read-only devices
        if (OmemoConfiguration.getIgnoreReadOnlyDevices()) {
            boolean readOnly = messageCounter >= OmemoConfiguration.getMaxReadOnlyMessageCount();
            if (readOnly) {
                LOGGER.log(Level.FINE, "Device " + contactsDevice + " seems to be read-only (We sent " + messageCounter + " messages without getting a reply back (max allowed is " + OmemoConfiguration.getMaxReadOnlyMessageCount() + "). Ignoring the device.");
                skippedRecipients.put(contactsDevice, new ReadOnlyDeviceException(contactsDevice));
                // Skip this device and handle next device
                continue;
            }
        }
        // Add recipients
        try {
            builder.addRecipient(contactsDevice);
        } catch (NoIdentityKeyException | CorruptedOmemoKeyException e) {
            LOGGER.log(Level.WARNING, "Encryption failed for device " + contactsDevice + ".", e);
            skippedRecipients.put(contactsDevice, e);
        } catch (UndecidedOmemoIdentityException e) {
            throw new AssertionError("Recipients device seems to be undecided, even though we should have thrown" + " an exception earlier in that case. " + e);
        } catch (UntrustedOmemoIdentityException e) {
            LOGGER.log(Level.WARNING, "Device " + contactsDevice + " is untrusted. Message is not encrypted for it.");
            skippedRecipients.put(contactsDevice, e);
        }
        // Increment the message counter of the device
        omemoStore.storeOmemoMessageCounter(userDevice, contactsDevice, messageCounter + 1);
    }
    OmemoElement element = builder.finish();
    return new OmemoMessage.Sent(element, messageKey, iv, contactsDevices, skippedRecipients);
}
Also used : HashMap(java.util.HashMap) IllegalBlockSizeException(javax.crypto.IllegalBlockSizeException) BadPaddingException(javax.crypto.BadPaddingException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) CorruptedOmemoKeyException(org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException) CannotEstablishOmemoSessionException(org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException) NoIdentityKeyException(org.jivesoftware.smackx.omemo.exceptions.NoIdentityKeyException) ReadOnlyDeviceException(org.jivesoftware.smackx.omemo.exceptions.ReadOnlyDeviceException) CryptoFailedException(org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException) UndecidedOmemoIdentityException(org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException) InvalidAlgorithmParameterException(java.security.InvalidAlgorithmParameterException) OmemoDevice(org.jivesoftware.smackx.omemo.internal.OmemoDevice) NoSuchPaddingException(javax.crypto.NoSuchPaddingException) InvalidKeyException(java.security.InvalidKeyException) OmemoFingerprint(org.jivesoftware.smackx.omemo.trust.OmemoFingerprint) UntrustedOmemoIdentityException(org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException) OmemoElement(org.jivesoftware.smackx.omemo.element.OmemoElement)

Example 5 with UntrustedOmemoIdentityException

use of org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException in project Smack by igniterealtime.

the class SignalOmemoRatchet method doubleRatchetDecrypt.

@Override
public byte[] doubleRatchetDecrypt(OmemoDevice sender, byte[] encryptedKey) throws CorruptedOmemoKeyException, NoRawSessionException, CryptoFailedException, UntrustedOmemoIdentityException, IOException {
    SessionCipher cipher = getCipher(sender);
    byte[] decryptedKey;
    // Try to handle the message as a PreKeySignalMessage...
    try {
        PreKeySignalMessage preKeyMessage = new PreKeySignalMessage(encryptedKey);
        if (!preKeyMessage.getPreKeyId().isPresent()) {
            throw new CryptoFailedException("PreKeyMessage did not contain a preKeyId.");
        }
        IdentityKey messageIdentityKey = preKeyMessage.getIdentityKey();
        IdentityKey previousIdentityKey = store.loadOmemoIdentityKey(storeConnector.getOurDevice(), sender);
        if (previousIdentityKey != null && !previousIdentityKey.getFingerprint().equals(messageIdentityKey.getFingerprint())) {
            throw new UntrustedOmemoIdentityException(sender, store.keyUtil().getFingerprintOfIdentityKey(previousIdentityKey), store.keyUtil().getFingerprintOfIdentityKey(messageIdentityKey));
        }
        try {
            decryptedKey = cipher.decrypt(preKeyMessage);
        } catch (UntrustedIdentityException e) {
            throw new AssertionError("Signals trust management MUST be disabled.");
        } catch (LegacyMessageException | InvalidKeyException e) {
            throw new CryptoFailedException(e);
        } catch (InvalidKeyIdException e) {
            throw new NoRawSessionException(sender, e);
        } catch (DuplicateMessageException e) {
            LOGGER.log(Level.INFO, "Decryption of PreKeyMessage from " + sender + " failed, since the message has been decrypted before.");
            return null;
        }
    } catch (InvalidVersionException | InvalidMessageException noPreKeyMessage) {
        // ...if that fails, handle it as a SignalMessage
        try {
            SignalMessage message = new SignalMessage(encryptedKey);
            decryptedKey = getCipher(sender).decrypt(message);
        } catch (UntrustedIdentityException e) {
            throw new AssertionError("Signals trust management MUST be disabled.");
        } catch (InvalidMessageException | NoSessionException e) {
            throw new NoRawSessionException(sender, e);
        } catch (LegacyMessageException e) {
            throw new CryptoFailedException(e);
        } catch (DuplicateMessageException e1) {
            LOGGER.log(Level.INFO, "Decryption of SignalMessage from " + sender + " failed, since the message has been decrypted before.");
            return null;
        }
    }
    return decryptedKey;
}
Also used : CryptoFailedException(org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException) InvalidMessageException(org.whispersystems.libsignal.InvalidMessageException) IdentityKey(org.whispersystems.libsignal.IdentityKey) UntrustedIdentityException(org.whispersystems.libsignal.UntrustedIdentityException) PreKeySignalMessage(org.whispersystems.libsignal.protocol.PreKeySignalMessage) SignalMessage(org.whispersystems.libsignal.protocol.SignalMessage) InvalidVersionException(org.whispersystems.libsignal.InvalidVersionException) NoRawSessionException(org.jivesoftware.smackx.omemo.exceptions.NoRawSessionException) InvalidKeyException(org.whispersystems.libsignal.InvalidKeyException) PreKeySignalMessage(org.whispersystems.libsignal.protocol.PreKeySignalMessage) DuplicateMessageException(org.whispersystems.libsignal.DuplicateMessageException) InvalidKeyIdException(org.whispersystems.libsignal.InvalidKeyIdException) LegacyMessageException(org.whispersystems.libsignal.LegacyMessageException) SessionCipher(org.whispersystems.libsignal.SessionCipher) UntrustedOmemoIdentityException(org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException)

Aggregations

UntrustedOmemoIdentityException (org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException)5 CryptoFailedException (org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException)4 UndecidedOmemoIdentityException (org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException)3 InvalidAlgorithmParameterException (java.security.InvalidAlgorithmParameterException)2 InvalidKeyException (java.security.InvalidKeyException)2 BadPaddingException (javax.crypto.BadPaddingException)2 IllegalBlockSizeException (javax.crypto.IllegalBlockSizeException)2 NoSuchPaddingException (javax.crypto.NoSuchPaddingException)2 OmemoKeyElement (org.jivesoftware.smackx.omemo.element.OmemoKeyElement)2 CorruptedOmemoKeyException (org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException)2 NoIdentityKeyException (org.jivesoftware.smackx.omemo.exceptions.NoIdentityKeyException)2 OmemoDevice (org.jivesoftware.smackx.omemo.internal.OmemoDevice)2 OmemoFingerprint (org.jivesoftware.smackx.omemo.trust.OmemoFingerprint)2 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 OmemoElement (org.jivesoftware.smackx.omemo.element.OmemoElement)1 CannotEstablishOmemoSessionException (org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException)1 MultipleCryptoFailedException (org.jivesoftware.smackx.omemo.exceptions.MultipleCryptoFailedException)1 NoRawSessionException (org.jivesoftware.smackx.omemo.exceptions.NoRawSessionException)1