mirror of
https://github.com/mautrix/signal.git
synced 2026-08-23 12:24:56 -04:00
282 lines
9.1 KiB
Go
282 lines
9.1 KiB
Go
// mautrix-signal - A Matrix-signal puppeting bridge.
|
|
// Copyright (C) 2023 Sumner Evans
|
|
// Copyright (C) 2025 Tulir Asokan
|
|
//
|
|
// 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/>.
|
|
|
|
package libsignalgo
|
|
|
|
/*
|
|
#include "./libsignal-ffi.h"
|
|
*/
|
|
import "C"
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"runtime"
|
|
"unsafe"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const RandomnessLength = 32
|
|
|
|
type Randomness = fixedArray32
|
|
|
|
func GenerateRandomness() Randomness {
|
|
var randomness Randomness
|
|
_, err := rand.Read(randomness[:])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return randomness
|
|
}
|
|
|
|
const GroupMasterKeyLength = 32
|
|
const GroupIdentifierLength = 32
|
|
const GroupSecretParamsLength = 289
|
|
|
|
type GroupMasterKey [GroupMasterKeyLength]byte
|
|
type GroupSecretParams [GroupSecretParamsLength]byte
|
|
type GroupPublicParams = fixedArray97
|
|
type GroupIdentifier [GroupIdentifierLength]byte
|
|
|
|
func (gmk *GroupMasterKey) cFixedArray() *C.SignalType_FixedArray32_uint8_t {
|
|
return (*C.SignalType_FixedArray32_uint8_t)(unsafe.Pointer(gmk))
|
|
}
|
|
|
|
func (gmk *GroupMasterKey) cConstFixedArray() cFixedArray32Compat {
|
|
return cFixedArray32Compat(gmk.cFixedArray())
|
|
}
|
|
|
|
func (gsp *GroupSecretParams) cFixedArray() *C.SignalType_FixedArray289_uint8_t {
|
|
return (*C.SignalType_FixedArray289_uint8_t)(unsafe.Pointer(gsp))
|
|
}
|
|
|
|
func (gsp *GroupSecretParams) cConstFixedArray() cFixedArray289Compat {
|
|
return cFixedArray289Compat(gsp.cFixedArray())
|
|
}
|
|
|
|
func (gid *GroupIdentifier) cFixedArray() *C.SignalType_FixedArray32_uint8_t {
|
|
return (*C.SignalType_FixedArray32_uint8_t)(unsafe.Pointer(gid))
|
|
}
|
|
|
|
func (gid *GroupIdentifier) cConstFixedArray() cFixedArray32Compat {
|
|
return cFixedArray32Compat(gid.cFixedArray())
|
|
}
|
|
|
|
func (gid *GroupIdentifier) String() string {
|
|
if gid == nil {
|
|
return ""
|
|
}
|
|
return base64.StdEncoding.EncodeToString(gid[:])
|
|
}
|
|
|
|
type UUIDCiphertext = fixedArray65
|
|
type ProfileKeyCiphertext = fixedArray65
|
|
|
|
func GenerateGroupSecretParams() (GroupSecretParams, error) {
|
|
return GenerateGroupSecretParamsWithRandomness(GenerateRandomness())
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
func (gmk GroupMasterKey) SecretParams() (GroupSecretParams, error) {
|
|
return DeriveGroupSecretParamsFromMasterKey(gmk)
|
|
}
|
|
|
|
func GenerateGroupSecretParamsWithRandomness(randomness Randomness) (GroupSecretParams, error) {
|
|
var params GroupSecretParams
|
|
signalFfiError := C.signal_group_secret_params_generate_deterministic(params.cFixedArray(), randomness.cConstFixedArray())
|
|
runtime.KeepAlive(randomness)
|
|
if signalFfiError != nil {
|
|
return GroupSecretParams{}, wrapError(signalFfiError)
|
|
}
|
|
return params, nil
|
|
}
|
|
|
|
func DeriveGroupSecretParamsFromMasterKey(groupMasterKey GroupMasterKey) (GroupSecretParams, error) {
|
|
var params GroupSecretParams
|
|
signalFfiError := C.signal_group_secret_params_derive_from_master_key(params.cFixedArray(), groupMasterKey.cConstFixedArray())
|
|
runtime.KeepAlive(groupMasterKey)
|
|
if signalFfiError != nil {
|
|
return GroupSecretParams{}, wrapError(signalFfiError)
|
|
}
|
|
return params, nil
|
|
}
|
|
|
|
func (gsp *GroupSecretParams) GetPublicParams() (*GroupPublicParams, error) {
|
|
var publicParams GroupPublicParams
|
|
signalFfiError := C.signal_group_secret_params_get_public_params(publicParams.cFixedArray(), gsp.cConstFixedArray())
|
|
runtime.KeepAlive(gsp)
|
|
if signalFfiError != nil {
|
|
return nil, wrapError(signalFfiError)
|
|
}
|
|
return &publicParams, nil
|
|
}
|
|
|
|
func GetGroupIdentifier(groupPublicParams GroupPublicParams) (*GroupIdentifier, error) {
|
|
var groupIdentifier GroupIdentifier
|
|
signalFfiError := C.signal_group_public_params_get_group_identifier(groupIdentifier.cFixedArray(), groupPublicParams.cConstFixedArray())
|
|
runtime.KeepAlive(groupPublicParams)
|
|
if signalFfiError != nil {
|
|
return nil, wrapError(signalFfiError)
|
|
}
|
|
return &groupIdentifier, nil
|
|
}
|
|
|
|
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,
|
|
gsp.cConstFixedArray(),
|
|
borrowedBlob,
|
|
)
|
|
runtime.KeepAlive(gsp)
|
|
runtime.KeepAlive(blob)
|
|
if signalFfiError != nil {
|
|
return nil, wrapError(signalFfiError)
|
|
}
|
|
return CopySignalOwnedBufferToBytes(plaintext), nil
|
|
}
|
|
|
|
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,
|
|
gsp.cConstFixedArray(),
|
|
randomness.cConstFixedArray(),
|
|
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
|
|
}
|
|
|
|
func (gsp *GroupSecretParams) DecryptServiceID(ciphertextServiceID UUIDCiphertext) (ServiceID, error) {
|
|
var serviceIDBytes ServiceIDFixedBytes
|
|
signalFfiError := C.signal_group_secret_params_decrypt_service_id(
|
|
serviceIDBytes.cFixedArray(),
|
|
gsp.cConstFixedArray(),
|
|
ciphertextServiceID.cConstFixedArray(),
|
|
)
|
|
runtime.KeepAlive(gsp)
|
|
runtime.KeepAlive(ciphertextServiceID)
|
|
if signalFfiError != nil {
|
|
return EmptyServiceID, wrapError(signalFfiError)
|
|
}
|
|
|
|
serviceID := ServiceIDFromCFixedBytes(serviceIDBytes.cFixedArray())
|
|
return serviceID, nil
|
|
}
|
|
|
|
func (gsp *GroupSecretParams) EncryptServiceID(serviceID ServiceID) (*UUIDCiphertext, error) {
|
|
var cipherTextServiceID UUIDCiphertext
|
|
signalFfiError := C.signal_group_secret_params_encrypt_service_id(
|
|
cipherTextServiceID.cFixedArray(),
|
|
gsp.cConstFixedArray(),
|
|
serviceID.cConstFixedArray(),
|
|
)
|
|
runtime.KeepAlive(gsp)
|
|
if signalFfiError != nil {
|
|
return nil, wrapError(signalFfiError)
|
|
}
|
|
return &cipherTextServiceID, nil
|
|
}
|
|
|
|
func (gsp *GroupSecretParams) DecryptProfileKey(ciphertextProfileKey ProfileKeyCiphertext, u uuid.UUID) (*ProfileKey, error) {
|
|
var profileKey ProfileKey
|
|
signalFfiError := C.signal_group_secret_params_decrypt_profile_key(
|
|
profileKey.cFixedArray(),
|
|
gsp.cConstFixedArray(),
|
|
ciphertextProfileKey.cConstFixedArray(),
|
|
NewACIServiceID(u).cConstFixedArray(),
|
|
)
|
|
runtime.KeepAlive(gsp)
|
|
runtime.KeepAlive(ciphertextProfileKey)
|
|
runtime.KeepAlive(u)
|
|
if signalFfiError != nil {
|
|
return nil, wrapError(signalFfiError)
|
|
}
|
|
return &profileKey, nil
|
|
}
|
|
|
|
func (gsp *GroupSecretParams) EncryptProfileKey(profileKey ProfileKey, u uuid.UUID) (*ProfileKeyCiphertext, error) {
|
|
var ciphertextProfileKey ProfileKeyCiphertext
|
|
signalFfiError := C.signal_group_secret_params_encrypt_profile_key(
|
|
ciphertextProfileKey.cFixedArray(),
|
|
gsp.cConstFixedArray(),
|
|
profileKey.cConstFixedArray(),
|
|
NewACIServiceID(u).cConstFixedArray(),
|
|
)
|
|
runtime.KeepAlive(gsp)
|
|
runtime.KeepAlive(profileKey)
|
|
if signalFfiError != nil {
|
|
return nil, wrapError(signalFfiError)
|
|
}
|
|
return &ciphertextProfileKey, nil
|
|
}
|
|
|
|
func (gsp *GroupSecretParams) CreateExpiringProfileKeyCredentialPresentation(spp *ServerPublicParams, credential ExpiringProfileKeyCredential) (*ProfileKeyCredentialPresentation, error) {
|
|
var out C.SignalOwnedBuffer = C.SignalOwnedBuffer{}
|
|
randomness := GenerateRandomness()
|
|
signalFfiError := C.signal_server_public_params_create_expiring_profile_key_credential_presentation_deterministic(
|
|
&out,
|
|
C.SignalConstPointerServerPublicParams{spp},
|
|
randomness.cConstFixedArray(),
|
|
gsp.cConstFixedArray(),
|
|
credential.cConstFixedArray(),
|
|
)
|
|
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
|
|
}
|
|
|
|
func (gsp *GroupSecretParams) GetMasterKey() (*GroupMasterKey, error) {
|
|
var masterKey GroupMasterKey
|
|
signalFfiError := C.signal_group_secret_params_get_master_key(
|
|
masterKey.cFixedArray(),
|
|
gsp.cConstFixedArray(),
|
|
)
|
|
runtime.KeepAlive(gsp)
|
|
if signalFfiError != nil {
|
|
return nil, wrapError(signalFfiError)
|
|
}
|
|
return &masterKey, nil
|
|
}
|