4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-06-27 13:29:25 +00:00

Add dependencies/vendor (whatsapp)

This commit is contained in:
Wim
2022-01-31 00:27:37 +01:00
parent e7b193788a
commit e3cafeaf92
1074 changed files with 3091569 additions and 26075 deletions

View File

@ -0,0 +1,19 @@
package protocol
type CiphertextMessage interface {
Serialize() []byte
Type() uint32
}
type GroupCiphertextMessage interface {
CiphertextMessage
SignedSerialize() []byte
}
const UnsupportedVersion = 1
const CurrentVersion = 3
const WHISPER_TYPE = 2
const PREKEY_TYPE = 3
const SENDERKEY_TYPE = 4
const SENDERKEY_DISTRIBUTION_TYPE = 5

View File

@ -0,0 +1,3 @@
// Package protocol provides address, group, and message structures that
// the Signal protocol uses for sending encrypted messages.
package protocol

View File

@ -0,0 +1,152 @@
package protocol
import (
"fmt"
"go.mau.fi/libsignal/ecc"
"go.mau.fi/libsignal/keys/identity"
"go.mau.fi/libsignal/signalerror"
"go.mau.fi/libsignal/util/optional"
)
// PreKeySignalMessageSerializer is an interface for serializing and deserializing
// PreKeySignalMessages into bytes. An implementation of this interface should be
// used to encode/decode the object into JSON, Protobuffers, etc.
type PreKeySignalMessageSerializer interface {
Serialize(signalMessage *PreKeySignalMessageStructure) []byte
Deserialize(serialized []byte) (*PreKeySignalMessageStructure, error)
}
// NewPreKeySignalMessageFromBytes will return a Signal Ciphertext message from the given
// bytes using the given serializer.
func NewPreKeySignalMessageFromBytes(serialized []byte, serializer PreKeySignalMessageSerializer,
msgSerializer SignalMessageSerializer) (*PreKeySignalMessage, error) {
// Use the given serializer to decode the signal message.
signalMessageStructure, err := serializer.Deserialize(serialized)
if err != nil {
return nil, err
}
return NewPreKeySignalMessageFromStruct(signalMessageStructure, serializer, msgSerializer)
}
// NewPreKeySignalMessageFromStruct will return a new PreKeySignalMessage from the given
// PreKeySignalMessageStructure.
func NewPreKeySignalMessageFromStruct(structure *PreKeySignalMessageStructure,
serializer PreKeySignalMessageSerializer, msgSerializer SignalMessageSerializer) (*PreKeySignalMessage, error) {
// Throw an error if the given message structure is an unsupported version.
if structure.Version <= UnsupportedVersion {
return nil, fmt.Errorf("%w %d (prekey message)", signalerror.ErrOldMessageVersion, structure.Version)
}
// Throw an error if the given message structure is a future version.
if structure.Version > CurrentVersion {
return nil, fmt.Errorf("%w %d (prekey message)", signalerror.ErrUnknownMessageVersion, structure.Version)
}
// Throw an error if the structure is missing critical fields.
if structure.BaseKey == nil || structure.IdentityKey == nil || structure.Message == nil {
return nil, fmt.Errorf("%w (prekey message)", signalerror.ErrIncompleteMessage)
}
// Create the signal message object from the structure.
preKeyWhisperMessage := &PreKeySignalMessage{structure: *structure, serializer: serializer}
// Generate the base ECC key from bytes.
var err error
preKeyWhisperMessage.baseKey, err = ecc.DecodePoint(structure.BaseKey, 0)
if err != nil {
return nil, err
}
// Generate the identity key from bytes
var identityKey ecc.ECPublicKeyable
identityKey, err = ecc.DecodePoint(structure.IdentityKey, 0)
if err != nil {
return nil, err
}
preKeyWhisperMessage.identityKey = identity.NewKey(identityKey)
// Generate the SignalMessage object from bytes.
preKeyWhisperMessage.message, err = NewSignalMessageFromBytes(structure.Message, msgSerializer)
if err != nil {
return nil, err
}
return preKeyWhisperMessage, nil
}
// NewPreKeySignalMessage will return a new PreKeySignalMessage object.
func NewPreKeySignalMessage(version int, registrationID uint32, preKeyID *optional.Uint32, signedPreKeyID uint32,
baseKey ecc.ECPublicKeyable, identityKey *identity.Key, message *SignalMessage, serializer PreKeySignalMessageSerializer,
msgSerializer SignalMessageSerializer) (*PreKeySignalMessage, error) {
structure := &PreKeySignalMessageStructure{
Version: version,
RegistrationID: registrationID,
PreKeyID: preKeyID,
SignedPreKeyID: signedPreKeyID,
BaseKey: baseKey.Serialize(),
IdentityKey: identityKey.PublicKey().Serialize(),
Message: message.Serialize(),
}
return NewPreKeySignalMessageFromStruct(structure, serializer, msgSerializer)
}
// PreKeySignalMessageStructure is a serializable structure for
// PreKeySignalMessages.
type PreKeySignalMessageStructure struct {
RegistrationID uint32
PreKeyID *optional.Uint32
SignedPreKeyID uint32
BaseKey []byte
IdentityKey []byte
Message []byte
Version int
}
// PreKeySignalMessage is an encrypted Signal message that is designed
// to be used when building a session with someone for the first time.
type PreKeySignalMessage struct {
structure PreKeySignalMessageStructure
baseKey ecc.ECPublicKeyable
identityKey *identity.Key
message *SignalMessage
serializer PreKeySignalMessageSerializer
}
func (p *PreKeySignalMessage) MessageVersion() int {
return p.structure.Version
}
func (p *PreKeySignalMessage) IdentityKey() *identity.Key {
return p.identityKey
}
func (p *PreKeySignalMessage) RegistrationID() uint32 {
return p.structure.RegistrationID
}
func (p *PreKeySignalMessage) PreKeyID() *optional.Uint32 {
return p.structure.PreKeyID
}
func (p *PreKeySignalMessage) SignedPreKeyID() uint32 {
return p.structure.SignedPreKeyID
}
func (p *PreKeySignalMessage) BaseKey() ecc.ECPublicKeyable {
return p.baseKey
}
func (p *PreKeySignalMessage) WhisperMessage() *SignalMessage {
return p.message
}
func (p *PreKeySignalMessage) Serialize() []byte {
return p.serializer.Serialize(&p.structure)
}
func (p *PreKeySignalMessage) Type() uint32 {
return PREKEY_TYPE
}

