Skip to content

Commit

Permalink
Expose raw asym key encodings to FFI/Python
Browse files Browse the repository at this point in the history
This is mostly to replace the dedicated functions for kyber's raw
encoding that were added to FFI and Python.

See also: #4366
  • Loading branch information
reneme committed Oct 11, 2024
1 parent c2a759b commit 1bfe01f
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 0 deletions.
12 changes: 12 additions & 0 deletions doc/api_ref/ffi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,12 @@ Public Key Creation, Import and Export

View the unencrypted PEM encoding of the private key

.. cpp:function:: int botan_privkey_view_raw(botan_privkey_t key, \
botan_view_ctx ctx, botan_view_str_fn view)

View the unencrypted canonical raw encoding of the private key
This might not be defined for all key types and throw in that case.

.. cpp:function:: int botan_privkey_export_encrypted(botan_privkey_t key, \
uint8_t out[], size_t* out_len, \
botan_rng_t rng, \
Expand Down Expand Up @@ -1007,6 +1013,12 @@ Public Key Creation, Import and Export

View the PEM encoding of the public key

.. cpp:function:: int botan_pubkey_view_raw(botan_pubkey_t key, \
botan_view_ctx ctx, botan_view_bin_fn view)

View the canonical raw encoding of the public key.
This may not be defined for all public key types and throw.

.. cpp:function:: int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len)

.. cpp:function:: int botan_pubkey_estimated_strength(botan_pubkey_t key, size_t* estimate)
Expand Down
10 changes: 10 additions & 0 deletions doc/api_ref/python.rst
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,11 @@ Public Key
Like ``self.export(True)``
.. py:method:: to_raw()
Exports the key in its canonical raw encoding. This might not be
available for all key types and raise an exception in that case.
.. py:method:: get_field(field_name)
Return an integer field related to the public key. The valid field names
Expand Down Expand Up @@ -392,6 +397,11 @@ Private Key
Return the PEM encoded private key (unencrypted). Like ``self.export(False)``
.. py:method:: to_raw()
Exports the key in its canonical raw encoding. This might not be
available for all key types and raise an exception in that case.
.. py:method:: check_key(rng_obj, strong=True):
Test the key for consistency. If ``strong`` is ``True`` then
Expand Down
11 changes: 11 additions & 0 deletions src/lib/ffi/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,7 @@ BOTAN_FFI_EXPORT(2, 0) int botan_privkey_destroy(botan_privkey_t key);

#define BOTAN_PRIVKEY_EXPORT_FLAG_DER 0
#define BOTAN_PRIVKEY_EXPORT_FLAG_PEM 1
#define BOTAN_PRIVKEY_EXPORT_FLAG_RAW 2

/**
* On input *out_len is number of bytes in out[]
Expand All @@ -1198,6 +1199,11 @@ BOTAN_FFI_EXPORT(3, 0) int botan_privkey_view_der(botan_privkey_t key, botan_vie
*/
BOTAN_FFI_EXPORT(3, 0) int botan_privkey_view_pem(botan_privkey_t key, botan_view_ctx ctx, botan_view_str_fn view);

/**
* View the private key's raw encoding
*/
BOTAN_FFI_EXPORT(3, 6) int botan_privkey_view_raw(botan_privkey_t key, botan_view_ctx ctx, botan_view_bin_fn view);

BOTAN_FFI_EXPORT(2, 8) int botan_privkey_algo_name(botan_privkey_t key, char out[], size_t* out_len);

/**
Expand Down Expand Up @@ -1325,6 +1331,11 @@ BOTAN_FFI_EXPORT(3, 0) int botan_pubkey_view_der(botan_pubkey_t key, botan_view_
*/
BOTAN_FFI_EXPORT(3, 0) int botan_pubkey_view_pem(botan_pubkey_t key, botan_view_ctx ctx, botan_view_str_fn view);

/**
* View the public key's raw encoding
*/
BOTAN_FFI_EXPORT(3, 6) int botan_pubkey_view_raw(botan_pubkey_t key, botan_view_ctx ctx, botan_view_bin_fn view);

