status-im/status-go

View on GitHub
protocol/messenger_contact_verification.go

Summary

Maintainability
B
4 hrs
Test Coverage
F
0%
package protocol

import (
    "context"
    "strings"

    "github.com/pkg/errors"
    "go.uber.org/zap"

    "github.com/ethereum/go-ethereum/common/hexutil"

    "github.com/golang/protobuf/proto"

    "github.com/status-im/status-go/eth-node/crypto"
    "github.com/status-im/status-go/eth-node/types"

    "github.com/status-im/status-go/protocol/common"
    "github.com/status-im/status-go/protocol/protobuf"
    "github.com/status-im/status-go/protocol/requests"
    v1protocol "github.com/status-im/status-go/protocol/v1"
    "github.com/status-im/status-go/protocol/verification"
)

const minContactVerificationMessageLen = 1
const maxContactVerificationMessageLen = 280

func (m *Messenger) SendContactVerificationRequest(ctx context.Context, contactID string, challenge string) (*MessengerResponse, error) {
    if len(challenge) < minContactVerificationMessageLen || len(challenge) > maxContactVerificationMessageLen {
        return nil, errors.New("invalid verification request challenge length")
    }

    contact, ok := m.allContacts.Load(contactID)
    if !ok || !contact.mutual() {
        return nil, errors.New("must be a mutual contact")
    }

    verifRequest := &verification.Request{
        From:          common.PubkeyToHex(&m.identity.PublicKey),
        To:            contact.ID,
        Challenge:     challenge,
        RequestStatus: verification.RequestStatusPENDING,
        RepliedAt:     0,
    }

    chat, ok := m.allChats.Load(contactID)
    if !ok {
        publicKey, err := contact.PublicKey()
        if err != nil {
            return nil, err
        }
        chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
        // We don't want to show the chat to the user
        chat.Active = false
    }

    m.allChats.Store(chat.ID, chat)
    clock, _ := chat.NextClockAndTimestamp(m.getTimesource())

    request := &protobuf.RequestContactVerification{
        Clock:     clock,
        Challenge: challenge,
    }

    encodedMessage, err := proto.Marshal(request)
    if err != nil {
        return nil, err
    }

    rawMessage, err := m.dispatchMessage(ctx, common.RawMessage{
        LocalChatID: chat.ID,
        Payload:     encodedMessage,
        MessageType: protobuf.ApplicationMetadataMessage_REQUEST_CONTACT_VERIFICATION,
        ResendType:  common.ResendTypeDataSync,
    })

    if err != nil {
        return nil, err
    }

    contact.VerificationStatus = VerificationStatusVERIFYING
    contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime()

    err = m.persistence.SaveContact(contact, nil)
    if err != nil {
        return nil, err
    }

    // We sync the contact with the other devices
    err = m.syncContact(context.Background(), contact, m.dispatchMessage)
    if err != nil {
        return nil, err
    }

    m.allContacts.Store(contact.ID, contact)

    verifRequest.RequestedAt = clock
    verifRequest.ID = rawMessage.ID

    err = m.verificationDatabase.SaveVerificationRequest(verifRequest)
    if err != nil {
        return nil, err
    }

    err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
    if err != nil {
        return nil, err
    }

    chatMessage, err := m.createLocalContactVerificationMessage(request.Challenge, chat, rawMessage.ID, common.ContactVerificationStatePending)
    if err != nil {
        return nil, err
    }

    err = m.persistence.SaveMessages([]*common.Message{chatMessage})
    if err != nil {
        return nil, err
    }

    response := &MessengerResponse{}

    response.AddVerificationRequest(verifRequest)

    err = m.createOrUpdateOutgoingContactVerificationNotification(contact, response, verifRequest, chatMessage, nil)
    if err != nil {
        return nil, err
    }

    response.AddMessage(chatMessage)

    err = m.prepareMessages(response.messages)
    if err != nil {
        return nil, err
    }

    return response, nil
}

func (m *Messenger) GetVerificationRequestSentTo(ctx context.Context, contactID string) (*verification.Request, error) {
    _, ok := m.allContacts.Load(contactID)
    if !ok {
        return nil, errors.New("contact not found")
    }

    return m.verificationDatabase.GetLatestVerificationRequestSentTo(contactID)
}

func (m *Messenger) GetReceivedVerificationRequests(ctx context.Context) ([]*verification.Request, error) {
    myPubKey := hexutil.Encode(crypto.FromECDSAPub(&m.identity.PublicKey))
    return m.verificationDatabase.GetReceivedVerificationRequests(myPubKey)
}

