Package pike :: Module crypto
[hide private]
[frames] | no frames]

Source Code for Module pike.crypto

  1  # 
  2  # Copyright (c) 2016, EMC Corporation 
  3  # All rights reserved. 
  4  # 
  5  # Redistribution and use in source and binary forms, with or without 
  6  # modification, are permitted provided that the following conditions are met: 
  7  # 
  8  # 1. Redistributions of source code must retain the above copyright notice, 
  9  # this list of conditions and the following disclaimer. 
 10  # 2. Redistributions in binary form must reproduce the above copyright notice, 
 11  # this list of conditions and the following disclaimer in the documentation 
 12  # and/or other materials provided with the distribution. 
 13  # 
 14  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 15  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 16  # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 17  # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
 18  # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 19  # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 20  # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 21  # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 22  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 23  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 24  # POSSIBILITY OF SUCH DAMAGE. 
 25  # 
 26  # Module Name: 
 27  # 
 28  #        crypto.py 
 29  # 
 30  # Abstract: 
 31  # 
 32  #        SMB3 Encryption 
 33  # 
 34  # Authors: Masen Furer (masen.furer@dell.com) 
 35  # 
 36  import core 
 37  import digest 
 38  import smb2 
 39   
 40  import array 
 41  import random 
 42   
 43  from Cryptodome.Cipher import AES 
 44   
 45   
