2023-12-17 15:54:35 +02:00
|
|
|
// mautrix-signal - A Matrix-signal puppeting bridge.
|
|
|
|
|
// Copyright (C) 2023 Sumner Evans
|
2025-01-17 17:49:37 +02:00
|
|
|
// Copyright (C) 2025 Tulir Asokan
|
2023-12-17 15:54:35 +02:00
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
2023-06-02 10:59:05 -06:00
|
|
|
package libsignalgo
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
#include "./libsignal-ffi.h"
|
|
|
|
|
*/
|
|
|
|
|
import "C"
|
|
|
|
|
import (
|
|
|
|
|
"crypto/rand"
|
2025-07-18 15:58:17 +03:00
|
|
|
"encoding/base64"
|
2025-01-18 16:29:20 +02:00
|
|
|
"fmt"
|
2024-01-03 20:31:27 +02:00
|
|
|
"runtime"
|
2023-06-02 10:59:05 -06:00
|
|
|
"unsafe"
|
2023-12-22 14:47:07 +02:00
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
2023-06-02 10:59:05 -06:00
|
|
|
)
|
|
|
|
|
|
2023-07-16 17:33:28 -04:00
|
|
|
type Randomness [C.SignalRANDOMNESS_LEN]byte
|
2023-06-02 10:59:05 -06:00
|
|
|
|
2024-01-06 14:53:48 -07:00
|
|
|
func GenerateRandomness() Randomness {
|
2023-07-16 17:33:28 -04:00
|
|
|
var randomness Randomness
|
2023-06-02 10:59:05 -06:00
|
|
|
_, err := rand.Read(randomness[:])
|
2024-01-06 14:53:48 -07:00
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return randomness
|
2023-06-02 10:59:05 -06:00
|
|
|
}
|
|
|
|
|
|
2024-03-22 22:11:43 +02:00
|
|
|
const GroupMasterKeyLength = C.SignalGROUP_MASTER_KEY_LEN
|
2025-07-18 15:58:17 +03:00
|
|
|
const GroupIdentifierLength = C.SignalGROUP_IDENTIFIER_LEN
|
2024-03-22 22:11:43 +02:00
|
|
|
|
|
|
|
|
type GroupMasterKey [GroupMasterKeyLength]byte
|
2023-07-14 15:00:25 -04:00
|
|
|
type GroupSecretParams [C.SignalGROUP_SECRET_PARAMS_LEN]byte
|
|
|
|
|
type GroupPublicParams [C.SignalGROUP_PUBLIC_PARAMS_LEN]byte
|
2025-07-18 15:58:17 +03:00
|
|
|
type GroupIdentifier [GroupIdentifierLength]byte
|
|
|
|
|
|
|
|
|
|
func (gid *GroupIdentifier) String() string {
|
|
|
|
|
if gid == nil {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
return base64.StdEncoding.EncodeToString(gid[:])
|
|
|
|
|
}
|
2023-06-02 10:59:05 -06:00
|
|
|
|
2023-07-16 23:31:38 -04:00
|
|
|
type UUIDCiphertext [C.SignalUUID_CIPHERTEXT_LEN]byte
|
|
|
|
|
type ProfileKeyCiphertext [C.SignalPROFILE_KEY_CIPHERTEXT_LEN]byte
|
|
|
|
|
|
2023-06-02 10:59:05 -06:00
|
|
|
func GenerateGroupSecretParams() (GroupSecretParams, error) {
|
2024-01-06 14:53:48 -07:00
|
|
|
return GenerateGroupSecretParamsWithRandomness(GenerateRandomness())
|
2023-06-02 10:59:05 -06:00
|
|
|
}
|
|
|
|
|
|
2025-01-18 16:29:20 +02:00
|
|
|
func (gmk GroupMasterKey) GroupIdentifier() (*GroupIdentifier, error) {
|
|
|
|
|
if groupSecretParams, err := DeriveGroupSecretParamsFromMasterKey(gmk); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("DeriveGroupSecretParamsFromMasterKey error: %w", err)
|
|
|
|
|
} else if groupPublicParams, err := groupSecretParams.GetPublicParams(); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("GetPublicParams error: %w", err)
|
|
|
|
|
} else if groupIdentifier, err := GetGroupIdentifier(*groupPublicParams); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("GetGroupIdentifier error: %w", err)
|
|
|
|
|
} else {
|
|
|
|
|
return groupIdentifier, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-28 16:17:38 +02:00
|
|
|
func (gmk GroupMasterKey) SecretParams() (GroupSecretParams, error) {
|
|
|
|
|
return DeriveGroupSecretParamsFromMasterKey(gmk)
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-02 10:59:05 -06:00
|
|
|
func GenerateGroupSecretParamsWithRandomness(randomness Randomness) (GroupSecretParams, error) {
|
2023-07-16 17:33:28 -04:00
|
|
|
var params [C.SignalGROUP_SECRET_PARAMS_LEN]C.uchar
|
|
|
|
|
signalFfiError := C.signal_group_secret_params_generate_deterministic(¶ms, (*[C.SignalRANDOMNESS_LEN]C.uint8_t)(unsafe.Pointer(&randomness)))
|
2024-01-03 20:31:27 +02:00
|
|
|
runtime.KeepAlive(randomness)
|
2023-06-02 10:59:05 -06:00
|
|
|
if signalFfiError != nil {
|
|
|
|
|
return GroupSecretParams{}, wrapError(signalFfiError)
|
|
|
|
|
}
|
|
|
|
|
var groupSecretParams GroupSecretParams
|
2023-07-16 17:33:28 -04:00
|
|
|
copy(groupSecretParams[:], C.GoBytes(unsafe.Pointer(¶ms), C.int(C.SignalGROUP_SECRET_PARAMS_LEN)))
|
2023-06-02 10:59:05 -06:00
|
|
|
return groupSecretParams, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func DeriveGroupSecretParamsFromMasterKey(groupMasterKey GroupMasterKey) (GroupSecretParams, error) {
|
2023-07-16 17:33:28 -04:00
|
|
|
var params [C.SignalGROUP_SECRET_PARAMS_LEN]C.uchar
|
|
|
|
|
signalFfiError := C.signal_group_secret_params_derive_from_master_key(¶ms, (*[C.SignalGROUP_MASTER_KEY_LEN]C.uint8_t)(unsafe.Pointer(&groupMasterKey)))
|
2024-01-03 20:31:27 +02:00
|
|
|
runtime.KeepAlive(groupMasterKey)
|
2023-06-02 10:59:05 -06:00
|
|
|
if signalFfiError != nil {
|
|
|
|
|
return GroupSecretParams{}, wrapError(signalFfiError)
|
|
|
|
|
}
|
|
|
|
|
var groupSecretParams GroupSecretParams
|
2023-07-16 17:33:28 -04:00
|
|
|
copy(groupSecretParams[:], C.GoBytes(unsafe.Pointer(¶ms), C.int(C.SignalGROUP_SECRET_PARAMS_LEN)))
|
2023-06-02 10:59:05 -06:00
|
|
|
return groupSecretParams, nil
|
|
|
|
|
}
|
2023-07-14 15:00:25 -04:00
|
|
|
|
|
|
|
|
func (gsp *GroupSecretParams) GetPublicParams() (*GroupPublicParams, error) {
|
|
|
|
|
var publicParams [C.SignalGROUP_PUBLIC_PARAMS_LEN]C.uchar
|
2023-07-16 17:33:28 -04:00
|
|
|
signalFfiError := C.signal_group_secret_params_get_public_params(&publicParams, (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)))
|
2024-01-03 20:31:27 +02:00
|
|
|
runtime.KeepAlive(gsp)
|
2023-07-14 15:00:25 -04:00
|
|
|
if signalFfiError != nil {
|
|
|
|
|
return nil, wrapError(signalFfiError)
|
|
|
|
|
}
|
|
|
|
|
var groupPublicParams GroupPublicParams
|
|
|
|
|
copy(groupPublicParams[:], C.GoBytes(unsafe.Pointer(&publicParams), C.int(C.SignalGROUP_PUBLIC_PARAMS_LEN)))
|
|
|
|
|
return &groupPublicParams, nil
|
|
|
|
|
}
|
2023-07-16 23:31:38 -04:00
|
|
|
|
2023-09-14 16:33:40 -04:00
|
|
|
func GetGroupIdentifier(groupPublicParams GroupPublicParams) (*GroupIdentifier, error) {
|
|
|
|
|
var groupIdentifier [C.SignalGROUP_IDENTIFIER_LEN]C.uchar
|
|
|
|
|
signalFfiError := C.signal_group_public_params_get_group_identifier(&groupIdentifier, (*[C.SignalGROUP_PUBLIC_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(&groupPublicParams)))
|
2024-01-03 20:31:27 +02:00
|
|
|
runtime.KeepAlive(groupPublicParams)
|
2023-09-14 16:33:40 -04:00
|
|
|
if signalFfiError != nil {
|
|
|
|
|
return nil, wrapError(signalFfiError)
|
|
|
|
|
}
|
|
|
|
|
var result GroupIdentifier
|
|
|
|
|
copy(result[:], C.GoBytes(unsafe.Pointer(&groupIdentifier), C.int(C.SignalGROUP_IDENTIFIER_LEN)))
|
|
|
|
|
return &result, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-16 23:31:38 -04:00
|
|
|
func (gsp *GroupSecretParams) DecryptBlobWithPadding(blob []byte) ([]byte, error) {
|
|
|
|
|
var plaintext C.SignalOwnedBuffer = C.SignalOwnedBuffer{}
|
|
|
|
|
borrowedBlob := BytesToBuffer(blob)
|
|
|
|
|
signalFfiError := C.signal_group_secret_params_decrypt_blob_with_padding(
|
|
|
|
|
&plaintext,
|
|
|
|
|
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
|
|
|
|
|
borrowedBlob,
|
|
|
|
|
)
|
2024-01-03 20:31:27 +02:00
|
|
|
runtime.KeepAlive(gsp)
|
|
|
|
|
runtime.KeepAlive(blob)
|
2023-07-16 23:31:38 -04:00
|
|
|
if signalFfiError != nil {
|
|
|
|
|
return nil, wrapError(signalFfiError)
|
|
|
|
|
}
|
|
|
|
|
return CopySignalOwnedBufferToBytes(plaintext), nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-29 20:12:10 +01:00
|
|
|
func (gsp *GroupSecretParams) EncryptBlobWithPaddingDeterministic(randomness Randomness, plaintext []byte, padding_len uint32) ([]byte, error) {
|
|
|
|
|
var ciphertext C.SignalOwnedBuffer = C.SignalOwnedBuffer{}
|
|
|
|
|
borrowedPlaintext := BytesToBuffer(plaintext)
|
|
|
|
|
signalFfiError := C.signal_group_secret_params_encrypt_blob_with_padding_deterministic(
|
|
|
|
|
&ciphertext,
|
|
|
|
|
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
|
|
|
|
|
(*[C.SignalRANDOMNESS_LEN]C.uint8_t)(unsafe.Pointer(&randomness)),
|
|
|
|
|
borrowedPlaintext,
|
|
|
|
|
(C.uint32_t)(padding_len),
|
|
|
|
|
)
|
|
|
|
|
runtime.KeepAlive(randomness)
|
|
|
|
|
runtime.KeepAlive(gsp)
|
|
|
|
|
runtime.KeepAlive(plaintext)
|
|
|
|
|
runtime.KeepAlive(padding_len)
|
|
|
|
|
if signalFfiError != nil {
|
|
|
|
|
return nil, wrapError(signalFfiError)
|
|
|
|
|
}
|
|
|
|
|
return CopySignalOwnedBufferToBytes(ciphertext), nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-01 12:59:04 +02:00
|
|
|
func (gsp *GroupSecretParams) DecryptServiceID(ciphertextServiceID UUIDCiphertext) (ServiceID, error) {
|
2023-12-22 14:47:07 +02:00
|
|
|
u := C.SignalServiceIdFixedWidthBinaryBytes{}
|
2023-12-10 17:32:33 -05:00
|
|
|
signalFfiError := C.signal_group_secret_params_decrypt_service_id(
|
2023-12-22 14:47:07 +02:00
|
|
|
&u,
|
2023-07-16 23:31:38 -04:00
|
|
|
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
|
2024-04-01 12:59:04 +02:00
|
|
|
(*[C.SignalUUID_CIPHERTEXT_LEN]C.uint8_t)(unsafe.Pointer(&ciphertextServiceID)),
|
2023-07-16 23:31:38 -04:00
|
|
|
)
|
2024-01-03 20:31:27 +02:00
|
|
|
runtime.KeepAlive(gsp)
|
2024-04-01 12:59:04 +02:00
|
|
|
runtime.KeepAlive(ciphertextServiceID)
|
2023-07-16 23:31:38 -04:00
|
|
|
if signalFfiError != nil {
|
2024-04-01 12:59:04 +02:00
|
|
|
return EmptyServiceID, wrapError(signalFfiError)
|
2023-07-16 23:31:38 -04:00
|
|
|
}
|
2023-12-10 17:32:33 -05:00
|
|
|
|
2024-03-15 15:30:44 +02:00
|
|
|
serviceID := ServiceIDFromCFixedBytes(&u)
|
2024-04-01 12:59:04 +02:00
|
|
|
return serviceID, nil
|
2023-07-16 23:31:38 -04:00
|
|
|
}
|
|
|
|
|
|
2024-04-01 12:59:04 +02:00
|
|
|
func (gsp *GroupSecretParams) EncryptServiceID(serviceID ServiceID) (*UUIDCiphertext, error) {
|
|
|
|
|
var cipherTextServiceID [C.SignalUUID_CIPHERTEXT_LEN]C.uchar
|
2024-02-29 20:12:10 +01:00
|
|
|
signalFfiError := C.signal_group_secret_params_encrypt_service_id(
|
2024-04-01 12:59:04 +02:00
|
|
|
&cipherTextServiceID,
|
2024-02-29 20:12:10 +01:00
|
|
|
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
|
2024-04-01 12:59:04 +02:00
|
|
|
serviceID.CFixedBytes(),
|
2024-02-29 20:12:10 +01:00
|
|
|
)
|
|
|
|
|
runtime.KeepAlive(gsp)
|
|
|
|
|
if signalFfiError != nil {
|
|
|
|
|
return nil, wrapError(signalFfiError)
|
|
|
|
|
}
|
|
|
|
|
var result UUIDCiphertext
|
2024-04-01 12:59:04 +02:00
|
|
|
copy(result[:], C.GoBytes(unsafe.Pointer(&cipherTextServiceID), C.int(C.SignalUUID_CIPHERTEXT_LEN)))
|
2024-02-29 20:12:10 +01:00
|
|
|
return &result, nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-22 14:47:07 +02:00
|
|
|
func (gsp *GroupSecretParams) DecryptProfileKey(ciphertextProfileKey ProfileKeyCiphertext, u uuid.UUID) (*ProfileKey, error) {
|
2023-07-17 16:06:48 -04:00
|
|
|
profileKey := [C.SignalPROFILE_KEY_LEN]C.uchar{}
|
2023-07-16 23:31:38 -04:00
|
|
|
signalFfiError := C.signal_group_secret_params_decrypt_profile_key(
|
2023-07-17 16:06:48 -04:00
|
|
|
&profileKey,
|
2023-07-16 23:31:38 -04:00
|
|
|
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
|
|
|
|
|
(*[C.SignalPROFILE_KEY_CIPHERTEXT_LEN]C.uint8_t)(unsafe.Pointer(&ciphertextProfileKey)),
|
2024-03-15 15:30:44 +02:00
|
|
|
NewACIServiceID(u).CFixedBytes(),
|
2023-07-16 23:31:38 -04:00
|
|
|
)
|
2024-01-03 20:31:27 +02:00
|
|
|
runtime.KeepAlive(gsp)
|
|
|
|
|
runtime.KeepAlive(ciphertextProfileKey)
|
2024-03-15 15:30:44 +02:00
|
|
|
runtime.KeepAlive(u)
|
2023-07-16 23:31:38 -04:00
|
|
|
if signalFfiError != nil {
|
|
|
|
|
return nil, wrapError(signalFfiError)
|
|
|
|
|
}
|
|
|
|
|
var result ProfileKey
|
2023-07-17 16:06:48 -04:00
|
|
|
copy(result[:], C.GoBytes(unsafe.Pointer(&profileKey), C.int(C.SignalPROFILE_KEY_LEN)))
|
2023-07-16 23:31:38 -04:00
|
|
|
return &result, nil
|
|
|
|
|
}
|
2024-02-29 20:12:10 +01:00
|
|
|
|
|
|
|
|
func (gsp *GroupSecretParams) EncryptProfileKey(profileKey ProfileKey, u uuid.UUID) (*ProfileKeyCiphertext, error) {
|
|
|
|
|
ciphertextProfileKey := [C.SignalPROFILE_KEY_CIPHERTEXT_LEN]C.uchar{}
|
|
|
|
|
signalFfiError := C.signal_group_secret_params_encrypt_profile_key(
|
|
|
|
|
&ciphertextProfileKey,
|
|
|
|
|
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
|
|
|
|
|
(*[C.SignalPROFILE_KEY_LEN]C.uint8_t)(unsafe.Pointer(&profileKey)),
|
2024-03-22 20:03:50 +02:00
|
|
|
NewACIServiceID(u).CFixedBytes(),
|
2024-02-29 20:12:10 +01:00
|
|
|
)
|
|
|
|
|
runtime.KeepAlive(gsp)
|
|
|
|
|
runtime.KeepAlive(profileKey)
|
|
|
|
|
if signalFfiError != nil {
|
|
|
|
|
return nil, wrapError(signalFfiError)
|
|
|
|
|
}
|
|
|
|
|
var result ProfileKeyCiphertext
|
|
|
|
|
copy(result[:], C.GoBytes(unsafe.Pointer(&ciphertextProfileKey), C.int(C.SignalPROFILE_KEY_CIPHERTEXT_LEN)))
|
|
|
|
|
return &result, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-03 16:57:45 +03:00
|
|
|
func (gsp *GroupSecretParams) CreateExpiringProfileKeyCredentialPresentation(spp *ServerPublicParams, credential ExpiringProfileKeyCredential) (*ProfileKeyCredentialPresentation, error) {
|
2024-02-29 20:12:10 +01:00
|
|
|
var out C.SignalOwnedBuffer = C.SignalOwnedBuffer{}
|
|
|
|
|
randomness := GenerateRandomness()
|
|
|
|
|
signalFfiError := C.signal_server_public_params_create_expiring_profile_key_credential_presentation_deterministic(
|
|
|
|
|
&out,
|
2025-01-17 17:49:37 +02:00
|
|
|
C.SignalConstPointerServerPublicParams{spp},
|
2024-02-29 20:12:10 +01:00
|
|
|
(*[C.SignalRANDOMNESS_LEN]C.uint8_t)(unsafe.Pointer(&randomness)),
|
|
|
|
|
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uchar)(unsafe.Pointer(gsp)),
|
|
|
|
|
(*[C.SignalEXPIRING_PROFILE_KEY_CREDENTIAL_LEN]C.uchar)(unsafe.Pointer(&credential)),
|
|
|
|
|
)
|
|
|
|
|
runtime.KeepAlive(gsp)
|
|
|
|
|
runtime.KeepAlive(credential)
|
|
|
|
|
runtime.KeepAlive(randomness)
|
|
|
|
|
if signalFfiError != nil {
|
|
|
|
|
return nil, wrapError(signalFfiError)
|
|
|
|
|
}
|
|
|
|
|
presentationBytes := CopySignalOwnedBufferToBytes(out)
|
|
|
|
|
presentation := ProfileKeyCredentialPresentation(presentationBytes)
|
|
|
|
|
return &presentation, nil
|
|
|
|
|
}
|
2024-03-20 22:49:03 +01:00
|
|
|
|
|
|
|
|
func (gsp *GroupSecretParams) GetMasterKey() (*GroupMasterKey, error) {
|
|
|
|
|
masterKeyBytes := [C.SignalGROUP_MASTER_KEY_LEN]C.uchar{}
|
|
|
|
|
signalFfiError := C.signal_group_secret_params_get_master_key(
|
|
|
|
|
&masterKeyBytes,
|
|
|
|
|
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uchar)(unsafe.Pointer(gsp)),
|
|
|
|
|
)
|
|
|
|
|
runtime.KeepAlive(gsp)
|
|
|
|
|
if signalFfiError != nil {
|
|
|
|
|
return nil, wrapError(signalFfiError)
|
|
|
|
|
}
|
|
|
|
|
var groupMasterKey GroupMasterKey
|
|
|
|
|
copy(groupMasterKey[:], C.GoBytes(unsafe.Pointer(&masterKeyBytes), C.int(C.SignalGROUP_MASTER_KEY_LEN)))
|
|
|
|
|
return &groupMasterKey, nil
|
|
|
|
|
}
|