func (m *Messenger) CancelVerificationRequest(ctx context.Context, id string) (*MessengerResponse, error) {
    verifRequest, err := m.verificationDatabase.GetVerificationRequest(id)
    if err != nil {
        return nil, err
    }

    if verifRequest == nil {
        m.logger.Error("could not find verification request with id", zap.String("id", id))
        return nil, verification.ErrVerificationRequestNotFound
    }

    if verifRequest.From != common.PubkeyToHex(&m.identity.PublicKey) {
        return nil, errors.New("Can cancel only outgoing contact request")
    }

    contactID := verifRequest.To
    contact, ok := m.allContacts.Load(contactID)
    if !ok || !contact.mutual() {
        return nil, errors.New("Can't find contact for canceling verification request")
    }

    if verifRequest.RequestStatus != verification.RequestStatusPENDING {
        return nil, errors.New("can cancel only pending verification request")
    }

    verifRequest.RequestStatus = verification.RequestStatusCANCELED
    err = m.verificationDatabase.SaveVerificationRequest(verifRequest)
    if err != nil {
        return nil, err
    }
    contact.VerificationStatus = VerificationStatusUNVERIFIED
    contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime()

    err = m.persistence.SaveContact(contact, nil)
    if err != nil {
        return nil, err
    }

    // We sync the contact with the other devices
    err = m.syncContact(context.Background(), contact, m.dispatchMessage)
    if err != nil {
        return nil, err
    }

    m.allContacts.Store(contact.ID, contact)

    // NOTE: does we need it?
    chat, ok := m.allChats.Load(verifRequest.To)
    if !ok {
        publicKey, err := contact.PublicKey()
        if err != nil {
            return nil, err
        }
        chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
        // We don't want to show the chat to the user
        chat.Active = false
    }

    m.allChats.Store(chat.ID, chat)
    clock, _ := chat.NextClockAndTimestamp(m.getTimesource())

    response := &MessengerResponse{}

    response.AddVerificationRequest(verifRequest)

    response.AddContact(contact)

    err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
    if err != nil {
        return nil, err
    }

    request := &protobuf.CancelContactVerification{
        Id:    id,
        Clock: clock,
    }

    encodedMessage, err := proto.Marshal(request)
    if err != nil {
        return nil, err
    }

    _, err = m.dispatchMessage(ctx, common.RawMessage{
        LocalChatID: chat.ID,
        Payload:     encodedMessage,
        MessageType: protobuf.ApplicationMetadataMessage_CANCEL_CONTACT_VERIFICATION,
        ResendType:  common.ResendTypeDataSync,
    })

    if err != nil {
        return nil, err
    }

    notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(id))
    if err != nil {
        return nil, err
    }

    if notification != nil {
        notification.ContactVerificationStatus = verification.RequestStatusCANCELED
        message := notification.Message
        message.ContactVerificationState = common.ContactVerificationStateCanceled
        notification.Read = true
        notification.UpdatedAt = m.GetCurrentTimeInMillis()

        err = m.addActivityCenterNotification(response, notification, m.syncActivityCenterReadByIDs)
        if err != nil {
            m.logger.Error("failed to save notification", zap.Error(err))
            return nil, err
        }
    }

    return response, nil
}

func (m *Messenger) AcceptContactVerificationRequest(ctx context.Context, id string, response string) (*MessengerResponse, error) {
    verifRequest, err := m.verificationDatabase.GetVerificationRequest(id)
    if err != nil {
        return nil, err
    }

    if verifRequest == nil {
        m.logger.Error("could not find verification request with id", zap.String("id", id))
        return nil, verification.ErrVerificationRequestNotFound
    }

    contactID := verifRequest.From

    contact, ok := m.allContacts.Load(contactID)
    if !ok || !contact.mutual() {
        return nil, errors.New("must be a mutual contact")
    }

    chat, ok := m.allChats.Load(contactID)
    if !ok {
        publicKey, err := contact.PublicKey()
        if err != nil {
            return nil, err
        }
        chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
        // We don't want to show the chat to the user
        chat.Active = false
    }

    m.allChats.Store(chat.ID, chat)
    clock, _ := chat.NextClockAndTimestamp(m.getTimesource())

    err = m.verificationDatabase.AcceptContactVerificationRequest(id, response)
    if err != nil {
        return nil, err
    }

    verifRequest, err = m.verificationDatabase.GetVerificationRequest(id)
    if err != nil {
        return nil, err
    }

    err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
    if err != nil {
        return nil, err
    }

    request := &protobuf.AcceptContactVerification{
        Clock:    clock,
        Id:       verifRequest.ID,
        Response: response,
    }

    encodedMessage, err := proto.Marshal(request)
    if err != nil {
        return nil, err
    }

    rawMessage, err := m.dispatchMessage(ctx, common.RawMessage{
        LocalChatID: chat.ID,
        Payload:     encodedMessage,
        MessageType: protobuf.ApplicationMetadataMessage_ACCEPT_CONTACT_VERIFICATION,
        ResendType:  common.ResendTypeDataSync,
    })

    if err != nil {
        return nil, err
    }

    // Pull one from the db if there
    notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(id))
    if err != nil {
        return nil, err
    }
    resp := &MessengerResponse{}

    resp.AddVerificationRequest(verifRequest)

    replyMessage, err := m.createLocalContactVerificationMessage(response, chat, rawMessage.ID, common.ContactVerificationStateAccepted)
    if err != nil {
        return nil, err
    }

    err = m.persistence.SaveMessages([]*common.Message{replyMessage})
    if err != nil {
        return nil, err
    }

    resp.AddMessage(replyMessage)

    if notification != nil {
        // TODO: Should we update only the message or only the notification or both?

        notification.ContactVerificationStatus = verification.RequestStatusACCEPTED
        message := notification.Message
        message.ContactVerificationState = common.ContactVerificationStateAccepted
        notification.ReplyMessage = replyMessage
        notification.Read = true
        notification.Accepted = true
        notification.UpdatedAt = m.GetCurrentTimeInMillis()
        err = m.addActivityCenterNotification(resp, notification, m.syncActivityCenterAcceptedByIDs)
        if err != nil {
            m.logger.Error("failed to save notification", zap.Error(err))
            return nil, err
        }
        resp.AddMessage(message) // <=== wasn't typo?
    }

    return resp, nil
}

