1#!/usr/bin/env python 2# 3# Public Domain 2014-2018 MongoDB, Inc. 4# Public Domain 2008-2014 WiredTiger, Inc. 5# 6# This is free and unencumbered software released into the public domain. 7# 8# Anyone is free to copy, modify, publish, use, compile, sell, or 9# distribute this software, either in source code form or as a compiled 10# binary, for any purpose, commercial or non-commercial, and by any 11# means. 12# 13# In jurisdictions that recognize copyright laws, the author or authors 14# of this software dedicate any and all copyright interest in the 15# software to the public domain. We make this dedication for the benefit 16# of the public at large and to the detriment of our heirs and 17# successors. We intend this dedication to be an overt act of 18# relinquishment in perpetuity of all present and future rights to this 19# software under copyright law. 20# 21# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27# OTHER DEALINGS IN THE SOFTWARE. 28# 29# test_encrypt04.py 30# Test mismatches error conditions with encryption. 31# 32 33import os, run, random 34import wiredtiger, wttest 35from wtscenario import make_scenarios 36from suite_subprocess import suite_subprocess 37 38# Test basic encryption with mismatched configuration 39class test_encrypt04(wttest.WiredTigerTestCase, suite_subprocess): 40 41 uri='table:test_encrypt04' 42 43 # For tests that are mismatching, we use a secretkey. The 'rotn' 44 # encryptor without a secretkey is too simple, and may leave 45 # substantial portions of its input unchanged - a root page decoded 46 # with simply the wrong keyid may appear valid when initially verified, 47 # but may result in error on first use. The odds that a real encryptor 48 # would leave a lot of its input unchanged is infinitesimally small. 49 # 50 # When both self.forceerror1 and self.forceerror2 occur, we set a config 51 # flag when loading the rotn encryptor, which forces a particular error 52 # return in rotn.decrypt. We look for that return back from 53 # wiredtiger_open. 54 encrypt_scen_1 = [ 55 ('none', dict( name1='none', keyid1='', secretkey1='')), 56 ('rotn17abc', dict( name1='rotn', keyid1='17', 57 secretkey1='ABC', forceerror1=True)), 58 ('rotn11abc', dict( name1='rotn', keyid1='11', secretkey1='ABC')), 59 ('rotn11xyz', dict( name1='rotn', keyid1='11', secretkey1='XYZ')), 60 ('rotn11xyz_and_clear', dict( name1='rotn', keyid1='11', 61 secretkey1='XYZ', fileinclear1=True)) 62 ] 63 encrypt_scen_2 = [ 64 ('none', dict( name2='none', keyid2='', secretkey2='')), 65 ('rotn17abc', dict( name2='rotn', keyid2='17', secretkey2='ABC')), 66 ('rotn11abc', dict( name2='rotn', keyid2='11', secretkey2='ABC')), 67 ('rotn11xyz', dict( name2='rotn', keyid2='11', 68 secretkey2='XYZ', forceerror2=True)), 69 ('rotn11xyz_and_clear', dict( name2='rotn', keyid2='11', 70 secretkey2='XYZ', fileinclear2=True)) 71 ] 72 scenarios = make_scenarios(encrypt_scen_1, encrypt_scen_2) 73 nrecords = 5000 74 bigvalue = "abcdefghij" * 1001 # len(bigvalue) = 10010 75 76 def __init__(self, *args, **kwargs): 77 wttest.WiredTigerTestCase.__init__(self, *args, **kwargs) 78 self.part = 1 79 80 def conn_extensions(self, extlist): 81 extarg = None 82 if self.expect_forceerror: 83 extarg='(config=\"rotn_force_error=true\")' 84 extlist.skip_if_missing = True 85 extlist.extension('encryptors', self.name, extarg) 86 87 # Override WiredTigerTestCase, we have extensions. 88 def setUpConnectionOpen(self, dir): 89 self.expect_forceerror = False 90 if self.part == 1: 91 self.name = self.name1 92 self.keyid = self.keyid1 93 self.secretkey = self.secretkey1 94 self.fileinclear = self.fileinclear1 if \ 95 hasattr(self, 'fileinclear1') else False 96 else: 97 self.name = self.name2 98 self.keyid = self.keyid2 99 self.secretkey = self.secretkey2 100 self.fileinclear = self.fileinclear2 if \ 101 hasattr(self, 'fileinclear2') else False 102 if hasattr(self, 'forceerror1') and hasattr(self, 'forceerror2'): 103 self.expect_forceerror = True 104 self.got_forceerror = False 105 106 encarg = 'encryption=(name={0},keyid={1},secretkey={2}),'.format( 107 self.name, self.keyid, self.secretkey) 108 # If forceerror is set for this test, conn_extensions adds a 109 # config arg to the extension string. That signals rotn to 110 # return a (-1000) error code, which we'll detect here. 111 extarg = self.extensionsConfig() 112 self.pr('encarg = ' + encarg + ' extarg = ' + extarg) 113 completed = False 114 try: 115 conn = self.wiredtiger_open(dir, 116 'create,error_prefix="{0}",{1}{2}'.format( 117 self.shortid(), encarg, extarg)) 118 except (BaseException) as err: 119 # Capture the recognizable error created by rotn 120 if str(-1000) in str(err): 121 self.got_forceerror = True 122 raise 123 self.pr(`conn`) 124 return conn 125 126 def create_records(self, cursor, r, low, high): 127 for idx in xrange(low, high): 128 start = r.randint(0,9) 129 key = self.bigvalue[start:r.randint(0,100)] + str(idx) 130 val = self.bigvalue[start:r.randint(0,10000)] + str(idx) 131 cursor.set_key(key) 132 cursor.set_value(val) 133 cursor.insert() 134 135 def check_records(self, cursor, r, low, high): 136 for idx in xrange(low, high): 137 start = r.randint(0,9) 138 key = self.bigvalue[start:r.randint(0,100)] + str(idx) 139 val = self.bigvalue[start:r.randint(0,10000)] + str(idx) 140 cursor.set_key(key) 141 self.assertEqual(cursor.search(), 0) 142 self.assertEquals(cursor.get_value(), val) 143 144 # Evaluate expression, which either must succeed (if expect_okay) 145 # or must fail (if !expect_okay). 146 def check_okay(self, expect_okay, expr): 147 completed = False 148 if expect_okay: 149 expr() 150 else: 151 # expect an error, and maybe error messages, 152 # so turn off stderr checking. 153 with self.expectedStderrPattern(''): 154 try: 155 expr() 156 completed = True 157 except: 158 pass 159 self.assertEqual(False, completed) 160 return expect_okay 161 162 # Create a table with encryption values that are in error. 163 def test_encrypt(self): 164 params = 'key_format=S,value_format=S' 165 if self.name == 'none' or self.fileinclear: 166 params += ',encryption=(name=none)' 167 else: 168 params += ',encryption=(name=' + self.name + \ 169 ',keyid=' + self.keyid + ')' 170 171 self.session.create(self.uri, params) 172 cursor = self.session.open_cursor(self.uri, None) 173 r = random.Random() 174 r.seed(0) 175 self.create_records(cursor, r, 0, self.nrecords) 176 cursor.close() 177 178 # Now intentionally expose the test to mismatched configuration 179 self.part = 2 180 self.name = self.name2 181 self.keyid = self.keyid2 182 self.secretkey = self.secretkey2 183 184 is_same = (self.name1 == self.name2 and 185 self.keyid1 == self.keyid2 and 186 self.secretkey1 == self.secretkey2) 187 188 # We expect an error if we specified different 189 # encryption from one open to the next. 190 expect_okay = is_same 191 192 # Force the cache to disk, so we read 193 # compressed/encrypted pages from disk. 194 if self.check_okay(expect_okay, lambda: self.reopen_conn()): 195 cursor = self.session.open_cursor(self.uri, None) 196 r.seed(0) 197 self.check_records(cursor, r, 0, self.nrecords) 198 199 if not is_same: 200 # With a configuration that has changed, we do a further test. 201 # Add some more items with the current configuration. 202 self.create_records(cursor, r, self.nrecords, self.nrecords * 2) 203 cursor.close() 204 205 # Force the cache to disk, so we read 206 # compressed/encrypted pages from disk. 207 # Now read both sets of data. 208 self.reopen_conn() 209 cursor = self.session.open_cursor(self.uri, None) 210 r.seed(0) 211 self.check_records(cursor, r, 0, self.nrecords) 212 self.check_records(cursor, r, self.nrecords, self.nrecords * 2) 213 cursor.close() 214 self.assertEqual(self.expect_forceerror, self.got_forceerror) 215 216if __name__ == '__main__': 217 wttest.run() 218