-
Notifications
You must be signed in to change notification settings - Fork 96
/
Copy pathintegrity.go
130 lines (109 loc) · 3.95 KB
/
integrity.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package stun
import ( //nolint:gci
"crypto/md5" //nolint:gosec
"crypto/sha1" //nolint:gosec
"errors"
"fmt"
"strings"
"github.com/pion/stun/v3/internal/hmac"
)
// separator for credentials.
const credentialsSep = ":"
// NewLongTermIntegrity returns new MessageIntegrity with key for long-term
// credentials. Password, username, and realm must be SASL-prepared.
func NewLongTermIntegrity(username, realm, password string) MessageIntegrity {
k := strings.Join([]string{username, realm, password}, credentialsSep)
h := md5.New() //nolint:gosec
fmt.Fprint(h, k) //nolint:errcheck
return MessageIntegrity(h.Sum(nil))
}
// NewShortTermIntegrity returns new MessageIntegrity with key for short-term
// credentials. Password must be SASL-prepared.
func NewShortTermIntegrity(password string) MessageIntegrity {
return MessageIntegrity(password)
}
// MessageIntegrity represents MESSAGE-INTEGRITY attribute.
//
// AddTo and Check methods are using zero-allocation version of hmac, see
// newHMAC function and internal/hmac/pool.go.
//
// RFC 5389 Section 15.4.
type MessageIntegrity []byte
func newHMAC(key, message, buf []byte) []byte {
mac := hmac.AcquireSHA1(key)
writeOrPanic(mac, message)
defer hmac.PutSHA1(mac)
return mac.Sum(buf)
}
func (i MessageIntegrity) String() string {
return fmt.Sprintf("KEY: 0x%x", []byte(i))
}
const messageIntegritySize = 20
// ErrFingerprintBeforeIntegrity means that FINGERPRINT attribute is already in
// message, so MESSAGE-INTEGRITY attribute cannot be added.
var ErrFingerprintBeforeIntegrity = errors.New("FINGERPRINT before MESSAGE-INTEGRITY attribute")
// AddTo adds MESSAGE-INTEGRITY attribute to message.
//
// CPU costly, see BenchmarkMessageIntegrity_AddTo.
func (i MessageIntegrity) AddTo(msg *Message) error {
for _, a := range msg.Attributes {
// Message should not contain FINGERPRINT attribute
// before MESSAGE-INTEGRITY.
if a.Type == AttrFingerprint {
return ErrFingerprintBeforeIntegrity
}
}
// The text used as input to HMAC is the STUN message,
// including the header, up to and including the attribute preceding the
// MESSAGE-INTEGRITY attribute.
length := msg.Length
// Adjusting m.Length to contain MESSAGE-INTEGRITY TLV.
msg.Length += messageIntegritySize + attributeHeaderSize
msg.WriteLength() // writing length to m.Raw
v := newHMAC(i, msg.Raw, msg.Raw[len(msg.Raw):]) // calculating HMAC for adjusted m.Raw
msg.Length = length // changing m.Length back
// Copy hmac value to temporary variable to protect it from resetting
// while processing m.Add call.
vBuf := make([]byte, sha1.Size)
copy(vBuf, v)
msg.Add(AttrMessageIntegrity, vBuf)
return nil
}
// ErrIntegrityMismatch means that computed HMAC differs from expected.
var ErrIntegrityMismatch = errors.New("integrity check failed")
// Check checks MESSAGE-INTEGRITY attribute.
//
// CPU costly, see BenchmarkMessageIntegrity_Check.
func (i MessageIntegrity) Check(msg *Message) error {
val, err := msg.Get(AttrMessageIntegrity)
if err != nil {
return err
}
// Adjusting length in header to match m.Raw that was
// used when computing HMAC.
var (
length = msg.Length
afterIntegrity = false
sizeReduced int
)
for _, a := range msg.Attributes {
if afterIntegrity {
sizeReduced += nearestPaddedValueLength(int(a.Length))
sizeReduced += attributeHeaderSize
}
if a.Type == AttrMessageIntegrity {
afterIntegrity = true
}
}
msg.Length -= uint32(sizeReduced) //nolint:gosec // G115
msg.WriteLength()
// startOfHMAC should be first byte of integrity attribute.
startOfHMAC := messageHeaderSize + msg.Length - (attributeHeaderSize + messageIntegritySize)
b := msg.Raw[:startOfHMAC] // data before integrity attribute
expected := newHMAC(i, b, msg.Raw[len(msg.Raw):])
msg.Length = length
msg.WriteLength() // writing length back
return checkHMAC(val, expected)
}