mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Update RapydScript
This commit is contained in:
parent
cd99eda4b2
commit
a1ce8f3eaa
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
# License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
# globals: crypto
|
||||
|
||||
@ -19,8 +19,10 @@ def string_to_bytes_slow(string):
|
||||
)
|
||||
return ua
|
||||
|
||||
def as_hex(bytes):
|
||||
return [str.format('{:02x}', x) for x in bytes].join(' ')
|
||||
def as_hex(array, sep=''):
|
||||
num = array.BYTES_PER_ELEMENT or 1
|
||||
fmt = '{:0' + num * 2 + 'x}'
|
||||
return [str.format(fmt, x) for x in array].join(sep)
|
||||
|
||||
def bytes_to_string_decoder(bytes, offset):
|
||||
offset = offset or 0
|
||||
@ -48,8 +50,8 @@ string_to_bytes = string_to_bytes_encoder if type(TextEncoder) is 'function' els
|
||||
bytes_to_string = bytes_to_string_decoder if type(TextDecoder) is 'function' else bytes_to_string_slow
|
||||
|
||||
def increment_counter(c):
|
||||
# c is a Uint8Array
|
||||
for v'var i = c.length; i >= 0; i--':
|
||||
# c must be a Uint8Array of length 16
|
||||
for v'var i = 15; i >= 12; i--':
|
||||
if c[i] is 255:
|
||||
c[i] = 0
|
||||
else:
|
||||
@ -257,7 +259,12 @@ def random_bytes_secure(sz):
|
||||
|
||||
random_bytes = random_bytes_secure if type(crypto) is not 'undefined' and type(crypto.getRandomValues) is 'function' else random_bytes_insecure
|
||||
if random_bytes is random_bytes_insecure:
|
||||
print('WARNING: Using insecure RNG for AES')
|
||||
try:
|
||||
noderandom = require('crypto').randomBytes
|
||||
random_bytes = def(sz):
|
||||
return Uint8Array(noderandom(sz))
|
||||
except:
|
||||
print('WARNING: Using insecure RNG for AES')
|
||||
|
||||
class ModeOfOperation: # {{{
|
||||
|
||||
@ -363,6 +370,7 @@ class GaloisField: # {{{
|
||||
return z
|
||||
|
||||
def ghash(self, x, y):
|
||||
# Corresponds to the XOR + mult_H operation from the paper
|
||||
z = self.wmem
|
||||
z[0] = y[0] ^ x[0]
|
||||
z[1] = y[1] ^ x[1]
|
||||
@ -388,17 +396,14 @@ def typed_array_as_js(x):
|
||||
|
||||
class CBC(ModeOfOperation): # {{{
|
||||
|
||||
def encrypt_bytes(self, bytes, tag_bytes):
|
||||
iv = first_iv = random_bytes(16)
|
||||
mlen = bytes.length + tag_bytes.length + 1
|
||||
padsz = 16 - (mlen % 16)
|
||||
def encrypt_bytes(self, bytes, tag_bytes, iv):
|
||||
iv = first_iv = iv or random_bytes(16)
|
||||
mlen = bytes.length + tag_bytes.length
|
||||
padsz = (16 - (mlen % 16)) % 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)
|
||||
inputbytes.set(tag_bytes)
|
||||
inputbytes.set(bytes, tag_bytes.length)
|
||||
|
||||
offset = 0
|
||||
outputbytes = Uint8Array(inputbytes.length)
|
||||
@ -411,16 +416,9 @@ class CBC(ModeOfOperation): # {{{
|
||||
return {'iv':first_iv, 'cipherbytes':outputbytes}
|
||||
|
||||
def encrypt(self, plaintext, tag):
|
||||
# Encrypt the plaintext (a string) returning an object that contains
|
||||
# the used initialization vector and the encrypted bytes (as
|
||||
# Uint8Arrays). If the optional tag (a string) is present, it is also
|
||||
# encrypted along with plaintext.It can be used to ensure message integrity.
|
||||
# See the __main__ block at the bottom of this file for example usage.
|
||||
return self.encrypt_bytes(string_to_bytes(plaintext), self.tag_as_bytes(tag))
|
||||
|
||||
def decrypt(self, output_from_encrypt, tag):
|
||||
tag_bytes = self.tag_as_bytes(tag)
|
||||
iv, inputbytes = output_from_encrypt.iv, output_from_encrypt.cipherbytes
|
||||
def decrypt_bytes(self, inputbytes, tag_bytes, iv):
|
||||
offset = 0
|
||||
outputbytes = Uint8Array(inputbytes.length)
|
||||
for v'var block = 0; block < inputbytes.length; block += 16':
|
||||
@ -429,14 +427,15 @@ class CBC(ModeOfOperation): # {{{
|
||||
iv, offset = inputbytes, block - 16
|
||||
for v'var i = 0; i < 16; i++':
|
||||
outputbytes[block + i] ^= iv[offset + i]
|
||||
padsz = outputbytes[0]
|
||||
for v'var i = 0; i < tag_bytes.length; i++':
|
||||
if tag_bytes[i] != outputbytes[i+1]:
|
||||
if tag_bytes[i] != outputbytes[i]:
|
||||
raise ValueError('Corrupt message')
|
||||
mlen = outputbytes.length - 1 - padsz - tag_bytes.length
|
||||
mstart = 1 + tag_bytes.length
|
||||
outputbytes = outputbytes.subarray(mstart, mstart + mlen)
|
||||
return bytes_to_string(outputbytes)
|
||||
outputbytes = outputbytes.subarray(tag_bytes.length)
|
||||
return outputbytes
|
||||
|
||||
def decrypt(self, output_from_encrypt, tag):
|
||||
ans = self.decrypt_bytes(output_from_encrypt.cipherbytes, self.tag_as_bytes(tag), output_from_encrypt.iv)
|
||||
return str.rstrip(bytes_to_string(ans), '\0')
|
||||
# }}}
|
||||
|
||||
class CTR(ModeOfOperation): # {{{
|
||||
@ -446,22 +445,22 @@ class CTR(ModeOfOperation): # {{{
|
||||
# using it for bi-directional messaging it is best to use a different
|
||||
# secret key for each direction
|
||||
|
||||
def __init__(self, key, counter):
|
||||
def __init__(self, key, iv):
|
||||
ModeOfOperation.__init__(self, key)
|
||||
self.wmem = Uint8Array(16)
|
||||
self.counter_block = Uint8Array(16)
|
||||
self.counter_block = Uint8Array(iv or 16)
|
||||
if self.counter_block.length != 16:
|
||||
raise ValueError('iv must be 16 bytes long')
|
||||
self.counter_index = 16
|
||||
|
||||
def _crypt(self, bytes):
|
||||
for v'var i = 0; i < bytes.length; i++':
|
||||
for v'var i = 0; i < bytes.length; i++, self.counter_index++':
|
||||
if self.counter_index is 16:
|
||||
self.counter_index = 0
|
||||
self.aes.encrypt(self.counter_block, self.wmem, 0)
|
||||
increment_counter(self.counter_block)
|
||||
bytes[i] ^= self.wmem[self.counter_index]
|
||||
self.counter_index += 1
|
||||
self.counter_index = 16
|
||||
increment_counter(self.counter_block)
|
||||
|
||||
def encrypt(self, plaintext, tag):
|
||||
outbytes = string_to_bytes(plaintext)
|
||||
@ -499,7 +498,7 @@ class CTR(ModeOfOperation): # {{{
|
||||
return bytes_to_string(b, offset)
|
||||
# }}}
|
||||
|
||||
class GCM(ModeOfOperation):
|
||||
class GCM(ModeOfOperation): # {{{
|
||||
|
||||
# See http://web.cs.ucdavis.edu/~rogaway/ocb/gcm.pdf
|
||||
|
||||
@ -514,7 +513,7 @@ class GCM(ModeOfOperation):
|
||||
# Working memory
|
||||
self.J0 = Uint32Array(4)
|
||||
self.wmem = Uint32Array(4)
|
||||
self.out_block = Uint8Array(16)
|
||||
self.byte_block = Uint8Array(16)
|
||||
|
||||
def _create_j0(self, iv):
|
||||
J0 = self.J0
|
||||
@ -532,54 +531,76 @@ class GCM(ModeOfOperation):
|
||||
J0 = self.galois.ghash(J0, tmp)
|
||||
return J0
|
||||
|
||||
def _crypt(self, iv, bytes, additional_data, decrypt):
|
||||
def _start(self, iv, additional_data):
|
||||
J0 = self._create_j0(iv)
|
||||
# Generate initial counter block
|
||||
in_block = J0.slice(0)
|
||||
in_block[3] = (in_block[3] + 1) & 0xFFFFFFFF # increment counter
|
||||
outbytes = Uint8Array(bytes.length)
|
||||
ghash = self.galois.ghash.bind(self.galois)
|
||||
|
||||
# Process additional_data
|
||||
overflow = additional_data.length % 16
|
||||
if overflow:
|
||||
t = Uint8Array(additional_data.length + 16 - overflow)
|
||||
t.set(additional_data)
|
||||
additional_data = t
|
||||
S = Uint32Array(4)
|
||||
while additional_data.length:
|
||||
additional_data = additional_data.subarray(4)
|
||||
convert_to_int32(additional_data, self.wmem, 0, 16)
|
||||
S = ghash(S, self.wmem)
|
||||
|
||||
# Create the ciphertext, encrypting block by block
|
||||
for v'var pos = 0; pos < bytes.length; pos += 16':
|
||||
self.aes.encrypt32(in_block, self.out_block, 0)
|
||||
num = min(16, bytes.length - pos) # noqa: unused-local
|
||||
for v'var i = 0; i < num; i++':
|
||||
outbytes[pos + i] = bytes[pos+i] ^ self.out_block[i]
|
||||
convert_to_int32(self.out_block, self.wmem)
|
||||
S = ghash(S, self.wmem)
|
||||
in_block[3] = (in_block[3] + 1) & 0xFFFFFFFF # increment counter
|
||||
overflow = additional_data.length % 16
|
||||
for v'var i = 0; i < additional_data.length - overflow; i += 16':
|
||||
convert_to_int32(additional_data, self.wmem, i, 16)
|
||||
S = self.galois.ghash(S, self.wmem)
|
||||
if overflow:
|
||||
self.byte_block.fill(0)
|
||||
self.byte_block.set(additional_data.subarray(additional_data.length - overflow))
|
||||
convert_to_int32(self.byte_block, self.wmem)
|
||||
S = self.galois.ghash(S, self.wmem)
|
||||
return J0, in_block, S
|
||||
|
||||
def _finish(self, iv, J0, adata_len, S, outbytes):
|
||||
# Mix the lengths into S
|
||||
lengths = Uint32Array(4)
|
||||
lengths.set(from_64_to_32(additional_data.length * 8))
|
||||
lengths.set(from_64_to_32(bytes.length * 8))
|
||||
S = ghash(S, lengths)
|
||||
lengths.set(from_64_to_32(adata_len * 8))
|
||||
lengths.set(from_64_to_32(outbytes.length * 8), 2)
|
||||
S = self.galois.ghash(S, lengths)
|
||||
|
||||
# Create the tag
|
||||
self.aes.encrypt32(J0, self.out_block, 0)
|
||||
convert_to_int32(self.out_block, self.wmem)
|
||||
self.aes.encrypt32(J0, self.byte_block, 0)
|
||||
convert_to_int32(self.byte_block, self.wmem)
|
||||
tag = Uint32Array(4)
|
||||
for v'var i = 0; i < S.length; i++':
|
||||
tag[i] = S[i] ^ self.wmem[i]
|
||||
return {'iv':iv, 'cipherbytes':outbytes, 'tag':tag}
|
||||
|
||||
def _crypt(self, iv, bytes, additional_data, decrypt):
|
||||
ghash = self.galois.ghash.bind(self.galois)
|
||||
outbytes = Uint8Array(bytes.length)
|
||||
J0, in_block, S = self._start(iv, additional_data)
|
||||
bb = self.byte_block
|
||||
enc = self.aes.encrypt32.bind(self.aes)
|
||||
hash_bytes = bytes if decrypt else outbytes
|
||||
|
||||
# Create the ciphertext, encrypting block by block
|
||||
for v'var i = 0, counter_index = 16; i < bytes.length; i++, counter_index++':
|
||||
if counter_index is 16:
|
||||
# Encrypt counter and increment it
|
||||
enc(in_block, bb, 0)
|
||||
in_block[3] = (in_block[3] + 1) & 0xFFFFFFFF # increment counter
|
||||
counter_index = 0
|
||||
# Output is XOR of encrypted counter with input
|
||||
outbytes[i] = bytes[i] ^ bb[counter_index]
|
||||
if counter_index is 15:
|
||||
# We have completed a block, update the hash
|
||||
convert_to_int32(hash_bytes, self.wmem, i - 15, 16)
|
||||
S = ghash(S, self.wmem)
|
||||
|
||||
# Check if we have a last partial block
|
||||
overflow = outbytes.length % 16
|
||||
if overflow:
|
||||
# partial output block
|
||||
bb.fill(0)
|
||||
bb.set(hash_bytes.subarray(hash_bytes.length - overflow))
|
||||
convert_to_int32(bb, self.wmem)
|
||||
S = ghash(S, self.wmem)
|
||||
|
||||
return self._finish(iv, J0, additional_data.length, S, outbytes)
|
||||
|
||||
def encrypt(self, plaintext, tag):
|
||||
iv = random_bytes(12)
|
||||
return self._crypt(iv, string_to_bytes(plaintext), self.tag_as_bytes(tag))
|
||||
return self._crypt(iv, string_to_bytes(plaintext), self.tag_as_bytes(tag), False)
|
||||
|
||||
def decrypt(self, output_from_encrypt, tag):
|
||||
if output_from_encrypt.tag.length != 4:
|
||||
@ -588,31 +609,4 @@ class GCM(ModeOfOperation):
|
||||
if ans.tag != output_from_encrypt.tag:
|
||||
raise ValueError('Corrupted message')
|
||||
return bytes_to_string(ans.cipherbytes)
|
||||
|
||||
if __name__ == '__main__':
|
||||
text = 'testing a basic roundtrip ø̄ū'
|
||||
|
||||
cbc = CBC()
|
||||
crypted = cbc.encrypt(text)
|
||||
decrypted = cbc.decrypt(crypted)
|
||||
print('CBC Roundtrip:', 'OK' if text is decrypted else 'FAILED')
|
||||
secret_tag = generate_tag()
|
||||
crypted = cbc.encrypt(text, secret_tag)
|
||||
decrypted = cbc.decrypt(crypted, secret_tag)
|
||||
print('CBC Roundtrip with tag:', 'OK' if text is decrypted else 'FAILED')
|
||||
|
||||
ctr = CTR()
|
||||
crypted = ctr.encrypt(text)
|
||||
decrypted = ctr.decrypt(crypted)
|
||||
print('CTR Roundtrip:', 'OK' if text is decrypted else 'FAILED')
|
||||
crypted = ctr.encrypt(text, secret_tag)
|
||||
decrypted = ctr.decrypt(crypted, secret_tag)
|
||||
print('CTR Roundtrip with tag:', 'OK' if text is decrypted else 'FAILED')
|
||||
|
||||
gcm = GCM()
|
||||
crypted = gcm.encrypt(text)
|
||||
decrypted = gcm.decrypt(crypted)
|
||||
print('GCM Roundtrip:', 'OK' if text is decrypted else 'FAILED')
|
||||
crypted = gcm.encrypt(text, secret_tag)
|
||||
decrypted = gcm.decrypt(crypted, secret_tag)
|
||||
print('GCM Roundtrip with tag:', 'OK' if text is decrypted else 'FAILED')
|
||||
# }}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user