Skip to content

Commit 05c4399

Browse files
committed
crypto/canonicaljson: disable old code entirely when jsonv2 is enabled
1 parent aa12073 commit 05c4399

16 files changed

Lines changed: 120 additions & 47 deletions

File tree

bridgev2/database/ghost.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ func (ep *ExtraProfile) Set(key string, value any) error {
3333
if key == "displayname" || key == "avatar_url" {
3434
return fmt.Errorf("cannot set reserved profile key %q", key)
3535
}
36-
marshaled, err := json.Marshal(value)
36+
marshaled, err := canonicaljson.Marshal(value)
3737
if err != nil {
3838
return err
3939
}
4040
if *ep == nil {
4141
*ep = make(ExtraProfile)
4242
}
43-
(*ep)[key] = canonicaljson.CanonicalJSONAssumeValid(marshaled)
43+
(*ep)[key] = marshaled
4444
return nil
4545
}
4646

@@ -49,11 +49,9 @@ func (ep *ExtraProfile) With(key string, value any) *ExtraProfile {
4949
return ep
5050
}
5151

52-
func canonicalizeIfObject(data json.RawMessage) json.RawMessage {
53-
if len(data) > 0 && (data[0] == '{' || data[0] == '[') {
54-
return canonicaljson.CanonicalJSONAssumeValid(data)
55-
}
56-
return data
52+
func canonicalize(data json.RawMessage) json.RawMessage {
53+
canonicalized, _ := canonicaljson.Marshal(data)
54+
return canonicalized
5755
}
5856

5957
func (ep *ExtraProfile) CopyTo(dest *ExtraProfile) (changed bool) {
@@ -68,7 +66,7 @@ func (ep *ExtraProfile) CopyTo(dest *ExtraProfile) (changed bool) {
6866
continue
6967
}
7068
existing, exists := (*dest)[key]
71-
if !exists || !bytes.Equal(canonicalizeIfObject(existing), val) {
69+
if !exists || !bytes.Equal(canonicalize(existing), val) {
7270
(*dest)[key] = val
7371
changed = true
7472
}

bridgev2/matrix/intent.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -475,20 +475,16 @@ func dataToFields(data any) (map[string]json.RawMessage, error) {
475475
if ok {
476476
return fields, nil
477477
}
478-
d, err := json.Marshal(data)
478+
d, err := canonicaljson.Marshal(data)
479479
if err != nil {
480480
return nil, err
481481
}
482-
d = canonicaljson.CanonicalJSONAssumeValid(d)
483482
err = json.Unmarshal(d, &fields)
484483
return fields, err
485484
}
486485

487486
func marshalField(val any) json.RawMessage {
488-
data, _ := json.Marshal(val)
489-
if len(data) > 0 && (data[0] == '{' || data[0] == '[') {
490-
return canonicaljson.CanonicalJSONAssumeValid(data)
491-
}
487+
data, _ := canonicaljson.Marshal(val)
492488
return data
493489
}
494490

crypto/account.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
package crypto
88

99
import (
10-
"encoding/json"
10+
"fmt"
1111

1212
"github.com/tidwall/sjson"
1313

@@ -72,13 +72,18 @@ func (account *OlmAccount) IdentityKey() id.IdentityKey {
7272
// SignJSON signs the given JSON object following the Matrix specification:
7373
// https://matrix.org/docs/spec/appendices#signing-json
7474
func (account *OlmAccount) SignJSON(obj any) (string, error) {
75-
objJSON, err := json.Marshal(obj)
75+
objJSON, err := canonicaljson.Marshal(obj)
7676
if err != nil {
7777
return "", err
7878
}
7979
objJSON, _ = sjson.DeleteBytes(objJSON, "unsigned")
8080
objJSON, _ = sjson.DeleteBytes(objJSON, "signatures")
81-
signed, err := account.Internal.Sign(canonicaljson.CanonicalJSONAssumeValid(objJSON))
81+
// This is probably not necessary
82+
err = canonicaljson.Canonicalize(&objJSON)
83+
if err != nil {
84+
return "", fmt.Errorf("failed to canonicalize JSON after deleting unsigned and signatures: %w", err)
85+
}
86+
signed, err := account.Internal.Sign(objJSON)
8287
return string(signed), err
8388
}
8489

crypto/canonicaljson/json.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !goexperiment.jsonv2
2+
13
/* Copyright 2016-2017 Vector Creations Ltd
24
*
35
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,16 +19,32 @@ package canonicaljson
1719

1820
import (
1921
"encoding/binary"
22+
"encoding/json"
2023
"fmt"
2124
"sort"
2225
"unicode/utf8"
2326

2427
"github.com/tidwall/gjson"
2528
)
2629

30+
func Canonicalize(input *json.RawMessage) error {
31+
*input = CanonicalJSONAssumeValid(*input)
32+
return nil
33+
}
34+
35+
func Marshal(v any) (json.RawMessage, error) {
36+
marshaled, err := json.Marshal(v)
37+
if err != nil {
38+
return nil, err
39+
}
40+
return CanonicalJSONAssumeValid(marshaled), nil
41+
}
42+
2743
// CanonicalJSON re-encodes the JSON in a canonical encoding. The encoding is
2844
// the shortest possible encoding using integer values with sorted object keys.
2945
// https://matrix.org/docs/spec/appendices#canonical-json
46+
//
47+
// Deprecated: Use the new Canonicalize or Marshal functions which will transparently migrate to jsonv2
3048
func CanonicalJSON(input []byte) ([]byte, error) {
3149
if !gjson.Valid(string(input)) {
3250
return nil, fmt.Errorf("invalid json")
@@ -37,13 +55,17 @@ func CanonicalJSON(input []byte) ([]byte, error) {
3755

3856
// CanonicalJSONAssumeValid is the same as CanonicalJSON, but assumes the
3957
// input is valid JSON
58+
//
59+
// Deprecated: Use the new Canonicalize or Marshal functions which will transparently migrate to jsonv2
4060
func CanonicalJSONAssumeValid(input []byte) []byte {
4161
input = CompactJSON(input, make([]byte, 0, len(input)))
4262
return SortJSON(input, make([]byte, 0, len(input)))
4363
}
4464

4565
// SortJSON reencodes the JSON with the object keys sorted by lexicographically
4666
// by codepoint. The input must be valid JSON.
67+
//
68+
// Deprecated: Use the new Canonicalize or Marshal functions which will transparently migrate to jsonv2
4769
func SortJSON(input, output []byte) []byte {
4870
result := gjson.ParseBytes(input)
4971

@@ -141,6 +163,8 @@ func sortJSONObject(input gjson.Result, inputJSON, output []byte) []byte {
141163

142164
// CompactJSON makes the encoded JSON as small as possible by removing
143165
// whitespace and unneeded unicode escapes
166+
//
167+
// Deprecated: Use the new Canonicalize or Marshal functions which will transparently migrate to jsonv2
144168
func CompactJSON(input, output []byte) []byte {
145169
var i int
146170
for i < len(input) {

crypto/canonicaljson/json_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !goexperiment.jsonv2
2+
13
/* Copyright 2016-2017 Vector Creations Ltd
24
*
35
* Licensed under the Apache License, Version 2.0 (the "License");

crypto/canonicaljson/jsonv2.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,16 @@ func reorderObjectsAndValidate(d *jsontext.Decoder, buf []byte, scratch *[]byte)
229229
return cmp.Or(err, fmt.Errorf("unexpected token: %s", tok))
230230
}
231231
}
232+
233+
// Deprecated: Use the new Canonicalize or Marshal functions
234+
func CanonicalJSONAssumeValid(input []byte) []byte {
235+
out, _ := CanonicalJSON(input)
236+
return out
237+
}
238+
239+
// Deprecated: Use the new Canonicalize or Marshal functions
240+
func CanonicalJSON(input []byte) ([]byte, error) {
241+
out := jsontext.Value(bytes.Clone(input))
242+
err := Canonicalize(&out)
243+
return out, err
244+
}

crypto/goolm/pk/signing.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package pk
22

33
import (
44
"crypto/rand"
5-
"encoding/json"
5+
"fmt"
66

77
"github.com/tidwall/sjson"
88

@@ -55,12 +55,17 @@ func (s Signing) Sign(message []byte) ([]byte, error) {
5555
// SignJSON creates a signature for the given object after encoding it to
5656
// canonical JSON.
5757
func (s Signing) SignJSON(obj any) (string, error) {
58-
objJSON, err := json.Marshal(obj)
58+
objJSON, err := canonicaljson.Marshal(obj)
5959
if err != nil {
6060
return "", err
6161
}
6262
objJSON, _ = sjson.DeleteBytes(objJSON, "unsigned")
6363
objJSON, _ = sjson.DeleteBytes(objJSON, "signatures")
64-
signature, err := s.Sign(canonicaljson.CanonicalJSONAssumeValid(objJSON))
64+
// This is probably not necessary
65+
err = canonicaljson.Canonicalize(&objJSON)
66+
if err != nil {
67+
return "", fmt.Errorf("failed to canonicalize JSON after deleting unsigned and signatures: %w", err)
68+
}
69+
signature, err := s.Sign(objJSON)
6570
return string(signature), err
6671
}

crypto/libolm/pk.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import "C"
1313

1414
import (
1515
"crypto/rand"
16-
"encoding/json"
16+
"fmt"
1717
"runtime"
1818
"unsafe"
1919

@@ -124,13 +124,18 @@ func (p *PKSigning) Sign(message []byte) ([]byte, error) {
124124

125125
// SignJSON creates a signature for the given object after encoding it to canonical JSON.
126126
func (p *PKSigning) SignJSON(obj interface{}) (string, error) {
127-
objJSON, err := json.Marshal(obj)
127+
objJSON, err := canonicaljson.Marshal(obj)
128128
if err != nil {
129129
return "", err
130130
}
131131
objJSON, _ = sjson.DeleteBytes(objJSON, "unsigned")
132132
objJSON, _ = sjson.DeleteBytes(objJSON, "signatures")
133-
signature, err := p.Sign(canonicaljson.CanonicalJSONAssumeValid(objJSON))
133+
// This is probably not necessary
134+
err = canonicaljson.Canonicalize(&objJSON)
135+
if err != nil {
136+
return "", fmt.Errorf("failed to canonicalize JSON after deleting unsigned and signatures: %w", err)
137+
}
138+
signature, err := p.Sign(objJSON)
134139
if err != nil {
135140
return "", err
136141
}

crypto/signatures/signatures.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package signatures
88

99
import (
10+
"bytes"
1011
"encoding/base64"
1112
"encoding/json"
1213
"errors"
@@ -67,10 +68,13 @@ func VerifySignatureJSON(obj any, userID id.UserID, keyName string, key id.Ed255
6768
var err error
6869
objJSON, ok := obj.(json.RawMessage)
6970
if !ok {
70-
objJSON, err = json.Marshal(obj)
71+
objJSON, err = canonicaljson.Marshal(obj)
7172
if err != nil {
7273
return false, err
7374
}
75+
} else {
76+
// Canonicalize later may mutate the data, so clone the input
77+
objJSON = bytes.Clone(objJSON)
7478
}
7579

7680
sig := gjson.GetBytes(objJSON, exgjson.Path("signatures", string(userID), fmt.Sprintf("ed25519:%s", keyName)))
@@ -85,10 +89,13 @@ func VerifySignatureJSON(obj any, userID id.UserID, keyName string, key id.Ed255
8589
if err != nil {
8690
return false, err
8791
}
88-
objJSONString := canonicaljson.CanonicalJSONAssumeValid(objJSON)
92+
err = canonicaljson.Canonicalize(&objJSON)
93+
if err != nil {
94+
return false, fmt.Errorf("failed to canonicalize JSON after deleting unsigned and signatures: %w", err)
95+
}
8996
sigBytes, err := base64.RawStdEncoding.DecodeString(sig.Str)
9097
if err != nil {
9198
return false, err
9299
}
93-
return VerifySignature(objJSONString, key, sigBytes)
100+
return VerifySignature(objJSON, key, sigBytes)
94101
}

crypto/verificationhelper/sas.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"crypto/sha256"
1616
"crypto/subtle"
1717
"encoding/base64"
18-
"encoding/json"
1918
"errors"
2019
"fmt"
2120
"strings"
@@ -259,11 +258,11 @@ func calculateCommitment(ephemeralPubKey *ecdh.PublicKey, txn VerificationTransa
259258
// hashing it, but we are just stuck on that.
260259
commitmentHashInput := sha256.New()
261260
commitmentHashInput.Write([]byte(base64.RawStdEncoding.EncodeToString(ephemeralPubKey.Bytes())))
262-
encodedStartEvt, err := json.Marshal(txn.StartEventContent)
261+
encodedStartEvt, err := canonicaljson.Marshal(txn.StartEventContent)
263262
if err != nil {
264263
return nil, err
265264
}
266-
commitmentHashInput.Write(canonicaljson.CanonicalJSONAssumeValid(encodedStartEvt))
265+
commitmentHashInput.Write(encodedStartEvt)
267266
return commitmentHashInput.Sum(nil), nil
268267
}
269268

0 commit comments

Comments
 (0)