func (m *Messenger) VerifiedTrusted(ctx context.Context, request *requests.VerifiedTrusted) (*MessengerResponse, error) {
    err := request.Validate()
    if err != nil {
        return nil, err
    }
    // Pull one from the db if there
    notification, err := m.persistence.GetActivityCenterNotificationByID(request.ID)
    if err != nil {
        return nil, err
    }

    if notification == nil || notification.ReplyMessage == nil {
        return nil, errors.New("could not find notification")
    }

    contactID := notification.ReplyMessage.From

    contact, ok := m.allContacts.Load(contactID)
    if !ok || !contact.mutual() {
        return nil, errors.New("must be a mutual contact")
    }

    err = m.setTrustStatusForContact(context.Background(), contactID, verification.TrustStatusTRUSTED)

    if err != nil {
        return nil, err
    }

    contact.VerificationStatus = VerificationStatusVERIFIED
    contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime()
    err = m.persistence.SaveContact(contact, nil)
    if err != nil {
        return nil, err
    }

    chat, ok := m.allChats.Load(contactID)
    clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
    if !ok {
        publicKey, err := contact.PublicKey()
        if err != nil {
            return nil, err
        }
        chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
        // We don't want to show the chat to the user
        chat.Active = false
    }

    verifRequest, err := m.verificationDatabase.GetLatestVerificationRequestSentTo(contactID)
    if err != nil {
        return nil, err
    }

    if verifRequest == nil {
        return nil, errors.New("no contact verification found")
    }

    verifRequest.RequestStatus = verification.RequestStatusTRUSTED
    verifRequest.RepliedAt = clock
    err = m.verificationDatabase.SaveVerificationRequest(verifRequest)
    if err != nil {
        return nil, err
    }

    err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
    if err != nil {
        return nil, err
    }

    // We sync the contact with the other devices
    err = m.syncContact(context.Background(), contact, m.dispatchMessage)
    if err != nil {
        return nil, err
    }

    // Dispatch profile message to save a contact to the encrypted profile part
    err = m.DispatchProfileShowcase()
    if err != nil {
        return nil, err
    }

    response := &MessengerResponse{}

    notification.ContactVerificationStatus = verification.RequestStatusTRUSTED
    notification.Message.ContactVerificationState = common.ContactVerificationStateTrusted
    notification.Read = true
    notification.Accepted = true
    notification.UpdatedAt = m.GetCurrentTimeInMillis()

    err = m.addActivityCenterNotification(response, notification, m.syncActivityCenterAcceptedByIDs)
    if err != nil {
        m.logger.Error("failed to save notification", zap.Error(err))
        return nil, err
    }

    msg, err := m.persistence.MessageByID(notification.ReplyMessage.ID)
    if err != nil {
        return nil, err
    }
    msg.ContactVerificationState = common.ContactVerificationStateTrusted

    err = m.persistence.SaveMessages([]*common.Message{msg})
    if err != nil {
        return nil, err
    }
    response.AddMessage(msg)

    response.AddContact(contact)

    return response, nil
}

