-
Notifications
You must be signed in to change notification settings - Fork 95
/
integrity.go
126 lines (109 loc) · 3.87 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
// 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(m *Message) error {
for _, a := range m.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 := m.Length
// Adjusting m.Length to contain MESSAGE-INTEGRITY TLV.
m.Length += messageIntegritySize + attributeHeaderSize
m.WriteLength() // writing length to m.Raw
v := newHMAC(i, m.Raw, m.Raw[len(m.Raw):]) // calculating HMAC for adjusted m.Raw
m.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)
m.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(m *Message) error {
v, err := m.Get(AttrMessageIntegrity)
if err != nil {
return err
}
// Adjusting length in header to match m.Raw that was
// used when computing HMAC.
var (
length = m.Length
afterIntegrity = false
sizeReduced int
)
for _, a := range m.Attributes {
if afterIntegrity {
sizeReduced += nearestPaddedValueLength(int(a.Length))
sizeReduced += attributeHeaderSize
}
if a.Type == AttrMessageIntegrity {
afterIntegrity = true
}
}
m.Length -= uint32(sizeReduced)
m.WriteLength()
// startOfHMAC should be first byte of integrity attribute.
startOfHMAC := messageHeaderSize + m.Length - (attributeHeaderSize + messageIntegritySize)
b := m.Raw[:startOfHMAC] // data before integrity attribute
expected := newHMAC(i, b, m.Raw[len(m.Raw):])
m.Length = length
m.WriteLength() // writing length back
return checkHMAC(v, expected)
}