public OutgoingPushMessage encrypt(SignalProtocolAddress destination, byte[] unpaddedMessage, bool legacy, bool silent) { SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, destination); PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); CiphertextMessage message = sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage)); uint remoteRegistrationId = sessionCipher.getRemoteRegistrationId(); String body = Base64.encodeBytes(message.serialize()); uint type; switch (message.getType()) { case CiphertextMessage.PREKEY_TYPE: type = (uint)Envelope.Types.Type.PrekeyBundle; break; // todo check case CiphertextMessage.WHISPER_TYPE: type = (uint)Envelope.Types.Type.Ciphertext; break; // todo check default: throw new Exception("Bad type: " + message.getType()); } return(new OutgoingPushMessage(type, destination.DeviceId, remoteRegistrationId, legacy ? body : null, legacy ? null : body, silent)); }
private async Task buildSessionForDevicesAsync(Dictionary <uint, SessionCipher> sessions, IList <SignalProtocolAddress> devices) { if (devices.Count <= 0) { return; } SignalProtocolAddress device = devices[0]; devices.RemoveAt(0); // Check if there exists already a session for this device: if (OMEMO_HELPER.OMEMO_STORE.ContainsSession(device)) { // If yes, the load it: SessionCipher cipher = OMEMO_HELPER.loadCipher(device); sessions.Add(device.getDeviceId(), cipher); Logger.Info("[OmemoSessionBuildHelper] Session for " + device.ToString() + " loaded from cache."); } else { // Else try to build a new one by requesting the devices bundle information: OmemoBundleInformationResultMessage bundleMsg = await requestBundleInformationAsync(device); if (!(bundleMsg is null)) { SignalProtocolAddress address = OMEMO_HELPER.newSession(CHAT_JID, bundleMsg); SessionCipher cipher = OMEMO_HELPER.loadCipher(address); sessions.Add(device.getDeviceId(), cipher); Logger.Info("[OmemoSessionBuildHelper] Session with " + device.ToString() + " established."); }
public OmemoFingerprint toOmemoFingerprint() { SignalProtocolAddress address = new SignalProtocolAddress(bareJid, deviceId); ECPublicKey pubKey = Curve.decodePoint(identityPubKey, 0); return(new OmemoFingerprint(pubKey, address, lastSeen, trusted)); }
private byte[] decrypt(SignalServiceEnvelope envelope, byte[] ciphertext) { SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.getSource(), (uint)envelope.getSourceDevice()); SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, sourceAddress); byte[] paddedMessage; if (envelope.isPreKeySignalMessage()) { paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext)); } else if (envelope.isSignalMessage()) { paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext)); } else { throw new InvalidMessageException("Unknown type: " + envelope.getType() + " from " + envelope.getSource()); } PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); return(transportDetails.getStrippedPaddingMessageBody(paddedMessage)); }
private bool onRequestBundleInformationMessage(IQMessage msg) { if (STATE != OmemoSessionBuildHelperState.REQUESTING_BUNDLE_INFORMATION) { return(true); } if (msg is OmemoBundleInformationResultMessage bundleMsg) { Logger.Info("[OmemoSessionBuildHelper] Session with " + curAddress.getName() + ':' + curAddress.getDeviceId() + " established."); SignalProtocolAddress address = OMEMO_HELPER.newSession(CHAT_JID, bundleMsg); SessionCipher cipher = OMEMO_HELPER.loadCipher(address); SESSION.DEVICE_SESSIONS.Add(curAddress.getDeviceId(), cipher); createSessionForNextDevice(); return(true); } else if (msg is IQErrorMessage errMsg) { if (errMsg.ERROR_OBJ.ERROR_NAME == ErrorName.ITEM_NOT_FOUND) { Logger.Error("[OmemoSessionBuildHelper] Failed to establish session - " + curAddress.getName() + ':' + curAddress.getDeviceId() + " doesn't support OMEMO: " + errMsg.ERROR_OBJ.ToString()); setState(OmemoSessionBuildHelperState.ERROR); ON_SESSION_RESULT(this, new OmemoSessionBuildResult(OmemoSessionBuildError.TARGET_DOES_NOT_SUPPORT_OMEMO)); } else { Logger.Error("[OmemoSessionBuildHelper] Failed to establish session - request bundle info failed(" + curAddress.getName() + ':' + curAddress.getDeviceId() + "): " + errMsg.ERROR_OBJ.ToString()); setState(OmemoSessionBuildHelperState.ERROR); ON_SESSION_RESULT(this, new OmemoSessionBuildResult(OmemoSessionBuildError.REQUEST_BUNDLE_INFORMATION_IQ_ERROR)); } return(true); } return(false); }
public bool IsTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { IdentityKey trusted; trustedKeys.TryGetValue(address, out trusted); // get(name) return(trusted == null || trusted.Equals(identityKey)); }
public static SessionRecord LoadSession(SignalProtocolAddress address) { string index = GetSessionCacheIndex(address.Name, address.DeviceId); SessionRecord record; lock (DBLock) { if (SessionsCache.TryGetValue(index, out record)) { return(record); } using (var ctx = new LibsignalDBContext()) { var session = ctx.Sessions .Where(s => s.Username == address.Name && s.DeviceId == address.DeviceId) .AsNoTracking() .SingleOrDefault(); if (session != null) { record = new SessionRecord(Base64.decode(session.Session)); } else { record = new SessionRecord(); } SessionsCache[index] = record; return(record); } } }
public static void SaveIdentityLocked(SignalProtocolAddress address, string identity) { lock (DBLock) { using (var ctx = new LibsignalDBContext()) { var old = ctx.Identities .Where(i => i.Username == address.Name) .FirstOrDefault(); //could contain stale data if (old == null) { ctx.Identities.Add(new SignalIdentity() { IdentityKey = identity, Username = address.Name, VerifiedStatus = VerifiedStatus.Default }); } else if (old.IdentityKey != identity) { if (old.VerifiedStatus == VerifiedStatus.Verified) { old.VerifiedStatus = VerifiedStatus.Unverified; } old.IdentityKey = identity; var childSessions = ctx.Sessions .Where(s => s.Username == address.Name && s.DeviceId != address.DeviceId); ctx.Sessions.RemoveRange(childSessions); var messages = InsertIdentityChangedMessages(address.Name); SignalLibHandle.Instance.DispatchHandleIdentityKeyChange(messages); } ctx.SaveChanges(); } } }
//--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- public OmemoFingerprint(ECPublicKey identityPubKey, SignalProtocolAddress address, DateTime lastSeen, bool trusted) { IDENTITY_PUB_KEY = identityPubKey; ADDRESS = address; this.lastSeen = lastSeen; this.trusted = trusted; }
public static void StoreSession(SignalProtocolAddress address, SessionRecord record) { string index = GetSessionCacheIndex(address.Name, address.DeviceId); lock (DBLock) { using (var ctx = new LibsignalDBContext()) { var session = ctx.Sessions .Where(s => s.DeviceId == address.DeviceId && s.Username == address.Name) .SingleOrDefault(); if (session != null) { session.Session = Base64.encodeBytes(record.serialize()); } else { ctx.Sessions.Add(new SignalSession() { DeviceId = address.DeviceId, Session = Base64.encodeBytes(record.serialize()), Username = address.Name }); } SessionsCache[index] = record; ctx.SaveChanges(); } } }
public OmemoDeviceTable(SignalProtocolAddress device, string accountId) { this.id = generateId(accountId, device.getName(), device.getDeviceId()); this.accountId = accountId; this.name = device.getName(); this.deviceId = device.getDeviceId(); }
public OutgoingPushMessage Encrypt(SignalProtocolAddress destination, UnidentifiedAccess?unidentifiedAccess, byte[] unpaddedMessage) { if (unidentifiedAccess != null) { SealedSessionCipher sessionCipher = new SealedSessionCipher(signalProtocolStore, localAddress.Uuid, localAddress.GetNumber(), 1); PushTransportDetails transportDetails = new PushTransportDetails((uint)sessionCipher.GetSessionVersion(destination)); byte[] ciphertext = sessionCipher.Encrypt(destination, unidentifiedAccess.UnidentifiedCertificate, transportDetails.getPaddedMessageBody(unpaddedMessage)); string body = Base64.EncodeBytes(ciphertext); uint remoteRegistrationId = (uint)sessionCipher.GetRemoteRegistrationId(destination); return(new OutgoingPushMessage((uint)Envelope.Types.Type.UnidentifiedSender, destination.DeviceId, remoteRegistrationId, body)); } else { SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, destination); PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); CiphertextMessage message = sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage)); uint remoteRegistrationId = sessionCipher.getRemoteRegistrationId(); string body = Base64.EncodeBytes(message.serialize()); var type = (message.getType()) switch { CiphertextMessage.PREKEY_TYPE => (uint)Envelope.Types.Type.PrekeyBundle, CiphertextMessage.WHISPER_TYPE => (uint)Envelope.Types.Type.Ciphertext, _ => throw new Exception("Bad type: " + message.getType()), }; return(new OutgoingPushMessage(type, destination.DeviceId, remoteRegistrationId, body)); } }
public OmemoFingerprint getFingerprint(SignalProtocolAddress address, string accountId) { string chatId = ChatTable.generateId(address.getName(), accountId); string id = OmemoFingerprintTable.generateId(chatId, address); List <OmemoFingerprintTable> list = dB.Query <OmemoFingerprintTable>(true, "SELECT * FROM " + DBTableConsts.OMEMO_FINGERPRINT_TABLE + " WHERE id = ?;", id); return(list.Count <= 0 ? null : list[0].toOmemoFingerprint()); }
public bool ContainsSession(SignalProtocolAddress address) { var name = address.getName(); var deviceId = address.getDeviceId(); var query = conn.Table <Session>().Where(v => v.Name == name && v.DeviceId == deviceId); return(query.Count() != 0); }
public SignalProtocolAddress newSession(string chatJid, uint recipientDeviceId, PreKeyBundle recipientPreKey) { SignalProtocolAddress address = new SignalProtocolAddress(chatJid, recipientDeviceId); SessionBuilder builder = new SessionBuilder(OMEMO_STORE, address); builder.process(recipientPreKey); return(address); }
//--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\ #region --Misc Methods (Public)-- public SessionRecord LoadSession(SignalProtocolAddress address) { if (ContainsSession(address)) { return(SESSIONS[address]); } return(new SessionRecord()); }
public SignalProtocolAddress newSession(string chatJid, uint recipientDeviceId, PreKeyBundle recipientPreKey) { SignalProtocolAddress address = new SignalProtocolAddress(chatJid, recipientDeviceId); SessionBuilder builder = new SessionBuilder(SESSION_STORE, PRE_KEY_STORE, SIGNED_PRE_KEY_STORE, IDENTITY_STORE, address); builder.process(recipientPreKey); return(address); }
public SessionRecord getSession(SignalProtocolAddress address, string accountId) { List <SessionStoreTable> list = dB.Query <SessionStoreTable>(true, "SELECT * FROM " + DBTableConsts.SESSION_STORE_TABLE + " WHERE id = ?;", SessionStoreTable.generateId(address, accountId)); if (list.Count <= 0) { return(null); } return(new SessionRecord(list[0].session)); }
private async Task <OutgoingPushMessage> GetEncryptedMessage(CancellationToken token, PushServiceSocket socket, SignalServiceAddress recipient, UnidentifiedAccess?unidentifiedAccess, uint deviceId, byte[] plaintext) { SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(recipient.E164number, deviceId); SignalServiceCipher cipher = new SignalServiceCipher(LocalAddress, Store, null); if (!Store.ContainsSession(signalProtocolAddress)) { try { List <PreKeyBundle> preKeys = await socket.GetPreKeys(token, recipient, unidentifiedAccess, deviceId); foreach (PreKeyBundle preKey in preKeys) { if (CredentialsProvider.User.Equals(recipient.E164number) && CredentialsProvider.DeviceId == preKey.getDeviceId()) { continue; } try { SignalProtocolAddress preKeyAddress = new SignalProtocolAddress(recipient.E164number, preKey.getDeviceId()); SessionBuilder sessionBuilder = new SessionBuilder(Store, preKeyAddress); sessionBuilder.process(preKey); } catch (libsignal.exceptions.UntrustedIdentityException) { throw new UntrustedIdentityException("Untrusted identity key!", recipient.E164number, preKey.getIdentityKey()); } } if (EventListener != null) { EventListener.OnSecurityEvent(recipient); } } catch (InvalidKeyException e) { throw new Exception(e.Message); } } try { return(cipher.Encrypt(signalProtocolAddress, unidentifiedAccess, plaintext)); } catch (libsignal.exceptions.UntrustedIdentityException e) { throw new UntrustedIdentityException("Untrusted on send", e.getName(), e.getUntrustedIdentity()); } }
public SessionRecord LoadSession(SignalProtocolAddress address) { SessionRecord session = OmemoSignalKeyDBManager.INSTANCE.getSession(address, ACCOUNT.getBareJid()); if (session is null) { Logger.Warn("No existing libsignal session found for: " + address.ToString()); session = new SessionRecord(); } return(session); }
public SessionRecord LoadSession(SignalProtocolAddress address) { SessionRecord session = SignalKeyDBManager.INSTANCE.getSession(address, ACCOUNT.getIdAndDomain()); if (session == null) { Logger.Warn("No existing session information found."); session = new SessionRecord(); } return(session); }
public void setSession(SignalProtocolAddress address, SessionRecord record, string accountId) { dB.InsertOrReplace(new SessionStoreTable() { id = SessionStoreTable.generateId(address, accountId), accountId = accountId, deviceId = address.getDeviceId(), name = address.getName(), session = record.serialize() }); }
internal static IdentityKey GetIdentityKey(SignalProtocolAddress address) { lock (DBLock) { using (var ctx = new LibsignalDBContext()) { return(new IdentityKey(Base64.Decode(ctx.Identities .Where(identity => identity.Username == address.Name) .Single().IdentityKey), 0)); } } }
public void StoreSession(SignalProtocolAddress address, SessionRecord record) { DeleteSession(address); // TODO: sqlite-net combined private keys for insertOrReplace var session = new Session() { DeviceId = address.getDeviceId(), Name = address.getName(), Record = record.serialize() }; conn.InsertOrReplace(session); return; }
public static bool ContainsSession(SignalProtocolAddress address) { lock (DBLock) { using (var ctx = new LibsignalDBContext()) { var session = ctx.Sessions .Where(s => s.Username == address.Name && s.DeviceId == address.DeviceId) .SingleOrDefault(); return(session != null); } } }
private byte[] Decrypt(UnidentifiedSenderMessageContent message) { SignalProtocolAddress sender = new SignalProtocolAddress(message.SenderCertificate.Sender, (uint)message.SenderCertificate.SenderDeviceId); switch ((uint)message.Type) { case CiphertextMessage.WHISPER_TYPE: return(new SessionCipher(SignalProtocolStore, sender).decrypt(new SignalMessage(message.Content))); case CiphertextMessage.PREKEY_TYPE: return(new SessionCipher(SignalProtocolStore, sender).decrypt(new PreKeySignalMessage(message.Content))); default: throw new InvalidMessageException("Unknown type: " + message.Type); } }
//--------------------------------------------------------Constructor:----------------------------------------------------------------\\ #region --Constructors-- /// <summary> /// Basic Constructor /// </summary> /// <history> /// 10/08/2018 Created [Fabian Sauter] /// </history> internal OmemoSessionBuildHelper(string chatJid, string bareAccountJid, string fullAccountJid, Action <OmemoSessionBuildHelper, OmemoSessionBuildResult> onSessionResult, XMPPConnection2 connection, OmemoHelper omemoHelper) { this.CONNECTION = connection; this.ON_SESSION_RESULT = onSessionResult; this.CHAT_JID = chatJid; this.BARE_ACCOUNT_JID = bareAccountJid; this.FULL_ACCOUNT_JID = fullAccountJid; this.OMEMO_HELPER = omemoHelper; this.STATE = OmemoSessionBuildHelperState.NOT_STARTED; this.requestDeviceListHelper = null; this.requestBundleInfoHelper = null; this.SESSION = new OmemoSession(chatJid); this.curAddress = null; }
/// <summary> /// Validates if the given identity public key should be trusted. /// </summary> /// <param name="address">The signal protocol address corresponding to the given public identity key.</param> /// <param name="publicKey">The public identity key we want to validate.</param> /// <param name="omemoStore">The OMEMO store that keeps all OMEMO related keys.</param> /// <returns>True if we trust else false.</returns> private Task <bool> isFingerprintTrustedAsync(SignalProtocolAddress address, ECPublicKey publicKey, IOmemoStore omemoStore) { return(Task.Run(() => { OmemoFingerprint fingerprint = omemoStore.LoadFingerprint(address); if (!(fingerprint is null)) { if (!fingerprint.checkIdentityKey(publicKey)) { Logger.Warn("Received not OMEMO encrypted message with a not matching public identity key from: " + address.ToString()); return false; } fingerprint.lastSeen = DateTime.Now; }
public static void DeleteSession(SignalProtocolAddress address) { lock (DBLock) { string index = GetSessionCacheIndex(address.Name, address.DeviceId); SessionsCache.Remove(index); using (var ctx = new LibsignalDBContext()) { var sessions = ctx.Sessions .Where(s => s.Username == address.Name && s.DeviceId == address.DeviceId); ctx.Sessions.RemoveRange(sessions); ctx.SaveChanges(); } } }
public SessionRecord LoadSession(SignalProtocolAddress address) { var name = address.getName(); var deviceId = address.getDeviceId(); var query = conn.Table <Session>().Where(t => t.Name == name && t.DeviceId == deviceId); if (query != null && query.Any()) { return(new SessionRecord(query.First().Record)); } else { return(new SessionRecord()); } }