From c8567e2f6c91b3713e3db106f496f22247428a7f Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Mon, 3 Aug 2020 10:24:01 -0700 Subject: Kernel tests for AES-CTR, AES-XCBC, ChaChaPoly Tested on 4.19: - without config set, tests were bypassed - with config set, tests ran and passed Tested on 5.9 (android-mainline) - without config set, tests ran and failed - with config set, tests ran and passed Bug: 161717358 Test: xfrm_algorithm_test.py Signed-off-by: Yan Yan Change-Id: I12cd15f00ae9b3f8cc793d4b7200f895f63c2e12 --- net/test/net_test.py | 2 +- net/test/xfrm.py | 3 + net/test/xfrm_algorithm_test.py | 155 +++++++++++++++++++++++++++++++++------- 3 files changed, 132 insertions(+), 28 deletions(-) (limited to 'net/test') diff --git a/net/test/net_test.py b/net/test/net_test.py index 1c9c1c47..50b24b77 100755 --- a/net/test/net_test.py +++ b/net/test/net_test.py @@ -91,7 +91,7 @@ AID_INET = 3003 KERN_INFO = 6 LINUX_VERSION = csocket.LinuxVersion() - +LINUX_ANY_VERSION = (0, 0) def GetWildcardAddress(version): return {4: "0.0.0.0", 6: "::"}[version] diff --git a/net/test/xfrm.py b/net/test/xfrm.py index d8f5ffec..c63d2a28 100755 --- a/net/test/xfrm.py +++ b/net/test/xfrm.py @@ -123,12 +123,15 @@ XFRM_STATE_AF_UNSPEC = 32 # XFRM algorithm names, as defined in net/xfrm/xfrm_algo.c. XFRM_EALG_CBC_AES = "cbc(aes)" +XFRM_EALG_CTR_AES = "rfc3686(ctr(aes))" XFRM_AALG_HMAC_MD5 = "hmac(md5)" XFRM_AALG_HMAC_SHA1 = "hmac(sha1)" XFRM_AALG_HMAC_SHA256 = "hmac(sha256)" XFRM_AALG_HMAC_SHA384 = "hmac(sha384)" XFRM_AALG_HMAC_SHA512 = "hmac(sha512)" +XFRM_AALG_AUTH_XCBC_AES = "xcbc(aes)" XFRM_AEAD_GCM_AES = "rfc4106(gcm(aes))" +XFRM_AEAD_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)" # Data structure formats. # These aren't constants, they're classes. So, pylint: disable=invalid-name diff --git a/net/test/xfrm_algorithm_test.py b/net/test/xfrm_algorithm_test.py index c2f836c4..8a50fdec 100755 --- a/net/test/xfrm_algorithm_test.py +++ b/net/test/xfrm_algorithm_test.py @@ -30,29 +30,43 @@ from tun_twister import TapTwister import util import xfrm import xfrm_base +import xfrm_test + +ANY_KVER = net_test.LINUX_ANY_VERSION # List of encryption algorithms for use in ParamTests. CRYPT_ALGOS = [ - xfrm.XfrmAlgo((xfrm.XFRM_EALG_CBC_AES, 128)), - xfrm.XfrmAlgo((xfrm.XFRM_EALG_CBC_AES, 192)), - xfrm.XfrmAlgo((xfrm.XFRM_EALG_CBC_AES, 256)), + (xfrm.XfrmAlgo((xfrm.XFRM_EALG_CBC_AES, 128)), ANY_KVER), + (xfrm.XfrmAlgo((xfrm.XFRM_EALG_CBC_AES, 192)), ANY_KVER), + (xfrm.XfrmAlgo((xfrm.XFRM_EALG_CBC_AES, 256)), ANY_KVER), + # RFC 3686 specifies that key length must be 128, 192 or 256 bits, with + # an additional 4 bytes (32 bits) of nonce. A fresh nonce value MUST be + # assigned for each SA. + # CTR-AES is enforced since kernel version 5.8 + (xfrm.XfrmAlgo((xfrm.XFRM_EALG_CTR_AES, 128+32)), (5, 8)), + (xfrm.XfrmAlgo((xfrm.XFRM_EALG_CTR_AES, 192+32)), (5, 8)), + (xfrm.XfrmAlgo((xfrm.XFRM_EALG_CTR_AES, 256+32)), (5, 8)), ] # List of auth algorithms for use in ParamTests. AUTH_ALGOS = [ # RFC 4868 specifies that the only supported truncation length is half the # hash size. - xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_MD5, 128, 96)), - xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA1, 160, 96)), - xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA256, 256, 128)), - xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA384, 384, 192)), - xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA512, 512, 256)), + (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_MD5, 128, 96)), ANY_KVER), + (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA1, 160, 96)), ANY_KVER), + (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA256, 256, 128)), ANY_KVER), + (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA384, 384, 192)), ANY_KVER), + (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA512, 512, 256)), ANY_KVER), # Test larger truncation lengths for good measure. - xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_MD5, 128, 128)), - xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA1, 160, 160)), - xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA256, 256, 256)), - xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA384, 384, 384)), - xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA512, 512, 512)), + (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_MD5, 128, 128)), ANY_KVER), + (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA1, 160, 160)), ANY_KVER), + (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA256, 256, 256)), ANY_KVER), + (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA384, 384, 384)), ANY_KVER), + (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_HMAC_SHA512, 512, 512)), ANY_KVER), + # RFC 3566 specifies that the only supported truncation length + # is 96 bits. + # XCBC-AES is enforced since kernel version 5.8 + (xfrm.XfrmAlgoAuth((xfrm.XFRM_AALG_AUTH_XCBC_AES, 128, 96)), (5, 8)), ] # List of aead algorithms for use in ParamTests. @@ -61,17 +75,88 @@ AEAD_ALGOS = [ # with an additional 4 bytes (32 bits) of salt. The salt must be unique # for each new SA using the same key. # RFC 4106 specifies that ICV length must be 8, 12, or 16 bytes - xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 128+32, 8*8)), - xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 128+32, 12*8)), - xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 128+32, 16*8)), - xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 192+32, 8*8)), - xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 192+32, 12*8)), - xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 192+32, 16*8)), - xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 256+32, 8*8)), - xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 256+32, 12*8)), - xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 256+32, 16*8)), + (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 128+32, 8*8)), ANY_KVER), + (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 128+32, 12*8)), ANY_KVER), + (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 128+32, 16*8)), ANY_KVER), + (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 192+32, 8*8)), ANY_KVER), + (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 192+32, 12*8)), ANY_KVER), + (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 192+32, 16*8)), ANY_KVER), + (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 256+32, 8*8)), ANY_KVER), + (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 256+32, 12*8)), ANY_KVER), + (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_GCM_AES, 256+32, 16*8)), ANY_KVER), + # RFC 7634 specifies that key length must be 256 bits, with an additional + # 4 bytes (32 bits) of nonce. A fresh nonce value MUST be assigned for + # each SA. RFC 7634 also specifies that ICV length must be 16 bytes. + # ChaCha20-Poly1305 is enforced since kernel version 5.8 + (xfrm.XfrmAlgoAead((xfrm.XFRM_AEAD_CHACHA20_POLY1305, 256+32, 16*8)), (5, 8)), ] +def GenerateKey(key_len): + if key_len % 8 != 0: + raise ValueError("Invalid key length in bits: " + str(key_len)) + return os.urandom(key_len / 8) + +# Does the kernel support this algorithm? +def HaveAlgo(crypt_algo, auth_algo, aead_algo): + try: + test_xfrm = xfrm.Xfrm() + test_xfrm.FlushSaInfo() + test_xfrm.FlushPolicyInfo() + + test_xfrm.AddSaInfo( + src=xfrm_test.TEST_ADDR1, + dst=xfrm_test.TEST_ADDR2, + spi=xfrm_test.TEST_SPI, + mode=xfrm.XFRM_MODE_TRANSPORT, + reqid=100, + encryption=(crypt_algo, + GenerateKey(crypt_algo.key_len)) if crypt_algo else None, + auth_trunc=(auth_algo, + GenerateKey(auth_algo.key_len)) if auth_algo else None, + aead=(aead_algo, GenerateKey(aead_algo.key_len)) if aead_algo else None, + encap=None, + mark=None, + output_mark=None) + + test_xfrm.FlushSaInfo() + test_xfrm.FlushPolicyInfo() + + return True + except IOError as err: + if err.errno == ENOSYS: + return False + else: + print("Unexpected error:", err.errno) + return True + +# Dictionary to record the algorithm state. Mark the state True if this +# algorithm is enforced or enabled on this kernel. Otherwise, mark it +# False. +algoState = {} + +def AlgoEnforcedOrEnabled(crypt, auth, aead, target_algo, target_kernel): + if algoState.get(target_algo) is None: + algoState[target_algo] = net_test.LINUX_VERSION >= target_kernel or HaveAlgo( + crypt, auth, aead) + return algoState.get(target_algo) + +# Return true if this algorithm should be enforced or is enabled on this kernel +def AuthEnforcedOrEnabled(authCase): + auth = authCase[0] + crypt = xfrm.XfrmAlgo(("ecb(cipher_null)", 0)) + return AlgoEnforcedOrEnabled(crypt, auth, None, auth.name, authCase[1]) + +# Return true if this algorithm should be enforced or is enabled on this kernel +def CryptEnforcedOrEnabled(cryptCase): + crypt = cryptCase[0] + auth = xfrm.XfrmAlgoAuth(("digest_null", 0, 0)) + return AlgoEnforcedOrEnabled(crypt, auth, None, crypt.name, cryptCase[1]) + +# Return true if this algorithm should be enforced or is enabled on this kernel +def AeadEnforcedOrEnabled(aeadCase): + aead = aeadCase[0] + return AlgoEnforcedOrEnabled(None, None, aead, aead.name, aeadCase[1]) + def InjectTests(): XfrmAlgorithmTest.InjectTests() @@ -92,18 +177,21 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): util.InjectParameterizedTest(cls, param_list, cls.TestNameGenerator) @staticmethod - def TestNameGenerator(version, proto, auth, crypt, aead): + def TestNameGenerator(version, proto, authCase, cryptCase, aeadCase): # Produce a unique and readable name for each test. e.g. # testSocketPolicySimple_cbc-aes_256_hmac-sha512_512_256_IPv6_UDP param_string = "" - if crypt is not None: + if cryptCase is not None: + crypt = cryptCase[0] param_string += "%s_%d_" % (crypt.name, crypt.key_len) - if auth is not None: + if authCase is not None: + auth = authCase[0] param_string += "%s_%d_%d_" % (auth.name, auth.key_len, auth.trunc_len) - if aead is not None: + if aeadCase is not None: + aead = aeadCase[0] param_string += "%s_%d_%d_" % (aead.name, aead.key_len, aead.icv_len) @@ -111,9 +199,22 @@ class XfrmAlgorithmTest(xfrm_base.XfrmLazyTest): "UDP" if proto == SOCK_DGRAM else "TCP") return param_string - def ParamTestSocketPolicySimple(self, version, proto, auth, crypt, aead): + def ParamTestSocketPolicySimple(self, version, proto, authCase, cryptCase, aeadCase): """Test two-way traffic using transport mode and socket policies.""" + # Bypass the test if any algorithm going to be tested is not enforced + # or enabled on this kernel + if authCase is not None and not AuthEnforcedOrEnabled(authCase): + return + if cryptCase is not None and not CryptEnforcedOrEnabled(cryptCase): + return + if aeadCase is not None and not AeadEnforcedOrEnabled(aeadCase): + return + + auth = authCase[0] if authCase else None + crypt = cryptCase[0] if cryptCase else None + aead = aeadCase[0] if aeadCase else None + def AssertEncrypted(packet): # This gives a free pass to ICMP and ICMPv6 packets, which show up # nondeterministically in tests. -- cgit v1.2.3