Skip to content

Commit

Permalink
UUID->ACI & invite command
Browse files Browse the repository at this point in the history
  • Loading branch information
maltee1 committed Apr 2, 2024
1 parent efc22ef commit f24a761
Show file tree
Hide file tree
Showing 5 changed files with 369 additions and 194 deletions.
83 changes: 80 additions & 3 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func (br *SignalBridge) RegisterCommands() {
cmdInviteLink,
cmdResetInviteLink,
cmdCreate,
cmdInvite,
)
}

Expand Down Expand Up @@ -284,6 +285,82 @@ func fnPM(ce *WrappedCommandEvent) {
}
}

var cmdInvite = &commands.FullHandler{
Func: wrapCommand(fnInvite),
Name: "invite",
Help: commands.HelpMeta{
Section: HelpSectionPortalManagement,
Description: "Invite a user by phone number.",
Args: "<_international phone number_>",
},
RequiresLogin: true,
RequiresPortal: true,
}

func fnInvite(ce *WrappedCommandEvent) {
if len(ce.Args) == 0 {
ce.Reply("**Usage:** `invite <international phone number>`")
return
}
number, err := strconv.ParseUint(numberCleaner.Replace(strings.Join(ce.Args, "")), 10, 64)
if err != nil {
ce.Reply("Failed to parse number")
return
}

user := ce.User
var aci, pni uuid.UUID
e164 := fmt.Sprintf("+%d", number)
var recipient *types.Recipient

if recipient, err = user.Client.ContactByE164(ce.Ctx, e164); err != nil {
ce.Reply("Error looking up number in local contact list: %v", err)
return
} else if recipient != nil && (recipient.ACI != uuid.Nil || recipient.PNI != uuid.Nil) {
// TODO maybe lookup PNI if there's only ACI and E164 stored?
aci = recipient.ACI
pni = recipient.PNI
} else if resp, err := user.Client.LookupPhone(ce.Ctx, number); err != nil {
ce.ZLog.Err(err).Uint64("e164", number).Msg("Failed to lookup number on server")
ce.Reply("Error looking up number on server: %v", err)
return
} else {
aci = resp[number].ACI
pni = resp[number].PNI
if aci == uuid.Nil && pni == uuid.Nil {
ce.Reply("+%d doesn't seem to be on Signal", number)
return
}
recipient, err = user.Client.Store.RecipientStore.UpdateRecipientE164(ce.Ctx, aci, pni, e164)
if err != nil {
ce.ZLog.Err(err).Msg("Failed to save recipient entry after looking up phone")
}
aci, pni = recipient.ACI, recipient.PNI
}
ce.ZLog.Debug().
Uint64("e164", number).
Stringer("aci", aci).
Stringer("pni", pni).
Msg("Found Invite target user")

var groupChange signalmeow.GroupChange
if aci != uuid.Nil {
groupChange.AddMembers = append(groupChange.AddMembers, &signalmeow.AddMember{
GroupMember: signalmeow.GroupMember{
ACI: aci,
Role: signalmeow.GroupMember_DEFAULT,
},
})
} else {
groupChange.AddPendingMembers = append(groupChange.AddPendingMembers, &signalmeow.PendingMember{
ServiceID: libsignalgo.NewPNIServiceID(pni),
AddedByUserID: ce.User.SignalID,
Role: signalmeow.GroupMember_DEFAULT,
})
}
ce.User.Client.UpdateGroup(ce.Ctx, &groupChange, ce.Portal.GroupID())
}

var cmdResolvePhone = &commands.FullHandler{
Func: wrapCommand(fnResolvePhone),
Name: "resolve-phone",
Expand Down Expand Up @@ -822,12 +899,12 @@ func fnCreate(ce *WrappedCommandEvent) {
// joined members that need to be pending-Members should have their signal invite auto-accepted
if membership == event.MembershipJoin || membership == event.MembershipInvite {
participants = append(participants, &signalmeow.GroupMember{
UserID: uuid,
Role: role,
ACI: uuid,
Role: role,
})
} else if membership == event.MembershipBan {
bannedMembers = append(bannedMembers, &signalmeow.BannedMember{
UserID: uuid,
ServiceID: libsignalgo.NewACIServiceID(uuid),
})
}
}
Expand Down
26 changes: 10 additions & 16 deletions pkg/libsignalgo/groupsecretparams.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ package libsignalgo
import "C"
import (
"crypto/rand"
"fmt"
"runtime"
"unsafe"

Expand Down Expand Up @@ -139,41 +138,36 @@ func (gsp *GroupSecretParams) EncryptBlobWithPaddingDeterministic(randomness Ran
return CopySignalOwnedBufferToBytes(ciphertext), nil
}

func (gsp *GroupSecretParams) DecryptUUID(ciphertextUUID UUIDCiphertext) (uuid.UUID, error) {
// TODO this should probably be DecryptServiceID

func (gsp *GroupSecretParams) DecryptServiceID(ciphertextServiceID UUIDCiphertext) (ServiceID, error) {
u := C.SignalServiceIdFixedWidthBinaryBytes{}
signalFfiError := C.signal_group_secret_params_decrypt_service_id(
&u,
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
(*[C.SignalUUID_CIPHERTEXT_LEN]C.uint8_t)(unsafe.Pointer(&ciphertextUUID)),
(*[C.SignalUUID_CIPHERTEXT_LEN]C.uint8_t)(unsafe.Pointer(&ciphertextServiceID)),
)
runtime.KeepAlive(gsp)
runtime.KeepAlive(ciphertextUUID)
runtime.KeepAlive(ciphertextServiceID)
if signalFfiError != nil {
return uuid.Nil, wrapError(signalFfiError)
return EmptyServiceID, wrapError(signalFfiError)
}

serviceID := ServiceIDFromCFixedBytes(&u)
if serviceID.Type != ServiceIDTypeACI {
return uuid.Nil, fmt.Errorf("unexpected service ID type %d", serviceID.Type)
}
return serviceID.UUID, nil
return serviceID, nil
}

func (gsp *GroupSecretParams) EncryptUUID(uuid uuid.UUID) (*UUIDCiphertext, error) {
var cipherTextUUID [C.SignalUUID_CIPHERTEXT_LEN]C.uchar
func (gsp *GroupSecretParams) EncryptServiceID(serviceID ServiceID) (*UUIDCiphertext, error) {
var cipherTextServiceID [C.SignalUUID_CIPHERTEXT_LEN]C.uchar
signalFfiError := C.signal_group_secret_params_encrypt_service_id(
&cipherTextUUID,
&cipherTextServiceID,
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
NewACIServiceID(uuid).CFixedBytes(),
serviceID.CFixedBytes(),
)
runtime.KeepAlive(gsp)
if signalFfiError != nil {
return nil, wrapError(signalFfiError)
}
var result UUIDCiphertext
copy(result[:], C.GoBytes(unsafe.Pointer(&cipherTextUUID), C.int(C.SignalUUID_CIPHERTEXT_LEN)))
copy(result[:], C.GoBytes(unsafe.Pointer(&cipherTextServiceID), C.int(C.SignalUUID_CIPHERTEXT_LEN)))
return &result, nil
}

Expand Down
Loading

0 comments on commit f24a761

Please sign in to comment.