Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: FFI botan_cipher_update() for SIV and CCM #3971

Merged
merged 6 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/api_ref/ffi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,8 @@ Symmetric Ciphers

.. cpp:function:: int botan_cipher_is_authenticated(botan_cipher_t cipher)

.. cpp:function:: int botan_cipher_requires_entire_message(botan_cipher_t cipher)

.. cpp:function:: int botan_cipher_get_tag_length(botan_cipher_t cipher, size_t* tag_len)

Write the tag length of the cipher to ``tag_len``. This will be zero for non-authenticated
Expand Down
5 changes: 5 additions & 0 deletions src/lib/ffi/ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ uint32_t botan_ffi_api_version() {
}

int botan_ffi_supports_api(uint32_t api_version) {
// This is the API introduced in 3.4
if(api_version == 20240408) {
return BOTAN_FFI_SUCCESS;
}

// This is the API introduced in 3.2
if(api_version == 20231009) {
return BOTAN_FFI_SUCCESS;
Expand Down
7 changes: 7 additions & 0 deletions src/lib/ffi/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,13 @@ BOTAN_FFI_EXPORT(2, 0) int botan_cipher_get_tag_length(botan_cipher_t cipher, si
*/
BOTAN_FFI_EXPORT(3, 3) int botan_cipher_is_authenticated(botan_cipher_t cipher);

/**
* Returns 1 iff the cipher requires the entire message before any
* encryption or decryption can be performed. No output data will be produced
* in botan_cipher_update() until the final flag is set.
*/
BOTAN_FFI_EXPORT(3, 4) int botan_cipher_requires_entire_message(botan_cipher_t cipher);

/**
* Get the default nonce length of this cipher
*/
Expand Down
17 changes: 11 additions & 6 deletions src/lib/ffi/ffi_cipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,18 +190,19 @@ int botan_cipher_update(botan_cipher_t cipher_obj,
size_t taken = 0, written = 0;

while(input_size >= ud && output_size >= ud) {
// FIXME we can use process here and avoid the copy
copy_mem(mbuf.data(), input, ud);
cipher.update(mbuf);
const size_t bytes_produced = cipher.process(mbuf);

input_size -= ud;
copy_mem(output, mbuf.data(), ud);
input += ud;
taken += ud;

output_size -= ud;
output += ud;
written += ud;
if(bytes_produced > 0) {
copy_mem(output, mbuf.data(), bytes_produced);
output_size -= bytes_produced;
output += bytes_produced;
written += bytes_produced;
}
}

*output_written = written;
Expand Down Expand Up @@ -245,6 +246,10 @@ int botan_cipher_is_authenticated(botan_cipher_t cipher) {
return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return c.authenticated() ? 1 : 0; });
}

int botan_cipher_requires_entire_message(botan_cipher_t cipher) {
return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return c.requires_entire_message() ? 1 : 0; });
}

