use of org.jivesoftware.smackx.omemo.element.OmemoElement in project Smack by igniterealtime.
the class OmemoService method decryptStanza.
/**
* Decrypt the OmemoElement inside the given Stanza and return it.
* Return null if something goes wrong.
*
* @param stanza stanza
* @param managerGuard authenticated OmemoManager
* @return decrypted OmemoMessage or null
*
* @throws IOException if an I/O error occurred.
*/
OmemoMessage.Received decryptStanza(Stanza stanza, OmemoManager.LoggedInOmemoManager managerGuard) throws IOException {
OmemoManager manager = managerGuard.get();
// Avoid the ratchet being manipulated and the bundle being published multiple times simultaneously
synchronized (manager) {
OmemoDevice userDevice = manager.getOwnDevice();
OmemoElement element = (OmemoElement) stanza.getExtensionElement(OmemoElement.NAME_ENCRYPTED, OmemoElement_VAxolotl.NAMESPACE);
if (element == null) {
return null;
}
OmemoMessage.Received decrypted = null;
BareJid sender;
try {
MultiUserChat muc = getMuc(manager.getConnection(), stanza.getFrom());
if (muc != null) {
Occupant occupant = muc.getOccupant(stanza.getFrom().asEntityFullJidIfPossible());
Jid occupantJid = occupant.getJid();
if (occupantJid == null) {
LOGGER.log(Level.WARNING, "MUC message received, but there is no way to retrieve the senders Jid. " + stanza.getFrom());
return null;
}
sender = occupantJid.asBareJid();
// try is for this
decrypted = decryptMessage(managerGuard, sender, element);
} else {
sender = stanza.getFrom().asBareJid();
// and this
decrypted = decryptMessage(managerGuard, sender, element);
}
if (decrypted.isPreKeyMessage() && OmemoConfiguration.getCompleteSessionWithEmptyMessage()) {
LOGGER.log(Level.FINE, "Received a preKeyMessage from " + decrypted.getSenderDevice() + ".\n" + "Complete the session by sending an empty response message.");
try {
sendRatchetUpdate(managerGuard, decrypted.getSenderDevice());
} catch (CannotEstablishOmemoSessionException e) {
throw new AssertionError("Since we successfully received a message, we MUST be able to " + "establish a session. " + e);
} catch (NoSuchAlgorithmException | InterruptedException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
LOGGER.log(Level.WARNING, "Cannot send a ratchet update message.", e);
}
}
} catch (NoRawSessionException e) {
OmemoDevice device = e.getDeviceWithoutSession();
LOGGER.log(Level.WARNING, "No raw session found for contact " + device + ". ", e);
} catch (CorruptedOmemoKeyException | CryptoFailedException e) {
LOGGER.log(Level.WARNING, "Could not decrypt incoming message: ", e);
}
// Upload fresh bundle.
if (getOmemoStoreBackend().loadOmemoPreKeys(userDevice).size() < OmemoConstants.PRE_KEY_COUNT_PER_BUNDLE) {
LOGGER.log(Level.FINE, "We used up a preKey. Upload a fresh bundle.");
try {
getOmemoStoreBackend().replenishKeys(userDevice);
OmemoBundleElement bundleElement = getOmemoStoreBackend().packOmemoBundle(userDevice);
publishBundle(manager.getConnection(), userDevice, bundleElement);
} catch (CorruptedOmemoKeyException | InterruptedException | SmackException.NoResponseException | SmackException.NotConnectedException | XMPPException.XMPPErrorException | NotALeafNodeException e) {
LOGGER.log(Level.WARNING, "Could not republish replenished bundle.", e);
}
}
return decrypted;
}
}
use of org.jivesoftware.smackx.omemo.element.OmemoElement in project Smack by igniterealtime.
the class OmemoService method sendRatchetUpdate.
/**
* Send an empty OMEMO message to contactsDevice in order to forward the ratchet.
*
* @param managerGuard OMEMO manager
* @param contactsDevice contacts OMEMO device
*
* @throws CorruptedOmemoKeyException if our or their OMEMO key is corrupted.
* @throws InterruptedException if the calling thread was interrupted.
* @throws SmackException.NoResponseException if there was no response from the remote entity.
* @throws NoSuchAlgorithmException if AES encryption fails
* @throws SmackException.NotConnectedException if the XMPP connection is not connected.
* @throws CryptoFailedException if encryption fails (should not happen though, but who knows...)
* @throws CannotEstablishOmemoSessionException if we cannot establish a session with contactsDevice.
* @throws IOException if an I/O error occurred.
*/
private void sendRatchetUpdate(OmemoManager.LoggedInOmemoManager managerGuard, OmemoDevice contactsDevice) throws CorruptedOmemoKeyException, InterruptedException, SmackException.NoResponseException, NoSuchAlgorithmException, SmackException.NotConnectedException, CryptoFailedException, CannotEstablishOmemoSessionException, IOException {
OmemoManager manager = managerGuard.get();
OmemoElement ratchetUpdate = createRatchetUpdateElement(managerGuard, contactsDevice);
XMPPConnection connection = manager.getConnection();
Message message = connection.getStanzaFactory().buildMessageStanza().to(contactsDevice.getJid()).addExtension(ratchetUpdate).build();
connection.sendStanza(message);
}
use of org.jivesoftware.smackx.omemo.element.OmemoElement 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);
}
Aggregations