mirror of
https://dev.narayana.im/narayana/telegabber.git
synced 2026-08-05 04:07:07 +00:00
142 lines
4.6 KiB
Go
142 lines
4.6 KiB
Go
package libsignal
|
|
|
|
/*
|
|
#include <signal_protocol.h>
|
|
#include <session_builder.h>
|
|
#include <session_pre_key.h>
|
|
#include <curve.h>
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"runtime"
|
|
"unsafe"
|
|
)
|
|
|
|
// Protocol version constants (protocol.h's CIPHERTEXT_CURRENT_VERSION /
|
|
// CIPHERTEXT_OMEMO_VERSION), selecting legacy siacs OMEMO vs modern OMEMO.
|
|
// Modern is shared by OMEMO 1 (urn:xmpp:omemo:1) and OMEMO 2
|
|
// (urn:xmpp:omemo:2) - those two differ only in outer SCE stanza framing,
|
|
// not in this underlying Double Ratchet/X3DH wire format.
|
|
const (
|
|
ProtocolVersionLegacy = 3
|
|
ProtocolVersionModern = 4
|
|
)
|
|
|
|
// RemoteBundle is the decoded material from a peer's XEP-0384 device
|
|
// bundle, as needed to establish a session with one of their devices via
|
|
// X3DH.
|
|
type RemoteBundle struct {
|
|
RegistrationID uint32
|
|
DeviceID uint32
|
|
PreKeyID uint32 // 0 if the bundle carried no one-time prekey
|
|
PreKeyPublic []byte // nil if PreKeyID == 0
|
|
SignedPreKeyID uint32
|
|
SignedPreKeyPublic []byte
|
|
SignedPreKeySignature []byte
|
|
IdentityKeyPublic []byte
|
|
}
|
|
|
|
// SessionBuilder establishes (or refreshes) a Double Ratchet session with
|
|
// one remote device.
|
|
type SessionBuilder struct {
|
|
raw *C.session_builder
|
|
}
|
|
|
|
// NewSessionBuilder creates a session builder for storeCtx's local identity
|
|
// talking to remote. version controls the protocol version new sessions
|
|
// built by ProcessPreKeyBundle will use for encryption going forward
|
|
// (ProtocolVersionLegacy or ProtocolVersionModern).
|
|
func NewSessionBuilder(ctx *Context, storeCtx *StoreContext, remote Address, version int) (*SessionBuilder, error) {
|
|
var raw *C.session_builder
|
|
var code C.int
|
|
withCAddress(remote, func(cAddr *C.signal_protocol_address) {
|
|
code = C.session_builder_create(&raw, storeCtx.raw, cAddr, ctx.raw)
|
|
})
|
|
if code != C.SG_SUCCESS {
|
|
return nil, newError("session_builder_create", int(code))
|
|
}
|
|
if err := setBuilderVersion(raw, version); err != nil {
|
|
C.session_builder_free(raw)
|
|
return nil, err
|
|
}
|
|
return &SessionBuilder{raw: raw}, nil
|
|
}
|
|
|
|
// Close frees the session builder. Do not use it afterward.
|
|
func (b *SessionBuilder) Close() {
|
|
if b.raw != nil {
|
|
C.session_builder_free(b.raw)
|
|
b.raw = nil
|
|
}
|
|
}
|
|
|
|
// ProcessPreKeyBundle establishes a session from a remote peer's fetched
|
|
// device bundle (X3DH).
|
|
//
|
|
// IsUntrustedIdentity(err) reports the case where the remote's identity key
|
|
// does not match a previously trusted one for this device id - under a
|
|
// TOFU trust policy this is the one case that must NOT be silently
|
|
// re-trusted (it is what TOFU exists to catch: either genuine key rotation
|
|
// or an impersonation attempt), unlike a first-ever sighting of a device,
|
|
// which the identity store's IsTrustedIdentity should accept.
|
|
func (b *SessionBuilder) ProcessPreKeyBundle(ctx *Context, bundle RemoteBundle) error {
|
|
identityKey, err := decodePoint(ctx, bundle.IdentityKeyPublic)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer C.signal_type_unref((*C.signal_type_base)(unsafe.Pointer(identityKey)))
|
|
|
|
signedPreKeyPublic, err := decodePoint(ctx, bundle.SignedPreKeyPublic)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer C.signal_type_unref((*C.signal_type_base)(unsafe.Pointer(signedPreKeyPublic)))
|
|
|
|
var preKeyPublic *C.ec_public_key
|
|
if len(bundle.PreKeyPublic) > 0 {
|
|
preKeyPublic, err = decodePoint(ctx, bundle.PreKeyPublic)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer C.signal_type_unref((*C.signal_type_base)(unsafe.Pointer(preKeyPublic)))
|
|
}
|
|
|
|
sigPtr, sigLen := cBytesPtrLen(bundle.SignedPreKeySignature)
|
|
|
|
var cBundle *C.session_pre_key_bundle
|
|
code := C.session_pre_key_bundle_create(&cBundle,
|
|
C.uint32_t(bundle.RegistrationID), C.int(bundle.DeviceID),
|
|
C.uint32_t(bundle.PreKeyID), preKeyPublic,
|
|
C.uint32_t(bundle.SignedPreKeyID), signedPreKeyPublic,
|
|
sigPtr, sigLen,
|
|
identityKey)
|
|
runtime.KeepAlive(bundle.SignedPreKeySignature)
|
|
if code != C.SG_SUCCESS {
|
|
return newError("session_pre_key_bundle_create", int(code))
|
|
}
|
|
defer C.signal_type_unref((*C.signal_type_base)(unsafe.Pointer(cBundle)))
|
|
|
|
if code := C.session_builder_process_pre_key_bundle(b.raw, cBundle); code != C.SG_SUCCESS {
|
|
return newError("session_builder_process_pre_key_bundle", int(code))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func decodePoint(ctx *Context, data []byte) (*C.ec_public_key, error) {
|
|
var key *C.ec_public_key
|
|
ptr, length := cBytesPtrLen(data)
|
|
code := C.curve_decode_point(&key, ptr, length, ctx.raw)
|
|
runtime.KeepAlive(data)
|
|
if code != C.SG_SUCCESS {
|
|
return nil, newError("curve_decode_point", int(code))
|
|
}
|
|
return key, nil
|
|
}
|
|
|
|
func cBytesPtrLen(b []byte) (*C.uint8_t, C.size_t) {
|
|
if len(b) == 0 {
|
|
return nil, 0
|
|
}
|
|
return (*C.uint8_t)(unsafe.Pointer(&b[0])), C.size_t(len(b))
|
|
}
|