func (m *Messenger) VerifiedUntrustworthy(ctx context.Context, request *requests.VerifiedUntrustworthy) (*MessengerResponse, error) {
    if err := request.Validate(); err != nil {
        return nil, err
    }

    // Pull one from the db if there
    notification, err := m.persistence.GetActivityCenterNotificationByID(request.ID)
    if err != nil {
        return nil, err
    }

    if notification == nil || notification.ReplyMessage == nil {
        return nil, errors.New("could not find notification")
    }

    contactID := notification.ReplyMessage.From

    err = m.setTrustStatusForContact(context.Background(), contactID, verification.TrustStatusUNTRUSTWORTHY)
    if err != nil {
        return nil, err
    }

    contact, err := m.setContactVerificationStatus(contactID, VerificationStatusVERIFIED)
    if err != nil {
        return nil, err
    }

    chat, ok := m.allChats.Load(contactID)
    clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
    if !ok {
        publicKey, err := contact.PublicKey()
        if err != nil {
            return nil, err
        }
        chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
        // We don't want to show the chat to the user
        chat.Active = false
    }

    verifRequest, err := m.verificationDatabase.GetLatestVerificationRequestSentTo(contactID)
    if err != nil {
        return nil, err
    }

    if verifRequest == nil {
        return nil, errors.New("no contact verification found")
    }

    verifRequest.RequestStatus = verification.RequestStatusUNTRUSTWORTHY
    verifRequest.RepliedAt = clock
    err = m.verificationDatabase.SaveVerificationRequest(verifRequest)
    if err != nil {
        return nil, err
    }

    err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
    if err != nil {
        return nil, err
    }

    response := &MessengerResponse{}

    notification.ContactVerificationStatus = verification.RequestStatusUNTRUSTWORTHY
    notification.Message.ContactVerificationState = common.ContactVerificationStateUntrustworthy
    notification.Read = true
    notification.Accepted = true
    notification.UpdatedAt = m.GetCurrentTimeInMillis()

    err = m.addActivityCenterNotification(response, notification, m.syncActivityCenterAcceptedByIDs)
    if err != nil {
        m.logger.Error("failed to save notification", zap.Error(err))
        return nil, err
    }

    msg, err := m.persistence.MessageByID(notification.ReplyMessage.ID)
    if err != nil {
        return nil, err
    }
    msg.ContactVerificationState = common.ContactVerificationStateUntrustworthy

    err = m.persistence.SaveMessages([]*common.Message{msg})
    if err != nil {
        return nil, err
    }

    response.AddMessage(msg)

    return response, nil
}

func (m *Messenger) DeclineContactVerificationRequest(ctx context.Context, id string) (*MessengerResponse, error) {
    verifRequest, err := m.verificationDatabase.GetVerificationRequest(id)
    if err != nil {
        return nil, err
    }

    if verifRequest == nil {
        m.logger.Error("could not find verification request with id", zap.String("id", id))
        return nil, verification.ErrVerificationRequestNotFound
    }

    contact, ok := m.allContacts.Load(verifRequest.From)
    if !ok || !contact.mutual() {
        return nil, errors.New("must be a mutual contact")
    }
    contactID := verifRequest.From
    contact, err = m.setContactVerificationStatus(contactID, VerificationStatusVERIFIED)

    if err != nil {
        return nil, err
    }

    if verifRequest == nil {
        return nil, errors.New("no contact verification found")
    }

    chat, ok := m.allChats.Load(verifRequest.From)
    if !ok {
        publicKey, err := contact.PublicKey()
        if err != nil {
            return nil, err
        }
        chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
        // We don't want to show the chat to the user
        chat.Active = false
    }

    m.allChats.Store(chat.ID, chat)
    clock, _ := chat.NextClockAndTimestamp(m.getTimesource())

    verifRequest.RequestStatus = verification.RequestStatusDECLINED
    verifRequest.RepliedAt = clock
    err = m.verificationDatabase.SaveVerificationRequest(verifRequest)
    if err != nil {
        return nil, err
    }

    response := &MessengerResponse{}

    response.AddVerificationRequest(verifRequest)

    err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
    if err != nil {
        return nil, err
    }

    request := &protobuf.DeclineContactVerification{
        Id:    id,
        Clock: clock,
    }

    encodedMessage, err := proto.Marshal(request)
    if err != nil {
        return nil, err
    }

    _, err = m.dispatchMessage(ctx, common.RawMessage{
        LocalChatID: chat.ID,
        Payload:     encodedMessage,
        MessageType: protobuf.ApplicationMetadataMessage_DECLINE_CONTACT_VERIFICATION,
        ResendType:  common.ResendTypeDataSync,
    })

    if err != nil {
        return nil, err
    }

    err = m.verificationDatabase.DeclineContactVerificationRequest(id)
    if err != nil {
        return nil, err
    }

    notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(id))
    if err != nil {
        return nil, err
    }

    if notification != nil {
        notification.ContactVerificationStatus = verification.RequestStatusDECLINED
        notification.Read = true
        notification.Dismissed = true
        notification.UpdatedAt = m.GetCurrentTimeInMillis()

        message := notification.Message
        message.ContactVerificationState = common.ContactVerificationStateDeclined

        err = m.addActivityCenterNotification(response, notification, m.syncActivityCenterDismissedByIDs)
        if err != nil {
            m.logger.Error("failed to save notification", zap.Error(err))
            return nil, err
        }
        response.AddMessage(message)
    }

    return response, nil
}