BOTAN_FFI_EXPORT(2, 0) int botan_pubkey_algo_name(botan_pubkey_t key, char out[], size_t* out_len);

/**
Expand Down
14 changes: 14 additions & 0 deletions src/lib/ffi/ffi_pkey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ int botan_pubkey_export(botan_pubkey_t key, uint8_t out[], size_t* out_len, uint
return copy_view_bin(out, out_len, botan_pubkey_view_der, key);
} else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM) {
return copy_view_str(out, out_len, botan_pubkey_view_pem, key);
} else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_RAW) {
return copy_view_bin(out, out_len, botan_pubkey_view_raw, key);
} else {
return BOTAN_FFI_ERROR_BAD_FLAG;
}
Expand All @@ -151,11 +153,18 @@ int botan_pubkey_view_pem(botan_pubkey_t key, botan_view_ctx ctx, botan_view_str
key, [=](const auto& k) -> int { return invoke_view_callback(view, ctx, Botan::X509::PEM_encode(k)); });
}

int botan_pubkey_view_raw(botan_pubkey_t key, botan_view_ctx ctx, botan_view_bin_fn view) {
return BOTAN_FFI_VISIT(
key, [=](const auto& k) -> int { return invoke_view_callback(view, ctx, k.raw_public_key_bits()); });
}

int botan_privkey_export(botan_privkey_t key, uint8_t out[], size_t* out_len, uint32_t flags) {
if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER) {
return copy_view_bin(out, out_len, botan_privkey_view_der, key);
} else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM) {
return copy_view_str(out, out_len, botan_privkey_view_pem, key);
} else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_RAW) {
return copy_view_bin(out, out_len, botan_privkey_view_raw, key);
} else {
return BOTAN_FFI_ERROR_BAD_FLAG;
}
Expand All @@ -171,6 +180,11 @@ int botan_privkey_view_pem(botan_privkey_t key, botan_view_ctx ctx, botan_view_s
key, [=](const auto& k) -> int { return invoke_view_callback(view, ctx, Botan::PKCS8::PEM_encode(k)); });
}

int botan_privkey_view_raw(botan_privkey_t key, botan_view_ctx ctx, botan_view_bin_fn view) {
return BOTAN_FFI_VISIT(
key, [=](const auto& k) -> int { return invoke_view_callback(view, ctx, k.raw_private_key_bits()); });
}

int botan_privkey_export_encrypted(botan_privkey_t key,
uint8_t out[],
size_t* out_len,
Expand Down
8 changes: 8 additions & 0 deletions src/python/botan3.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ def ffi_api(fn, args, allowed_errors=None):

ffi_api(dll.botan_privkey_view_der, [c_void_p, c_void_p, VIEW_BIN_CALLBACK])
ffi_api(dll.botan_privkey_view_pem, [c_void_p, c_void_p, VIEW_STR_CALLBACK])
ffi_api(dll.botan_privkey_view_raw, [c_void_p, c_void_p, VIEW_BIN_CALLBACK])

ffi_api(dll.botan_privkey_algo_name, [c_void_p, c_char_p, POINTER(c_size_t)])
ffi_api(dll.botan_privkey_export_encrypted,
Expand All @@ -306,6 +307,7 @@ def ffi_api(fn, args, allowed_errors=None):

ffi_api(dll.botan_pubkey_view_der, [c_void_p, c_void_p, VIEW_BIN_CALLBACK])
ffi_api(dll.botan_pubkey_view_pem, [c_void_p, c_void_p, VIEW_STR_CALLBACK])
ffi_api(dll.botan_pubkey_view_raw, [c_void_p, c_void_p, VIEW_BIN_CALLBACK])

ffi_api(dll.botan_pubkey_algo_name, [c_void_p, c_char_p, POINTER(c_size_t)])
ffi_api(dll.botan_pubkey_check_key, [c_void_p, c_void_p, c_uint32], [-1])
Expand Down Expand Up @@ -1147,6 +1149,9 @@ def to_der(self):
def to_pem(self):
return _call_fn_viewing_str(lambda vc, vfn: _DLL.botan_pubkey_view_pem(self.__obj, vc, vfn))

def to_raw(self):
return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_pubkey_view_raw(self.__obj, vc, vfn))

def view_kyber_raw_key(self):
return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_pubkey_view_kyber_raw_key(self.__obj, vc, vfn))

Expand Down Expand Up @@ -1296,6 +1301,9 @@ def to_der(self):
def to_pem(self):
return _call_fn_viewing_str(lambda vc, vfn: _DLL.botan_privkey_view_pem(self.__obj, vc, vfn))

def to_raw(self):
return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_privkey_view_raw(self.__obj, vc, vfn))

def view_kyber_raw_key(self):
return _call_fn_viewing_vec(lambda vc, vfn: _DLL.botan_privkey_view_kyber_raw_key(self.__obj, vc, vfn))

Expand Down
17 changes: 17 additions & 0 deletions src/scripts/test_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,17 @@ def test_rsa_load_store(self):

self.assertEqual(rsapriv.to_pem(), rsa_priv_pem)

# RSA does not provide a dedicated raw encoding for its keys
with self.assertRaisesRegex(botan.BotanException, r".*Not implemented.*"):
rsapriv.to_raw()

rsapub = rsapriv.get_public_key()
self.assertEqual(rsapub.to_pem(), rsa_pub_pem)

# RSA does not provide a dedicated raw encoding for its keys
with self.assertRaisesRegex(botan.BotanException, r".*Not implemented.*"):
rsapub.to_raw()

rsapub = botan.PublicKey.load(rsa_pub_pem)
self.assertEqual(rsapub.to_pem(), rsa_pub_pem)

Expand Down Expand Up @@ -524,9 +532,11 @@ def test_ecdh(self):

a_pub_pt = a_priv.get_public_key().get_public_point()
b_pub_pt = b_priv.get_public_key().get_public_point()
a_pub_raw = a_priv.get_public_key().to_raw()

self.assertEqual(a_op.public_value(), a_pub_pt)
self.assertEqual(b_op.public_value(), b_pub_pt)
self.assertEqual(a_pub_raw, a_pubv)

salt = a_rng.get(8) + b_rng.get(8)

Expand All @@ -543,6 +553,9 @@ def test_ecdh(self):

self.assertEqual(a_pem, new_a.to_pem())

a_raw = hex_encode(a_priv.to_raw())
self.assertEqual(int(a_raw, base=16), a_priv_x)

def test_rfc7748_kex(self):
rng = botan.RandomNumberGenerator()

Expand Down Expand Up @@ -852,11 +865,15 @@ def test_kyber_raw_keys(self):
b_priv = botan.PrivateKey.load_kyber(b_priv_bits) #2400

privkey_read = b_priv.view_kyber_raw_key()
privkey_generic_raw = b_priv.to_raw()
self.assertEqual(privkey_read, b_priv_bits)
self.assertEqual(privkey_generic_raw, b_priv_bits)

b_pub = b_priv.get_public_key()
pubkey_read = b_pub.view_kyber_raw_key()
pubkey_generic_raw = b_pub.to_raw()
self.assertEqual(pubkey_read, b_pub_bits)
self.assertEqual(pubkey_generic_raw, b_pub_bits)

a_pub = botan.PublicKey.load_kyber(a_pub_bits) #1184
pubkey_read = a_pub.view_kyber_raw_key()
Expand Down
18 changes: 18 additions & 0 deletions src/tests/test_ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3263,15 +3263,21 @@ class FFI_Kyber512_Test final : public FFI_Test {
}

std::vector<uint8_t> privkey_read(1632);
std::vector<uint8_t> privkey_read_raw(1632);
TEST_FFI_OK(botan_privkey_view_kyber_raw_key, (b_priv, privkey_read.data(), botan_ffi_view_u8_fn));
TEST_FFI_OK(botan_privkey_view_raw, (b_priv, privkey_read_raw.data(), botan_ffi_view_u8_fn));
result.test_eq("kyber512 private key", privkey_read, b_priv_bits);
result.test_eq("kyber512 private key raw", privkey_read_raw, b_priv_bits);

std::vector<uint8_t> pubkey_read(800);
std::vector<uint8_t> pubkey_read_raw(800);

botan_pubkey_t b_pub;
TEST_FFI_OK(botan_privkey_export_pubkey, (&b_pub, b_priv));
TEST_FFI_OK(botan_pubkey_view_kyber_raw_key, (b_pub, pubkey_read.data(), botan_ffi_view_u8_fn));
TEST_FFI_OK(botan_pubkey_view_raw, (b_pub, pubkey_read_raw.data(), botan_ffi_view_u8_fn));
result.test_eq("kyber512 public key b", pubkey_read, b_pub_bits);
result.test_eq("kyber512 raw public key b", pubkey_read_raw, b_pub_bits);

botan_pubkey_t a_pub;
TEST_FFI_OK(botan_pubkey_load_kyber, (&a_pub, a_pub_bits.data(), 800));
Expand Down Expand Up @@ -3302,15 +3308,21 @@ class FFI_Kyber768_Test final : public FFI_Test {
}

std::vector<uint8_t> privkey_read(2400);
std::vector<uint8_t> privkey_read_raw(2400);
TEST_FFI_OK(botan_privkey_view_kyber_raw_key, (b_priv, privkey_read.data(), botan_ffi_view_u8_fn));
TEST_FFI_OK(botan_privkey_view_raw, (b_priv, privkey_read_raw.data(), botan_ffi_view_u8_fn));
result.test_eq("kyber768 private key", privkey_read, b_priv_bits);
result.test_eq("kyber768 private key raw", privkey_read_raw, b_priv_bits);

std::vector<uint8_t> pubkey_read(1184);
std::vector<uint8_t> pubkey_read_raw(1184);

botan_pubkey_t b_pub;
TEST_FFI_OK(botan_privkey_export_pubkey, (&b_pub, b_priv));
TEST_FFI_OK(botan_pubkey_view_kyber_raw_key, (b_pub, pubkey_read.data(), botan_ffi_view_u8_fn));
TEST_FFI_OK(botan_pubkey_view_raw, (b_pub, pubkey_read_raw.data(), botan_ffi_view_u8_fn));
result.test_eq("kyber768 public key b", pubkey_read, b_pub_bits);
result.test_eq("kyber768 public key raw b", pubkey_read_raw, b_pub_bits);

botan_pubkey_t a_pub;
TEST_FFI_OK(botan_pubkey_load_kyber, (&a_pub, a_pub_bits.data(), 1184));
Expand Down Expand Up @@ -3341,15 +3353,21 @@ class FFI_Kyber1024_Test final : public FFI_Test {
}

std::vector<uint8_t> privkey_read(3168);
std::vector<uint8_t> privkey_read_raw(3168);
TEST_FFI_OK(botan_privkey_view_kyber_raw_key, (b_priv, privkey_read.data(), botan_ffi_view_u8_fn));
TEST_FFI_OK(botan_privkey_view_raw, (b_priv, privkey_read_raw.data(), botan_ffi_view_u8_fn));
result.test_eq("kyber1024 private key", privkey_read, b_priv_bits);
result.test_eq("kyber1024 private key raw", privkey_read_raw, b_priv_bits);

std::vector<uint8_t> pubkey_read(1568);
std::vector<uint8_t> pubkey_read_raw(1568);

botan_pubkey_t b_pub;
TEST_FFI_OK(botan_privkey_export_pubkey, (&b_pub, b_priv));
TEST_FFI_OK(botan_pubkey_view_kyber_raw_key, (b_pub, pubkey_read.data(), botan_ffi_view_u8_fn));
TEST_FFI_OK(botan_pubkey_view_raw, (b_pub, pubkey_read_raw.data(), botan_ffi_view_u8_fn));
result.test_eq("kyber1024 public key b", pubkey_read, b_pub_bits);
result.test_eq("kyber1024 public key raw b", pubkey_read_raw, b_pub_bits);

botan_pubkey_t a_pub;
TEST_FFI_OK(botan_pubkey_load_kyber, (&a_pub, a_pub_bits.data(), 1568));
Expand Down

0 comments on commit 1bfe01f

Please sign in to comment.