diff --git a/pkg/connector/handlesignal.go b/pkg/connector/handlesignal.go index 72759059..b39f2028 100644 --- a/pkg/connector/handlesignal.go +++ b/pkg/connector/handlesignal.go @@ -110,7 +110,8 @@ func (s *SignalClient) wrapDecryptionError(evt *events.DecryptionError) bridgev2 Timestamp: time.UnixMilli(int64(evt.Timestamp)), }, Data: evt, - ID: "decrypterr|" + signalid.MakeMessageID(evt.Sender, evt.Timestamp), + // TODO use main message id and edit it if it later becomes decryptable? + ID: "decrypterr|" + signalid.MakeMessageID(evt.Sender, evt.Timestamp), ConvertMessageFunc: convertDecryptionError, } diff --git a/pkg/libsignalgo/sealedsender.go b/pkg/libsignalgo/sealedsender.go index f747b357..253e512a 100644 --- a/pkg/libsignalgo/sealedsender.go +++ b/pkg/libsignalgo/sealedsender.go @@ -24,7 +24,6 @@ import "C" import ( "context" "runtime" - "time" "github.com/google/uuid" ) @@ -113,7 +112,7 @@ func SealedSenderDecrypt( ciphertext []byte, localAddress *SealedSenderAddress, trustRoot *PublicKey, - timestamp time.Time, + timestamp uint64, sessionStore SessionStore, identityStore IdentityKeyStore, preKeyStore PreKeyStore, @@ -134,7 +133,7 @@ func SealedSenderDecrypt( &senderDeviceID, BytesToBuffer(ciphertext), trustRoot.ptr, - C.uint64_t(timestamp.UnixMilli()), + C.uint64_t(timestamp), C.CString(localAddress.E164), C.CString(localAddress.UUID.String()), C.uint32_t(localAddress.DeviceID), diff --git a/pkg/libsignalgo/session_test.go b/pkg/libsignalgo/session_test.go index 17ea8c97..9d108d59 100644 --- a/pkg/libsignalgo/session_test.go +++ b/pkg/libsignalgo/session_test.go @@ -294,7 +294,7 @@ func TestSealedSenderSession(t *testing.T) { ciphertext, recipientAddress, trustRoot.GetPublicKey(), - time.UnixMilli(31335), + 31335, bobStore, bobStore, bobStore, diff --git a/pkg/signalmeow/receiving.go b/pkg/signalmeow/receiving.go index 850e204d..8a40a169 100644 --- a/pkg/signalmeow/receiving.go +++ b/pkg/signalmeow/receiving.go @@ -22,7 +22,6 @@ import ( "fmt" "net/http" "strings" - "time" "github.com/google/uuid" "github.com/rs/zerolog" @@ -287,9 +286,9 @@ func (cli *Client) incomingAPIMessageHandler(ctx context.Context, req *signalpb. result := cli.decryptEnvelope(ctx, envelope) - err = cli.handleDecryptedResult(ctx, result, destinationServiceID, envelope.GetServerTimestamp()) + err = cli.handleDecryptedResult(ctx, result, envelope, destinationServiceID) if err != nil { - log.Err(err).Msg("handleDecryptedResult error") + log.Err(err).Msg("Error handling decrypted result") return nil, err } @@ -307,17 +306,17 @@ func (cli *Client) decryptEnvelope( destinationServiceID, err := libsignalgo.ServiceIDFromString(envelope.GetDestinationServiceId()) if err != nil { log.Err(err).Str("destination_service_id", envelope.GetDestinationServiceId()).Msg("Failed to parse destination service ID") - return DecryptionResult{Err: err, Urgent: envelope.GetUrgent()} + return DecryptionResult{Err: fmt.Errorf("failed to parse destination service ID: %w", err)} } - var result *DecryptionResult switch *envelope.Type { case signalpb.Envelope_UNIDENTIFIED_SENDER: - result, err = cli.decryptUnidentifiedSenderEnvelope(ctx, destinationServiceID, envelope) + result, err := cli.decryptUnidentifiedSenderEnvelope(ctx, destinationServiceID, envelope) if err != nil { - log.Err(err).Msg("decryptUnidentifiedSenderEnvelope error") - return DecryptionResult{Err: err, Urgent: envelope.GetUrgent()} + log.Err(err).Msg("Failed to decrypt sealed sender message") + result.Err = fmt.Errorf("failed to decrypt unidentified sender envelope: %w", err) } + return result case signalpb.Envelope_PREKEY_BUNDLE: sender, err := libsignalgo.NewUUIDAddressFromString( @@ -325,44 +324,50 @@ func (cli *Client) decryptEnvelope( uint(*envelope.SourceDevice), ) if err != nil { - return DecryptionResult{Err: fmt.Errorf("NewAddress error: %v", err), Urgent: envelope.GetUrgent()} + return DecryptionResult{Err: fmt.Errorf("failed to wrap address: %v", err)} } - result, err = cli.prekeyDecrypt(ctx, destinationServiceID, sender, envelope.Content) + result, err := cli.prekeyDecrypt(ctx, destinationServiceID, sender, envelope.Content) if err != nil { - log.Err(err).Msg("Prekey bundle decryption error") - } else { - log.Trace(). - Any("sender_address", result.SenderAddress). - Any("content", result.Content). - Msg("Prekey bundle decryption result") + log.Err(err).Msg("Failed to decrypt prekey bundle message") + return DecryptionResult{Err: fmt.Errorf("failed to decrypt prekey bundle envelope: %w", err), SenderAddress: sender} } + log.Trace(). + Any("sender_address", result.SenderAddress). + Any("content", result.Content). + Msg("Prekey bundle decryption result") + return *result case signalpb.Envelope_PLAINTEXT_CONTENT: + return DecryptionResult{Err: fmt.Errorf("plaintext messages are not supported")} case signalpb.Envelope_CIPHERTEXT: - message, err := libsignalgo.DeserializeMessage(envelope.Content) - if err != nil { - log.Err(err).Msg("DeserializeMessage error") - } senderAddress, err := libsignalgo.NewUUIDAddressFromString( *envelope.SourceServiceId, uint(*envelope.SourceDevice), ) if err != nil { - return DecryptionResult{Err: fmt.Errorf("NewAddress error: %v", err), Urgent: envelope.GetUrgent()} + return DecryptionResult{Err: fmt.Errorf("failed to wrap address: %w", err)} + } + message, err := libsignalgo.DeserializeMessage(envelope.Content) + if err != nil { + log.Err(err).Msg("Failed to deserialize ciphertext message") + return DecryptionResult{ + Err: fmt.Errorf("failed to deserialize message: %w", err), + SenderAddress: senderAddress, + } } sessionStore := cli.Store.SessionStore(destinationServiceID) if sessionStore == nil { return DecryptionResult{ - Err: fmt.Errorf("no session store for destination service ID %s", destinationServiceID), - Urgent: envelope.GetUrgent(), + Err: fmt.Errorf("no session store for destination service ID %s", destinationServiceID), + SenderAddress: senderAddress, } } identityStore := cli.Store.IdentityStore(destinationServiceID) if identityStore == nil { return DecryptionResult{ - Err: fmt.Errorf("no identity store for destination service ID %s", destinationServiceID), - Urgent: envelope.GetUrgent(), + Err: fmt.Errorf("no identity store for destination service ID %s", destinationServiceID), + SenderAddress: senderAddress, } } decryptedText, err := libsignalgo.Decrypt( @@ -374,105 +379,93 @@ func (cli *Client) decryptEnvelope( ) if err != nil { if strings.Contains(err.Error(), "message with old counter") { - log.Info().Msg("Duplicate message, ignoring") + log.Warn().Err(err).Msg("Duplicate message error while decrypting whisper ciphertext") } else { - log.Err(err).Msg("Whisper Decryption error") - } - } else { - err = stripPadding(&decryptedText) - if err != nil { - return DecryptionResult{Err: fmt.Errorf("stripPadding error: %v", err), Urgent: envelope.GetUrgent()} - } - content := signalpb.Content{} - err = proto.Unmarshal(decryptedText, &content) - if err != nil { - log.Err(err).Msg("Unmarshal error") - } - result = &DecryptionResult{ - SenderAddress: senderAddress, - Content: &content, + log.Err(err).Msg("Failed to decrypt whisper ciphertext message") } + return DecryptionResult{Err: fmt.Errorf("failed to decrypt ciphertext message: %w", err), SenderAddress: senderAddress} + } + err = stripPadding(&decryptedText) + if err != nil { + return DecryptionResult{Err: fmt.Errorf("failed to strip padding: %w", err), SenderAddress: senderAddress} + } + content := signalpb.Content{} + err = proto.Unmarshal(decryptedText, &content) + if err != nil { + return DecryptionResult{Err: fmt.Errorf("failed to unmarshal decrypted message: %w", err), SenderAddress: senderAddress} + } + return DecryptionResult{ + SenderAddress: senderAddress, + Content: &content, } case signalpb.Envelope_RECEIPT: - log.Warn().Msg("Received RECEIPT envelope, not yet implemented") - // TODO: handle receipt + return DecryptionResult{Err: fmt.Errorf("receipt envelopes are not yet supported")} case signalpb.Envelope_KEY_EXCHANGE: - log.Warn().Msg("Received KEY_EXCHANGE envelope, not supported") + return DecryptionResult{Err: fmt.Errorf("key exchange envelopes are not yet supported")} case signalpb.Envelope_UNKNOWN: - log.Warn().Msg("Received UNKNOWN envelope, not supported") + return DecryptionResult{Err: fmt.Errorf("unknown envelope type")} default: - log.Warn().Msg("Received actual unknown envelope type") + return DecryptionResult{Err: fmt.Errorf("unrecognized envelope type")} } - if result == nil { - log.Warn().Msg("Decryption result is nil") - return DecryptionResult{ - Err: fmt.Errorf("decryption result is nil"), - Urgent: envelope.GetUrgent(), - } - } - result.Urgent = envelope.GetUrgent() - return *result } -func (cli *Client) decryptUnidentifiedSenderEnvelope(ctx context.Context, destinationServiceID libsignalgo.ServiceID, envelope *signalpb.Envelope) (*DecryptionResult, error) { +func (cli *Client) decryptUnidentifiedSenderEnvelope(ctx context.Context, destinationServiceID libsignalgo.ServiceID, envelope *signalpb.Envelope) (result DecryptionResult, err error) { log := zerolog.Ctx(ctx) - var result *DecryptionResult if destinationServiceID != cli.Store.ACIServiceID() { log.Warn().Stringer("destination_service_id", destinationServiceID). Msg("Received UNIDENTIFIED_SENDER envelope for non-ACI destination") - // Return nil, nil to skip the rest of the handling and return 200 OK to the server - return nil, nil + return result, fmt.Errorf("received unidentified sender envelope for non-ACI destination") } usmc, err := libsignalgo.SealedSenderDecryptToUSMC( ctx, envelope.GetContent(), cli.Store.ACIIdentityStore, ) - if err != nil || usmc == nil { - if err == nil { - err = fmt.Errorf("usmc is nil") - } - log.Err(err).Msg("SealedSenderDecryptToUSMC error") - return nil, err + if err != nil { + return result, fmt.Errorf("failed to decrypt to USMC: %w", err) + } else if usmc == nil { + return result, fmt.Errorf("decrypting to USMC returned nil") } messageType, err := usmc.GetMessageType() if err != nil { - log.Err(err).Msg("GetMessageType error") + return result, fmt.Errorf("failed to get message type: %w", err) } senderCertificate, err := usmc.GetSenderCertificate() if err != nil { - log.Err(err).Msg("GetSenderCertificate error") + return result, fmt.Errorf("failed to get sender certificate: %w", err) } senderUUID, err := senderCertificate.GetSenderUUID() if err != nil { - log.Err(err).Msg("GetSenderUUID error") + return result, fmt.Errorf("failed to get sender UUID: %w", err) } senderDeviceID, err := senderCertificate.GetDeviceID() if err != nil { - log.Err(err).Msg("GetDeviceID error") + return result, fmt.Errorf("failed to get sender device ID: %w", err) } senderAddress, err := libsignalgo.NewACIServiceID(senderUUID).Address(uint(senderDeviceID)) if err != nil { - log.Err(err).Msg("NewAddress error") + return result, fmt.Errorf("failed to create sender address: %w", err) } + result.SenderAddress = senderAddress senderE164, err := senderCertificate.GetSenderE164() if err != nil { - log.Err(err).Msg("GetSenderE164 error") + return result, fmt.Errorf("failed to get sender E164: %w", err) } usmcContents, err := usmc.GetContents() if err != nil { - log.Err(err).Msg("GetContents error") + return result, fmt.Errorf("failed to get USMC contents: %w", err) } newLog := log.With(). Stringer("sender_uuid", senderUUID). Uint32("sender_device_id", senderDeviceID). Str("sender_e164", senderE164). + Uint8("sealed_sender_type", uint8(messageType)). Logger() log = &newLog ctx = log.WithContext(ctx) @@ -487,7 +480,6 @@ func (cli *Client) decryptUnidentifiedSenderEnvelope(ctx context.Context, destin switch messageType { case libsignalgo.CiphertextMessageTypeSenderKey: - log.Trace().Msg("SealedSender messageType is CiphertextMessageTypeSenderKey") decryptedText, err := libsignalgo.GroupDecrypt( ctx, usmcContents, @@ -496,39 +488,37 @@ func (cli *Client) decryptUnidentifiedSenderEnvelope(ctx context.Context, destin ) if err != nil { if strings.Contains(err.Error(), "message with old counter") { - log.Warn().Msg("Duplicate message, ignoring") - } else { - log.Err(err).Msg("GroupDecrypt error") - } - } else { - err = stripPadding(&decryptedText) - if err != nil { - return nil, fmt.Errorf("stripPadding error: %v", err) - } - content := signalpb.Content{} - err = proto.Unmarshal(decryptedText, &content) - if err != nil { - log.Err(err).Msg("Unmarshal error") - } - result = &DecryptionResult{ - SenderAddress: senderAddress, - Content: &content, - SealedSender: true, + log.Warn().Err(err).Msg("Duplicate message error while decrypting sealed sender sender key") + return result, err } + log.Err(err).Msg("Failed to decrypt sealed sender sender key message, trying generic function") + return cli.fallbackDecryptSealedSender(ctx, result, envelope) + } + err = stripPadding(&decryptedText) + if err != nil { + return result, fmt.Errorf("failed to strip padding: %w", err) + } + content := signalpb.Content{} + err = proto.Unmarshal(decryptedText, &content) + if err != nil { + return result, fmt.Errorf("failed to unmarshal decrypted sender key message: %w", err) } + result.Content = &content + return result, nil case libsignalgo.CiphertextMessageTypePreKey: - log.Trace().Msg("SealedSender messageType is CiphertextMessageTypePreKey") - result, err = cli.prekeyDecrypt(ctx, destinationServiceID, senderAddress, usmcContents) + var resultPtr *DecryptionResult + resultPtr, err = cli.prekeyDecrypt(ctx, destinationServiceID, senderAddress, usmcContents) if err != nil { - log.Err(err).Msg("Sealed sender prekey ciphertext decryption error") + log.Err(err).Msg("Failed to decrypt sealed sender prekey message, trying generic function") + return cli.fallbackDecryptSealedSender(ctx, result, envelope) } + return *resultPtr, nil case libsignalgo.CiphertextMessageTypeWhisper: - log.Trace().Msg("SealedSender messageType is CiphertextMessageTypeWhisper") message, err := libsignalgo.DeserializeMessage(usmcContents) if err != nil { - log.Err(err).Msg("Sealed sender whisper deserialize error") + return result, fmt.Errorf("failed to deserialize whisper message: %w", err) } decryptedText, err := libsignalgo.Decrypt( ctx, @@ -538,26 +528,23 @@ func (cli *Client) decryptUnidentifiedSenderEnvelope(ctx context.Context, destin cli.Store.ACIIdentityStore, ) if err != nil { - log.Err(err).Msg("Sealed sender whisper decryption error") - } else { - err = stripPadding(&decryptedText) - if err != nil { - return nil, fmt.Errorf("stripPadding error: %v", err) - } - content := signalpb.Content{} - err = proto.Unmarshal(decryptedText, &content) - if err != nil { - log.Err(err).Msg("Unmarshal error") - } - result = &DecryptionResult{ - SenderAddress: senderAddress, - Content: &content, - SealedSender: true, - } + log.Err(err).Msg("Failed to decrypt whisper message, trying generic function") + return cli.fallbackDecryptSealedSender(ctx, result, envelope) } + err = stripPadding(&decryptedText) + if err != nil { + return result, fmt.Errorf("failed to strip padding: %w", err) + } + content := signalpb.Content{} + err = proto.Unmarshal(decryptedText, &content) + if err != nil { + return result, fmt.Errorf("failed to unmarshal decrypted whisper message: %w", err) + } + result.Content = &content + return result, nil case libsignalgo.CiphertextMessageTypePlaintext: - log.Debug().Msg("SealedSender messageType is CiphertextMessageTypePlaintext") + log.Warn().Msg("Unsupported plaintext sealed sender message") // TODO: handle plaintext (usually DecryptionErrorMessage) and retries // when implementing SenderKey groups @@ -580,47 +567,42 @@ func (cli *Client) decryptUnidentifiedSenderEnvelope(ctx context.Context, destin // SealedSender: true, //} - return nil, nil + return result, fmt.Errorf("plaintext sealed sender messages are not supported") default: - log.Warn().Msg("SealedSender messageType is unknown") + log.Warn().Msg("Unrecognized sealed sender message type") + return cli.fallbackDecryptSealedSender(ctx, result, envelope) } +} - // If we couldn't decrypt with specific decryption methods, try sealedSenderDecrypt - if result == nil { - log.Debug().Msg("Didn't decrypt with specific methods, trying sealedSenderDecrypt") - var err error - result, err = cli.sealedSenderDecrypt(ctx, envelope) - if err != nil { - if strings.Contains(err.Error(), "self send of a sealed sender message") { - log.Debug().Msg("Message sent by us, ignoring") - } else if strings.Contains(err.Error(), "message with old counter") { - log.Info().Msg("Duplicate message, ignoring (sealedSenderDecrypt)") - } else { - log.Err(err).Msg("sealedSenderDecrypt error") - } +func (cli *Client) fallbackDecryptSealedSender(ctx context.Context, fallbackResult DecryptionResult, envelope *signalpb.Envelope) (DecryptionResult, error) { + log := zerolog.Ctx(ctx) + result, err := cli.sealedSenderDecrypt(ctx, envelope) + if err != nil { + if strings.Contains(err.Error(), "self send of a sealed sender message") { + log.Debug().Msg("Message sent by us, ignoring") + } else if strings.Contains(err.Error(), "message with old counter") { + log.Info().Msg("Duplicate message, ignoring (sealedSenderDecrypt)") } else { - log.Trace(). - Any("sender_address", result.SenderAddress). - Any("content", result.Content). - Msg("SealedSender decrypt result") + log.Err(err).Msg("Failed to decrypt sealed sender message with fallback method") } + return fallbackResult, fmt.Errorf("failed to decrypt unrecognized sealed sender message: %w", err) } - return result, nil + log.Trace(). + Any("sender_address", result.SenderAddress). + Any("content", result.Content). + Msg("SealedSender decrypt result") + return *result, nil } // TODO: we should split this up into multiple functions func (cli *Client) handleDecryptedResult( ctx context.Context, result DecryptionResult, + envelope *signalpb.Envelope, destinationServiceID libsignalgo.ServiceID, - serverTimestamp uint64, ) error { log := zerolog.Ctx(ctx) - if result.Content == nil { - log.Warn().Msg("Decrypted content is nil") - return nil - } // result.Err is set if there was an error during decryption and we // should notifiy the user that the message could not be decrypted @@ -631,21 +613,29 @@ func (cli *Client) handleDecryptedResult( } else if theirServiceID.Type != libsignalgo.ServiceIDTypeACI { log.Warn().Any("their_service_id", theirServiceID).Msg("Sender ServiceID is not an ACI") } - log.Err(result.Err).Stringer("sender", theirServiceID).Bool("urgent", result.Urgent).Msg("Decryption error") + log.Err(result.Err). + Stringer("sender", theirServiceID). + Bool("urgent", envelope.GetUrgent()). + Uint64("server_ts", envelope.GetServerTimestamp()). + Uint64("client_ts", envelope.GetTimestamp()). + Msg("Decryption error") // Only send decryption error event if the message was urgent, // to prevent spamming errors for typing notifications and whatnot - if result.Urgent { + if envelope.GetUrgent() { cli.handleEvent(&events.DecryptionError{ Sender: theirServiceID.UUID, Err: result.Err, - Timestamp: serverTimestamp, + Timestamp: envelope.GetTimestamp(), }) } - // Intentionally not returning here. In most cases there's nothing besides the error so - // nothing else will happen, but if there is content we still want to do what we can with it + // TODO there are probably no cases with both content and an error } content := result.Content + if content == nil { + log.Warn().Msg("Decrypted content is nil") + return nil + } name, _ := result.SenderAddress.Name() deviceId, _ := result.SenderAddress.DeviceID() @@ -1055,7 +1045,6 @@ type DecryptionResult struct { Content *signalpb.Content SealedSender bool Err error - Urgent bool } const prodServerTrustRootStr = "BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF" @@ -1074,35 +1063,34 @@ func (cli *Client) sealedSenderDecrypt(ctx context.Context, envelope *signalpb.E cli.Store.ACI, uint32(cli.Store.DeviceID), ) - timestamp := time.Unix(0, int64(*envelope.Timestamp)) result, err := libsignalgo.SealedSenderDecrypt( ctx, envelope.Content, localAddress, prodServerTrustRootKey, - timestamp, + envelope.GetTimestamp(), cli.Store.ACISessionStore, cli.Store.ACIIdentityStore, cli.Store.ACIPreKeyStore, cli.Store.ACIPreKeyStore, ) if err != nil { - return nil, fmt.Errorf("SealedSenderDecrypt error: %w", err) + return nil, err } msg := result.Message err = stripPadding(&msg) if err != nil { - return nil, fmt.Errorf("stripPadding error: %w", err) + return nil, fmt.Errorf("failed to strip padding: %w", err) } address, err := libsignalgo.NewACIServiceID(result.Sender.UUID).Address(uint(result.Sender.DeviceID)) if err != nil { - return nil, fmt.Errorf("NewAddress error: %w", err) + return nil, fmt.Errorf("failed to wrap sender address: %w", err) } content := &signalpb.Content{} err = proto.Unmarshal(msg, content) if err != nil { - return nil, fmt.Errorf("Unmarshal error: %w", err) + return nil, fmt.Errorf("failed to unmarshal decrypted content: %w", err) } return &DecryptionResult{ SenderAddress: address, @@ -1141,25 +1129,21 @@ func (cli *Client) prekeyDecrypt(ctx context.Context, destination libsignalgo.Se pks, ) if err != nil { - err = fmt.Errorf("DecryptPreKey error: %v", err) - return nil, err + return nil, fmt.Errorf("failed to decrypt prekey message: %w", err) } err = stripPadding(&data) if err != nil { - err = fmt.Errorf("stripPadding error: %v", err) - return nil, err + return nil, fmt.Errorf("failed to strip padding: %w", err) } content := &signalpb.Content{} err = proto.Unmarshal(data, content) if err != nil { - err = fmt.Errorf("Unmarshal error: %v", err) - return nil, err + return nil, fmt.Errorf("failed to unmarshal decrypted prekey message: %w", err) } - DecryptionResult := &DecryptionResult{ + return &DecryptionResult{ SenderAddress: sender, Content: content, - } - return DecryptionResult, nil + }, nil } func stripPadding(contents *[]byte) error {