1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import base64
6import json
7import logging
8import os.path
9import tempfile
10
11import common
12from autotest_lib.client.bin import test
13from autotest_lib.client.common_lib import error
14from autotest_lib.client.common_lib import utils
15
16# This modifies the include path to include the shill test-scripts.
17# pylint: disable=W0611
18from autotest_lib.client.cros import flimflam_test_path
19# pylint: enable=W0611
20import crypto_util_pb2
21
22
23TEST_DATA1 = """{
24    "connected":false,
25    "debug_build":true,
26    "has_update":false,
27    "hotspot_bssid":"00:1A:11:FF:AC:DF",
28    "locale":"en_US",
29    "location": {
30        "latitude":37.4193105,
31        "longitude":-122.07878869999999
32    },
33    "mac_address":"4C:AA:16:A5:AC:DF",
34    "name":"eureka8997",
35    "public_key":"MIGJAoGBAK3SXmWZBOhJibv8It05qIbgHXXhnCXxHkW+C6jNMHR5sZgDpFaOY1xwXERjKdJxcwrEy3VAT5Uv9MgHPBvxxJku76HYh1yVfIw1rhLnHBTHSxwUzJNCrgc3l3t/UACacLjVNIzccDpYf2vnOcA+t1t6IXRjzuU2NdwY4dJXNtWPAgMBAAE=",
36    "setup_state":11,
37    "sign": {
38        "certificate":"-----BEGIN CERTIFICATE-----\\nMIIDhzCCAm8CBFE2SCMwDQYJKoZIhvcNAQEFBQAwfTELMAkGA1UEBhMCVVMxEzAR\\nBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEzARBgNV\\nBAoMCkdvb2dsZSBJbmMxEjAQBgNVBAsMCUdvb2dsZSBUVjEYMBYGA1UEAwwPRXVy\\nZWthIEdlbjEgSUNBMB4XDTEzMDMwNTE5MzE0N1oXDTMzMDIyODE5MzE0N1owgYMx\\nFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxEjAQBgNVBAsTCUdvb2dsZSBUVjETMBEG\\nA1UEChMKR29vZ2xlIEluYzETMBEGA1UECBMKQ2FsaWZvcm5pYTELMAkGA1UEBhMC\\nVVMxHjAcBgNVBAMUFWV2dF9lMTYxIDAwMWExMWZmYWNkZjCCASIwDQYJKoZIhvcN\\nAQEBBQADggEPADCCAQoCggEBAPHGDV0lLoTYK78q13y/2u77YTjgbBlWAOxgrSNc\\nMmGHx1K0aPyo50p99dGQnjapW6jtGrMzReWV2Wz3VL8rYlqY7oWjeJwsLQwo2tcn\\n7vIZ/PuvPz9xgnGMUbBOfhCf3Epb1N4Jz82pxxrOFhUawWAglC9C4fUeZLCZpOJs\\nQd4QeAznkydl3xbqdSm74kwxE6vkGEzSCDnC7aYx0Rvvr1mZOKdl4AinYrxzWgmV\\nsTnaFT1soSjmC5e/i6Jcrs4dDFgY6mKy9Qtly2XPSCYljm6L4SgqgJNmlpY0qYJg\\nO++BdofIbU2jsOiCMvIuKkbMn72NsPQG0QhnVMwk7kYg6kkCAwEAAaMNMAswCQYD\\nVR0TBAIwADANBgkqhkiG9w0BAQUFAAOCAQEAW0bQl9yjBc7DgMp94i7ZDOUxKQrz\\nthephuwzb3/wWiTHcw6KK6FRPefXn6NPWxKKeQmv/tBxHbVlmYRXUbrhksnD0aUk\\ni4InvtL2m0H1fPfMxmJRFE+HoSXu+s0sGON831JaMcYRbAku5uHnltaGNzOI0KPH\\nFGoCDmjAZD+IuoR2LR4FuuTrECK7KLjkdf//z5d5j7nBDPZS7uTCwC/BwM9asRj3\\ntJA5VRFbLbsit1VI7IaRCk9rsSKkpBUaVeKbPLz+y/Z6JonXXT6AxsfgUSKDd4B7\\nMYLrTwMQfGuUaaaKko6ldKIrovjrcPloQr1Hxb2bipFcjLmG7nxQLoS6vQ==\\n-----END CERTIFICATE-----\\n",
39        "nonce":"+6KSGuRu833m1+TP",
40        "signed_data":"vwMBgANrp5XpCswLyk/OTXT56ORPeIWjH7xAdCk3qgjkwI6+8o56zJS02+tC5hhIHWh7oppTmWYF4tKvBQ3GeCz7IW9f7HWDMtO7x7yRWxzJyehaJbCfXvLdfs0/WKllzvGVBgNpcIAwU2NSFUG/jpXclntFzds0EUJG9wHxS6PXXSYRu+PlIFdCDcQJsUlnwO9AGFOJRV/aARGh8YUTWCFIQPOtPEqT5eegt+TLf01Gq0YcrRwSTKy1I3twOnWiMfIdkJdQKPtBwwbvuAyGuqYFocfjKABbnH9Tvl04yyO3euKbYlSqaF/l8CXmzDJTyO7tDOFK59bV9auE4KljrQ=="
41    },
42    "ssdp_udn":"c5b2a83b-5958-7ce6-b179-e1f44699429b",
43    "ssid":"",
44    "timezone":"America/Los_Angeles",
45    "uptime":1991.7,
46    "version":4,
47    "wpa_configured":false,
48    "wpa_state":1
49} """
50TEST_DATA2 = """{
51    "connected": false,
52    "debug_build": true,
53    "has_update": false,
54    "hotspot_bssid": "00:1A:11:FF:AC:1E",
55    "ip_address": "192.168.43.32",
56    "locale": "en_US",
57    "mac_address": "B0:EE:45:49:AC:1E",
58    "name": "Greg's Eureka",
59    "noise_level": -85,
60    "public_key": "MIGJAoGBAOe+6bF51A7wFVMbyPiHYLdgAmP6sdhOUohqCHn4qHSfDY41AbAbVmXLbUZ5BF2KSdDYqU4fAXoaI8V8D5DRWh57Ax10Sl1/6M1u22KT6FYQyUToXGPcXldBzRRMok8H4XyiebDVevjvvV6yuABSYYhfSlrMdGj8qxRVwTxx0CItAgMBAAE=",
61    "release_track": "beta-channel",
62    "setup_state": 50,
63    "sign": {
64        "certificate": "-----BEGIN CERTIFICATE-----\\nMIIDejCCAmICBFEtN4wwDQYJKoZIhvcNAQEFBQAwfTELMAkGA1UEBhMCVVMxEzAR\\nBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEzARBgNV\\nBAoMCkdvb2dsZSBJbmMxEjAQBgNVBAsMCUdvb2dsZSBUVjEYMBYGA1UEAwwPRXVy\\nZWthIEdlbjEgSUNBMB4XDTEzMDIyNjIyMzAzNloXDTMzMDIyMTIyMzAzNlowdzET\\nMBEGA1UECBMKQ2FsaWZvcm5pYTELMAkGA1UEBhMCVVMxFjAUBgNVBAcTDU1vdW50\\nYWluIFZpZXcxEjAQBgNVBAsTCUdvb2dsZSBUVjETMBEGA1UEChMKR29vZ2xlIElu\\nYzESMBAGA1UEAxQJZXZ0X2UxMjYyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\\nCgKCAQEAo7Uu+bdyCjtiUYpmNU4ZvRjDg6VkEh/g0YPDG2pICBU4XKvsqHH1i0hb\\ntWp1J79hV9Rqst1yHT02Oeh3o1SOd2zeamYzmvXRVN7AZqfQlzWxwxk/ltpXGwew\\nm+EIR2bP4kpvyEKvvziTMtTxviOK+A395QyodMhMXClKTus/Gme2r1fBoQqJJR/z\\nrmwXCsl5kpdhj7FOIII3BCYV0zejjQquzywjsKfCVON28VGgJdaKgmXxkeRYYWVN\\nnuTNna57vXe16FP6hS1ty1U77ESffLTpNJ/M4tsd2dMVVTDuGeX3q8Ix4TN8cqpq\\nu1AKEf59hygys9j6cHZRKR/div0+uQIDAQABow0wCzAJBgNVHRMEAjAAMA0GCSqG\\nSIb3DQEBBQUAA4IBAQAZx6XyEK9SLHE+rbKCVsLN9+hTEa50aikPmxOZt+lFuB4+\\nVJZ/GCPQCZJIde2tlWUe2YBgoZw2xUKgIsM3Yq42Gawi35/oZ3qycTgYU8KJP9kU\\nMbYNAH90mz9BDH7MmnRID5dFexHyBCG88EJ+ZvxmUVn0EVDcsSMt11wIAZ/T+/gs\\nE1120d/GxhjYQ9YZz7SZXBQfRdqCdcPNl2+QSHHl+WvYLzdJa2xYj39/kQu47Vp7\\nX5rZrHSBvzdVymH0Od2D18t+Q6lxbSdyUNhP1MVhdkT1Ct4OmRS3FJ4aannXMhfq\\nNg7k4Sfif5iktYT4VRKpThe0EGJNfqKJKYtvHEVC\\n-----END CERTIFICATE-----\\n",
65        "nonce": "pTwRhdsB5cb3Ev8o",
66        "signed_data": "WIvN6Ujo4CoxUNcsm1YaLRUe1KJVEG+oXorUBJae/fKQrLfnH9ChDfxzW+EDZlLBLPk9u5EAptr8LKK6AunbBTDIeBkjzXv3nS+xlmV9ZdA71imitva88HPzC+a2H61dJL8puNbZu9j1Zs3sCybw2F+qZbDBhbG0sJTEXytPjewqAl8iBZSAS0BoNJZYA7Q/bCPI07pg404pI392cKP8FYJR08Y4xoV94Em+jnZ2nZabSsmsScYGvpWVNeS2z+f0to6ureOxaqgT+AAckqtCRcHd66QtLGwKXWviaevKte1z185f4r55U4P5pkQi+xd6lZRsMQydwUzLxgk7UY5U3Q=="
67    },
68    "signal_level": -42,
69    "ssdp_udn": "cd7c1f15-2f49-076b-51ac-9d651872c784",
70    "ssid": "Greg's Phone",
71    "timezone": "America/Los_Angeles",
72    "uptime": 207.33,
73    "version": 4,
74    "wpa_configured": true,
75    "wpa_id": 0,
76    "wpa_state": 10
77}
78"""
79TEST_DATA3 = """{
80    "build_version":"10566",
81    "connected":false,
82    "has_update":false,
83    "hotspot_bssid":"FA:8F:CA:30:08:26",
84    "locale":"en_US",
85    "mac_address":"B0:EE:45:68:B5:52",
86    "name":"Chromekey Greg",
87    "public_key":"MIIBCgKCAQEApQoxFRJWEP9Oa+lSF2PCMBCpd2LYeyPMHjVKCF2IlrGbrgQ3U9wI6tBvLkHg570KSA6onL/7e9J31RLwA8gIE0zJ9M7NToxj1cVa8jOqXPIllJP7uY/TmtMIwCvuMCu6KBNW/wzKK6jkT7wScwrPJscmwWr/0h6lKXZrv0DhwvVv3i6VGsasZzVYlOoZ7eRe1OHiJgehPFUh0Vp7lVRDzumtJ0N6hybWP/Ap6dNlcO9Hq67bljHRrgwuBT2iRJBIdZt6m4xZlf78dH6Y2gfeQ3GBtDZWbZ5v6hzaYeIxlMTelz6ZlEQa0fPegK1dsxCddvLhRCDQr2bZlwyRdof6uwIDAQAB",
88    "release_track":"beta-channel",
89    "setup_state":11,
90    "sign": {
91        "certificate": "-----BEGIN CERTIFICATE-----\\nMIIDpjCCAo4CBFFfTz8wDQYJKoZIhvcNAQEFBQAwfTELMAkGA1UEBhMCVVMxEzAR\\nBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEzARBgNV\\nBAoMCkdvb2dsZSBJbmMxEjAQBgNVBAsMCUdvb2dsZSBUVjEYMBYGA1UEAwwPRXVy\\nZWthIEdlbjEgSUNBMB4XDTEzMDQwNTIyMjUwM1oXDTMzMDMzMTIyMjUwM1owgYAx\\nCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRMwEQYDVQQKEwpHb29n\\nbGUgSW5jMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRIwEAYDVQQLEwlHb29nbGUg\\nVFYxGzAZBgNVBAMTElpaQUlNIEZBOEZDQTMwMDgyNjCCASIwDQYJKoZIhvcNAQEB\\nBQADggEPADCCAQoCggEBALmAGRcIQt/k7WfoUHDCWQhzQ/FrHDnNiHcVj09Tyu7/\\n2pIB+pVnLac8pfFwdVAcDG4xmjnROIHeajz627uq8bHEVDfPCHe8WiM+CsPrMC85\\nlQOq8rBwNw3WO82suvWL8wjdqFy0uQHajp9e9ix3FXzfZDAuf7ABHmVZnPMWQz0a\\nR3zDCCF2NhEVhBwq+IGKNNvuIPhVF/5tkdWPmihFukJabmV2kjFUuPbEf4T1OMu+\\nhyqZ+lsTc/XVIqICRIq9ZE36MquAw0g9Hoah8OFFpkq2xJqlCY+HKvdhiA1HybfY\\nXSxOsI2rTQk3YHFMrAGMu16UYuOA+CRHD+UC8SI0fqkCAwEAAaMvMC0wCQYDVR0T\\nBAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcN\\nAQEFBQADggEBAJGyWBoDvwZBlDcyX31l5kwGVf6nVZR4KedcQMGpm76C6fgzeLHt\\nAq7OQhZgifFVLvzG7kqYgRRsW7WQKTF/QmHn8rnCdY16YtZv3vFzLbdBWkmSqAxD\\nqmBum9oD74AYeJaR0v68KCGvAVG0juRSs3H93s8k/fhY+LespUMlHwB4DwRYxDNP\\nIu3HX8NEmOBwjodKDDa8yu1weYUf8GFXH3b+tu7IHRzQpPzbSSibs+AXc8WbJWk3\\nrtQSugqegiiM/OAdRuSvkXtc1DJP9F7OLsVpF9i/VZ2k/QDPCu06vXB2w3wlRyR3\\nf6nQGB1Ej8Klsxlo8B+rmQ6N8C7f48GMI8s=\\n-----END CERTIFICATE-----\\n",
92        "nonce": "E1oqNODwWVIfSjUs",
93        "signed_data": "aaT56/P7KiMxVbWCedpSNIikpxh0EzdjgEuP153pCNfYys2KmlVtvEnXeFGYKJP2ypsX0qx/9Bx2C18T8HZ4PvNW0fUEheScvkzbDTn/gBpwUJv3XT+55XneUdjIAQu83qNR/AjxYa9cDGzxbnZJL91Fd1Grw/fNaKEMiedWRiESABQREiS2OW+iZ3Z/JRs99o4M6m+6f/mvNkgZ3GrFxNnPEXe7g1sYjspPhnDLvASNzd9j24YIxu+PTWZCNL6clps+/ghNA/0fB3PBDILrcGnER36Vc8gNhjD1r1f/ACU1qk06yMNC/Kgt2pMQkpjaXby+B75Z9yrogNHs4f5tIA=="
94    },
95    "ssdp_udn":"b91027b5-f19e-7df6-6533-5056ad190575",
96    "ssid":"Greg's Phone",
97    "uptime":262650.75,
98    "version":4,
99    "wpa_configured":false,
100    "wpa_id":0,
101    "wpa_state":1
102}"""
103# This is just a DER formatted public key with the first 22 bytes stripped.
104# Technically, this is called RSAPublicKey format.
105ENCRYPTION_PUBLIC_KEY = (
106    '0\x81\x89\x02\x81\x81\x00\xd4\xe3z\x82\x0b6D\x8a\x1dY'
107    '\x1d\xdel\xbf\xad,\r\x8b\xefm\xe3\xbd\xef<\x954\x96C<'
108    '\x9e\x7fB\xef\xe8&h\xfb\xc4Z\x0e\x196C\x1d\xd7f\x88\\5'
109    '\xd5\x1e\xb8\xe0\x8d\xc4\xbc\xd1\xb3\xc4\xd3)!7\xd2{'
110    '\x11\x89\t\xfb\x8d\xe8\xbaRL\xacI\xf2\x0f.JB\x8e\xd4'
111    '\xdc\xc2\x81v6\x8a\x154\xbe8\xfc\xa8\x84zH\xf0j\x87('
112    '\xcf\x142B\xcfV\xd0\n\xbbPW\x80d\xd2\x0e\xf1\x1a\x04'
113    '\x0b\xa8U=\x8f3\x85\xcd\x02\x03\x01\x00\x01'
114)
115# This is the matching private key.  Note that the public key is
116# a subset of this information should you need it for debugging.
117ENCRYPTION_PRIVATE_KEY = """
118-----BEGIN RSA PRIVATE KEY-----
119MIICXQIBAAKBgQDU43qCCzZEih1ZHd5sv60sDYvvbeO97zyVNJZDPJ5/Qu/oJmj7
120xFoOGTZDHddmiFw11R644I3EvNGzxNMpITfSexGJCfuN6LpSTKxJ8g8uSkKO1NzC
121gXY2ihU0vjj8qIR6SPBqhyjPFDJCz1bQCrtQV4Bk0g7xGgQLqFU9jzOFzQIDAQAB
122AoGBALfdR/9s44/KoZJIQ8Q0v8HeaU9+30U5jF9pLaYggtty2nTsR5u6d/TZPY42
123BcVeXBV6XbBa8NZMJelXQvCw6d3hrirtRgqo+R9n6Z5Ji9oKA9NR0ZD0d66OLgRz
1247U1w2wiM9KqfjRpRhuID1MrDF8R0mxORSFnlMeoHD74ncK6ZAkEA+NBV2OOigyU/
125CVWCTKmOpxUGb6bRZfvQRln4XlVUhSgZU0Acf0APl3Av1vUPyPjRIXRnMS5ZBDFK
126YoUqWH3ymwJBANsJhX3HHYXk7aDrmPKE7q25xR7Eu4C7XEdiG0SSl8PNAicU0APy
127zFs9GLhTgGCTZf/SCzH6ejEIiOC/RZeqW7cCQCapAF3F6O9lryi9H5TX17GAY9Kf
128YfPtr4vu2NeXfJ2AAIdd88+V3ZZTOSu2QjCg8KW5F3udzvkGy58JP+4mC7cCQBEU
129+gMoHyZNBzcwiHoJYe/MeBIBN7o/Yl/yx7ueTxWnDE7t8ZcNPWC0MBRX9sARXrgH
130snXQWe0vBDW61PuR/psCQQDthOoMBnuWamOEZrEJr+itK5+ZPlE+KpT3ladXui57
131zvz3CljmlLwyufahWaF+LzfohVAG5CLr4Zi/bv7j1lnJ
132-----END RSA PRIVATE KEY-----
133"""
134
135
136def unicode2str(value):
137    """
138    Recurses through value, converting unicode to strings.
139
140    The output of the json python decoder looks like a dictionary with
141    unicode keys mapping to more unicode strings, and possibly
142    dictionaries.  Convert those unicode strings to ASCII strings
143    recursively with this function.
144
145    @param value: dict or unicode.
146    @returns value with unicode recursively replaced with string.
147
148    """
149    if isinstance(value, dict):
150        ret = {}
151        for k,v in value.items():
152            ret[unicode2str(k)] = unicode2str(v)
153        return ret
154
155    if isinstance(value, unicode):
156        return str(value)
157
158    return value
159
160
161class network_DestinationVerification(test.test):
162    """Tests that the destination verification logic in shill works.
163
164    This logic allows us to verify that (for instance) we trust the
165    credentials of a Chromekey.
166
167    """
168    version = 1
169
170
171    @property
172    def shim_path(self):
173        """@return path to crypto-util shim."""
174        lib_paths = ['lib', 'lib64']
175        shim_path = '/usr/%s/shill/shims/crypto-util'
176        for lib in lib_paths:
177            path = shim_path % lib
178            if os.path.exists(path):
179                return path
180        raise error.TestFail('Unable to find crypto-util.')
181
182
183    def _call_shim_with_args(self, args, protobuffer):
184        """Calls the crypto_util shim with args and stdin as specified.
185
186        Calls crypto shim with args for paramters, and the serialized form of
187        protobuffer on stdin.  Asserts that the command completes successfully
188        and returns the raw output bytestring.
189
190        @param args: tuple of string arguments to the shim.
191        @param protobuffer: python protocol buffer object.
192        @return stdout of the shim call as string.
193
194        """
195        raw_input_bytes = protobuffer.SerializeToString()
196        result  = utils.run(self.shim_path,
197                            stderr_tee=utils.TEE_TO_LOGS,
198                            stdin=raw_input_bytes,
199                            verbose=True,
200                            args=args,
201                            timeout=10,
202                            ignore_status=True)
203        if result.exit_status:
204            return None
205        raw_output = result.stdout
206        logging.debug('Got raw output: %s.',
207                      ''.join([hex(ord(c)) for c in raw_output]))
208        return raw_output
209
210
211    def _test_verify(self, test_data, expect_failure=False):
212        """Test that shim verify operation works correctly.
213
214        Call into the shim to perform a verify operation, check that the call
215        succeeds unless |expect_failure|.  |test_data| looks like the decoded
216        json data we're given by the destination.
217
218        @param test_data: dictionary of test data to use with the call.
219        @param expect_failure: bool true if test_data should fail to verify.
220
221        @raises TestFail when verification doesn't go as expected.
222
223        """
224        # See doc/manager-api.txt, but the format goes roughly:
225        #     ssid,udn,bssid,key,nonce
226        sign_bundle = test_data['sign']
227        unsigned_data = '%s,%s,%s,%s,%s' % (test_data['name'],
228                                            test_data['ssdp_udn'],
229                                            test_data['hotspot_bssid'],
230                                            test_data['public_key'],
231                                            sign_bundle['nonce'])
232        message = crypto_util_pb2.VerifyCredentialsMessage()
233        message.certificate = sign_bundle['certificate']
234        message.signed_data = base64.b64decode(sign_bundle['signed_data'])
235        message.unsigned_data = unsigned_data
236        message.mac_address = test_data['hotspot_bssid']
237        output = self._call_shim_with_args(('verify',), message)
238        if expect_failure:
239            if output is None:
240                # Expected a failure, and got it.  We're done here.
241                return
242            raise error.TestFail('Expected verification to fail, but it '
243                                 'succeeded.')
244
245        if not output:
246            raise error.TestFail('Verification failed unexpectedly.')
247
248        response = crypto_util_pb2.VerifyCredentialsResponse()
249        response.ParseFromString(output)
250        if response.ret != crypto_util_pb2.OK:
251            raise error.TestFail('Expected verification success, '
252                                 'but got: %d.' % response.ret)
253
254
255    def _test_encrypt(self, public_key, data_to_encrypt, expect_failure=False):
256        """Test that shim encrypt operation works correctly.
257
258        Call into the shim to perform an encrypt operation.
259        Raise a test error if the call fails, unless |expect_failure|.
260
261        @param public_key: string containing RSAPublicKey format RSA key.
262        @param data_to_encrypt: string data to encrypt.
263        @param expect_failure: bool true if encrypt should fail.
264
265        """
266        message = crypto_util_pb2.EncryptDataMessage()
267        message.public_key = public_key
268        message.data = data_to_encrypt
269        output = self._call_shim_with_args(('encrypt',), message)
270        if expect_failure:
271            if output is None:
272                # Expected a failure, and got it.  We're done here.
273                return
274            raise error.TestFail('Expected encryption to fail, '
275                                 'but it did not.')
276
277        if not output:
278            raise error.TestFail('Encryption failed unexpectedly.')
279
280        response = crypto_util_pb2.EncryptDataResponse()
281        response.ParseFromString(output)
282        if response.ret != crypto_util_pb2.OK:
283            raise error.TestFail('Data encryption failed.')
284
285        logging.debug('Decrypting %d bytes of encrypted data to confirm match.',
286                      len(response.encrypted_data))
287        try:
288            private_key = tempfile.NamedTemporaryFile()
289            encrypted_data = tempfile.NamedTemporaryFile()
290            private_key.write(ENCRYPTION_PRIVATE_KEY)
291            private_key.flush()
292            encrypted_data.write(response.encrypted_data)
293            encrypted_data.flush()
294            result = utils.run('openssl rsautl -decrypt -inkey %s -in %s' %
295                                       (private_key.name, encrypted_data.name),
296                               stdin=response.encrypted_data,
297                               stderr_tee=utils.TEE_TO_LOGS,
298                               verbose=True,
299                               ignore_status=True)
300            if result.exit_status:
301                if data_to_encrypt == '':
302                    # Due to an open bug in OpenSSL, rsautl doesn't handle this
303                    # case correctly.  For now, just pass and assume that
304                    # we're doing this correctly.
305                    # http://rt.openssl.org/Ticket/Display.html?id=2018
306                    return
307                raise error.TestFail('Failed to decrypt shim output.')
308
309            if result.stdout != data_to_encrypt:
310                raise error.TestFail('Data encyption failed: '
311                                     'expected: %s, but got: %s' %
312                                     (data_to_encrypt, result.stdout))
313
314        finally:
315            private_key.close()
316            encrypted_data.close()
317
318
319    def run_once(self):
320        """Body of the test."""
321        logging.info('Checking basic encryption operation of the shim.')
322        self._test_encrypt(ENCRYPTION_PUBLIC_KEY, 'disco boy')
323
324        logging.info('Checking graceful fail for too much data to encrypt.')
325        self._test_encrypt(ENCRYPTION_PUBLIC_KEY,
326                           ''.join(['x' for i in range(500)]),
327                            expect_failure=True)
328
329        logging.info('Checking graceful fail for a bad key format.')
330        self._test_encrypt('this will not parse in openssl',
331                            'disco boy',
332                            expect_failure=True)
333
334        logging.info('Checking that we encrypt the empty string correctly.')
335        self._test_encrypt(ENCRYPTION_PUBLIC_KEY, '')
336
337        logging.info('Checking basic verification operation.')
338        test_data = unicode2str(json.loads(TEST_DATA1))
339        self._test_verify(test_data)
340
341        logging.info('Checking bad nonce in unsigned_data.')
342        test_data = unicode2str(json.loads(TEST_DATA1))
343        test_data['sign']['nonce'] = 'does not match'
344        self._test_verify(test_data, expect_failure=True)
345
346        logging.info('Checking for graceful fail for invalid '
347                     'certificate format')
348        test_data = unicode2str(json.loads(TEST_DATA1))
349        test_data['sign']['certificate'] = 'not a certificate'
350        self._test_verify(test_data, expect_failure=True)
351
352        logging.info('Verification fails when the certificate is signed, but '
353                     'subject is malformed.')
354        test_data = unicode2str(json.loads(TEST_DATA2))
355        self._test_verify(test_data, expect_failure=True)
356
357        logging.info('Verification should succeed on third set of data.')
358        test_data = unicode2str(json.loads(TEST_DATA3))
359        self._test_verify(test_data)
360
361        logging.info('Test completed successfully.')
362