View File

@ -0,0 +1,147 @@
package protocol
import (
"fmt"
"go.mau.fi/libsignal/ecc"
"go.mau.fi/libsignal/signalerror"
)
// SenderKeyDistributionMessageSerializer is an interface for serializing and deserializing
// SenderKeyDistributionMessages into bytes. An implementation of this interface should be
// used to encode/decode the object into JSON, Protobuffers, etc.
type SenderKeyDistributionMessageSerializer interface {
Serialize(signalMessage *SenderKeyDistributionMessageStructure) []byte
Deserialize(serialized []byte) (*SenderKeyDistributionMessageStructure, error)
}
// NewSenderKeyDistributionMessageFromBytes will return a Signal Ciphertext message from the given
// bytes using the given serializer.
func NewSenderKeyDistributionMessageFromBytes(serialized []byte,
serializer SenderKeyDistributionMessageSerializer) (*SenderKeyDistributionMessage, error) {
// Use the given serializer to decode the signal message.
signalMessageStructure, err := serializer.Deserialize(serialized)
if err != nil {
return nil, err
}
return NewSenderKeyDistributionMessageFromStruct(signalMessageStructure, serializer)
}
// NewSenderKeyDistributionMessageFromStruct returns a Signal Ciphertext message from the
// given serializable structure.
func NewSenderKeyDistributionMessageFromStruct(structure *SenderKeyDistributionMessageStructure,
serializer SenderKeyDistributionMessageSerializer) (*SenderKeyDistributionMessage, error) {
// Throw an error if the given message structure is an unsupported version.
if structure.Version <= UnsupportedVersion {
return nil, fmt.Errorf("%w %d (sender key distribution)", signalerror.ErrOldMessageVersion, structure.Version)
}
// Throw an error if the given message structure is a future version.
if structure.Version > CurrentVersion {
return nil, fmt.Errorf("%w %d (sender key distribution)", signalerror.ErrUnknownMessageVersion, structure.Version)
}
// Throw an error if the structure is missing critical fields.
if structure.SigningKey == nil || structure.ChainKey == nil {
return nil, fmt.Errorf("%w (sender key distribution)", signalerror.ErrIncompleteMessage)
}
// Get the signing key object from bytes.
signingKey, err := ecc.DecodePoint(structure.SigningKey, 0)
if err != nil {
return nil, err
}
// Create the signal message object from the structure.
message := &SenderKeyDistributionMessage{
id: structure.ID,
iteration: structure.Iteration,
chainKey: structure.ChainKey,
version: structure.Version,
signatureKey: signingKey,
serializer: serializer,
}
// Generate the ECC key from bytes.
message.signatureKey, err = ecc.DecodePoint(structure.SigningKey, 0)
if err != nil {
return nil, err
}
return message, nil
}
// NewSenderKeyDistributionMessage returns a Signal Ciphertext message.
func NewSenderKeyDistributionMessage(id uint32, iteration uint32,
chainKey []byte, signatureKey ecc.ECPublicKeyable,
serializer SenderKeyDistributionMessageSerializer) *SenderKeyDistributionMessage {
return &SenderKeyDistributionMessage{
id: id,
iteration: iteration,
chainKey: chainKey,
signatureKey: signatureKey,
serializer: serializer,
}
}
// SenderKeyDistributionMessageStructure is a serializeable structure for senderkey
// distribution messages.
type SenderKeyDistributionMessageStructure struct {
ID uint32
Iteration uint32
ChainKey []byte
SigningKey []byte
Version uint32
}
// SenderKeyDistributionMessage is a structure for senderkey distribution messages.
type SenderKeyDistributionMessage struct {
id uint32
iteration uint32
chainKey []byte
version uint32
signatureKey ecc.ECPublicKeyable
serializer SenderKeyDistributionMessageSerializer
}
// ID will return the message's id.
func (p *SenderKeyDistributionMessage) ID() uint32 {
return p.id
}
// Iteration will return the message's iteration.
func (p *SenderKeyDistributionMessage) Iteration() uint32 {
return p.iteration
}
// ChainKey will return the message's chain key in bytes.
func (p *SenderKeyDistributionMessage) ChainKey() []byte {
return p.chainKey
}
// SignatureKey will return the message's signature public key
func (p *SenderKeyDistributionMessage) SignatureKey() ecc.ECPublicKeyable {
return p.signatureKey
}
// Serialize will use the given serializer and return the message as
// bytes.
func (p *SenderKeyDistributionMessage) Serialize() []byte {
structure := &SenderKeyDistributionMessageStructure{
ID: p.id,
Iteration: p.iteration,
ChainKey: p.chainKey,
SigningKey: p.signatureKey.Serialize(),
Version: CurrentVersion,
}
return p.serializer.Serialize(structure)
}
// Type will return the message's type.
func (p *SenderKeyDistributionMessage) Type() uint32 {
return SENDERKEY_DISTRIBUTION_TYPE
}

