• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2#
3# Copyright 2017 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17# pylint: disable=g-bad-todo,g-bad-file-header,wildcard-import
18from errno import *  # pylint: disable=wildcard-import
19import os
20import itertools
21from scapy import all as scapy
22from socket import *  # pylint: disable=wildcard-import
23import subprocess
24import threading
25import unittest
26
27import multinetwork_base
28import net_test
29from tun_twister import TapTwister
30import util
31import xfrm
32import xfrm_base
33import xfrm_test
34
35ANY_KVER = net_test.LINUX_ANY_VERSION
36
37# List of encryption algorithms for use in ParamTests.
38CRYPT_ALGOS = [
39    (xfrm.XfrmAlgo((xfrm.XFRM_EALG_CBC_AES, 128)), ANY_KVER),
40    (xfrm.XfrmAlgo((xfrm.XFRM_EALG_CBC_AES, 192)), ANY_KVER),
41    (xfrm.XfrmAlgo((xfrm.XFRM_EALG_CBC_AES, 256)), ANY_KVER),
42    # RFC 3686 specifies that key length must be 128, 192 or 256 bits, with
43    # an additional 4 bytes (32 bits) of nonce. A fresh nonce value MUST be
44    # assigned for each SA.
45    # CTR-AES is enforced since kernel version 5.8
46    (xfrm.XfrmAlgo((xfrm.XFRM_EALG_CTR_AES, 128+32)), (5, 8)),
47    (xfrm.XfrmAlgo((xfrm.XFRM_EALG_CTR_AES, 192+32)), (5, 8)),
48    (xfrm.XfrmAlgo((xfrm.XFRM_EALG_CTR_AES, 256+32)), (5, 8)),
49]
50
51# List of auth algorithms for use in ParamTests.
52AUTH_ALGOS = [
53    # RFC 4868 specifies that the only supported truncation length is half the
54    # hash size.
55    (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_MD5, 128, 96)), ANY_KVER),
56    (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA1, 160, 96)), ANY_KVER),
57    (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA256, 256, 128)), ANY_KVER),
58    (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA384, 384, 192)), ANY_KVER),
59    (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA512, 512, 256)), ANY_KVER),
60    # Test larger truncation lengths for good measure.
61    (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_MD5, 128, 128)), ANY_KVER),
62    (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA1, 160, 160)), ANY_KVER),
63    (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA256, 256, 256)), ANY_KVER),
64    (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA384, 384, 384)), ANY_KVER),
65    (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA512, 512, 512)), ANY_KVER),
66    # RFC 3566 specifies that the only supported truncation length
67    # is 96 bits.
68    # XCBC-AES is enforced since kernel version 5.8
69    (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_AUTH_XCBC_AES, 128, 96)), (5, 8)),
70]
71
72# List of aead algorithms for use in ParamTests.
73AEAD_ALGOS = [
74    # RFC 4106 specifies that key length must be 128, 192 or 256 bits,
75    #   with an additional 4 bytes (32 bits) of salt. The salt must be unique
76    #   for each new SA using the same key.
77    # RFC 4106 specifies that ICV length must be 8, 12, or 16 bytes
78    (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 128+32, 8*8)), ANY_KVER),
79    (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 128+32, 12*8)), ANY_KVER),
80    (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 128+32, 16*8)), ANY_KVER),
81    (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 192+32, 8*8)), ANY_KVER),
82    (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 192+32, 12*8)), ANY_KVER),
83    (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 192+32, 16*8)), ANY_KVER),
84    (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 256+32, 8*8)), ANY_KVER),
85    (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 256+32, 12*8)), ANY_KVER),
86    (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 256+32, 16*8)), ANY_KVER),
87    # RFC 7634 specifies that key length must be 256 bits, with an additional
88    # 4 bytes (32 bits) of nonce. A fresh nonce value MUST be assigned for
89    # each SA. RFC 7634 also specifies that ICV length must be 16 bytes.
90    # ChaCha20-Poly1305 is enforced since kernel version 5.8
91    (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_CHACHA20_POLY1305, 256+32, 16*8)), (5, 8)),
92]
93
94def GenerateKey(key_len):
95  if key_len % 8 != 0:
96    raise ValueError("Invalid key length in bits: " + str(key_len))
97  return os.urandom(key_len / 8)
98
99# Does the kernel support this algorithm?
100def HaveAlgo(crypt_algo, auth_algo, aead_algo):
101  try:
102    test_xfrm = xfrm.Xfrm()
103    test_xfrm.FlushSaInfo()
104    test_xfrm.FlushPolicyInfo()
105
106    test_xfrm.AddSaInfo(
107        src=xfrm_test.TEST_ADDR1,
108        dst=xfrm_test.TEST_ADDR2,
109        spi=xfrm_test.TEST_SPI,
110        mode=xfrm.XFRM_MODE_TRANSPORT,
111        reqid=100,
112        encryption=(crypt_algo,
113                    GenerateKey(crypt_algo.key_len)) if crypt_algo else None,
114        auth_trunc=(auth_algo,
115                    GenerateKey(auth_algo.key_len)) if auth_algo else None,
116        aead=(aead_algo, GenerateKey(aead_algo.key_len)) if aead_algo else None,
117        encap=None,
118        mark=None,
119        output_mark=None)
120
121    test_xfrm.FlushSaInfo()
122    test_xfrm.FlushPolicyInfo()
123
124    return True
125  except IOError as err:
126    if err.errno == ENOSYS:
127      return False
128    else:
129      print("Unexpected error:", err.errno)
130      return True
131
132# Dictionary to record the algorithm state. Mark the state True if this
133# algorithm is enforced or enabled on this kernel. Otherwise, mark it
134# False.
135algoState = {}
136
137def AlgoEnforcedOrEnabled(crypt, auth, aead, target_algo, target_kernel):
138  if algoState.get(target_algo) is None:
139    algoState[target_algo] = net_test.LINUX_VERSION >= target_kernel or HaveAlgo(
140        crypt, auth, aead)
141  return algoState.get(target_algo)
142
143# Return true if this algorithm should be enforced or is enabled on this kernel
144def AuthEnforcedOrEnabled(authCase):
145  auth = authCase[0]
146  crypt = xfrm.XfrmAlgo(("ecb(cipher_null)", 0))
147  return AlgoEnforcedOrEnabled(crypt, auth, None, auth.name, authCase[1])
148
149# Return true if this algorithm should be enforced or is enabled on this kernel
150def CryptEnforcedOrEnabled(cryptCase):
151  crypt = cryptCase[0]
152  auth = xfrm.XfrmAlgoAuth(("digest_null", 0, 0))
153  return AlgoEnforcedOrEnabled(crypt, auth, None, crypt.name, cryptCase[1])
154
155# Return true if this algorithm should be enforced or is enabled on this kernel
156def AeadEnforcedOrEnabled(aeadCase):
157  aead = aeadCase[0]
158  return AlgoEnforcedOrEnabled(None, None, aead, aead.name, aeadCase[1])
159
160def InjectTests():
161  XfrmAlgorithmTest.InjectTests()
162
163
164class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest):
165  @classmethod
166  def InjectTests(cls):
167    VERSIONS = (4, 6)
168    TYPES = (SOCK_DGRAM, SOCK_STREAM)
169
170    # Tests all combinations of auth & crypt. Mutually exclusive with aead.
171    param_list = itertools.product(VERSIONS, TYPES, AUTH_ALGOS, CRYPT_ALGOS,
172                                   [None])
173    util.InjectParameterizedTest(cls, param_list, cls.TestNameGenerator)
174
175    # Tests all combinations of aead. Mutually exclusive with auth/crypt.
176    param_list = itertools.product(VERSIONS, TYPES, [None], [None], AEAD_ALGOS)
177    util.InjectParameterizedTest(cls, param_list, cls.TestNameGenerator)
178
179  @staticmethod
180  def TestNameGenerator(version, proto, authCase, cryptCase, aeadCase):
181    # Produce a unique and readable name for each test. e.g.
182    #     testSocketPolicySimple_cbc-aes_256_hmac-sha512_512_256_IPv6_UDP
183    param_string = ""
184    if cryptCase is not None:
185      crypt = cryptCase[0]
186      param_string += "%s_%d_" % (crypt.name, crypt.key_len)
187
188    if authCase is not None:
189      auth = authCase[0]
190      param_string += "%s_%d_%d_" % (auth.name, auth.key_len,
191          auth.trunc_len)
192
193    if aeadCase is not None:
194      aead = aeadCase[0]
195      param_string += "%s_%d_%d_" % (aead.name, aead.key_len,
196          aead.icv_len)
197
198    param_string += "%s_%s" % ("IPv4" if version == 4 else "IPv6",
199        "UDP" if proto == SOCK_DGRAM else "TCP")
200    return param_string
201
202  def ParamTestSocketPolicySimple(self, version, proto, authCase, cryptCase, aeadCase):
203    """Test two-way traffic using transport mode and socket policies."""
204
205    # Bypass the test if any algorithm going to be tested is not enforced
206    # or enabled on this kernel
207    if authCase is not None and not AuthEnforcedOrEnabled(authCase):
208      return
209    if cryptCase is not None and not CryptEnforcedOrEnabled(cryptCase):
210      return
211    if aeadCase is not None and not AeadEnforcedOrEnabled(aeadCase):
212      return
213
214    auth = authCase[0] if authCase else None
215    crypt = cryptCase[0] if cryptCase else None
216    aead = aeadCase[0] if aeadCase else None
217
218    def AssertEncrypted(packet):
219      # This gives a free pass to ICMP and ICMPv6 packets, which show up
220      # nondeterministically in tests.
221      self.assertEqual(None,
222                        packet.getlayer(scapy.UDP),
223                        "UDP packet sent in the clear")
224      self.assertEqual(None,
225                        packet.getlayer(scapy.TCP),
226                        "TCP packet sent in the clear")
227
228    # We create a pair of sockets, "left" and "right", that will talk to each
229    # other using transport mode ESP. Because of TapTwister, both sockets
230    # perceive each other as owning "remote_addr".
231    netid = self.RandomNetid()
232    family = net_test.GetAddressFamily(version)
233    local_addr = self.MyAddress(version, netid)
234    remote_addr = self.GetRemoteSocketAddress(version)
235    auth_left = (xfrm.XfrmAlgoAuth((auth.name, auth.key_len, auth.trunc_len)),
236                 os.urandom(auth.key_len / 8)) if auth else None
237    auth_right = (xfrm.XfrmAlgoAuth((auth.name, auth.key_len, auth.trunc_len)),
238                  os.urandom(auth.key_len / 8)) if auth else None
239    crypt_left = (xfrm.XfrmAlgo((crypt.name, crypt.key_len)),
240                  os.urandom(crypt.key_len / 8)) if crypt else None
241    crypt_right = (xfrm.XfrmAlgo((crypt.name, crypt.key_len)),
242                   os.urandom(crypt.key_len / 8)) if crypt else None
243    aead_left = (xfrm.XfrmAlgoAead((aead.name, aead.key_len, aead.icv_len)),
244                 os.urandom(aead.key_len / 8)) if aead else None
245    aead_right = (xfrm.XfrmAlgoAead((aead.name, aead.key_len, aead.icv_len)),
246                  os.urandom(aead.key_len / 8)) if aead else None
247    spi_left = 0xbeefface
248    spi_right = 0xcafed00d
249    req_ids = [100, 200, 300, 400]  # Used to match templates and SAs.
250
251    # Left outbound SA
252    self.xfrm.AddSaInfo(
253        src=local_addr,
254        dst=remote_addr,
255        spi=spi_right,
256        mode=xfrm.XFRM_MODE_TRANSPORT,
257        reqid=req_ids[0],
258        encryption=crypt_right,
259        auth_trunc=auth_right,
260        aead=aead_right,
261        encap=None,
262        mark=None,
263        output_mark=None)
264    # Right inbound SA
265    self.xfrm.AddSaInfo(
266        src=remote_addr,
267        dst=local_addr,
268        spi=spi_right,
269        mode=xfrm.XFRM_MODE_TRANSPORT,
270        reqid=req_ids[1],
271        encryption=crypt_right,
272        auth_trunc=auth_right,
273        aead=aead_right,
274        encap=None,
275        mark=None,
276        output_mark=None)
277    # Right outbound SA
278    self.xfrm.AddSaInfo(
279        src=local_addr,
280        dst=remote_addr,
281        spi=spi_left,
282        mode=xfrm.XFRM_MODE_TRANSPORT,
283        reqid=req_ids[2],
284        encryption=crypt_left,
285        auth_trunc=auth_left,
286        aead=aead_left,
287        encap=None,
288        mark=None,
289        output_mark=None)
290    # Left inbound SA
291    self.xfrm.AddSaInfo(
292        src=remote_addr,
293        dst=local_addr,
294        spi=spi_left,
295        mode=xfrm.XFRM_MODE_TRANSPORT,
296        reqid=req_ids[3],
297        encryption=crypt_left,
298        auth_trunc=auth_left,
299        aead=aead_left,
300        encap=None,
301        mark=None,
302        output_mark=None)
303
304    # Make two sockets.
305    sock_left = socket(family, proto, 0)
306    sock_left.settimeout(2.0)
307    sock_left.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
308    self.SelectInterface(sock_left, netid, "mark")
309    sock_right = socket(family, proto, 0)
310    sock_right.settimeout(2.0)
311    sock_right.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
312    self.SelectInterface(sock_right, netid, "mark")
313
314    # For UDP, set SO_LINGER to 0, to prevent TCP sockets from hanging around
315    # in a TIME_WAIT state.
316    if proto == SOCK_STREAM:
317      net_test.DisableFinWait(sock_left)
318      net_test.DisableFinWait(sock_right)
319
320    # Apply the left outbound socket policy.
321    xfrm_base.ApplySocketPolicy(sock_left, family, xfrm.XFRM_POLICY_OUT,
322                                spi_right, req_ids[0], None)
323    # Apply right inbound socket policy.
324    xfrm_base.ApplySocketPolicy(sock_right, family, xfrm.XFRM_POLICY_IN,
325                                spi_right, req_ids[1], None)
326    # Apply right outbound socket policy.
327    xfrm_base.ApplySocketPolicy(sock_right, family, xfrm.XFRM_POLICY_OUT,
328                                spi_left, req_ids[2], None)
329    # Apply left inbound socket policy.
330    xfrm_base.ApplySocketPolicy(sock_left, family, xfrm.XFRM_POLICY_IN,
331                                spi_left, req_ids[3], None)
332
333    server_ready = threading.Event()
334    server_error = None  # Save exceptions thrown by the server.
335
336    def TcpServer(sock, client_port):
337      try:
338        sock.listen(1)
339        server_ready.set()
340        accepted, peer = sock.accept()
341        self.assertEqual(remote_addr, peer[0])
342        self.assertEqual(client_port, peer[1])
343        data = accepted.recv(2048)
344        self.assertEqual("hello request", data)
345        accepted.send("hello response")
346      except Exception as e:
347        server_error = e
348      finally:
349        sock.close()
350
351    def UdpServer(sock, client_port):
352      try:
353        server_ready.set()
354        data, peer = sock.recvfrom(2048)
355        self.assertEqual(remote_addr, peer[0])
356        self.assertEqual(client_port, peer[1])
357        self.assertEqual("hello request", data)
358        sock.sendto("hello response", peer)
359      except Exception as e:
360        server_error = e
361      finally:
362        sock.close()
363
364    # Server and client need to know each other's port numbers in advance.
365    wildcard_addr = net_test.GetWildcardAddress(version)
366    sock_left.bind((wildcard_addr, 0))
367    sock_right.bind((wildcard_addr, 0))
368    left_port = sock_left.getsockname()[1]
369    right_port = sock_right.getsockname()[1]
370
371    # Start the appropriate server type on sock_right.
372    target = TcpServer if proto == SOCK_STREAM else UdpServer
373    server = threading.Thread(
374        target=target,
375        args=(sock_right, left_port),
376        name="SocketServer")
377    server.start()
378    # Wait for server to be ready before attempting to connect. TCP retries
379    # hide this problem, but UDP will fail outright if the server socket has
380    # not bound when we send.
381    self.assertTrue(server_ready.wait(2.0), "Timed out waiting for server thread")
382
383    with TapTwister(fd=self.tuns[netid].fileno(), validator=AssertEncrypted):
384      sock_left.connect((remote_addr, right_port))
385      sock_left.send("hello request")
386      data = sock_left.recv(2048)
387      self.assertEqual("hello response", data)
388      sock_left.close()
389      server.join()
390    if server_error:
391      raise server_error
392
393
394if __name__ == "__main__":
395  XfrmAlgorithmTest.InjectTests()
396  unittest.main()
397