Skip to content

Commit 9a406ce

Browse files
committed
Crypto: Use official terms from RFC3711
1 parent 5aa195f commit 9a406ce

2 files changed

Lines changed: 89 additions & 62 deletions

File tree

tests/test_crypto.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ def test_init_master_keys(srtp_key: str):
1717
null_keys = srtp_crypto.SrtpMasterKeys.null_keys()
1818
dummy_keys = srtp_crypto.SrtpMasterKeys.dummy_keys()
1919

20-
assert len(from_base64.key1_buf) == 0x10
21-
assert from_base64.key1_len == 0x10
22-
assert len(from_base64.key2_buf) == 0x0E
23-
assert from_base64.key2_len == 0x0E
20+
assert len(from_base64.master_key) == 0x10
21+
assert len(from_base64.master_salt) == 0x0E
2422

2523
assert null_keys is not None
2624
assert dummy_keys is not None

xcloud/protocol/srtp_crypto.py

Lines changed: 87 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -15,60 +15,93 @@ class TransformDirection(Enum):
1515
Decrypt = 1
1616

1717
class SrtpMasterKeys:
18-
MASTER_KEY_SIZE = 30
18+
MASTER_KEY_SIZE = 16
19+
MASTER_SALT_SIZE = 14
1920
DUMMY_KEY = (
2021
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F'
2122
b'\x10\x11\x12\x13'
2223
)
2324

24-
def __init__(self, master_key: bytes):
25+
def __init__(self, master_key: bytes, master_salt: bytes):
2526
assert len(master_key) == SrtpMasterKeys.MASTER_KEY_SIZE
26-
self.key1_buf = master_key[:0x10]
27-
self.key1_len = len(self.key1_buf)
28-
self.key1_counter = 0
27+
assert len(master_salt) == SrtpMasterKeys.MASTER_SALT_SIZE
2928

30-
self.key2_buf = master_key[0x10:]
31-
self.key2_len = len(self.key2_buf)
32-
self.key2_counter = 0
29+
self._master_key = master_key
30+
self._master_key_id = 0
31+
32+
self._master_salt = master_salt
33+
self._master_salt_id = 0
3334

3435
@classmethod
35-
def from_base64(cls, master_key_b64: str):
36-
return cls(base64.b64decode(master_key_b64))
36+
def from_base64(cls, master_bytes_b64: str):
37+
decoded = base64.b64decode(master_bytes_b64)
38+
return cls(
39+
decoded[:SrtpMasterKeys.MASTER_KEY_SIZE],
40+
decoded[SrtpMasterKeys.MASTER_KEY_SIZE:]
41+
)
3742

3843
@classmethod
3944
def null_keys(cls):
40-
return cls(SrtpMasterKeys.MASTER_KEY_SIZE * b'\x00')
45+
return cls(
46+
SrtpMasterKeys.MASTER_KEY_SIZE * b'\x00',
47+
SrtpMasterKeys.MASTER_SALT_SIZE * b'\x00',
48+
)
4149

4250
@classmethod
4351
def dummy_keys(cls):
44-
dummy_key = SrtpMasterKeys.DUMMY_KEY[:0x10] + SrtpMasterKeys.DUMMY_KEY[:0x0E]
45-
return cls(dummy_key)
52+
return cls(
53+
SrtpMasterKeys.DUMMY_KEY[:SrtpMasterKeys.MASTER_KEY_SIZE],
54+
SrtpMasterKeys.DUMMY_KEY[:SrtpMasterKeys.MASTER_SALT_SIZE]
55+
)
56+
57+
@property
58+
def master_key(self) -> bytes:
59+
return self._master_key
4660

47-
@dataclass
48-
class SrtpSessionKey:
49-
buf: bytes
50-
len: int
51-
tag: bytes
61+
@property
62+
def master_key_id(self) -> int:
63+
return self._master_key_id
5264