func (m *Messenger) setContactVerificationStatus(contactID string, verificationStatus VerificationStatus) (*Contact, error) {
    contact, ok := m.allContacts.Load(contactID)
    if !ok || !contact.mutual() {
        return nil, errors.New("must be a mutual contact")
    }

    contact.VerificationStatus = verificationStatus
    contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime()

    err := m.persistence.SaveContact(contact, nil)
    if err != nil {
        return nil, err
    }

    err = m.syncContact(context.Background(), contact, m.dispatchMessage)
    if err != nil {
        return nil, err
    }

    m.allContacts.Store(contact.ID, contact)

    // Dispatch profile message to save a contact to the encrypted profile part
    err = m.DispatchProfileShowcase()
    if err != nil {
        return nil, err
    }

    return contact, nil
}

func (m *Messenger) setTrustStatusForContact(ctx context.Context, contactID string, trustStatus verification.TrustStatus) error {
    currentTime := m.getTimesource().GetCurrentTime()

    err := m.verificationDatabase.SetTrustStatus(contactID, trustStatus, currentTime)
    if err != nil {
        return err
    }

    return m.SyncTrustedUser(ctx, contactID, trustStatus, m.dispatchMessage)
}

func (m *Messenger) MarkAsTrusted(ctx context.Context, contactID string) error {
    return m.setTrustStatusForContact(ctx, contactID, verification.TrustStatusTRUSTED)
}

func (m *Messenger) MarkAsUntrustworthy(ctx context.Context, contactID string) error {
    return m.setTrustStatusForContact(ctx, contactID, verification.TrustStatusUNTRUSTWORTHY)
}

func (m *Messenger) RemoveTrustStatus(ctx context.Context, contactID string) error {
    return m.setTrustStatusForContact(ctx, contactID, verification.TrustStatusUNKNOWN)
}

func (m *Messenger) RemoveTrustVerificationStatus(ctx context.Context, contactID string) (*MessengerResponse, error) {
    err := m.setTrustStatusForContact(ctx, contactID, verification.TrustStatusUNKNOWN)
    if err != nil {
        return nil, err
    }

    contact, err := m.setContactVerificationStatus(contactID, VerificationStatusUNVERIFIED)
    if err != nil {
        return nil, err
    }

    response := &MessengerResponse{}
    response.AddContact(contact)

    return response, nil
}

func (m *Messenger) GetTrustStatus(contactID string) (verification.TrustStatus, error) {
    return m.verificationDatabase.GetTrustStatus(contactID)
}

func ValidateContactVerificationRequest(request *protobuf.RequestContactVerification) error {
    challengeLen := len(strings.TrimSpace(request.Challenge))
    if challengeLen < minContactVerificationMessageLen || challengeLen > maxContactVerificationMessageLen {
        return errors.New("invalid verification request challenge length")
    }

    return nil
}

