mirror of
https://github.com/mautrix/signal.git
synced 2026-08-22 12:04:54 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d5f3f457d | ||
|
|
77a6664bc5 |
||
|
|
771150ee4a |
||
|
|
4eaacd6c56 |
||
|
|
8c7333a033 | ||
|
|
979d48aeb1 | ||
|
|
9142786157 | ||
|
|
542e5a3284 | ||
|
|
4f4f00423c | ||
|
|
3ddb32dd23 |
||
|
|
6e57bf3956 |
||
|
|
f25f588bc4 |
||
|
|
bed128ab84 | ||
|
|
4bbfe7e2cc |
39 changed files with 6774 additions and 3448 deletions
|
|
@ -1,3 +1,10 @@
|
|||
# v26.08
|
||||
|
||||
* Updated libsignal to v0.100.0
|
||||
* Added support for handling own profile key changes.
|
||||
* Changed attachment bridging behavior to match Signal Desktop.
|
||||
* Fixed edited messages being bridged twice if certain race conditions occurred.
|
||||
|
||||
# v26.07
|
||||
|
||||
* Updated Docker image to Alpine 3.24.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ var m = mxmain.BridgeMain{
|
|||
Name: "mautrix-signal",
|
||||
URL: "https://github.com/mautrix/signal",
|
||||
Description: "A Matrix-Signal puppeting bridge.",
|
||||
Version: "26.07",
|
||||
Version: "26.08",
|
||||
SemCalVer: true,
|
||||
|
||||
Connector: &connector.SignalConnector{},
|
||||
|
|
|
|||
24
go.mod
24
go.mod
|
|
@ -2,7 +2,7 @@ module go.mau.fi/mautrix-signal
|
|||
|
||||
go 1.25.0
|
||||
|
||||
toolchain go1.26.5
|
||||
toolchain go1.26.6
|
||||
|
||||
tool go.mau.fi/util/cmd/maubuild
|
||||
|
||||
|
|
@ -14,14 +14,14 @@ require (
|
|||
github.com/rs/zerolog v1.35.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tidwall/gjson v1.19.0
|
||||
go.mau.fi/util v0.9.11
|
||||
golang.org/x/crypto v0.54.0
|
||||
golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597
|
||||
golang.org/x/net v0.57.0
|
||||
go.mau.fi/util v0.10.0
|
||||
golang.org/x/crypto v0.55.0
|
||||
golang.org/x/exp v0.0.0-20260813180055-c1d0aacb2297
|
||||
golang.org/x/net v0.58.0
|
||||
golang.org/x/sync v0.22.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
google.golang.org/protobuf v1.36.12
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
maunium.net/go/mautrix v0.29.0
|
||||
maunium.net/go/mautrix v0.30.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
@ -32,8 +32,8 @@ require (
|
|||
github.com/lib/pq v1.12.3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.48 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20260713124913-97594f28f5ca // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.49 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20260816044145-ed329add6b1b // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
|
|
@ -41,11 +41,11 @@ require (
|
|||
github.com/tidwall/match v1.2.0 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/yuin/goldmark v1.8.4 // indirect
|
||||
github.com/yuin/goldmark v1.8.5 // indirect
|
||||
go.mau.fi/zeroconfig v0.2.0 // indirect
|
||||
golang.org/x/mod v0.38.0 // indirect
|
||||
golang.org/x/mod v0.40.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/text v0.40.0 // indirect
|
||||
golang.org/x/text v0.41.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
maunium.net/go/mauflag v1.0.0 // indirect
|
||||
|
|
|
|||
44
go.sum
44
go.sum
|
|
@ -30,10 +30,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
|||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
|
||||
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
|
||||
github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs=
|
||||
github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
|
||||
github.com/petermattis/goid v0.0.0-20260713124913-97594f28f5ca h1:GHSUVE4yOgX4E7kTRzpxCPbCOYkd3Kj8Dgdod30OI1E=
|
||||
github.com/petermattis/goid v0.0.0-20260713124913-97594f28f5ca/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/mattn/go-sqlite3 v1.14.49 h1:B8jBHC3xhxZgxztrgruTuLucebnULQnx4W7cF7SAE9w=
|
||||
github.com/mattn/go-sqlite3 v1.14.49/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
|
||||
github.com/petermattis/goid v0.0.0-20260816044145-ed329add6b1b h1:sS7HLzwS+dO+gxATgQfeZDEdUZe2pKAB3nGoUwP5zU0=
|
||||
github.com/petermattis/goid v0.0.0-20260816044145-ed329add6b1b/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
|
@ -59,29 +59,29 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
|||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/yuin/goldmark v1.8.4 h1:oat/nd3U6NeQqFEL3xpEJq7d7c86NI+DbSNGAs4xnjA=
|
||||
github.com/yuin/goldmark v1.8.4/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
go.mau.fi/util v0.9.11 h1:Cus1Lu/t7d3OG6VF4aYWvlUUS0Q4O1/lcpPNJZ0jsw0=
|
||||
go.mau.fi/util v0.9.11/go.mod h1:xunp/oIQfFD68HHcNHfG0pOiHkvEtDhTweeIwKJ//+Q=
|
||||
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
|
||||
github.com/yuin/goldmark v1.8.5/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
go.mau.fi/util v0.10.0 h1:vH9IXZmfBKa96p47HxrVqEPkrj02zDJg3o4EF172+Lk=
|
||||
go.mau.fi/util v0.10.0/go.mod h1:uZwpm9sK4wO2Qqy+t6QoVq29szMsRxWXp9/BkQLG4xk=
|
||||
go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU=
|
||||
go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w=
|
||||
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||
golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597 h1:qLvzZeaANDgyVOA8pyHCOStGlXn0rseXma+GQjeuv2g=
|
||||
golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597/go.mod h1:EdfpwwqSu+0Li0mzskwHU6FWDV3t9Q+RZDo3QMUtL3Q=
|
||||
golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
|
||||
golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
|
||||
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
||||
golang.org/x/exp v0.0.0-20260813180055-c1d0aacb2297 h1:YXnL44eJ77R+ji4/ooy8UsXIhz+lbi2Qgdlc8iRN0gY=
|
||||
golang.org/x/exp v0.0.0-20260813180055-c1d0aacb2297/go.mod h1:Mkmymgv+uMpSQ/XxJ/7GpdrdYoqm3u72jEbpCLiJmNk=
|
||||
golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
|
||||
golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE=
|
||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
|
||||
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
|
||||
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
|
||||
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
|
@ -91,5 +91,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
||||
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
||||
maunium.net/go/mautrix v0.29.0 h1:OkcBJF1dvp+93EgahxMxOUZZOrGTYculI9IprvRIMOQ=
|
||||
maunium.net/go/mautrix v0.29.0/go.mod h1:LynuVr8N9nWsE1N4WAE+vItRACDB1pt9M3gN4SIBpeY=
|
||||
maunium.net/go/mautrix v0.30.0 h1:bad+q7w5tLqiHpr+oUxVI+8m8ePbV3AvoFKg2jQzPyo=
|
||||
maunium.net/go/mautrix v0.30.0/go.mod h1:bb0gjxbTFOqTaAYKGw5E7j9XROUR2Sl1Etm3IbmYXbo=
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ func supportedIfFFmpeg() event.CapabilitySupportLevel {
|
|||
}
|
||||
|
||||
func capID() string {
|
||||
base := "fi.mau.signal.capabilities.2026_07_14"
|
||||
base := "fi.mau.signal.capabilities.2026_07_22"
|
||||
if ffmpeg.Supported() {
|
||||
return base + "+ffmpeg"
|
||||
}
|
||||
|
|
@ -77,6 +77,7 @@ var signalCaps = &event.RoomFeatures{
|
|||
"image/jpeg": event.CapLevelFullySupported,
|
||||
"image/webp": event.CapLevelFullySupported,
|
||||
"image/bmp": event.CapLevelFullySupported,
|
||||
"image/avif": event.CapLevelFullySupported,
|
||||
},
|
||||
MaxWidth: 4096,
|
||||
MaxHeight: 4096,
|
||||
|
|
@ -98,6 +99,8 @@ var signalCaps = &event.RoomFeatures{
|
|||
MimeTypes: map[string]event.CapabilitySupportLevel{
|
||||
"audio/aac": event.CapLevelFullySupported,
|
||||
"audio/mpeg": event.CapLevelFullySupported,
|
||||
"audio/mp3": event.CapLevelFullySupported,
|
||||
"audio/flac": event.CapLevelFullySupported,
|
||||
},
|
||||
MaxSize: MaxFileSize,
|
||||
},
|
||||
|
|
@ -154,6 +157,11 @@ var signalCaps = &event.RoomFeatures{
|
|||
MaxTextLength: MaxTextLength, // TODO support arbitrary sized text messages with files
|
||||
LocationMessage: event.CapLevelPartialSupport,
|
||||
Poll: event.CapLevelFullySupported,
|
||||
PollEnd: event.CapLevelUnsupported,
|
||||
PollHiddenVotes: event.CapLevelUnsupported,
|
||||
PollDuplicateOptions: event.CapLevelFullySupported,
|
||||
PollMaxOptions: 10,
|
||||
PollOptionMaxLength: 100,
|
||||
Thread: event.CapLevelUnsupported,
|
||||
Reply: event.CapLevelFullySupported,
|
||||
Edit: event.CapLevelFullySupported,
|
||||
|
|
@ -237,5 +245,5 @@ func (s *SignalConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilities
|
|||
}
|
||||
|
||||
func (s *SignalConnector) GetBridgeInfoVersion() (info, capabilities int) {
|
||||
return 1, 9
|
||||
return 1, 11
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,12 +180,43 @@ func (s *SignalClient) HandleMatrixEdit(ctx context.Context, msg *bridgev2.Matri
|
|||
if err != nil {
|
||||
return bridgev2.WrapErrorInStatus(err).WithSendNotice(true)
|
||||
}
|
||||
prevID := msg.EditTarget.ID
|
||||
msg.EditTarget.ID = signalid.MakeMessageID(s.Client.Store.ACI, ts)
|
||||
msg.EditTarget.Metadata = &signalid.MessageMetadata{ContainsAttachments: len(converted.Attachments) > 0}
|
||||
msg.EditTarget.EditCount++
|
||||
if prevID != msg.EditTarget.ID {
|
||||
err = s.Main.Bridge.DB.Message.Update(ctx, msg.EditTarget)
|
||||
if err != nil {
|
||||
zerolog.Ctx(ctx).Err(err).Msg("Failed to save message after editing")
|
||||
} else {
|
||||
saveEditStub(ctx, s.Main.Bridge, prevID, msg.EditTarget)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveEditStub saves a placeholder message row pointing at the pre-edit ID of a message, such that
|
||||
// duplicate checks on incoming edits find it and are dropped. This is necessary because the first
|
||||
// time we see an edit it modifies the ID in place.
|
||||
func saveEditStub(ctx context.Context, bridge *bridgev2.Bridge, prevID networkid.MessageID, target *database.Message) {
|
||||
stub := &database.Message{
|
||||
ID: prevID,
|
||||
PartID: editStubPartID,
|
||||
Room: target.Room,
|
||||
SenderID: target.SenderID,
|
||||
SenderMXID: target.SenderMXID,
|
||||
Timestamp: target.Timestamp,
|
||||
}
|
||||
stub.SetFakeMXID()
|
||||
err := bridge.DB.Message.Insert(ctx, stub)
|
||||
if err != nil {
|
||||
zerolog.Ctx(ctx).Warn().Err(err).
|
||||
Str("prev_message_id", string(prevID)).
|
||||
Str("message_id", string(target.ID)).
|
||||
Msg("Failed to save stub row for pre-edit message ID")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SignalClient) PreHandleMatrixReaction(ctx context.Context, msg *bridgev2.MatrixReaction) (bridgev2.MatrixReactionPreResponse, error) {
|
||||
return bridgev2.MatrixReactionPreResponse{
|
||||
SenderID: signalid.MakeUserID(s.Client.Store.ACI),
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -358,20 +359,52 @@ func (evt *Bv2ChatEvent) ConvertMessage(ctx context.Context, portal *bridgev2.Po
|
|||
return converted, nil
|
||||
}
|
||||
|
||||
const editStubPartID networkid.PartID = "editstub"
|
||||
|
||||
func isEditStub(msg *database.Message) bool {
|
||||
return msg.PartID == editStubPartID
|
||||
}
|
||||
|
||||
// editStubMessage returns a non-bridged message part which is saved as a placeholder row pointing
|
||||
// at the pre-edit ID of a message, such that duplicate checks on incoming edits find it and are
|
||||
// dropped. This is necessary because the first time we see an edit it modifies the ID in place.
|
||||
func editStubMessage() *bridgev2.ConvertedMessage {
|
||||
return &bridgev2.ConvertedMessage{
|
||||
Parts: []*bridgev2.ConvertedMessagePart{{
|
||||
ID: editStubPartID,
|
||||
Type: event.EventMessage,
|
||||
Content: &event.MessageEventContent{},
|
||||
DontBridge: true,
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func (evt *Bv2ChatEvent) ConvertEdit(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, existing []*database.Message) (*bridgev2.ConvertedEdit, error) {
|
||||
editMsg, ok := evt.Event.(*signalpb.EditMessage)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("ConvertEdit() called for non-EditMessage event")
|
||||
}
|
||||
existing = slices.DeleteFunc(slices.Clone(existing), isEditStub)
|
||||
if len(existing) == 0 {
|
||||
return nil, fmt.Errorf("%w: edit target has already been edited", bridgev2.ErrIgnoringRemoteEvent)
|
||||
}
|
||||
// TODO tell converter about existing parts to avoid reupload?
|
||||
converted := evt.s.Main.MsgConv.ToMatrix(ctx, evt.s.Client, portal, evt.Info.Sender, intent, editMsg.GetDataMessage(), nil)
|
||||
// TODO can anything other than the text be edited?
|
||||
editPart := converted.Parts[len(converted.Parts)-1].ToEditPart(existing[len(existing)-1])
|
||||
prevID := editPart.Part.ID
|
||||
// Clone the database message struct to avoid mutating the ID.
|
||||
// The ID from the original struct is used for AddedParts (we specifically want the old ID for that)
|
||||
editPart.Part = ptr.Clone(editPart.Part)
|
||||
editPart.Part.EditCount++
|
||||
editPart.Part.ID = signalid.MakeMessageID(evt.Info.Sender, editMsg.GetDataMessage().GetTimestamp())
|
||||
return &bridgev2.ConvertedEdit{
|
||||
convertedEdit := &bridgev2.ConvertedEdit{
|
||||
ModifiedParts: []*bridgev2.ConvertedEditPart{editPart},
|
||||
}, nil
|
||||
}
|
||||
if prevID != editPart.Part.ID {
|
||||
convertedEdit.AddedParts = editStubMessage()
|
||||
}
|
||||
return convertedEdit, nil
|
||||
}
|
||||
|
||||
func (evt *Bv2ChatEvent) GetStreamOrder() int64 {
|
||||
|
|
|
|||
|
|
@ -18,9 +18,12 @@ package connector
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/google/uuid"
|
||||
"maunium.net/go/mautrix/bridgev2"
|
||||
"maunium.net/go/mautrix/bridgev2/database"
|
||||
|
|
@ -41,7 +44,7 @@ func (s *SignalConnector) GetLoginFlows() []bridgev2.LoginFlow {
|
|||
|
||||
func (s *SignalConnector) CreateLogin(ctx context.Context, user *bridgev2.User, flowID string) (bridgev2.LoginProcess, error) {
|
||||
if flowID != "qr" {
|
||||
return nil, fmt.Errorf("invalid login flow ID")
|
||||
return nil, bridgev2.ErrInvalidLoginFlowID
|
||||
}
|
||||
return &QRLogin{User: user, Main: s}, nil
|
||||
}
|
||||
|
|
@ -66,10 +69,78 @@ func (qr *QRLogin) Cancel() {
|
|||
|
||||
const (
|
||||
LoginStepQR = "fi.mau.signal.login.qr"
|
||||
LoginStepProcess = "fi.mau.signal.login.processing"
|
||||
LoginStepComplete = "fi.mau.signal.login.complete"
|
||||
)
|
||||
|
||||
const (
|
||||
qrRefreshInterval = 45 * time.Second
|
||||
maxQRRefreshes = 20
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLoginTimedOut = bridgev2.RespError{
|
||||
ErrCode: "FI.MAU.BRIDGE.LOGIN_TIMED_OUT",
|
||||
Err: "The QR code wasn't scanned in time, please start a new login",
|
||||
StatusCode: http.StatusGone,
|
||||
}
|
||||
ErrLoginCancelled = bridgev2.RespError{
|
||||
ErrCode: "FI.MAU.BRIDGE.LOGIN_CANCELLED",
|
||||
Err: "Login process was cancelled",
|
||||
StatusCode: http.StatusGone,
|
||||
}
|
||||
ErrDeviceLinkMissingCapability = bridgev2.RespError{
|
||||
ErrCode: "FI.MAU.SIGNAL.DEVICE_LINK_MISSING_CAPABILITY",
|
||||
Err: "Signal rejected linking because the bridge is missing a capability required by your account's other devices. Please try again later",
|
||||
StatusCode: http.StatusConflict,
|
||||
}
|
||||
ErrDeviceLimitReached = bridgev2.RespError{
|
||||
ErrCode: "FI.MAU.SIGNAL.DEVICE_LIMIT_REACHED",
|
||||
Err: "Your Signal account already has the maximum number of linked devices. Remove one in the Signal app and try again",
|
||||
StatusCode: http.StatusBadRequest,
|
||||
}
|
||||
ErrDeviceLinkCodeInvalid = bridgev2.RespError{
|
||||
ErrCode: "FI.MAU.SIGNAL.DEVICE_LINK_CODE_INVALID",
|
||||
Err: "The scanned QR code was invalid or already used, please start a new login",
|
||||
StatusCode: http.StatusForbidden,
|
||||
}
|
||||
ErrDeviceLinkRateLimited = bridgev2.RespError{
|
||||
ErrCode: "FI.MAU.SIGNAL.DEVICE_LINK_RATE_LIMITED",
|
||||
Err: "Signal rate-limited the linking attempt, please wait a few minutes and try again",
|
||||
StatusCode: http.StatusTooManyRequests,
|
||||
}
|
||||
ErrDeviceLinkRejected = bridgev2.RespError{
|
||||
ErrCode: "FI.MAU.SIGNAL.DEVICE_LINK_REJECTED",
|
||||
Err: "Signal rejected linking the device",
|
||||
StatusCode: http.StatusBadRequest,
|
||||
}
|
||||
)
|
||||
|
||||
// Statuses of PUT /v1/devices/link, per Signal-Server's DeviceController
|
||||
func wrapProvisioningError(err error) error {
|
||||
var linkErr signalmeow.DeviceLinkError
|
||||
if errors.As(err, &linkErr) {
|
||||
switch linkErr.StatusCode {
|
||||
case http.StatusConflict:
|
||||
return ErrDeviceLinkMissingCapability
|
||||
case http.StatusLengthRequired:
|
||||
return ErrDeviceLimitReached
|
||||
case http.StatusForbidden:
|
||||
return ErrDeviceLinkCodeInvalid
|
||||
case http.StatusTooManyRequests:
|
||||
return ErrDeviceLinkRateLimited
|
||||
default:
|
||||
if linkErr.Message != "" {
|
||||
return ErrDeviceLinkRejected.AppendMessage(" (HTTP %d: %s)", linkErr.StatusCode, linkErr.Message)
|
||||
}
|
||||
return ErrDeviceLinkRejected.AppendMessage(" (HTTP %d)", linkErr.StatusCode)
|
||||
}
|
||||
}
|
||||
if websocket.CloseStatus(err) == websocket.StatusGoingAway {
|
||||
return ErrLoginTimedOut
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (qr *QRLogin) Start(ctx context.Context) (*bridgev2.LoginStep, error) {
|
||||
log := qr.Main.Bridge.Log.With().
|
||||
Str("action", "login").
|
||||
|
|
@ -85,14 +156,16 @@ func (qr *QRLogin) Start(ctx context.Context) (*bridgev2.LoginStep, error) {
|
|||
select {
|
||||
case resp = <-qr.ProvChan:
|
||||
if resp.Err != nil {
|
||||
return nil, resp.Err
|
||||
return nil, wrapProvisioningError(resp.Err)
|
||||
} else if resp.State != signalmeow.StateProvisioningURLReceived {
|
||||
return nil, fmt.Errorf("unexpected state %v", resp.State)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
cancel()
|
||||
return nil, ctx.Err()
|
||||
// TODO separate timeout here?
|
||||
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
||||
return nil, ErrLoginTimedOut
|
||||
}
|
||||
return nil, ErrLoginCancelled
|
||||
}
|
||||
return &bridgev2.LoginStep{
|
||||
Type: bridgev2.LoginStepTypeDisplayAndWait,
|
||||
|
|
@ -114,7 +187,7 @@ func (qr *QRLogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) {
|
|||
case resp := <-qr.ProvChan:
|
||||
if resp.Err != nil {
|
||||
qr.cancelChan()
|
||||
return nil, resp.Err
|
||||
return nil, wrapProvisioningError(resp.Err)
|
||||
} else if resp.State != signalmeow.StateProvisioningDataReceived {
|
||||
qr.cancelChan()
|
||||
return nil, fmt.Errorf("unexpected state %v", resp.State)
|
||||
|
|
@ -126,17 +199,20 @@ func (qr *QRLogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) {
|
|||
|
||||
// Server will timeout the request after 60 seconds, but Signal Desktop opens
|
||||
// a new socket and gets a new QR code after 45 seconds. We should do the same.
|
||||
case <-time.After(45 * time.Second):
|
||||
case <-time.After(qrRefreshInterval):
|
||||
qr.cancelChan()
|
||||
qr.newQRCount++
|
||||
if qr.newQRCount >= 6 {
|
||||
return nil, fmt.Errorf("too many QR code refreshes")
|
||||
if qr.newQRCount >= maxQRRefreshes {
|
||||
return nil, ErrLoginTimedOut
|
||||
}
|
||||
return qr.Start(ctx)
|
||||
|
||||
case <-ctx.Done():
|
||||
qr.cancelChan()
|
||||
return nil, ctx.Err()
|
||||
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
||||
return nil, ErrLoginTimedOut
|
||||
}
|
||||
return nil, ErrLoginCancelled
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,19 +20,17 @@ package libsignalgo
|
|||
#include "./libsignal-ffi.h"
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
import "runtime"
|
||||
|
||||
type AccountEntropyPool string
|
||||
type SVRKey = fixedArray32
|
||||
|
||||
func (aep AccountEntropyPool) DeriveSVRKey() ([]byte, error) {
|
||||
var out [C.SignalSVR_KEY_LEN]byte
|
||||
var out SVRKey
|
||||
aepC, free := GoStringToCString(string(aep))
|
||||
defer free()
|
||||
signalFfiError := C.signal_account_entropy_pool_derive_svr_key(
|
||||
(*[C.SignalSVR_KEY_LEN]C.uint8_t)(unsafe.Pointer(&out)),
|
||||
out.cFixedArray(),
|
||||
aepC,
|
||||
)
|
||||
runtime.KeepAlive(aep)
|
||||
|
|
@ -43,11 +41,11 @@ func (aep AccountEntropyPool) DeriveSVRKey() ([]byte, error) {
|
|||
}
|
||||
|
||||
func (aep AccountEntropyPool) DeriveBackupKey() ([]byte, error) {
|
||||
var out [C.SignalBACKUP_KEY_LEN]byte
|
||||
var out BackupKey
|
||||
aepC, free := GoStringToCString(string(aep))
|
||||
defer free()
|
||||
signalFfiError := C.signal_account_entropy_pool_derive_backup_key(
|
||||
(*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(&out)),
|
||||
out.cFixedArray(),
|
||||
aepC,
|
||||
)
|
||||
runtime.KeepAlive(aep)
|
||||
|
|
|
|||
|
|
@ -24,15 +24,16 @@ package libsignalgo
|
|||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// type AuthCredential [C.SignalAUTH_CREDENTIAL_LEN]byte
|
||||
// type AuthCredentialResponse [C.SignalAUTH_CREDENTIAL_RESPONSE_LEN]byte
|
||||
type AuthCredentialWithPni [C.SignalAUTH_CREDENTIAL_WITH_PNI_LEN]byte
|
||||
type AuthCredentialWithPniResponse [C.SignalAUTH_CREDENTIAL_WITH_PNI_RESPONSE_LEN]byte
|
||||
// type AuthCredential [181]byte
|
||||
// type AuthCredentialResponse [361]byte
|
||||
const AuthCredentialWithPniLength = 265
|
||||
|
||||
type AuthCredentialWithPni [AuthCredentialWithPniLength]byte
|
||||
type AuthCredentialWithPniResponse [425]byte
|
||||
type AuthCredentialPresentation []byte
|
||||
|
||||
func (ac *AuthCredentialWithPni) Slice() []byte {
|
||||
|
|
@ -51,8 +52,8 @@ func ReceiveAuthCredentialWithPni(
|
|||
signalFfiError := C.signal_server_public_params_receive_auth_credential_with_pni_as_service_id(
|
||||
&c_result,
|
||||
C.SignalConstPointerServerPublicParams{serverPublicParams},
|
||||
NewACIServiceID(aci).CFixedBytes(),
|
||||
NewPNIServiceID(pni).CFixedBytes(),
|
||||
NewACIServiceID(aci).cConstFixedArray(),
|
||||
NewPNIServiceID(pni).cConstFixedArray(),
|
||||
C.uint64_t(redemptionTime),
|
||||
BytesToBuffer(authCredResponse[:]),
|
||||
)
|
||||
|
|
@ -60,8 +61,8 @@ func ReceiveAuthCredentialWithPni(
|
|||
return nil, wrapError(signalFfiError)
|
||||
}
|
||||
resultBytes := CopySignalOwnedBufferToBytes(c_result)
|
||||
if len(resultBytes) != C.SignalAUTH_CREDENTIAL_WITH_PNI_LEN {
|
||||
return nil, fmt.Errorf("invalid response length %d (expected %d)", len(resultBytes), C.SignalAUTH_CREDENTIAL_WITH_PNI_LEN)
|
||||
if len(resultBytes) != AuthCredentialWithPniLength {
|
||||
return nil, fmt.Errorf("invalid response length %d (expected %d)", len(resultBytes), AuthCredentialWithPniLength)
|
||||
}
|
||||
return (*AuthCredentialWithPni)(resultBytes), nil
|
||||
}
|
||||
|
|
@ -83,14 +84,12 @@ func CreateAuthCredentialWithPniPresentation(
|
|||
authCredWithPni AuthCredentialWithPni,
|
||||
) (*AuthCredentialPresentation, error) {
|
||||
var c_result C.SignalOwnedBuffer = C.SignalOwnedBuffer{}
|
||||
c_randomness := (*[C.SignalRANDOMNESS_LEN]C.uchar)(unsafe.Pointer(&randomness[0]))
|
||||
c_groupSecretParams := (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uchar)(unsafe.Pointer(&groupSecretParams[0]))
|
||||
|
||||
signalFfiError := C.signal_server_public_params_create_auth_credential_with_pni_presentation_deterministic(
|
||||
&c_result,
|
||||
C.SignalConstPointerServerPublicParams{serverPublicParams},
|
||||
c_randomness,
|
||||
c_groupSecretParams,
|
||||
randomness.cConstFixedArray(),
|
||||
groupSecretParams.cConstFixedArray(),
|
||||
BytesToBuffer(authCredWithPni[:]),
|
||||
)
|
||||
if signalFfiError != nil {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ import (
|
|||
"go.mau.fi/util/random"
|
||||
)
|
||||
|
||||
type BackupKey [C.SignalBACKUP_KEY_LEN]byte
|
||||
const BackupKeyLength = 32
|
||||
|
||||
type BackupKey [BackupKeyLength]byte
|
||||
|
||||
func (bk *BackupKey) Slice() []byte {
|
||||
if bk == nil {
|
||||
|
|
@ -38,17 +40,25 @@ func (bk *BackupKey) Slice() []byte {
|
|||
|
||||
const BackupIDLength = 16
|
||||
|
||||
type BackupID [BackupIDLength]byte
|
||||
type BackupMetadataKey [C.SignalLOCAL_BACKUP_METADATA_KEY_LEN]byte
|
||||
type BackupMediaID [C.SignalMEDIA_ID_LEN]byte
|
||||
type BackupMediaKey [C.SignalMEDIA_ENCRYPTION_KEY_LEN]byte
|
||||
type BackupID = fixedArray16
|
||||
type BackupMetadataKey = fixedArray32
|
||||
type BackupMediaID = fixedArray15
|
||||
type BackupMediaKey = fixedArray64
|
||||
|
||||
func (bk *BackupKey) cFixedArray() *C.SignalType_FixedArray32_uint8_t {
|
||||
return (*C.SignalType_FixedArray32_uint8_t)(unsafe.Pointer(bk))
|
||||
}
|
||||
|
||||
func (bk *BackupKey) cConstFixedArray() cFixedArray32Compat {
|
||||
return cFixedArray32Compat(bk.cFixedArray())
|
||||
}
|
||||
|
||||
func GenerateRandomBackupKey() *BackupKey {
|
||||
return (*BackupKey)(random.Bytes(C.SignalBACKUP_KEY_LEN))
|
||||
return (*BackupKey)(random.Bytes(BackupKeyLength))
|
||||
}
|
||||
|
||||
func BytesToBackupKey(bytes []byte) *BackupKey {
|
||||
if len(bytes) != C.SignalBACKUP_KEY_LEN {
|
||||
if len(bytes) != BackupKeyLength {
|
||||
return nil
|
||||
}
|
||||
return (*BackupKey)(bytes)
|
||||
|
|
@ -57,9 +67,9 @@ func BytesToBackupKey(bytes []byte) *BackupKey {
|
|||
func (bk *BackupKey) DeriveBackupID(aci ServiceID) (*BackupID, error) {
|
||||
var out BackupID
|
||||
signalFfiError := C.signal_backup_key_derive_backup_id(
|
||||
(*[BackupIDLength]C.uint8_t)(unsafe.Pointer(&out)),
|
||||
(*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(bk)),
|
||||
aci.CFixedBytes(),
|
||||
out.cFixedArray(),
|
||||
bk.cConstFixedArray(),
|
||||
aci.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(bk)
|
||||
if signalFfiError != nil {
|
||||
|
|
@ -72,8 +82,8 @@ func (bk *BackupKey) DeriveECKey(aci ServiceID) (*PrivateKey, error) {
|
|||
var out C.SignalMutPointerPrivateKey
|
||||
signalFfiError := C.signal_backup_key_derive_ec_key(
|
||||
&out,
|
||||
(*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(&bk)),
|
||||
aci.CFixedBytes(),
|
||||
bk.cConstFixedArray(),
|
||||
aci.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(bk)
|
||||
if signalFfiError != nil {
|
||||
|
|
@ -85,8 +95,8 @@ func (bk *BackupKey) DeriveECKey(aci ServiceID) (*PrivateKey, error) {
|
|||
func (bk *BackupKey) DeriveLocalBackupMetadataKey() (*BackupMetadataKey, error) {
|
||||
var out BackupMetadataKey
|
||||
signalFfiError := C.signal_backup_key_derive_local_backup_metadata_key(
|
||||
(*[C.SignalLOCAL_BACKUP_METADATA_KEY_LEN]C.uint8_t)(unsafe.Pointer(&out)),
|
||||
(*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(bk)),
|
||||
out.cFixedArray(),
|
||||
bk.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(bk)
|
||||
if signalFfiError != nil {
|
||||
|
|
@ -100,8 +110,8 @@ func (bk *BackupKey) DeriveMediaID(mediaName string) (*BackupMediaID, error) {
|
|||
mediaNameStr, mediaNameFree := GoStringToCString(mediaName)
|
||||
defer mediaNameFree()
|
||||
signalFfiError := C.signal_backup_key_derive_media_id(
|
||||
(*[C.SignalMEDIA_ID_LEN]C.uint8_t)(unsafe.Pointer(&out)),
|
||||
(*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(bk)),
|
||||
out.cFixedArray(),
|
||||
bk.cConstFixedArray(),
|
||||
mediaNameStr,
|
||||
)
|
||||
runtime.KeepAlive(bk)
|
||||
|
|
@ -114,9 +124,9 @@ func (bk *BackupKey) DeriveMediaID(mediaName string) (*BackupMediaID, error) {
|
|||
func (bk *BackupKey) DeriveMediaEncryptionKey(mediaID *BackupMediaID) (*BackupMediaKey, error) {
|
||||
var out BackupMediaKey
|
||||
signalFfiError := C.signal_backup_key_derive_media_encryption_key(
|
||||
(*[C.SignalMEDIA_ENCRYPTION_KEY_LEN]C.uint8_t)(unsafe.Pointer(&out)),
|
||||
(*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(bk)),
|
||||
(*[C.SignalMEDIA_ID_LEN]C.uint8_t)(unsafe.Pointer(mediaID)),
|
||||
out.cFixedArray(),
|
||||
bk.cConstFixedArray(),
|
||||
mediaID.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(bk)
|
||||
runtime.KeepAlive(mediaID)
|
||||
|
|
@ -129,9 +139,9 @@ func (bk *BackupKey) DeriveMediaEncryptionKey(mediaID *BackupMediaID) (*BackupMe
|
|||
func (bk *BackupKey) DeriveThumbnailTransitEncryptionKey(mediaID *BackupMediaID) (*BackupMediaKey, error) {
|
||||
var out BackupMediaKey
|
||||
signalFfiError := C.signal_backup_key_derive_thumbnail_transit_encryption_key(
|
||||
(*[C.SignalMEDIA_ENCRYPTION_KEY_LEN]C.uint8_t)(unsafe.Pointer(&out)),
|
||||
(*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(bk)),
|
||||
(*[C.SignalMEDIA_ID_LEN]C.uint8_t)(unsafe.Pointer(mediaID)),
|
||||
out.cFixedArray(),
|
||||
bk.cConstFixedArray(),
|
||||
mediaID.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(bk)
|
||||
runtime.KeepAlive(mediaID)
|
||||
|
|
|
|||
|
|
@ -18,19 +18,20 @@ package libsignalgo
|
|||
|
||||
/*
|
||||
#include "./libsignal-ffi.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
func GoStringToCString(str string) (C.SignalCStringPtr, func()) {
|
||||
cStr := C.CString(str)
|
||||
return cStr, func() {
|
||||
return C.SignalCStringPtr(unsafe.Pointer(cStr)), func() {
|
||||
C.free(unsafe.Pointer(cStr))
|
||||
}
|
||||
}
|
||||
|
||||
func CopyCStringToString(cString C.SignalCStringPtr) (s string) {
|
||||
s = C.GoString(cString)
|
||||
s = C.GoString((*C.char)(unsafe.Pointer(cString)))
|
||||
C.signal_free_string(cString)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
159
pkg/libsignalgo/fixedarray.go
Normal file
159
pkg/libsignalgo/fixedarray.go
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
// mautrix-signal - A Matrix-signal puppeting bridge.
|
||||
// Copyright (C) 2026 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 "unsafe"
|
||||
|
||||
type fixedArray15 [15]byte
|
||||
type fixedArray16 [16]byte
|
||||
type fixedArray17 [17]byte
|
||||
type fixedArray32 [32]byte
|
||||
type fixedArray64 [64]byte
|
||||
type fixedArray65 [65]byte
|
||||
type fixedArray97 [97]byte
|
||||
type fixedArray129 [129]byte
|
||||
type fixedArray153 [153]byte
|
||||
type fixedArray177 [177]byte
|
||||
type fixedArray289 [289]byte
|
||||
type fixedArray329 [329]byte
|
||||
type fixedArray409 [409]byte
|
||||
type fixedArray473 [473]byte
|
||||
type fixedArray497 [497]byte
|
||||
|
||||
func (a *fixedArray15) cFixedArray() *C.SignalType_FixedArray15_uint8_t {
|
||||
return (*C.SignalType_FixedArray15_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray15) cConstFixedArray() cFixedArray15Compat {
|
||||
return cFixedArray15Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray16) cFixedArray() *C.SignalType_FixedArray16_uint8_t {
|
||||
return (*C.SignalType_FixedArray16_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray16) cConstFixedArray() cFixedArray16Compat {
|
||||
return cFixedArray16Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray17) cFixedArray() *C.SignalType_FixedArray17_uint8_t {
|
||||
return (*C.SignalType_FixedArray17_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray17) cConstFixedArray() cFixedArray17Compat {
|
||||
return cFixedArray17Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray32) cFixedArray() *C.SignalType_FixedArray32_uint8_t {
|
||||
return (*C.SignalType_FixedArray32_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray32) cConstFixedArray() cFixedArray32Compat {
|
||||
return cFixedArray32Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray64) cFixedArray() *C.SignalType_FixedArray64_uint8_t {
|
||||
return (*C.SignalType_FixedArray64_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray64) cConstFixedArray() cFixedArray64Compat {
|
||||
return cFixedArray64Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray65) cFixedArray() *C.SignalType_FixedArray65_uint8_t {
|
||||
return (*C.SignalType_FixedArray65_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray65) cConstFixedArray() cFixedArray65Compat {
|
||||
return cFixedArray65Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray97) cFixedArray() *C.SignalType_FixedArray97_uint8_t {
|
||||
return (*C.SignalType_FixedArray97_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray97) cConstFixedArray() cFixedArray97Compat {
|
||||
return cFixedArray97Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray129) cFixedArray() *C.SignalType_FixedArray129_uint8_t {
|
||||
return (*C.SignalType_FixedArray129_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray129) cConstFixedArray() cFixedArray129Compat {
|
||||
return cFixedArray129Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray153) cFixedArray() *C.SignalType_FixedArray153_uint8_t {
|
||||
return (*C.SignalType_FixedArray153_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray153) cConstFixedArray() cFixedArray153Compat {
|
||||
return cFixedArray153Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray177) cFixedArray() *C.SignalType_FixedArray177_uint8_t {
|
||||
return (*C.SignalType_FixedArray177_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray177) cConstFixedArray() cFixedArray177Compat {
|
||||
return cFixedArray177Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray289) cFixedArray() *C.SignalType_FixedArray289_uint8_t {
|
||||
return (*C.SignalType_FixedArray289_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray289) cConstFixedArray() cFixedArray289Compat {
|
||||
return cFixedArray289Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray329) cFixedArray() *C.SignalType_FixedArray329_uint8_t {
|
||||
return (*C.SignalType_FixedArray329_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray329) cConstFixedArray() cFixedArray329Compat {
|
||||
return cFixedArray329Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray409) cFixedArray() *C.SignalType_FixedArray409_uint8_t {
|
||||
return (*C.SignalType_FixedArray409_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray409) cConstFixedArray() cFixedArray409Compat {
|
||||
return cFixedArray409Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray473) cFixedArray() *C.SignalType_FixedArray473_uint8_t {
|
||||
return (*C.SignalType_FixedArray473_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray473) cConstFixedArray() cFixedArray473Compat {
|
||||
return cFixedArray473Compat(a.cFixedArray())
|
||||
}
|
||||
|
||||
func (a *fixedArray497) cFixedArray() *C.SignalType_FixedArray497_uint8_t {
|
||||
return (*C.SignalType_FixedArray497_uint8_t)(unsafe.Pointer(a))
|
||||
}
|
||||
|
||||
func (a *fixedArray497) cConstFixedArray() cFixedArray497Compat {
|
||||
return cFixedArray497Compat(a.cFixedArray())
|
||||
}
|
||||
24
pkg/libsignalgo/fixedarray_clang.go
Normal file
24
pkg/libsignalgo/fixedarray_clang.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
//go:build darwin || android || ios || (windows && arm64)
|
||||
|
||||
package libsignalgo
|
||||
|
||||
/*
|
||||
#include "./libsignal-ffi.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type cFixedArray15Compat = *C.SignalType_FixedArray15_uint8_t
|
||||
type cFixedArray16Compat = *C.SignalType_FixedArray16_uint8_t
|
||||
type cFixedArray17Compat = *C.SignalType_FixedArray17_uint8_t
|
||||
type cFixedArray32Compat = *C.SignalType_FixedArray32_uint8_t
|
||||
type cFixedArray64Compat = *C.SignalType_FixedArray64_uint8_t
|
||||
type cFixedArray65Compat = *C.SignalType_FixedArray65_uint8_t
|
||||
type cFixedArray97Compat = *C.SignalType_FixedArray97_uint8_t
|
||||
type cFixedArray129Compat = *C.SignalType_FixedArray129_uint8_t
|
||||
type cFixedArray153Compat = *C.SignalType_FixedArray153_uint8_t
|
||||
type cFixedArray177Compat = *C.SignalType_FixedArray177_uint8_t
|
||||
type cFixedArray289Compat = *C.SignalType_FixedArray289_uint8_t
|
||||
type cFixedArray329Compat = *C.SignalType_FixedArray329_uint8_t
|
||||
type cFixedArray409Compat = *C.SignalType_FixedArray409_uint8_t
|
||||
type cFixedArray473Compat = *C.SignalType_FixedArray473_uint8_t
|
||||
type cFixedArray497Compat = *C.SignalType_FixedArray497_uint8_t
|
||||
26
pkg/libsignalgo/fixedarray_gcc.go
Normal file
26
pkg/libsignalgo/fixedarray_gcc.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
//go:build !(darwin || android || ios || (windows && arm64))
|
||||
|
||||
package libsignalgo
|
||||
|
||||
/*
|
||||
#include "./libsignal-ffi.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// Hack for https://github.com/golang/go/issues/7270
|
||||
// The clang version is more correct, but doesn't work with gcc.
|
||||
type cFixedArray15Compat = *[15]C.uint8_t
|
||||
type cFixedArray16Compat = *[16]C.uint8_t
|
||||
type cFixedArray17Compat = *[17]C.uint8_t
|
||||
type cFixedArray32Compat = *[32]C.uint8_t
|
||||
type cFixedArray64Compat = *[64]C.uint8_t
|
||||
type cFixedArray65Compat = *[65]C.uint8_t
|
||||
type cFixedArray97Compat = *[97]C.uint8_t
|
||||
type cFixedArray129Compat = *[129]C.uint8_t
|
||||
type cFixedArray153Compat = *[153]C.uint8_t
|
||||
type cFixedArray177Compat = *[177]C.uint8_t
|
||||
type cFixedArray289Compat = *[289]C.uint8_t
|
||||
type cFixedArray329Compat = *[329]C.uint8_t
|
||||
type cFixedArray409Compat = *[409]C.uint8_t
|
||||
type cFixedArray473Compat = *[473]C.uint8_t
|
||||
type cFixedArray497Compat = *[497]C.uint8_t
|
||||
|
|
@ -31,7 +31,9 @@ import (
|
|||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Randomness [C.SignalRANDOMNESS_LEN]byte
|
||||
const RandomnessLength = 32
|
||||
|
||||
type Randomness = fixedArray32
|
||||
|
||||
func GenerateRandomness() Randomness {
|
||||
var randomness Randomness
|
||||
|
|
@ -42,14 +44,39 @@ func GenerateRandomness() Randomness {
|
|||
return randomness
|
||||
}
|
||||
|
||||
const GroupMasterKeyLength = C.SignalGROUP_MASTER_KEY_LEN
|
||||
const GroupIdentifierLength = C.SignalGROUP_IDENTIFIER_LEN
|
||||
const GroupMasterKeyLength = 32
|
||||
const GroupIdentifierLength = 32
|
||||
const GroupSecretParamsLength = 289
|
||||
|
||||
type GroupMasterKey [GroupMasterKeyLength]byte
|
||||
type GroupSecretParams [C.SignalGROUP_SECRET_PARAMS_LEN]byte
|
||||
type GroupPublicParams [C.SignalGROUP_PUBLIC_PARAMS_LEN]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 ""
|
||||
|
|
@ -57,8 +84,8 @@ func (gid *GroupIdentifier) String() string {
|
|||
return base64.StdEncoding.EncodeToString(gid[:])
|
||||
}
|
||||
|
||||
type UUIDCiphertext [C.SignalUUID_CIPHERTEXT_LEN]byte
|
||||
type ProfileKeyCiphertext [C.SignalPROFILE_KEY_CIPHERTEXT_LEN]byte
|
||||
type UUIDCiphertext = fixedArray65
|
||||
type ProfileKeyCiphertext = fixedArray65
|
||||
|
||||
func GenerateGroupSecretParams() (GroupSecretParams, error) {
|
||||
return GenerateGroupSecretParamsWithRandomness(GenerateRandomness())
|
||||
|
|
@ -81,51 +108,43 @@ func (gmk GroupMasterKey) SecretParams() (GroupSecretParams, error) {
|
|||
}
|
||||
|
||||
func GenerateGroupSecretParamsWithRandomness(randomness Randomness) (GroupSecretParams, error) {
|
||||
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)))
|
||||
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)
|
||||
}
|
||||
var groupSecretParams GroupSecretParams
|
||||
copy(groupSecretParams[:], C.GoBytes(unsafe.Pointer(¶ms), C.int(C.SignalGROUP_SECRET_PARAMS_LEN)))
|
||||
return groupSecretParams, nil
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func DeriveGroupSecretParamsFromMasterKey(groupMasterKey GroupMasterKey) (GroupSecretParams, error) {
|
||||
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)))
|
||||
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)
|
||||
}
|
||||
var groupSecretParams GroupSecretParams
|
||||
copy(groupSecretParams[:], C.GoBytes(unsafe.Pointer(¶ms), C.int(C.SignalGROUP_SECRET_PARAMS_LEN)))
|
||||
return groupSecretParams, nil
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func (gsp *GroupSecretParams) GetPublicParams() (*GroupPublicParams, error) {
|
||||
var publicParams [C.SignalGROUP_PUBLIC_PARAMS_LEN]C.uchar
|
||||
signalFfiError := C.signal_group_secret_params_get_public_params(&publicParams, (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)))
|
||||
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)
|
||||
}
|
||||
var groupPublicParams GroupPublicParams
|
||||
copy(groupPublicParams[:], C.GoBytes(unsafe.Pointer(&publicParams), C.int(C.SignalGROUP_PUBLIC_PARAMS_LEN)))
|
||||
return &groupPublicParams, nil
|
||||
return &publicParams, nil
|
||||
}
|
||||
|
||||
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)))
|
||||
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)
|
||||
}
|
||||
var result GroupIdentifier
|
||||
copy(result[:], C.GoBytes(unsafe.Pointer(&groupIdentifier), C.int(C.SignalGROUP_IDENTIFIER_LEN)))
|
||||
return &result, nil
|
||||
return &groupIdentifier, nil
|
||||
}
|
||||
|
||||
func (gsp *GroupSecretParams) DecryptBlobWithPadding(blob []byte) ([]byte, error) {
|
||||
|
|
@ -133,7 +152,7 @@ func (gsp *GroupSecretParams) DecryptBlobWithPadding(blob []byte) ([]byte, error
|
|||
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)),
|
||||
gsp.cConstFixedArray(),
|
||||
borrowedBlob,
|
||||
)
|
||||
runtime.KeepAlive(gsp)
|
||||
|
|
@ -149,8 +168,8 @@ func (gsp *GroupSecretParams) EncryptBlobWithPaddingDeterministic(randomness Ran
|
|||
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)),
|
||||
gsp.cConstFixedArray(),
|
||||
randomness.cConstFixedArray(),
|
||||
borrowedPlaintext,
|
||||
(C.uint32_t)(padding_len),
|
||||
)
|
||||
|
|
@ -165,11 +184,11 @@ func (gsp *GroupSecretParams) EncryptBlobWithPaddingDeterministic(randomness Ran
|
|||
}
|
||||
|
||||
func (gsp *GroupSecretParams) DecryptServiceID(ciphertextServiceID UUIDCiphertext) (ServiceID, error) {
|
||||
u := C.SignalServiceIdFixedWidthBinaryBytes{}
|
||||
var serviceIDBytes ServiceIDFixedBytes
|
||||
signalFfiError := C.signal_group_secret_params_decrypt_service_id(
|
||||
&u,
|
||||
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
|
||||
(*[C.SignalUUID_CIPHERTEXT_LEN]C.uint8_t)(unsafe.Pointer(&ciphertextServiceID)),
|
||||
serviceIDBytes.cFixedArray(),
|
||||
gsp.cConstFixedArray(),
|
||||
ciphertextServiceID.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(gsp)
|
||||
runtime.KeepAlive(ciphertextServiceID)
|
||||
|
|
@ -177,33 +196,31 @@ func (gsp *GroupSecretParams) DecryptServiceID(ciphertextServiceID UUIDCiphertex
|
|||
return EmptyServiceID, wrapError(signalFfiError)
|
||||
}
|
||||
|
||||
serviceID := ServiceIDFromCFixedBytes(&u)
|
||||
serviceID := ServiceIDFromCFixedBytes(serviceIDBytes.cFixedArray())
|
||||
return serviceID, nil
|
||||
}
|
||||
|
||||
func (gsp *GroupSecretParams) EncryptServiceID(serviceID ServiceID) (*UUIDCiphertext, error) {
|
||||
var cipherTextServiceID [C.SignalUUID_CIPHERTEXT_LEN]C.uchar
|
||||
var cipherTextServiceID UUIDCiphertext
|
||||
signalFfiError := C.signal_group_secret_params_encrypt_service_id(
|
||||
&cipherTextServiceID,
|
||||
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
|
||||
serviceID.CFixedBytes(),
|
||||
cipherTextServiceID.cFixedArray(),
|
||||
gsp.cConstFixedArray(),
|
||||
serviceID.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(gsp)
|
||||
if signalFfiError != nil {
|
||||
return nil, wrapError(signalFfiError)
|
||||
}
|
||||
var result UUIDCiphertext
|
||||
copy(result[:], C.GoBytes(unsafe.Pointer(&cipherTextServiceID), C.int(C.SignalUUID_CIPHERTEXT_LEN)))
|
||||
return &result, nil
|
||||
return &cipherTextServiceID, nil
|
||||
}
|
||||
|
||||
func (gsp *GroupSecretParams) DecryptProfileKey(ciphertextProfileKey ProfileKeyCiphertext, u uuid.UUID) (*ProfileKey, error) {
|
||||
profileKey := [C.SignalPROFILE_KEY_LEN]C.uchar{}
|
||||
var profileKey ProfileKey
|
||||
signalFfiError := C.signal_group_secret_params_decrypt_profile_key(
|
||||
&profileKey,
|
||||
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
|
||||
(*[C.SignalPROFILE_KEY_CIPHERTEXT_LEN]C.uint8_t)(unsafe.Pointer(&ciphertextProfileKey)),
|
||||
NewACIServiceID(u).CFixedBytes(),
|
||||
profileKey.cFixedArray(),
|
||||
gsp.cConstFixedArray(),
|
||||
ciphertextProfileKey.cConstFixedArray(),
|
||||
NewACIServiceID(u).cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(gsp)
|
||||
runtime.KeepAlive(ciphertextProfileKey)
|
||||
|
|
@ -211,27 +228,23 @@ func (gsp *GroupSecretParams) DecryptProfileKey(ciphertextProfileKey ProfileKeyC
|
|||
if signalFfiError != nil {
|
||||
return nil, wrapError(signalFfiError)
|
||||
}
|
||||
var result ProfileKey
|
||||
copy(result[:], C.GoBytes(unsafe.Pointer(&profileKey), C.int(C.SignalPROFILE_KEY_LEN)))
|
||||
return &result, nil
|
||||
return &profileKey, nil
|
||||
}
|
||||
|
||||
func (gsp *GroupSecretParams) EncryptProfileKey(profileKey ProfileKey, u uuid.UUID) (*ProfileKeyCiphertext, error) {
|
||||
ciphertextProfileKey := [C.SignalPROFILE_KEY_CIPHERTEXT_LEN]C.uchar{}
|
||||
var ciphertextProfileKey ProfileKeyCiphertext
|
||||
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)),
|
||||
NewACIServiceID(u).CFixedBytes(),
|
||||
ciphertextProfileKey.cFixedArray(),
|
||||
gsp.cConstFixedArray(),
|
||||
profileKey.cConstFixedArray(),
|
||||
NewACIServiceID(u).cConstFixedArray(),
|
||||
)
|
||||
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
|
||||
return &ciphertextProfileKey, nil
|
||||
}
|
||||
|
||||
func (gsp *GroupSecretParams) CreateExpiringProfileKeyCredentialPresentation(spp *ServerPublicParams, credential ExpiringProfileKeyCredential) (*ProfileKeyCredentialPresentation, error) {
|
||||
|
|
@ -240,9 +253,9 @@ func (gsp *GroupSecretParams) CreateExpiringProfileKeyCredentialPresentation(spp
|
|||
signalFfiError := C.signal_server_public_params_create_expiring_profile_key_credential_presentation_deterministic(
|
||||
&out,
|
||||
C.SignalConstPointerServerPublicParams{spp},
|
||||
(*[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)),
|
||||
randomness.cConstFixedArray(),
|
||||
gsp.cConstFixedArray(),
|
||||
credential.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(gsp)
|
||||
runtime.KeepAlive(credential)
|
||||
|
|
@ -256,16 +269,14 @@ func (gsp *GroupSecretParams) CreateExpiringProfileKeyCredentialPresentation(spp
|
|||
}
|
||||
|
||||
func (gsp *GroupSecretParams) GetMasterKey() (*GroupMasterKey, error) {
|
||||
masterKeyBytes := [C.SignalGROUP_MASTER_KEY_LEN]C.uchar{}
|
||||
var masterKey GroupMasterKey
|
||||
signalFfiError := C.signal_group_secret_params_get_master_key(
|
||||
&masterKeyBytes,
|
||||
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uchar)(unsafe.Pointer(gsp)),
|
||||
masterKey.cFixedArray(),
|
||||
gsp.cConstFixedArray(),
|
||||
)
|
||||
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
|
||||
return &masterKey, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import (
|
|||
"encoding/base64"
|
||||
"runtime"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type GroupSendFullToken []byte
|
||||
|
|
@ -91,7 +90,7 @@ func (gse GroupSendEndorsement) ToToken(groupSecretParams *GroupSecretParams) (G
|
|||
signalFfiError := C.signal_group_send_endorsement_to_token(
|
||||
&token,
|
||||
BytesToBuffer(gse),
|
||||
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(groupSecretParams)),
|
||||
groupSecretParams.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(gse)
|
||||
runtime.KeepAlive(groupSecretParams)
|
||||
|
|
@ -180,17 +179,17 @@ func (gser GroupSendEndorsementsResponse) ReceiveWithServiceIDs(
|
|||
groupMembers []ServiceID, localUser ServiceID, params *GroupSecretParams, spp *ServerPublicParams,
|
||||
) (GroupSendEndorsement, map[ServiceID]GroupSendEndorsement, error) {
|
||||
var out C.SignalBytestringArray = C.SignalBytestringArray{}
|
||||
concatenatedMembers := make([]byte, len(groupMembers)*17)
|
||||
concatenatedMembers := make([]byte, len(groupMembers)*ServiceIDFixedBytesLength)
|
||||
for i, member := range groupMembers {
|
||||
copy(concatenatedMembers[i*17:(i+1)*17], member.FixedBytes()[:])
|
||||
copy(concatenatedMembers[i*ServiceIDFixedBytesLength:(i+1)*ServiceIDFixedBytesLength], member.FixedBytes()[:])
|
||||
}
|
||||
signalFfiError := C.signal_group_send_endorsements_response_receive_and_combine_with_service_ids(
|
||||
&out,
|
||||
BytesToBuffer(gser),
|
||||
BytesToBuffer(concatenatedMembers),
|
||||
localUser.CFixedBytes(),
|
||||
localUser.cConstFixedArray(),
|
||||
C.uint64_t(time.Now().Unix()),
|
||||
(*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(params)),
|
||||
params.cConstFixedArray(),
|
||||
C.SignalConstPointerServerPublicParams{spp},
|
||||
)
|
||||
runtime.KeepAlive(gser)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 4e9bd5d7feca2f61cf2ce0c7d99a60eb8b9e0102
|
||||
Subproject commit 857c4dca03537dc5e395a5e1eda6bf18f59c3601
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -33,7 +33,7 @@ var ffiLogger Logger
|
|||
|
||||
//export signal_log_callback
|
||||
func signal_log_callback(ctx unsafe.Pointer, level C.SignalLogLevel, file C.SignalCStringPtr, line C.uint32_t, message C.SignalCStringPtr) {
|
||||
ffiLogger.Log(LogLevel(int(level)), C.GoString(file), uint(line), C.GoString(message))
|
||||
ffiLogger.Log(LogLevel(int(level)), CopyCStringToString(file), uint(line), CopyCStringToString(message))
|
||||
}
|
||||
|
||||
//export signal_log_flush_callback
|
||||
|
|
|
|||
|
|
@ -71,10 +71,10 @@ func Decrypt(ctx context.Context, message *Message, fromAddress, localAddress *A
|
|||
|
||||
type Message struct {
|
||||
nc noCopy
|
||||
ptr *C.SignalMessage
|
||||
ptr *C.SignalSignalMessage
|
||||
}
|
||||
|
||||
func wrapMessage(ptr *C.SignalMessage) *Message {
|
||||
func wrapMessage(ptr *C.SignalSignalMessage) *Message {
|
||||
message := &Message{ptr: ptr}
|
||||
runtime.SetFinalizer(message, (*Message).Destroy)
|
||||
return message
|
||||
|
|
|
|||
|
|
@ -20,16 +20,17 @@ package libsignalgo
|
|||
#include "./libsignal-ffi.h"
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
import "runtime"
|
||||
|
||||
type MessageBackupKey struct {
|
||||
nc noCopy
|
||||
ptr *C.SignalMessageBackupKey
|
||||
}
|
||||
|
||||
const MessageBackupKeyBytesLength = 32
|
||||
|
||||
type messageBackupKeyBytes = fixedArray32
|
||||
|
||||
func wrapMessageBackupKey(ptr *C.SignalMessageBackupKey) *MessageBackupKey {
|
||||
backupKey := &MessageBackupKey{ptr: ptr}
|
||||
runtime.SetFinalizer(backupKey, (*MessageBackupKey).Destroy)
|
||||
|
|
@ -43,7 +44,7 @@ func MessageBackupKeyFromAccountEntropyPool(aep AccountEntropyPool, aci ServiceI
|
|||
signalFfiError := C.signal_message_backup_key_from_account_entropy_pool(
|
||||
&bk,
|
||||
aepC,
|
||||
aci.CFixedBytes(),
|
||||
aci.cConstFixedArray(),
|
||||
nil, // TODO what's a forward secrecy token?
|
||||
)
|
||||
runtime.KeepAlive(aep)
|
||||
|
|
@ -57,8 +58,8 @@ func MessageBackupKeyFromBackupKeyAndID(backupKey *BackupKey, backupID *BackupID
|
|||
var bk C.SignalMutPointerMessageBackupKey
|
||||
signalFfiError := C.signal_message_backup_key_from_backup_key_and_backup_id(
|
||||
&bk,
|
||||
(*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(backupKey)),
|
||||
(*[BackupIDLength]C.uint8_t)(unsafe.Pointer(backupID)),
|
||||
backupKey.cConstFixedArray(),
|
||||
backupID.cConstFixedArray(),
|
||||
nil, // TODO what's a forward secrecy token?
|
||||
)
|
||||
runtime.KeepAlive(backupKey)
|
||||
|
|
@ -82,26 +83,26 @@ func (bk *MessageBackupKey) Destroy() error {
|
|||
return wrapError(C.signal_message_backup_key_destroy(bk.mutPtr()))
|
||||
}
|
||||
|
||||
func (bk *MessageBackupKey) GetHMACKey() ([32]byte, error) {
|
||||
var out [32]byte
|
||||
func (bk *MessageBackupKey) GetHMACKey() ([MessageBackupKeyBytesLength]byte, error) {
|
||||
var out messageBackupKeyBytes
|
||||
signalFfiError := C.signal_message_backup_key_get_hmac_key(
|
||||
(*[32]C.uint8_t)(unsafe.Pointer(&out)),
|
||||
out.cFixedArray(),
|
||||
bk.constPtr(),
|
||||
)
|
||||
if signalFfiError != nil {
|
||||
return out, wrapError(signalFfiError)
|
||||
return [MessageBackupKeyBytesLength]byte(out), wrapError(signalFfiError)
|
||||
}
|
||||
return out, nil
|
||||
return [MessageBackupKeyBytesLength]byte(out), nil
|
||||
}
|
||||
|
||||
func (bk *MessageBackupKey) GetAESKey() ([32]byte, error) {
|
||||
var out [32]byte
|
||||
func (bk *MessageBackupKey) GetAESKey() ([MessageBackupKeyBytesLength]byte, error) {
|
||||
var out messageBackupKeyBytes
|
||||
signalFfiError := C.signal_message_backup_key_get_aes_key(
|
||||
(*[32]C.uint8_t)(unsafe.Pointer(&out)),
|
||||
out.cFixedArray(),
|
||||
bk.constPtr(),
|
||||
)
|
||||
if signalFfiError != nil {
|
||||
return out, wrapError(signalFfiError)
|
||||
return [MessageBackupKeyBytesLength]byte(out), wrapError(signalFfiError)
|
||||
}
|
||||
return out, nil
|
||||
return [MessageBackupKeyBytesLength]byte(out), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,12 +32,38 @@ import (
|
|||
"go.mau.fi/util/random"
|
||||
)
|
||||
|
||||
const ProfileKeyLength = C.SignalPROFILE_KEY_LEN
|
||||
const ProfileKeyLength = 32
|
||||
const AccessKeyLength = 16
|
||||
const ProfileKeyVersionLength = 64
|
||||
|
||||
type ProfileKey [ProfileKeyLength]byte
|
||||
type ProfileKeyCommitment [C.SignalPROFILE_KEY_COMMITMENT_LEN]byte
|
||||
type ProfileKeyVersion [C.SignalPROFILE_KEY_VERSION_ENCODED_LEN]byte
|
||||
type AccessKey [C.SignalACCESS_KEY_LEN]byte
|
||||
type ProfileKeyCommitment = fixedArray97
|
||||
type ProfileKeyVersion [ProfileKeyVersionLength]byte
|
||||
type AccessKey [AccessKeyLength]byte
|
||||
|
||||
func (pk *ProfileKey) cFixedArray() *C.SignalType_FixedArray32_uint8_t {
|
||||
return (*C.SignalType_FixedArray32_uint8_t)(unsafe.Pointer(pk))
|
||||
}
|
||||
|
||||
func (pk *ProfileKey) cConstFixedArray() cFixedArray32Compat {
|
||||
return cFixedArray32Compat(pk.cFixedArray())
|
||||
}
|
||||
|
||||
func (pkv *ProfileKeyVersion) cFixedArray() *C.SignalType_FixedArray64_uint8_t {
|
||||
return (*C.SignalType_FixedArray64_uint8_t)(unsafe.Pointer(pkv))
|
||||
}
|
||||
|
||||
func (pkv *ProfileKeyVersion) cConstFixedArray() cFixedArray64Compat {
|
||||
return cFixedArray64Compat(pkv.cFixedArray())
|
||||
}
|
||||
|
||||
func (ak *AccessKey) cFixedArray() *C.SignalType_FixedArray16_uint8_t {
|
||||
return (*C.SignalType_FixedArray16_uint8_t)(unsafe.Pointer(ak))
|
||||
}
|
||||
|
||||
func (ak *AccessKey) cConstFixedArray() cFixedArray16Compat {
|
||||
return cFixedArray16Compat(ak.cFixedArray())
|
||||
}
|
||||
|
||||
func DeserializeProfileKey(bytes []byte) (*ProfileKey, error) {
|
||||
if len(bytes) == 0 {
|
||||
|
|
@ -73,7 +99,7 @@ func (ak *AccessKey) Xor(other *AccessKey) *AccessKey {
|
|||
return ak
|
||||
}
|
||||
var result AccessKey
|
||||
for i := 0; i < C.SignalACCESS_KEY_LEN; i++ {
|
||||
for i := 0; i < AccessKeyLength; i++ {
|
||||
result[i] = ak[i] ^ other[i]
|
||||
}
|
||||
return &result
|
||||
|
|
@ -84,13 +110,12 @@ func (ak *AccessKey) String() string {
|
|||
}
|
||||
|
||||
func (pk *ProfileKey) GetCommitment(u uuid.UUID) (*ProfileKeyCommitment, error) {
|
||||
c_result := [C.SignalPROFILE_KEY_COMMITMENT_LEN]C.uchar{}
|
||||
c_profileKey := (*[C.SignalPROFILE_KEY_LEN]C.uchar)(unsafe.Pointer(pk))
|
||||
c_uuid := NewACIServiceID(u).CFixedBytes()
|
||||
var result ProfileKeyCommitment
|
||||
c_uuid := NewACIServiceID(u).cConstFixedArray()
|
||||
|
||||
signalFfiError := C.signal_profile_key_get_commitment(
|
||||
&c_result,
|
||||
c_profileKey,
|
||||
result.cFixedArray(),
|
||||
pk.cConstFixedArray(),
|
||||
c_uuid,
|
||||
)
|
||||
runtime.KeepAlive(pk)
|
||||
|
|
@ -100,19 +125,16 @@ func (pk *ProfileKey) GetCommitment(u uuid.UUID) (*ProfileKeyCommitment, error)
|
|||
return nil, wrapError(signalFfiError)
|
||||
}
|
||||
|
||||
var result ProfileKeyCommitment
|
||||
copy(result[:], C.GoBytes(unsafe.Pointer(&c_result), C.int(C.SignalPROFILE_KEY_COMMITMENT_LEN)))
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (pk *ProfileKey) GetProfileKeyVersion(u uuid.UUID) (*ProfileKeyVersion, error) {
|
||||
c_result := [C.SignalPROFILE_KEY_VERSION_ENCODED_LEN]C.uchar{}
|
||||
c_profileKey := (*[C.SignalPROFILE_KEY_LEN]C.uchar)(unsafe.Pointer(pk))
|
||||
c_uuid := NewACIServiceID(u).CFixedBytes()
|
||||
var result ProfileKeyVersion
|
||||
c_uuid := NewACIServiceID(u).cConstFixedArray()
|
||||
|
||||
signalFfiError := C.signal_profile_key_get_profile_key_version(
|
||||
&c_result,
|
||||
c_profileKey,
|
||||
result.cFixedArray(),
|
||||
pk.cConstFixedArray(),
|
||||
c_uuid,
|
||||
)
|
||||
runtime.KeepAlive(pk)
|
||||
|
|
@ -122,18 +144,15 @@ func (pk *ProfileKey) GetProfileKeyVersion(u uuid.UUID) (*ProfileKeyVersion, err
|
|||
return nil, wrapError(signalFfiError)
|
||||
}
|
||||
|
||||
var result ProfileKeyVersion
|
||||
copy(result[:], C.GoBytes(unsafe.Pointer(&c_result), C.int(C.SignalPROFILE_KEY_VERSION_ENCODED_LEN)))
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (pk *ProfileKey) DeriveAccessKey() (*AccessKey, error) {
|
||||
c_result := [C.SignalACCESS_KEY_LEN]C.uchar{}
|
||||
c_profileKey := (*[C.SignalPROFILE_KEY_LEN]C.uchar)(unsafe.Pointer(pk))
|
||||
var result AccessKey
|
||||
|
||||
signalFfiError := C.signal_profile_key_derive_access_key(
|
||||
&c_result,
|
||||
c_profileKey,
|
||||
result.cFixedArray(),
|
||||
pk.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(pk)
|
||||
|
||||
|
|
@ -141,31 +160,35 @@ func (pk *ProfileKey) DeriveAccessKey() (*AccessKey, error) {
|
|||
return nil, wrapError(signalFfiError)
|
||||
}
|
||||
|
||||
var result AccessKey
|
||||
copy(result[:], C.GoBytes(unsafe.Pointer(&c_result), C.int(C.SignalACCESS_KEY_LEN)))
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
type ProfileKeyCredentialRequestContext [C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_CONTEXT_LEN]byte
|
||||
type ProfileKeyCredentialRequest [C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_LEN]byte
|
||||
type ProfileKeyCredentialRequestContext [473]byte
|
||||
type ProfileKeyCredentialRequest = fixedArray329
|
||||
type ProfileKeyCredentialResponse []byte
|
||||
type ProfileKeyCredentialPresentation []byte
|
||||
type ExpiringProfileKeyCredential [C.SignalEXPIRING_PROFILE_KEY_CREDENTIAL_LEN]byte
|
||||
type ExpiringProfileKeyCredentialResponse [C.SignalEXPIRING_PROFILE_KEY_CREDENTIAL_RESPONSE_LEN]byte
|
||||
type ExpiringProfileKeyCredential = fixedArray153
|
||||
type ExpiringProfileKeyCredentialResponse = fixedArray497
|
||||
|
||||
func (p *ProfileKeyCredentialRequestContext) cFixedArray() *C.SignalType_FixedArray473_uint8_t {
|
||||
return (*C.SignalType_FixedArray473_uint8_t)(unsafe.Pointer(p))
|
||||
}
|
||||
|
||||
func (p *ProfileKeyCredentialRequestContext) cConstFixedArray() cFixedArray473Compat {
|
||||
return cFixedArray473Compat(p.cFixedArray())
|
||||
}
|
||||
|
||||
func CreateProfileKeyCredentialRequestContext(serverPublicParams *ServerPublicParams, u uuid.UUID, profileKey ProfileKey) (*ProfileKeyCredentialRequestContext, error) {
|
||||
c_result := [C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_CONTEXT_LEN]C.uchar{}
|
||||
randBytes := [32]byte(random.Bytes(32))
|
||||
c_random := (*[32]C.uchar)(unsafe.Pointer(&randBytes[0]))
|
||||
c_profileKey := (*[C.SignalPROFILE_KEY_LEN]C.uchar)(unsafe.Pointer(&profileKey[0]))
|
||||
c_uuid := NewACIServiceID(u).CFixedBytes()
|
||||
var result ProfileKeyCredentialRequestContext
|
||||
randBytes := Randomness(random.Bytes(RandomnessLength))
|
||||
c_uuid := NewACIServiceID(u).cConstFixedArray()
|
||||
|
||||
signalFfiError := C.signal_server_public_params_create_profile_key_credential_request_context_deterministic(
|
||||
&c_result,
|
||||
result.cFixedArray(),
|
||||
C.SignalConstPointerServerPublicParams{serverPublicParams},
|
||||
c_random,
|
||||
randBytes.cConstFixedArray(),
|
||||
c_uuid,
|
||||
c_profileKey,
|
||||
profileKey.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(u)
|
||||
runtime.KeepAlive(profileKey)
|
||||
|
|
@ -173,23 +196,20 @@ func CreateProfileKeyCredentialRequestContext(serverPublicParams *ServerPublicPa
|
|||
if signalFfiError != nil {
|
||||
return nil, wrapError(signalFfiError)
|
||||
}
|
||||
result := ProfileKeyCredentialRequestContext(C.GoBytes(unsafe.Pointer(&c_result), C.int(C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_CONTEXT_LEN)))
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (p *ProfileKeyCredentialRequestContext) ProfileKeyCredentialRequestContextGetRequest() (*ProfileKeyCredentialRequest, error) {
|
||||
c_result := [C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_LEN]C.uchar{}
|
||||
c_context := (*[C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_CONTEXT_LEN]C.uchar)(unsafe.Pointer(p))
|
||||
var result ProfileKeyCredentialRequest
|
||||
|
||||
signalFfiError := C.signal_profile_key_credential_request_context_get_request(
|
||||
&c_result,
|
||||
c_context,
|
||||
result.cFixedArray(),
|
||||
p.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(p)
|
||||
if signalFfiError != nil {
|
||||
return nil, wrapError(signalFfiError)
|
||||
}
|
||||
result := ProfileKeyCredentialRequest(C.GoBytes(unsafe.Pointer(&c_result), C.int(C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_LEN)))
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
|
|
@ -205,12 +225,12 @@ func NewExpiringProfileKeyCredentialResponse(b []byte) (*ExpiringProfileKeyCrede
|
|||
}
|
||||
|
||||
func ReceiveExpiringProfileKeyCredential(spp *ServerPublicParams, requestContext *ProfileKeyCredentialRequestContext, response *ExpiringProfileKeyCredentialResponse, currentTimeInSeconds uint64) (*ExpiringProfileKeyCredential, error) {
|
||||
c_credential := [C.SignalEXPIRING_PROFILE_KEY_CREDENTIAL_LEN]C.uchar{}
|
||||
var credential ExpiringProfileKeyCredential
|
||||
signalFfiError := C.signal_server_public_params_receive_expiring_profile_key_credential(
|
||||
&c_credential,
|
||||
credential.cFixedArray(),
|
||||
C.SignalConstPointerServerPublicParams{spp},
|
||||
(*[C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_CONTEXT_LEN]C.uchar)(unsafe.Pointer(requestContext)),
|
||||
(*[C.SignalEXPIRING_PROFILE_KEY_CREDENTIAL_RESPONSE_LEN]C.uchar)(unsafe.Pointer(response)),
|
||||
requestContext.cConstFixedArray(),
|
||||
response.cConstFixedArray(),
|
||||
(C.uint64_t)(currentTimeInSeconds),
|
||||
)
|
||||
runtime.KeepAlive(requestContext)
|
||||
|
|
@ -219,8 +239,6 @@ func ReceiveExpiringProfileKeyCredential(spp *ServerPublicParams, requestContext
|
|||
if signalFfiError != nil {
|
||||
return nil, wrapError(signalFfiError)
|
||||
}
|
||||
credential := ExpiringProfileKeyCredential{}
|
||||
copy(credential[:], C.GoBytes(unsafe.Pointer(&c_credential), C.int(C.SignalEXPIRING_PROFILE_KEY_CREDENTIAL_LEN)))
|
||||
return &credential, nil
|
||||
}
|
||||
|
||||
|
|
@ -231,25 +249,21 @@ func (a ProfileKeyCredentialPresentation) CheckValidContents() error {
|
|||
}
|
||||
|
||||
func (a ProfileKeyCredentialPresentation) UUIDCiphertext() (UUIDCiphertext, error) {
|
||||
out := [C.SignalUUID_CIPHERTEXT_LEN]C.uchar{}
|
||||
signalFfiError := C.signal_profile_key_credential_presentation_get_uuid_ciphertext(&out, BytesToBuffer(a))
|
||||
var out UUIDCiphertext
|
||||
signalFfiError := C.signal_profile_key_credential_presentation_get_uuid_ciphertext(out.cFixedArray(), BytesToBuffer(a))
|
||||
runtime.KeepAlive(a)
|
||||
if signalFfiError != nil {
|
||||
return UUIDCiphertext{}, wrapError(signalFfiError)
|
||||
}
|
||||
var result UUIDCiphertext
|
||||
copy(result[:], C.GoBytes(unsafe.Pointer(&out), C.int(C.SignalUUID_CIPHERTEXT_LEN)))
|
||||
return result, nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (a ProfileKeyCredentialPresentation) ProfileKeyCiphertext() (ProfileKeyCiphertext, error) {
|
||||
out := [C.SignalPROFILE_KEY_CIPHERTEXT_LEN]C.uchar{}
|
||||
signalFfiError := C.signal_profile_key_credential_presentation_get_profile_key_ciphertext(&out, BytesToBuffer(a))
|
||||
var out ProfileKeyCiphertext
|
||||
signalFfiError := C.signal_profile_key_credential_presentation_get_profile_key_ciphertext(out.cFixedArray(), BytesToBuffer(a))
|
||||
runtime.KeepAlive(a)
|
||||
if signalFfiError != nil {
|
||||
return ProfileKeyCiphertext{}, wrapError(signalFfiError)
|
||||
}
|
||||
var result ProfileKeyCiphertext
|
||||
copy(result[:], C.GoBytes(unsafe.Pointer(&out), C.int(C.SignalPROFILE_KEY_CIPHERTEXT_LEN)))
|
||||
return result, nil
|
||||
return out, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,15 +24,16 @@ import "C"
|
|||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type ServerPublicParams = C.SignalServerPublicParams
|
||||
type NotarySignature [C.SignalSIGNATURE_LEN]byte
|
||||
type NotarySignature = fixedArray64
|
||||
|
||||
const ServerPublicParamsLength = 673
|
||||
|
||||
func DeserializeServerPublicParams(params []byte) (*ServerPublicParams, error) {
|
||||
if len(params) != C.SignalSERVER_PUBLIC_PARAMS_LEN {
|
||||
return nil, fmt.Errorf("invalid server public params length: %d (expected %d)", len(params), int(C.SignalSERVER_PUBLIC_PARAMS_LEN))
|
||||
if len(params) != ServerPublicParamsLength {
|
||||
return nil, fmt.Errorf("invalid server public params length: %d (expected %d)", len(params), ServerPublicParamsLength)
|
||||
}
|
||||
var out C.SignalMutPointerServerPublicParams
|
||||
signalFfiError := C.signal_server_public_params_deserialize(&out, BytesToBuffer(params[:]))
|
||||
|
|
@ -47,11 +48,10 @@ func ServerPublicParamsVerifySignature(
|
|||
messageBytes []byte,
|
||||
NotarySignature NotarySignature,
|
||||
) error {
|
||||
c_notarySignature := (*[C.SignalSIGNATURE_LEN]C.uint8_t)(unsafe.Pointer(&NotarySignature[0]))
|
||||
signalFfiError := C.signal_server_public_params_verify_signature(
|
||||
C.SignalConstPointerServerPublicParams{serverPublicParams},
|
||||
BytesToBuffer(messageBytes),
|
||||
c_notarySignature,
|
||||
NotarySignature.cConstFixedArray(),
|
||||
)
|
||||
runtime.KeepAlive(messageBytes)
|
||||
return wrapError(signalFfiError)
|
||||
|
|
|
|||
|
|
@ -133,7 +133,10 @@ func (s ServiceID) MarshalZerologObject(e *zerolog.Event) {
|
|||
e.Stringer("uuid", s.UUID)
|
||||
}
|
||||
|
||||
type ServiceIDFixedBytes [17]byte
|
||||
const ServiceIDUUIDLength = 16
|
||||
const ServiceIDFixedBytesLength = 17
|
||||
|
||||
type ServiceIDFixedBytes = fixedArray17
|
||||
|
||||
func (s ServiceID) FixedBytes() *ServiceIDFixedBytes {
|
||||
var result ServiceIDFixedBytes
|
||||
|
|
@ -162,18 +165,18 @@ func ServiceIDFromString(val string) (ServiceID, error) {
|
|||
}
|
||||
|
||||
func ServiceIDFromBytes(bytes []byte) (ServiceID, error) {
|
||||
if len(bytes) == 16 {
|
||||
if len(bytes) == ServiceIDUUIDLength {
|
||||
return NewACIServiceID(uuid.UUID(bytes)), nil
|
||||
} else if len(bytes) == 17 {
|
||||
} else if len(bytes) == ServiceIDFixedBytesLength {
|
||||
return ServiceID{
|
||||
Type: ServiceIDType(bytes[0]),
|
||||
UUID: uuid.UUID(bytes[1:]),
|
||||
}, nil
|
||||
}
|
||||
return EmptyServiceID, fmt.Errorf("invalid ServiceID byte length: %d (expected 16 or 17)", len(bytes))
|
||||
return EmptyServiceID, fmt.Errorf("invalid ServiceID byte length: %d (expected %d or %d)", len(bytes), ServiceIDUUIDLength, ServiceIDFixedBytesLength)
|
||||
}
|
||||
|
||||
func ServiceIDFromCFixedBytes(serviceID *C.SignalServiceIdFixedWidthBinaryBytes) ServiceID {
|
||||
func ServiceIDFromCFixedBytes(serviceID *C.SignalType_FixedArray17_uint8_t) ServiceID {
|
||||
var id ServiceID
|
||||
fixedBytes := (*ServiceIDFixedBytes)(unsafe.Pointer(serviceID))
|
||||
id.Type = ServiceIDType(fixedBytes[0])
|
||||
|
|
@ -181,6 +184,10 @@ func ServiceIDFromCFixedBytes(serviceID *C.SignalServiceIdFixedWidthBinaryBytes)
|
|||
return id
|
||||
}
|
||||
|
||||
func (s ServiceID) CFixedBytes() cPNIType {
|
||||
return cPNIType(unsafe.Pointer(s.FixedBytes()))
|
||||
func (s ServiceID) cFixedArray() *C.SignalType_FixedArray17_uint8_t {
|
||||
return s.FixedBytes().cFixedArray()
|
||||
}
|
||||
|
||||
func (s ServiceID) cConstFixedArray() cFixedArray17Compat {
|
||||
return cFixedArray17Compat(s.cFixedArray())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
//go:build darwin || android || ios || (windows && arm64)
|
||||
|
||||
package libsignalgo
|
||||
|
||||
/*
|
||||
#include "./libsignal-ffi.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type cPNIType = *C.SignalServiceIdFixedWidthBinaryBytes
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
//go:build !(darwin || android || ios || (windows && arm64))
|
||||
|
||||
package libsignalgo
|
||||
|
||||
/*
|
||||
#include "./libsignal-ffi.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// Hack for https://github.com/golang/go/issues/7270
|
||||
// The clang version is more correct, but doesn't work with gcc
|
||||
|
||||
type cPNIType = *[17]C.uint8_t
|
||||
|
|
@ -105,7 +105,6 @@ func (sr *SessionRecord) HasCurrentState() (bool, error) {
|
|||
signalFfiError := C.signal_session_record_has_usable_sender_chain(
|
||||
&result,
|
||||
sr.constPtr(),
|
||||
C.double(0.0),
|
||||
C.uint64_t(time.Now().Unix()),
|
||||
)
|
||||
runtime.KeepAlive(sr)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
#!/bin/sh
|
||||
cd /data
|
||||
export RUSTFLAGS="-Ctarget-feature=-crt-static" RUSTC_WRAPPER=""
|
||||
apk add --no-cache git make cmake protobuf-dev musl-dev g++ clang-dev cbindgen
|
||||
apk add --no-cache git make cmake protobuf-dev musl-dev g++ clang-dev
|
||||
cd libsignal
|
||||
cargo build -p libsignal-ffi --release
|
||||
cbindgen --profile release rust/bridge/ffi -o libsignal-ffi.h
|
||||
cd ..
|
||||
mv libsignal/target/release/libsignal_ffi.a .
|
||||
mv libsignal/libsignal-ffi.h .
|
||||
cp libsignal/swift/Sources/SignalFfi/signal_ffi.h libsignal-ffi.h
|
||||
chown 1000:1000 libsignal_ffi.a libsignal-ffi.h version.go
|
||||
|
|
|
|||
|
|
@ -28,14 +28,11 @@ echo "const Version = \"$(git describe --tags --always)\"" >> ../version.go
|
|||
# Build libsignal
|
||||
cargo build -p libsignal-ffi --release
|
||||
|
||||
# Regenerate the header file
|
||||
cbindgen --profile release rust/bridge/ffi -o libsignal-ffi.h
|
||||
|
||||
# Navigate back to the original directory
|
||||
cd "$ORIGINAL_DIR"
|
||||
|
||||
# Copy files from the libsignal directory
|
||||
cp "${LIBSIGNAL_DIRECTORY}/target/release/libsignal_ffi.a" .
|
||||
cp "${LIBSIGNAL_DIRECTORY}/libsignal-ffi.h" .
|
||||
cp "${LIBSIGNAL_DIRECTORY}/swift/Sources/SignalFfi/signal_ffi.h" libsignal-ffi.h
|
||||
|
||||
echo "Files copied successfully."
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
package libsignalgo
|
||||
|
||||
const Version = "v0.97.2"
|
||||
const Version = "v0.100.0"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
|
@ -569,16 +570,21 @@ func (mc *MessageConverter) reuploadAttachment(ctx context.Context, att *signalp
|
|||
content.Info.Blurhash = att.GetBlurHash()
|
||||
content.Info.AnoaBlurhash = att.GetBlurHash()
|
||||
}
|
||||
switch strings.Split(content.Info.MimeType, "/")[0] {
|
||||
case "image":
|
||||
plainMime, _, _ := mime.ParseMediaType(content.Info.MimeType)
|
||||
// Supported mime types from https://github.com/signalapp/Signal-Desktop/blob/main/ts/util/GoogleChrome.std.ts
|
||||
switch plainMime {
|
||||
case "image/avif", "image/bmp", "image/gif", "image/jpeg", "image/webp", "image/x-xbitmap",
|
||||
"image/vnd.microsoft.icon", "image/ico", "image/icon", "image/x-icon", "image/png", "image/apng":
|
||||
content.MsgType = event.MsgImage
|
||||
case "video":
|
||||
case "video/mp4", "video/ogg", "video/webm":
|
||||
content.MsgType = event.MsgVideo
|
||||
case "audio":
|
||||
content.MsgType = event.MsgAudio
|
||||
default:
|
||||
if strings.HasPrefix(plainMime, "audio/") && !strings.HasSuffix(plainMime, "aiff") {
|
||||
content.MsgType = event.MsgAudio
|
||||
} else {
|
||||
content.MsgType = event.MsgFile
|
||||
}
|
||||
}
|
||||
var extra map[string]any
|
||||
if att.GetFlags()&uint32(signalpb.AttachmentPointer_GIF) != 0 {
|
||||
content.Info.MauGIF = true
|
||||
|
|
|
|||
|
|
@ -111,22 +111,13 @@ func TestParse_HTML(t *testing.T) {
|
|||
{
|
||||
name: "List",
|
||||
in: "<ul><li>woof</li><li><strong>meow</strong></li><li><pre><code>hmm\nmeow</code></pre></li><li><blockquote>meow<br><h1>meow</h1></blockquote></li></ul>",
|
||||
out: "* woof\n* meow\n* hmm\n meow\n* > meow\n > \n > # meow",
|
||||
out: "* woof\n* meow\n* ```\n hmm\n meow\n ```\n* > meow\n > \n > # meow",
|
||||
ent: signalfmt.BodyRangeList{{
|
||||
Start: 9,
|
||||
Length: 4,
|
||||
Value: signalfmt.StyleBold,
|
||||
}, {
|
||||
Start: 16,
|
||||
Length: 3,
|
||||
Value: signalfmt.StyleMonospace,
|
||||
}, {
|
||||
// FIXME optimally this would be a single range with the previous one so the indent is also monospace
|
||||
Start: 22,
|
||||
Length: 4,
|
||||
Value: signalfmt.StyleMonospace,
|
||||
}, {
|
||||
Start: 45,
|
||||
Start: 57,
|
||||
Length: 6,
|
||||
Value: signalfmt.StyleBold,
|
||||
}},
|
||||
|
|
@ -134,21 +125,13 @@ func TestParse_HTML(t *testing.T) {
|
|||
{
|
||||
name: "OrderedList",
|
||||
in: "<ol start=9><li>woof</li><li><strong>meow</strong></li><li><pre><code>hmm\nmeow</code></pre></li><li><blockquote>meow<br><h1>meow</h1></blockquote></li></ol>",
|
||||
out: "9. woof\n10. meow\n11. hmm\n meow\n12. > meow\n > \n > # meow",
|
||||
out: "9. woof\n10. meow\n11. ```\n hmm\n meow\n ```\n12. > meow\n > \n > # meow",
|
||||
ent: signalfmt.BodyRangeList{{
|
||||
Start: 13,
|
||||
Length: 4,
|
||||
Value: signalfmt.StyleBold,
|
||||
}, {
|
||||
Start: 22,
|
||||
Length: 3,
|
||||
Value: signalfmt.StyleMonospace,
|
||||
}, {
|
||||
Start: 30,
|
||||
Length: 4,
|
||||
Value: signalfmt.StyleMonospace,
|
||||
}, {
|
||||
Start: 59,
|
||||
Start: 75,
|
||||
Length: 6,
|
||||
Value: signalfmt.StyleBold,
|
||||
}},
|
||||
|
|
|
|||
|
|
@ -404,17 +404,17 @@ func (parser *HTMLParser) tagToString(node *html.Node, ctx Context) *EntityStrin
|
|||
return NewEntityString("---")
|
||||
case "pre":
|
||||
var preStr *EntityString
|
||||
//var language string
|
||||
var language string
|
||||
if node.FirstChild != nil && node.FirstChild.Type == html.ElementNode && node.FirstChild.Data == "code" {
|
||||
//class := parser.getAttribute(node.FirstChild, "class")
|
||||
//if strings.HasPrefix(class, "language-") {
|
||||
// language = class[len("language-"):]
|
||||
//}
|
||||
class := parser.getAttribute(node.FirstChild, "class")
|
||||
if strings.HasPrefix(class, "language-") {
|
||||
language = class[len("language-"):]
|
||||
}
|
||||
preStr = parser.nodeToString(node.FirstChild.FirstChild, ctx.WithWhitespace())
|
||||
} else {
|
||||
preStr = parser.nodeToString(node.FirstChild, ctx.WithWhitespace())
|
||||
}
|
||||
return preStr.Format(signalfmt.StyleMonospace)
|
||||
return NewEntityString(fmt.Sprintf("```%s\n", language)).Append(preStr).AppendString("\n```")
|
||||
default:
|
||||
return parser.nodeToTagAwareString(node.FirstChild, ctx)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,15 @@ type ProvisioningResponse struct {
|
|||
Err error
|
||||
}
|
||||
|
||||
type DeviceLinkError struct {
|
||||
StatusCode int
|
||||
Message string
|
||||
}
|
||||
|
||||
func (dle DeviceLinkError) Error() string {
|
||||
return fmt.Sprintf("non-200 status code (%d) from devices response: %s", dle.StatusCode, dle.Message)
|
||||
}
|
||||
|
||||
func PerformProvisioning(ctx context.Context, deviceStore store.DeviceStore, deviceName string, allowBackup bool) chan ProvisioningResponse {
|
||||
log := zerolog.Ctx(ctx).With().Str("action", "perform provisioning").Logger()
|
||||
c := make(chan ProvisioningResponse, 4)
|
||||
|
|
@ -441,9 +450,9 @@ func confirmDevice(
|
|||
return nil, fmt.Errorf("failed to read from websocket after devices call: %w", err)
|
||||
}
|
||||
|
||||
status := int(*receivedMsg.Response.Status)
|
||||
status := int(receivedMsg.GetResponse().GetStatus())
|
||||
if status < 200 || status >= 300 {
|
||||
return nil, fmt.Errorf("non-200 status code (%d) from devices response: %s", status, *receivedMsg.Response.Message)
|
||||
return nil, DeviceLinkError{StatusCode: status, Message: receivedMsg.GetResponse().GetMessage()}
|
||||
}
|
||||
|
||||
// unmarshal JSON response into ConfirmDeviceResponse
|
||||
|
|
|
|||
|
|
@ -137,6 +137,12 @@ func (cli *Client) processStorageInTxn(ctx context.Context, update *StorageUpdat
|
|||
case *signalpb.StorageRecord_Account:
|
||||
log.Trace().Any("account_record", data.Account).Msg("Found account record")
|
||||
cli.Store.AccountRecord = data.Account
|
||||
if len(data.Account.ProfileKey) == libsignalgo.ProfileKeyLength {
|
||||
err := cli.Store.RecipientStore.StoreProfileKey(ctx, cli.Store.ACI, libsignalgo.ProfileKey(data.Account.ProfileKey))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to store own profile key: %w", err)
|
||||
}
|
||||
}
|
||||
err := cli.Store.DeviceStore.PutDevice(ctx, &cli.Store.DeviceData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save device after receiving account record: %w", err)
|
||||
|
|
|
|||
|
|
@ -58,8 +58,7 @@ func deleteExtraPrekeys(ctx context.Context, db *dbutil.Database, selectQuery, d
|
|||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
Table.Register(-1, 16, 13, "Remove extra prekeys", dbutil.TxnModeOn, func(ctx context.Context, db *dbutil.Database) error {
|
||||
var upgradeV16 = dbutil.WrapUpgrade(-1, 16, 13, "Remove extra prekeys", dbutil.TxnModeOn, func(ctx context.Context, db *dbutil.Database) error {
|
||||
err := deleteExtraPrekeys(ctx, db, `
|
||||
SELECT account_id, service_id, COUNT(*), MAX(key_id) FROM signalmeow_pre_keys WHERE is_signed=false GROUP BY 1, 2
|
||||
`, `
|
||||
|
|
@ -77,5 +76,4 @@ func init() {
|
|||
return fmt.Errorf("failed to process kyber: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ import (
|
|||
"go.mau.fi/util/dbutil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Table.Register(-1, 20, 13, "Add missing columns for backup chat table", dbutil.TxnModeOn, func(ctx context.Context, db *dbutil.Database) (err error) {
|
||||
var upgradeV20 = dbutil.WrapUpgrade(-1, 20, 13, "Add missing columns for backup chat table", dbutil.TxnModeOn, func(ctx context.Context, db *dbutil.Database) (err error) {
|
||||
var exists bool
|
||||
if exists, err = db.ColumnExists(ctx, "signalmeow_backup_chat", "latest_message_id"); err == nil && !exists {
|
||||
_, err = db.Exec(ctx, `
|
||||
|
|
@ -32,5 +31,4 @@ func init() {
|
|||
`)
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ import (
|
|||
"go.mau.fi/util/dbutil"
|
||||
)
|
||||
|
||||
var Table dbutil.UpgradeTable
|
||||
|
||||
//go:embed *.sql
|
||||
var rawUpgrades embed.FS
|
||||
|
||||
func init() {
|
||||
Table.RegisterFS(rawUpgrades)
|
||||
}
|
||||
var Table = dbutil.BuildUpgradeTable().
|
||||
WithFS(rawUpgrades).
|
||||
With(upgradeV16).
|
||||
With(upgradeV20).
|
||||
Finish()
|
||||
|
|
|
|||
Loading…
Reference in a new issue