53-
def __init__(self, key: bytes):
54-
self.buf = key
55-
self.len = len(key)
56-
self.tag = 1
65+
@property
66+
def master_salt(self) -> bytes:
67+
return self._master_salt
68+
69+
@property
70+
def master_salt_id(self) -> int:
71+
return self._master_salt_id
5772

5873
class SrtpSessionKeys:
59-
def __init__(self, session_keys: List[SrtpSessionKey]):
60-
assert len(session_keys) == 3
61-
self.session_key_1 = session_keys[0]
62-
self.session_key_2 = session_keys[1]
63-
self.session_key_3 = session_keys[2]
74+
SRTP_CRYPT = 0
75+
SRTP_AUTH = 1
76+
SRTP_SALT = 2
77+
# Max count of keys
78+
SRTP_SESSION_KEYS_MAX = 3
79+
80+
def __init__(self, crypt_key: bytes, auth_key: bytes, salt_key: bytes):
81+
self._crypt_key = crypt_key
82+
self._auth_key = auth_key
83+
self._salt_key = salt_key
6484

85+
@classmethod
86+
def from_list(cls, session_keys: List[bytes]):
87+
assert len(session_keys) == SrtpSessionKeys.SRTP_SESSION_KEYS_MAX
88+
return cls(
89+
session_keys[SrtpSessionKeys.SRTP_CRYPT],
90+
session_keys[SrtpSessionKeys.SRTP_AUTH],
91+
session_keys[SrtpSessionKeys.SRTP_SALT]
92+
)
93+
6594
@property
66-
def aes_gcm_key(self) -> bytes:
67-
return self.session_key_1.buf
95+
def crypt_key(self) -> bytes:
96+
return self._crypt_key
6897

6998
@property
70-
def nonce_key(self) -> bytes:
71-
return self.session_key_3.buf
99+
def auth_key(self) -> bytes:
100+
return self._auth_key
101+
102+
@property
103+
def salt_key(self) -> bytes:
104+
return self._salt_key
72105

