191 lines
6.5 KiB
Python
191 lines
6.5 KiB
Python
# Copyright (c) (2020) Apple Inc. All rights reserved.
|
||
#
|
||
# corecrypto is licensed under Apple Inc.’s Internal Use License Agreement (which
|
||
# is contained in the License.txt file distributed with corecrypto) and only to
|
||
# people who accept that license. IMPORTANT: Any license rights granted to you by
|
||
# Apple Inc. (if any) are limited to internal use within your organization only on
|
||
# devices and computers you own or control, for the sole purpose of verifying the
|
||
# security characteristics and correct functioning of the Apple Software. You may
|
||
# not, directly or indirectly, redistribute the Apple Software or any portions thereof.
|
||
|
||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||
from cryptography.hazmat.backends import default_backend
|
||
import hashlib
|
||
import binascii
|
||
import struct
|
||
|
||
RESEED_INTERVAL = 1 << 48
|
||
|
||
dec64 = lambda s: struct.unpack('>Q', s)[0]
|
||
enc64 = lambda i: struct.pack('>Q', i)
|
||
|
||
dec32 = lambda s: struct.unpack('>L', s)[0]
|
||
enc32 = lambda i: struct.pack('>L', i)
|
||
|
||
xor = lambda a,b: bytes([x^y for (x,y) in zip(a,b)])
|
||
|
||
class CTR_DRBG:
|
||
_OUTLEN = 16
|
||
_KEYLEN = 32
|
||
|
||
def _block_encrypt(self, key, value):
|
||
cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend())
|
||
encryptor = cipher.encryptor()
|
||
return encryptor.update(value) + encryptor.finalize()
|
||
|
||
def _bcc(self, K, data):
|
||
chaining_value = bytes(self._OUTLEN)
|
||
n = len(data) // self._OUTLEN
|
||
|
||
for i in range(0, n):
|
||
input_block = xor(chaining_value, data[i * self._OUTLEN:(i + 1) * self._OUTLEN])
|
||
chaining_value = self._block_encrypt(K, input_block)
|
||
|
||
return chaining_value
|
||
|
||
def _df(self, input_string, nbytes):
|
||
L = enc32(len(input_string))
|
||
N = enc32(nbytes)
|
||
S = L + N + input_string + b"\x80"
|
||
|
||
while len(S) % self._OUTLEN != 0:
|
||
S += b"\x00"
|
||
|
||
i = 0
|
||
temp = b""
|
||
K = bytes([x for x in range(0, self._KEYLEN)])
|
||
|
||
while len(temp) < self._KEYLEN + self._OUTLEN:
|
||
IV = enc32(i) + bytes(12)
|
||
temp = temp + self._bcc(K, IV + S)
|
||
i += 1
|
||
|
||
K = temp[:self._KEYLEN]
|
||
X = temp[self._KEYLEN:self._KEYLEN + self._OUTLEN]
|
||
|
||
temp = b""
|
||
while len(temp) < nbytes:
|
||
X = self._block_encrypt(K, X)
|
||
temp = temp + X
|
||
return temp[:nbytes]
|
||
|
||
def __init__(self, entropy, nonce, ps):
|
||
seed_material = entropy + nonce + ps
|
||
seed_material = self._df(seed_material, self._OUTLEN + self._KEYLEN)
|
||
self.V = bytes(self._OUTLEN)
|
||
self.K = bytes(self._KEYLEN)
|
||
|
||
self._update(seed_material)
|
||
self.reseed_counter = 1
|
||
|
||
def _update(self, provided_data):
|
||
temp = b""
|
||
seedlen = self._OUTLEN + self._KEYLEN
|
||
|
||
while len(temp) < seedlen:
|
||
nc = dec64(self.V[8:]) + 1
|
||
self.V = self.V[:8] + enc64(nc)
|
||
temp += self._block_encrypt(self.K, self.V)
|
||
|
||
temp = xor(temp[:seedlen], provided_data)
|
||
self.K = temp[:self._KEYLEN]
|
||
self.V = temp[-self._OUTLEN:]
|
||
|
||
|
||
def reseed(self, entropy, ai = b""):
|
||
seedlen = self._OUTLEN + self._KEYLEN
|
||
seed_material = entropy + ai
|
||
seed_material = self._df(seed_material, seedlen)
|
||
self._update(seed_material)
|
||
self.reseed_counter = 1
|
||
|
||
def generate(self, n, ai = b""):
|
||
seedlen = self._OUTLEN + self._KEYLEN
|
||
if not (self.reseed_counter <= RESEED_INTERVAL):
|
||
return None
|
||
|
||
if len(ai) != 0:
|
||
ai = self._df(ai, seedlen)
|
||
self._update(ai)
|
||
else:
|
||
ai = bytes(seedlen)
|
||
|
||
temp = b""
|
||
while len(temp) < n:
|
||
nc = dec64(self.V[8:]) + 1
|
||
self.V = self.V[:8] + enc64(nc)
|
||
temp += self._block_encrypt(self.K, self.V)
|
||
|
||
self._update(ai)
|
||
self.reseed_counter += 1
|
||
return temp[:n]
|
||
|
||
'''
|
||
struct ccdrbg_vector {
|
||
size_t entropyLen;
|
||
const void *entropy;
|
||
size_t nonceLen;
|
||
const void *nonce;
|
||
size_t psLen;
|
||
const void *ps; /* Personalization String */
|
||
size_t ai1Len;
|
||
const void *ai1; /* Additional Input */
|
||
size_t entropyReseedLen;
|
||
const void *entropyReseed;
|
||
size_t aiReseedLen;
|
||
const void *aiReseed; /* Additional Input */
|
||
size_t ai2Len;
|
||
const void *ai2; /* Additional Input */
|
||
size_t randomLen;
|
||
const void *random; /* Returned bytes */
|
||
};
|
||
'''
|
||
|
||
if __name__ == "__main__":
|
||
entropy = b"\xec\x01\x97\xa5\x5b\x0c\x99\x62\xd5\x49\xb1\x61\xe9\x6e\x73\x2a\x0e\xe3\xe1\x77\x00\x4f\xe9\x5f\x5d\x61\x20\xbf\x82\xe2\xc0\xea"
|
||
nonce = b"\x9b\x13\x1c\x60\x1e\xfd\x6a\x7c\xc2\xa2\x1c\xd0\x53\x4d\xe8\xd8"
|
||
ps =bytes([0x78, 0x6e, 0x75, 0x70, 0x72, 0x6e, 0x67, 0x04])
|
||
ai1 = b""
|
||
entropy_reseed = b"\x61\x81\x0b\x74\xd2\xed\x76\x36\x5a\xe7\x0e\xe6\x77\x2b\xba\x49\x38\xee\x38\xd8\x19\xec\x1a\x74\x1f\xb3\xff\x4c\x35\x2f\x14\x0c"
|
||
ai_reseed = b""
|
||
ai2 = b""
|
||
random = b"\x7e\xa8\x9c\xe6\x13\xe1\x1b\x5d\xe7\xf9\x79\xe1\x4e\xb0\xda\x4d"
|
||
|
||
drbg = CTR_DRBG(entropy, nonce, ps)
|
||
r1 = drbg.generate(16, ai = ai1)
|
||
drbg.reseed(entropy_reseed, ai = ai_reseed)
|
||
r2 = drbg.generate(16, ai = ai2)
|
||
#assert r2 == random
|
||
print(binascii.hexlify(r1))
|
||
|
||
|
||
|
||
entropy = b"\xa3\xa0\x68\x3a\x84\x12\x51\x36\x11\x3c\x1e\x68\x0a\x18\x4c\x0d\x57\x3d\x24\x1d\x9f\xae\x74\xb7\x28\xe5\x8e\xd1\xf0\x1d\x85\x20"
|
||
nonce = b"\x8d\xb5\x71\x0e\xd2\x87\xd7\x1d\xd2\xf0\x51\x64\x8e\xd5\x63\xc5"
|
||
ps = b""
|
||
ai1 = b""
|
||
entropy_reseed = b"\x2f\xfa\x80\x67\x39\xc0\x10\x06\x62\x8b\x60\x5e\x18\x73\xe3\x02\x23\xc4\x50\x1b\xc6\x4d\xdb\x53\x18\xed\xb6\xfd\xa4\x59\xc8\x88"
|
||
ai_reseed = b""
|
||
ai2 = b"\x51\xa9\x24\x03\x30\xed\x0f\x1e\x8b\x18\x70\xb5\x31\x75\xfc\xf1\x0f\x45\x6e\x4e\x4b\x0b\xbf\x89\xa2\x19\xa5\xcb\x00\x5a\x4c\x14"
|
||
random = b"\x64\x69\x47\x4a\xf0\x67\x02\x13\x44\xb4\x12\xc8\x9d\xa4\x4f\x97"
|
||
|
||
drbg = CTR_DRBG(entropy, nonce, ps)
|
||
r1 = drbg.generate(16, ai = ai1)
|
||
drbg.reseed(entropy_reseed, ai = ai_reseed)
|
||
r2 = drbg.generate(16, ai = ai2)
|
||
#assert r2 == random
|
||
|
||
entropy = binascii.unhexlify("ec0197a55b0c9962d549b161e96e732a0ee3e177004fe95f5d6120bf82e2c0ea")
|
||
nonce = binascii.unhexlify("9b131c601efd6a7cc2a21cd0534de8d8")
|
||
ps = bytes([0x78, 0x6e, 0x75, 0x70, 0x72, 0x6e, 0x67, 0x04])
|
||
reseed = binascii.unhexlify("61810b74d2ed76365ae70ee6772bba4938ee38d819ec1a741fb3ff4c352f140c")
|
||
drbg = CTR_DRBG(entropy, nonce, ps)
|
||
r1 = drbg.generate(16)
|
||
drbg.reseed(reseed)
|
||
r2 = drbg.generate(16)
|
||
print("==========")
|
||
print(binascii.hexlify(r1))
|
||
print(binascii.hexlify(r2))
|
||
|
||
|