233 lines
7.1 KiB
Go
233 lines
7.1 KiB
Go
|
package olm
|
||
|
|
||
|
// #cgo LDFLAGS: -lolm -lstdc++
|
||
|
// #include <olm/olm.h>
|
||
|
import "C"
|
||
|
|
||
|
import (
|
||
|
"crypto/rand"
|
||
|
"unsafe"
|
||
|
|
||
|
"maunium.net/go/mautrix/id"
|
||
|
)
|
||
|
|
||
|
// OutboundGroupSession stores an outbound encrypted messaging session for a
|
||
|
// group.
|
||
|
type OutboundGroupSession struct {
|
||
|
int *C.OlmOutboundGroupSession
|
||
|
mem []byte
|
||
|
}
|
||
|
|
||
|
// OutboundGroupSessionFromPickled loads an OutboundGroupSession from a pickled
|
||
|
// base64 string. Decrypts the OutboundGroupSession using the supplied key.
|
||
|
// Returns error on failure. If the key doesn't match the one used to encrypt
|
||
|
// the OutboundGroupSession then the error will be "BAD_SESSION_KEY". If the
|
||
|
// base64 couldn't be decoded then the error will be "INVALID_BASE64".
|
||
|
func OutboundGroupSessionFromPickled(pickled, key []byte) (*OutboundGroupSession, error) {
|
||
|
if len(pickled) == 0 {
|
||
|
return nil, EmptyInput
|
||
|
}
|
||
|
s := NewBlankOutboundGroupSession()
|
||
|
return s, s.Unpickle(pickled, key)
|
||
|
}
|
||
|
|
||
|
// NewOutboundGroupSession creates a new outbound group session.
|
||
|
func NewOutboundGroupSession() *OutboundGroupSession {
|
||
|
s := NewBlankOutboundGroupSession()
|
||
|
random := make([]byte, s.createRandomLen()+1)
|
||
|
_, err := rand.Read(random)
|
||
|
if err != nil {
|
||
|
panic(NotEnoughGoRandom)
|
||
|
}
|
||
|
r := C.olm_init_outbound_group_session(
|
||
|
(*C.OlmOutboundGroupSession)(s.int),
|
||
|
(*C.uint8_t)(&random[0]),
|
||
|
C.size_t(len(random)))
|
||
|
if r == errorVal() {
|
||
|
panic(s.lastError())
|
||
|
}
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
// outboundGroupSessionSize is the size of an outbound group session object in
|
||
|
// bytes.
|
||
|
func outboundGroupSessionSize() uint {
|
||
|
return uint(C.olm_outbound_group_session_size())
|
||
|
}
|
||
|
|
||
|
// newOutboundGroupSession initialises an empty OutboundGroupSession.
|
||
|
func NewBlankOutboundGroupSession() *OutboundGroupSession {
|
||
|
memory := make([]byte, outboundGroupSessionSize())
|
||
|
return &OutboundGroupSession{
|
||
|
int: C.olm_outbound_group_session(unsafe.Pointer(&memory[0])),
|
||
|
mem: memory,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// lastError returns an error describing the most recent error to happen to an
|
||
|
// outbound group session.
|
||
|
func (s *OutboundGroupSession) lastError() error {
|
||
|
return convertError(C.GoString(C.olm_outbound_group_session_last_error((*C.OlmOutboundGroupSession)(s.int))))
|
||
|
}
|
||
|
|
||
|
// Clear clears the memory used to back this OutboundGroupSession.
|
||
|
func (s *OutboundGroupSession) Clear() error {
|
||
|
r := C.olm_clear_outbound_group_session((*C.OlmOutboundGroupSession)(s.int))
|
||
|
if r == errorVal() {
|
||
|
return s.lastError()
|
||
|
} else {
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// pickleLen returns the number of bytes needed to store an outbound group
|
||
|
// session.
|
||
|
func (s *OutboundGroupSession) pickleLen() uint {
|
||
|
return uint(C.olm_pickle_outbound_group_session_length((*C.OlmOutboundGroupSession)(s.int)))
|
||
|
}
|
||
|
|
||
|
// Pickle returns an OutboundGroupSession as a base64 string. Encrypts the
|
||
|
// OutboundGroupSession using the supplied key.
|
||
|
func (s *OutboundGroupSession) Pickle(key []byte) []byte {
|
||
|
if len(key) == 0 {
|
||
|
panic(NoKeyProvided)
|
||
|
}
|
||
|
pickled := make([]byte, s.pickleLen())
|
||
|
r := C.olm_pickle_outbound_group_session(
|
||
|
(*C.OlmOutboundGroupSession)(s.int),
|
||
|
unsafe.Pointer(&key[0]),
|
||
|
C.size_t(len(key)),
|
||
|
unsafe.Pointer(&pickled[0]),
|
||
|
C.size_t(len(pickled)))
|
||
|
if r == errorVal() {
|
||
|
panic(s.lastError())
|
||
|
}
|
||
|
return pickled[:r]
|
||
|
}
|
||
|
|
||
|
func (s *OutboundGroupSession) Unpickle(pickled, key []byte) error {
|
||
|
if len(key) == 0 {
|
||
|
return NoKeyProvided
|
||
|
}
|
||
|
r := C.olm_unpickle_outbound_group_session(
|
||
|
(*C.OlmOutboundGroupSession)(s.int),
|
||
|
unsafe.Pointer(&key[0]),
|
||
|
C.size_t(len(key)),
|
||
|
unsafe.Pointer(&pickled[0]),
|
||
|
C.size_t(len(pickled)))
|
||
|
if r == errorVal() {
|
||
|
return s.lastError()
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *OutboundGroupSession) GobEncode() ([]byte, error) {
|
||
|
pickled := s.Pickle(pickleKey)
|
||
|
length := unpaddedBase64.DecodedLen(len(pickled))
|
||
|
rawPickled := make([]byte, length)
|
||
|
_, err := unpaddedBase64.Decode(rawPickled, pickled)
|
||
|
return rawPickled, err
|
||
|
}
|
||
|
|
||
|
func (s *OutboundGroupSession) GobDecode(rawPickled []byte) error {
|
||
|
if s == nil || s.int == nil {
|
||
|
*s = *NewBlankOutboundGroupSession()
|
||
|
}
|
||
|
length := unpaddedBase64.EncodedLen(len(rawPickled))
|
||
|
pickled := make([]byte, length)
|
||
|
unpaddedBase64.Encode(pickled, rawPickled)
|
||
|
return s.Unpickle(pickled, pickleKey)
|
||
|
}
|
||
|
|
||
|
func (s *OutboundGroupSession) MarshalJSON() ([]byte, error) {
|
||
|
pickled := s.Pickle(pickleKey)
|
||
|
quotes := make([]byte, len(pickled)+2)
|
||
|
quotes[0] = '"'
|
||
|
quotes[len(quotes)-1] = '"'
|
||
|
copy(quotes[1:len(quotes)-1], pickled)
|
||
|
return quotes, nil
|
||
|
}
|
||
|
|
||
|
func (s *OutboundGroupSession) UnmarshalJSON(data []byte) error {
|
||
|
if len(data) == 0 || data[0] != '"' || data[len(data)-1] != '"' {
|
||
|
return InputNotJSONString
|
||
|
}
|
||
|
if s == nil || s.int == nil {
|
||
|
*s = *NewBlankOutboundGroupSession()
|
||
|
}
|
||
|
return s.Unpickle(data[1:len(data)-1], pickleKey)
|
||
|
}
|
||
|
|
||
|
// createRandomLen returns the number of random bytes needed to create an
|
||
|
// Account.
|
||
|
func (s *OutboundGroupSession) createRandomLen() uint {
|
||
|
return uint(C.olm_init_outbound_group_session_random_length((*C.OlmOutboundGroupSession)(s.int)))
|
||
|
}
|
||
|
|
||
|
// encryptMsgLen returns the size of the next message in bytes for the given
|
||
|
// number of plain-text bytes.
|
||
|
func (s *OutboundGroupSession) encryptMsgLen(plainTextLen int) uint {
|
||
|
return uint(C.olm_group_encrypt_message_length((*C.OlmOutboundGroupSession)(s.int), C.size_t(plainTextLen)))
|
||
|
}
|
||
|
|
||
|
// Encrypt encrypts a message using the Session. Returns the encrypted message
|
||
|
// as base64.
|
||
|
func (s *OutboundGroupSession) Encrypt(plaintext []byte) []byte {
|
||
|
if len(plaintext) == 0 {
|
||
|
panic(EmptyInput)
|
||
|
}
|
||
|
message := make([]byte, s.encryptMsgLen(len(plaintext)))
|
||
|
r := C.olm_group_encrypt(
|
||
|
(*C.OlmOutboundGroupSession)(s.int),
|
||
|
(*C.uint8_t)(&plaintext[0]),
|
||
|
C.size_t(len(plaintext)),
|
||
|
(*C.uint8_t)(&message[0]),
|
||
|
C.size_t(len(message)))
|
||
|
if r == errorVal() {
|
||
|
panic(s.lastError())
|
||
|
}
|
||
|
return message[:r]
|
||
|
}
|
||
|
|
||
|
// sessionIdLen returns the number of bytes needed to store a session ID.
|
||
|
func (s *OutboundGroupSession) sessionIdLen() uint {
|
||
|
return uint(C.olm_outbound_group_session_id_length((*C.OlmOutboundGroupSession)(s.int)))
|
||
|
}
|
||
|
|
||
|
// ID returns a base64-encoded identifier for this session.
|
||
|
func (s *OutboundGroupSession) ID() id.SessionID {
|
||
|
sessionID := make([]byte, s.sessionIdLen())
|
||
|
r := C.olm_outbound_group_session_id(
|
||
|
(*C.OlmOutboundGroupSession)(s.int),
|
||
|
(*C.uint8_t)(&sessionID[0]),
|
||
|
C.size_t(len(sessionID)))
|
||
|
if r == errorVal() {
|
||
|
panic(s.lastError())
|
||
|
}
|
||
|
return id.SessionID(sessionID[:r])
|
||
|
}
|
||
|
|
||
|
// MessageIndex returns the message index for this session. Each message is
|
||
|
// sent with an increasing index; this returns the index for the next message.
|
||
|
func (s *OutboundGroupSession) MessageIndex() uint {
|
||
|
return uint(C.olm_outbound_group_session_message_index((*C.OlmOutboundGroupSession)(s.int)))
|
||
|
}
|
||
|
|
||
|
// sessionKeyLen returns the number of bytes needed to store a session key.
|
||
|
func (s *OutboundGroupSession) sessionKeyLen() uint {
|
||
|
return uint(C.olm_outbound_group_session_key_length((*C.OlmOutboundGroupSession)(s.int)))
|
||
|
}
|
||
|
|
||
|
// Key returns the base64-encoded current ratchet key for this session.
|
||
|
func (s *OutboundGroupSession) Key() string {
|
||
|
sessionKey := make([]byte, s.sessionKeyLen())
|
||
|
r := C.olm_outbound_group_session_key(
|
||
|
(*C.OlmOutboundGroupSession)(s.int),
|
||
|
(*C.uint8_t)(&sessionKey[0]),
|
||
|
C.size_t(len(sessionKey)))
|
||
|
if r == errorVal() {
|
||
|
panic(s.lastError())
|
||
|
}
|
||
|
return string(sessionKey[:r])
|
||
|
}
|