View File

@ -0,0 +1,168 @@
package protocol
import (
"fmt"
"go.mau.fi/libsignal/ecc"
"go.mau.fi/libsignal/signalerror"
"go.mau.fi/libsignal/util/bytehelper"
)
// SenderKeyMessageSerializer is an interface for serializing and deserializing
// SenderKeyMessages into bytes. An implementation of this interface should be
// used to encode/decode the object into JSON, Protobuffers, etc.
type SenderKeyMessageSerializer interface {
Serialize(signalMessage *SenderKeyMessageStructure) []byte
Deserialize(serialized []byte) (*SenderKeyMessageStructure, error)
}
// NewSenderKeyMessageFromBytes will return a Signal Ciphertext message from the given
// bytes using the given serializer.
func NewSenderKeyMessageFromBytes(serialized []byte,
serializer SenderKeyMessageSerializer) (*SenderKeyMessage, error) {
// Use the given serializer to decode the signal message.
senderKeyMessageStructure, err := serializer.Deserialize(serialized)
if err != nil {
return nil, err
}
return NewSenderKeyMessageFromStruct(senderKeyMessageStructure, serializer)
}
// NewSenderKeyMessageFromStruct returns a Signal Ciphertext message from the
// given serializable structure.
func NewSenderKeyMessageFromStruct(structure *SenderKeyMessageStructure,
serializer SenderKeyMessageSerializer) (*SenderKeyMessage, error) {
// Throw an error if the given message structure is an unsupported version.
if structure.Version <= UnsupportedVersion {
return nil, fmt.Errorf("%w %d (sender key message)", signalerror.ErrOldMessageVersion, structure.Version)
}
// Throw an error if the given message structure is a future version.
if structure.Version > CurrentVersion {
return nil, fmt.Errorf("%w %d (sender key message)", signalerror.ErrUnknownMessageVersion, structure.Version)
}
// Throw an error if the structure is missing critical fields.
if structure.CipherText == nil {
return nil, fmt.Errorf("%w (sender key message)", signalerror.ErrIncompleteMessage)
}
// Create the signal message object from the structure.
whisperMessage := &SenderKeyMessage{
keyID: structure.ID,
version: structure.Version,
iteration: structure.Iteration,
ciphertext: structure.CipherText,
signature: structure.Signature,
serializer: serializer,
}
return whisperMessage, nil
}
// NewSenderKeyMessage returns a SenderKeyMessage.
func NewSenderKeyMessage(keyID uint32, iteration uint32, ciphertext []byte,
signatureKey ecc.ECPrivateKeyable, serializer SenderKeyMessageSerializer) *SenderKeyMessage {
// Ensure we have a valid signature key
if signatureKey == nil {
panic("Signature is nil. Unable to sign new senderkey message.")
}
// Build our SenderKeyMessage.
senderKeyMessage := &SenderKeyMessage{
keyID: keyID,
iteration: iteration,
ciphertext: ciphertext,
version: CurrentVersion,
serializer: serializer,
}
// Sign the serialized message and include it in the message. This will be included
// in the signed serialized version of the message.
signature := ecc.CalculateSignature(signatureKey, senderKeyMessage.Serialize())
senderKeyMessage.signature = bytehelper.ArrayToSlice64(signature)
return senderKeyMessage
}
// SenderKeyMessageStructure is a serializeable structure for SenderKey messages.
type SenderKeyMessageStructure struct {
ID uint32
Iteration uint32
CipherText []byte
Version uint32
Signature []byte
}
// SenderKeyMessage is a structure for messages using senderkey groups.
type SenderKeyMessage struct {
version uint32
keyID uint32
iteration uint32
ciphertext []byte
signature []byte
serializer SenderKeyMessageSerializer
}
// KeyID returns the SenderKeyMessage key ID.
func (p *SenderKeyMessage) KeyID() uint32 {
return p.keyID
}
// Iteration returns the SenderKeyMessage iteration.
func (p *SenderKeyMessage) Iteration() uint32 {
return p.iteration
}
// Ciphertext returns the SenderKeyMessage encrypted ciphertext.
func (p *SenderKeyMessage) Ciphertext() []byte {
return p.ciphertext
}
// Version returns the Signal message version of the message.
func (p *SenderKeyMessage) Version() uint32 {
return p.version
}
// Serialize will use the given serializer to return the message as bytes
// excluding the signature. This should be used for signing and verifying
// message signatures.
func (p *SenderKeyMessage) Serialize() []byte {
structure := &SenderKeyMessageStructure{
ID: p.keyID,
Iteration: p.iteration,
CipherText: p.ciphertext,
Version: p.version,
}
return p.serializer.Serialize(structure)
}
// SignedSerialize will use the given serializer to return the message as
// bytes with the message signature included. This should be used when
// sending the message over the network.
func (p *SenderKeyMessage) SignedSerialize() []byte {
structure := &SenderKeyMessageStructure{
ID: p.keyID,
Iteration: p.iteration,
CipherText: p.ciphertext,
Version: p.version,
Signature: p.signature,
}
return p.serializer.Serialize(structure)
}
// Signature returns the SenderKeyMessage signature
func (p *SenderKeyMessage) Signature() [64]byte {
return bytehelper.SliceToArray64(p.signature)
}
// Type returns the sender key type.
func (p *SenderKeyMessage) Type() uint32 {
return SENDERKEY_TYPE
}