func (m *Messenger) HandleRequestContactVerification(state *ReceivedMessageState, request *protobuf.RequestContactVerification, statusMessage *v1protocol.StatusMessage) error {
    if err := ValidateContactVerificationRequest(request); err != nil {
        m.logger.Debug("Invalid verification request", zap.Error(err))
        return err
    }

    id := state.CurrentMessageState.MessageID

    if common.IsPubKeyEqual(state.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
        return nil // Is ours, do nothing
    }

    myPubKey := hexutil.Encode(crypto.FromECDSAPub(&m.identity.PublicKey))
    contactID := hexutil.Encode(crypto.FromECDSAPub(state.CurrentMessageState.PublicKey))

    contact := state.CurrentMessageState.Contact
    if !contact.mutual() {
        m.logger.Debug("Received a verification request for a non added mutual contact", zap.String("contactID", contactID))
        return errors.New("must be a mutual contact")
    }

    persistedVR, err := m.verificationDatabase.GetVerificationRequest(id)
    if err != nil {
        m.logger.Debug("Error obtaining verification request", zap.Error(err))
        return err
    }

    if persistedVR != nil && persistedVR.RequestedAt > request.Clock {
        return nil // older message, ignore it
    }

    if persistedVR == nil {
        // This is a new verification request, and we have not received its acceptance/decline before
        persistedVR = &verification.Request{}
        persistedVR.ID = id
        persistedVR.From = contactID
        persistedVR.To = myPubKey
        persistedVR.RequestStatus = verification.RequestStatusPENDING
    }

    if persistedVR.From != contactID {
        return errors.New("mismatch contactID and ID")
    }

    persistedVR.Challenge = request.Challenge
    persistedVR.RequestedAt = request.Clock

    err = m.verificationDatabase.SaveVerificationRequest(persistedVR)
    if err != nil {
        m.logger.Debug("Error storing verification request", zap.Error(err))
        return err
    }
    m.logger.Info("SAVED", zap.String("id", persistedVR.ID))

    err = m.SyncVerificationRequest(context.Background(), persistedVR, m.dispatchMessage)
    if err != nil {
        return err
    }

    chat, ok := m.allChats.Load(contactID)
    if !ok {
        publicKey, err := contact.PublicKey()
        if err != nil {
            return err
        }
        chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
        // We don't want to show the chat to the user
        chat.Active = false
    }

    m.allChats.Store(chat.ID, chat)

    chatMessage, err := m.createContactVerificationMessage(request.Challenge, chat, state, common.ContactVerificationStatePending)
    if err != nil {
        return err
    }

    state.Response.AddMessage(chatMessage)

    state.AllVerificationRequests = append(state.AllVerificationRequests, persistedVR)

    return m.createOrUpdateIncomingContactVerificationNotification(contact, state, persistedVR, chatMessage, nil)
}

func ValidateAcceptContactVerification(request *protobuf.AcceptContactVerification) error {
    responseLen := len(strings.TrimSpace(request.Response))
    if responseLen < minContactVerificationMessageLen || responseLen > maxContactVerificationMessageLen {
        return errors.New("invalid verification request response length")
    }

    return nil
}

func (m *Messenger) HandleAcceptContactVerification(state *ReceivedMessageState, request *protobuf.AcceptContactVerification, statusMessage *v1protocol.StatusMessage) error {
    if err := ValidateAcceptContactVerification(request); err != nil {
        m.logger.Debug("Invalid AcceptContactVerification", zap.Error(err))
        return err
    }

    if common.IsPubKeyEqual(state.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
        return nil // Is ours, do nothing
    }

    myPubKey := hexutil.Encode(crypto.FromECDSAPub(&m.identity.PublicKey))
    contactID := hexutil.Encode(crypto.FromECDSAPub(state.CurrentMessageState.PublicKey))

    contact := state.CurrentMessageState.Contact
    if !contact.mutual() {
        m.logger.Debug("Received a verification response for a non mutual contact", zap.String("contactID", contactID))
        return errors.New("must be a mutual contact")
    }

    persistedVR, err := m.verificationDatabase.GetVerificationRequest(request.Id)
    if err != nil {
        m.logger.Debug("Error obtaining verification request", zap.Error(err))
        return err
    }

    if persistedVR == nil {
        // This is a response for which we have not received its request before
        persistedVR = &verification.Request{}
        persistedVR.ID = request.Id
        persistedVR.From = contactID
        persistedVR.To = myPubKey
    } else {
        if persistedVR.RepliedAt > request.Clock {
            return nil // older message, ignore it
        }

        if persistedVR.RequestStatus == verification.RequestStatusCANCELED {
            return nil // Do nothing, We have already cancelled the verification request
        }
    }

    persistedVR.RequestStatus = verification.RequestStatusACCEPTED
    persistedVR.Response = request.Response
    persistedVR.RepliedAt = request.Clock

    err = m.verificationDatabase.SaveVerificationRequest(persistedVR)
    if err != nil {
        m.logger.Debug("Error storing verification request", zap.Error(err))
        return err
    }

    err = m.SyncVerificationRequest(context.Background(), persistedVR, m.dispatchMessage)
    if err != nil {
        return err
    }

    chat, ok := m.allChats.Load(contactID)
    if !ok {
        publicKey, err := contact.PublicKey()
        if err != nil {
            return err
        }
        chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
        // We don't want to show the chat to the user
        chat.Active = false
    }

    m.allChats.Store(chat.ID, chat)

    chatMessage, err := m.createContactVerificationMessage(request.Response, chat, state, common.ContactVerificationStateAccepted)
    if err != nil {
        return err
    }

    state.Response.AddMessage(chatMessage)

    msg, err := m.persistence.MessageByID(request.Id)
    if err != nil {
        return err
    }
    msg.ContactVerificationState = common.ContactVerificationStateAccepted

    state.Response.AddMessage(msg)

    err = m.createOrUpdateOutgoingContactVerificationNotification(contact, state.Response, persistedVR, msg, chatMessage)
    if err != nil {
        return err
    }

    state.AllVerificationRequests = append(state.AllVerificationRequests, persistedVR)

    return nil
}

