Repository: randombit/botan
Pull request #2357 (2020-05-15 10:35:08)

Make it possible to DER encode signatures through C API


Patch:
diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h
index a7cd11d25..cf8931acf 100644
--- a/src/lib/ffi/ffi.h
+++ b/src/lib/ffi/ffi.h
@@ -1406,6 +1406,9 @@ BOTAN_PUBLIC_API(2,0) int botan_pk_op_decrypt(botan_pk_op_decrypt_t op,
 /*
 * Signature Generation
 */
+
+#define BOTAN_PUBKEY_DER_FORMAT_SIGNATURE 1
+
 typedef struct botan_pk_op_sign_struct* botan_pk_op_sign_t;
 
 BOTAN_PUBLIC_API(2,0)
diff --git a/src/lib/ffi/ffi_pk_op.cpp b/src/lib/ffi/ffi_pk_op.cpp
index 525f28eeb..2efb297ac 100644
--- a/src/lib/ffi/ffi_pk_op.cpp
+++ b/src/lib/ffi/ffi_pk_op.cpp
@@ -28,7 +28,7 @@ int botan_pk_op_encrypt_create(botan_pk_op_encrypt_t* op,
    if(op == nullptr)
       return BOTAN_FFI_ERROR_NULL_POINTER;
 
-   if(flags != 0)
+   if(flags != 0 && flags != BOTAN_PUBKEY_DER_FORMAT_SIGNATURE)
       return BOTAN_FFI_ERROR_BAD_FLAG;
 
    return ffi_guard_thunk(__func__, [=]() -> int {
@@ -117,13 +117,15 @@ int botan_pk_op_sign_create(botan_pk_op_sign_t* op,
    if(op == nullptr)
       return BOTAN_FFI_ERROR_NULL_POINTER;
 
-   if(flags != 0)
+   if(flags != 0 && flags != BOTAN_PUBKEY_DER_FORMAT_SIGNATURE)
       return BOTAN_FFI_ERROR_BAD_FLAG;
 
    return ffi_guard_thunk(__func__, [=]() -> int {
       *op = nullptr;
 
-      std::unique_ptr<Botan::PK_Signer> pk(new Botan::PK_Signer(safe_get(key_obj), Botan::system_rng(), hash));
+      auto format = (flags & BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) ? Botan::DER_SEQUENCE : Botan::IEEE_1363;
+
+      std::unique_ptr<Botan::PK_Signer> pk(new Botan::PK_Signer(safe_get(key_obj), Botan::system_rng(), hash, format));
       *op = new botan_pk_op_sign_struct(pk.release());
       return BOTAN_FFI_SUCCESS;
       });
@@ -162,12 +164,13 @@ int botan_pk_op_verify_create(botan_pk_op_verify_t* op,
    if(op == nullptr)
       return BOTAN_FFI_ERROR_NULL_POINTER;
 
-   if(flags != 0)
+   if(flags != 0 && flags != BOTAN_PUBKEY_DER_FORMAT_SIGNATURE)
       return BOTAN_FFI_ERROR_BAD_FLAG;
 
    return ffi_guard_thunk(__func__, [=]() -> int {
       *op = nullptr;
-      std::unique_ptr<Botan::PK_Verifier> pk(new Botan::PK_Verifier(safe_get(key_obj), hash));
+      auto format = (flags & BOTAN_PUBKEY_DER_FORMAT_SIGNATURE) ? Botan::DER_SEQUENCE : Botan::IEEE_1363;
+      std::unique_ptr<Botan::PK_Verifier> pk(new Botan::PK_Verifier(safe_get(key_obj), hash, format));
       *op = new botan_pk_op_verify_struct(pk.release());
       return BOTAN_FFI_SUCCESS;
       });
diff --git a/src/python/botan2.py b/src/python/botan2.py
index 4cae19d1a..6ae1bd6da 100755
--- a/src/python/botan2.py
+++ b/src/python/botan2.py
@@ -1170,9 +1170,9 @@ class PKDecrypt(object):
         return outbuf.raw[0:int(outbuf_sz.value)]
 
 class PKSign(object): # pylint: disable=invalid-name
-    def __init__(self, key, padding):
+    def __init__(self, key, padding, der=False):
         self.__obj = c_void_p(0)
-        flags = c_uint32(0) # always zero in this ABI
+        flags = c_uint32(1) if der else c_uint32(0)
         _DLL.botan_pk_op_sign_create(byref(self.__obj), key.handle_(), _ctype_str(padding), flags)
 
     def __del__(self):
@@ -1189,9 +1189,9 @@ class PKSign(object): # pylint: disable=invalid-name
         return outbuf.raw[0:int(outbuf_sz.value)]
 
 class PKVerify(object):
-    def __init__(self, key, padding):
+    def __init__(self, key, padding, der=False):
         self.__obj = c_void_p(0)
-        flags = c_uint32(0) # always zero in this ABI
+        flags = c_uint32(1) if der else c_uint32(0)
         _DLL.botan_pk_op_verify_create(byref(self.__obj), key.handle_(), _ctype_str(padding), flags)
 
     def __del__(self):


Test patch:
diff --git a/src/scripts/test_python.py b/src/scripts/test_python.py
index 73cb29258..2202c0e4b 100644
--- a/src/scripts/test_python.py
+++ b/src/scripts/test_python.py
@@ -351,23 +351,28 @@ ofvkP1EDmpx50fHLawIDAQAB
         self.assertEqual(pub.get_field('public_x'), priv.get_field('public_x'))
         self.assertEqual(pub.get_field('public_y'), priv.get_field('public_y'))
 
-        signer = botan2.PKSign(priv, hash_fn)
+        signer = botan2.PKSign(priv, hash_fn, True)
         signer.update(msg)
         signature = signer.finish(rng)
 
         verifier = botan2.PKVerify(pub, hash_fn)
         verifier.update(msg)
+        #fails because DER/not-DER mismatch
+        self.assertFalse(verifier.check_signature(signature))
+
+        verifier = botan2.PKVerify(pub, hash_fn, True)
+        verifier.update(msg)
         self.assertTrue(verifier.check_signature(signature))
 
         pub_x = pub.get_field('public_x')
         pub_y = priv.get_field('public_y')
         pub2 = botan2.PublicKey.load_ecdsa(group, pub_x, pub_y)
-        verifier = botan2.PKVerify(pub2, hash_fn)
+        verifier = botan2.PKVerify(pub2, hash_fn, True)
         verifier.update(msg)
         self.assertTrue(verifier.check_signature(signature))
 
         priv2 = botan2.PrivateKey.load_ecdsa(group, priv.get_field('x'))
-        signer = botan2.PKSign(priv2, hash_fn)
+        signer = botan2.PKSign(priv2, hash_fn, True)
         # sign empty message
         signature = signer.finish(rng)
 
diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp
index 2e6e5cf6f..8046ab998 100644
--- a/src/tests/test_ffi.cpp
+++ b/src/tests/test_ffi.cpp
@@ -2081,49 +2081,53 @@ class FFI_Unit_Tests final : public Test
 
          std::vector<uint8_t> message(1280), signature;
          TEST_FFI_OK(botan_rng_get, (rng, message.data(), message.size()));
-         botan_pk_op_sign_t signer;
-         if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, loaded_privkey, "EMSA1(SHA-384)", 0)))
+
+         for(uint32_t flags = 0; flags <= 1; ++flags)
             {
-            // TODO: break input into multiple calls to update
-            TEST_FFI_OK(botan_pk_op_sign_update, (signer, message.data(), message.size()));
+            botan_pk_op_sign_t signer;
+            if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, loaded_privkey, "EMSA1(SHA-384)", flags)))
+               {
+               // TODO: break input into multiple calls to update
+               TEST_FFI_OK(botan_pk_op_sign_update, (signer, message.data(), message.size()));
 
-            size_t sig_len;
-            TEST_FFI_OK(botan_pk_op_sign_output_length, (signer, &sig_len));
+               size_t sig_len;
+               TEST_FFI_OK(botan_pk_op_sign_output_length, (signer, &sig_len));
 
-            signature.resize(sig_len);
+               signature.resize(sig_len);
 
-            size_t output_sig_len = signature.size();
-            TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &output_sig_len));
-            signature.resize(output_sig_len);
+               size_t output_sig_len = signature.size();
+               TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &output_sig_len));
+               signature.resize(output_sig_len);
 
-            TEST_FFI_OK(botan_pk_op_sign_destroy, (signer));
-            }
+               TEST_FFI_OK(botan_pk_op_sign_destroy, (signer));
+               }
 
-         botan_pk_op_verify_t verifier = nullptr;
+            botan_pk_op_verify_t verifier = nullptr;
 
-         if(signature.size() > 0 && TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, "EMSA1(SHA-384)", 0)))
-            {
-            TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
-            TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+            if(signature.size() > 0 && TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, "EMSA1(SHA-384)", flags)))
+               {
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
 
-            // TODO: randomize this
-            signature[0] ^= 1;
-            TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
-            TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+               // TODO: randomize this
+               signature[0] ^= 1;
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
 
-            message[0] ^= 1;
-            TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
-            TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+               message[0] ^= 1;
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
 
-            signature[0] ^= 1;
-            TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
-            TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+               signature[0] ^= 1;
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_RC(BOTAN_FFI_INVALID_VERIFIER, botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
 
-            message[0] ^= 1;
-            TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
-            TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+               message[0] ^= 1;
+               TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+               TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
 
-            TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier));
+               TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier));
+               }
             }
 
          TEST_FFI_OK(botan_mp_destroy, (private_scalar));