int botan_cipher_name(botan_cipher_t cipher, char* name, size_t* name_len) {
return BOTAN_FFI_VISIT(cipher, [=](const auto& c) { return write_str_output(name, name_len, c.name()); });
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/ffi/info.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<defines>
FFI -> 20231009
FFI -> 20240408
</defines>

<module_info>
Expand Down
13 changes: 11 additions & 2 deletions src/python/botan3.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from binascii import hexlify
from datetime import datetime

BOTAN_FFI_VERSION = 20230403
BOTAN_FFI_VERSION = 20240408

#
# Base exception for all exceptions raised from this module
Expand Down Expand Up @@ -176,7 +176,10 @@ def ffi_api(fn, args, allowed_errors=None):
ffi_api(dll.botan_cipher_valid_nonce_length, [c_void_p, c_size_t])
ffi_api(dll.botan_cipher_get_tag_length, [c_void_p, POINTER(c_size_t)])
ffi_api(dll.botan_cipher_get_default_nonce_length, [c_void_p, POINTER(c_size_t)])
ffi_api(dll.botan_cipher_is_authenticated, [c_void_p])
ffi_api(dll.botan_cipher_requires_entire_message, [c_void_p])
ffi_api(dll.botan_cipher_get_update_granularity, [c_void_p, POINTER(c_size_t)])
ffi_api(dll.botan_cipher_get_ideal_update_granularity, [c_void_p, POINTER(c_size_t)])
ffi_api(dll.botan_cipher_query_keylen, [c_void_p, POINTER(c_size_t), POINTER(c_size_t)])
ffi_api(dll.botan_cipher_get_keyspec, [c_void_p, POINTER(c_size_t), POINTER(c_size_t), POINTER(c_size_t)])
ffi_api(dll.botan_cipher_set_key, [c_void_p, c_char_p, c_size_t])
Expand Down Expand Up @@ -853,6 +856,11 @@ def update_granularity(self):
_DLL.botan_cipher_get_update_granularity(self.__obj, byref(l))
return l.value

def ideal_update_granularity(self):
l = c_size_t(0)
_DLL.botan_cipher_get_ideal_update_granularity(self.__obj, byref(l))
return l.value

def key_length(self):
kmin = c_size_t(0)
kmax = c_size_t(0)
Expand All @@ -875,7 +883,8 @@ def tag_length(self):
return l.value

def is_authenticated(self):
return self.tag_length() > 0
rc = _DLL.botan_cipher_is_authenticated(self.__obj)
return rc == 1

def valid_nonce_length(self, nonce_len):
rc = _DLL.botan_cipher_valid_nonce_length(self.__obj, nonce_len)
Expand Down
28 changes: 24 additions & 4 deletions src/scripts/test_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,14 +212,31 @@ def test_hash(self):

def test_cipher(self):
for mode in ['AES-128/CTR-BE', 'Serpent/GCM', 'ChaCha20Poly1305', 'AES-128/CBC/PKCS7']:
enc = botan.SymmetricCipher(mode, encrypt=True)
try:
enc = botan.SymmetricCipher(mode, encrypt=True)
except botan.BotanException as e:
raise RuntimeError("Failed to create encrypting cipher for " + mode) from e

if mode == 'AES-128/CTR-BE':
self.assertEqual(enc.algo_name(), 'CTR-BE(AES-128)')
self.assertFalse(enc.is_authenticated())
self.assertEqual(enc.update_granularity(), 1)
self.assertGreater(enc.ideal_update_granularity(), 1)
elif mode == 'Serpent/GCM':
self.assertEqual(enc.algo_name(), 'Serpent/GCM(16)')
else:
self.assertEqual(enc.algo_name(), mode)
self.assertTrue(enc.is_authenticated())
self.assertEqual(enc.update_granularity(), 16)
self.assertGreater(enc.ideal_update_granularity(), 16)
elif mode == 'ChaCha20Poly1305':
self.assertEqual(enc.algo_name(), 'ChaCha20Poly1305')
self.assertTrue(enc.is_authenticated())
self.assertEqual(enc.update_granularity(), 1)
self.assertGreater(enc.ideal_update_granularity(), 1)
elif mode == 'AES-128/CBC/PKCS7':
self.assertEqual(enc.algo_name(), 'AES-128/CBC/PKCS7')
self.assertFalse(enc.is_authenticated())
self.assertEqual(enc.update_granularity(), 16)
self.assertGreater(enc.ideal_update_granularity(), 16)

(kmin, kmax) = enc.key_length()

Expand All @@ -238,7 +255,10 @@ def test_cipher(self):

ct = enc.finish(pt)

dec = botan.SymmetricCipher(mode, encrypt=False)
try:
dec = botan.SymmetricCipher(mode, encrypt=False)
except botan.BotanException as e:
raise RuntimeError("Failed to create decrypting cipher for " + mode) from e
dec.set_key(key)
dec.start(iv)
decrypted = dec.finish(ct)
Expand Down
Loading
Loading