func (m *Messenger) HandleDeclineContactVerification(state *ReceivedMessageState, request *protobuf.DeclineContactVerification, statusMessage *v1protocol.StatusMessage) error {
    if common.IsPubKeyEqual(state.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
        return nil // Is ours, do nothing
    }

    myPubKey := hexutil.Encode(crypto.FromECDSAPub(&m.identity.PublicKey))
    contactID := hexutil.Encode(crypto.FromECDSAPub(state.CurrentMessageState.PublicKey))

    contact := state.CurrentMessageState.Contact
    if !contact.mutual() {
        m.logger.Debug("Received a verification decline for a non mutual contact", zap.String("contactID", contactID))
        return errors.New("must be a mutual contact")
    }

    persistedVR, err := m.verificationDatabase.GetVerificationRequest(request.Id)
    if err != nil {
        m.logger.Debug("Error obtaining verification request", zap.Error(err))
        return err
    }

    if persistedVR != nil && persistedVR.RepliedAt > request.Clock {
        return nil // older message, ignore it
    }

    if persistedVR.RequestStatus == verification.RequestStatusCANCELED {
        return nil // Do nothing, We have already cancelled the verification request
    }

    contact.VerificationStatus = VerificationStatusUNVERIFIED
    contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime()

    err = m.persistence.SaveContact(contact, nil)
    if err != nil {
        return err
    }

    // We sync the contact with the other devices
    err = m.syncContact(context.Background(), contact, m.dispatchMessage)
    if err != nil {
        return err
    }

    m.allContacts.Store(contact.ID, contact)

    state.Response.AddContact(contact)

    if persistedVR == nil {
        // This is a response for which we have not received its request before
        persistedVR = &verification.Request{}
        persistedVR.From = contactID
        persistedVR.To = myPubKey
    }

    persistedVR.RequestStatus = verification.RequestStatusDECLINED
    persistedVR.RepliedAt = request.Clock

    err = m.verificationDatabase.SaveVerificationRequest(persistedVR)
    if err != nil {
        m.logger.Debug("Error storing verification request", zap.Error(err))
        return err
    }

    err = m.SyncVerificationRequest(context.Background(), persistedVR, m.dispatchMessage)
    if err != nil {
        return err
    }

    state.AllVerificationRequests = append(state.AllVerificationRequests, persistedVR)

    msg, err := m.persistence.MessageByID(request.Id)
    if err != nil {
        return err
    }

    if msg != nil {
        msg.ContactVerificationState = common.ContactVerificationStateDeclined
        state.Response.AddMessage(msg)
    }

    return m.createOrUpdateOutgoingContactVerificationNotification(contact, state.Response, persistedVR, msg, nil)
}

func (m *Messenger) HandleCancelContactVerification(state *ReceivedMessageState, request *protobuf.CancelContactVerification, statusMessage *v1protocol.StatusMessage) error {
    myPubKey := hexutil.Encode(crypto.FromECDSAPub(&m.identity.PublicKey))
    contactID := hexutil.Encode(crypto.FromECDSAPub(state.CurrentMessageState.PublicKey))

    contact := state.CurrentMessageState.Contact

    persistedVR, err := m.verificationDatabase.GetVerificationRequest(request.Id)
    if err != nil {
        m.logger.Debug("Error obtaining verification request", zap.Error(err))
        return err
    }

    if persistedVR != nil && persistedVR.RequestStatus != verification.RequestStatusPENDING {
        m.logger.Debug("Only pending verification request can be canceled", zap.String("contactID", contactID))
        return errors.New("must be a pending verification request")
    }

    if persistedVR == nil {
        // This is a response for which we have not received its request before
        persistedVR = &verification.Request{}
        persistedVR.From = contactID
        persistedVR.To = myPubKey
    }

    persistedVR.RequestStatus = verification.RequestStatusCANCELED
    persistedVR.RepliedAt = request.Clock

    err = m.verificationDatabase.SaveVerificationRequest(persistedVR)
    if err != nil {
        m.logger.Debug("Error storing verification request", zap.Error(err))
        return err
    }

    err = m.SyncVerificationRequest(context.Background(), persistedVR, m.dispatchMessage)
    if err != nil {
        return err
    }

    state.AllVerificationRequests = append(state.AllVerificationRequests, persistedVR)

    msg, err := m.persistence.MessageByID(request.Id)
    if err != nil {
        return err
    }

    if msg != nil {
        msg.ContactVerificationState = common.ContactVerificationStateCanceled
        state.Response.AddMessage(msg)
    }

    return m.createOrUpdateIncomingContactVerificationNotification(contact, state, persistedVR, msg, nil)
}

