Watch
1
0
Fork
You've already forked mautrix-signal
0
mirror of https://github.com/mautrix/signal.git synced 2026-09-16 15:52:04 -04:00

Compare commits

..
167 changed files with 5826 additions and 46400 deletions

View file

@ -11,14 +11,14 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
go-version: ["1.26", "1.27"] go-version: ["1.25", "1.26"]
name: Lint ${{ matrix.go-version == '1.27' && '(latest)' || '(old)' }} name: Lint ${{ matrix.go-version == '1.26' && '(latest)' || '(old)' }}
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v7 uses: actions/setup-go@v6
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
cache: true cache: true
@ -40,14 +40,14 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
go-version: ["1.26", "1.27"] go-version: ["1.25", "1.26"]
name: Test ${{ matrix.go-version == '1.27' && '(latest)' || '(old)' }} name: Test ${{ matrix.go-version == '1.26' && '(latest)' || '(old)' }}
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v7 uses: actions/setup-go@v6
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
cache: true cache: true

View file

@ -1,19 +1,3 @@
# v26.09
* Bumped minimum Go version to 1.26.
* Updated libsignal to v0.102.2.
* Fixed handling messages with invalid values in formatting body ranges.
* Fixed websocket request retrying not working correctly if the first attempt
times out.
* Fixed another potential race condition with edited message bridging.
# 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 # v26.07
* Updated Docker image to Alpine 3.24. * Updated Docker image to Alpine 3.24.

View file

@ -1 +0,0 @@
See <https://docs.mau.fi/bridges/general/contributing.html>

View file

@ -37,7 +37,7 @@ var m = mxmain.BridgeMain{
Name: "mautrix-signal", Name: "mautrix-signal",
URL: "https://github.com/mautrix/signal", URL: "https://github.com/mautrix/signal",
Description: "A Matrix-Signal puppeting bridge.", Description: "A Matrix-Signal puppeting bridge.",
Version: "26.09", Version: "26.07",
SemCalVer: true, SemCalVer: true,
Connector: &connector.SignalConnector{}, Connector: &connector.SignalConnector{},

36
go.mod
View file

@ -1,8 +1,8 @@
module go.mau.fi/mautrix-signal module go.mau.fi/mautrix-signal
go 1.26.0 go 1.25.0
toolchain go1.27.1 toolchain go1.26.5
tool go.mau.fi/util/cmd/maubuild tool go.mau.fi/util/cmd/maubuild
@ -12,40 +12,40 @@ require (
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/mattn/go-pointer v0.0.1 github.com/mattn/go-pointer v0.0.1
github.com/rs/zerolog v1.35.1 github.com/rs/zerolog v1.35.1
github.com/stretchr/testify v1.12.0 github.com/stretchr/testify v1.11.1
github.com/tidwall/gjson v1.19.0 github.com/tidwall/gjson v1.19.0
go.mau.fi/util v0.10.1 go.mau.fi/util v0.9.11
golang.org/x/crypto v0.57.0 golang.org/x/crypto v0.54.0
golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597
golang.org/x/net v0.59.0 golang.org/x/net v0.57.0
golang.org/x/sync v0.23.0 golang.org/x/sync v0.22.0
google.golang.org/grpc v1.83.2 google.golang.org/protobuf v1.36.11
google.golang.org/protobuf v1.36.12
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
maunium.net/go/mautrix v0.31.0 maunium.net/go/mautrix v0.29.0
) )
require ( require (
filippo.io/edwards25519 v1.2.0 // indirect filippo.io/edwards25519 v1.2.0 // indirect
github.com/coreos/go-systemd/v22 v22.7.0 // indirect github.com/coreos/go-systemd/v22 v22.7.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.3.1 // indirect github.com/kr/pretty v0.3.1 // indirect
github.com/lib/pq v1.12.3 // indirect github.com/lib/pq v1.12.3 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.52 // indirect github.com/mattn/go-sqlite3 v1.14.48 // indirect
github.com/petermattis/goid v0.0.0-20260820044319-269ab09b5261 // indirect github.com/petermattis/goid v0.0.0-20260713124913-97594f28f5ca // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/rs/xid v1.6.0 // indirect github.com/rs/xid v1.6.0 // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/match v1.2.0 // indirect
github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect github.com/tidwall/sjson v1.2.5 // indirect
github.com/yuin/goldmark v1.8.6 // indirect github.com/yuin/goldmark v1.8.4 // indirect
go.mau.fi/zeroconfig v0.2.0 // indirect go.mau.fi/zeroconfig v0.2.0 // indirect
golang.org/x/mod v0.41.0 // indirect golang.org/x/mod v0.38.0 // indirect
golang.org/x/sys v0.48.0 // indirect golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.42.0 // indirect golang.org/x/text v0.40.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
maunium.net/go/mauflag v1.0.0 // indirect maunium.net/go/mauflag v1.0.0 // indirect

86
go.sum
View file

@ -2,21 +2,15 @@ filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNUA= github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNUA=
github.com/coder/websocket v1.8.15/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= github.com/coder/websocket v1.8.15/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emersion/go-vcard v0.0.0-20241024213814-c9703dde27ff h1:4N8wnS3f1hNHSmFD5zgFkWCyA4L1kCDkImPAtK7D6tg= github.com/emersion/go-vcard v0.0.0-20241024213814-c9703dde27ff h1:4N8wnS3f1hNHSmFD5zgFkWCyA4L1kCDkImPAtK7D6tg=
github.com/emersion/go-vcard v0.0.0-20241024213814-c9703dde27ff/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM= github.com/emersion/go-vcard v0.0.0-20241024213814-c9703dde27ff/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@ -36,11 +30,13 @@ 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-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 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
github.com/mattn/go-sqlite3 v1.14.52 h1:wVbm2Qnf4OXkqhBTSPuCRZDRnxfbVrrmiCEroVdog8U= github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs=
github.com/mattn/go-sqlite3 v1.14.52/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w= github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
github.com/petermattis/goid v0.0.0-20260820044319-269ab09b5261 h1:lcWAnrqr2nNfDiArwFNHCE4787Mw2tCdVSOXCru0/0E= github.com/petermattis/goid v0.0.0-20260713124913-97594f28f5ca h1:GHSUVE4yOgX4E7kTRzpxCPbCOYkd3Kj8Dgdod30OI1E=
github.com/petermattis/goid v0.0.0-20260820044319-269ab09b5261/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/petermattis/goid v0.0.0-20260713124913-97594f28f5ca/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 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=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
@ -50,8 +46,8 @@ github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI=
github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU= github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc= github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
@ -63,47 +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/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 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/yuin/goldmark v1.8.6 h1:d0VcaP1sx9GkFVkoW+KtggpGi2KZ965i14b0+bDQST4= github.com/yuin/goldmark v1.8.4 h1:oat/nd3U6NeQqFEL3xpEJq7d7c86NI+DbSNGAs4xnjA=
github.com/yuin/goldmark v1.8.6/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/yuin/goldmark v1.8.4/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
go.mau.fi/util v0.10.1 h1:1oSqb4TwzLA0cUDY0aomyBPFKkZ3J5fqCmrXV3VH3GQ= go.mau.fi/util v0.9.11 h1:Cus1Lu/t7d3OG6VF4aYWvlUUS0Q4O1/lcpPNJZ0jsw0=
go.mau.fi/util v0.10.1/go.mod h1:40TDo7/ekSeOjgr8KAmX31Yf4zrOF94j83WQB+u5ZPc= go.mau.fi/util v0.9.11/go.mod h1:xunp/oIQfFD68HHcNHfG0pOiHkvEtDhTweeIwKJ//+Q=
go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU= go.mau.fi/zeroconfig v0.2.0 h1:e/OGEERqVRRKlgaro7E6bh8xXiKFSXB3eNNIud7FUjU=
go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w= go.mau.fi/zeroconfig v0.2.0/go.mod h1:J0Vn0prHNOm493oZoQ84kq83ZaNCYZnq+noI1b1eN8w=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597 h1:qLvzZeaANDgyVOA8pyHCOStGlXn0rseXma+GQjeuv2g=
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597/go.mod h1:EdfpwwqSu+0Li0mzskwHU6FWDV3t9Q+RZDo3QMUtL3Q=
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba h1:Ck8QetSgk912qxWLMCKxd0in+aiyBQyDSMae6e/xmpU=
golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba/go.mod h1:50RgIsmK7OwqzTTeqcSXQW8SswW0o8fRcDxmqGluJ8E=
golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues=
golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU=
google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8=
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 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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@ -113,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= 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 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/mautrix v0.31.0 h1:x6XNBSa0kOaBgcleI+ZBLkGvBdHIE0qdeY6TZ4wWUeI= maunium.net/go/mautrix v0.29.0 h1:OkcBJF1dvp+93EgahxMxOUZZOrGTYculI9IprvRIMOQ=
maunium.net/go/mautrix v0.31.0/go.mod h1:vfPYtoGAjlTnIF+W8tl+rjX3yB4PEfqYR2sDXvQo4Co= maunium.net/go/mautrix v0.29.0/go.mod h1:LynuVr8N9nWsE1N4WAE+vItRACDB1pt9M3gN4SIBpeY=

View file

@ -38,7 +38,7 @@ func supportedIfFFmpeg() event.CapabilitySupportLevel {
} }
func capID() string { func capID() string {
base := "fi.mau.signal.capabilities.2026_07_22" base := "fi.mau.signal.capabilities.2026_07_14"
if ffmpeg.Supported() { if ffmpeg.Supported() {
return base + "+ffmpeg" return base + "+ffmpeg"
} }
@ -77,7 +77,6 @@ var signalCaps = &event.RoomFeatures{
"image/jpeg": event.CapLevelFullySupported, "image/jpeg": event.CapLevelFullySupported,
"image/webp": event.CapLevelFullySupported, "image/webp": event.CapLevelFullySupported,
"image/bmp": event.CapLevelFullySupported, "image/bmp": event.CapLevelFullySupported,
"image/avif": event.CapLevelFullySupported,
}, },
MaxWidth: 4096, MaxWidth: 4096,
MaxHeight: 4096, MaxHeight: 4096,
@ -99,8 +98,6 @@ var signalCaps = &event.RoomFeatures{
MimeTypes: map[string]event.CapabilitySupportLevel{ MimeTypes: map[string]event.CapabilitySupportLevel{
"audio/aac": event.CapLevelFullySupported, "audio/aac": event.CapLevelFullySupported,
"audio/mpeg": event.CapLevelFullySupported, "audio/mpeg": event.CapLevelFullySupported,
"audio/mp3": event.CapLevelFullySupported,
"audio/flac": event.CapLevelFullySupported,
}, },
MaxSize: MaxFileSize, MaxSize: MaxFileSize,
}, },
@ -154,23 +151,18 @@ var signalCaps = &event.RoomFeatures{
event.MemberActionBan: event.CapLevelFullySupported, event.MemberActionBan: event.CapLevelFullySupported,
event.MemberActionKick: event.CapLevelFullySupported, event.MemberActionKick: event.CapLevelFullySupported,
}, },
MaxTextLength: MaxTextLength, // TODO support arbitrary sized text messages with files MaxTextLength: MaxTextLength, // TODO support arbitrary sized text messages with files
LocationMessage: event.CapLevelPartialSupport, LocationMessage: event.CapLevelPartialSupport,
Poll: event.CapLevelFullySupported, Poll: event.CapLevelFullySupported,
PollEnd: event.CapLevelUnsupported, Thread: event.CapLevelUnsupported,
PollHiddenVotes: event.CapLevelUnsupported, Reply: event.CapLevelFullySupported,
PollDuplicateOptions: event.CapLevelFullySupported, Edit: event.CapLevelFullySupported,
PollMaxOptions: 10, EditMaxCount: 10,
PollOptionMaxLength: 100, EditMaxAge: ptr.Ptr(jsontime.S(24 * time.Hour)),
Thread: event.CapLevelUnsupported, Delete: event.CapLevelFullySupported,
Reply: event.CapLevelFullySupported, DeleteForMe: false,
Edit: event.CapLevelFullySupported, DeleteMaxAge: ptr.Ptr(jsontime.S(24 * time.Hour)),
EditMaxCount: 10, DisappearingTimer: signalDisappearingCap,
EditMaxAge: ptr.Ptr(jsontime.S(24 * time.Hour)),
Delete: event.CapLevelFullySupported,
DeleteForMe: false,
DeleteMaxAge: ptr.Ptr(jsontime.S(24 * time.Hour)),
DisappearingTimer: signalDisappearingCap,
Reaction: event.CapLevelFullySupported, Reaction: event.CapLevelFullySupported,
ReactionCount: 1, ReactionCount: 1,
@ -245,5 +237,5 @@ func (s *SignalConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilities
} }
func (s *SignalConnector) GetBridgeInfoVersion() (info, capabilities int) { func (s *SignalConnector) GetBridgeInfoVersion() (info, capabilities int) {
return 1, 11 return 1, 9
} }

View file

