/usr/share/pyshared/pyhsm/soft_hsm.py is in python-pyhsm 1.0.4f-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | """
functions for implementing parts of the HSMs machinery in software
"""
# Copyright (c) 2012 Yubico AB
# See the file COPYING for licence statement.
import struct
__all__ = [
# constants
# functions
'aesCCM',
# classes
]
import pyhsm.exception
from Crypto.Cipher import AES
def _xor_block(a, b):
""" XOR two blocks of equal length. """
return ''.join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])
class _ctr_counter():
"""
An object implementation of the struct aesCtr.
"""
def __init__(self, key_handle, nonce, flags = None, value = 0):
self.flags = pyhsm.defines.YSM_CCM_CTR_SIZE - 1 if flags is None else flags
self.key_handle = key_handle
self.nonce = nonce
self.value = value
def next(self):
"""
Return next counter value, encoded into YSM_BLOCK_SIZE.
"""
self.value += 1
return self.pack()
def pack(self):
fmt = b'< B I %is BBB 2s' % (pyhsm.defines.YSM_AEAD_NONCE_SIZE)
val = struct.pack('> H', self.value)
return struct.pack(fmt,
self.flags,
self.key_handle,
self.nonce,
0, 0, 0, # rfu
val
)
class _cbc_mac():
def __init__(self, key, key_handle, nonce, data_len):
"""
Initialize CBC-MAC like the YubiHSM does.
"""
flags = (((pyhsm.defines.YSM_AEAD_MAC_SIZE - 2) / 2) << 3) | (pyhsm.defines.YSM_CCM_CTR_SIZE - 1)
t = _ctr_counter(key_handle, nonce, flags = flags, value = data_len)
t_mac = t.pack()
self.mac_aes = AES.new(key, AES.MODE_ECB)
self.mac = self.mac_aes.encrypt(t_mac)
def update(self, block):
block = block.ljust(pyhsm.defines.YSM_BLOCK_SIZE, chr(0x0))
t1 = _xor_block(self.mac, block)
t2 = self.mac_aes.encrypt(t1)
self.mac = t2
def finalize(self, block):
"""
The final step of CBC-MAC encrypts before xor.
"""
t1 = self.mac_aes.encrypt(block)
t2 = _xor_block(self.mac, t1)
self.mac = t2
def get(self):
return self.mac[: pyhsm.defines.YSM_AEAD_MAC_SIZE]
def _split_data(data, pos):
a = data[:pos]
b = data[pos:]
return (a, b,)
def aesCCM(key, key_handle, nonce, data, decrypt=False):
"""
Function implementing YubiHSM AEAD encrypt/decrypt in software.
"""
if decrypt:
(data, saved_mac) = _split_data(data, len(data) - pyhsm.defines.YSM_AEAD_MAC_SIZE)
nonce = pyhsm.util.input_validate_nonce(nonce, pad = True)
mac = _cbc_mac(key, key_handle, nonce, len(data))
counter = _ctr_counter(key_handle, nonce, value = 0)
ctr_aes = AES.new(key, AES.MODE_CTR, counter = counter.next)
out = []
while data:
(thisblock, data) = _split_data(data, pyhsm.defines.YSM_BLOCK_SIZE)
# encrypt/decrypt and CBC MAC
if decrypt:
aes_out = ctr_aes.decrypt(thisblock)
mac.update(aes_out)
else:
mac.update(thisblock)
aes_out = ctr_aes.encrypt(thisblock)
out.append(aes_out)
# Finalize MAC
counter.value = 0
mac.finalize(counter.pack())
if decrypt:
if mac.get() != saved_mac:
raise pyhsm.exception.YHSM_Error('AEAD integrity check failed')
else:
out.append(mac.get())
return ''.join(out)
|