func (m *Messenger) GetLatestVerificationRequestFrom(contactID string) (*verification.Request, error) {
    return m.verificationDatabase.GetLatestVerificationRequestFrom(contactID)
}

func (m *Messenger) createOrUpdateOutgoingContactVerificationNotification(contact *Contact, response *MessengerResponse, vr *verification.Request, chatMessage *common.Message, replyMessage *common.Message) error {
    notification := &ActivityCenterNotification{
        ID:                        types.FromHex(vr.ID),
        Name:                      contact.PrimaryName(),
        Type:                      ActivityCenterNotificationTypeContactVerification,
        Author:                    chatMessage.From,
        Message:                   chatMessage,
        ReplyMessage:              replyMessage,
        Timestamp:                 chatMessage.WhisperTimestamp,
        ChatID:                    contact.ID,
        ContactVerificationStatus: vr.RequestStatus,
        Read:                      vr.RequestStatus != verification.RequestStatusACCEPTED, // Mark as Unread Accepted notification because we are waiting for the asnwer
        Accepted:                  vr.RequestStatus == verification.RequestStatusTRUSTED || vr.RequestStatus == verification.RequestStatusUNTRUSTWORTHY,
        Dismissed:                 vr.RequestStatus == verification.RequestStatusDECLINED,
        UpdatedAt:                 m.GetCurrentTimeInMillis(),
    }

    return m.addActivityCenterNotification(response, notification, nil)
}

func (m *Messenger) createOrUpdateIncomingContactVerificationNotification(contact *Contact, messageState *ReceivedMessageState, vr *verification.Request, chatMessage *common.Message, replyMessage *common.Message) error {
    notification := &ActivityCenterNotification{
        ID:                        types.FromHex(vr.ID),
        Name:                      contact.PrimaryName(),
        Type:                      ActivityCenterNotificationTypeContactVerification,
        Author:                    messageState.CurrentMessageState.Contact.ID,
        Message:                   chatMessage,
        ReplyMessage:              replyMessage,
        Timestamp:                 messageState.CurrentMessageState.WhisperTimestamp,
        ChatID:                    contact.ID,
        ContactVerificationStatus: vr.RequestStatus,
        Read:                      vr.RequestStatus != verification.RequestStatusPENDING, // Unread only for pending incomming
        Accepted:                  vr.RequestStatus == verification.RequestStatusACCEPTED || vr.RequestStatus == verification.RequestStatusTRUSTED || vr.RequestStatus == verification.RequestStatusUNTRUSTWORTHY,
        Dismissed:                 vr.RequestStatus == verification.RequestStatusDECLINED,
        UpdatedAt:                 m.GetCurrentTimeInMillis(),
    }

    return m.addActivityCenterNotification(messageState.Response, notification, nil)
}

func (m *Messenger) createContactVerificationMessage(challenge string, chat *Chat, state *ReceivedMessageState, verificationStatus common.ContactVerificationState) (*common.Message, error) {
    chatMessage := common.NewMessage()
    chatMessage.ID = state.CurrentMessageState.MessageID
    chatMessage.From = state.CurrentMessageState.Contact.ID
    chatMessage.Alias = state.CurrentMessageState.Contact.Alias
    chatMessage.SigPubKey = state.CurrentMessageState.PublicKey
    chatMessage.Identicon = state.CurrentMessageState.Contact.Identicon
    chatMessage.WhisperTimestamp = state.CurrentMessageState.WhisperTimestamp

    chatMessage.ChatId = chat.ID
    chatMessage.Text = challenge
    chatMessage.ContentType = protobuf.ChatMessage_IDENTITY_VERIFICATION
    chatMessage.ContactVerificationState = verificationStatus

    err := chatMessage.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
    if err != nil {
        return nil, err
    }
    return chatMessage, nil
}

func (m *Messenger) createLocalContactVerificationMessage(challenge string, chat *Chat, id string, status common.ContactVerificationState) (*common.Message, error) {

    chatMessage := common.NewMessage()
    chatMessage.ID = id
    err := extendMessageFromChat(chatMessage, chat, &m.identity.PublicKey, m.getTimesource())
    if err != nil {
        return nil, err
    }

    chatMessage.ChatId = chat.ID
    chatMessage.Text = challenge
    chatMessage.ContentType = protobuf.ChatMessage_IDENTITY_VERIFICATION
    chatMessage.ContactVerificationState = status
    err = extendMessageFromChat(chatMessage, chat, &m.identity.PublicKey, m.getTimesource())
    if err != nil {
        return nil, err
    }

    err = chatMessage.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
    if err != nil {
        return nil, err
    }
    return chatMessage, nil
}