@ -39,7 +39,7 @@ import (
"go.mau.fi/mautrix-signal/pkg/libsignalgo" "go.mau.fi/mautrix-signal/pkg/libsignalgo"
"go.mau.fi/mautrix-signal/pkg/signalid" "go.mau.fi/mautrix-signal/pkg/signalid"
"go.mau.fi/mautrix-signal/pkg/signalmeow" "go.mau.fi/mautrix-signal/pkg/signalmeow"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
) )
var ( var (
@ -180,48 +180,12 @@ func (s *SignalClient) HandleMatrixEdit(ctx context.Context, msg *bridgev2.Matri
if err != nil { if err != nil {
return bridgev2.WrapErrorInStatus(err).WithSendNotice(true) return bridgev2.WrapErrorInStatus(err).WithSendNotice(true)
} }
prevID := msg.EditTarget.ID
msg.EditTarget.ID = signalid.MakeMessageID(s.Client.Store.ACI, ts) msg.EditTarget.ID = signalid.MakeMessageID(s.Client.Store.ACI, ts)
msg.EditTarget.Metadata = &signalid.MessageMetadata{ContainsAttachments: len(converted.Attachments) > 0} msg.EditTarget.Metadata = &signalid.MessageMetadata{ContainsAttachments: len(converted.Attachments) > 0}
msg.EditTarget.EditCount++ msg.EditTarget.EditCount++
if prevID != msg.EditTarget.ID {
err = s.Main.Bridge.DB.DoTxn(ctx, nil, func(ctx context.Context) error {
err = s.Main.Bridge.DB.Message.Update(ctx, msg.EditTarget)
if err != nil {
return err
}
err = saveEditStub(ctx, s.Main.Bridge, prevID, msg.EditTarget)
if err != nil {
return fmt.Errorf("failed to save edit stub: %w", err)
}
return nil
})
if err != nil {
zerolog.Ctx(ctx).Err(err).
Str("prev_message_id", string(prevID)).
Str("message_id", string(msg.EditTarget.ID)).
Msg("Failed to save message after editing")
}
}
return nil 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) error {
stub := &database.Message{
ID: prevID,
PartID: editStubPartID,
Room: target.Room,
SenderID: target.SenderID,
SenderMXID: target.SenderMXID,
Timestamp: target.Timestamp,
}
stub.SetFakeMXID()
return bridge.DB.Message.Insert(ctx, stub)
}
func (s *SignalClient) PreHandleMatrixReaction(ctx context.Context, msg *bridgev2.MatrixReaction) (bridgev2.MatrixReactionPreResponse, error) { func (s *SignalClient) PreHandleMatrixReaction(ctx context.Context, msg *bridgev2.MatrixReaction) (bridgev2.MatrixReactionPreResponse, error) {
return bridgev2.MatrixReactionPreResponse{ return bridgev2.MatrixReactionPreResponse{
SenderID: signalid.MakeUserID(s.Client.Store.ACI), SenderID: signalid.MakeUserID(s.Client.Store.ACI),

View file

@ -20,7 +20,6 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"slices"
"strings" "strings"
"time" "time"
@ -40,7 +39,7 @@ import (
"go.mau.fi/mautrix-signal/pkg/signalid" "go.mau.fi/mautrix-signal/pkg/signalid"
"go.mau.fi/mautrix-signal/pkg/signalmeow" "go.mau.fi/mautrix-signal/pkg/signalmeow"
"go.mau.fi/mautrix-signal/pkg/signalmeow/events" "go.mau.fi/mautrix-signal/pkg/signalmeow/events"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
"go.mau.fi/mautrix-signal/pkg/signalmeow/types" "go.mau.fi/mautrix-signal/pkg/signalmeow/types"
) )
@ -359,52 +358,20 @@ func (evt *Bv2ChatEvent) ConvertMessage(ctx context.Context, portal *bridgev2.Po
return converted, nil 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) { 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) editMsg, ok := evt.Event.(*signalpb.EditMessage)
if !ok { if !ok {
return nil, fmt.Errorf("ConvertEdit() called for non-EditMessage event") 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? // 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) 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? // TODO can anything other than the text be edited?
editPart := converted.Parts[len(converted.Parts)-1].ToEditPart(existing[len(existing)-1]) 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.EditCount++
editPart.Part.ID = signalid.MakeMessageID(evt.Info.Sender, editMsg.GetDataMessage().GetTimestamp()) editPart.Part.ID = signalid.MakeMessageID(evt.Info.Sender, editMsg.GetDataMessage().GetTimestamp())
convertedEdit := &bridgev2.ConvertedEdit{ return &bridgev2.ConvertedEdit{
ModifiedParts: []*bridgev2.ConvertedEditPart{editPart}, ModifiedParts: []*bridgev2.ConvertedEditPart{editPart},
} }, nil
if prevID != editPart.Part.ID {
convertedEdit.AddedParts = editStubMessage()
}
return convertedEdit, nil
} }
func (evt *Bv2ChatEvent) GetStreamOrder() int64 { func (evt *Bv2ChatEvent) GetStreamOrder() int64 {

View file

@ -18,12 +18,9 @@ package connector
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net/http"
"time" "time"
"github.com/coder/websocket"
"github.com/google/uuid" "github.com/google/uuid"
"maunium.net/go/mautrix/bridgev2" "maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/bridgev2/database" "maunium.net/go/mautrix/bridgev2/database"
@ -44,7 +41,7 @@ func (s *SignalConnector) GetLoginFlows() []bridgev2.LoginFlow {
func (s *SignalConnector) CreateLogin(ctx context.Context, user *bridgev2.User, flowID string) (bridgev2.LoginProcess, error) { func (s *SignalConnector) CreateLogin(ctx context.Context, user *bridgev2.User, flowID string) (bridgev2.LoginProcess, error) {
if flowID != "qr" { if flowID != "qr" {
return nil, bridgev2.ErrInvalidLoginFlowID return nil, fmt.Errorf("invalid login flow ID")
} }
return &QRLogin{User: user, Main: s}, nil return &QRLogin{User: user, Main: s}, nil
} }
@ -69,78 +66,10 @@ func (qr *QRLogin) Cancel() {
const ( const (
LoginStepQR = "fi.mau.signal.login.qr" LoginStepQR = "fi.mau.signal.login.qr"
LoginStepProcess = "fi.mau.signal.login.processing"
LoginStepComplete = "fi.mau.signal.login.complete" 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) { func (qr *QRLogin) Start(ctx context.Context) (*bridgev2.LoginStep, error) {
log := qr.Main.Bridge.Log.With(). log := qr.Main.Bridge.Log.With().
Str("action", "login"). Str("action", "login").
@ -156,16 +85,14 @@ func (qr *QRLogin) Start(ctx context.Context) (*bridgev2.LoginStep, error) {
select { select {
case resp = <-qr.ProvChan: case resp = <-qr.ProvChan:
if resp.Err != nil { if resp.Err != nil {
return nil, wrapProvisioningError(resp.Err) return nil, resp.Err
} else if resp.State != signalmeow.StateProvisioningURLReceived { } else if resp.State != signalmeow.StateProvisioningURLReceived {
return nil, fmt.Errorf("unexpected state %v", resp.State) return nil, fmt.Errorf("unexpected state %v", resp.State)
} }
case <-ctx.Done(): case <-ctx.Done():
cancel() cancel()
if errors.Is(ctx.Err(), context.DeadlineExceeded) { return nil, ctx.Err()
return nil, ErrLoginTimedOut // TODO separate timeout here?
}
return nil, ErrLoginCancelled
} }
return &bridgev2.LoginStep{ return &bridgev2.LoginStep{
Type: bridgev2.LoginStepTypeDisplayAndWait, Type: bridgev2.LoginStepTypeDisplayAndWait,
@ -187,7 +114,7 @@ func (qr *QRLogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) {
case resp := <-qr.ProvChan: case resp := <-qr.ProvChan:
if resp.Err != nil { if resp.Err != nil {
qr.cancelChan() qr.cancelChan()
return nil, wrapProvisioningError(resp.Err) return nil, resp.Err
} else if resp.State != signalmeow.StateProvisioningDataReceived { } else if resp.State != signalmeow.StateProvisioningDataReceived {
qr.cancelChan() qr.cancelChan()
return nil, fmt.Errorf("unexpected state %v", resp.State) return nil, fmt.Errorf("unexpected state %v", resp.State)
@ -199,20 +126,17 @@ func (qr *QRLogin) Wait(ctx context.Context) (*bridgev2.LoginStep, error) {
// Server will timeout the request after 60 seconds, but Signal Desktop opens // 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. // a new socket and gets a new QR code after 45 seconds. We should do the same.
case <-time.After(qrRefreshInterval): case <-time.After(45 * time.Second):
qr.cancelChan() qr.cancelChan()
qr.newQRCount++ qr.newQRCount++
if qr.newQRCount >= maxQRRefreshes { if qr.newQRCount >= 6 {
return nil, ErrLoginTimedOut return nil, fmt.Errorf("too many QR code refreshes")
} }
return qr.Start(ctx) return qr.Start(ctx)
case <-ctx.Done(): case <-ctx.Done():
qr.cancelChan() qr.cancelChan()
if errors.Is(ctx.Err(), context.DeadlineExceeded) { return nil, ctx.Err()
return nil, ErrLoginTimedOut
}
return nil, ErrLoginCancelled
} }
} }

View file

@ -20,17 +20,19 @@ package libsignalgo
#include "./libsignal-ffi.h" #include "./libsignal-ffi.h"
*/ */
import "C" import "C"
import "runtime" import (
"runtime"
"unsafe"
)
type AccountEntropyPool string type AccountEntropyPool string
type SVRKey = fixedArray32
func (aep AccountEntropyPool) DeriveSVRKey() ([]byte, error) { func (aep AccountEntropyPool) DeriveSVRKey() ([]byte, error) {
var out SVRKey var out [C.SignalSVR_KEY_LEN]byte
aepC, free := GoStringToCString(string(aep)) aepC, free := GoStringToCString(string(aep))
defer free() defer free()
signalFfiError := C.signal_account_entropy_pool_derive_svr_key( signalFfiError := C.signal_account_entropy_pool_derive_svr_key(
out.cFixedArray(), (*[C.SignalSVR_KEY_LEN]C.uint8_t)(unsafe.Pointer(&out)),
aepC, aepC,
) )
runtime.KeepAlive(aep) runtime.KeepAlive(aep)
@ -41,11 +43,11 @@ func (aep AccountEntropyPool) DeriveSVRKey() ([]byte, error) {
} }
func (aep AccountEntropyPool) DeriveBackupKey() ([]byte, error) { func (aep AccountEntropyPool) DeriveBackupKey() ([]byte, error) {
var out BackupKey var out [C.SignalBACKUP_KEY_LEN]byte
aepC, free := GoStringToCString(string(aep)) aepC, free := GoStringToCString(string(aep))
defer free() defer free()
signalFfiError := C.signal_account_entropy_pool_derive_backup_key( signalFfiError := C.signal_account_entropy_pool_derive_backup_key(
out.cFixedArray(), (*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(&out)),
aepC, aepC,
) )
runtime.KeepAlive(aep) runtime.KeepAlive(aep)

View file

@ -82,7 +82,6 @@ func (aes *AES256_GCM_SIV) Decrypt(ciphertext, nonce, associatedData []byte) ([]
BytesToBuffer(nonce), BytesToBuffer(nonce),
BytesToBuffer(associatedData), BytesToBuffer(associatedData),
) )
runtime.KeepAlive(aes)
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }

View file

@ -24,17 +24,15 @@ package libsignalgo
import "C" import "C"
import ( import (
"fmt" "fmt"
"unsafe"
"github.com/google/uuid" "github.com/google/uuid"
) )
// type AuthCredential [181]byte // type AuthCredential [C.SignalAUTH_CREDENTIAL_LEN]byte
// type AuthCredentialResponse [361]byte // type AuthCredentialResponse [C.SignalAUTH_CREDENTIAL_RESPONSE_LEN]byte
const AuthCredentialWithPniLength = 265 type AuthCredentialWithPni [C.SignalAUTH_CREDENTIAL_WITH_PNI_LEN]byte
const AuthCredentialWithPniResponseLength = 425 type AuthCredentialWithPniResponse [C.SignalAUTH_CREDENTIAL_WITH_PNI_RESPONSE_LEN]byte
type AuthCredentialWithPni [AuthCredentialWithPniLength]byte
type AuthCredentialWithPniResponse [AuthCredentialWithPniResponseLength]byte
type AuthCredentialPresentation []byte type AuthCredentialPresentation []byte
func (ac *AuthCredentialWithPni) Slice() []byte { func (ac *AuthCredentialWithPni) Slice() []byte {
@ -53,8 +51,8 @@ func ReceiveAuthCredentialWithPni(
signalFfiError := C.signal_server_public_params_receive_auth_credential_with_pni_as_service_id( signalFfiError := C.signal_server_public_params_receive_auth_credential_with_pni_as_service_id(
&c_result, &c_result,
C.SignalConstPointerServerPublicParams{serverPublicParams}, C.SignalConstPointerServerPublicParams{serverPublicParams},
NewACIServiceID(aci).cConstFixedArray(), NewACIServiceID(aci).CFixedBytes(),
NewPNIServiceID(pni).cConstFixedArray(), NewPNIServiceID(pni).CFixedBytes(),
C.uint64_t(redemptionTime), C.uint64_t(redemptionTime),
BytesToBuffer(authCredResponse[:]), BytesToBuffer(authCredResponse[:]),
) )
@ -62,16 +60,13 @@ func ReceiveAuthCredentialWithPni(
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
resultBytes := CopySignalOwnedBufferToBytes(c_result) resultBytes := CopySignalOwnedBufferToBytes(c_result)
if len(resultBytes) != AuthCredentialWithPniLength { if len(resultBytes) != C.SignalAUTH_CREDENTIAL_WITH_PNI_LEN {
return nil, fmt.Errorf("invalid response length %d (expected %d)", len(resultBytes), AuthCredentialWithPniLength) return nil, fmt.Errorf("invalid response length %d (expected %d)", len(resultBytes), C.SignalAUTH_CREDENTIAL_WITH_PNI_LEN)
} }
return (*AuthCredentialWithPni)(resultBytes), nil return (*AuthCredentialWithPni)(resultBytes), nil
} }
func NewAuthCredentialWithPniResponse(b []byte) (*AuthCredentialWithPniResponse, error) { func NewAuthCredentialWithPniResponse(b []byte) (*AuthCredentialWithPniResponse, error) {
if len(b) != AuthCredentialWithPniResponseLength {
return nil, fmt.Errorf("invalid auth credential with pni response length %d (expected %d)", len(b), AuthCredentialWithPniResponseLength)
}
borrowedBuffer := BytesToBuffer(b) borrowedBuffer := BytesToBuffer(b)
signalFfiError := C.signal_auth_credential_with_pni_response_check_valid_contents(borrowedBuffer) signalFfiError := C.signal_auth_credential_with_pni_response_check_valid_contents(borrowedBuffer)
if signalFfiError != nil { if signalFfiError != nil {
@ -88,12 +83,14 @@ func CreateAuthCredentialWithPniPresentation(
authCredWithPni AuthCredentialWithPni, authCredWithPni AuthCredentialWithPni,
) (*AuthCredentialPresentation, error) { ) (*AuthCredentialPresentation, error) {
var c_result C.SignalOwnedBuffer = C.SignalOwnedBuffer{} 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( signalFfiError := C.signal_server_public_params_create_auth_credential_with_pni_presentation_deterministic(
&c_result, &c_result,
C.SignalConstPointerServerPublicParams{serverPublicParams}, C.SignalConstPointerServerPublicParams{serverPublicParams},
randomness.cConstFixedArray(), c_randomness,
groupSecretParams.cConstFixedArray(), c_groupSecretParams,
BytesToBuffer(authCredWithPni[:]), BytesToBuffer(authCredWithPni[:]),
) )
if signalFfiError != nil { if signalFfiError != nil {

View file

@ -27,9 +27,7 @@ import (
"go.mau.fi/util/random" "go.mau.fi/util/random"
) )
const BackupKeyLength = 32 type BackupKey [C.SignalBACKUP_KEY_LEN]byte
type BackupKey [BackupKeyLength]byte
func (bk *BackupKey) Slice() []byte { func (bk *BackupKey) Slice() []byte {
if bk == nil { if bk == nil {
@ -40,25 +38,17 @@ func (bk *BackupKey) Slice() []byte {
const BackupIDLength = 16 const BackupIDLength = 16
type BackupID = fixedArray16 type BackupID [BackupIDLength]byte
type BackupMetadataKey = fixedArray32 type BackupMetadataKey [C.SignalLOCAL_BACKUP_METADATA_KEY_LEN]byte
type BackupMediaID = fixedArray15 type BackupMediaID [C.SignalMEDIA_ID_LEN]byte
type BackupMediaKey = fixedArray64 type BackupMediaKey [C.SignalMEDIA_ENCRYPTION_KEY_LEN]byte
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 { func GenerateRandomBackupKey() *BackupKey {
return (*BackupKey)(random.Bytes(BackupKeyLength)) return (*BackupKey)(random.Bytes(C.SignalBACKUP_KEY_LEN))
} }
func BytesToBackupKey(bytes []byte) *BackupKey { func BytesToBackupKey(bytes []byte) *BackupKey {
if len(bytes) != BackupKeyLength { if len(bytes) != C.SignalBACKUP_KEY_LEN {
return nil return nil
} }
return (*BackupKey)(bytes) return (*BackupKey)(bytes)
@ -67,9 +57,9 @@ func BytesToBackupKey(bytes []byte) *BackupKey {
func (bk *BackupKey) DeriveBackupID(aci ServiceID) (*BackupID, error) { func (bk *BackupKey) DeriveBackupID(aci ServiceID) (*BackupID, error) {
var out BackupID var out BackupID
signalFfiError := C.signal_backup_key_derive_backup_id( signalFfiError := C.signal_backup_key_derive_backup_id(
out.cFixedArray(), (*[BackupIDLength]C.uint8_t)(unsafe.Pointer(&out)),
bk.cConstFixedArray(), (*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(bk)),
aci.cConstFixedArray(), aci.CFixedBytes(),
) )
runtime.KeepAlive(bk) runtime.KeepAlive(bk)
if signalFfiError != nil { if signalFfiError != nil {
@ -82,8 +72,8 @@ func (bk *BackupKey) DeriveECKey(aci ServiceID) (*PrivateKey, error) {
var out C.SignalMutPointerPrivateKey var out C.SignalMutPointerPrivateKey
signalFfiError := C.signal_backup_key_derive_ec_key( signalFfiError := C.signal_backup_key_derive_ec_key(
&out, &out,
bk.cConstFixedArray(), (*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(&bk)),
aci.cConstFixedArray(), aci.CFixedBytes(),
) )
runtime.KeepAlive(bk) runtime.KeepAlive(bk)
if signalFfiError != nil { if signalFfiError != nil {
@ -95,8 +85,8 @@ func (bk *BackupKey) DeriveECKey(aci ServiceID) (*PrivateKey, error) {
func (bk *BackupKey) DeriveLocalBackupMetadataKey() (*BackupMetadataKey, error) { func (bk *BackupKey) DeriveLocalBackupMetadataKey() (*BackupMetadataKey, error) {
var out BackupMetadataKey var out BackupMetadataKey
signalFfiError := C.signal_backup_key_derive_local_backup_metadata_key( signalFfiError := C.signal_backup_key_derive_local_backup_metadata_key(
out.cFixedArray(), (*[C.SignalLOCAL_BACKUP_METADATA_KEY_LEN]C.uint8_t)(unsafe.Pointer(&out)),
bk.cConstFixedArray(), (*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(bk)),
) )
runtime.KeepAlive(bk) runtime.KeepAlive(bk)
if signalFfiError != nil { if signalFfiError != nil {
@ -110,8 +100,8 @@ func (bk *BackupKey) DeriveMediaID(mediaName string) (*BackupMediaID, error) {
mediaNameStr, mediaNameFree := GoStringToCString(mediaName) mediaNameStr, mediaNameFree := GoStringToCString(mediaName)
defer mediaNameFree() defer mediaNameFree()
signalFfiError := C.signal_backup_key_derive_media_id( signalFfiError := C.signal_backup_key_derive_media_id(
out.cFixedArray(), (*[C.SignalMEDIA_ID_LEN]C.uint8_t)(unsafe.Pointer(&out)),
bk.cConstFixedArray(), (*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(bk)),
mediaNameStr, mediaNameStr,
) )
runtime.KeepAlive(bk) runtime.KeepAlive(bk)
@ -124,9 +114,9 @@ func (bk *BackupKey) DeriveMediaID(mediaName string) (*BackupMediaID, error) {
func (bk *BackupKey) DeriveMediaEncryptionKey(mediaID *BackupMediaID) (*BackupMediaKey, error) { func (bk *BackupKey) DeriveMediaEncryptionKey(mediaID *BackupMediaID) (*BackupMediaKey, error) {
var out BackupMediaKey var out BackupMediaKey
signalFfiError := C.signal_backup_key_derive_media_encryption_key( signalFfiError := C.signal_backup_key_derive_media_encryption_key(
out.cFixedArray(), (*[C.SignalMEDIA_ENCRYPTION_KEY_LEN]C.uint8_t)(unsafe.Pointer(&out)),
bk.cConstFixedArray(), (*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(bk)),
mediaID.cConstFixedArray(), (*[C.SignalMEDIA_ID_LEN]C.uint8_t)(unsafe.Pointer(mediaID)),
) )
runtime.KeepAlive(bk) runtime.KeepAlive(bk)
runtime.KeepAlive(mediaID) runtime.KeepAlive(mediaID)
@ -139,9 +129,9 @@ func (bk *BackupKey) DeriveMediaEncryptionKey(mediaID *BackupMediaID) (*BackupMe
func (bk *BackupKey) DeriveThumbnailTransitEncryptionKey(mediaID *BackupMediaID) (*BackupMediaKey, error) { func (bk *BackupKey) DeriveThumbnailTransitEncryptionKey(mediaID *BackupMediaID) (*BackupMediaKey, error) {
var out BackupMediaKey var out BackupMediaKey
signalFfiError := C.signal_backup_key_derive_thumbnail_transit_encryption_key( signalFfiError := C.signal_backup_key_derive_thumbnail_transit_encryption_key(
out.cFixedArray(), (*[C.SignalMEDIA_ENCRYPTION_KEY_LEN]C.uint8_t)(unsafe.Pointer(&out)),
bk.cConstFixedArray(), (*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(bk)),
mediaID.cConstFixedArray(), (*[C.SignalMEDIA_ID_LEN]C.uint8_t)(unsafe.Pointer(mediaID)),
) )
runtime.KeepAlive(bk) runtime.KeepAlive(bk)
runtime.KeepAlive(mediaID) runtime.KeepAlive(mediaID)

View file

@ -21,14 +21,12 @@ package libsignalgo
*/ */
import "C" import "C"
import ( import (
"fmt"
"runtime" "runtime"
"unsafe" "unsafe"
) )
func BorrowedMutableBuffer(length int) C.SignalBorrowedMutableBuffer { func BorrowedMutableBuffer(length int) C.SignalBorrowedMutableBuffer {
if length <= 0 {
return C.SignalBorrowedMutableBuffer{}
}
data := make([]byte, length) data := make([]byte, length)
return C.SignalBorrowedMutableBuffer{ return C.SignalBorrowedMutableBuffer{
base: (*C.uchar)(unsafe.Pointer(&data[0])), base: (*C.uchar)(unsafe.Pointer(&data[0])),
@ -50,9 +48,10 @@ func ManyBytesToBuffer[T ~[]byte](datas []T) (C.SignalBorrowedSliceOfBuffers, fu
buffers := make([]C.SignalBorrowedBuffer, len(datas)) buffers := make([]C.SignalBorrowedBuffer, len(datas))
var pinner runtime.Pinner var pinner runtime.Pinner
for i, data := range datas { for i, data := range datas {
if len(data) > 0 { if len(data) == 0 {
pinner.Pin(&data[0]) panic(fmt.Errorf("empty slice passed to ManyBytesToBuffer at index %d", i))
} }
pinner.Pin(&data[0])
buffers[i] = BytesToBuffer(data) buffers[i] = BytesToBuffer(data)
} }
return C.SignalBorrowedSliceOfBuffers{ return C.SignalBorrowedSliceOfBuffers{

View file

@ -4,9 +4,3 @@ package libsignalgo
#cgo LDFLAGS: -lsignal_ffi -ldl -lm -lz -lstdc++ #cgo LDFLAGS: -lsignal_ffi -ldl -lm -lz -lstdc++
*/ */
import "C" import "C"
import (
"go.mau.fi/mautrix-signal/pkg/libsignalgo/signalversion"
)
const Version = signalversion.Version

View file

@ -49,7 +49,6 @@ func NewCiphertextMessage(plaintext *PlaintextContent) (*CiphertextMessage, erro
&ciphertextMessage, &ciphertextMessage,
plaintext.constPtr(), plaintext.constPtr(),
) )
runtime.KeepAlive(plaintext)
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }

View file

@ -18,20 +18,19 @@ package libsignalgo
/* /*
#include "./libsignal-ffi.h" #include "./libsignal-ffi.h"
#include <stdlib.h>
*/ */
import "C" import "C"
import "unsafe" import "unsafe"
func GoStringToCString(str string) (C.SignalCStringPtr, func()) { func GoStringToCString(str string) (C.SignalCStringPtr, func()) {
cStr := C.CString(str) cStr := C.CString(str)
return C.SignalCStringPtr(unsafe.Pointer(cStr)), func() { return cStr, func() {
C.free(unsafe.Pointer(cStr)) C.free(unsafe.Pointer(cStr))
} }
} }
func CopyCStringToString(cString C.SignalCStringPtr) (s string) { func CopyCStringToString(cString C.SignalCStringPtr) (s string) {
s = C.GoString((*C.char)(unsafe.Pointer(cString))) s = C.GoString(cString)
C.signal_free_string(cString) C.signal_free_string(cString)
return return
} }

View file

@ -52,8 +52,6 @@ func NewFingerprint(iterations, version FingerprintVersion, localIdentifier []by
BytesToBuffer(remoteIdentifier), BytesToBuffer(remoteIdentifier),
remoteKey.constPtr(), remoteKey.constPtr(),
) )
runtime.KeepAlive(localKey)
runtime.KeepAlive(remoteKey)
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }

View file

@ -1,159 +0,0 @@
// 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())
}

View file

@ -1,24 +0,0 @@
//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

View file

@ -1,26 +0,0 @@
//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

View file

@ -31,9 +31,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
const RandomnessLength = 32 type Randomness [C.SignalRANDOMNESS_LEN]byte
type Randomness = fixedArray32
func GenerateRandomness() Randomness { func GenerateRandomness() Randomness {
var randomness Randomness var randomness Randomness
@ -44,39 +42,14 @@ func GenerateRandomness() Randomness {
return randomness return randomness
} }
const GroupMasterKeyLength = 32 const GroupMasterKeyLength = C.SignalGROUP_MASTER_KEY_LEN
const GroupIdentifierLength = 32 const GroupIdentifierLength = C.SignalGROUP_IDENTIFIER_LEN
const GroupSecretParamsLength = 289
type GroupMasterKey [GroupMasterKeyLength]byte type GroupMasterKey [GroupMasterKeyLength]byte
type GroupSecretParams [GroupSecretParamsLength]byte type GroupSecretParams [C.SignalGROUP_SECRET_PARAMS_LEN]byte
type GroupPublicParams = fixedArray97 type GroupPublicParams [C.SignalGROUP_PUBLIC_PARAMS_LEN]byte
type GroupIdentifier [GroupIdentifierLength]byte 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 { func (gid *GroupIdentifier) String() string {
if gid == nil { if gid == nil {
return "" return ""
@ -84,8 +57,8 @@ func (gid *GroupIdentifier) String() string {
return base64.StdEncoding.EncodeToString(gid[:]) return base64.StdEncoding.EncodeToString(gid[:])
} }
type UUIDCiphertext = fixedArray65 type UUIDCiphertext [C.SignalUUID_CIPHERTEXT_LEN]byte
type ProfileKeyCiphertext = fixedArray65 type ProfileKeyCiphertext [C.SignalPROFILE_KEY_CIPHERTEXT_LEN]byte
func GenerateGroupSecretParams() (GroupSecretParams, error) { func GenerateGroupSecretParams() (GroupSecretParams, error) {
return GenerateGroupSecretParamsWithRandomness(GenerateRandomness()) return GenerateGroupSecretParamsWithRandomness(GenerateRandomness())
@ -108,43 +81,51 @@ func (gmk GroupMasterKey) SecretParams() (GroupSecretParams, error) {
} }
func GenerateGroupSecretParamsWithRandomness(randomness Randomness) (GroupSecretParams, error) { func GenerateGroupSecretParamsWithRandomness(randomness Randomness) (GroupSecretParams, error) {
var params GroupSecretParams var params [C.SignalGROUP_SECRET_PARAMS_LEN]C.uchar
signalFfiError := C.signal_group_secret_params_generate_deterministic(params.cFixedArray(), randomness.cConstFixedArray()) signalFfiError := C.signal_group_secret_params_generate_deterministic(&params, (*[C.SignalRANDOMNESS_LEN]C.uint8_t)(unsafe.Pointer(&randomness)))
runtime.KeepAlive(randomness) runtime.KeepAlive(randomness)
if signalFfiError != nil { if signalFfiError != nil {
return GroupSecretParams{}, wrapError(signalFfiError) return GroupSecretParams{}, wrapError(signalFfiError)
} }
return params, nil var groupSecretParams GroupSecretParams
copy(groupSecretParams[:], C.GoBytes(unsafe.Pointer(&params), C.int(C.SignalGROUP_SECRET_PARAMS_LEN)))
return groupSecretParams, nil
} }
func DeriveGroupSecretParamsFromMasterKey(groupMasterKey GroupMasterKey) (GroupSecretParams, error) { func DeriveGroupSecretParamsFromMasterKey(groupMasterKey GroupMasterKey) (GroupSecretParams, error) {
var params GroupSecretParams var params [C.SignalGROUP_SECRET_PARAMS_LEN]C.uchar
signalFfiError := C.signal_group_secret_params_derive_from_master_key(params.cFixedArray(), groupMasterKey.cConstFixedArray()) signalFfiError := C.signal_group_secret_params_derive_from_master_key(&params, (*[C.SignalGROUP_MASTER_KEY_LEN]C.uint8_t)(unsafe.Pointer(&groupMasterKey)))
runtime.KeepAlive(groupMasterKey) runtime.KeepAlive(groupMasterKey)
if signalFfiError != nil { if signalFfiError != nil {
return GroupSecretParams{}, wrapError(signalFfiError) return GroupSecretParams{}, wrapError(signalFfiError)
} }
return params, nil var groupSecretParams GroupSecretParams
copy(groupSecretParams[:], C.GoBytes(unsafe.Pointer(&params), C.int(C.SignalGROUP_SECRET_PARAMS_LEN)))
return groupSecretParams, nil
} }
func (gsp *GroupSecretParams) GetPublicParams() (*GroupPublicParams, error) { func (gsp *GroupSecretParams) GetPublicParams() (*GroupPublicParams, error) {
var publicParams GroupPublicParams var publicParams [C.SignalGROUP_PUBLIC_PARAMS_LEN]C.uchar
signalFfiError := C.signal_group_secret_params_get_public_params(publicParams.cFixedArray(), gsp.cConstFixedArray()) signalFfiError := C.signal_group_secret_params_get_public_params(&publicParams, (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)))
runtime.KeepAlive(gsp) runtime.KeepAlive(gsp)
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
return &publicParams, nil var groupPublicParams GroupPublicParams
copy(groupPublicParams[:], C.GoBytes(unsafe.Pointer(&publicParams), C.int(C.SignalGROUP_PUBLIC_PARAMS_LEN)))
return &groupPublicParams, nil
} }
func GetGroupIdentifier(groupPublicParams GroupPublicParams) (*GroupIdentifier, error) { func GetGroupIdentifier(groupPublicParams GroupPublicParams) (*GroupIdentifier, error) {
var groupIdentifier GroupIdentifier var groupIdentifier [C.SignalGROUP_IDENTIFIER_LEN]C.uchar
signalFfiError := C.signal_group_public_params_get_group_identifier(groupIdentifier.cFixedArray(), groupPublicParams.cConstFixedArray()) signalFfiError := C.signal_group_public_params_get_group_identifier(&groupIdentifier, (*[C.SignalGROUP_PUBLIC_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(&groupPublicParams)))
runtime.KeepAlive(groupPublicParams) runtime.KeepAlive(groupPublicParams)
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
return &groupIdentifier, nil var result GroupIdentifier
copy(result[:], C.GoBytes(unsafe.Pointer(&groupIdentifier), C.int(C.SignalGROUP_IDENTIFIER_LEN)))
return &result, nil
} }
func (gsp *GroupSecretParams) DecryptBlobWithPadding(blob []byte) ([]byte, error) { func (gsp *GroupSecretParams) DecryptBlobWithPadding(blob []byte) ([]byte, error) {
@ -152,7 +133,7 @@ func (gsp *GroupSecretParams) DecryptBlobWithPadding(blob []byte) ([]byte, error
borrowedBlob := BytesToBuffer(blob) borrowedBlob := BytesToBuffer(blob)
signalFfiError := C.signal_group_secret_params_decrypt_blob_with_padding( signalFfiError := C.signal_group_secret_params_decrypt_blob_with_padding(
&plaintext, &plaintext,
gsp.cConstFixedArray(), (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
borrowedBlob, borrowedBlob,
) )
runtime.KeepAlive(gsp) runtime.KeepAlive(gsp)
@ -168,8 +149,8 @@ func (gsp *GroupSecretParams) EncryptBlobWithPaddingDeterministic(randomness Ran
borrowedPlaintext := BytesToBuffer(plaintext) borrowedPlaintext := BytesToBuffer(plaintext)
signalFfiError := C.signal_group_secret_params_encrypt_blob_with_padding_deterministic( signalFfiError := C.signal_group_secret_params_encrypt_blob_with_padding_deterministic(
&ciphertext, &ciphertext,
gsp.cConstFixedArray(), (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
randomness.cConstFixedArray(), (*[C.SignalRANDOMNESS_LEN]C.uint8_t)(unsafe.Pointer(&randomness)),
borrowedPlaintext, borrowedPlaintext,
(C.uint32_t)(padding_len), (C.uint32_t)(padding_len),
) )
@ -184,11 +165,11 @@ func (gsp *GroupSecretParams) EncryptBlobWithPaddingDeterministic(randomness Ran
} }
func (gsp *GroupSecretParams) DecryptServiceID(ciphertextServiceID UUIDCiphertext) (ServiceID, error) { func (gsp *GroupSecretParams) DecryptServiceID(ciphertextServiceID UUIDCiphertext) (ServiceID, error) {
var serviceIDBytes ServiceIDFixedBytes u := C.SignalServiceIdFixedWidthBinaryBytes{}
signalFfiError := C.signal_group_secret_params_decrypt_service_id( signalFfiError := C.signal_group_secret_params_decrypt_service_id(
serviceIDBytes.cFixedArray(), &u,
gsp.cConstFixedArray(), (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
ciphertextServiceID.cConstFixedArray(), (*[C.SignalUUID_CIPHERTEXT_LEN]C.uint8_t)(unsafe.Pointer(&ciphertextServiceID)),
) )
runtime.KeepAlive(gsp) runtime.KeepAlive(gsp)
runtime.KeepAlive(ciphertextServiceID) runtime.KeepAlive(ciphertextServiceID)
@ -196,31 +177,33 @@ func (gsp *GroupSecretParams) DecryptServiceID(ciphertextServiceID UUIDCiphertex
return EmptyServiceID, wrapError(signalFfiError) return EmptyServiceID, wrapError(signalFfiError)
} }
serviceID := ServiceIDFromCFixedBytes(serviceIDBytes.cFixedArray()) serviceID := ServiceIDFromCFixedBytes(&u)
return serviceID, nil return serviceID, nil
} }
func (gsp *GroupSecretParams) EncryptServiceID(serviceID ServiceID) (*UUIDCiphertext, error) { func (gsp *GroupSecretParams) EncryptServiceID(serviceID ServiceID) (*UUIDCiphertext, error) {
var cipherTextServiceID UUIDCiphertext var cipherTextServiceID [C.SignalUUID_CIPHERTEXT_LEN]C.uchar
signalFfiError := C.signal_group_secret_params_encrypt_service_id( signalFfiError := C.signal_group_secret_params_encrypt_service_id(
cipherTextServiceID.cFixedArray(), &cipherTextServiceID,
gsp.cConstFixedArray(), (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
serviceID.cConstFixedArray(), serviceID.CFixedBytes(),
) )
runtime.KeepAlive(gsp) runtime.KeepAlive(gsp)
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
return &cipherTextServiceID, nil var result UUIDCiphertext
copy(result[:], C.GoBytes(unsafe.Pointer(&cipherTextServiceID), C.int(C.SignalUUID_CIPHERTEXT_LEN)))
return &result, nil
} }
func (gsp *GroupSecretParams) DecryptProfileKey(ciphertextProfileKey ProfileKeyCiphertext, u uuid.UUID) (*ProfileKey, error) { func (gsp *GroupSecretParams) DecryptProfileKey(ciphertextProfileKey ProfileKeyCiphertext, u uuid.UUID) (*ProfileKey, error) {
var profileKey ProfileKey profileKey := [C.SignalPROFILE_KEY_LEN]C.uchar{}
signalFfiError := C.signal_group_secret_params_decrypt_profile_key( signalFfiError := C.signal_group_secret_params_decrypt_profile_key(
profileKey.cFixedArray(), &profileKey,
gsp.cConstFixedArray(), (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
ciphertextProfileKey.cConstFixedArray(), (*[C.SignalPROFILE_KEY_CIPHERTEXT_LEN]C.uint8_t)(unsafe.Pointer(&ciphertextProfileKey)),
NewACIServiceID(u).cConstFixedArray(), NewACIServiceID(u).CFixedBytes(),
) )
runtime.KeepAlive(gsp) runtime.KeepAlive(gsp)
runtime.KeepAlive(ciphertextProfileKey) runtime.KeepAlive(ciphertextProfileKey)
@ -228,23 +211,27 @@ func (gsp *GroupSecretParams) DecryptProfileKey(ciphertextProfileKey ProfileKeyC
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
return &profileKey, nil var result ProfileKey
copy(result[:], C.GoBytes(unsafe.Pointer(&profileKey), C.int(C.SignalPROFILE_KEY_LEN)))
return &result, nil
} }
func (gsp *GroupSecretParams) EncryptProfileKey(profileKey ProfileKey, u uuid.UUID) (*ProfileKeyCiphertext, error) { func (gsp *GroupSecretParams) EncryptProfileKey(profileKey ProfileKey, u uuid.UUID) (*ProfileKeyCiphertext, error) {
var ciphertextProfileKey ProfileKeyCiphertext ciphertextProfileKey := [C.SignalPROFILE_KEY_CIPHERTEXT_LEN]C.uchar{}
signalFfiError := C.signal_group_secret_params_encrypt_profile_key( signalFfiError := C.signal_group_secret_params_encrypt_profile_key(
ciphertextProfileKey.cFixedArray(), &ciphertextProfileKey,
gsp.cConstFixedArray(), (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(gsp)),
profileKey.cConstFixedArray(), (*[C.SignalPROFILE_KEY_LEN]C.uint8_t)(unsafe.Pointer(&profileKey)),
NewACIServiceID(u).cConstFixedArray(), NewACIServiceID(u).CFixedBytes(),
) )
runtime.KeepAlive(gsp) runtime.KeepAlive(gsp)
runtime.KeepAlive(profileKey) runtime.KeepAlive(profileKey)
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
return &ciphertextProfileKey, nil var result ProfileKeyCiphertext
copy(result[:], C.GoBytes(unsafe.Pointer(&ciphertextProfileKey), C.int(C.SignalPROFILE_KEY_CIPHERTEXT_LEN)))
return &result, nil
} }
func (gsp *GroupSecretParams) CreateExpiringProfileKeyCredentialPresentation(spp *ServerPublicParams, credential ExpiringProfileKeyCredential) (*ProfileKeyCredentialPresentation, error) { func (gsp *GroupSecretParams) CreateExpiringProfileKeyCredentialPresentation(spp *ServerPublicParams, credential ExpiringProfileKeyCredential) (*ProfileKeyCredentialPresentation, error) {
@ -253,9 +240,9 @@ func (gsp *GroupSecretParams) CreateExpiringProfileKeyCredentialPresentation(spp
signalFfiError := C.signal_server_public_params_create_expiring_profile_key_credential_presentation_deterministic( signalFfiError := C.signal_server_public_params_create_expiring_profile_key_credential_presentation_deterministic(
&out, &out,
C.SignalConstPointerServerPublicParams{spp}, C.SignalConstPointerServerPublicParams{spp},
randomness.cConstFixedArray(), (*[C.SignalRANDOMNESS_LEN]C.uint8_t)(unsafe.Pointer(&randomness)),
gsp.cConstFixedArray(), (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uchar)(unsafe.Pointer(gsp)),
credential.cConstFixedArray(), (*[C.SignalEXPIRING_PROFILE_KEY_CREDENTIAL_LEN]C.uchar)(unsafe.Pointer(&credential)),
) )
runtime.KeepAlive(gsp) runtime.KeepAlive(gsp)
runtime.KeepAlive(credential) runtime.KeepAlive(credential)
@ -269,14 +256,16 @@ func (gsp *GroupSecretParams) CreateExpiringProfileKeyCredentialPresentation(spp
} }
func (gsp *GroupSecretParams) GetMasterKey() (*GroupMasterKey, error) { func (gsp *GroupSecretParams) GetMasterKey() (*GroupMasterKey, error) {
var masterKey GroupMasterKey masterKeyBytes := [C.SignalGROUP_MASTER_KEY_LEN]C.uchar{}
signalFfiError := C.signal_group_secret_params_get_master_key( signalFfiError := C.signal_group_secret_params_get_master_key(
masterKey.cFixedArray(), &masterKeyBytes,
gsp.cConstFixedArray(), (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uchar)(unsafe.Pointer(gsp)),
) )
runtime.KeepAlive(gsp) runtime.KeepAlive(gsp)
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
return &masterKey, nil var groupMasterKey GroupMasterKey
copy(groupMasterKey[:], C.GoBytes(unsafe.Pointer(&masterKeyBytes), C.int(C.SignalGROUP_MASTER_KEY_LEN)))
return &groupMasterKey, nil
} }

View file

@ -24,6 +24,7 @@ import (
"encoding/base64" "encoding/base64"
"runtime" "runtime"
"time" "time"
"unsafe"
) )
type GroupSendFullToken []byte type GroupSendFullToken []byte
@ -90,7 +91,7 @@ func (gse GroupSendEndorsement) ToToken(groupSecretParams *GroupSecretParams) (G
signalFfiError := C.signal_group_send_endorsement_to_token( signalFfiError := C.signal_group_send_endorsement_to_token(
&token, &token,
BytesToBuffer(gse), BytesToBuffer(gse),
groupSecretParams.cConstFixedArray(), (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(groupSecretParams)),
) )
runtime.KeepAlive(gse) runtime.KeepAlive(gse)
runtime.KeepAlive(groupSecretParams) runtime.KeepAlive(groupSecretParams)
@ -179,17 +180,17 @@ func (gser GroupSendEndorsementsResponse) ReceiveWithServiceIDs(
groupMembers []ServiceID, localUser ServiceID, params *GroupSecretParams, spp *ServerPublicParams, groupMembers []ServiceID, localUser ServiceID, params *GroupSecretParams, spp *ServerPublicParams,
) (GroupSendEndorsement, map[ServiceID]GroupSendEndorsement, error) { ) (GroupSendEndorsement, map[ServiceID]GroupSendEndorsement, error) {
var out C.SignalBytestringArray = C.SignalBytestringArray{} var out C.SignalBytestringArray = C.SignalBytestringArray{}
concatenatedMembers := make([]byte, len(groupMembers)*ServiceIDFixedBytesLength) concatenatedMembers := make([]byte, len(groupMembers)*17)
for i, member := range groupMembers { for i, member := range groupMembers {
copy(concatenatedMembers[i*ServiceIDFixedBytesLength:(i+1)*ServiceIDFixedBytesLength], member.FixedBytes()[:]) copy(concatenatedMembers[i*17:(i+1)*17], member.FixedBytes()[:])
} }
signalFfiError := C.signal_group_send_endorsements_response_receive_and_combine_with_service_ids( signalFfiError := C.signal_group_send_endorsements_response_receive_and_combine_with_service_ids(
&out, &out,
BytesToBuffer(gser), BytesToBuffer(gser),
BytesToBuffer(concatenatedMembers), BytesToBuffer(concatenatedMembers),
localUser.cConstFixedArray(), localUser.CFixedBytes(),
C.uint64_t(time.Now().Unix()), C.uint64_t(time.Now().Unix()),
params.cConstFixedArray(), (*[C.SignalGROUP_SECRET_PARAMS_LEN]C.uint8_t)(unsafe.Pointer(params)),
C.SignalConstPointerServerPublicParams{spp}, C.SignalConstPointerServerPublicParams{spp},
) )
runtime.KeepAlive(gser) runtime.KeepAlive(gser)
@ -206,13 +207,7 @@ func (gser GroupSendEndorsementsResponse) ReceiveWithServiceIDs(
memberEndorsements[member] = endorsements[i] memberEndorsements[member] = endorsements[i]
} }
} }
nonEmptyEndorsements := make([]GroupSendEndorsement, 0, len(endorsements)) combined, err := GroupSendEndorsementCombine(endorsements...)
for _, endorsement := range endorsements {
if len(endorsement) > 0 {
nonEmptyEndorsements = append(nonEmptyEndorsements, endorsement)
}
}
combined, err := GroupSendEndorsementCombine(nonEmptyEndorsements...)
if err != nil { if err != nil {
return nil, memberEndorsements, err return nil, memberEndorsements, err
} }

@ -1 +1 @@
Subproject commit 8fc2113bda042fc972a166a24b974b0d34155c6c Subproject commit 4e9bd5d7feca2f61cf2ce0c7d99a60eb8b9e0102

File diff suppressed because it is too large Load diff

View file

@ -19,8 +19,8 @@ package libsignalgo
/* /*
#include <./libsignal-ffi.h> #include <./libsignal-ffi.h>
extern int signal_log_callback(void *ctx, SignalLogLevel level, SignalCStringPtr file, uint32_t line, SignalCStringPtr message); extern void signal_log_callback(void *ctx, SignalLogLevel level, SignalCStringPtr file, uint32_t line, SignalCStringPtr message);
extern int signal_log_flush_callback(void *ctx); extern void signal_log_flush_callback(void *ctx);
extern void signal_log_destroy_callback(void *ctx); extern void signal_log_destroy_callback(void *ctx);
*/ */
import "C" import "C"
@ -32,15 +32,13 @@ import (
var ffiLogger Logger var ffiLogger Logger
//export signal_log_callback //export signal_log_callback
func signal_log_callback(ctx unsafe.Pointer, level C.SignalLogLevel, file C.SignalCStringPtr, line C.uint32_t, message C.SignalCStringPtr) C.int { 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)), CopyCStringToString(file), uint(line), CopyCStringToString(message)) ffiLogger.Log(LogLevel(int(level)), C.GoString(file), uint(line), C.GoString(message))
return 0
} }
//export signal_log_flush_callback //export signal_log_flush_callback
func signal_log_flush_callback(ctx unsafe.Pointer) C.int { func signal_log_flush_callback(ctx unsafe.Pointer) {
ffiLogger.Flush() ffiLogger.Flush()
return 0
} }
//export signal_log_destroy_callback //export signal_log_destroy_callback

View file

@ -43,7 +43,6 @@ func Encrypt(ctx context.Context, plaintext []byte, forAddress, localAddress *Ad
) )
runtime.KeepAlive(plaintext) runtime.KeepAlive(plaintext)
runtime.KeepAlive(forAddress) runtime.KeepAlive(forAddress)
runtime.KeepAlive(localAddress)
if signalFfiError != nil { if signalFfiError != nil {
return nil, callbackCtx.wrapError(signalFfiError) return nil, callbackCtx.wrapError(signalFfiError)
} }
@ -64,7 +63,6 @@ func Decrypt(ctx context.Context, message *Message, fromAddress, localAddress *A
) )
runtime.KeepAlive(message) runtime.KeepAlive(message)
runtime.KeepAlive(fromAddress) runtime.KeepAlive(fromAddress)
runtime.KeepAlive(localAddress)
if signalFfiError != nil { if signalFfiError != nil {
return nil, callbackCtx.wrapError(signalFfiError) return nil, callbackCtx.wrapError(signalFfiError)
} }
@ -73,10 +71,10 @@ func Decrypt(ctx context.Context, message *Message, fromAddress, localAddress *A
type Message struct { type Message struct {
nc noCopy nc noCopy
ptr *C.SignalSignalMessage ptr *C.SignalMessage
} }
func wrapMessage(ptr *C.SignalSignalMessage) *Message { func wrapMessage(ptr *C.SignalMessage) *Message {
message := &Message{ptr: ptr} message := &Message{ptr: ptr}
runtime.SetFinalizer(message, (*Message).Destroy) runtime.SetFinalizer(message, (*Message).Destroy)
return message return message

View file

@ -20,17 +20,16 @@ package libsignalgo
#include "./libsignal-ffi.h" #include "./libsignal-ffi.h"
*/ */
import "C" import "C"
import "runtime" import (
"runtime"
"unsafe"
)
type MessageBackupKey struct { type MessageBackupKey struct {
nc noCopy nc noCopy
ptr *C.SignalMessageBackupKey ptr *C.SignalMessageBackupKey
} }
const MessageBackupKeyBytesLength = 32
type messageBackupKeyBytes = fixedArray32
func wrapMessageBackupKey(ptr *C.SignalMessageBackupKey) *MessageBackupKey { func wrapMessageBackupKey(ptr *C.SignalMessageBackupKey) *MessageBackupKey {
backupKey := &MessageBackupKey{ptr: ptr} backupKey := &MessageBackupKey{ptr: ptr}
runtime.SetFinalizer(backupKey, (*MessageBackupKey).Destroy) runtime.SetFinalizer(backupKey, (*MessageBackupKey).Destroy)
@ -44,7 +43,7 @@ func MessageBackupKeyFromAccountEntropyPool(aep AccountEntropyPool, aci ServiceI
signalFfiError := C.signal_message_backup_key_from_account_entropy_pool( signalFfiError := C.signal_message_backup_key_from_account_entropy_pool(
&bk, &bk,
aepC, aepC,
aci.cConstFixedArray(), aci.CFixedBytes(),
nil, // TODO what's a forward secrecy token? nil, // TODO what's a forward secrecy token?
) )
runtime.KeepAlive(aep) runtime.KeepAlive(aep)
@ -58,8 +57,8 @@ func MessageBackupKeyFromBackupKeyAndID(backupKey *BackupKey, backupID *BackupID
var bk C.SignalMutPointerMessageBackupKey var bk C.SignalMutPointerMessageBackupKey
signalFfiError := C.signal_message_backup_key_from_backup_key_and_backup_id( signalFfiError := C.signal_message_backup_key_from_backup_key_and_backup_id(
&bk, &bk,
backupKey.cConstFixedArray(), (*[C.SignalBACKUP_KEY_LEN]C.uint8_t)(unsafe.Pointer(backupKey)),
backupID.cConstFixedArray(), (*[BackupIDLength]C.uint8_t)(unsafe.Pointer(backupID)),
nil, // TODO what's a forward secrecy token? nil, // TODO what's a forward secrecy token?
) )
runtime.KeepAlive(backupKey) runtime.KeepAlive(backupKey)
@ -83,28 +82,26 @@ func (bk *MessageBackupKey) Destroy() error {
return wrapError(C.signal_message_backup_key_destroy(bk.mutPtr())) return wrapError(C.signal_message_backup_key_destroy(bk.mutPtr()))
} }
func (bk *MessageBackupKey) GetHMACKey() ([MessageBackupKeyBytesLength]byte, error) { func (bk *MessageBackupKey) GetHMACKey() ([32]byte, error) {
var out messageBackupKeyBytes var out [32]byte
signalFfiError := C.signal_message_backup_key_get_hmac_key( signalFfiError := C.signal_message_backup_key_get_hmac_key(
out.cFixedArray(), (*[32]C.uint8_t)(unsafe.Pointer(&out)),
bk.constPtr(), bk.constPtr(),
) )
runtime.KeepAlive(bk)
if signalFfiError != nil { if signalFfiError != nil {
return [MessageBackupKeyBytesLength]byte(out), wrapError(signalFfiError) return out, wrapError(signalFfiError)
} }
return [MessageBackupKeyBytesLength]byte(out), nil return out, nil
} }
func (bk *MessageBackupKey) GetAESKey() ([MessageBackupKeyBytesLength]byte, error) { func (bk *MessageBackupKey) GetAESKey() ([32]byte, error) {
var out messageBackupKeyBytes var out [32]byte
signalFfiError := C.signal_message_backup_key_get_aes_key( signalFfiError := C.signal_message_backup_key_get_aes_key(
out.cFixedArray(), (*[32]C.uint8_t)(unsafe.Pointer(&out)),
bk.constPtr(), bk.constPtr(),
) )
runtime.KeepAlive(bk)
if signalFfiError != nil { if signalFfiError != nil {
return [MessageBackupKeyBytesLength]byte(out), wrapError(signalFfiError) return out, wrapError(signalFfiError)
} }
return [MessageBackupKeyBytesLength]byte(out), nil return out, nil
} }

View file

@ -43,7 +43,6 @@ func DecryptPreKey(ctx context.Context, preKeyMessage *PreKeyMessage, fromAddres
) )
runtime.KeepAlive(preKeyMessage) runtime.KeepAlive(preKeyMessage)
runtime.KeepAlive(fromAddress) runtime.KeepAlive(fromAddress)
runtime.KeepAlive(localAddress)
if signalFfiError != nil { if signalFfiError != nil {
return nil, callbackCtx.wrapError(signalFfiError) return nil, callbackCtx.wrapError(signalFfiError)
} }

View file

@ -41,7 +41,6 @@ func ProcessPreKeyBundle(ctx context.Context, bundle *PreKeyBundle, forAddress,
) )
runtime.KeepAlive(bundle) runtime.KeepAlive(bundle)
runtime.KeepAlive(forAddress) runtime.KeepAlive(forAddress)
runtime.KeepAlive(localAddress)
return callbackCtx.wrapError(signalFfiError) return callbackCtx.wrapError(signalFfiError)
} }
@ -119,10 +118,10 @@ func (pkb *PreKeyBundle) constPtr() C.SignalConstPointerPreKeyBundle {
func (pkb *PreKeyBundle) Clone() (*PreKeyBundle, error) { func (pkb *PreKeyBundle) Clone() (*PreKeyBundle, error) {
var cloned C.SignalMutPointerPreKeyBundle var cloned C.SignalMutPointerPreKeyBundle
signalFfiError := C.signal_pre_key_bundle_clone(&cloned, pkb.constPtr()) signalFfiError := C.signal_pre_key_bundle_clone(&cloned, pkb.constPtr())
runtime.KeepAlive(pkb)
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
runtime.KeepAlive(pkb)
return wrapPreKeyBundle(cloned.raw), nil return wrapPreKeyBundle(cloned.raw), nil
} }
@ -138,9 +137,9 @@ func (pkb *PreKeyBundle) CancelFinalizer() {
func (pkb *PreKeyBundle) GetIdentityKey() (*IdentityKey, error) { func (pkb *PreKeyBundle) GetIdentityKey() (*IdentityKey, error) {
var pk C.SignalMutPointerPublicKey var pk C.SignalMutPointerPublicKey
signalFfiError := C.signal_pre_key_bundle_get_identity_key(&pk, pkb.constPtr()) signalFfiError := C.signal_pre_key_bundle_get_identity_key(&pk, pkb.constPtr())
runtime.KeepAlive(pkb)
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
runtime.KeepAlive(pkb)
return NewIdentityKeyFromPublicKey(wrapPublicKey(pk.raw)) return NewIdentityKeyFromPublicKey(wrapPublicKey(pk.raw))
} }

View file

@ -25,7 +25,6 @@ import "C"
import ( import (
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt"
"runtime" "runtime"
"unsafe" "unsafe"
@ -33,38 +32,12 @@ import (
"go.mau.fi/util/random" "go.mau.fi/util/random"
) )
const ProfileKeyLength = 32 const ProfileKeyLength = C.SignalPROFILE_KEY_LEN
const AccessKeyLength = 16
const ProfileKeyVersionLength = 64
type ProfileKey [ProfileKeyLength]byte type ProfileKey [ProfileKeyLength]byte
type ProfileKeyCommitment = fixedArray97 type ProfileKeyCommitment [C.SignalPROFILE_KEY_COMMITMENT_LEN]byte
type ProfileKeyVersion [ProfileKeyVersionLength]byte type ProfileKeyVersion [C.SignalPROFILE_KEY_VERSION_ENCODED_LEN]byte
type AccessKey [AccessKeyLength]byte type AccessKey [C.SignalACCESS_KEY_LEN]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) { func DeserializeProfileKey(bytes []byte) (*ProfileKey, error) {
if len(bytes) == 0 { if len(bytes) == 0 {
@ -100,7 +73,7 @@ func (ak *AccessKey) Xor(other *AccessKey) *AccessKey {
return ak return ak
} }
var result AccessKey var result AccessKey
for i := 0; i < AccessKeyLength; i++ { for i := 0; i < C.SignalACCESS_KEY_LEN; i++ {
result[i] = ak[i] ^ other[i] result[i] = ak[i] ^ other[i]
} }
return &result return &result
@ -111,12 +84,13 @@ func (ak *AccessKey) String() string {
} }
func (pk *ProfileKey) GetCommitment(u uuid.UUID) (*ProfileKeyCommitment, error) { func (pk *ProfileKey) GetCommitment(u uuid.UUID) (*ProfileKeyCommitment, error) {
var result ProfileKeyCommitment c_result := [C.SignalPROFILE_KEY_COMMITMENT_LEN]C.uchar{}
c_uuid := NewACIServiceID(u).cConstFixedArray() c_profileKey := (*[C.SignalPROFILE_KEY_LEN]C.uchar)(unsafe.Pointer(pk))
c_uuid := NewACIServiceID(u).CFixedBytes()
signalFfiError := C.signal_profile_key_get_commitment( signalFfiError := C.signal_profile_key_get_commitment(
result.cFixedArray(), &c_result,
pk.cConstFixedArray(), c_profileKey,
c_uuid, c_uuid,
) )
runtime.KeepAlive(pk) runtime.KeepAlive(pk)
@ -126,16 +100,19 @@ func (pk *ProfileKey) GetCommitment(u uuid.UUID) (*ProfileKeyCommitment, error)
return nil, wrapError(signalFfiError) 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 return &result, nil
} }
func (pk *ProfileKey) GetProfileKeyVersion(u uuid.UUID) (*ProfileKeyVersion, error) { func (pk *ProfileKey) GetProfileKeyVersion(u uuid.UUID) (*ProfileKeyVersion, error) {
var result ProfileKeyVersion c_result := [C.SignalPROFILE_KEY_VERSION_ENCODED_LEN]C.uchar{}
c_uuid := NewACIServiceID(u).cConstFixedArray() c_profileKey := (*[C.SignalPROFILE_KEY_LEN]C.uchar)(unsafe.Pointer(pk))
c_uuid := NewACIServiceID(u).CFixedBytes()
signalFfiError := C.signal_profile_key_get_profile_key_version( signalFfiError := C.signal_profile_key_get_profile_key_version(
result.cFixedArray(), &c_result,
pk.cConstFixedArray(), c_profileKey,
c_uuid, c_uuid,
) )
runtime.KeepAlive(pk) runtime.KeepAlive(pk)
@ -145,15 +122,18 @@ func (pk *ProfileKey) GetProfileKeyVersion(u uuid.UUID) (*ProfileKeyVersion, err
return nil, wrapError(signalFfiError) 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 return &result, nil
} }
func (pk *ProfileKey) DeriveAccessKey() (*AccessKey, error) { func (pk *ProfileKey) DeriveAccessKey() (*AccessKey, error) {
var result AccessKey c_result := [C.SignalACCESS_KEY_LEN]C.uchar{}
c_profileKey := (*[C.SignalPROFILE_KEY_LEN]C.uchar)(unsafe.Pointer(pk))
signalFfiError := C.signal_profile_key_derive_access_key( signalFfiError := C.signal_profile_key_derive_access_key(
result.cFixedArray(), &c_result,
pk.cConstFixedArray(), c_profileKey,
) )
runtime.KeepAlive(pk) runtime.KeepAlive(pk)
@ -161,37 +141,31 @@ func (pk *ProfileKey) DeriveAccessKey() (*AccessKey, error) {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
var result AccessKey
copy(result[:], C.GoBytes(unsafe.Pointer(&c_result), C.int(C.SignalACCESS_KEY_LEN)))
return &result, nil return &result, nil
} }
const ExpiringProfileKeyCredentialResponseLength = 497 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 ProfileKeyCredentialResponse []byte
type ProfileKeyCredentialPresentation []byte type ProfileKeyCredentialPresentation []byte
type ExpiringProfileKeyCredential = fixedArray153 type ExpiringProfileKeyCredential [C.SignalEXPIRING_PROFILE_KEY_CREDENTIAL_LEN]byte
type ExpiringProfileKeyCredentialResponse = fixedArray497 type ExpiringProfileKeyCredentialResponse [C.SignalEXPIRING_PROFILE_KEY_CREDENTIAL_RESPONSE_LEN]byte
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) { func CreateProfileKeyCredentialRequestContext(serverPublicParams *ServerPublicParams, u uuid.UUID, profileKey ProfileKey) (*ProfileKeyCredentialRequestContext, error) {
var result ProfileKeyCredentialRequestContext c_result := [C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_CONTEXT_LEN]C.uchar{}
randBytes := Randomness(random.Bytes(RandomnessLength)) randBytes := [32]byte(random.Bytes(32))
c_uuid := NewACIServiceID(u).cConstFixedArray() 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()
signalFfiError := C.signal_server_public_params_create_profile_key_credential_request_context_deterministic( signalFfiError := C.signal_server_public_params_create_profile_key_credential_request_context_deterministic(
result.cFixedArray(), &c_result,
C.SignalConstPointerServerPublicParams{serverPublicParams}, C.SignalConstPointerServerPublicParams{serverPublicParams},
randBytes.cConstFixedArray(), c_random,
c_uuid, c_uuid,
profileKey.cConstFixedArray(), c_profileKey,
) )
runtime.KeepAlive(u) runtime.KeepAlive(u)
runtime.KeepAlive(profileKey) runtime.KeepAlive(profileKey)
@ -199,27 +173,27 @@ func CreateProfileKeyCredentialRequestContext(serverPublicParams *ServerPublicPa
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
result := ProfileKeyCredentialRequestContext(C.GoBytes(unsafe.Pointer(&c_result), C.int(C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_CONTEXT_LEN)))
return &result, nil return &result, nil
} }
func (p *ProfileKeyCredentialRequestContext) ProfileKeyCredentialRequestContextGetRequest() (*ProfileKeyCredentialRequest, error) { func (p *ProfileKeyCredentialRequestContext) ProfileKeyCredentialRequestContextGetRequest() (*ProfileKeyCredentialRequest, error) {
var result ProfileKeyCredentialRequest c_result := [C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_LEN]C.uchar{}
c_context := (*[C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_CONTEXT_LEN]C.uchar)(unsafe.Pointer(p))
signalFfiError := C.signal_profile_key_credential_request_context_get_request( signalFfiError := C.signal_profile_key_credential_request_context_get_request(
result.cFixedArray(), &c_result,
p.cConstFixedArray(), c_context,
) )
runtime.KeepAlive(p) runtime.KeepAlive(p)
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
result := ProfileKeyCredentialRequest(C.GoBytes(unsafe.Pointer(&c_result), C.int(C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_LEN)))
return &result, nil return &result, nil
} }
func NewExpiringProfileKeyCredentialResponse(b []byte) (*ExpiringProfileKeyCredentialResponse, error) { func NewExpiringProfileKeyCredentialResponse(b []byte) (*ExpiringProfileKeyCredentialResponse, error) {
if len(b) != ExpiringProfileKeyCredentialResponseLength {
return nil, fmt.Errorf("invalid expiring profile key credential response length %d (expected %d)", len(b), ExpiringProfileKeyCredentialResponseLength)
}
borrowedBuffer := BytesToBuffer(b) borrowedBuffer := BytesToBuffer(b)
signalFfiError := C.signal_expiring_profile_key_credential_response_check_valid_contents(borrowedBuffer) signalFfiError := C.signal_expiring_profile_key_credential_response_check_valid_contents(borrowedBuffer)
runtime.KeepAlive(b) runtime.KeepAlive(b)
@ -231,12 +205,12 @@ func NewExpiringProfileKeyCredentialResponse(b []byte) (*ExpiringProfileKeyCrede
} }
func ReceiveExpiringProfileKeyCredential(spp *ServerPublicParams, requestContext *ProfileKeyCredentialRequestContext, response *ExpiringProfileKeyCredentialResponse, currentTimeInSeconds uint64) (*ExpiringProfileKeyCredential, error) { func ReceiveExpiringProfileKeyCredential(spp *ServerPublicParams, requestContext *ProfileKeyCredentialRequestContext, response *ExpiringProfileKeyCredentialResponse, currentTimeInSeconds uint64) (*ExpiringProfileKeyCredential, error) {
var credential ExpiringProfileKeyCredential c_credential := [C.SignalEXPIRING_PROFILE_KEY_CREDENTIAL_LEN]C.uchar{}
signalFfiError := C.signal_server_public_params_receive_expiring_profile_key_credential( signalFfiError := C.signal_server_public_params_receive_expiring_profile_key_credential(
credential.cFixedArray(), &c_credential,
C.SignalConstPointerServerPublicParams{spp}, C.SignalConstPointerServerPublicParams{spp},
requestContext.cConstFixedArray(), (*[C.SignalPROFILE_KEY_CREDENTIAL_REQUEST_CONTEXT_LEN]C.uchar)(unsafe.Pointer(requestContext)),
response.cConstFixedArray(), (*[C.SignalEXPIRING_PROFILE_KEY_CREDENTIAL_RESPONSE_LEN]C.uchar)(unsafe.Pointer(response)),
(C.uint64_t)(currentTimeInSeconds), (C.uint64_t)(currentTimeInSeconds),
) )
runtime.KeepAlive(requestContext) runtime.KeepAlive(requestContext)
@ -245,6 +219,8 @@ func ReceiveExpiringProfileKeyCredential(spp *ServerPublicParams, requestContext
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) 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 return &credential, nil
} }
@ -255,21 +231,25 @@ func (a ProfileKeyCredentialPresentation) CheckValidContents() error {
} }
func (a ProfileKeyCredentialPresentation) UUIDCiphertext() (UUIDCiphertext, error) { func (a ProfileKeyCredentialPresentation) UUIDCiphertext() (UUIDCiphertext, error) {
var out UUIDCiphertext out := [C.SignalUUID_CIPHERTEXT_LEN]C.uchar{}
signalFfiError := C.signal_profile_key_credential_presentation_get_uuid_ciphertext(out.cFixedArray(), BytesToBuffer(a)) signalFfiError := C.signal_profile_key_credential_presentation_get_uuid_ciphertext(&out, BytesToBuffer(a))
runtime.KeepAlive(a) runtime.KeepAlive(a)
if signalFfiError != nil { if signalFfiError != nil {
return UUIDCiphertext{}, wrapError(signalFfiError) return UUIDCiphertext{}, wrapError(signalFfiError)
} }
return out, nil var result UUIDCiphertext
copy(result[:], C.GoBytes(unsafe.Pointer(&out), C.int(C.SignalUUID_CIPHERTEXT_LEN)))
return result, nil
} }
func (a ProfileKeyCredentialPresentation) ProfileKeyCiphertext() (ProfileKeyCiphertext, error) { func (a ProfileKeyCredentialPresentation) ProfileKeyCiphertext() (ProfileKeyCiphertext, error) {
var out ProfileKeyCiphertext out := [C.SignalPROFILE_KEY_CIPHERTEXT_LEN]C.uchar{}
signalFfiError := C.signal_profile_key_credential_presentation_get_profile_key_ciphertext(out.cFixedArray(), BytesToBuffer(a)) signalFfiError := C.signal_profile_key_credential_presentation_get_profile_key_ciphertext(&out, BytesToBuffer(a))
runtime.KeepAlive(a) runtime.KeepAlive(a)
if signalFfiError != nil { if signalFfiError != nil {
return ProfileKeyCiphertext{}, wrapError(signalFfiError) return ProfileKeyCiphertext{}, wrapError(signalFfiError)
} }
return out, nil var result ProfileKeyCiphertext
copy(result[:], C.GoBytes(unsafe.Pointer(&out), C.int(C.SignalPROFILE_KEY_CIPHERTEXT_LEN)))
return result, nil
} }

View file

@ -169,19 +169,6 @@ const (
UnidentifiedSenderMessageContentHintImplicit UnidentifiedSenderMessageContentHint = 2 UnidentifiedSenderMessageContentHintImplicit UnidentifiedSenderMessageContentHint = 2
) )
func (hint UnidentifiedSenderMessageContentHint) String() string {
switch hint {
case UnidentifiedSenderMessageContentHintDefault:
return "Default"
case UnidentifiedSenderMessageContentHintResendable:
return "Resendable"
case UnidentifiedSenderMessageContentHintImplicit:
return "Implicit"
default:
return fmt.Sprintf("Unknown(%d)", hint)
}
}
type UnidentifiedSenderMessageContent struct { type UnidentifiedSenderMessageContent struct {
nc noCopy nc noCopy
ptr *C.SignalUnidentifiedSenderMessageContent ptr *C.SignalUnidentifiedSenderMessageContent

View file

@ -208,7 +208,6 @@ func (sc *SenderCertificate) Validate(trustRoots []*PublicKey, ts time.Time) (bo
C.uint64_t(ts.UnixMilli()), C.uint64_t(ts.UnixMilli()),
) )
runtime.KeepAlive(sc) runtime.KeepAlive(sc)
runtime.KeepAlive(trustRoots)
runtime.KeepAlive(constRoots) runtime.KeepAlive(constRoots)
if signalFfiError != nil { if signalFfiError != nil {
return false, wrapError(signalFfiError) return false, wrapError(signalFfiError)

View file

@ -101,7 +101,6 @@ func (sc *SenderKeyDistributionMessage) CancelFinalizer() {
func (sc *SenderKeyDistributionMessage) Serialize() ([]byte, error) { func (sc *SenderKeyDistributionMessage) Serialize() ([]byte, error) {
var serialized C.SignalOwnedBuffer = C.SignalOwnedBuffer{} var serialized C.SignalOwnedBuffer = C.SignalOwnedBuffer{}
signalFfiError := C.signal_sender_key_distribution_message_serialize(&serialized, sc.constPtr()) signalFfiError := C.signal_sender_key_distribution_message_serialize(&serialized, sc.constPtr())
runtime.KeepAlive(sc)
if signalFfiError != nil { if signalFfiError != nil {
return nil, wrapError(signalFfiError) return nil, wrapError(signalFfiError)
} }
@ -117,7 +116,6 @@ func (sc *SenderKeyDistributionMessage) Process(ctx context.Context, sender *Add
callbackCtx.wrapSenderKeyStore(store), callbackCtx.wrapSenderKeyStore(store),
) )
runtime.KeepAlive(sender) runtime.KeepAlive(sender)
runtime.KeepAlive(sc)
if signalFfiError != nil { if signalFfiError != nil {
return callbackCtx.wrapError(signalFfiError) return callbackCtx.wrapError(signalFfiError)
} }

View file

@ -24,16 +24,15 @@ import "C"
import ( import (
"fmt" "fmt"
"runtime" "runtime"
"unsafe"
) )
type ServerPublicParams = C.SignalServerPublicParams type ServerPublicParams = C.SignalServerPublicParams
type NotarySignature = fixedArray64 type NotarySignature [C.SignalSIGNATURE_LEN]byte
const ServerPublicParamsLength = 673
func DeserializeServerPublicParams(params []byte) (*ServerPublicParams, error) { func DeserializeServerPublicParams(params []byte) (*ServerPublicParams, error) {
if len(params) != ServerPublicParamsLength { if len(params) != C.SignalSERVER_PUBLIC_PARAMS_LEN {
return nil, fmt.Errorf("invalid server public params length: %d (expected %d)", len(params), ServerPublicParamsLength) return nil, fmt.Errorf("invalid server public params length: %d (expected %d)", len(params), int(C.SignalSERVER_PUBLIC_PARAMS_LEN))
} }
var out C.SignalMutPointerServerPublicParams var out C.SignalMutPointerServerPublicParams
signalFfiError := C.signal_server_public_params_deserialize(&out, BytesToBuffer(params[:])) signalFfiError := C.signal_server_public_params_deserialize(&out, BytesToBuffer(params[:]))
@ -48,10 +47,11 @@ func ServerPublicParamsVerifySignature(
messageBytes []byte, messageBytes []byte,
NotarySignature NotarySignature, NotarySignature NotarySignature,
) error { ) error {
c_notarySignature := (*[C.SignalSIGNATURE_LEN]C.uint8_t)(unsafe.Pointer(&NotarySignature[0]))
signalFfiError := C.signal_server_public_params_verify_signature( signalFfiError := C.signal_server_public_params_verify_signature(
C.SignalConstPointerServerPublicParams{serverPublicParams}, C.SignalConstPointerServerPublicParams{serverPublicParams},
BytesToBuffer(messageBytes), BytesToBuffer(messageBytes),
NotarySignature.cConstFixedArray(), c_notarySignature,
) )
runtime.KeepAlive(messageBytes) runtime.KeepAlive(messageBytes)
return wrapError(signalFfiError) return wrapError(signalFfiError)

View file

@ -133,10 +133,7 @@ func (s ServiceID) MarshalZerologObject(e *zerolog.Event) {
e.Stringer("uuid", s.UUID) e.Stringer("uuid", s.UUID)
} }
const ServiceIDUUIDLength = 16 type ServiceIDFixedBytes [17]byte
const ServiceIDFixedBytesLength = 17
type ServiceIDFixedBytes = fixedArray17
func (s ServiceID) FixedBytes() *ServiceIDFixedBytes { func (s ServiceID) FixedBytes() *ServiceIDFixedBytes {
var result ServiceIDFixedBytes var result ServiceIDFixedBytes
@ -165,18 +162,18 @@ func ServiceIDFromString(val string) (ServiceID, error) {
} }
func ServiceIDFromBytes(bytes []byte) (ServiceID, error) { func ServiceIDFromBytes(bytes []byte) (ServiceID, error) {
if len(bytes) == ServiceIDUUIDLength { if len(bytes) == 16 {
return NewACIServiceID(uuid.UUID(bytes)), nil return NewACIServiceID(uuid.UUID(bytes)), nil
} else if len(bytes) == ServiceIDFixedBytesLength { } else if len(bytes) == 17 {
return ServiceID{ return ServiceID{
Type: ServiceIDType(bytes[0]), Type: ServiceIDType(bytes[0]),
UUID: uuid.UUID(bytes[1:]), UUID: uuid.UUID(bytes[1:]),
}, nil }, nil
} }
return EmptyServiceID, fmt.Errorf("invalid ServiceID byte length: %d (expected %d or %d)", len(bytes), ServiceIDUUIDLength, ServiceIDFixedBytesLength) return EmptyServiceID, fmt.Errorf("invalid ServiceID byte length: %d (expected 16 or 17)", len(bytes))
} }
func ServiceIDFromCFixedBytes(serviceID *C.SignalType_FixedArray17_uint8_t) ServiceID { func ServiceIDFromCFixedBytes(serviceID *C.SignalServiceIdFixedWidthBinaryBytes) ServiceID {
var id ServiceID var id ServiceID
fixedBytes := (*ServiceIDFixedBytes)(unsafe.Pointer(serviceID)) fixedBytes := (*ServiceIDFixedBytes)(unsafe.Pointer(serviceID))
id.Type = ServiceIDType(fixedBytes[0]) id.Type = ServiceIDType(fixedBytes[0])
@ -184,10 +181,6 @@ func ServiceIDFromCFixedBytes(serviceID *C.SignalType_FixedArray17_uint8_t) Serv
return id return id
} }
func (s ServiceID) cFixedArray() *C.SignalType_FixedArray17_uint8_t { func (s ServiceID) CFixedBytes() cPNIType {
return s.FixedBytes().cFixedArray() return cPNIType(unsafe.Pointer(s.FixedBytes()))
}
func (s ServiceID) cConstFixedArray() cFixedArray17Compat {
return cFixedArray17Compat(s.cFixedArray())
} }

View file

@ -0,0 +1,11 @@
//go:build darwin || android || ios || (windows && arm64)
package libsignalgo
/*
#include "./libsignal-ffi.h"
#include <stdlib.h>
*/
import "C"
type cPNIType = *C.SignalServiceIdFixedWidthBinaryBytes

View file

@ -0,0 +1,14 @@
//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

View file

@ -105,6 +105,7 @@ func (sr *SessionRecord) HasCurrentState() (bool, error) {
signalFfiError := C.signal_session_record_has_usable_sender_chain( signalFfiError := C.signal_session_record_has_usable_sender_chain(
&result, &result,
sr.constPtr(), sr.constPtr(),
C.double(0.0),
C.uint64_t(time.Now().Unix()), C.uint64_t(time.Now().Unix()),
) )
runtime.KeepAlive(sr) runtime.KeepAlive(sr)

View file

@ -1,5 +0,0 @@
// Generated by update-ffi.sh; DO NOT EDIT.
package signalversion
const Version = "v0.102.2"

View file

@ -1,10 +1,11 @@
#!/bin/sh #!/bin/sh
cd /data cd /data
export RUSTFLAGS="-Ctarget-feature=-crt-static" RUSTC_WRAPPER="" export RUSTFLAGS="-Ctarget-feature=-crt-static" RUSTC_WRAPPER=""
apk add --no-cache git make cmake protobuf-dev musl-dev g++ clang-dev apk add --no-cache git make cmake protobuf-dev musl-dev g++ clang-dev cbindgen
cd libsignal cd libsignal
cargo build -p libsignal-ffi --release cargo build -p libsignal-ffi --release
cbindgen --profile release rust/bridge/ffi -o libsignal-ffi.h
cd .. cd ..
mv libsignal/target/release/libsignal_ffi.a . mv libsignal/target/release/libsignal_ffi.a .
cp libsignal/swift/Sources/SignalFfi/signal_ffi.h libsignal-ffi.h mv libsignal/libsignal-ffi.h .
chown 1000:1000 libsignal_ffi.a libsignal-ffi.h chown 1000:1000 libsignal_ffi.a libsignal-ffi.h version.go

View file

@ -1,9 +1,9 @@
#!/bin/bash #!/bin/bash
docker run --rm -itv $(pwd):/data rust:1-alpine /data/update-ffi-docker-inner.sh docker run --rm -itv $(pwd):/data rust:1-alpine /data/update-ffi-docker-inner.sh
echo "// Generated by update-ffi.sh; DO NOT EDIT." > signalversion/version.go echo "// Generated by update-ffi.sh; DO NOT EDIT." > version.go
echo >> signalversion/version.go echo >> version.go
echo "package signalversion" >> signalversion/version.go echo "package libsignalgo" >> version.go
echo >> signalversion/version.go echo >> version.go
cd libsignal cd libsignal
echo "const Version = \"$(git describe --tags --always)\"" >> ../signalversion/version.go echo "const Version = \"$(git describe --tags --always)\"" >> ../version.go

View file

@ -12,10 +12,10 @@ if [ ! -d "$LIBSIGNAL_DIRECTORY" ]; then
exit 1 exit 1
fi fi
echo "// Generated by update-ffi.sh; DO NOT EDIT." > signalversion/version.go echo "// Generated by update-ffi.sh; DO NOT EDIT." > version.go
echo >> signalversion/version.go echo >> version.go
echo "package signalversion" >> signalversion/version.go echo "package libsignalgo" >> version.go
echo >> signalversion/version.go echo >> version.go
# Store the current working directory # Store the current working directory
ORIGINAL_DIR="$(pwd)" ORIGINAL_DIR="$(pwd)"
@ -23,16 +23,19 @@ ORIGINAL_DIR="$(pwd)"
# Navigate to libsignal directory # Navigate to libsignal directory
cd "$LIBSIGNAL_DIRECTORY" cd "$LIBSIGNAL_DIRECTORY"
echo "const Version = \"$(git describe --tags --always)\"" >> ../signalversion/version.go echo "const Version = \"$(git describe --tags --always)\"" >> ../version.go
# Build libsignal # Build libsignal
cargo build -p libsignal-ffi --release 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 # Navigate back to the original directory
cd "$ORIGINAL_DIR" cd "$ORIGINAL_DIR"
# Copy files from the libsignal directory # Copy files from the libsignal directory
cp "${LIBSIGNAL_DIRECTORY}/target/release/libsignal_ffi.a" . cp "${LIBSIGNAL_DIRECTORY}/target/release/libsignal_ffi.a" .
cp "${LIBSIGNAL_DIRECTORY}/swift/Sources/SignalFfi/signal_ffi.h" libsignal-ffi.h cp "${LIBSIGNAL_DIRECTORY}/libsignal-ffi.h" .
echo "Files copied successfully." echo "Files copied successfully."

View file

@ -0,0 +1,5 @@
// Generated by update-ffi.sh; DO NOT EDIT.
package libsignalgo
const Version = "v0.97.2"

View file

@ -35,7 +35,7 @@ import (
"go.mau.fi/mautrix-signal/pkg/msgconv/matrixfmt" "go.mau.fi/mautrix-signal/pkg/msgconv/matrixfmt"
"go.mau.fi/mautrix-signal/pkg/signalid" "go.mau.fi/mautrix-signal/pkg/signalid"
"go.mau.fi/mautrix-signal/pkg/signalmeow" "go.mau.fi/mautrix-signal/pkg/signalmeow"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
) )
func (mc *MessageConverter) ToSignal( func (mc *MessageConverter) ToSignal(

View file

@ -23,8 +23,8 @@ import (
"go.mau.fi/util/exslices" "go.mau.fi/util/exslices"
"go.mau.fi/util/ptr" "go.mau.fi/util/ptr"
signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/backuppb" "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/backuppb"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb"
) )
func boolToInt(b bool) int { func boolToInt(b bool) int {

View file

@ -23,7 +23,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"mime"
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
@ -43,7 +42,7 @@ import (
"go.mau.fi/mautrix-signal/pkg/msgconv/signalfmt" "go.mau.fi/mautrix-signal/pkg/msgconv/signalfmt"
"go.mau.fi/mautrix-signal/pkg/signalid" "go.mau.fi/mautrix-signal/pkg/signalid"
"go.mau.fi/mautrix-signal/pkg/signalmeow" "go.mau.fi/mautrix-signal/pkg/signalmeow"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
) )
var ( var (
@ -570,20 +569,15 @@ func (mc *MessageConverter) reuploadAttachment(ctx context.Context, att *signalp
content.Info.Blurhash = att.GetBlurHash() content.Info.Blurhash = att.GetBlurHash()
content.Info.AnoaBlurhash = att.GetBlurHash() content.Info.AnoaBlurhash = att.GetBlurHash()
} }
plainMime, _, _ := mime.ParseMediaType(content.Info.MimeType) switch strings.Split(content.Info.MimeType, "/")[0] {
// Supported mime types from https://github.com/signalapp/Signal-Desktop/blob/main/ts/util/GoogleChrome.std.ts case "image":
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 content.MsgType = event.MsgImage
case "video/mp4", "video/ogg", "video/webm": case "video":
content.MsgType = event.MsgVideo content.MsgType = event.MsgVideo
case "audio":
content.MsgType = event.MsgAudio
default: default:
if strings.HasPrefix(plainMime, "audio/") && !strings.HasSuffix(plainMime, "aiff") { content.MsgType = event.MsgFile
content.MsgType = event.MsgAudio
} else {
content.MsgType = event.MsgFile
}
} }
var extra map[string]any var extra map[string]any
if att.GetFlags()&uint32(signalpb.AttachmentPointer_GIF) != 0 { if att.GetFlags()&uint32(signalpb.AttachmentPointer_GIF) != 0 {

View file

@ -35,7 +35,7 @@ import (
"go.mau.fi/mautrix-signal/pkg/signalid" "go.mau.fi/mautrix-signal/pkg/signalid"
"go.mau.fi/mautrix-signal/pkg/signalmeow" "go.mau.fi/mautrix-signal/pkg/signalmeow"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
) )
const StickerSourceID = "signal" const StickerSourceID = "signal"
@ -158,24 +158,18 @@ func (mc *MessageConverter) DownloadImagePack(ctx context.Context, url string) (
imagesByID[stickerID] = mxc imagesByID[stickerID] = mxc
return mxc, nil return mxc, nil
} }
duplicateCounter := make(map[string]int)
for _, sticker := range manifest.Stickers { for _, sticker := range manifest.Stickers {
mxc, err := uploadImage(sticker) mxc, err := uploadImage(sticker)
if err != nil { if err != nil {
return nil, err return nil, err
} }
shortcode := emojishortcodes.Get(sticker.GetEmoji()) shortcode := emojishortcodes.Get(sticker.GetEmoji())
if _, alreadyExists := content.Images[shortcode]; alreadyExists { realShortcode := shortcode
counter, ok := duplicateCounter[shortcode] i := 2
if !ok { for _, alreadyExists := content.Images[realShortcode]; alreadyExists; i++ {
counter = 2 realShortcode = fmt.Sprintf("%s_%d", shortcode, i)
} else {
counter++
}
duplicateCounter[shortcode] = counter
shortcode = fmt.Sprintf("%s_%d", shortcode, counter)
} }
content.Images[shortcode] = &event.ImagePackImage{ content.Images[realShortcode] = &event.ImagePackImage{
URL: mxc, URL: mxc,
Body: sticker.GetEmoji(), Body: sticker.GetEmoji(),
Info: &event.FileInfo{ Info: &event.FileInfo{

View file

@ -21,7 +21,7 @@ import (
"maunium.net/go/mautrix/event" "maunium.net/go/mautrix/event"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
) )
func Parse(ctx context.Context, parser *HTMLParser, content *event.MessageEventContent) (string, []*signalpb.BodyRange) { func Parse(ctx context.Context, parser *HTMLParser, content *event.MessageEventContent) (string, []*signalpb.BodyRange) {

View file

@ -111,13 +111,22 @@ func TestParse_HTML(t *testing.T) {
{ {
name: "List", 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>", 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* ```\n hmm\n meow\n ```\n* > meow\n > \n > # meow", out: "* woof\n* meow\n* hmm\n meow\n* > meow\n > \n > # meow",
ent: signalfmt.BodyRangeList{{ ent: signalfmt.BodyRangeList{{
Start: 9, Start: 9,
Length: 4, Length: 4,
Value: signalfmt.StyleBold, Value: signalfmt.StyleBold,
}, { }, {
Start: 57, 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,
Length: 6, Length: 6,
Value: signalfmt.StyleBold, Value: signalfmt.StyleBold,
}}, }},
@ -125,13 +134,21 @@ func TestParse_HTML(t *testing.T) {
{ {
name: "OrderedList", 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>", 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. ```\n hmm\n meow\n ```\n12. > meow\n > \n > # meow", out: "9. woof\n10. meow\n11. hmm\n meow\n12. > meow\n > \n > # meow",
ent: signalfmt.BodyRangeList{{ ent: signalfmt.BodyRangeList{{
Start: 13, Start: 13,
Length: 4, Length: 4,
Value: signalfmt.StyleBold, Value: signalfmt.StyleBold,
}, { }, {
Start: 75, Start: 22,
Length: 3,
Value: signalfmt.StyleMonospace,
}, {
Start: 30,
Length: 4,
Value: signalfmt.StyleMonospace,
}, {
Start: 59,
Length: 6, Length: 6,
Value: signalfmt.StyleBold, Value: signalfmt.StyleBold,
}}, }},

View file

@ -404,17 +404,17 @@ func (parser *HTMLParser) tagToString(node *html.Node, ctx Context) *EntityStrin
return NewEntityString("---") return NewEntityString("---")
case "pre": case "pre":
var preStr *EntityString var preStr *EntityString
var language string //var language string
if node.FirstChild != nil && node.FirstChild.Type == html.ElementNode && node.FirstChild.Data == "code" { if node.FirstChild != nil && node.FirstChild.Type == html.ElementNode && node.FirstChild.Data == "code" {
class := parser.getAttribute(node.FirstChild, "class") //class := parser.getAttribute(node.FirstChild, "class")
if strings.HasPrefix(class, "language-") { //if strings.HasPrefix(class, "language-") {
language = class[len("language-"):] // language = class[len("language-"):]
} //}
preStr = parser.nodeToString(node.FirstChild.FirstChild, ctx.WithWhitespace()) preStr = parser.nodeToString(node.FirstChild.FirstChild, ctx.WithWhitespace())
} else { } else {
preStr = parser.nodeToString(node.FirstChild, ctx.WithWhitespace()) preStr = parser.nodeToString(node.FirstChild, ctx.WithWhitespace())
} }
return NewEntityString(fmt.Sprintf("```%s\n", language)).Append(preStr).AppendString("\n```") return preStr.Format(signalfmt.StyleMonospace)
default: default:
return parser.nodeToTagAwareString(node.FirstChild, ctx) return parser.nodeToTagAwareString(node.FirstChild, ctx)
} }

View file

@ -28,7 +28,7 @@ import (
"maunium.net/go/mautrix/event" "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id" "maunium.net/go/mautrix/id"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
) )
type UserInfo struct { type UserInfo struct {
@ -86,9 +86,6 @@ func Parse(ctx context.Context, message string, ranges []*signalpb.BodyRange, pa
Start: int(*r.Start), Start: int(*r.Start),
Length: int(*r.Length), Length: int(*r.Length),
}.TruncateEnd(maxLength) }.TruncateEnd(maxLength)
if br.Start >= maxLength {
continue
}
var mentionACI uuid.UUID var mentionACI uuid.UUID
switch rv := r.GetAssociatedValue().(type) { switch rv := r.GetAssociatedValue().(type) {
case *signalpb.BodyRange_Style_: case *signalpb.BodyRange_Style_:

View file

@ -27,7 +27,7 @@ import (
"maunium.net/go/mautrix/id" "maunium.net/go/mautrix/id"
"go.mau.fi/mautrix-signal/pkg/msgconv/signalfmt" "go.mau.fi/mautrix-signal/pkg/msgconv/signalfmt"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
) )
var realUser = uuid.New() var realUser = uuid.New()

View file

@ -21,7 +21,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
) )
type BodyRangeValue interface { type BodyRangeValue interface {

View file

@ -22,7 +22,7 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
) )
type BodyRange struct { type BodyRange struct {

View file

@ -24,7 +24,7 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"maunium.net/go/mautrix/event" "maunium.net/go/mautrix/event"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
) )
func (mc *MessageConverter) convertURLPreviewsToBeeper(ctx context.Context, preview []*signalpb.Preview, attMap AttachmentMap) []*event.BeeperLinkPreview { func (mc *MessageConverter) convertURLPreviewsToBeeper(ctx context.Context, preview []*signalpb.Preview, attMap AttachmentMap) []*event.BeeperLinkPreview {

View file

@ -40,7 +40,7 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"go.mau.fi/mautrix-signal/pkg/libsignalgo" "go.mau.fi/mautrix-signal/pkg/libsignalgo"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
"go.mau.fi/mautrix-signal/pkg/signalmeow/types" "go.mau.fi/mautrix-signal/pkg/signalmeow/types"
"go.mau.fi/mautrix-signal/pkg/signalmeow/web" "go.mau.fi/mautrix-signal/pkg/signalmeow/web"
) )

View file

@ -30,7 +30,7 @@ import (
"go.mau.fi/mautrix-signal/pkg/libsignalgo" "go.mau.fi/mautrix-signal/pkg/libsignalgo"
"go.mau.fi/mautrix-signal/pkg/signalmeow/events" "go.mau.fi/mautrix-signal/pkg/signalmeow/events"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
"go.mau.fi/mautrix-signal/pkg/signalmeow/store" "go.mau.fi/mautrix-signal/pkg/signalmeow/store"
"go.mau.fi/mautrix-signal/pkg/signalmeow/types" "go.mau.fi/mautrix-signal/pkg/signalmeow/types"
"go.mau.fi/mautrix-signal/pkg/signalmeow/web" "go.mau.fi/mautrix-signal/pkg/signalmeow/web"
@ -55,7 +55,6 @@ type Client struct {
AuthedWS *web.SignalWebsocket AuthedWS *web.SignalWebsocket
UnauthedWS *web.SignalWebsocket UnauthedWS *web.SignalWebsocket
GRPC *web.GRPCClient
lastConnectionStatus SignalConnectionStatus lastConnectionStatus SignalConnectionStatus
loopCancel context.CancelFunc loopCancel context.CancelFunc

View file

@ -31,7 +31,7 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
"go.mau.fi/mautrix-signal/pkg/signalmeow/types" "go.mau.fi/mautrix-signal/pkg/signalmeow/types"
) )

View file

@ -29,7 +29,7 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"go.mau.fi/mautrix-signal/pkg/libsignalgo" "go.mau.fi/mautrix-signal/pkg/libsignalgo"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
) )
func hmacSHA256(key, input []byte) []byte { func hmacSHA256(key, input []byte) []byte {

View file

@ -20,7 +20,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"go.mau.fi/mautrix-signal/pkg/libsignalgo" "go.mau.fi/mautrix-signal/pkg/libsignalgo"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
"go.mau.fi/mautrix-signal/pkg/signalmeow/types" "go.mau.fi/mautrix-signal/pkg/signalmeow/types"
) )

View file

@ -37,7 +37,7 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"go.mau.fi/mautrix-signal/pkg/libsignalgo" "go.mau.fi/mautrix-signal/pkg/libsignalgo"
"go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf/signalpb" signalpb "go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf"
"go.mau.fi/mautrix-signal/pkg/signalmeow/types" "go.mau.fi/mautrix-signal/pkg/signalmeow/types"
"go.mau.fi/mautrix-signal/pkg/signalmeow/web" "go.mau.fi/mautrix-signal/pkg/signalmeow/web"
) )

View file

@ -1,13 +1,11 @@
//*
// Copyright (C) 2014-2016 Open Whisper Systems
//
// Licensed according to the LICENSE file in this repository.
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.36.11
// protoc v7.34.1 // protoc v7.34.1
// source: signalpb/DeviceName.proto // source: DeviceName.proto
// Copyright 2018 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
package signalpb package signalpb
@ -37,7 +35,7 @@ type DeviceName struct {
func (x *DeviceName) Reset() { func (x *DeviceName) Reset() {
*x = DeviceName{} *x = DeviceName{}
mi := &file_signalpb_DeviceName_proto_msgTypes[0] mi := &file_DeviceName_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -49,7 +47,7 @@ func (x *DeviceName) String() string {
func (*DeviceName) ProtoMessage() {} func (*DeviceName) ProtoMessage() {}
func (x *DeviceName) ProtoReflect() protoreflect.Message { func (x *DeviceName) ProtoReflect() protoreflect.Message {
mi := &file_signalpb_DeviceName_proto_msgTypes[0] mi := &file_DeviceName_proto_msgTypes[0]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -62,7 +60,7 @@ func (x *DeviceName) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeviceName.ProtoReflect.Descriptor instead. // Deprecated: Use DeviceName.ProtoReflect.Descriptor instead.
func (*DeviceName) Descriptor() ([]byte, []int) { func (*DeviceName) Descriptor() ([]byte, []int) {
return file_signalpb_DeviceName_proto_rawDescGZIP(), []int{0} return file_DeviceName_proto_rawDescGZIP(), []int{0}
} }
func (x *DeviceName) GetEphemeralPublic() []byte { func (x *DeviceName) GetEphemeralPublic() []byte {
@ -86,37 +84,36 @@ func (x *DeviceName) GetCiphertext() []byte {
return nil return nil
} }
var File_signalpb_DeviceName_proto protoreflect.FileDescriptor var File_DeviceName_proto protoreflect.FileDescriptor
const file_signalpb_DeviceName_proto_rawDesc = "" + const file_DeviceName_proto_rawDesc = "" +
"\n" + "\n" +
"\x19signalpb/DeviceName.proto\x12\rsignalservice\"x\n" + "\x10DeviceName.proto\x12\rsignalservice\"x\n" +
"\n" + "\n" +
"DeviceName\x12(\n" + "DeviceName\x12(\n" +
"\x0fephemeralPublic\x18\x01 \x01(\fR\x0fephemeralPublic\x12 \n" + "\x0fephemeralPublic\x18\x01 \x01(\fR\x0fephemeralPublic\x12 \n" +
"\vsyntheticIv\x18\x02 \x01(\fR\vsyntheticIv\x12\x1e\n" + "\vsyntheticIv\x18\x02 \x01(\fR\vsyntheticIv\x12\x1e\n" +
"\n" + "\n" +
"ciphertext\x18\x03 \x01(\fR\n" + "ciphertext\x18\x03 \x01(\fR\n" +
"ciphertextB\x1d\n" + "ciphertext"
"\x1borg.signal.core.util.crypto"
var ( var (
file_signalpb_DeviceName_proto_rawDescOnce sync.Once file_DeviceName_proto_rawDescOnce sync.Once
file_signalpb_DeviceName_proto_rawDescData []byte file_DeviceName_proto_rawDescData []byte
) )
func file_signalpb_DeviceName_proto_rawDescGZIP() []byte { func file_DeviceName_proto_rawDescGZIP() []byte {
file_signalpb_DeviceName_proto_rawDescOnce.Do(func() { file_DeviceName_proto_rawDescOnce.Do(func() {
file_signalpb_DeviceName_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_signalpb_DeviceName_proto_rawDesc), len(file_signalpb_DeviceName_proto_rawDesc))) file_DeviceName_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_DeviceName_proto_rawDesc), len(file_DeviceName_proto_rawDesc)))
}) })
return file_signalpb_DeviceName_proto_rawDescData return file_DeviceName_proto_rawDescData
} }
var file_signalpb_DeviceName_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_DeviceName_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_signalpb_DeviceName_proto_goTypes = []any{ var file_DeviceName_proto_goTypes = []any{
(*DeviceName)(nil), // 0: signalservice.DeviceName (*DeviceName)(nil), // 0: signalservice.DeviceName
} }
var file_signalpb_DeviceName_proto_depIdxs = []int32{ var file_DeviceName_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension type_name
@ -124,26 +121,26 @@ var file_signalpb_DeviceName_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for field type_name 0, // [0:0] is the sub-list for field type_name
} }
func init() { file_signalpb_DeviceName_proto_init() } func init() { file_DeviceName_proto_init() }
func file_signalpb_DeviceName_proto_init() { func file_DeviceName_proto_init() {
if File_signalpb_DeviceName_proto != nil { if File_DeviceName_proto != nil {
return return
} }
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_signalpb_DeviceName_proto_rawDesc), len(file_signalpb_DeviceName_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_DeviceName_proto_rawDesc), len(file_DeviceName_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 1, NumMessages: 1,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },
GoTypes: file_signalpb_DeviceName_proto_goTypes, GoTypes: file_DeviceName_proto_goTypes,
DependencyIndexes: file_signalpb_DeviceName_proto_depIdxs, DependencyIndexes: file_DeviceName_proto_depIdxs,
MessageInfos: file_signalpb_DeviceName_proto_msgTypes, MessageInfos: file_DeviceName_proto_msgTypes,
}.Build() }.Build()
File_signalpb_DeviceName_proto = out.File File_DeviceName_proto = out.File
file_signalpb_DeviceName_proto_goTypes = nil file_DeviceName_proto_goTypes = nil
file_signalpb_DeviceName_proto_depIdxs = nil file_DeviceName_proto_depIdxs = nil
} }

View file

@ -0,0 +1,10 @@
// Copyright 2018 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
package signalservice;
message DeviceName {
optional bytes ephemeralPublic = 1;
optional bytes syntheticIv = 2;
optional bytes ciphertext = 3;
}

View file

@ -6,7 +6,7 @@
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.36.11
// protoc v7.34.1 // protoc v7.34.1
// source: signalpb/Provisioning.proto // source: Provisioning.proto
package signalpb package signalpb
@ -58,11 +58,11 @@ func (x ProvisioningVersion) String() string {
} }
func (ProvisioningVersion) Descriptor() protoreflect.EnumDescriptor { func (ProvisioningVersion) Descriptor() protoreflect.EnumDescriptor {
return file_signalpb_Provisioning_proto_enumTypes[0].Descriptor() return file_Provisioning_proto_enumTypes[0].Descriptor()
} }
func (ProvisioningVersion) Type() protoreflect.EnumType { func (ProvisioningVersion) Type() protoreflect.EnumType {
return &file_signalpb_Provisioning_proto_enumTypes[0] return &file_Provisioning_proto_enumTypes[0]
} }
func (x ProvisioningVersion) Number() protoreflect.EnumNumber { func (x ProvisioningVersion) Number() protoreflect.EnumNumber {
@ -81,7 +81,7 @@ func (x *ProvisioningVersion) UnmarshalJSON(b []byte) error {
// Deprecated: Use ProvisioningVersion.Descriptor instead. // Deprecated: Use ProvisioningVersion.Descriptor instead.
func (ProvisioningVersion) EnumDescriptor() ([]byte, []int) { func (ProvisioningVersion) EnumDescriptor() ([]byte, []int) {
return file_signalpb_Provisioning_proto_rawDescGZIP(), []int{0} return file_Provisioning_proto_rawDescGZIP(), []int{0}
} }
// An opaque address sent by the server when clients first open a provisioning // An opaque address sent by the server when clients first open a provisioning
@ -98,7 +98,7 @@ type ProvisioningAddress struct {
func (x *ProvisioningAddress) Reset() { func (x *ProvisioningAddress) Reset() {
*x = ProvisioningAddress{} *x = ProvisioningAddress{}
mi := &file_signalpb_Provisioning_proto_msgTypes[0] mi := &file_Provisioning_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -110,7 +110,7 @@ func (x *ProvisioningAddress) String() string {
func (*ProvisioningAddress) ProtoMessage() {} func (*ProvisioningAddress) ProtoMessage() {}
func (x *ProvisioningAddress) ProtoReflect() protoreflect.Message { func (x *ProvisioningAddress) ProtoReflect() protoreflect.Message {
mi := &file_signalpb_Provisioning_proto_msgTypes[0] mi := &file_Provisioning_proto_msgTypes[0]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -123,7 +123,7 @@ func (x *ProvisioningAddress) ProtoReflect() protoreflect.Message {
// Deprecated: Use ProvisioningAddress.ProtoReflect.Descriptor instead. // Deprecated: Use ProvisioningAddress.ProtoReflect.Descriptor instead.
func (*ProvisioningAddress) Descriptor() ([]byte, []int) { func (*ProvisioningAddress) Descriptor() ([]byte, []int) {
return file_signalpb_Provisioning_proto_rawDescGZIP(), []int{0} return file_Provisioning_proto_rawDescGZIP(), []int{0}
} }
func (x *ProvisioningAddress) GetAddress() string { func (x *ProvisioningAddress) GetAddress() string {
@ -143,7 +143,7 @@ type ProvisionEnvelope struct {
func (x *ProvisionEnvelope) Reset() { func (x *ProvisionEnvelope) Reset() {
*x = ProvisionEnvelope{} *x = ProvisionEnvelope{}
mi := &file_signalpb_Provisioning_proto_msgTypes[1] mi := &file_Provisioning_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -155,7 +155,7 @@ func (x *ProvisionEnvelope) String() string {
func (*ProvisionEnvelope) ProtoMessage() {} func (*ProvisionEnvelope) ProtoMessage() {}
func (x *ProvisionEnvelope) ProtoReflect() protoreflect.Message { func (x *ProvisionEnvelope) ProtoReflect() protoreflect.Message {
mi := &file_signalpb_Provisioning_proto_msgTypes[1] mi := &file_Provisioning_proto_msgTypes[1]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -168,7 +168,7 @@ func (x *ProvisionEnvelope) ProtoReflect() protoreflect.Message {
// Deprecated: Use ProvisionEnvelope.ProtoReflect.Descriptor instead. // Deprecated: Use ProvisionEnvelope.ProtoReflect.Descriptor instead.
func (*ProvisionEnvelope) Descriptor() ([]byte, []int) { func (*ProvisionEnvelope) Descriptor() ([]byte, []int) {
return file_signalpb_Provisioning_proto_rawDescGZIP(), []int{1} return file_Provisioning_proto_rawDescGZIP(), []int{1}
} }
func (x *ProvisionEnvelope) GetPublicKey() []byte { func (x *ProvisionEnvelope) GetPublicKey() []byte {
@ -204,14 +204,13 @@ type ProvisionMessage struct {
MediaRootBackupKey []byte `protobuf:"bytes,16,opt,name=mediaRootBackupKey" json:"mediaRootBackupKey,omitempty"` // 32-bytes MediaRootBackupKey []byte `protobuf:"bytes,16,opt,name=mediaRootBackupKey" json:"mediaRootBackupKey,omitempty"` // 32-bytes
AciBinary []byte `protobuf:"bytes,17,opt,name=aciBinary" json:"aciBinary,omitempty"` // 16-byte UUID AciBinary []byte `protobuf:"bytes,17,opt,name=aciBinary" json:"aciBinary,omitempty"` // 16-byte UUID
PniBinary []byte `protobuf:"bytes,18,opt,name=pniBinary" json:"pniBinary,omitempty"` // 16-byte UUID PniBinary []byte `protobuf:"bytes,18,opt,name=pniBinary" json:"pniBinary,omitempty"` // 16-byte UUID
AuthCredentialSalt []byte `protobuf:"bytes,19,opt,name=authCredentialSalt" json:"authCredentialSalt,omitempty"` // 16-bytes
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
func (x *ProvisionMessage) Reset() { func (x *ProvisionMessage) Reset() {
*x = ProvisionMessage{} *x = ProvisionMessage{}
mi := &file_signalpb_Provisioning_proto_msgTypes[2] mi := &file_Provisioning_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -223,7 +222,7 @@ func (x *ProvisionMessage) String() string {
func (*ProvisionMessage) ProtoMessage() {} func (*ProvisionMessage) ProtoMessage() {}
func (x *ProvisionMessage) ProtoReflect() protoreflect.Message { func (x *ProvisionMessage) ProtoReflect() protoreflect.Message {
mi := &file_signalpb_Provisioning_proto_msgTypes[2] mi := &file_Provisioning_proto_msgTypes[2]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -236,7 +235,7 @@ func (x *ProvisionMessage) ProtoReflect() protoreflect.Message {
// Deprecated: Use ProvisionMessage.ProtoReflect.Descriptor instead. // Deprecated: Use ProvisionMessage.ProtoReflect.Descriptor instead.
func (*ProvisionMessage) Descriptor() ([]byte, []int) { func (*ProvisionMessage) Descriptor() ([]byte, []int) {
return file_signalpb_Provisioning_proto_rawDescGZIP(), []int{2} return file_Provisioning_proto_rawDescGZIP(), []int{2}
} }
func (x *ProvisionMessage) GetAciIdentityKeyPublic() []byte { func (x *ProvisionMessage) GetAciIdentityKeyPublic() []byte {
@ -358,23 +357,16 @@ func (x *ProvisionMessage) GetPniBinary() []byte {
return nil return nil
} }
func (x *ProvisionMessage) GetAuthCredentialSalt() []byte { var File_Provisioning_proto protoreflect.FileDescriptor
if x != nil {
return x.AuthCredentialSalt
}
return nil
}
var File_signalpb_Provisioning_proto protoreflect.FileDescriptor const file_Provisioning_proto_rawDesc = "" +
const file_signalpb_Provisioning_proto_rawDesc = "" +
"\n" + "\n" +
"\x1bsignalpb/Provisioning.proto\x12\rsignalservice\"/\n" + "\x12Provisioning.proto\x12\rsignalservice\"/\n" +
"\x13ProvisioningAddress\x12\x18\n" + "\x13ProvisioningAddress\x12\x18\n" +
"\aaddress\x18\x01 \x01(\tR\aaddress\"E\n" + "\aaddress\x18\x01 \x01(\tR\aaddress\"E\n" +
"\x11ProvisionEnvelope\x12\x1c\n" + "\x11ProvisionEnvelope\x12\x1c\n" +
"\tpublicKey\x18\x01 \x01(\fR\tpublicKey\x12\x12\n" + "\tpublicKey\x18\x01 \x01(\fR\tpublicKey\x12\x12\n" +
"\x04body\x18\x02 \x01(\fR\x04body\"\xe4\x05\n" + "\x04body\x18\x02 \x01(\fR\x04body\"\xb4\x05\n" +
"\x10ProvisionMessage\x122\n" + "\x10ProvisionMessage\x122\n" +
"\x14aciIdentityKeyPublic\x18\x01 \x01(\fR\x14aciIdentityKeyPublic\x124\n" + "\x14aciIdentityKeyPublic\x18\x01 \x01(\fR\x14aciIdentityKeyPublic\x124\n" +
"\x15aciIdentityKeyPrivate\x18\x02 \x01(\fR\x15aciIdentityKeyPrivate\x122\n" + "\x15aciIdentityKeyPrivate\x18\x02 \x01(\fR\x15aciIdentityKeyPrivate\x122\n" +
@ -395,8 +387,7 @@ const file_signalpb_Provisioning_proto_rawDesc = "" +
"\x12accountEntropyPool\x18\x0f \x01(\tR\x12accountEntropyPool\x12.\n" + "\x12accountEntropyPool\x18\x0f \x01(\tR\x12accountEntropyPool\x12.\n" +
"\x12mediaRootBackupKey\x18\x10 \x01(\fR\x12mediaRootBackupKey\x12\x1c\n" + "\x12mediaRootBackupKey\x18\x10 \x01(\fR\x12mediaRootBackupKey\x12\x1c\n" +
"\taciBinary\x18\x11 \x01(\fR\taciBinary\x12\x1c\n" + "\taciBinary\x18\x11 \x01(\fR\taciBinary\x12\x1c\n" +
"\tpniBinary\x18\x12 \x01(\fR\tpniBinary\x12.\n" + "\tpniBinary\x18\x12 \x01(\fR\tpniBinaryJ\x04\b\r\x10\x0e*G\n" +
"\x12authCredentialSalt\x18\x13 \x01(\fR\x12authCredentialSaltJ\x04\b\r\x10\x0e*G\n" +
"\x13ProvisioningVersion\x12\v\n" + "\x13ProvisioningVersion\x12\v\n" +
"\aINITIAL\x10\x00\x12\x12\n" + "\aINITIAL\x10\x00\x12\x12\n" +
"\x0eTABLET_SUPPORT\x10\x01\x12\v\n" + "\x0eTABLET_SUPPORT\x10\x01\x12\v\n" +
@ -404,26 +395,26 @@ const file_signalpb_Provisioning_proto_rawDesc = "" +
".org.whispersystems.signalservice.internal.pushB\x12ProvisioningProtos" ".org.whispersystems.signalservice.internal.pushB\x12ProvisioningProtos"
var ( var (
file_signalpb_Provisioning_proto_rawDescOnce sync.Once file_Provisioning_proto_rawDescOnce sync.Once
file_signalpb_Provisioning_proto_rawDescData []byte file_Provisioning_proto_rawDescData []byte
) )
func file_signalpb_Provisioning_proto_rawDescGZIP() []byte { func file_Provisioning_proto_rawDescGZIP() []byte {
file_signalpb_Provisioning_proto_rawDescOnce.Do(func() { file_Provisioning_proto_rawDescOnce.Do(func() {
file_signalpb_Provisioning_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_signalpb_Provisioning_proto_rawDesc), len(file_signalpb_Provisioning_proto_rawDesc))) file_Provisioning_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_Provisioning_proto_rawDesc), len(file_Provisioning_proto_rawDesc)))
}) })
return file_signalpb_Provisioning_proto_rawDescData return file_Provisioning_proto_rawDescData
} }
var file_signalpb_Provisioning_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_Provisioning_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_signalpb_Provisioning_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_Provisioning_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_signalpb_Provisioning_proto_goTypes = []any{ var file_Provisioning_proto_goTypes = []any{
(ProvisioningVersion)(0), // 0: signalservice.ProvisioningVersion (ProvisioningVersion)(0), // 0: signalservice.ProvisioningVersion
(*ProvisioningAddress)(nil), // 1: signalservice.ProvisioningAddress (*ProvisioningAddress)(nil), // 1: signalservice.ProvisioningAddress
(*ProvisionEnvelope)(nil), // 2: signalservice.ProvisionEnvelope (*ProvisionEnvelope)(nil), // 2: signalservice.ProvisionEnvelope
(*ProvisionMessage)(nil), // 3: signalservice.ProvisionMessage (*ProvisionMessage)(nil), // 3: signalservice.ProvisionMessage
} }
var file_signalpb_Provisioning_proto_depIdxs = []int32{ var file_Provisioning_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension type_name
@ -431,27 +422,27 @@ var file_signalpb_Provisioning_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for field type_name 0, // [0:0] is the sub-list for field type_name
} }
func init() { file_signalpb_Provisioning_proto_init() } func init() { file_Provisioning_proto_init() }
func file_signalpb_Provisioning_proto_init() { func file_Provisioning_proto_init() {
if File_signalpb_Provisioning_proto != nil { if File_Provisioning_proto != nil {
return return
} }
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_signalpb_Provisioning_proto_rawDesc), len(file_signalpb_Provisioning_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_Provisioning_proto_rawDesc), len(file_Provisioning_proto_rawDesc)),
NumEnums: 1, NumEnums: 1,
NumMessages: 3, NumMessages: 3,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },
GoTypes: file_signalpb_Provisioning_proto_goTypes, GoTypes: file_Provisioning_proto_goTypes,
DependencyIndexes: file_signalpb_Provisioning_proto_depIdxs, DependencyIndexes: file_Provisioning_proto_depIdxs,
EnumInfos: file_signalpb_Provisioning_proto_enumTypes, EnumInfos: file_Provisioning_proto_enumTypes,
MessageInfos: file_signalpb_Provisioning_proto_msgTypes, MessageInfos: file_Provisioning_proto_msgTypes,
}.Build() }.Build()
File_signalpb_Provisioning_proto = out.File File_Provisioning_proto = out.File
file_signalpb_Provisioning_proto_goTypes = nil file_Provisioning_proto_goTypes = nil
file_signalpb_Provisioning_proto_depIdxs = nil file_Provisioning_proto_depIdxs = nil
} }

View file

@ -44,8 +44,7 @@ message ProvisionMessage {
optional bytes mediaRootBackupKey = 16; // 32-bytes optional bytes mediaRootBackupKey = 16; // 32-bytes
optional bytes aciBinary = 17; // 16-byte UUID optional bytes aciBinary = 17; // 16-byte UUID
optional bytes pniBinary = 18; // 16-byte UUID optional bytes pniBinary = 18; // 16-byte UUID
optional bytes authCredentialSalt = 19; // 16-bytes // NEXT ID: 19
// NEXT ID: 20
} }
enum ProvisioningVersion { enum ProvisioningVersion {

View file

@ -579,29 +579,10 @@ message SyncMessage {
} }
message Blocked { message Blocked {
repeated string numbers = 1; // deprecated: this field will be removed in a future release. repeated string numbers = 1;
repeated string acis = 3; repeated string acis = 3;
repeated bytes groupIds = 2; // deprecated: this field will be removed in a future release. repeated bytes groupIds = 2;
repeated bytes acisBinary = 4; // deprecated: this field will be removed in a future release. repeated bytes acisBinary = 4; // 16-byte UUID
message BlockedE164 {
optional string e164 = 1;
optional uint64 timestamp = 2;
}
message BlockedAci {
optional bytes aciBinary = 1; // 16-byte UUID
optional uint64 timestamp = 2;
}
message BlockedGroup {
optional bytes groupId = 1;
optional uint64 timestamp = 2;
}
repeated BlockedE164 blockedE164s = 5;
repeated BlockedAci blockedAcis = 6;
repeated BlockedGroup blockedGroups = 7;
} }
message Request { message Request {

View file

@ -7,7 +7,7 @@
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.36.11
// protoc v7.34.1 // protoc v7.34.1
// source: signalpb/StickerResources.proto // source: StickerResources.proto
package signalpb package signalpb
@ -38,7 +38,7 @@ type Pack struct {
func (x *Pack) Reset() { func (x *Pack) Reset() {
*x = Pack{} *x = Pack{}
mi := &file_signalpb_StickerResources_proto_msgTypes[0] mi := &file_StickerResources_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -50,7 +50,7 @@ func (x *Pack) String() string {
func (*Pack) ProtoMessage() {} func (*Pack) ProtoMessage() {}
func (x *Pack) ProtoReflect() protoreflect.Message { func (x *Pack) ProtoReflect() protoreflect.Message {
mi := &file_signalpb_StickerResources_proto_msgTypes[0] mi := &file_StickerResources_proto_msgTypes[0]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -63,7 +63,7 @@ func (x *Pack) ProtoReflect() protoreflect.Message {
// Deprecated: Use Pack.ProtoReflect.Descriptor instead. // Deprecated: Use Pack.ProtoReflect.Descriptor instead.
func (*Pack) Descriptor() ([]byte, []int) { func (*Pack) Descriptor() ([]byte, []int) {
return file_signalpb_StickerResources_proto_rawDescGZIP(), []int{0} return file_StickerResources_proto_rawDescGZIP(), []int{0}
} }
func (x *Pack) GetTitle() string { func (x *Pack) GetTitle() string {
@ -105,7 +105,7 @@ type Pack_Sticker struct {
func (x *Pack_Sticker) Reset() { func (x *Pack_Sticker) Reset() {
*x = Pack_Sticker{} *x = Pack_Sticker{}
mi := &file_signalpb_StickerResources_proto_msgTypes[1] mi := &file_StickerResources_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -117,7 +117,7 @@ func (x *Pack_Sticker) String() string {
func (*Pack_Sticker) ProtoMessage() {} func (*Pack_Sticker) ProtoMessage() {}
func (x *Pack_Sticker) ProtoReflect() protoreflect.Message { func (x *Pack_Sticker) ProtoReflect() protoreflect.Message {
mi := &file_signalpb_StickerResources_proto_msgTypes[1] mi := &file_StickerResources_proto_msgTypes[1]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -130,7 +130,7 @@ func (x *Pack_Sticker) ProtoReflect() protoreflect.Message {
// Deprecated: Use Pack_Sticker.ProtoReflect.Descriptor instead. // Deprecated: Use Pack_Sticker.ProtoReflect.Descriptor instead.
func (*Pack_Sticker) Descriptor() ([]byte, []int) { func (*Pack_Sticker) Descriptor() ([]byte, []int) {
return file_signalpb_StickerResources_proto_rawDescGZIP(), []int{0, 0} return file_StickerResources_proto_rawDescGZIP(), []int{0, 0}
} }
func (x *Pack_Sticker) GetId() uint32 { func (x *Pack_Sticker) GetId() uint32 {
@ -154,11 +154,11 @@ func (x *Pack_Sticker) GetContentType() string {
return "" return ""
} }
var File_signalpb_StickerResources_proto protoreflect.FileDescriptor var File_StickerResources_proto protoreflect.FileDescriptor
const file_signalpb_StickerResources_proto_rawDesc = "" + const file_StickerResources_proto_rawDesc = "" +
"\n" + "\n" +
"\x1fsignalpb/StickerResources.proto\x12\rsignalservice\"\xf3\x01\n" + "\x16StickerResources.proto\x12\rsignalservice\"\xf3\x01\n" +
"\x04Pack\x12\x14\n" + "\x04Pack\x12\x14\n" +
"\x05title\x18\x01 \x01(\tR\x05title\x12\x16\n" + "\x05title\x18\x01 \x01(\tR\x05title\x12\x16\n" +
"\x06author\x18\x02 \x01(\tR\x06author\x121\n" + "\x06author\x18\x02 \x01(\tR\x06author\x121\n" +
@ -171,23 +171,23 @@ const file_signalpb_StickerResources_proto_rawDesc = "" +
"1org.whispersystems.signalservice.internal.stickerB\rStickerProtos" "1org.whispersystems.signalservice.internal.stickerB\rStickerProtos"
var ( var (
file_signalpb_StickerResources_proto_rawDescOnce sync.Once file_StickerResources_proto_rawDescOnce sync.Once
file_signalpb_StickerResources_proto_rawDescData []byte file_StickerResources_proto_rawDescData []byte
) )
func file_signalpb_StickerResources_proto_rawDescGZIP() []byte { func file_StickerResources_proto_rawDescGZIP() []byte {
file_signalpb_StickerResources_proto_rawDescOnce.Do(func() { file_StickerResources_proto_rawDescOnce.Do(func() {
file_signalpb_StickerResources_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_signalpb_StickerResources_proto_rawDesc), len(file_signalpb_StickerResources_proto_rawDesc))) file_StickerResources_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_StickerResources_proto_rawDesc), len(file_StickerResources_proto_rawDesc)))
}) })
return file_signalpb_StickerResources_proto_rawDescData return file_StickerResources_proto_rawDescData
} }
var file_signalpb_StickerResources_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_StickerResources_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_signalpb_StickerResources_proto_goTypes = []any{ var file_StickerResources_proto_goTypes = []any{
(*Pack)(nil), // 0: signalservice.Pack (*Pack)(nil), // 0: signalservice.Pack
(*Pack_Sticker)(nil), // 1: signalservice.Pack.Sticker (*Pack_Sticker)(nil), // 1: signalservice.Pack.Sticker
} }
var file_signalpb_StickerResources_proto_depIdxs = []int32{ var file_StickerResources_proto_depIdxs = []int32{
1, // 0: signalservice.Pack.cover:type_name -> signalservice.Pack.Sticker 1, // 0: signalservice.Pack.cover:type_name -> signalservice.Pack.Sticker
1, // 1: signalservice.Pack.stickers:type_name -> signalservice.Pack.Sticker 1, // 1: signalservice.Pack.stickers:type_name -> signalservice.Pack.Sticker
2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method output_type
@ -197,26 +197,26 @@ var file_signalpb_StickerResources_proto_depIdxs = []int32{
0, // [0:2] is the sub-list for field type_name 0, // [0:2] is the sub-list for field type_name
} }
func init() { file_signalpb_StickerResources_proto_init() } func init() { file_StickerResources_proto_init() }
func file_signalpb_StickerResources_proto_init() { func file_StickerResources_proto_init() {
if File_signalpb_StickerResources_proto != nil { if File_StickerResources_proto != nil {
return return
} }
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_signalpb_StickerResources_proto_rawDesc), len(file_signalpb_StickerResources_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_StickerResources_proto_rawDesc), len(file_StickerResources_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 2, NumMessages: 2,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },
GoTypes: file_signalpb_StickerResources_proto_goTypes, GoTypes: file_StickerResources_proto_goTypes,
DependencyIndexes: file_signalpb_StickerResources_proto_depIdxs, DependencyIndexes: file_StickerResources_proto_depIdxs,
MessageInfos: file_signalpb_StickerResources_proto_msgTypes, MessageInfos: file_StickerResources_proto_msgTypes,
}.Build() }.Build()
File_signalpb_StickerResources_proto = out.File File_StickerResources_proto = out.File
file_signalpb_StickerResources_proto_goTypes = nil file_StickerResources_proto_goTypes = nil
file_signalpb_StickerResources_proto_depIdxs = nil file_StickerResources_proto_depIdxs = nil
} }

View file

@ -1,6 +1,7 @@
/* /**
* Copyright 2020-2021 Signal Messenger, LLC * Copyright (C) 2019 Open Whisper Systems
* SPDX-License-Identifier: AGPL-3.0-only *
* Licensed according to the LICENSE file in this repository.
*/ */
syntax = "proto3"; syntax = "proto3";
@ -49,7 +50,6 @@ message ManifestRecord {
GROUPV2 = 3; GROUPV2 = 3;
ACCOUNT = 4; ACCOUNT = 4;
STORY_DISTRIBUTION_LIST = 5; STORY_DISTRIBUTION_LIST = 5;
STICKER_PACK = 6;
CALL_LINK = 7; CALL_LINK = 7;
CHAT_FOLDER = 8; CHAT_FOLDER = 8;
NOTIFICATION_PROFILE = 9; NOTIFICATION_PROFILE = 9;
@ -73,7 +73,6 @@ message StorageRecord {
GroupV2Record groupV2 = 3; GroupV2Record groupV2 = 3;
AccountRecord account = 4; AccountRecord account = 4;
StoryDistributionListRecord storyDistributionList = 5; StoryDistributionListRecord storyDistributionList = 5;
StickerPackRecord stickerPack = 6;
CallLinkRecord callLink = 7; CallLinkRecord callLink = 7;
ChatFolderRecord chatFolder = 8; ChatFolderRecord chatFolder = 8;
NotificationProfile notificationProfile = 9; NotificationProfile notificationProfile = 9;
@ -143,10 +142,7 @@ message ContactRecord {
optional AvatarColor avatarColor = 24; optional AvatarColor avatarColor = 24;
bytes aciBinary = 25; // 16-byte UUID bytes aciBinary = 25; // 16-byte UUID
bytes pniBinary = 26; // 16-byte UUID bytes pniBinary = 26; // 16-byte UUID
uint64 blockedAtTimestamp = 27; // 0 means the blocked time is unknown // Next ID: 27
OptionalBool notifyForCallsIfMuted = 28; // If unset, use the default settings
OptionalBool showUnreadReminders = 29; // If unset, use the default settings
// Next ID: 30
} }
message GroupV1Record { message GroupV1Record {
@ -171,17 +167,12 @@ message GroupV2Record {
bool archived = 4; bool archived = 4;
bool markedUnread = 5; bool markedUnread = 5;
uint64 mutedUntilTimestamp = 6; uint64 mutedUntilTimestamp = 6;
bool dontNotifyForMentionsIfMuted = 7; // will be deprecated in favor of [notifyForMentionsIfMuted] bool dontNotifyForMentionsIfMuted = 7;
bool hideStory = 8; bool hideStory = 8;
reserved /* storySendEnabled */ 9; reserved /* storySendEnabled */ 9;
StorySendMode storySendMode = 10; StorySendMode storySendMode = 10;
optional AvatarColor avatarColor = 11; optional AvatarColor avatarColor = 11;
bytes verifiedNameHash = 12; // SHA-256 of UTF-8 encoded decrypted group title that was last verified bytes verifiedNameHash = 12; // SHA-256 of UTF-8 encoded decrypted group title that was last verified
uint64 blockedAtTimestamp = 13; // 0 means the blocked time is unknown
OptionalBool notifyForCallsIfMuted = 14; // If unset, use the default settings
OptionalBool notifyForMentionsIfMuted = 15; // If unset, use the default settings. If [dontNotifyForMentionsIfMuted] is true, this should be initialized to false.
OptionalBool notifyForRepliesIfMuted = 16; // If unset, use the default settings
OptionalBool showUnreadReminders = 17; // If unset, use the default settings
} }
message Payments { message Payments {
@ -191,12 +182,6 @@ message Payments {
message AccountRecord { message AccountRecord {
enum UnreadBadgeType {
UNKNOWN_BADGE_TYPE = 0; // Interpret as "Unread messages"
UNREAD_MESSAGES = 1;
UNREAD_CHATS = 2;
}
enum PhoneNumberSharingMode { enum PhoneNumberSharingMode {
UNKNOWN = 0; UNKNOWN = 0;
EVERYBODY = 1; EVERYBODY = 1;
@ -249,6 +234,12 @@ message AccountRecord {
} }
} }
message BackupTierHistory {
// See zkgroup for integer particular values. Unset if backups are not enabled.
optional uint64 backupTier = 1;
optional uint64 endedAtTimestamp = 2;
}
message NotificationProfileManualOverride { message NotificationProfileManualOverride {
message ManuallyEnabled { message ManuallyEnabled {
bytes id = 1; bytes id = 1;
@ -305,7 +296,7 @@ message AccountRecord {
optional uint64 backupTier = 40; // See zkgroup for integer particular values. Unset if backups are not enabled. optional uint64 backupTier = 40; // See zkgroup for integer particular values. Unset if backups are not enabled.
IAPSubscriberData backupSubscriberData = 41; IAPSubscriberData backupSubscriberData = 41;
optional AvatarColor avatarColor = 42; optional AvatarColor avatarColor = 42;
reserved /* backupTierHistory */ 43; BackupTierHistory backupTierHistory = 43;
NotificationProfileManualOverride notificationProfileManualOverride = 44; NotificationProfileManualOverride notificationProfileManualOverride = 44;
bool notificationProfileSyncDisabled = 45; bool notificationProfileSyncDisabled = 45;
bool automaticKeyVerificationDisabled = 46; bool automaticKeyVerificationDisabled = 46;
@ -314,15 +305,6 @@ message AccountRecord {
optional uint64 releaseNotesChatMutedUntilTimestamp = 49; optional uint64 releaseNotesChatMutedUntilTimestamp = 49;
optional bool releaseNotesChatBlocked = 50; optional bool releaseNotesChatBlocked = 50;
optional bool releaseNotesChatMarkedUnread = 51; optional bool releaseNotesChatMarkedUnread = 51;
optional uint64 releaseNotesChatBlockedAt = 52; // only set if known (>0)
UnreadBadgeType unreadBadgeType = 53; // Only used in desktop/ios
OptionalBool includeMutedChatsInBadge = 54; // Only used in desktop/ios. If unset, consider this off. Only used in desktop/ios
OptionalBool reactionNotifications = 55; // If unset, consider this on
OptionalBool notifyForCallsIfMuted = 56; // If unset, consider this off
OptionalBool notifyForMentionsIfMuted = 57; // If unset, consider this on
OptionalBool notifyForRepliesIfMuted = 58; // If unset, consider this on
OptionalBool showUnreadReminders = 59; // If unset, consider this on
OptionalBool notifyWhenContactJoins = 60; // If unset, consider this off
} }
message StoryDistributionListRecord { message StoryDistributionListRecord {
@ -335,27 +317,6 @@ message StoryDistributionListRecord {
repeated bytes recipientServiceIdsBinary = 7; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI) repeated bytes recipientServiceIdsBinary = 7; // service ID binary (i.e. 16 byte UUID for ACI, 1 byte prefix + 16 byte UUID for PNI)
} }
message StickerPackRecord {
bytes packId = 1; // 16 bytes
bytes packKey = 2; // 32 bytes, used to derive the AES-256 key
// aesKey = HKDF(
// input = packKey,
// salt = 32 zero bytes,
// info = "Sticker Pack"
// )
uint32 position = 3; // When displayed sticker packs should be first sorted
// in ascending order by zero-based `position` and
// then by ascending `packId` (lexicographically,
// packId can be treated as a hex string).
// When installing a sticker pack the client should find
// the maximum `position` among currently known stickers
// and use `max_position + 1` as the value for the new
// `position`.
uint64 deletedAtTimestamp = 4; // Timestamp in milliseconds. When present and
// non-zero - `packKey` and `position` should
// be unset
}
message CallLinkRecord { message CallLinkRecord {
bytes rootKey = 1; bytes rootKey = 1;
bytes adminPasskey = 2; bytes adminPasskey = 2;

View file

@ -0,0 +1,635 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v7.34.1
// source: UnidentifiedDelivery.proto
// Copyright 2018 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
package signalpb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type UnidentifiedSenderMessage_Message_Type int32
const (
// Our parser does not handle reserved in enums: DESKTOP-1569
// reserved 1;
UnidentifiedSenderMessage_Message_MESSAGE UnidentifiedSenderMessage_Message_Type = 2
UnidentifiedSenderMessage_Message_PREKEY_MESSAGE UnidentifiedSenderMessage_Message_Type = 3 // Further cases should line up with Envelope.Type, even though old cases don't.
UnidentifiedSenderMessage_Message_SENDERKEY_MESSAGE UnidentifiedSenderMessage_Message_Type = 7
UnidentifiedSenderMessage_Message_PLAINTEXT_CONTENT UnidentifiedSenderMessage_Message_Type = 8
)
// Enum value maps for UnidentifiedSenderMessage_Message_Type.
var (
UnidentifiedSenderMessage_Message_Type_name = map[int32]string{
2: "MESSAGE",
3: "PREKEY_MESSAGE",
7: "SENDERKEY_MESSAGE",
8: "PLAINTEXT_CONTENT",
}
UnidentifiedSenderMessage_Message_Type_value = map[string]int32{
"MESSAGE": 2,
"PREKEY_MESSAGE": 3,
"SENDERKEY_MESSAGE": 7,
"PLAINTEXT_CONTENT": 8,
}
)
func (x UnidentifiedSenderMessage_Message_Type) Enum() *UnidentifiedSenderMessage_Message_Type {
p := new(UnidentifiedSenderMessage_Message_Type)
*p = x
return p
}
func (x UnidentifiedSenderMessage_Message_Type) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (UnidentifiedSenderMessage_Message_Type) Descriptor() protoreflect.EnumDescriptor {
return file_UnidentifiedDelivery_proto_enumTypes[0].Descriptor()
}
func (UnidentifiedSenderMessage_Message_Type) Type() protoreflect.EnumType {
return &file_UnidentifiedDelivery_proto_enumTypes[0]
}
func (x UnidentifiedSenderMessage_Message_Type) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Do not use.
func (x *UnidentifiedSenderMessage_Message_Type) UnmarshalJSON(b []byte) error {
num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
if err != nil {
return err
}
*x = UnidentifiedSenderMessage_Message_Type(num)
return nil
}
// Deprecated: Use UnidentifiedSenderMessage_Message_Type.Descriptor instead.
func (UnidentifiedSenderMessage_Message_Type) EnumDescriptor() ([]byte, []int) {
return file_UnidentifiedDelivery_proto_rawDescGZIP(), []int{2, 0, 0}
}
type UnidentifiedSenderMessage_Message_ContentHint int32
const (
// Show an error immediately; it was important but we can't retry.
UnidentifiedSenderMessage_Message_DEFAULT UnidentifiedSenderMessage_Message_ContentHint = 0
// Sender will try to resend; delay any error UI if possible
UnidentifiedSenderMessage_Message_RESENDABLE UnidentifiedSenderMessage_Message_ContentHint = 1
// Don't show any error UI at all; this is something sent implicitly like a typing message or a receipt
UnidentifiedSenderMessage_Message_IMPLICIT UnidentifiedSenderMessage_Message_ContentHint = 2
)
// Enum value maps for UnidentifiedSenderMessage_Message_ContentHint.
var (
UnidentifiedSenderMessage_Message_ContentHint_name = map[int32]string{
0: "DEFAULT",
1: "RESENDABLE",
2: "IMPLICIT",
}
UnidentifiedSenderMessage_Message_ContentHint_value = map[string]int32{
"DEFAULT": 0,
"RESENDABLE": 1,
"IMPLICIT": 2,
}
)
func (x UnidentifiedSenderMessage_Message_ContentHint) Enum() *UnidentifiedSenderMessage_Message_ContentHint {
p := new(UnidentifiedSenderMessage_Message_ContentHint)
*p = x
return p
}
func (x UnidentifiedSenderMessage_Message_ContentHint) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (UnidentifiedSenderMessage_Message_ContentHint) Descriptor() protoreflect.EnumDescriptor {
return file_UnidentifiedDelivery_proto_enumTypes[1].Descriptor()
}
func (UnidentifiedSenderMessage_Message_ContentHint) Type() protoreflect.EnumType {
return &file_UnidentifiedDelivery_proto_enumTypes[1]
}
func (x UnidentifiedSenderMessage_Message_ContentHint) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Do not use.
func (x *UnidentifiedSenderMessage_Message_ContentHint) UnmarshalJSON(b []byte) error {
num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
if err != nil {
return err
}
*x = UnidentifiedSenderMessage_Message_ContentHint(num)
return nil
}
// Deprecated: Use UnidentifiedSenderMessage_Message_ContentHint.Descriptor instead.
func (UnidentifiedSenderMessage_Message_ContentHint) EnumDescriptor() ([]byte, []int) {
return file_UnidentifiedDelivery_proto_rawDescGZIP(), []int{2, 0, 1}
}
type ServerCertificate struct {
state protoimpl.MessageState `protogen:"open.v1"`
Certificate []byte `protobuf:"bytes,1,opt,name=certificate" json:"certificate,omitempty"`
Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ServerCertificate) Reset() {
*x = ServerCertificate{}
mi := &file_UnidentifiedDelivery_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ServerCertificate) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ServerCertificate) ProtoMessage() {}
func (x *ServerCertificate) ProtoReflect() protoreflect.Message {
mi := &file_UnidentifiedDelivery_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ServerCertificate.ProtoReflect.Descriptor instead.
func (*ServerCertificate) Descriptor() ([]byte, []int) {
return file_UnidentifiedDelivery_proto_rawDescGZIP(), []int{0}
}
func (x *ServerCertificate) GetCertificate() []byte {
if x != nil {
return x.Certificate
}
return nil
}
func (x *ServerCertificate) GetSignature() []byte {
if x != nil {
return x.Signature
}
return nil
}
type SenderCertificate struct {
state protoimpl.MessageState `protogen:"open.v1"`
Certificate []byte `protobuf:"bytes,1,opt,name=certificate" json:"certificate,omitempty"`
Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SenderCertificate) Reset() {
*x = SenderCertificate{}
mi := &file_UnidentifiedDelivery_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SenderCertificate) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SenderCertificate) ProtoMessage() {}
func (x *SenderCertificate) ProtoReflect() protoreflect.Message {
mi := &file_UnidentifiedDelivery_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SenderCertificate.ProtoReflect.Descriptor instead.
func (*SenderCertificate) Descriptor() ([]byte, []int) {
return file_UnidentifiedDelivery_proto_rawDescGZIP(), []int{1}
}
func (x *SenderCertificate) GetCertificate() []byte {
if x != nil {
return x.Certificate
}
return nil
}
func (x *SenderCertificate) GetSignature() []byte {
if x != nil {
return x.Signature
}
return nil
}
type UnidentifiedSenderMessage struct {
state protoimpl.MessageState `protogen:"open.v1"`
EphemeralPublic []byte `protobuf:"bytes,1,opt,name=ephemeralPublic" json:"ephemeralPublic,omitempty"`
EncryptedStatic []byte `protobuf:"bytes,2,opt,name=encryptedStatic" json:"encryptedStatic,omitempty"`
EncryptedMessage []byte `protobuf:"bytes,3,opt,name=encryptedMessage" json:"encryptedMessage,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *UnidentifiedSenderMessage) Reset() {
*x = UnidentifiedSenderMessage{}
mi := &file_UnidentifiedDelivery_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *UnidentifiedSenderMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UnidentifiedSenderMessage) ProtoMessage() {}
func (x *UnidentifiedSenderMessage) ProtoReflect() protoreflect.Message {
mi := &file_UnidentifiedDelivery_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UnidentifiedSenderMessage.ProtoReflect.Descriptor instead.
func (*UnidentifiedSenderMessage) Descriptor() ([]byte, []int) {
return file_UnidentifiedDelivery_proto_rawDescGZIP(), []int{2}
}
func (x *UnidentifiedSenderMessage) GetEphemeralPublic() []byte {
if x != nil {
return x.EphemeralPublic
}
return nil
}
func (x *UnidentifiedSenderMessage) GetEncryptedStatic() []byte {
if x != nil {
return x.EncryptedStatic
}
return nil
}
func (x *UnidentifiedSenderMessage) GetEncryptedMessage() []byte {
if x != nil {
return x.EncryptedMessage
}
return nil
}
type ServerCertificate_Certificate struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id *uint32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
Key []byte `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ServerCertificate_Certificate) Reset() {
*x = ServerCertificate_Certificate{}
mi := &file_UnidentifiedDelivery_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ServerCertificate_Certificate) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ServerCertificate_Certificate) ProtoMessage() {}
func (x *ServerCertificate_Certificate) ProtoReflect() protoreflect.Message {
mi := &file_UnidentifiedDelivery_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ServerCertificate_Certificate.ProtoReflect.Descriptor instead.
func (*ServerCertificate_Certificate) Descriptor() ([]byte, []int) {
return file_UnidentifiedDelivery_proto_rawDescGZIP(), []int{0, 0}
}
func (x *ServerCertificate_Certificate) GetId() uint32 {
if x != nil && x.Id != nil {
return *x.Id
}
return 0
}
func (x *ServerCertificate_Certificate) GetKey() []byte {
if x != nil {
return x.Key
}
return nil
}
type SenderCertificate_Certificate struct {
state protoimpl.MessageState `protogen:"open.v1"`
SenderE164 *string `protobuf:"bytes,1,opt,name=senderE164" json:"senderE164,omitempty"`
SenderUuid *string `protobuf:"bytes,6,opt,name=senderUuid" json:"senderUuid,omitempty"`
SenderDevice *uint32 `protobuf:"varint,2,opt,name=senderDevice" json:"senderDevice,omitempty"`
Expires *uint64 `protobuf:"fixed64,3,opt,name=expires" json:"expires,omitempty"`
IdentityKey []byte `protobuf:"bytes,4,opt,name=identityKey" json:"identityKey,omitempty"`
Signer *ServerCertificate `protobuf:"bytes,5,opt,name=signer" json:"signer,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SenderCertificate_Certificate) Reset() {
*x = SenderCertificate_Certificate{}
mi := &file_UnidentifiedDelivery_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SenderCertificate_Certificate) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SenderCertificate_Certificate) ProtoMessage() {}
func (x *SenderCertificate_Certificate) ProtoReflect() protoreflect.Message {
mi := &file_UnidentifiedDelivery_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SenderCertificate_Certificate.ProtoReflect.Descriptor instead.
func (*SenderCertificate_Certificate) Descriptor() ([]byte, []int) {
return file_UnidentifiedDelivery_proto_rawDescGZIP(), []int{1, 0}
}
func (x *SenderCertificate_Certificate) GetSenderE164() string {
if x != nil && x.SenderE164 != nil {
return *x.SenderE164
}
return ""
}
func (x *SenderCertificate_Certificate) GetSenderUuid() string {
if x != nil && x.SenderUuid != nil {
return *x.SenderUuid
}
return ""
}
func (x *SenderCertificate_Certificate) GetSenderDevice() uint32 {
if x != nil && x.SenderDevice != nil {
return *x.SenderDevice
}
return 0
}
func (x *SenderCertificate_Certificate) GetExpires() uint64 {
if x != nil && x.Expires != nil {
return *x.Expires
}
return 0
}
func (x *SenderCertificate_Certificate) GetIdentityKey() []byte {
if x != nil {
return x.IdentityKey
}
return nil
}
func (x *SenderCertificate_Certificate) GetSigner() *ServerCertificate {
if x != nil {
return x.Signer
}
return nil
}
type UnidentifiedSenderMessage_Message struct {
state protoimpl.MessageState `protogen:"open.v1"`
Type *UnidentifiedSenderMessage_Message_Type `protobuf:"varint,1,opt,name=type,enum=signalservice.UnidentifiedSenderMessage_Message_Type" json:"type,omitempty"`
SenderCertificate *SenderCertificate `protobuf:"bytes,2,opt,name=senderCertificate" json:"senderCertificate,omitempty"`
Content []byte `protobuf:"bytes,3,opt,name=content" json:"content,omitempty"`
ContentHint *UnidentifiedSenderMessage_Message_ContentHint `protobuf:"varint,4,opt,name=contentHint,enum=signalservice.UnidentifiedSenderMessage_Message_ContentHint" json:"contentHint,omitempty"`
GroupId []byte `protobuf:"bytes,5,opt,name=groupId" json:"groupId,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *UnidentifiedSenderMessage_Message) Reset() {
*x = UnidentifiedSenderMessage_Message{}
mi := &file_UnidentifiedDelivery_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *UnidentifiedSenderMessage_Message) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UnidentifiedSenderMessage_Message) ProtoMessage() {}
func (x *UnidentifiedSenderMessage_Message) ProtoReflect() protoreflect.Message {
mi := &file_UnidentifiedDelivery_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UnidentifiedSenderMessage_Message.ProtoReflect.Descriptor instead.
func (*UnidentifiedSenderMessage_Message) Descriptor() ([]byte, []int) {
return file_UnidentifiedDelivery_proto_rawDescGZIP(), []int{2, 0}
}
func (x *UnidentifiedSenderMessage_Message) GetType() UnidentifiedSenderMessage_Message_Type {
if x != nil && x.Type != nil {
return *x.Type
}
return UnidentifiedSenderMessage_Message_MESSAGE
}
func (x *UnidentifiedSenderMessage_Message) GetSenderCertificate() *SenderCertificate {
if x != nil {
return x.SenderCertificate
}
return nil
}
func (x *UnidentifiedSenderMessage_Message) GetContent() []byte {
if x != nil {
return x.Content
}
return nil
}
func (x *UnidentifiedSenderMessage_Message) GetContentHint() UnidentifiedSenderMessage_Message_ContentHint {
if x != nil && x.ContentHint != nil {
return *x.ContentHint
}
return UnidentifiedSenderMessage_Message_DEFAULT
}
func (x *UnidentifiedSenderMessage_Message) GetGroupId() []byte {
if x != nil {
return x.GroupId
}
return nil
}
var File_UnidentifiedDelivery_proto protoreflect.FileDescriptor
const file_UnidentifiedDelivery_proto_rawDesc = "" +
"\n" +
"\x1aUnidentifiedDelivery.proto\x12\rsignalservice\"\x84\x01\n" +
"\x11ServerCertificate\x12 \n" +
"\vcertificate\x18\x01 \x01(\fR\vcertificate\x12\x1c\n" +
"\tsignature\x18\x02 \x01(\fR\tsignature\x1a/\n" +
"\vCertificate\x12\x0e\n" +
"\x02id\x18\x01 \x01(\rR\x02id\x12\x10\n" +
"\x03key\x18\x02 \x01(\fR\x03key\"\xbd\x02\n" +
"\x11SenderCertificate\x12 \n" +
"\vcertificate\x18\x01 \x01(\fR\vcertificate\x12\x1c\n" +
"\tsignature\x18\x02 \x01(\fR\tsignature\x1a\xe7\x01\n" +
"\vCertificate\x12\x1e\n" +
"\n" +
"senderE164\x18\x01 \x01(\tR\n" +
"senderE164\x12\x1e\n" +
"\n" +
"senderUuid\x18\x06 \x01(\tR\n" +
"senderUuid\x12\"\n" +
"\fsenderDevice\x18\x02 \x01(\rR\fsenderDevice\x12\x18\n" +
"\aexpires\x18\x03 \x01(\x06R\aexpires\x12 \n" +
"\videntityKey\x18\x04 \x01(\fR\videntityKey\x128\n" +
"\x06signer\x18\x05 \x01(\v2 .signalservice.ServerCertificateR\x06signer\"\xe7\x04\n" +
"\x19UnidentifiedSenderMessage\x12(\n" +
"\x0fephemeralPublic\x18\x01 \x01(\fR\x0fephemeralPublic\x12(\n" +
"\x0fencryptedStatic\x18\x02 \x01(\fR\x0fencryptedStatic\x12*\n" +
"\x10encryptedMessage\x18\x03 \x01(\fR\x10encryptedMessage\x1a\xc9\x03\n" +
"\aMessage\x12I\n" +
"\x04type\x18\x01 \x01(\x0e25.signalservice.UnidentifiedSenderMessage.Message.TypeR\x04type\x12N\n" +
"\x11senderCertificate\x18\x02 \x01(\v2 .signalservice.SenderCertificateR\x11senderCertificate\x12\x18\n" +
"\acontent\x18\x03 \x01(\fR\acontent\x12^\n" +
"\vcontentHint\x18\x04 \x01(\x0e2<.signalservice.UnidentifiedSenderMessage.Message.ContentHintR\vcontentHint\x12\x18\n" +
"\agroupId\x18\x05 \x01(\fR\agroupId\"U\n" +
"\x04Type\x12\v\n" +
"\aMESSAGE\x10\x02\x12\x12\n" +
"\x0ePREKEY_MESSAGE\x10\x03\x12\x15\n" +
"\x11SENDERKEY_MESSAGE\x10\a\x12\x15\n" +
"\x11PLAINTEXT_CONTENT\x10\b\"8\n" +
"\vContentHint\x12\v\n" +
"\aDEFAULT\x10\x00\x12\x0e\n" +
"\n" +
"RESENDABLE\x10\x01\x12\f\n" +
"\bIMPLICIT\x10\x02B6\n" +
"%org.whispersystems.libsignal.protocolB\rWhisperProtos"
var (
file_UnidentifiedDelivery_proto_rawDescOnce sync.Once
file_UnidentifiedDelivery_proto_rawDescData []byte
)
func file_UnidentifiedDelivery_proto_rawDescGZIP() []byte {
file_UnidentifiedDelivery_proto_rawDescOnce.Do(func() {
file_UnidentifiedDelivery_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_UnidentifiedDelivery_proto_rawDesc), len(file_UnidentifiedDelivery_proto_rawDesc)))
})
return file_UnidentifiedDelivery_proto_rawDescData
}
var file_UnidentifiedDelivery_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_UnidentifiedDelivery_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_UnidentifiedDelivery_proto_goTypes = []any{
(UnidentifiedSenderMessage_Message_Type)(0), // 0: signalservice.UnidentifiedSenderMessage.Message.Type
(UnidentifiedSenderMessage_Message_ContentHint)(0), // 1: signalservice.UnidentifiedSenderMessage.Message.ContentHint
(*ServerCertificate)(nil), // 2: signalservice.ServerCertificate
(*SenderCertificate)(nil), // 3: signalservice.SenderCertificate
(*UnidentifiedSenderMessage)(nil), // 4: signalservice.UnidentifiedSenderMessage
(*ServerCertificate_Certificate)(nil), // 5: signalservice.ServerCertificate.Certificate
(*SenderCertificate_Certificate)(nil), // 6: signalservice.SenderCertificate.Certificate
(*UnidentifiedSenderMessage_Message)(nil), // 7: signalservice.UnidentifiedSenderMessage.Message
}
var file_UnidentifiedDelivery_proto_depIdxs = []int32{
2, // 0: signalservice.SenderCertificate.Certificate.signer:type_name -> signalservice.ServerCertificate
0, // 1: signalservice.UnidentifiedSenderMessage.Message.type:type_name -> signalservice.UnidentifiedSenderMessage.Message.Type
3, // 2: signalservice.UnidentifiedSenderMessage.Message.senderCertificate:type_name -> signalservice.SenderCertificate
1, // 3: signalservice.UnidentifiedSenderMessage.Message.contentHint:type_name -> signalservice.UnidentifiedSenderMessage.Message.ContentHint
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_UnidentifiedDelivery_proto_init() }
func file_UnidentifiedDelivery_proto_init() {
if File_UnidentifiedDelivery_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_UnidentifiedDelivery_proto_rawDesc), len(file_UnidentifiedDelivery_proto_rawDesc)),
NumEnums: 2,
NumMessages: 6,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_UnidentifiedDelivery_proto_goTypes,
DependencyIndexes: file_UnidentifiedDelivery_proto_depIdxs,
EnumInfos: file_UnidentifiedDelivery_proto_enumTypes,
MessageInfos: file_UnidentifiedDelivery_proto_msgTypes,
}.Build()
File_UnidentifiedDelivery_proto = out.File
file_UnidentifiedDelivery_proto_goTypes = nil
file_UnidentifiedDelivery_proto_depIdxs = nil
}

View file

@ -0,0 +1,70 @@
// Copyright 2018 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
package signalservice;
option java_package = "org.whispersystems.libsignal.protocol";
option java_outer_classname = "WhisperProtos";
message ServerCertificate {
message Certificate {
optional uint32 id = 1;
optional bytes key = 2;
}
optional bytes certificate = 1;
optional bytes signature = 2;
}
message SenderCertificate {
message Certificate {
optional string senderE164 = 1;
optional string senderUuid = 6;
optional uint32 senderDevice = 2;
optional fixed64 expires = 3;
optional bytes identityKey = 4;
optional ServerCertificate signer = 5;
}
optional bytes certificate = 1;
optional bytes signature = 2;
}
message UnidentifiedSenderMessage {
message Message {
enum Type {
// Our parser does not handle reserved in enums: DESKTOP-1569
// reserved 1;
MESSAGE = 2;
PREKEY_MESSAGE = 3;
// Further cases should line up with Envelope.Type, even though old cases don't.
// reserved 3 to 6;
SENDERKEY_MESSAGE = 7;
PLAINTEXT_CONTENT = 8;
}
enum ContentHint {
// Show an error immediately; it was important but we can't retry.
DEFAULT = 0;
// Sender will try to resend; delay any error UI if possible
RESENDABLE = 1;
// Don't show any error UI at all; this is something sent implicitly like a typing message or a receipt
IMPLICIT = 2;
}
optional Type type = 1;
optional SenderCertificate senderCertificate = 2;
optional bytes content = 3;
optional ContentHint contentHint = 4;
optional bytes groupId = 5;
}
optional bytes ephemeralPublic = 1;
optional bytes encryptedStatic = 2;
optional bytes encryptedMessage = 3;
}

View file

@ -7,7 +7,7 @@
// versions: // versions:
// protoc-gen-go v1.36.11 // protoc-gen-go v1.36.11
// protoc v7.34.1 // protoc v7.34.1
// source: signalpb/WebSocketResources.proto // source: WebSocketResources.proto
package signalpb package signalpb
@ -59,11 +59,11 @@ func (x WebSocketMessage_Type) String() string {
} }
func (WebSocketMessage_Type) Descriptor() protoreflect.EnumDescriptor { func (WebSocketMessage_Type) Descriptor() protoreflect.EnumDescriptor {
return file_signalpb_WebSocketResources_proto_enumTypes[0].Descriptor() return file_WebSocketResources_proto_enumTypes[0].Descriptor()
} }
func (WebSocketMessage_Type) Type() protoreflect.EnumType { func (WebSocketMessage_Type) Type() protoreflect.EnumType {
return &file_signalpb_WebSocketResources_proto_enumTypes[0] return &file_WebSocketResources_proto_enumTypes[0]
} }
func (x WebSocketMessage_Type) Number() protoreflect.EnumNumber { func (x WebSocketMessage_Type) Number() protoreflect.EnumNumber {
@ -82,7 +82,7 @@ func (x *WebSocketMessage_Type) UnmarshalJSON(b []byte) error {
// Deprecated: Use WebSocketMessage_Type.Descriptor instead. // Deprecated: Use WebSocketMessage_Type.Descriptor instead.
func (WebSocketMessage_Type) EnumDescriptor() ([]byte, []int) { func (WebSocketMessage_Type) EnumDescriptor() ([]byte, []int) {
return file_signalpb_WebSocketResources_proto_rawDescGZIP(), []int{2, 0} return file_WebSocketResources_proto_rawDescGZIP(), []int{2, 0}
} }
type WebSocketRequestMessage struct { type WebSocketRequestMessage struct {
@ -98,7 +98,7 @@ type WebSocketRequestMessage struct {
func (x *WebSocketRequestMessage) Reset() { func (x *WebSocketRequestMessage) Reset() {
*x = WebSocketRequestMessage{} *x = WebSocketRequestMessage{}
mi := &file_signalpb_WebSocketResources_proto_msgTypes[0] mi := &file_WebSocketResources_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -110,7 +110,7 @@ func (x *WebSocketRequestMessage) String() string {
func (*WebSocketRequestMessage) ProtoMessage() {} func (*WebSocketRequestMessage) ProtoMessage() {}
func (x *WebSocketRequestMessage) ProtoReflect() protoreflect.Message { func (x *WebSocketRequestMessage) ProtoReflect() protoreflect.Message {
mi := &file_signalpb_WebSocketResources_proto_msgTypes[0] mi := &file_WebSocketResources_proto_msgTypes[0]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -123,7 +123,7 @@ func (x *WebSocketRequestMessage) ProtoReflect() protoreflect.Message {
// Deprecated: Use WebSocketRequestMessage.ProtoReflect.Descriptor instead. // Deprecated: Use WebSocketRequestMessage.ProtoReflect.Descriptor instead.
func (*WebSocketRequestMessage) Descriptor() ([]byte, []int) { func (*WebSocketRequestMessage) Descriptor() ([]byte, []int) {
return file_signalpb_WebSocketResources_proto_rawDescGZIP(), []int{0} return file_WebSocketResources_proto_rawDescGZIP(), []int{0}
} }
func (x *WebSocketRequestMessage) GetVerb() string { func (x *WebSocketRequestMessage) GetVerb() string {
@ -174,7 +174,7 @@ type WebSocketResponseMessage struct {
func (x *WebSocketResponseMessage) Reset() { func (x *WebSocketResponseMessage) Reset() {
*x = WebSocketResponseMessage{} *x = WebSocketResponseMessage{}
mi := &file_signalpb_WebSocketResources_proto_msgTypes[1] mi := &file_WebSocketResources_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -186,7 +186,7 @@ func (x *WebSocketResponseMessage) String() string {
func (*WebSocketResponseMessage) ProtoMessage() {} func (*WebSocketResponseMessage) ProtoMessage() {}
func (x *WebSocketResponseMessage) ProtoReflect() protoreflect.Message { func (x *WebSocketResponseMessage) ProtoReflect() protoreflect.Message {
mi := &file_signalpb_WebSocketResources_proto_msgTypes[1] mi := &file_WebSocketResources_proto_msgTypes[1]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -199,7 +199,7 @@ func (x *WebSocketResponseMessage) ProtoReflect() protoreflect.Message {
// Deprecated: Use WebSocketResponseMessage.ProtoReflect.Descriptor instead. // Deprecated: Use WebSocketResponseMessage.ProtoReflect.Descriptor instead.
func (*WebSocketResponseMessage) Descriptor() ([]byte, []int) { func (*WebSocketResponseMessage) Descriptor() ([]byte, []int) {
return file_signalpb_WebSocketResources_proto_rawDescGZIP(), []int{1} return file_WebSocketResources_proto_rawDescGZIP(), []int{1}
} }
func (x *WebSocketResponseMessage) GetId() uint64 { func (x *WebSocketResponseMessage) GetId() uint64 {
@ -248,7 +248,7 @@ type WebSocketMessage struct {
func (x *WebSocketMessage) Reset() { func (x *WebSocketMessage) Reset() {
*x = WebSocketMessage{} *x = WebSocketMessage{}
mi := &file_signalpb_WebSocketResources_proto_msgTypes[2] mi := &file_WebSocketResources_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -260,7 +260,7 @@ func (x *WebSocketMessage) String() string {
func (*WebSocketMessage) ProtoMessage() {} func (*WebSocketMessage) ProtoMessage() {}
func (x *WebSocketMessage) ProtoReflect() protoreflect.Message { func (x *WebSocketMessage) ProtoReflect() protoreflect.Message {
mi := &file_signalpb_WebSocketResources_proto_msgTypes[2] mi := &file_WebSocketResources_proto_msgTypes[2]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -273,7 +273,7 @@ func (x *WebSocketMessage) ProtoReflect() protoreflect.Message {
// Deprecated: Use WebSocketMessage.ProtoReflect.Descriptor instead. // Deprecated: Use WebSocketMessage.ProtoReflect.Descriptor instead.
func (*WebSocketMessage) Descriptor() ([]byte, []int) { func (*WebSocketMessage) Descriptor() ([]byte, []int) {
return file_signalpb_WebSocketResources_proto_rawDescGZIP(), []int{2} return file_WebSocketResources_proto_rawDescGZIP(), []int{2}
} }
func (x *WebSocketMessage) GetType() WebSocketMessage_Type { func (x *WebSocketMessage) GetType() WebSocketMessage_Type {
@ -297,11 +297,11 @@ func (x *WebSocketMessage) GetResponse() *WebSocketResponseMessage {
return nil return nil
} }
var File_signalpb_WebSocketResources_proto protoreflect.FileDescriptor var File_WebSocketResources_proto protoreflect.FileDescriptor
const file_signalpb_WebSocketResources_proto_rawDesc = "" + const file_WebSocketResources_proto_rawDesc = "" +
"\n" + "\n" +
"!signalpb/WebSocketResources.proto\x12\rsignalservice\"\x7f\n" + "\x18WebSocketResources.proto\x12\rsignalservice\"\x7f\n" +
"\x17WebSocketRequestMessage\x12\x12\n" + "\x17WebSocketRequestMessage\x12\x12\n" +
"\x04verb\x18\x01 \x01(\tR\x04verb\x12\x12\n" + "\x04verb\x18\x01 \x01(\tR\x04verb\x12\x12\n" +
"\x04path\x18\x02 \x01(\tR\x04path\x12\x12\n" + "\x04path\x18\x02 \x01(\tR\x04path\x12\x12\n" +
@ -325,26 +325,26 @@ const file_signalpb_WebSocketResources_proto_rawDesc = "" +
"\x1corg.signal.network.websocketB\x0fWebSocketProtos" "\x1corg.signal.network.websocketB\x0fWebSocketProtos"
var ( var (
file_signalpb_WebSocketResources_proto_rawDescOnce sync.Once file_WebSocketResources_proto_rawDescOnce sync.Once
file_signalpb_WebSocketResources_proto_rawDescData []byte file_WebSocketResources_proto_rawDescData []byte
) )
func file_signalpb_WebSocketResources_proto_rawDescGZIP() []byte { func file_WebSocketResources_proto_rawDescGZIP() []byte {
file_signalpb_WebSocketResources_proto_rawDescOnce.Do(func() { file_WebSocketResources_proto_rawDescOnce.Do(func() {
file_signalpb_WebSocketResources_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_signalpb_WebSocketResources_proto_rawDesc), len(file_signalpb_WebSocketResources_proto_rawDesc))) file_WebSocketResources_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_WebSocketResources_proto_rawDesc), len(file_WebSocketResources_proto_rawDesc)))
}) })
return file_signalpb_WebSocketResources_proto_rawDescData return file_WebSocketResources_proto_rawDescData
} }
var file_signalpb_WebSocketResources_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_WebSocketResources_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_signalpb_WebSocketResources_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_WebSocketResources_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_signalpb_WebSocketResources_proto_goTypes = []any{ var file_WebSocketResources_proto_goTypes = []any{
(WebSocketMessage_Type)(0), // 0: signalservice.WebSocketMessage.Type (WebSocketMessage_Type)(0), // 0: signalservice.WebSocketMessage.Type
(*WebSocketRequestMessage)(nil), // 1: signalservice.WebSocketRequestMessage (*WebSocketRequestMessage)(nil), // 1: signalservice.WebSocketRequestMessage
(*WebSocketResponseMessage)(nil), // 2: signalservice.WebSocketResponseMessage (*WebSocketResponseMessage)(nil), // 2: signalservice.WebSocketResponseMessage
(*WebSocketMessage)(nil), // 3: signalservice.WebSocketMessage (*WebSocketMessage)(nil), // 3: signalservice.WebSocketMessage
} }
var file_signalpb_WebSocketResources_proto_depIdxs = []int32{ var file_WebSocketResources_proto_depIdxs = []int32{
0, // 0: signalservice.WebSocketMessage.type:type_name -> signalservice.WebSocketMessage.Type 0, // 0: signalservice.WebSocketMessage.type:type_name -> signalservice.WebSocketMessage.Type
1, // 1: signalservice.WebSocketMessage.request:type_name -> signalservice.WebSocketRequestMessage 1, // 1: signalservice.WebSocketMessage.request:type_name -> signalservice.WebSocketRequestMessage
2, // 2: signalservice.WebSocketMessage.response:type_name -> signalservice.WebSocketResponseMessage 2, // 2: signalservice.WebSocketMessage.response:type_name -> signalservice.WebSocketResponseMessage
@ -355,27 +355,27 @@ var file_signalpb_WebSocketResources_proto_depIdxs = []int32{
0, // [0:3] is the sub-list for field type_name 0, // [0:3] is the sub-list for field type_name
} }
func init() { file_signalpb_WebSocketResources_proto_init() } func init() { file_WebSocketResources_proto_init() }
func file_signalpb_WebSocketResources_proto_init() { func file_WebSocketResources_proto_init() {
if File_signalpb_WebSocketResources_proto != nil { if File_WebSocketResources_proto != nil {
return return
} }
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_signalpb_WebSocketResources_proto_rawDesc), len(file_signalpb_WebSocketResources_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_WebSocketResources_proto_rawDesc), len(file_WebSocketResources_proto_rawDesc)),
NumEnums: 1, NumEnums: 1,
NumMessages: 3, NumMessages: 3,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },
GoTypes: file_signalpb_WebSocketResources_proto_goTypes, GoTypes: file_WebSocketResources_proto_goTypes,
DependencyIndexes: file_signalpb_WebSocketResources_proto_depIdxs, DependencyIndexes: file_WebSocketResources_proto_depIdxs,
EnumInfos: file_signalpb_WebSocketResources_proto_enumTypes, EnumInfos: file_WebSocketResources_proto_enumTypes,
MessageInfos: file_signalpb_WebSocketResources_proto_msgTypes, MessageInfos: file_WebSocketResources_proto_msgTypes,
}.Build() }.Build()
File_signalpb_WebSocketResources_proto = out.File File_WebSocketResources_proto = out.File
file_signalpb_WebSocketResources_proto_goTypes = nil file_WebSocketResources_proto_goTypes = nil
file_signalpb_WebSocketResources_proto_depIdxs = nil file_WebSocketResources_proto_depIdxs = nil
} }

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -103,12 +103,6 @@ message AccountData {
} }
message AccountSettings { message AccountSettings {
enum UnreadBadgeType {
UNKNOWN_BADGE_TYPE = 0; // Interpret as "Unread messages"
UNREAD_MESSAGES = 1;
UNREAD_CHATS = 2;
}
bool readReceipts = 1; bool readReceipts = 1;
bool sealedSenderIndicators = 2; bool sealedSenderIndicators = 2;
bool typingIndicators = 3; bool typingIndicators = 3;
@ -142,14 +136,6 @@ message AccountData {
bool allowSealedSenderFromAnyone = 30; bool allowSealedSenderFromAnyone = 30;
bool allowAutomaticKeyVerification = 31; bool allowAutomaticKeyVerification = 31;
bool hasSeenAdminDeleteEducationDialog = 32; bool hasSeenAdminDeleteEducationDialog = 32;
UnreadBadgeType unreadBadgeType = 33; // Only used in ios/desktop
optional bool includeMutedChatsInBadge = 34; // Only used in ios/desktop. If unset, consider this disabled
optional bool reactionNotifications = 35; // If unset, consider this enabled
optional bool notifyForCallsIfMuted = 36; // If unset, consider this disabled
optional bool notifyForMentionsIfMuted = 37; // If unset, consider this enabled
optional bool notifyForRepliesIfMuted = 38; // If unset, consider this enabled
optional bool showUnreadReminders = 39; // If unset, consider this enabled
optional bool notifyWhenContactJoins = 40; // If unset, consider this disabled
} }
message SubscriberData { message SubscriberData {
@ -286,7 +272,6 @@ message Contact {
string systemNickname = 20; string systemNickname = 20;
optional AvatarColor avatarColor = 21; optional AvatarColor avatarColor = 21;
optional bytes keyTransparencyData = 22; optional bytes keyTransparencyData = 22;
uint64 blockedAtTimestamp = 23; // if `blocked` is true, 0 means unknown block time
} }
message Group { message Group {
@ -303,7 +288,6 @@ message Group {
GroupSnapshot snapshot = 5; GroupSnapshot snapshot = 5;
bool blocked = 6; bool blocked = 6;
optional AvatarColor avatarColor = 7; optional AvatarColor avatarColor = 7;
uint64 blockedAtTimestamp = 8; // if `blocked` is true, 0 means unknown block time
// These are simply plaintext copies of the groups proto from Groups.proto. // These are simply plaintext copies of the groups proto from Groups.proto.
// They should be kept completely in-sync with Groups.proto. // They should be kept completely in-sync with Groups.proto.
@ -401,13 +385,9 @@ message Chat {
optional uint64 expirationTimerMs = 5; optional uint64 expirationTimerMs = 5;
optional uint64 muteUntilMs = 6; // INT64_MAX (2^63 - 1) = "always muted". optional uint64 muteUntilMs = 6; // INT64_MAX (2^63 - 1) = "always muted".
bool markedUnread = 7; bool markedUnread = 7;
bool dontNotifyForMentionsIfMuted = 8; // will be deprecated in favor of [notifyForMentionsIfMuted] bool dontNotifyForMentionsIfMuted = 8;
ChatStyle style = 9; ChatStyle style = 9;
uint32 expireTimerVersion = 10; uint32 expireTimerVersion = 10;
optional bool notifyForCallsIfMuted = 11; // If unset, use default global settings
optional bool notifyForMentionsIfMuted = 12; // If unset, use default global settings. Only for groups. If [dontNotifyForMentionsIfMuted] is true, this should be initialized to false.
optional bool notifyForRepliesIfMuted = 13; // If unset, use default global settings. Only for groups.
optional bool showUnreadReminders = 14; // If unset, use default global settings
} }
/** /**

View file

@ -1,18 +1,19 @@
#!/bin/bash #!/bin/sh
cd $(dirname "$0") PKG_IMPORT_PATH="go.mau.fi/mautrix-signal/pkg/signalmeow/signalpb"
BASE_IMPORT_PATH="go.mau.fi/mautrix-signal/pkg/signalmeow/protobuf" for file in *.proto
opts=() do
for file in */*.proto; do # Requires https://go-review.googlesource.com/c/protobuf/+/369634
opts+=("--go_opt=M${file}=${BASE_IMPORT_PATH}/$(dirname "$file")") protoc --go_out=. \
opts+=("--go-grpc_opt=M${file}=${BASE_IMPORT_PATH}/$(dirname "$file")") --go_opt=M${file}=$PKG_IMPORT_PATH \
--go_opt=paths=source_relative \
$file
done done
for file in org/signal/chat/*.proto; do protoc --go_out=. \
file_without_ext=$(basename "$file" .proto) --go_opt=Mbackuppb/Backup.proto=$PKG_IMPORT_PATH/backuppb \
opts+=("--go_opt=M${file}=${BASE_IMPORT_PATH}/rpc/${file_without_ext}") --go_opt=paths=source_relative \
opts+=("--go-grpc_opt=M${file}=${BASE_IMPORT_PATH}/rpc/${file_without_ext}") backuppb/Backup.proto
done protoc --go_out=. \
protoc --go_out=. --go-grpc_out=. \ --go_opt=Mcds2pb/cds2.proto=$PKG_IMPORT_PATH/cds2pb \
--go_opt=module=$BASE_IMPORT_PATH \ --go_opt=paths=source_relative \
--go-grpc_opt=module=$BASE_IMPORT_PATH "${opts[@]}" \ cds2pb/cds2.proto
*/*.proto org/signal/chat/*.proto
pre-commit run -a pre-commit run -a

View file

@ -1,608 +0,0 @@
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.account;
import "google/protobuf/empty.proto";
import "org/signal/chat/common.proto";
import "org/signal/chat/errors.proto";
import "org/signal/chat/messages.proto";
import "org/signal/chat/require.proto";
import "org/signal/chat/tag.proto";
// Provides methods for working with Signal accounts.
service Accounts {
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
// Returns basic identifiers for the authenticated account.
rpc GetAccountIdentity(GetAccountIdentityRequest) returns (GetAccountIdentityResponse) {}
// Returns entitlements for the authenticated account.
rpc GetEntitlements(GetEntitlementsRequest) returns (GetEntitlementsResponse) {}
// Deletes the authenticated account, purging all associated data in the
// process.
rpc DeleteAccount(DeleteAccountRequest) returns (DeleteAccountResponse) {}
// Sets the registration lock secret for the authenticated account. To remove
// a registration lock, please use `ClearRegistrationLock`.
rpc SetRegistrationLock(SetRegistrationLockRequest) returns (SetRegistrationLockResponse) {}
// Removes any registration lock credentials from the authenticated account.
rpc ClearRegistrationLock(ClearRegistrationLockRequest) returns (ClearRegistrationLockResponse) {}
// Attempts to reserve one of multiple given username hashes. Reserved
// usernames may be claimed later via `ConfirmUsernameHash`.
rpc ReserveUsernameHash(ReserveUsernameHashRequest) returns (ReserveUsernameHashResponse) {}
// Sets the username hash/encrypted username to a previously-reserved value
// (see `ReserveUsernameHash`).
rpc ConfirmUsernameHash(ConfirmUsernameHashRequest) returns (ConfirmUsernameHashResponse) {}
// Clears the current username hash, ciphertext, and link for the
// authenticated user.
rpc DeleteUsernameHash(DeleteUsernameHashRequest) returns (DeleteUsernameHashResponse) {}
// Associates the given username ciphertext with the account, replacing any
// previously stored ciphertext. A new link handle will optionally be created,
// and the link handle to use will be returned in any event.
rpc SetUsernameLink(SetUsernameLinkRequest) returns (SetUsernameLinkResponse) {}
// Clears any username link associated with the authenticated account.
rpc DeleteUsernameLink(DeleteUsernameLinkRequest) returns (DeleteUsernameLinkResponse) {}
// Configures "unidentified access" keys and preferences for the authenticated
// account. Other users permitted to interact with this account anonymously
// may take actions like fetching pre-keys and profiles for this account or
// sending sealed-sender messages without providing identifying credentials.
rpc ConfigureUnidentifiedAccess(ConfigureUnidentifiedAccessRequest) returns (ConfigureUnidentifiedAccessResponse) {}
// Sets whether the authenticated account may be discovered by phone number
// via the Contact Discovery Service (CDS).
rpc SetDiscoverableByPhoneNumber(SetDiscoverableByPhoneNumberRequest) returns (SetDiscoverableByPhoneNumberResponse) {}
// Sets the registration recovery password for the authenticated account.
rpc SetRegistrationRecoveryPassword(SetRegistrationRecoveryPasswordRequest) returns (SetRegistrationRecoveryPasswordResponse) {}
// Store a public key used to issue and verify zero-knowledge (anonymous) credentials for the account.
rpc SetZkCredentialKey(SetZkCredentialKeyRequest) returns (SetZkCredentialKeyResponse) {}
// Changes the phone number associated with the authenticated account.
rpc ChangeNumber(ChangeNumberRequest) returns (ChangeNumberResponse) {}
// Produces a report of non-ephemeral account data stored by the service
rpc GetAccountDataReport(GetAccountDataReportRequest) returns (GetAccountDataReportResponse) {}
// Gets the capabilities for the authenticated account.
rpc GetCapabilities(GetCapabilitiesRequest) returns (GetCapabilitiesResponse) {}
// Generates and stores a pending TOTP key for the authenticated account.
// To "activate" the key, callers must call the `ConfirmTotpKey` endpoint.
rpc GenerateTotpKey(GenerateTotpKeyRequest) returns (GenerateTotpKeyResponse) {}
// Confirms that the caller has stored and can derive one-time passwords from
// a pending TOTP key generated via `GenerateTotpKey` and stores/activates the
// key for the caller's account
rpc ConfirmTotpKey(ConfirmTotpKeyRequest) returns (ConfirmTotpKeyResponse) {}
// Returns a list of confirmed TOTP keys for the authenticated account
rpc ListTotpKeys(ListTotpKeysRequest) returns (ListTotpKeysResponse) {}
// Updates encrypted, user-supplied metadata (e.g. a human-readable name and
// creation timestamp) for an existing, confirmed TOTP key
rpc SetTotpKeyMetadata(SetTotpKeyMetadataRequest) returns (SetTotpKeyMetadataResponse) {}
// Removes a TOTP from the authenticated account
rpc RemoveTotpKey(RemoveTotpKeyRequest) returns (RemoveTotpKeyResponse) {}
}
// Provides methods for looking up Signal accounts. Callers must not provide
// identifying credentials when calling methods in this service.
service AccountsAnonymous {
option (require.auth) = AUTH_ONLY_ANONYMOUS;
// Checks whether an account with the given service identifier exists.
rpc CheckAccountExistence(CheckAccountExistenceRequest) returns (CheckAccountExistenceResponse) {}
// Finds the service identifier of the account associated with the given
// username hash.
rpc LookupUsernameHash(LookupUsernameHashRequest) returns (LookupUsernameHashResponse) {}
// Finds the encrypted username identified by a given username link handle.
rpc LookupUsernameLink(LookupUsernameLinkRequest) returns (LookupUsernameLinkResponse) {}
// Gets the publicly-visible capabilities enabled on the account identified by
// the provided ACI.
rpc GetCapabilities(GetCapabilitiesAnonymousRequest) returns (GetCapabilitiesAnonymousResponse) {}
}
message GetAccountIdentityRequest {
}
message GetAccountIdentityResponse {
// The identifiers for the authenticated account.
common.AccountIdentifiers account_identifiers = 1;
}
message GetEntitlementsRequest {
}
message GetEntitlementsResponse {
message BadgeEntitlement {
// The id of the badge the account is entitled. Metadata to display for
// badges may be obtained by cross-referencing badge ids with
// RemoteConfiguration.GetBadges.
string badge_id = 1;
// When the badge expires, in number of seconds since epoch
uint64 expiration_epoch_seconds = 2;
// Whether the badge is currently configured to be visible
bool visible = 3;
}
message BackupEntitlement {
// The backup level of the account
uint64 level = 1;
// When the backup entitlement expires, in number of seconds since epoch
uint64 expiration_epoch_seconds = 2;
}
// Active badges added via Donations.redeemReceipt
repeated BadgeEntitlement badges = 1;
// If present, the backup level set via Backups.redeemReceipt
BackupEntitlement backup = 2;
}
message DeleteAccountRequest {
}
message DeleteAccountResponse {
}
message SetRegistrationLockRequest {
// The new registration lock secret for the authenticated account.
bytes registration_lock = 1 [(require.exactlySize) = 32];
}
message SetRegistrationLockResponse {
}
message ClearRegistrationLockRequest {
}
message ClearRegistrationLockResponse {
}
message ReserveUsernameHashRequest {
// A prioritized list of username hashes to attempt to reserve.
repeated bytes username_hashes = 1 [(require.size) = {min: 1, max: 20}, (require.each) = {exactlySize: 32}];
}
message UsernameNotAvailable {}
message ReserveUsernameHashResponse {
oneof response {
// The first username hash that was available (and actually reserved).
bytes username_hash = 1;
// Indicates that, of all of the candidate hashes provided, none were
// available. Callers may generate a new set of hashes and and retry.
UsernameNotAvailable username_not_available = 2 [(tag.reason) = "username_not_available"];
}
}
message ConfirmUsernameHashRequest {
// The username hash to claim for the authenticated account.
bytes username_hash = 1 [(require.exactlySize) = 32];
// A zero-knowledge proof that the given username hash was generated by the
// Signal username algorithm.
bytes zk_proof = 2 [(require.nonEmpty) = true];
// The ciphertext of the chosen username for use in public-facing contexts
// (e.g. links and QR codes).
bytes username_ciphertext = 3 [(require.size) = {min: 1, max: 128}];
}
message ConfirmUsernameHashResponse {
message ConfirmedUsernameHash {
reserved 1; // username_hash
// The server-generated username link handle for the newly-confirmed username.
bytes username_link_handle = 2;
}
oneof response {
// The details of the successfully confirmed username.
ConfirmedUsernameHash confirmed_username_hash = 1;
// The provided hash was not reserved for the account.
errors.FailedPrecondition reservation_not_found = 2 [(tag.reason) = "reservation_not_found"];
// The reservation has lapsed and the requested username has been claimed by
// another caller.
UsernameNotAvailable username_not_available = 3 [(tag.reason) = "username_not_available"];
}
}
message DeleteUsernameHashRequest {
}
message DeleteUsernameHashResponse {
}
message SetUsernameLinkRequest {
// The username ciphertext for which to generate a new link handle.
bytes username_ciphertext = 1 [(require.size) = {min: 1, max: 128}];
// If true and the account already had an encrypted username stored, the
// existing link handle will be reused. Otherwise a new link handle will be
// created.
bool keep_link_handle = 2;
}
message SetUsernameLinkResponse {
oneof response {
// A new link handle for the given username ciphertext.
bytes username_link_handle = 1;
// The authenticated account did not have a username set.
errors.FailedPrecondition no_username_set = 2 [(tag.reason) = "no_username_set"];
}
}
message DeleteUsernameLinkRequest {
}
message DeleteUsernameLinkResponse {
}
message ConfigureUnidentifiedAccessRequest {
oneof configuration {
// The key that other users must provide to interact with this account
// anonymously (i.e. to retrieve keys or profiles or to send messages) unless
// unrestricted unidentified access is permitted. Must be present if
// unrestricted unidentified access is not allowed.
bytes unidentified_access_key = 1 [(require.exactlySize) = 16];
// If set, any user may interact with this account anonymously without
// providing an unidentified access key. Otherwise, users must provide the
// given unidentified access key to interact with this account anonymously.
// Setting unrestricted unidentified access will clear any existing
// unidentified_access_key
google.protobuf.Empty allow_unrestricted_unidentified_access = 2;
}
}
message ConfigureUnidentifiedAccessResponse {
}
message SetDiscoverableByPhoneNumberRequest {
// If true, the authenticated account may be discovered by phone number via
// the Contact Discovery Service (CDS). Otherwise, other users must discover
// this account by other means (i.e. by username).
bool discoverable_by_phone_number = 1;
}
message SetDiscoverableByPhoneNumberResponse {
}
message SetRegistrationRecoveryPasswordRequest {
// The new registration recovery password for the authenticated account.
bytes registration_recovery_password = 1 [(require.exactlySize) = 32];
}
message SetRegistrationRecoveryPasswordResponse {
}
message CheckAccountExistenceRequest {
// The service identifier of an account that may or may not exist.
common.ServiceIdentifier service_identifier = 1;
}
message CheckAccountExistenceResponse {
// True if an account exists with the given service identifier or false if no
// account was found.
bool account_exists = 1;
}
message LookupUsernameHashRequest {
// A 32-byte username hash for which to find an account.
bytes username_hash = 1 [(require.exactlySize) = 32];
}
message LookupUsernameHashResponse {
oneof response {
// The service identifier associated with the provided username hash.
common.ServiceIdentifier service_identifier = 1;
// No account was found for the provided username hash.
errors.NotFound not_found = 2 [(tag.reason) = "not_found"];
}
}
message LookupUsernameLinkRequest {
// The link handle for which to find an encrypted username. Link handles are
// 16-byte representations of UUIDs.
bytes username_link_handle = 1 [(require.exactlySize) = 16];
}
message LookupUsernameLinkResponse {
oneof response {
// The ciphertext of the username identified by the provided link handle.
bytes username_ciphertext = 1;
// No username was found for the provided link handle.
errors.NotFound not_found = 2 [(tag.reason) = "not_found"];
}
}
message SetZkCredentialKeyRequest {
// A serialized libsignal ZkCredentialPublicKey
bytes public_key = 1 [(require.exactlySize) = 33];
}
message SetZkCredentialKeyResponse {
// A random, non-zero, value that must be included in credential requests using the key.
//
// This value allows the server to ratchet the resulting binding identity,
// as reverting to the previous key will result in a new rotation ID.
uint64 rotation_id = 1;
}
message ChangeNumberRequest {
// A means of authenticating the change-number request for the new phone
// number. Exactly one must be provided.
oneof verification {
// A verified registration session ID (as returned by the registration
// service) for the new phone number.
bytes session_id = 1 [(require.nonEmpty) = true];
// A registration recovery password for the new phone number.
bytes recovery_password = 2 [(require.exactlySize) = 32];
}
// The new phone number for the authenticated account.
string number = 3 [(require.e164) = true];
// The registration lock secret for the new phone number, if the account
// associated with the new phone number has a registration lock configured.
bytes registration_lock = 4 [(require.exactlySize) = 0, (require.exactlySize) = 32];
// The new public identity key to use for the phone-number identity (PNI)
// associated with the new phone number.
bytes pni_identity_key = 5 [(require.nonEmpty) = true];
// Synchronization messages to send to companion devices to supply the private
// keys associated with the new identity key and their new pre-keys. Exactly
// one message must be supplied for each device other than the sending
// (primary) device. May be omitted if no companion devices are linked to the
// account.
messages.IndividualRecipientMessageBundle device_messages = 6;
// A new signed EC pre-key for each device on the account, including the
// sending device, keyed by device ID. Each must be accompanied by a valid
// signature from the identity key in this request.
map<uint32, common.EcSignedPreKey> device_pni_signed_pre_keys = 7;
// A new signed post-quantum last-resort pre-key for each device on the
// account, including the sending device, keyed by device ID. Each must be
// accompanied by a valid signature from the identity key in this request.
map<uint32, common.KemSignedPreKey> device_pni_pq_last_resort_pre_keys = 8;
// The new phone-number-identity registration ID for each device on the
// account, including the sending device, keyed by device ID.
map<uint32, uint32> pni_registration_ids = 9;
}
message ChangeNumberResponse {
oneof response {
// The identifiers of the account after the successful
// number change.
common.AccountIdentifiers account_identifiers = 1;
// Mismatched number of devices or device ids in 'devices to notify' list
messages.MismatchedDevices mismatched_devices = 2 [(tag.reason) = "mismatched_devices"];
// The account associated with the new phone number has a registration lock,
// and the provided registration lock secret was missing or incorrect.
RegistrationLockFailure registration_lock_failure = 3 [(tag.reason) = "registration_lock_failure"];
// Mismatched registration ids in 'devices to notify' list
StaleDevices stale_devices = 4 [(tag.reason) = "stale_devices"];
// One or more device messages was too large
errors.FailedPrecondition message_too_large = 5 [(tag.reason) = "message_too_large"];
// The registration session is unverified
errors.FailedPrecondition unverified_registration_session = 6 [(tag.reason) = "unverified_registration_session"];
// The number does not match the registration session, or the registration session is invalid
errors.FailedPrecondition invalid_registration_session = 7 [(tag.reason) = "invalid_registration_session"];
errors.FailedPrecondition recovery_password_verification_failed = 8 [(tag.reason) = "recovery_password_verification_failed"];
}
}
// Information about the current Registration lock and SVR credentials. With a correct PIN, the credentials can
// be used to recover the secret used to derive the registration lock password.
message RegistrationLockFailure {
// Time remaining in milliseconds before the existing registration lock expires
uint64 time_remaining_millis = 1;
// Credentials that can be used with SVR2
ExternalServiceCredentials svr2_credentials = 2;
}
// A username/password pair for authenticating with an external service.
message ExternalServiceCredentials {
// A username that can be presented to authenticate with the external service.
string username = 1;
// A password that can be presented to authenticate with the external service.
string password = 2;
}
// A list of devices that are linked to the account but presented a stale
// registration ID (indicating the device has likely been replaced by another
// device).
message StaleDevices {
// The IDs of devices that are no longer active.
repeated uint32 stale_devices = 1 [(require.each) = {range: {max: 0x7f}}];
}
message GetAccountDataReportRequest {
}
message GetAccountDataReportResponse {
// The JSON representation of the data report
string json = 3;
// A plaintext representation of the data report
string text = 4;
}
message GetCapabilitiesRequest {
}
message Capabilities {
// A list of capabilities enabled on the account.
repeated common.DeviceCapability capabilities = 1;
}
message GetCapabilitiesResponse {
// A list of capabilities enabled on the account.
Capabilities capabilities = 1;
}
message GetCapabilitiesAnonymousRequest {
// The ACI of the account for which to get capabilities.
common.ServiceIdentifier account_identifier = 1 [(require.present) = true, (require.identityType) = IDENTITY_TYPE_ACI];
oneof authentication {
// The unidentified access key for the targeted account.
bytes unidentified_access_key = 2 [(require.exactlySize) = 16];
// A group send endorsement token for the targeted account.
bytes group_send_token = 3 [(require.nonEmpty) = true];
}
}
message GetCapabilitiesAnonymousResponse {
oneof response {
// A list of capabilities enabled on the account.
Capabilities capabilities = 1;
errors.NotFound not_found = 2 [(tag.reason) = "not_found"];
errors.FailedUnidentifiedAuthorization failed_unidentified_authorization = 3 [(tag.reason) = "failed_unidentified_authorization"];
}
}
message TotpParameters {
// The HMAC algorithm (e.g. "HmacSHA256") used by the TOTP generator
string algorithm = 1;
// The length of one-time passwords (in decimal digits) produced and expected
// by the TOTP generator
uint32 password_length = 2;
// The time step (in seconds) used by the TOTP generator
uint32 time_step_seconds = 3;
}
message GenerateTotpKeyRequest {
}
message GenerateTotpKeyResponse {
message KeyGenerated {
// The raw TOTP key
bytes key = 1;
// The TOTP parameters associated with the generated key
TotpParameters totp_parameters = 2;
}
oneof response {
// A new, pending TOTP key has been generated and added to the authenticated
// account
KeyGenerated key_generated = 1;
// The authenticated account already has too many TOTP keys, and the caller
// must remove one before adding more
errors.FailedPrecondition too_many_totp_keys = 2 [(tag.reason) = "too_many_totp_keys"];
}
}
message ConfirmTotpKeyRequest {
// A one-time password derived from the current pending TOTP key
uint32 one_time_password = 1;
// The ciphertext of user-provided metadata (presumably including a
// human-readable name and creation timestamp) to be attached to the
// newly-confirmed key
bytes metadata_ciphertext = 2 [(require.exactlySize) = 160];
}
message ConfirmTotpKeyResponse {
message KeyConfirmed {
// The account-specific identifier for the newly-confirmed TOTP key
uint32 key_id = 1;
}
oneof response {
// The provided one-time password was accepted and the pending TOTP key was
// stored with the provided name ciphertext
KeyConfirmed key_confirmed = 1;
// The provided one-time password was not valid for any reason (including
// incorrect passwords, misaligned clocks, or missing account records)
errors.FailedPrecondition one_time_password_not_verified = 2 [(tag.reason) = "one_time_password_not_verified"];
}
}
message ListTotpKeysRequest {}
message ListTotpKeysResponse {
message TotpKeyMetadata {
// The user-provided ciphertext for metadata associated with this TOTP key
bytes metadata_ciphertext = 1;
// The TOTP parameters associated with this key
TotpParameters totp_parameters = 2;
}
map<int32, TotpKeyMetadata> keys = 1;
}
message SetTotpKeyMetadataRequest {
// The account-specific identifier of the TOTP key to modify
uint32 key_id = 1;
// The ciphertext of the new user-provided metadata to be attached to the
// identified key
bytes metadata_ciphertext = 2 [(require.exactlySize) = 160];
}
message SetTotpKeyMetadataResponse {
message MetadataUpdated {
}
oneof response {
// New metadata was stored for the identified TOTP key
MetadataUpdated metadata_updated = 1;
// No TOTP was found with the given ID
errors.NotFound key_not_found = 2 [(tag.reason) = "key_not_found"];
}
}
message RemoveTotpKeyRequest {
// The account-specific identifier of the TOTP key to remove
uint32 key_id = 1;
}
message RemoveTotpKeyResponse {
}

View file

@ -1,58 +0,0 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.attachments;
import "org/signal/chat/common.proto";
import "org/signal/chat/require.proto";
import "org/signal/chat/errors.proto";
import "org/signal/chat/tag.proto";
service Attachments {
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
// Retrieve an upload form that can be used to perform a resumable upload
rpc GetUploadForm(GetUploadFormRequest) returns (GetUploadFormResponse) {}
// Retrieve an upload form that can be used to upload a sticker pack
rpc GetStickerUploadForm(GetStickerUploadFormRequest) returns (GetStickerUploadFormResponse) {}
}
message GetUploadFormRequest {
// The length of the attachment for the requested upload form. Uploads
// performed with this form will be limited to the provided length.
uint64 uploadLength = 1 [(require.range) = {min: 1}];
}
message GetUploadFormResponse {
oneof outcome {
common.UploadForm upload_form = 1;
// The request size was larger than the maximum supported upload size. The
// maximum upload size is subject to change and is governed by
// `global.attachments.maxBytes`
errors.FailedPrecondition exceeds_max_upload_length = 2 [(tag.reason) = "oversize_upload"];
}
}
message GetStickerUploadFormRequest {
// The number of stickers in the sticker pack to upload
uint32 sticker_count = 1 [(require.range) = {min: 1, max: 201}];
}
message GetStickerUploadFormResponse {
// A randomly-generated ID for the new sticker pack
string pack_id = 1;
// An upload form clients must use to upload a manifest for the sticker pack
common.S3UploadForm manifest_upload_form = 2;
// Upload forms for individual stickers within the sticker pack
repeated common.S3UploadForm sticker_upload_forms = 3;
}

View file

@ -1,554 +0,0 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.backup;
import "google/protobuf/empty.proto";
import "org/signal/chat/common.proto";
import "org/signal/chat/errors.proto";
import "org/signal/chat/require.proto";
import "org/signal/chat/tag.proto";
// Service for backup operations that require account authentication.
//
// Most actual backup operations operate on the backup-id and cannot be linked
// to the caller's account, but setting up anonymous credentials and changing
// backup tier requires account authentication.
service Backups {
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
// Set (blinded) backup-id(s) for the account.
//
// Each account may have a single active backup-id for each credential type
// that can be used to store and retrieve backups. Once the backup-id is set,
// BackupAuthCredentials can be generated using GetBackupAuthCredentials.
//
// The blinded backup-id and the key-pair used to blind it must be derived
// from a recoverable secret.
//
// At least one of the credential types must be set on the request.
// Only the primary device can set a blinded backup-id.
rpc SetBackupId(SetBackupIdRequest) returns (SetBackupIdResponse) {}
// Redeem a receipt acquired from /v1/subscription/{subscriberId}/receipt_credentials
// to mark the account as eligible for the paid backup tier.
//
// After successful redemption, subsequent requests to
// GetBackupAuthCredentials will return credentials with the level on the
// provided receipt until the expiration time on the receipt.
rpc RedeemReceipt(RedeemReceiptRequest) returns (RedeemReceiptResponse) {}
// After setting a blinded backup-id with PUT /v1/archives/, this fetches
// credentials that can be used to perform operations against that backup-id.
// Clients may (and should) request up to 7 days of credentials at a time.
//
// The redemption_start and redemption_end seconds must be UTC day aligned, and
// must not span more than 7 days.
//
// Each credential contains a receipt level which indicates the backup level
// the credential is good for. If the account has paid backup access that
// expires at some point in the provided redemption window, credentials with
// redemption times after the expiration may be on a lower backup level.
//
// Clients must validate the receipt level on the credential matches a known
// receipt level before using it.
rpc GetBackupAuthCredentials(GetBackupAuthCredentialsRequest) returns (GetBackupAuthCredentialsResponse) {}
}
message SetBackupIdRequest {
// A BackupAuthCredentialRequest containing a blinded encrypted backup-id,
// encoded in standard padded base64. This backup-id should be used for
// message backups only, and must have the message backup type set on the
// credential. If absent, the message credential request will not be updated.
bytes messages_backup_auth_credential_request = 1;
// A BackupAuthCredentialRequest containing a blinded encrypted backup-id,
// encoded in standard padded base64. This backup-id should be used for
// media only, and must have the media type set on the credential. If absent,
// the media credential request will not be updated.
bytes media_backup_auth_credential_request = 2;
}
message SetBackupIdResponse {}
message RedeemReceiptRequest {
// Presentation for a previously acquired receipt, serialized with libsignal
bytes presentation = 1;
}
message RedeemReceiptResponse {
oneof response {
// The receipt was successfully redeemed
google.protobuf.Empty success = 1;
// The target account does not have a backup-id commitment
errors.FailedPrecondition account_missing_commitment = 2 [(tag.reason) = "account_missing_commitment"];
// The provided receipt presentation was malformed or expired
errors.FailedPrecondition invalid_receipt = 3 [(tag.reason) = "invalid_receipt"];
}
}
message GetBackupAuthCredentialsRequest {
// The redemption time for the first credential. This must be a day-aligned
// seconds since epoch in UTC.
int64 redemption_start = 1 [(require.range).min = 1];
// The redemption time for the last credential. This must be a day-aligned
// seconds since epoch in UTC. The span between redemptionStart and
// redemptionEnd must not exceed 7 days.
int64 redemption_stop = 2 [(require.range).min = 1];
}
message GetBackupAuthCredentialsResponse {
message Credentials {
// The requested message backup ZkCredentials indexed by the start of their
// validity period. The smallest key should be for the requested
// redemption_start, the largest for the requested redemption_end.
map<int64, common.ZkCredential> message_credentials = 1;
// The requested media backup ZkCredentials indexed by the start of their
// validity period. The smallest key should be for the requested
// redemption_start, the largest for the requested redemption_end.
map<int64, common.ZkCredential> media_credentials = 2;
}
// The requested credentials. If absent, there was no existing blinded
// backup id associated with the provided account.
Credentials credentials = 1;
}
// Service for backup operations with anonymous credentials
//
// This service never requires account authentication. It instead requires a
// backup-id authenticated with an anonymous credential that cannot be linked
// to the account.
//
// To register an anonymous credential:
//
// 1. Set a backup-id on the authenticated channel via Backups::SetBackupId
// 2. Retrieve BackupAuthCredentials via Backups::GetBackupAuthCredentials
// 3. Generate a key pair and set the public key via
// BackupsAnonymous::SetPublicKey
//
// Unless otherwise noted, requests for this service require a
// SignedPresentation, which includes:
//
// - a presentation generated from a BackupAuthCredential issued by
// GetBackupAuthCredentials
// - a signature of that presentation using the private key of a key pair
// previously set with SetPublicKey.
service BackupsAnonymous {
option (require.auth) = AUTH_ONLY_ANONYMOUS;
// Retrieve credentials used to read objects stored on the backup cdn
rpc GetCdnCredentials(GetCdnCredentialsRequest) returns (GetCdnCredentialsResponse) {}
// Retrieve credentials used to interact with the SecureValueRecoveryB service
rpc GetSvrBCredentials(GetSvrBCredentialsRequest) returns (GetSvrBCredentialsResponse) {}
// Retrieve information about the currently stored message backup
rpc GetMessageBackupInfo(GetBackupInfoRequest) returns (GetMessageBackupInfoResponse) {}
// Retrieve information about the currently stored media backup
rpc GetMediaBackupInfo(GetBackupInfoRequest) returns (GetMediaBackupInfoResponse) {}
// Permanently set the public key of an ED25519 key-pair for the backup-id.
// All requests (including this one!) must sign their BackupAuthCredential
// presentations with the private key corresponding to the provided public key.
rpc SetPublicKey(SetPublicKeyRequest) returns (SetPublicKeyResponse) {}
// Refresh the backup, indicating that the backup is still active. Clients
// must periodically upload new backups or perform a refresh. If a backup has
// not been active for 30 days, it may be deleted.
rpc Refresh(RefreshRequest) returns (RefreshResponse) {}
// Retrieve an upload form that can be used to perform a resumable upload
rpc GetUploadForm(GetUploadFormRequest) returns (GetUploadFormResponse) {}
// Copy and re-encrypt media from the attachments cdn into the backup cdn.
// The original, already encrypted, attachments will be encrypted with the
// provided key material before being copied.
//
// The copy operation is not atomic and responses will be returned as copy
// operations complete with detailed information about the outcome. If an
// error is encountered, not all requests may be reflected in the responses.
//
// On retries, a particular destination media id must not be reused with a
// different source media id or different encryption parameters.
//
// The response stream may be closed with STREAM_CLOSED error reason. In this
// case, a BackupStreamClosed message will be present in the error details.
rpc CopyMedia(CopyMediaRequest) returns (stream CopyMediaResponse) {}
// Retrieve a page of media objects stored for this backup-id. A client may
// have previously stored media objects that are no longer referenced in their
// current backup. To reclaim storage space used by these orphaned objects,
// perform a list operation and remove any unreferenced media objects
// via DeleteMedia.
rpc ListMedia(ListMediaRequest) returns (ListMediaResponse) {}
// Delete media objects stored with this backup-id. Streams the locations of
// media items back when the item has successfully been removed.
//
// The response stream may be closed with STREAM_CLOSED error reason. In this
// case, a BackupStreamClosed message will be present in the error details.
rpc DeleteMedia(DeleteMediaRequest) returns (stream DeleteMediaResponse) {}
// Delete all backup metadata, objects, and stored public key. To use
// backups again, a public key must be resupplied.
rpc DeleteAll(DeleteAllRequest) returns (DeleteAllResponse) {}
}
message SignedPresentation {
// Presentation of a BackupAuthCredential previously retrieved from
// GetBackupAuthCredentials on the authenticated channel
bytes presentation = 1 [(require.nonEmpty) = true];
// The presentation signed with the private key corresponding to the public
// key set with SetPublicKey
bytes presentation_signature = 2 [(require.nonEmpty) = true];
}
message SetPublicKeyRequest {
SignedPresentation signed_presentation = 1;
// The public key, serialized in libsignal's elliptic-curve public key format.
bytes public_key = 2 [(require.nonEmpty) = true];
}
message SetPublicKeyResponse {
oneof response {
// The public key was successfully set
google.protobuf.Empty success = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
//
// This may also be returned if there was an existing public key and the
// provided public key did not match.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message GetCdnCredentialsRequest {
SignedPresentation signed_presentation = 1;
uint32 cdn = 2;
}
message GetCdnCredentialsResponse {
message CdnCredentials {
map<string, string> headers = 1;
}
oneof response {
// Headers to include with requests to the read from the backup CDN. Includes
// time limited read-only credentials.
CdnCredentials cdn_credentials = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message GetSvrBCredentialsRequest {
SignedPresentation signed_presentation = 1;
}
message GetSvrBCredentialsResponse {
message SvrBCredentials {
// A username that can be presented to authenticate with SVRB
string username = 1;
// A password that can be presented to authenticate with SVRB
string password = 2;
}
oneof response {
SvrBCredentials svrb_credentials = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message GetBackupInfoRequest {
SignedPresentation signed_presentation = 1;
}
message GetMessageBackupInfoResponse {
message MessageBackupInfo {
// The base directory of your backup data on the cdn. Always non-empty, even
// if a backup has not actually been stored to the cdn. If a backup was
// previously uploaded and has not expired, it can be found in the returned
// cdn at /backup_dir/backup_name.
string backup_dir = 1;
// The CDN type where the message backup is stored. Media may be stored
// elsewhere.
uint32 cdn = 2;
// The location of the message backup on the cdn. Always non-empty, even
// if a backup has not actually been stored to the cdn. If a backup was
// previously uploaded and has not expired, it can be found in the returned
// cdn at /backup_dir/backup_name.
string backup_name = 3;
}
oneof response {
MessageBackupInfo backup_info = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message GetMediaBackupInfoResponse {
message MediaBackupInfo {
// The base directory of your backup data on the cdn. Always non-empty, even
// if no media has been stored to the cdn or the credential is for a tier
// that does not support media.
string backup_dir = 1;
// The prefix path component for media objects on a cdn. Stored media for a
// media_id can be found at /backup_dir/media_dir/media_id, where the
// media_id is encoded in unpadded url-safe base64. Always non-empty, even
// if no media has been stored to the cdn or the credential is for a tier
// that does not support media.
string media_dir = 2;
// The amount of space used to store media
uint64 used_space = 3;
}
oneof response {
MediaBackupInfo backup_info = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message RefreshRequest {
SignedPresentation signed_presentation = 1;
}
message RefreshResponse {
oneof response {
// The backup was successfully refreshed
google.protobuf.Empty success = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message GetUploadFormRequest {
SignedPresentation signed_presentation = 1;
message MessagesUploadType {}
message MediaUploadType {}
oneof upload_type {
// Retrieve an upload form that can be used to perform a resumable upload of
// a message backup. The finished upload will be available on the backup cdn.
MessagesUploadType messages = 2;
// Retrieve an upload form for a temporary location that can be used to
// perform a resumable upload of an attachment. After uploading, the
// attachment can be copied into the backup via CopyMedia.
//
// Behaves identically to the account authenticated version at /attachments.
MediaUploadType media = 3;
}
// The length of the attachment for the requested upload form. Uploads
// performed with this form will be limited to the provided length.
uint64 uploadLength = 4 [(require.range) = {min: 1}];
}
message GetUploadFormResponse {
oneof response {
common.UploadForm upload_form = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
// The request size was larger than the maximum supported upload size. The
// maximum upload size is subject to change and is governed by
// `global.attachments.maxBytes`
errors.FailedPrecondition exceeds_max_upload_length = 3 [(tag.reason) = "oversize_upload"];
}
}
message CopyMediaItem {
// The attachment cdn of the object to copy into the backup
uint32 source_attachment_cdn = 1 [(require.range).min = 1, (require.range).max = 3];
// The attachment key of the object to copy into the backup
string source_key = 2 [(require.nonEmpty) = true, (require.base64url) = true];
// The length of the source attachment before the encryption applied by the
// copy operation
uint64 object_length = 3;
// media_id to copy on to the backup CDN
bytes media_id = 4 [(require.exactlySize) = 15];
// A 32-byte key for the MAC
bytes hmac_key = 5 [(require.exactlySize) = 32];
// A 32-byte encryption key for AES
bytes encryption_key = 6 [(require.exactlySize) = 32];
}
message CopyMediaRequest {
SignedPresentation signed_presentation = 1;
// Items to copy
repeated CopyMediaItem items = 2 [(require.size) = {min: 1, max: 1000}];
}
message CopyMediaResponse {
message SourceNotFound {}
message WrongSourceLength {}
message OutOfSpace {}
message CopySuccess {
// The backup cdn where this media object is stored
uint32 cdn = 1;
}
// The 15-byte media_id from the corresponding CopyMediaItem in the request
bytes media_id = 1;
oneof response {
// The media item was successfully copied into the backup
CopySuccess success = 2;
// The source object was not found
SourceNotFound source_not_found = 3 [(tag.reason) = "source_not_found"];
// The provided object length was incorrect
WrongSourceLength wrong_source_length = 4 [(tag.reason) = "wrong_source_length"];
// All media capacity has been consumed. Free some space to continue.
OutOfSpace out_of_space = 5 [(tag.reason) = "out_of_space"];
}
}
// The reason why a media stream RPC is being prematurely closed by the server.
message BackupStreamClosed {
oneof reason {
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 1 [(tag.reason) = "failed_authentication"];
}
}
message ListMediaRequest {
SignedPresentation signed_presentation = 1;
// A cursor returned by a previous call to ListMedia, absent on the first call
optional string cursor = 2;
// If provided, the maximum number of entries to return in a page. If absent,
// a server-chosen default is used.
optional uint32 limit = 3 [(require.range) = {min: 1, max: 10000}];
}
message ListMediaResponse {
message ListEntry {
// The backup cdn where this media object is stored
uint32 cdn = 1;
// The media_id of the object
bytes media_id = 2;
// The length of the object in bytes
uint64 length = 3;
}
message ListResult {
// A page of media objects stored for this backup ID
repeated ListEntry page = 1;
// The base directory of the backup data on the cdn. The stored media can be
// found at /backup_dir/media_dir/media_id, where the media_id is encoded with
// unpadded url-safe base64.
string backup_dir = 2;
// The prefix path component for the media objects. The stored media for
// media_id can be found at /backup_dir/media_dir/media_id, where the media_id
// is encoded with unpadded url-safe base64.
string media_dir = 3;
// If set, the cursor value to pass to the next list request to continue
// listing. If absent, all objects have been listed
optional string cursor = 4;
}
oneof response {
ListResult list_result = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message DeleteAllRequest {
SignedPresentation signed_presentation = 1;
}
message DeleteAllResponse {
oneof response {
// The backup was successfully scheduled for deletion
google.protobuf.Empty success = 1;
// The provided backup auth credential presentation could not be
// authenticated. Either, the presentation could not be verified, or
// the public key signature was invalid, or there is no backup associated
// with the backup-id in the presentation.
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
}
}
message DeleteMediaItem {
// The backup cdn where this media object is stored
uint32 cdn = 1;
// The media_id of the object to delete
bytes media_id = 2 [(require.exactlySize) = 15];
}
message DeleteMediaRequest {
SignedPresentation signed_presentation = 1;
repeated DeleteMediaItem items = 2 [(require.size) = {min: 1, max: 1000}];
}
message DeleteMediaResponse {
DeleteMediaItem deleted_item = 1;
}

View file

@ -1,118 +0,0 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.calling.quality;
import "org/signal/chat/require.proto";
// Provides methods for submitting call quality surveys
service CallQuality {
option (require.auth) = AUTH_ONLY_ANONYMOUS;
// Submits a call quality survey response.
rpc SubmitCallQualitySurvey(SubmitCallQualitySurveyRequest) returns (SubmitCallQualitySurveyResponse) {}
}
message SubmitCallQualitySurveyRequest {
// Indicates whether the caller was generally satisfied with the quality of
// the call
bool user_satisfied = 1;
// A list of call quality issues selected by the caller
repeated string call_quality_issues = 2;
// A free-form description of any additional issues as written by the caller
optional string additional_issues_description = 3;
// A URL for a set of debug logs associated with the call if the caller chose
// to submit debug logs
optional string debug_log_url = 4;
// The time at which the call started in milliseconds since the epoch
int64 start_timestamp = 5;
// The time at which the call ended in milliseconds since the epoch
int64 end_timestamp = 6;
// The type of call; note that direct voice calls can become video calls and
// vice versa, and this field indicates which mode was selected at call
// initiation time. At the time of writing, expected call types are
// "direct_voice", "direct_video", "group", and "call_link".
string call_type = 7;
// Indicates whether the call completed without error or if it terminated
// abnormally
bool success = 8;
// A client-defined, but human-readable reason for call termination
string call_end_reason = 9;
// The median round-trip time, measured in milliseconds, for STUN/ICE packets
// (i.e. connection maintenance and establishment)
optional float connection_rtt_median = 10;
// The median round-trip time, measured in milliseconds, for RTP/RTCP packets
// for audio streams
optional float audio_rtt_median = 11;
// The median round-trip time, measured in milliseconds, for RTP/RTCP packets
// for video streams
optional float video_rtt_median = 12;
// The median jitter for audio streams, measured in milliseconds, for the
// duration of the call as measured by the client submitting the survey
optional float audio_recv_jitter_median = 13;
// The median jitter for video streams, measured in milliseconds, for the
// duration of the call as measured by the client submitting the survey
optional float video_recv_jitter_median = 14;
// The median jitter for audio streams, measured in milliseconds, for the
// duration of the call as measured by the remote endpoint in the call (either
// the peer of the client submitting the survey in a direct call or the SFU in
// a group call)
optional float audio_send_jitter_median = 15;
// The median jitter for video streams, measured in milliseconds, for the
// duration of the call as measured by the remote endpoint in the call (either
// the peer of the client submitting the survey in a direct call or the SFU in
// a group call)
optional float video_send_jitter_median = 16;
// The fraction of audio packets lost over the duration of the call as
// measured by the client submitting the survey
optional float audio_recv_packet_loss_fraction = 17;
// The fraction of video packets lost over the duration of the call as
// measured by the client submitting the survey
optional float video_recv_packet_loss_fraction = 18;
// The fraction of audio packets lost over the duration of the call as
// measured by the remote endpoint in the call (either the peer of the client
// submitting the survey in a direct call or the SFU in a group call)
optional float audio_send_packet_loss_fraction = 19;
// The fraction of video packets lost over the duration of the call as
// measured by the remote endpoint in the call (either the peer of the client
// submitting the survey in a direct call or the SFU in a group call)
optional float video_send_packet_loss_fraction = 20;
// Machine-generated telemetry from the call; this is a serialized protobuf
// entity generated (and, critically, explained to the user!) by the calling
// library
optional bytes call_telemetry = 21;
// A hash of a call ID (shared between clients and never sent to the calling
// server) that can be used to correlate survey responses from multiple
// participants in a call
optional bytes call_id_hash = 22;
}
message SubmitCallQualitySurveyResponse {
}

View file

@ -1,64 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.calling;
import "org/signal/chat/require.proto";
// Provides methods for getting credentials and relay options for one-on-one
// calls.
service Calling {
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
// Retrieves TURN credentials and relay options for one-on-one calls.
rpc GetCallingRelays(GetCallingRelaysRequest) returns (GetCallingRelaysResponse) {}
}
message GetCallingRelaysRequest {}
message GetCallingRelaysResponse {
message HostnameUrlList {
// A collection of hostname-based TURN, TURNS, or STUN URLs a client can use
// to connect to a relay.
repeated string urls = 1;
}
message IpUrlList {
// A collection of IP-based TURN, TURNS, or STUN URLs a client can use to
// connect to a relay.
repeated string urls = 1;
// A hostname clients must use to validate the relay's TLS certificate when
// connecting via an IP-based TURNS URL. May not be specified if `urls`
// contains no TURNS URLs.
optional string hostname = 2;
}
message Relay {
// A username that can be presented to authenticate with a TURN server.
string username = 1;
// A password that can be presented to authenticate with a TURN server.
string password = 2;
// The duration, in seconds, after which the included username and password
// will no longer be valid.
uint64 credential_ttl_seconds = 3;
// A collection of hostname-based URLs clients may use to connect to this
// relay.
optional HostnameUrlList hostname_urls = 4;
// A collection of IP-based URLs clients may use to connect to this relay.
optional IpUrlList ip_urls = 5;
}
// A collection of calling relays a client may use for one-on-one calls.
repeated Relay relays = 1;
}

View file

@ -1,52 +0,0 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.challenge;
import "org/signal/chat/require.proto";
service Challenge {
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
// Submit proof of a challenge completion
// Some server endpoints (the "send message" endpoint, for example) may return
// a response indicating the client must complete a challenge before continuing.
// Clients may use this endpoint to provide proof of a completed challenge.
// If successful, the client may then continue their original operation.
rpc HandleChallengeResponse(AnswerChallengeRequest) returns (AnswerChallengeResponse) {}
}
message AnswerChallengeRequest {
message AnswerPushChallengeRequest {
// The challenge string provided to the client via a push payload
string challenge = 1 [(require.nonEmpty) = true];
}
message AnswerCaptchaChallengeRequest {
// A string representing a solved captcha
// Example: signal-hcaptcha.30b01b46-d8c9-4c30-bbd7-9719acfe0c10.challenge.abcdefg1345
string captcha = 1 [(require.nonEmpty) = true];
}
// The opaque token id from the ChallengeRequired response returned by the
// server endpoint that requested the challenge
string token = 1 [(require.nonEmpty) = true];
oneof request {
AnswerPushChallengeRequest push = 2;
AnswerCaptchaChallengeRequest captcha = 3;
}
}
message AnswerChallengeResponse {
// Whether the challenge proof was accepted
bool success = 1;
}

View file

@ -1,176 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.common;
import "org/signal/chat/require.proto";
enum IdentityType {
IDENTITY_TYPE_UNSPECIFIED = 0;
IDENTITY_TYPE_ACI = 1;
IDENTITY_TYPE_PNI = 2;
}
message ServiceIdentifier {
// The type of identity represented by this service identifier.
IdentityType identity_type = 1 [(require.specified) = true];
// The UUID of the identity represented by this service identifier.
bytes uuid = 2 [(require.exactlySize) = 16];
}
// All identifiers associated with an account.
message AccountIdentifiers {
// A list of service identifiers for the identified account. Always includes
// exactly one ACI service identifier and at most one PNI service identifier.
repeated ServiceIdentifier service_identifiers = 1;
// The phone number associated with the identified account. May be empty if
// the given account does not have a phone number.
string e164 = 2 [(org.signal.chat.require.e164) = true];
// The username hash (if any) associated with the identified account. May be
// empty if no username is associated with the identified account.
bytes username_hash = 3 [(org.signal.chat.require.exactlySize) = 0, (org.signal.chat.require.exactlySize) = 32];
// The username link handle UUID associated with the identified account.
// May be empty if no username is associated with the identified account.
bytes username_link_handle = 4 [(org.signal.chat.require.exactlySize) = 0, (org.signal.chat.require.exactlySize) = 16];
}
message EcPreKey {
// A locally-unique identifier for this key, which will be provided by
// peers using this key to encrypt messages so the private key can be looked
// up.
int32 key_id = 1 [(require.range).min = 0];
// The public key, serialized in libsignal's elliptic-curve public key format.
bytes public_key = 2 [(require.nonEmpty) = true];
}
message EcSignedPreKey {
// A locally-unique identifier for this key, which will be provided by
// peers using this key to encrypt messages so the private key can be looked
// up.
int32 key_id = 1 [(require.range).min = 0];
// The public key, serialized in libsignal's elliptic-curve public key format.
bytes public_key = 2 [(require.nonEmpty) = true];
// A signature of the public key, verifiable with the identity key for the
// account/identity associated with this pre-key.
bytes signature = 3 [(require.nonEmpty) = true];
}
message KemSignedPreKey {
// An locally-unique identifier for this key, which will be provided by peers
// using this key to encrypt messages so the private key can be looked up.
int32 key_id = 1 [(require.range).min = 0];
// The public key, serialized in libsignal's Kyber1024 public key format.
bytes public_key = 2 [(require.nonEmpty) = true];
// A signature of the public key, verifiable with the identity key for the
// account/identity associated with this pre-key.
bytes signature = 3 [(require.nonEmpty) = true];
}
enum DeviceCapability {
DEVICE_CAPABILITY_UNSPECIFIED = 0;
DEVICE_CAPABILITY_STORAGE = 1;
DEVICE_CAPABILITY_TRANSFER = 2;
reserved 3;
reserved 4;
reserved 5;
DEVICE_CAPABILITY_ATTACHMENT_BACKFILL = 6;
DEVICE_CAPABILITY_SPARSE_POST_QUANTUM_RATCHET = 7;
DEVICE_CAPABILITY_PROFILES_V2 = 8;
DEVICE_CAPABILITY_USERNAME_CHANGE_SYNC_MESSAGE = 9;
DEVICE_CAPABILITY_OPTIONAL_PHONE_NUMBER = 10;
}
message ZkCredential {
/*
* Day on which this credential can be redeemed, in UTC seconds since epoch
*/
int64 redemption_time = 1;
/*
* The ZK credential, using libsignal's serialization
*/
bytes credential = 2 [(require.nonEmpty) = true];
}
// An upload location and credentials which may be used to upload an object
// to an external CDN
message UploadForm {
// Indicates the CDN type. 3 indicates resumable uploads using TUS
uint32 cdn = 1;
// The location within the specified cdn where the finished upload can be found
string key = 2;
// A map of headers to include with all upload requests. Potentially contains
// time-limited upload credentials
map<string, string> headers = 3;
// The URL to upload to with the appropriate protocol
string signed_upload_location = 4;
}
// An upload location, credentials, and metadata which may be used to upload an
// object to AWS S3
message S3UploadForm {
// The S3 key (i.e. path and filename) for the uploaded file.
string key = 1;
// A scoped credential. Includes the AWS access key, date, region targeted,
// and AWS service.
string credential = 2;
// The type of access control for the uploaded file.
string acl = 3;
// The algorithm used to calculate a signature on the S3 policy.
string algorithm = 4;
// The timestamp (formatted as "yyyyMMdd'T'HHmmssX") at which the S3 policy
// and signature were generated.
string date = 5;
// The S3 policy (as a base64-encoded JSON string) used to upload the file.
string policy = 6;
// A digital signature (formatted as a hex string) on the S3 policy.
string signature = 7;
}
message BadgeSvg {
// File name of the scalable vector graphic for light mode.
string light = 1;
// File name of the scalable vector graphic for dark mode.
string dark = 2;
}
message Badge {
// An ID that uniquely identifies the badge.
string id = 1;
// The category the badge falls in ("donor" or "other").
string category = 2;
// The badge name.
string name = 3;
// The badge description.
string description = 4;
// Different size badge SVG files.
repeated string sprites6 = 5;
// File name of the scalable vector graphic representing this badge.
string svg = 6;
// Pairs of light/dark SVG files designed for display at different sizes.
repeated BadgeSvg svgs = 7;
}

View file

@ -1,166 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
import "org/signal/chat/require.proto";
package org.signal.chat.credentials;
// Provides methods for obtaining and verifying credentials that allow an
// authenticated user to authenticate in another service or context without
// revealing their identity.
service Credentials {
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
// Generates and returns an external service credentials for the caller.
rpc GetExternalServiceCredentials(GetExternalServiceCredentialsRequest)
returns (GetExternalServiceCredentialsResponse) {}
// Generates a pair of delivery certificates that the holder can include to
// identify themselves to a message recipient (but not the server) in a
// sealed-sender message.
rpc GetDeliveryCertificate(GetDeliveryCertificateRequest)
returns (GetDeliveryCertificateResponse) {}
// Generates a set of zero-knowledge credentials for various group-related
// actions.
rpc GetGroupCredentials(GetGroupCredentialsRequest)
returns (GetGroupCredentialsResponse) {}
// Generates zero-knowledge credentials for creating call links.
rpc GetCreateCallLinkCredentials(GetCreateCallLinkCredentialsRequest)
returns (GetCreateCallLinkCredentialsResponse) {}
}
// Provides methods for working with previously-generated credentials without
// revealing an association between the credentials and the caller's identity to
// the server.
service CredentialsAnonymous {
option (require.auth) = AUTH_ONLY_ANONYMOUS;
// Given a list of secure value recovery (SVR) service credentials and a phone
// number, checks and returns which of the provided credentials were generated
// by the user with the given phone number and have not yet expired.
rpc CheckSvrCredentials(CheckSvrCredentialsRequest)
returns (CheckSvrCredentialsResponse) {}
}
enum ExternalServiceType {
EXTERNAL_SERVICE_TYPE_UNSPECIFIED = 0;
EXTERNAL_SERVICE_TYPE_DIRECTORY = 1;
EXTERNAL_SERVICE_TYPE_PAYMENTS = 2;
EXTERNAL_SERVICE_TYPE_STORAGE = 3;
EXTERNAL_SERVICE_TYPE_SVR = 4;
}
message GetExternalServiceCredentialsRequest {
// A service to request credentials for.
ExternalServiceType externalService = 1;
}
message GetExternalServiceCredentialsResponse {
// A username that can be presented to authenticate with the external service.
string username = 1;
// A password that can be presented to authenticate with the external service.
string password = 2;
}
enum AuthCheckResult {
AUTH_CHECK_RESULT_UNSPECIFIED = 0;
// The credentials could be used to make a call to SVR service by the user
// associated with the `CheckSvrCredentialsRequest.number` phone number.
AUTH_CHECK_RESULT_MATCH = 1;
// The credentials were generated by a different user.
AUTH_CHECK_RESULT_NO_MATCH = 2;
// This status indicates that the corresponding credentials token should no longer be used.
// This may be because it has expired or invalid, but it can also mean that there is a more
// recent token in the request which should be used instead.
AUTH_CHECK_RESULT_INVALID = 3;
}
message CheckSvrCredentialsRequest {
// A phone number in the E164 format to check the passwords against.
// Only passwords generated for the user associated with the given number will be marked as `AUTH_CHECK_RESULT_MATCH`.
string number = 1;
// A list of credentials from previously made calls to `ExternalServiceCredentials.GetExternalServiceCredentials()`
// for `EXTERNAL_SERVICE_TYPE_SVR`. This list may contain credentials generated by different users. Up to 10 credentials
// can be checked.
repeated string passwords = 2 [(require.nonEmpty) = true, (require.size) = {max: 10}];
}
// For each of the credentials tokens in the `CheckSvrCredentialsRequest` contains the result of the check.
message CheckSvrCredentialsResponse {
map<string, AuthCheckResult> matches = 1;
}
message GetDeliveryCertificateRequest {
}
// A pair of message delivery certificates. The response unconditionally
// includes certificates with and without the caller's phone number so the
// server never learns anything about the caller's intent to share their phone
// number with their contacts.
message GetDeliveryCertificateResponse {
// A delivery receipt that includes the caller's phone number; may be empty if
// the authenticated account does not have a phone number
bytes certificate_with_e164 = 1;
// A delivery receipt that does not include the caller's phone number
bytes certificate_without_e164 = 2;
}
message GetGroupCredentialsRequest {
// The earliest time for which to issue group credentials; must be aligned to
// a UTC day boundary and may be no more than one day in the past at the time
// of the call.
uint64 redemption_start_seconds = 1;
// The latest time for which to issue group credentials; must be aligned to a
// UTC day boundary and no more than seven days in the future at the time of
// the call.
uint64 redemption_end_seconds = 2;
}
message GetGroupCredentialsResponse {
// A zero-knowledge credential that may be redeemed at or up to one day after
// the given redemption time
message CredentialAndRedemptionTime {
bytes credential = 1;
uint64 redemption_time_seconds = 2;
}
// A collection of credentials allowing the holder to anonymously
// authenticate themselves for group-related actions
repeated CredentialAndRedemptionTime group_credentials = 1;
// A collection of credentials allowing the holder to read, update, and delete
// group call links
repeated CredentialAndRedemptionTime call_link_auth_credentials = 2;
// The phone number identifier for which the included credentials were
// generated. Empty if the account does not have a phone number.
bytes pni = 3;
}
message GetCreateCallLinkCredentialsRequest {
// A zero-knowledge credential request
bytes credential_request = 1;
}
message GetCreateCallLinkCredentialsResponse {
// A zero-knowledge credential that may be redeemed at or up to one day after
// the given redemption time
bytes credential = 1;
// The earliest time, in seconds since the epoch, at which the associated
// credential may be redeemed
uint64 redemption_time_seconds = 2;
}

View file

@ -1,138 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.device;
import "google/protobuf/empty.proto";
import "org/signal/chat/common.proto";
import "org/signal/chat/errors.proto";
import "org/signal/chat/require.proto";
import "org/signal/chat/tag.proto";
// Provides methods for working with devices attached to a Signal account.
service Devices {
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
// Returns a list of devices associated with the caller's account.
rpc GetDevices(GetDevicesRequest) returns (GetDevicesResponse) {}
// Removes a linked device from the caller's account.
//
// Linked devices may only remove themselves. Primary devices may remove
// any device other than themselves.
rpc RemoveDevice(RemoveDeviceRequest) returns (RemoveDeviceResponse) {}
// Sets the encrypted human-readable name for a specific devices. Primary
// devices may change the name of any device associated with their account,
// but linked devices may only change their own name. The response will
// indicate if the target device was not found.
rpc SetDeviceName(SetDeviceNameRequest) returns (SetDeviceNameResponse) {}
// Sets the token(s) the server should use to send new message notifications
// to the authenticated device.
rpc SetPushToken(SetPushTokenRequest) returns (SetPushTokenResponse) {}
// Removes any push tokens associated with the authenticated device. After
// calling this method, the server will assume that the authenticated device
// will periodically poll for new messages.
rpc ClearPushToken(ClearPushTokenRequest) returns (ClearPushTokenResponse) {}
// Declares that the authenticated device supports certain features.
rpc SetCapabilities(SetCapabilitiesRequest) returns (SetCapabilitiesResponse) {}
}
message GetDevicesRequest {}
message GetDevicesResponse {
message LinkedDevice {
// The identifier for the device within an account.
uint32 id = 1;
// A sequence of bytes that encodes an encrypted human-readable name for
// this device.
bytes name = 2;
// The approximate time, in milliseconds since the epoch, at which this
// device last connected to the server.
uint64 last_seen = 3;
// The registration ID of the given device.
uint32 registration_id = 4 [(require.range).max = 0x3fff];
// A sequence of bytes that encodes the time,
// in milliseconds since the epoch, at which this device was
// attached to its parent account.
bytes created_at_ciphertext = 5;
}
// A list of devices linked to the authenticated account.
repeated LinkedDevice devices = 1;
}
message RemoveDeviceRequest {
// The identifier for the device to remove from the authenticated account. The
// identifier must not be for the primary device.
uint32 id = 1;
}
message SetDeviceNameRequest {
// A sequence of bytes that encodes an encrypted human-readable name for this
// device.
bytes name = 1 [(require.size) = {min: 1, max: 225}];
// The identifier for the device for which to set a name.
uint32 id = 2;
}
message SetDeviceNameResponse {
oneof response {
// The device name was successfully set
google.protobuf.Empty success = 1;
// No device with the provided identifier was found on the account
errors.NotFound target_device_not_found = 2 [(tag.reason) = "not_found"];
}
}
message RemoveDeviceResponse {}
message SetPushTokenRequest {
message ApnsTokenRequest {
// A "standard" APNs device token.
string apns_token = 1 [(require.nonEmpty) = true];
}
message FcmTokenRequest {
// An FCM push token.
string fcm_token = 1 [(require.nonEmpty) = true];
}
oneof token_request {
// If present, specifies the APNs device token(s) the server will use to
// send new message notifications to the authenticated device.
ApnsTokenRequest apns_token_request = 1;
// If present, specifies the FCM push token the server will use to send new
// message notifications to the authenticated device.
FcmTokenRequest fcm_token_request = 2;
}
}
message SetPushTokenResponse {}
message ClearPushTokenRequest {}
message ClearPushTokenResponse {}
message SetCapabilitiesRequest {
repeated common.DeviceCapability capabilities = 1;
}
message SetCapabilitiesResponse {}

View file

@ -1,59 +0,0 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.donations;
import "google/protobuf/empty.proto";
import "org/signal/chat/require.proto";
import "org/signal/chat/errors.proto";
import "org/signal/chat/tag.proto";
service Donations {
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
// Redeem a receipt acquired from Subscriptions.CreateSubscriptionReceiptCredentials
// to add a badge to the account. After successful redemption, profile
// responses will include the corresponding badge (if configured as visible)
// until the expiration time on the receipt.
rpc RedeemReceipt(RedeemReceiptRequest) returns (RedeemReceiptResponse) {}
// Generate a set of anonymous, single-use, permits for use with /v1/subscription endpoints.
//
// If rate limited, reduce requested permit count and/or try again after the prescribed delay.
rpc CreateDonationPermit(CreateDonationPermitRequest) returns (CreateDonationPermitResponse) {}
}
message RedeemReceiptRequest {
// Presentation of the ZK receipt acquired when the subscription was created
bytes receiptCredentialPresentation = 1 [(require.exactlySize) = 329];
// If true, the corresponding badge should be visible on the profile
bool visible = 2;
// If true, and the new badge is visible, it should be the primary badge on the profile
bool primary = 3;
}
message RedeemReceiptResponse {
oneof response {
// The receipt was successfully redeemed
google.protobuf.Empty success = 1;
// The provided presentation is invalid
errors.FailedZkAuthentication failed_authentication = 2 [(tag.reason) = "failed_authentication"];
// The receipt was already redeemed for a different account
errors.FailedPrecondition already_redeemed = 3 [(tag.reason) = "already_redeemed"];
}
}
message CreateDonationPermitRequest {
// a serialized libsignal DonationPermitRequest
bytes donation_permit_request = 1 [(require.nonEmpty) = true];
}
message CreateDonationPermitResponse {
// a serialized libsignal DonationPermitResponse
bytes donation_permit_response = 1;
}

View file

@ -1,34 +0,0 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.errors;
// Response message that indicates a particular resource was not found.
message NotFound {}
// Response message that indicates that some precondition of the request was not
// met. For example, if there was a request to update foo, but foo had not been
// set, this would be an appropriate error.
message FailedPrecondition {
// An optional description indicating what precondition failed.
string description = 1;
}
// Response message that authentication via an anonymous credential failed.
message FailedZkAuthentication {
// An optional description with additional information about the failure.
string description = 1;
}
// Response message that indicates authorization to perform an unidentified
// operation via an endorsement or access key failed
message FailedUnidentifiedAuthorization {
// An optional description with additional information about the failure.
string description = 1;
}

View file

@ -1,236 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.keys;
import "google/protobuf/empty.proto";
import "org/signal/chat/common.proto";
import "org/signal/chat/errors.proto";
import "org/signal/chat/require.proto";
import "org/signal/chat/tag.proto";
// Provides methods for working with pre-keys.
service Keys {
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
// Retrieves an approximate count of the number of the various kinds of
// pre-keys stored for the authenticated device.
rpc GetPreKeyCount (GetPreKeyCountRequest) returns (GetPreKeyCountResponse) {}
// Retrieves a set of pre-keys for establishing a session with the targeted
// device or devices. Note that callers with an unidentified access key for
// the targeted account should use the version of this method in
// `KeysAnonymous` instead.
rpc GetPreKeys(GetPreKeysRequest) returns (GetPreKeysResponse) {}
// Uploads a new set of one-time EC pre-keys for the authenticated device,
// clearing any previously-stored pre-keys. Note that all keys submitted via
// a single call to this method _must_ have the same identity type (i.e. if
// the first key has an ACI identity type, then all other keys in the same
// stream must also have an ACI identity type). The provided list of pre-keys
// must be non-empty.
rpc SetOneTimeEcPreKeys (SetOneTimeEcPreKeysRequest) returns (SetPreKeyResponse) {}
// Uploads a new set of one-time KEM pre-keys for the authenticated device,
// clearing any previously-stored pre-keys. Note that all keys submitted via
// a single call to this method _must_ have the same identity type (i.e. if
// the first key has an ACI identity type, then all other keys in the same
// stream must also have an ACI identity type). The provided list of pre-keys
// must be non-empty.
rpc SetOneTimeKemSignedPreKeys (SetOneTimeKemSignedPreKeysRequest) returns (SetPreKeyResponse) {}
// Sets the signed EC pre-key for one identity (i.e. ACI or PNI) associated
// with the authenticated device.
rpc SetEcSignedPreKey (SetEcSignedPreKeyRequest) returns (SetPreKeyResponse) {}
// Sets the last-resort KEM pre-key for one identity (i.e. ACI or PNI)
// associated with the authenticated device.
rpc SetKemLastResortPreKey (SetKemLastResortPreKeyRequest) returns (SetPreKeyResponse) {}
}
// Provides methods for working with pre-keys using "unidentified access"
// credentials.
service KeysAnonymous {
option (require.auth) = AUTH_ONLY_ANONYMOUS;
// Retrieves a set of pre-keys for establishing a session with the targeted
// device or devices. Callers must not submit any self-identifying credentials
// when calling this method and must instead present the targeted account's
// unidentified access key as an anonymous authentication mechanism. Callers
// without an unidentified access key should use the equivalent, authenticated
// method in `Keys` instead.
rpc GetPreKeys(GetPreKeysAnonymousRequest) returns (GetPreKeysAnonymousResponse) {}
// Checks identity key fingerprints of the target accounts.
//
// Returns a stream of elements, each one representing an account that had a mismatched
// identity key fingerprint with the server and the corresponding identity key stored by the server.
rpc CheckIdentityKeys(stream CheckIdentityKeyRequest) returns (stream CheckIdentityKeyResponse) {}
}
message GetPreKeyCountRequest {
}
message GetPreKeyCountResponse {
// The approximate number of one-time EC pre-keys stored for the
// authenticated device and associated with the caller's ACI.
uint32 aci_ec_pre_key_count = 1;
// The approximate number of one-time Kyber pre-keys stored for the
// authenticated device and associated with the caller's ACI.
uint32 aci_kem_pre_key_count = 2;
// The approximate number of one-time EC pre-keys stored for the
// authenticated device and associated with the caller's PNI. 0 if
// the account does not possess a phone number.
uint32 pni_ec_pre_key_count = 3;
// The approximate number of one-time KEM pre-keys stored for the
// authenticated device and associated with the caller's PNI. 0 if
// the account does not possess a phone number.
uint32 pni_kem_pre_key_count = 4;
}
message GetPreKeysRequest {
// The service identifier of the account for which to retrieve pre-keys.
common.ServiceIdentifier target_identifier = 1;
// The ID of the device associated with the targeted account for which to
// retrieve pre-keys. If not set, pre-keys are returned for all devices
// associated with the targeted account.
optional uint32 device_id = 2;
}
message GetPreKeysAnonymousRequest {
// The request to retrieve pre-keys for a specific account/device(s).
GetPreKeysRequest request = 1;
// A means to authorize the request.
oneof authorization {
// The unidentified access key (UAK) for the targeted account.
bytes unidentified_access_key = 2;
// A group send endorsement token for the targeted account.
bytes group_send_token = 3;
// The destination account allows unrestricted unidentified access
google.protobuf.Empty unrestricted_access = 4;
}
}
message DevicePreKeyBundle {
// The EC signed pre-key associated with the targeted
// account/device/identity.
common.EcSignedPreKey ec_signed_pre_key = 1;
// A one-time EC pre-key for the targeted account/device/identity. May not
// be set if no one-time EC pre-keys are available.
common.EcPreKey ec_one_time_pre_key = 2;
// A one-time KEM pre-key (or a last-resort KEM pre-key) for the targeted
// account/device/identity.
common.KemSignedPreKey kem_one_time_pre_key = 3;
// The registration ID for the targeted account/device/identity.
uint32 registration_id = 4;
}
message AccountPreKeyBundles {
// The identity key associated with the targeted account/identity.
bytes identity_key = 1;
// A map of device IDs to pre-key "bundles" for the targeted account.
map<uint32, DevicePreKeyBundle> device_pre_keys = 2;
// Whether the account has enabled sealed sender from anyone. Always false
// if the request was for a PNI, which does not allow any unidentified access.
bool unrestricted_unidentified_access = 3;
// If the target supports unidentified access and has an unidentified access
// key, a fingerprint of the target's UAK. This may be used to detect a change
// in the UAK that the sender has for the target before actually sending a message. Otherwise, empty.
bytes unidentified_access_key_fingerprint = 4;
}
message GetPreKeysResponse {
oneof response {
// The requested pre-key bundles
AccountPreKeyBundles pre_keys = 1;
// Either the target account was not found, no active device with the given
// ID (if specified) was found on the target account.
errors.NotFound target_not_found = 2 [(tag.reason) = "not_found"];
}
}
message GetPreKeysAnonymousResponse {
oneof response {
// The requested pre-key bundles
AccountPreKeyBundles pre_keys = 1;
// Either the target account was not found, no active device with the given
// ID (if specified) was found on the target account.
errors.NotFound target_not_found = 2 [(tag.reason) = "not_found"];
// The provided unidentified authorization credential was invalid
errors.FailedUnidentifiedAuthorization failed_unidentified_authorization = 3 [(tag.reason) = "failed_unidentified_authorization"];
}
}
message SetOneTimeEcPreKeysRequest {
// The identity type (i.e. ACI/PNI) with which the keys in this request are
// associated.
common.IdentityType identity_type = 1;
// The unsigned EC pre-keys to be stored.
repeated common.EcPreKey pre_keys = 2 [(require.size) = {min: 1, max: 100}];
}
message SetOneTimeKemSignedPreKeysRequest {
// The identity type (i.e. ACI/PNI) with which the keys in this request are
// associated.
common.IdentityType identity_type = 1;
// The KEM pre-keys to be stored.
repeated common.KemSignedPreKey pre_keys = 2 [(require.size) = {min: 1, max: 100}];
}
message SetEcSignedPreKeyRequest {
// The identity type (i.e. ACI/PNI) with which this key is associated.
common.IdentityType identity_type = 1;
// The signed EC pre-key itself.
common.EcSignedPreKey signed_pre_key = 2 [(require.present) = true];
}
message SetKemLastResortPreKeyRequest {
// The identity type (i.e. ACI/PNI) with which this key is associated.
common.IdentityType identity_type = 1;
// The signed KEM pre-key itself.
common.KemSignedPreKey signed_pre_key = 2 [(require.present) = true];
}
message SetPreKeyResponse {
}
message CheckIdentityKeyRequest {
// The service identifier of the account for which we want to check the associated identity key fingerprint.
common.ServiceIdentifier target_identifier = 1;
// The most significant 4 bytes of the SHA-256 hash of the identity key associated with the target account/identity type.
bytes fingerprint = 2 [(require.exactlySize) = 4];
}
message CheckIdentityKeyResponse {
// The service identifier of the account for which there is a mismatch between the client and server identity key fingerprints.
common.ServiceIdentifier target_identifier = 1;
// The identity key that is stored by the server for the target account/identity type.
bytes identity_key = 2;
}

View file

@ -1,65 +0,0 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.purchase;
import "org/signal/chat/require.proto";
import "org/signal/chat/errors.proto";
import "org/signal/chat/tag.proto";
import "org/signal/chat/subscriptions.proto";
// Service for one-time purchases of logins
service LoginPurchase {
option (require.auth) = AUTH_ONLY_ANONYMOUS;
// Obtain a ZK receipt credential for a completed one-time login payment.
//
// The receipt credential can then be presented at registration
rpc CreateLoginReceiptCredential(CreateLoginReceiptCredentialRequest) returns (CreateLoginReceiptCredentialResponse) {}
}
message CreateLoginReceiptCredentialRequest {
// The payment processor. Currently, only GOOGLE_PLAY_BILLING and
// APPLE_APP_STORE are supported.
PaymentProvider processor = 1 [(require.specified) = true];
// The identifier of a completed purchase for a signal login within the
// payment provider
string purchase_identifier = 2 [(require.nonEmpty) = true];
// The receipt credential request. Subsequent retries to create a login
// credential for the same purchase_identifier must use an identical
// receipt_credential_request.
//
// Callers must validate that the generated receipt credential has the
// following properties:
//
// - Level == 300 (The login level)
// - ExpirationTime % 86400 == 0
// - ExpirationTime == PurchaseTime + (5 * 366 * 86400) +/- (7 * 86400)
bytes receipt_credential_request = 3 [(require.nonEmpty) = true];
}
message CreateLoginReceiptCredentialResponse {
message CreateLoginReceiptCredentialResult {
bytes receipt_credential_response = 1;
}
oneof response {
CreateLoginReceiptCredentialResult result = 1;
// The purchase is still pending with the payment provider. The client may retry later.
errors.FailedPrecondition payment_still_processing = 2 [(tag.reason) = "payment_still_processing"];
// The purchase did not complete successfully.
PaymentRequired payment_required = 3 [(tag.reason) = "payment_required"];
// The payment provider has no purchase with the provided purchase_identifier
errors.NotFound payment_not_found = 4 [(tag.reason) = "payment_not_found"];
// The purchase was already redeemed for a receipt credential, but with a different receipt credential request
errors.FailedPrecondition receipt_already_issued = 5 [(tag.reason) = "receipt_already_issued"];
}
}

View file

@ -1,437 +0,0 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.messages;
import "google/protobuf/empty.proto";
import "org/signal/chat/common.proto";
import "org/signal/chat/require.proto";
import "org/signal/chat/errors.proto";
import "org/signal/chat/tag.proto";
import "signalpb/SignalService.proto";
// Provides methods for sending "unsealed sender" messages.
service Messages {
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
// Sends an "unsealed sender" message to all devices linked to a single
// destination account.
//
// The destination account must not be the same as the authenticated caller.
// Callers should use `SendSyncMessage` to send messages to themselves.
rpc SendMessage(SendAuthenticatedSenderMessageRequest) returns (SendMessageAuthenticatedSenderResponse) {}
// Sends a "sync" message to all other devices linked to the authenticated
// sender's account.
rpc SendSyncMessage(SendSyncMessageRequest) returns (SendMessageAuthenticatedSenderResponse) {}
// Retrieve messages for the authenticated device. When the caller receives
// and successfully processes a message returned in a GetMessagesResponse they
// must send a corresponding GetMessagesRequest indicating that the message
// has been processed (an ack). Acks should only be sent for messages
// delivered via the currently open RPC.
//
// Only the first GetMessagesRequest may contain request parameters that
// configure the stream. The first request must not contain an ack.
//
// The server will keep the stream open until the client disconnects. Only
// one GetMessages stream may be open per device. If a second GetMessages
// stream is opened, the server may terminate any of the streams with
// a STREAM_CLOSED error reason. A GetMessagesStreamClosed message will be
// present in the error details.
rpc GetMessages(stream GetMessagesRequest) returns (stream GetMessagesResponse) {}
}
message GetMessagesRequest {
message GetMessageOptions {
// If present and true, the server will not deliver any messages with the
// story flag set. This flag may only be set on the first GetMessagesRequest
// sent from the client to the server in an RPC stream.
bool drop_stories = 1;
}
oneof request {
// Configuration options for the message stream. Required for the first
// request of the stream, forbidden on subsequent reqeusts.
GetMessageOptions options = 1;
// The server_guid of an envelope previously returned in a
// GetMessagesResponse that has been successfully processed by the caller.
// Forbidden on the first request of the stream, required on subsequent
// requests.
bytes server_guid_ack = 2;
}
}
// The reason why a GetMessages RPC is being closed by the server.
message GetMessagesStreamClosed {
oneof reason {
// Another caller has opened a GetMessages stream for the same device.
google.protobuf.Empty conflicting_stream = 1;
}
}
message GetMessagesResponse {
oneof response {
// A message. On successful receipt of an envelope the caller should ack
// the envelope by sending a GetMessagesRequest with the envelope's server
// guid. Acks should only be sent for envelopes received on the currently
// open RPC.
signalservice.Envelope envelope = 1;
// An indicator that all outstanding messages for the device have been
// drained and acked by the client. The stream will remain open and continue
// to deliver newly arrived messages.
google.protobuf.Empty queue_empty = 2;
}
}
// Provides methods for sending "sealed sender" messages.
service MessagesAnonymous {
option (require.auth) = AUTH_ONLY_ANONYMOUS;
// Sends a "sealed sender" message to all devices linked to a single
// destination account.
//
// If this RPC is authorized with an unidentified access key, it will fail
// with an authorization failure if the credential is invalid OR if the
// destination account was not found. If it is authorized using a group send
// token, it will fail with an authorization failure if the credential is
// invalid and with an destination not found error if the account does not
// exist
rpc SendSingleRecipientMessage(SendSealedSenderMessageRequest) returns (SendMessageResponse) {}
// Sends a "sealed sender" message with a common payload to all devices linked
// to multiple destination accounts.
rpc SendMultiRecipientMessage(SendMultiRecipientMessageRequest) returns (SendMultiRecipientMessageResponse) {}
// Sends a story message to devices linked to a single destination account.
rpc SendStory(SendStoryMessageRequest) returns (SendMessageResponse) {}
// Sends a story message with a common payload to devices linked to devices
// linked to multiple destination accounts.
rpc SendMultiRecipientStory(SendMultiRecipientStoryRequest) returns (SendMultiRecipientMessageResponse) {}
}
message IndividualRecipientMessageBundle {
// A message for an individual device linked to a destination account.
message Message {
// The registration ID for the destination device.
uint32 registration_id = 1 [(require.range).max = 0x3fff];
// The content of the message to deliver to the destination device.
bytes payload = 2 [(require.size) = {min: 1, max: 262144}]; // 256 KiB
// The message type of the message. If this message is part of an
// unidentified send, this must be UNIDENTIFIED_SENDER
SendMessageType type = 3;
}
// The time, in milliseconds since the epoch, at which this message was
// originally sent from the perspective of the sender. Note that the maximum
// allowable timestamp for JavaScript clients is less than Long.MAX_VALUE; see
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#the_epoch_timestamps_and_invalid_date
// for additional details and discussion.
uint64 timestamp = 1 [(require.range).min = 1, (require.range).max = 8640000000000000];
// A map of device IDs to individual messages. Generally, callers must include
// one message for each device linked to the destination account. In cases of
// "sync messages" where a sender is distributing information to other devices
// linked to the sender's account, senders may omit a message for the sending
// device.
map<uint32, Message> messages = 2 [(require.nonEmpty) = true];
}
enum SendMessageType {
UNSPECIFIED = 0;
// A double-ratchet message represents a "normal," "unsealed-sender" message
// encrypted using the Double Ratchet within an established Signal session.
DOUBLE_RATCHET = 1;
// A prekey message begins a new Signal session. The `content` of a prekey
// message is a superset of a double-ratchet message's `content` and
// contains the sender's identity public key and information identifying the
// pre-keys used in the message's ciphertext.
PREKEY_MESSAGE = 2;
// A plaintext message is used solely to convey encryption error receipts
// and never contains encrypted message content. Encryption error receipts
// must be delivered in plaintext because encryption/decryption of a prior
// message failed and there is no reason to believe that
// encryption/decryption of subsequent messages with the same key material
// would succeed.
//
// Critically, plaintext messages never have "real" message content
// generated by users. Plaintext messages include sender information.
PLAINTEXT_CONTENT = 3;
// An unidentified sender message is an encrypted message. No other
// information about the type of the encrypted message is known to the server.
//
// Unidenitfied sender messages require an unidentified access token or a
// group send endorsement token to prove the unidentified sender is authorized
// to send messages to the destination.
UNIDENTIFIED_SENDER = 4;
}
message SendAuthenticatedSenderMessageRequest {
// The service identifier of the account to which to deliver the message.
common.ServiceIdentifier destination = 1;
// If true, this message will only be delivered to destination devices that
// have an active message delivery channel with a Signal server.
bool ephemeral = 2;
// Indicates whether this message is urgent and should trigger a high-priority
// notification if the destination device does not have an active message
// delivery channel with a Signal server
bool urgent = 3;
// The messages to send to the destination account.
IndividualRecipientMessageBundle messages = 4;
}
message SendMessageAuthenticatedSenderResponse {
// The outcome of the message delivery
oneof response {
// The message was successfully delivered to all destination devices
google.protobuf.Empty success = 1;
// A list of discrepancies between the destination devices identified in a
// request to send a message and the devices that are actually linked to an
// account.
MismatchedDevices mismatched_devices = 2 [(tag.reason) = "mismatched_devices"];
// A description of a challenge callers must complete before sending
// additional messages.
ChallengeRequired challenge_required = 3 [(tag.reason) = "challenge_required"];
// The destination account did not exist
errors.NotFound destination_not_found = 4 [(tag.reason) = "destination_not_found"];
}
}
message SendSyncMessageRequest {
// Indicates whether this message is urgent and should trigger a high-priority
// notification if the destination device does not have an active message
// delivery channel with a Signal server
bool urgent = 1;
// The messages to send to the destination account.
IndividualRecipientMessageBundle messages = 2;
}
message SendSealedSenderMessageRequest {
// The service identifier of the account to which to deliver the message.
common.ServiceIdentifier destination = 1;
// If true, this message will only be delivered to destination devices that
// have an active message delivery channel with a Signal server.
bool ephemeral = 2;
// Indicates whether this message is urgent and should trigger a high-priority
// notification if the destination device does not have an active message
// delivery channel with a Signal server
bool urgent = 3;
// The messages to send to the destination account.
IndividualRecipientMessageBundle messages = 4;
// A means to authorize the request.
oneof authorization {
// The unidentified access key (UAK) for the destination account.
bytes unidentified_access_key = 5 [(require.exactlySize) = 16];
// A group send endorsement token for the destination account.
bytes group_send_token = 6;
// The destination account allows unrestricted unidentified access
google.protobuf.Empty unrestricted_access = 7;
}
}
message SendStoryMessageRequest {
// The service identifier of the account to which to deliver the message.
common.ServiceIdentifier destination = 1;
// Indicates whether this message is urgent and should trigger a high-priority
// notification if the destination device does not have an active message
// delivery channel with a Signal server
bool urgent = 2;
// The messages to send to the destination account.
IndividualRecipientMessageBundle messages = 3;
}
message SendMessageResponse {
// The outcome of the message delivery
oneof response {
// The message was successfully delivered to all destination devices
google.protobuf.Empty success = 1;
// A list of discrepancies between the destination devices identified in a
// request to send a message and the devices that are actually linked to an
// account.
MismatchedDevices mismatched_devices = 2 [(tag.reason) = "mismatched_devices"];
// The provided unidentified authorization credential was invalid
errors.FailedUnidentifiedAuthorization failed_unidentified_authorization = 3 [(tag.reason) = "failed_unidentified_authorization"];
// The destination account did not exist
errors.NotFound destination_not_found = 4 [(tag.reason) = "destination_not_found"];
}
}
message MultiRecipientMessage {
// The time, in milliseconds since the epoch, at which this message was
// originally sent from the perspective of the sender. Note that the maximum
// allowable timestamp for JavaScript clients is less than Long.MAX_VALUE; see
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#the_epoch_timestamps_and_invalid_date
// for additional details and discussion.
uint64 timestamp = 1 [(require.range).min = 1, (require.range).max = 8640000000000000];
// The serialized multi-recipient message payload.
bytes payload = 2 [(require.size).max = 762144]; // 256 KiB payload + (5000 * 100) of overhead
}
message SendMultiRecipientMessageRequest {
// If true, this message will only be delivered to destination devices that
// have an active message delivery channel with a Signal server.
bool ephemeral = 1;
// Indicates whether this message is urgent and should trigger a high-priority
// notification if the destination device does not have an active message
// delivery channel with a Signal server
bool urgent = 2;
// The multi-recipient message to send to all destination accounts and
// devices.
MultiRecipientMessage message = 3;
// A group send endorsement token for the destination account.
bytes group_send_token = 4 [(require.nonEmpty) = true];
}
message SendMultiRecipientStoryRequest {
// Indicates whether this message is urgent and should trigger a high-priority
// notification if the destination device does not have an active message
// delivery channel with a Signal server
bool urgent = 1;
// The multi-recipient story message to send to all destination accounts and
// devices.
MultiRecipientMessage message = 2;
}
message MultiRecipientSuccess {
// A list of destination service identifiers that could not be resolved to
// registered Signal accounts. The message in the original request was sent
// to all service identifiers/devices in the original request except for the
// destination devices associated with the service identifiers in this list.
repeated common.ServiceIdentifier unresolved_recipients = 1;
}
message SendMultiRecipientMessageResponse {
// The outcome of the message delivery
oneof response {
// The message was sent to at least some of the destination accounts/devices
// identified in the original request.
MultiRecipientSuccess success = 1;
// A list of sets of discrepancies between the destination devices
// identified in a request to send a message and the devices that are
// actually linked to a destination account.
MultiRecipientMismatchedDevices mismatched_devices = 2 [(tag.reason) = "mismatched_devices"];
// The provided unidentified authorization credential was invalid
errors.FailedUnidentifiedAuthorization failed_unidentified_authorization = 3 [(tag.reason) = "failed_unidentified_authorization"];
}
}
message MismatchedDevices {
// The service identifier to which the devices named in this object are
// linked.
common.ServiceIdentifier service_identifier = 1;
// A list of device IDs that are linked to the destination account, but were
// not included in the collection of messages bound for the destination
// account.
repeated uint32 missing_devices = 2 [(require.each) = {range: {max: 0x7f}}];
// A list of device IDs that were included in the collection of messages bound
// for the destination account, but are not currently linked to the
// destination account.
repeated uint32 extra_devices = 3 [(require.each) = {range: {max: 0x7f}}];
// A list of device IDs that present in the collection of messages bound for
// the destination account and are linked to the destination account, but have
// a different registration ID than the registration ID presented by the
// sender (indicating that the destination device has likely been replaced by
// another device).
repeated uint32 stale_devices = 4 [(require.each) = {range: {max: 0x7f}}];
}
message MultiRecipientMismatchedDevices {
// A list of sets of discrepancies between the destination devices identified
// in a request to send a message and the devices that are actually linked to
// a destination account.
repeated MismatchedDevices mismatched_devices = 1;
}
message ChallengeRequired {
enum ChallengeType {
UNSPECIFIED = 0;
// A challenge that callers can fulfill by completing a captcha.
CAPTCHA = 1;
// A challenge that callers can fulfill by supplying a token delivered via
// push notification.
PUSH_CHALLENGE = 2;
};
// An opaque token identifying this challenge request. Clients must generally
// submit this token when submitting a challenge response.
string token = 1;
// A list of challenge types callers may choose to complete to resolve the
// challenge requirement. May be empty, in which case callers cannot resolve
// the challenge by any means other than waiting.
repeated ChallengeType challenge_options = 2;
// A duration (in seconds) after which the challenge requirement may be
// resolved by simply waiting. May not be set if the challenge cannot be
// resolved by waiting.
optional uint64 retry_after_seconds = 3;
}

View file

@ -1,169 +0,0 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.purchase;
import "org/signal/chat/require.proto";
import "org/signal/chat/errors.proto";
import "org/signal/chat/tag.proto";
import "org/signal/chat/subscriptions.proto";
// Service for making one-time donation payments (boost and gift)
//
// Configuration for one-time donations can be found in ProductConfiguration.
service OneTimeDonations {
option (require.auth) = AUTH_ONLY_ANONYMOUS;
// Create a Stripe payment intent and return a client secret that can be used to complete the payment.
// Once the payment is complete, the paymentIntentId can be used with CreateBoostReceiptCredentials
rpc CreateBoost(CreateBoostRequest) returns (CreateBoostResponse) {}
// Create a PayPal one-time payment.
// Once the payment is complete, call ConfirmPayPalBoost with the payment ID and token
rpc CreatePayPalBoost(CreatePayPalBoostRequest) returns (CreatePayPalBoostResponse) {}
// Confirm a PayPal one-time payment
rpc ConfirmPayPalBoost(ConfirmPayPalBoostRequest) returns (ConfirmPayPalBoostResponse) {}
// Obtain a ZK receipt credential for a completed one-time donation payment.
// The receipt credential can then be used to redeem the one-time donation entitlement
// via Donations.RedeemReceipt
rpc CreateBoostReceiptCredentials(CreateBoostReceiptCredentialsRequest) returns (CreateBoostReceiptCredentialsResponse) {}
}
// The amount is below the minimum for the currency.
message AmountBelowMinimumError {
// The minimum amount for the currency
string minimum = 1;
}
// The SEPA Direct Debit amount exceeds the allowed maximum.
message AmountAboveSepaLimitError {
// The maximum amount for a SEPA transaction
string maximum = 1;
}
message CreateBoostRequest {
// ISO 4217 currency code, case-insensitive (e.g. "usd", "EUR")
string currency = 1 [(require.exactlySize) = 3];
// The amount to pay in the [currency's minor unit](https://docs.stripe.com/currencies#minor-units)
uint64 amount = 2 [(require.range).min = 1];
// The level for the boost payment
uint64 level = 3 [(require.range).min = 1];
// The payment method
PaymentMethod payment_method = 4 [(require.specified) = true];
// A donation permit retrieved from Donations.createDonationPermit
bytes donation_permit = 5 [(require.nonEmpty) = true];
}
message CreateBoostResponse {
oneof response {
// A client secret that can be used to complete a stripe PaymentIntent
string client_secret = 1;
// The amount is below the minimum for the currency
AmountBelowMinimumError amount_below_minimum = 2 [(tag.reason) = "amount_below_minimum"];
// The amount exceeds the maximum for SEPA Direct Debit
AmountAboveSepaLimitError amount_above_sepa_limit = 3 [(tag.reason) = "amount_above_sepa_limit"];
// The requested currency is not supported for the given payment method
errors.FailedPrecondition unsupported_currency = 4 [(tag.reason) = "unsupported_currency"];
// The requested level is not a valid one-time donation level
errors.FailedPrecondition unsupported_level = 5 [(tag.reason) = "unsupported_level"];
// Donation permit was invalid or already spent
errors.FailedZkAuthentication permit_rejected = 6 [(tag.reason) = "permit_rejected"];
// The amount in the specified currency is not supported by the payment provider
errors.FailedPrecondition invalid_amount = 7 [(tag.reason) = "invalid_amount"];
}
}
message CreatePayPalBoostRequest {
// ISO 4217 currency code, case-insensitive (e.g. "usd", "EUR")
string currency = 1 [(require.exactlySize) = 3];
// Amount in the currency's minor unit (e.g. cents for USD), must be >= 1
uint64 amount = 2 [(require.range).min = 1];
// Donation level.
uint64 level = 3 [(require.range).min = 1];
// URL to redirect the user to after PayPal approval
string return_url = 4 [(require.nonEmpty) = true];
// URL to redirect the user to if they cancel
string cancel_url = 5 [(require.nonEmpty) = true];
}
message CreatePayPalBoostResponse {
message CreatePayPalBoostResult {
string approval_url = 1;
string payment_id = 2;
}
oneof response {
CreatePayPalBoostResult result = 1;
// The amount is below the minimum for the currency
AmountBelowMinimumError amount_below_minimum = 2 [(tag.reason) = "amount_below_minimum"];
// The requested currency is not supported for PayPal
errors.FailedPrecondition unsupported_currency = 3 [(tag.reason) = "unsupported_currency"];
// The requested level is not a valid one-time donation level
errors.FailedPrecondition unsupported_level = 4 [(tag.reason) = "unsupported_level"];
}
}
message ConfirmPayPalBoostRequest {
// ISO 4217 currency code, case-insensitive (e.g. "usd", "EUR")
string currency = 1 [(require.exactlySize) = 3];
// Amount in the currency's minor unit, must be >= 1
uint64 amount = 2 [(require.range).min = 1];
// Donation level.
uint64 level = 3 [(require.range).min = 1];
// PayPal payer ID from the approval redirect
string payer_id = 4 [(require.nonEmpty) = true];
// PayPal payment ID (PAYID-) from CreatePayPalBoost
string payment_id = 5 [(require.nonEmpty) = true];
// PayPal payment token (EC-) from the approval redirect
string payment_token = 6 [(require.nonEmpty) = true];
}
message ConfirmPayPalBoostResponse {
message ConfirmPayPalBoostResult {
string payment_id = 1;
}
oneof response {
ConfirmPayPalBoostResult result = 1;
// The amount is below the minimum for the currency
AmountBelowMinimumError amount_below_minimum = 2 [(tag.reason) = "amount_below_minimum"];
// The requested currency is not supported for PayPal
errors.FailedPrecondition unsupported_currency = 3 [(tag.reason) = "unsupported_currency"];
// The requested level is not a valid one-time donation level
errors.FailedPrecondition unsupported_level = 4 [(tag.reason) = "unsupported_level"];
// The payment failed; see charge failure details
ChargeFailure charge_failure = 5 [(tag.reason) = "charge_failure"];
}
}
message CreateBoostReceiptCredentialsRequest {
// a payment ID from the processor
string payment_intent_id = 1 [(require.nonEmpty) = true];
// ZK blind-signature receipt credential request bytes
bytes receipt_credential_request = 2 [(require.nonEmpty) = true];
// The processor that handled the payment
PaymentProvider processor = 3 [(require.specified) = true];
}
message CreateBoostReceiptCredentialsResponse {
message CreateBoostReceiptCredentialsResult {
bytes receipt_credential_response = 1;
}
oneof response {
CreateBoostReceiptCredentialsResult result = 1;
// Payment is still processing; client should retry
errors.FailedPrecondition payment_still_processing = 2 [(tag.reason) = "payment_still_processing"];
// Payment failed
PaymentRequired payment_required = 3 [(tag.reason) = "payment_required"];
// Payment intent not found
errors.NotFound payment_not_found = 4 [(tag.reason) = "payment_not_found"];
// A receipt credential was already issued for this payment
errors.FailedPrecondition receipt_already_issued = 5 [(tag.reason) = "receipt_already_issued"];
}
}

View file

@ -1,37 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
option java_multiple_files = true;
package org.signal.chat.payments;
import "org/signal/chat/require.proto";
// Provides methods for working with payments.
service Payments {
option (require.auth) = AUTH_ONLY_AUTHENTICATED;
rpc GetCurrencyConversions(GetCurrencyConversionsRequest) returns (GetCurrencyConversionsResponse) {}
}
message GetCurrencyConversionsRequest {
}
message GetCurrencyConversionsResponse {
message CurrencyConversionEntity {
string base = 1;
map<string, string> conversions = 2;
}
uint64 timestamp = 1;
repeated CurrencyConversionEntity currencies = 2;
}

Some files were not shown because too many files have changed in this diff Show more