mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Make AES-CBC secure using a secret tag for message authentication
This commit is contained in:
parent
e8912e3a3e
commit
186daa0417
@ -244,13 +244,19 @@ class CBC:
|
|||||||
def __init__(self, key):
|
def __init__(self, key):
|
||||||
self.aes = AES(key or generate_key(32))
|
self.aes = AES(key or generate_key(32))
|
||||||
|
|
||||||
def encrypt_bytes(self, bytes):
|
def encrypt_bytes(self, bytes, tag_bytes):
|
||||||
iv = first_iv = random_bytes(16)
|
iv = first_iv = random_bytes(16)
|
||||||
padsz = 32 - (bytes.length % 16)
|
mlen = bytes.length + tag_bytes.length + 1
|
||||||
|
padsz = 16 - (mlen % 16)
|
||||||
|
inputbytes = Uint8Array(mlen + padsz)
|
||||||
|
inputbytes[0] = padsz
|
||||||
|
if tag_bytes.length:
|
||||||
|
inputbytes.set(tag_bytes, 1)
|
||||||
|
inputbytes.set(bytes, 1 + tag_bytes.length)
|
||||||
|
if padsz:
|
||||||
|
inputbytes.set(random_bytes(padsz), 1 + tag_bytes.length + bytes.length)
|
||||||
|
|
||||||
offset = 0
|
offset = 0
|
||||||
tag = random_bytes(padsz)
|
|
||||||
inputbytes = Uint8Array(bytes.length + padsz)
|
|
||||||
inputbytes.set(tag), inputbytes.set(bytes, tag.length)
|
|
||||||
outputbytes = Uint8Array(inputbytes.length)
|
outputbytes = Uint8Array(inputbytes.length)
|
||||||
for v'var block = 0; block < inputbytes.length; block += 16':
|
for v'var block = 0; block < inputbytes.length; block += 16':
|
||||||
if block > 0:
|
if block > 0:
|
||||||
@ -258,13 +264,14 @@ class CBC:
|
|||||||
for v'var i = 0; i < 16; i++':
|
for v'var i = 0; i < 16; i++':
|
||||||
inputbytes[block + i] ^= iv[offset + i]
|
inputbytes[block + i] ^= iv[offset + i]
|
||||||
self.aes.encrypt(inputbytes, outputbytes, block)
|
self.aes.encrypt(inputbytes, outputbytes, block)
|
||||||
return {'iv':first_iv, 'tag':tag, 'cipherbytes':outputbytes}
|
return {'iv':first_iv, 'cipherbytes':outputbytes}
|
||||||
|
|
||||||
def encrypt(self, plaintext):
|
def encrypt(self, plaintext, tag):
|
||||||
return self.encrypt_bytes(string_to_bytes(plaintext))
|
return self.encrypt_bytes(string_to_bytes(plaintext), string_to_bytes(tag) if tag else Uint8Array(0))
|
||||||
|
|
||||||
def decrypt(self, output_from_encrypt):
|
def decrypt(self, output_from_encrypt, tag):
|
||||||
iv, tag, inputbytes = output_from_encrypt.iv, output_from_encrypt.tag, output_from_encrypt.cipherbytes
|
tag_bytes = string_to_bytes(tag) if tag else Uint8Array(0)
|
||||||
|
iv, inputbytes = output_from_encrypt.iv, output_from_encrypt.cipherbytes
|
||||||
offset = 0
|
offset = 0
|
||||||
outputbytes = Uint8Array(inputbytes.length)
|
outputbytes = Uint8Array(inputbytes.length)
|
||||||
for v'var block = 0; block < inputbytes.length; block += 16':
|
for v'var block = 0; block < inputbytes.length; block += 16':
|
||||||
@ -273,10 +280,14 @@ class CBC:
|
|||||||
iv, offset = inputbytes, block - 16
|
iv, offset = inputbytes, block - 16
|
||||||
for v'var i = 0; i < 16; i++':
|
for v'var i = 0; i < 16; i++':
|
||||||
outputbytes[block + i] ^= iv[offset + i]
|
outputbytes[block + i] ^= iv[offset + i]
|
||||||
for v'var i = 0; i < tag.length; i++':
|
padsz = outputbytes[0]
|
||||||
if tag[i] != outputbytes[i]:
|
for v'var i = 0; i < tag_bytes.length; i++':
|
||||||
|
if tag_bytes[i] != outputbytes[i+1]:
|
||||||
raise ValueError('Corrupt message')
|
raise ValueError('Corrupt message')
|
||||||
return bytes_to_string(outputbytes, tag.length)
|
mlen = outputbytes.length - 1 - padsz - tag_bytes.length
|
||||||
|
mstart = 1 + tag_bytes.length
|
||||||
|
outputbytes = outputbytes.slice(mstart, mstart + mlen)
|
||||||
|
return bytes_to_string(outputbytes)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
cbc = CBC()
|
cbc = CBC()
|
||||||
@ -284,3 +295,7 @@ if __name__ == '__main__':
|
|||||||
crypted = cbc.encrypt(text)
|
crypted = cbc.encrypt(text)
|
||||||
decrypted = cbc.decrypt(crypted)
|
decrypted = cbc.decrypt(crypted)
|
||||||
print('Roundtrip:', 'OK' if text is decrypted else 'FAILED')
|
print('Roundtrip:', 'OK' if text is decrypted else 'FAILED')
|
||||||
|
crypted = cbc.encrypt(text, 'secret')
|
||||||
|
decrypted = cbc.decrypt(crypted, 'secret')
|
||||||
|
print('Roundtrip with tag:', 'OK' if text is decrypted else 'FAILED')
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user