73106
class SrtpContext:
74107
_backend = default_backend()
@@ -79,27 +112,27 @@ def __init__(self, master_keys: SrtpMasterKeys):
79112
"""
80113
self.master_keys = master_keys
81114
self.session_keys = SrtpContext._derive_session_keys(
82-
self.master_keys.key1_buf, self.master_keys.key2_buf
115+
self.master_keys.master_key, self.master_keys.master_salt
83116
)
84117

85118
# Set-up GCM crypto instances
86-
self.decryptor_ctx = SrtpContext._init_gcm_cryptor(self.session_keys.aes_gcm_key)
87-
self.decryptor_ctx = SrtpContext._init_gcm_cryptor(self.session_keys.aes_gcm_key)
119+
self.decryptor_ctx = SrtpContext._init_gcm_cryptor(self.session_keys.crypt_key)
120+
self.decryptor_ctx = SrtpContext._init_gcm_cryptor(self.session_keys.crypt_key)
88121

89122
@classmethod
90-
def from_base64(cls, master_key_b64: str):
123+
def from_base64(cls, master_bytes_b64: str):
91124
return cls(
92-
SrtpMasterKeys.from_base64(master_key_b64)
125+
SrtpMasterKeys.from_base64(master_bytes_b64)
93126
)
94127

95128
@classmethod
96-
def from_bytes(cls, master_key: bytes):
129+
def from_bytes(cls, master_key: bytes, master_salt: bytes):
97130
return cls(
98-
SrtpMasterKeys(master_key)
131+
SrtpMasterKeys(master_key, master_salt)
99132
)
100133

101134
@staticmethod
102-
def _derive_single_key(input_key: bytes, bitmask: int = 0) -> bytes:
135+
def _derive_single_key(input_key: bytes, key_index: int = 0) -> bytes:
103136
keysize = len(input_key)
104137
keyout = bytearray(b'\x00' * 16)
105138

@@ -111,10 +144,10 @@ def _derive_single_key(input_key: bytes, bitmask: int = 0) -> bytes:
111144
if keysize != 1:
112145
keyout[12] = input_key[keysize - 2]
113146
if keysize >= 3:
114-
key_index = 0
147+
pos = 0
115148
for _ in range(2, keysize):
116-
keyout[key_index + 11] = input_key[key_index + keysize - 3]
117-
key_index = key_index - 1
149+
keyout[pos + 11] = input_key[pos + keysize - 3]
150+
pos -= 1
118151

119152
if keysize <= 13:
120153
null_count = 14 - keysize
@@ -124,10 +157,10 @@ def _derive_single_key(input_key: bytes, bitmask: int = 0) -> bytes:
124157
for index in range(14, 16):
125158
keyout[index] = 0
126159

127-
if bitmask:
160+
if key_index:
128161
len_before_xor = len(keyout)
129162
value_to_xor = struct.unpack_from('<I', keyout, 4)[0]
130-
value_to_xor ^= bitmask
163+
value_to_xor ^= (key_index * 0x1000000)
131164
keyout = keyout[:4] + struct.pack('<I', value_to_xor) + keyout[8:]
132165
assert len(keyout) == len_before_xor
133166
return keyout
@@ -146,20 +179,16 @@ def _crypt_ctr_oneshot(key: bytes, iv: bytes, plaintext: bytes, max_bytes: Optio
146179
return cipher_out
147180

148181
@staticmethod
149-
def _derive_session_keys(key1: bytes, key2: bytes) -> SrtpSessionKeys:
150-
session1 = SrtpContext._derive_single_key(key2)
151-
session2 = SrtpContext._derive_single_key(key2, 0x1000000)
152-
session3 = SrtpContext._derive_single_key(key2, 0x2000000)
182+
def _derive_session_keys(master_key: bytes, master_salt: bytes) -> SrtpSessionKeys:
183+
tmp1 = SrtpContext._derive_single_key(master_salt, SrtpSessionKeys.SRTP_CRYPT)
184+
tmp2 = SrtpContext._derive_single_key(master_salt, SrtpSessionKeys.SRTP_AUTH)
185+
tmp3 = SrtpContext._derive_single_key(master_salt, SrtpSessionKeys.SRTP_SALT)
153186

154-
session1 = SrtpContext._crypt_ctr_oneshot(key1, session1, b'\x00' * 16)
155-
session2 = SrtpContext._crypt_ctr_oneshot(key1, session2, b'\x00' * 16)
156-
session3 = SrtpContext._crypt_ctr_oneshot(key1, session3, b'\x00' * 16, max_bytes=14)
187+
crypt_key = SrtpContext._crypt_ctr_oneshot(master_key, tmp1, b'\x00' * 16)
188+
auth_key = SrtpContext._crypt_ctr_oneshot(master_key, tmp2, b'\x00' * 16)
189+
salt_key = SrtpContext._crypt_ctr_oneshot(master_key, tmp3, b'\x00' * 16, max_bytes=14)
157190

158-
return SrtpSessionKeys([
159-
SrtpSessionKey(session1),
160-
SrtpSessionKey(session2),
161-
SrtpSessionKey(session3)
162-
])
191+
return SrtpSessionKeys(crypt_key, auth_key, salt_key)
163192

164193
@staticmethod
165194
def _init_gcm_cryptor(key: bytes) -> AESGCM:
@@ -175,11 +204,11 @@ def _encrypt(ctx: AESGCM, nonce: bytes, data: bytes, aad: bytes) -> bytes:
175204

176205
def _get_transformed_nonce(self, transform_direction: TransformDirection) -> bytes:
177206
# Skip first 2 bytes of Nonce key
178-
nonce = bytearray(self.session_keys.nonce_key[2:])
207+
nonce = bytearray(self.session_keys.salt_key[2:])
179208
# TODO: Implement transform logic
180209
# FIXME: Just tranforming the Nonce to a known value for
181210
# our single test packet
182-
nonce[-1] = nonce[-1] + 1
211+
nonce[-1] += 1
183212

184213
return nonce
185214

0 commit comments

Comments
 (0)