View File

@ -0,0 +1,25 @@
package protocol
// NewSenderKeyName returns a new SenderKeyName object.
func NewSenderKeyName(groupID string, sender *SignalAddress) *SenderKeyName {
return &SenderKeyName{
groupID: groupID,
sender: sender,
}
}
// SenderKeyName is a structure for a group session address.
type SenderKeyName struct {
groupID string
sender *SignalAddress
}
// GroupID returns the sender key group id
func (n *SenderKeyName) GroupID() string {
return n.groupID
}
// Sender returns the Signal address of sending user in the group.
func (n *SenderKeyName) Sender() *SignalAddress {
return n.sender
}

View File

@ -0,0 +1,226 @@
package protocol
import (
"crypto/hmac"
"crypto/sha256"
"fmt"
"strconv"
"go.mau.fi/libsignal/ecc"
"go.mau.fi/libsignal/keys/identity"
"go.mau.fi/libsignal/logger"
"go.mau.fi/libsignal/signalerror"
"go.mau.fi/libsignal/util/bytehelper"
)
const MacLength int = 8
// SignalMessageSerializer is an interface for serializing and deserializing
// SignalMessages into bytes. An implementation of this interface should be
// used to encode/decode the object into JSON, Protobuffers, etc.
type SignalMessageSerializer interface {
Serialize(signalMessage *SignalMessageStructure) []byte
Deserialize(serialized []byte) (*SignalMessageStructure, error)
}
// NewSignalMessageFromBytes will return a Signal Ciphertext message from the given
// bytes using the given serializer.
func NewSignalMessageFromBytes(serialized []byte, serializer SignalMessageSerializer) (*SignalMessage, error) {
// Use the given serializer to decode the signal message.
signalMessageStructure, err := serializer.Deserialize(serialized)
if err != nil {
return nil, err
}
return NewSignalMessageFromStruct(signalMessageStructure, serializer)
}
// NewSignalMessageFromStruct returns a Signal Ciphertext message from the
// given serializable structure.
func NewSignalMessageFromStruct(structure *SignalMessageStructure, serializer SignalMessageSerializer) (*SignalMessage, error) {
// Throw an error if the given message structure is an unsupported version.
if structure.Version <= UnsupportedVersion {
return nil, fmt.Errorf("%w %d (normal message)", signalerror.ErrOldMessageVersion, structure.Version)
}
// Throw an error if the given message structure is a future version.
if structure.Version > CurrentVersion {
return nil, fmt.Errorf("%w %d (normal message)", signalerror.ErrUnknownMessageVersion, structure.Version)
}
// Throw an error if the structure is missing critical fields.
if structure.CipherText == nil || structure.RatchetKey == nil {
return nil, fmt.Errorf("%w (normal message)", signalerror.ErrIncompleteMessage)
}
// Create the signal message object from the structure.
whisperMessage := &SignalMessage{structure: *structure, serializer: serializer}
// Generate the ECC key from bytes.
var err error
whisperMessage.senderRatchetKey, err = ecc.DecodePoint(structure.RatchetKey, 0)
if err != nil {
return nil, err
}
return whisperMessage, nil
}
// NewSignalMessage returns a Signal Ciphertext message.
func NewSignalMessage(messageVersion int, counter, previousCounter uint32, macKey []byte,
senderRatchetKey ecc.ECPublicKeyable, ciphertext []byte, senderIdentityKey,
receiverIdentityKey *identity.Key, serializer SignalMessageSerializer) (*SignalMessage, error) {
version := []byte(strconv.Itoa(messageVersion))
// Build the signal message structure with the given data.
structure := &SignalMessageStructure{
Counter: counter,
PreviousCounter: previousCounter,
RatchetKey: senderRatchetKey.Serialize(),
CipherText: ciphertext,
}
serialized := append(version, serializer.Serialize(structure)...)
// Get the message authentication code from the serialized structure.
mac, err := getMac(
messageVersion, senderIdentityKey, receiverIdentityKey,
macKey, serialized,
)
if err != nil {
return nil, err
}
structure.Mac = mac
structure.Version = messageVersion
// Generate a SignalMessage with the structure.
whisperMessage, err := NewSignalMessageFromStruct(structure, serializer)
if err != nil {
return nil, err
}
return whisperMessage, nil
}
// SignalMessageStructure is a serializeable structure of a signal message
// object.
type SignalMessageStructure struct {
RatchetKey []byte
Counter uint32
PreviousCounter uint32
CipherText []byte
Version int
Mac []byte
}
// SignalMessage is a cipher message that contains a message encrypted
// with the Signal protocol.
type SignalMessage struct {
structure SignalMessageStructure
senderRatchetKey ecc.ECPublicKeyable
serializer SignalMessageSerializer
}
// SenderRatchetKey returns the SignalMessage's sender ratchet key. This
// key is used for ratcheting the chain forward to negotiate a new shared
// secret that cannot be derived from previous chains.
func (s *SignalMessage) SenderRatchetKey() ecc.ECPublicKeyable {
return s.senderRatchetKey
}
// MessageVersion returns the message version this SignalMessage supports.
func (s *SignalMessage) MessageVersion() int {
return s.structure.Version
}
// Counter will return the SignalMessage counter.
func (s *SignalMessage) Counter() uint32 {
return s.structure.Counter
}
// Body will return the SignalMessage's ciphertext in bytes.
func (s *SignalMessage) Body() []byte {
return s.structure.CipherText
}
// VerifyMac will return an error if the message's message authentication code
// is invalid. This should be used on SignalMessages that have been constructed
// from a sent message.
func (s *SignalMessage) VerifyMac(messageVersion int, senderIdentityKey,
receiverIdentityKey *identity.Key, macKey []byte) error {
// Create a copy of the message without the mac. We'll use this to calculate
// the message authentication code.
structure := s.structure
signalMessage, err := NewSignalMessageFromStruct(&structure, s.serializer)
if err != nil {
return err
}
signalMessage.structure.Mac = nil
signalMessage.structure.Version = 0
version := []byte(strconv.Itoa(s.MessageVersion()))
serialized := append(version, signalMessage.Serialize()...)
// Calculate the message authentication code from the serialized structure.
ourMac, err := getMac(
messageVersion,
senderIdentityKey,
receiverIdentityKey,
macKey,
serialized,
)
if err != nil {
logger.Error(err)
return err
}
// Get the message authentication code that was sent to us as part of
// the signal message structure.
theirMac := s.structure.Mac
logger.Debug("Verifying macs...")
logger.Debug(" Our MAC: ", ourMac)
logger.Debug(" Their MAC: ", theirMac)
// Return an error if our calculated mac doesn't match the mac sent to us.
if !hmac.Equal(ourMac, theirMac) {
return signalerror.ErrBadMAC
}
return nil
}
// Serialize will return the Signal Message as bytes.
func (s *SignalMessage) Serialize() []byte {
return s.serializer.Serialize(&s.structure)
}
// Structure will return a serializeable structure of the Signal Message.
func (s *SignalMessage) Structure() *SignalMessageStructure {
structure := s.structure
return &structure
}
// Type will return the type of Signal Message this is.
func (s *SignalMessage) Type() uint32 {
return WHISPER_TYPE
}
// getMac will calculate the mac using the given message version, identity
// keys, macKey and SignalMessageStructure. The MAC key is a private key held
// by both parties that is concatenated with the message and hashed.
func getMac(messageVersion int, senderIdentityKey, receiverIdentityKey *identity.Key,
macKey, serialized []byte) ([]byte, error) {
mac := hmac.New(sha256.New, macKey[:])
if messageVersion >= 3 {
mac.Write(senderIdentityKey.PublicKey().Serialize())
mac.Write(receiverIdentityKey.PublicKey().Serialize())
}
mac.Write(serialized)
fullMac := mac.Sum(nil)
return bytehelper.Trim(fullMac, MacLength), nil
}

View File

@ -0,0 +1,38 @@
package protocol
import (
"fmt"
)
const ADDRESS_SEPARATOR = ":"
// NewSignalAddress returns a new signal address.
func NewSignalAddress(name string, deviceID uint32) *SignalAddress {
addr := SignalAddress{
name: name,
deviceID: deviceID,
}
return &addr
}
// SignalAddress is a combination of a name and a device ID.
type SignalAddress struct {
name string
deviceID uint32
}
// Name returns the signal address's name.
func (s *SignalAddress) Name() string {
return s.name
}
// DeviceID returns the signal address's device ID.
func (s *SignalAddress) DeviceID() uint32 {
return s.deviceID
}
// String returns a string of both the address name and device id.
func (s *SignalAddress) String() string {
return fmt.Sprintf("%s%s%d", s.name, ADDRESS_SEPARATOR, s.deviceID)
}