46 -def pad_right(value, length, byte='\0'):
47 if len(value) > length: 48 value = value[:length] 49 elif len(value) < 16: 50 value += array.array('B', byte*(length - len(value))) 51 return value
52 53
54 -class CipherMismatch(Exception):
55 pass
56 57
58 -class Ciphers(core.ValueEnum):
59 SMB2_NONE_CIPHER = 0x0000 60 SMB2_AES_128_CCM = 0x0001 61 SMB2_AES_128_GCM = 0x0002
62 63 Ciphers.import_items(globals()) 64 65 cipher_map = { 66 SMB2_AES_128_CCM: (AES.MODE_CCM, 11), 67 SMB2_AES_128_GCM: (AES.MODE_GCM, 12) 68 } 69 70
71 -class EncryptionCapabilities(core.Frame):
72 context_type = smb2.SMB2_ENCRYPTION_CAPABILITIES 73
74 - def __init__(self):
75 self.ciphers = [] 76 self.ciphers_count = None
77
78 - def _encode(self, cur):
79 if self.ciphers_count is None: 80 self.ciphers_count = len(self.ciphers) 81 cur.encode_uint16le(self.ciphers_count) 82 for c in self.ciphers: 83 cur.encode_uint16le(c)
84
85 - def _decode(self, cur):
86 self.ciphers_count = cur.decode_uint16le() 87 for ix in xrange(self.ciphers_count): 88 self.ciphers.append(Ciphers(cur.decode_uint16le()))
89 90
91 -class EncryptionCapabilitiesRequest(smb2.NegotiateRequestContext, 92 EncryptionCapabilities):
93 - def __init__(self, parent):
96 97
98 -class EncryptionCapabilitiesResponse(smb2.NegotiateResponseContext, 99 EncryptionCapabilities):
100 - def __init__(self, parent):
103 104
105 -class TransformHeader(core.Frame):
106 """ 107 TransformHeader is designed to be a transparent slip attached to a Netbios 108 frame object (specified as parent). During serialization, if a Netbios frame 109 contains an attribute "transform", then that object's encode method will be 110 called instead of each child frames' encode method. This frame is responsible 111 for both encrypting and decrypting Smb2 frames according to an attached 112 encryption context. 113 114 If the encryption_context is not explicitly specified, then it will be looked 115 up based on session_id from the parent Netbios frame's connection reference 116 """
117 - def __init__(self, parent):
118 core.Frame.__init__(self, parent) 119 self.protocol_id = array.array('B', "\xfdSMB") 120 self.signature = None 121 # the value of nonce is always used in the encryption routine 122 self.nonce = array.array('B', 123 map(random.randint, [0]*16, [255]*16)) 124 # if wire_nonce is set, it will be sent on the wire instead of nonce 125 self.wire_nonce = None 126 self.original_message_size = None 127 self.reserved = None 128 self.flags = 0x1 129 self.session_id = None 130 self.encryption_context = None 131 self.additional_authenticated_data_buf = array.array('B') 132 if parent is not None: 133 parent.transform = self 134 else: 135 self._smb2_frames = []
136
137 - def _children(self):
138 if self.parent is not None: 139 return self.parent._children() 140 else: 141 return self._smb2_frames
142
143 - def append(self, smb2_frame):
144 if self.parent is not None: 145 self.parent.append(smb2_frame) 146 else: 147 self._smb2_frames.append(smb2_frame)
148
149 - def _encode(self, cur):
150 self._encode_header(cur) 151 self._encode_smb2(cur)
152
153 - def _encode_header(self, cur):
154 # look up the encryption context if not specified 155 if self.encryption_context is None and self.parent is not None: 156 self.encryption_context = self.parent.conn.encryption_context(self.session_id) 157 cur.encode_bytes(self.protocol_id) 158 159 # the signature will be written in _encode_smb2 160 self.signature_offset = cur.offset 161 cur.advanceto(cur + 16) 162 # the crypto header will be written in _encode_smb2 163 self.crypto_header_offset = cur.offset 164 # save a space for the wire nonce 165 self.wire_nonce_hole = cur.hole.encode_bytes('\0'*16) 166 cur.advanceto(cur + 16) 167 168 # the following fields are part of AdditionalAuthenticatedData and are 169 # used as inputs to the AES cipher 170 aad_cur = core.Cursor(self.additional_authenticated_data_buf, 0) 171 # nonce field size is 16 bytes right padded with zeros 172 self.nonce = pad_right(self.nonce, 16) 173 aad_cur.encode_bytes(self.nonce) 174 self.original_message_size_hole = aad_cur.hole.encode_uint32le(0) 175 if self.reserved is None: 176 self.reserved = 0 177 aad_cur.encode_uint16le(self.reserved) # reserved 178 aad_cur.encode_uint16le(self.flags) 179 aad_cur.encode_uint64le(self.session_id)
180
181 - def _encode_smb2(self, cur):
182 # serialize all chained Smb2 commands into one buffer 183 original_message_buf = array.array('B') 184 original_message_cur = core.Cursor(original_message_buf, 0) 185 for smb_frame in self.parent: 186 smb_frame.encode(original_message_cur) 187 if self.original_message_size is None: 188 self.original_message_size = len(original_message_buf) 189 self.original_message_size_hole(self.original_message_size) 190 (self.ciphertext, 191 crypto_hmac) = self.encryption_context.encrypt( 192 original_message_buf, 193 self.additional_authenticated_data_buf, 194 self.nonce) 195 cur.encode_bytes(self.ciphertext) 196 197 # fill in the signature hole 198 sig_cur = core.Cursor(cur.array, self.signature_offset) 199 if self.signature is None: 200 self.signature = crypto_hmac 201 sig_cur.encode_bytes(self.signature) 202 203 # fill in the header 204 aad_cur = core.Cursor(cur.array, self.crypto_header_offset) 205 aad_cur.encode_bytes(self.additional_authenticated_data_buf) 206 207 # fill in the wire nonce 208 if self.wire_nonce is None: 209 self.wire_nonce = self.nonce 210 self.wire_nonce_hole(pad_right(self.wire_nonce, 16))
211
212 - def _decode(self, cur):
213 self._decode_header(cur) 214 self._decode_smb2(cur)
215
216 - def _decode_header(self, cur):
217 self.protocol_id = cur.decode_bytes(4) 218 self.signature = cur.decode_bytes(16) 219 # the following fields are part of AdditionalAuthenticatedData and are 220 # used as inputs to the AES cipher 221 self.crypto_header_start = cur.offset 222 self.nonce = cur.decode_bytes(16) 223 self.original_message_size = cur.decode_uint32le() 224 self.reserved = cur.decode_uint16le() # reserved 225 self.flags = cur.decode_uint16le() 226 self.session_id = cur.decode_uint64le() 227 self.crypto_header_end = cur.offset 228 if self.encryption_context is None and self.parent is not None: 229 self.encryption_context = self.parent.conn.encryption_context(self.session_id)
230
231 - def _decode_smb2(self, cur):
232 self.encrypted_data = cur.decode_bytes(self.original_message_size) 233 crypto_header = cur.array[self.crypto_header_start:self.crypto_header_end] 234 self.plaintext = self.encryption_context.decrypt( 235 self.encrypted_data, 236 self.signature, 237 crypto_header, 238 self.nonce) 239 # scan through the plaintext for chained messages 240 pt_cur = core.Cursor(self.plaintext, 0) 241 end = pt_cur + len(self.plaintext) 242 with pt_cur.bounded(pt_cur, end): 243 while (pt_cur < end): 244 start = pt_cur.offset 245 message = smb2.Smb2(self.parent) 246 message.decode(pt_cur) 247 message.buf = pt_cur.array[start:pt_cur.offset]
248
249 - def verify(self, *args, **kwds):
250 pass # verification occurs at the point of decryption
251 252
253 -class CryptoKeys300(object):
254 """ Key generation for SMB 0x300 and 0x302 """
255 - def __init__(self, session_key, *args, **kwds):
256 self.encryption = digest.derive_key( 257 session_key, "SMB2AESCCM", "ServerIn \0")[:16].tostring() 258 self.decryption = digest.derive_key( 259 session_key, "SMB2AESCCM", "ServerOut\0")[:16].tostring()
260 261
262 -class CryptoKeys311(object):
263 """ Key generation for SMB 0x311 + """
264 - def __init__(self, session_key, pre_auth_integrity_hash, *args, **kwds):
265 self.encryption = digest.derive_key( 266 session_key, "SMBC2SCipherKey", pre_auth_integrity_hash)[:16].tostring() 267 self.decryption = digest.derive_key( 268 session_key, "SMBS2CCipherKey", pre_auth_integrity_hash)[:16].tostring()
269 270
271 -class EncryptionContext(object):
272 """ 273 Encapsulates all information needed to encrypt and decrypt messages. 274 This context is attached to an SMB Session object 275 """
276 - def __init__(self, keys, ciphers):
277 self.keys = keys 278 for c in ciphers: 279 if c in cipher_map: 280 self.aes_mode, self.nonce_length = cipher_map[c] 281 self.cipher = c 282 break 283 else: 284 raise CipherMismatch( 285 "Client did not recognize any ciphers returned by the " 286 "server: {0}".format(ciphers))
287
288 - def encrypt(self, plaintext, authenticated_data, nonce):
289 enc_cipher = AES.new(self.keys.encryption, 290 self.aes_mode, 291 nonce=nonce[:self.nonce_length].tostring()) 292 enc_cipher.update(authenticated_data.tostring()) 293 ciphertext, signature = enc_cipher.encrypt_and_digest(plaintext.tostring()) 294 return array.array('B', ciphertext), array.array('B', signature)
295
296 - def decrypt(self, ciphertext, signature, authenticated_data, nonce):
297 dec_cipher = AES.new(self.keys.decryption, 298 self.aes_mode, 299 nonce=nonce[:self.nonce_length].tostring()) 300 dec_cipher.update(authenticated_data.tostring()) 301 return array.array( 302 'B', 303 dec_cipher.decrypt_and_verify( 304 ciphertext.tostring(), 305 signature.tostring()))
306