"""
A Cython interface to OpenSSL using the api in signer.c

Cython not only provides syntax closer to Python, 
but can generate code that works with Python 3.x as well as 2.x
"""

"""
RCSid:
	$Id: ossl.pyx,v 1.8 2024/09/16 06:19:10 sjg Exp $

	@(#) Copyright (c) 2013-2024 Simon J. Gerraty

	This file is provided in the hope that it will
	be of use.  There is absolutely NO WARRANTY.
	Permission to copy, redistribute or otherwise
	use this file is hereby granted provided that 
	the above copyright notice and this notice are
	left intact. 
      
	Please send copies of changes and bug-fixes to:
	sjg@crufty.net

"""


from libc.stdint cimport *

cdef extern from "openssl/evp.h":
    ctypedef struct EVP_PKEY

cdef extern char * c_get_error_string "get_error_string" ()
cdef extern int c_use_engine "use_engine" (char *engine)
cdef extern EVP_PKEY * c_load_key "load_key" (char *file)
cdef extern EVP_PKEY * c_load_x509_pubkey "load_x509_pubkey" (char *file)
cdef extern EVP_PKEY * c_load_PUBKEY "load_PUBKEY" (char *file)
cdef extern EVP_PKEY * c_set_rsa_pubkey "set_rsa_pubkey" (unsigned char *ndata,
							  size_t nlen,
							  unsigned char *edata,
							  size_t elen)
cdef extern size_t c_sign_digest_buf "sign_digest_buf" (EVP_PKEY *pkey, char *digest,
                                              unsigned char *mdata, size_t mlen,
                                              unsigned char *buf, size_t bufsz)

cdef extern size_t c_verify_digest "verify_digest" (EVP_PKEY *pkey,
                                                    const char *digest,
                                                    unsigned char *mdata, size_t mlen,
                                                    unsigned char *sdata, size_t slen)


# the Python api
def use_engine(engine):
     """Attempt to load an engine for OpenSSL"""
     return c_use_engine(engine)

def error_string():
    """Get last error from OpenSSL"""
    cdef char * c_string = c_get_error_string()
    cdef bytes py_string = c_string
    return py_string

def load_key(file):
     """Load a private key from named file.

     Any file and key format supported by OpenSSL can be used.
     We have to cast the key to intptr_t since opaque handles
     do not otherwise work.
     """
     if isinstance(file,str):
          utf8 = file.encode('UTF-8')
     else:
          utf8 = file
     return <intptr_t>c_load_key(utf8)

def load_x509_pubkey(file):
     """Load a public key from named file.

     Any file and key format supported by OpenSSL can be used.
     We have to cast the key to intptr_t since opaque handles
     do not otherwise work.
     """
     if isinstance(file,str):
          utf8 = file.encode('UTF-8')
     else:
          utf8 = file
     return <intptr_t>c_load_x509_pubkey(utf8)

def load_pubkey(file):
     return load_x509_pubkey(file)

def load_PUBKEY(file):
     """Load a public key from named file which is not an X.509 certificate.

     Any file and key format supported by OpenSSL can be used.
     We have to cast the key to intptr_t since opaque handles
     do not otherwise work.
     """
     if isinstance(file,str):
          utf8 = file.encode('UTF-8')
     else:
          utf8 = file
     return <intptr_t>c_load_PUBKEY(utf8)

def set_rsa_pubkey(bytes ndata, bytes edata):
    """Populate an RSA pubkey"""

    return <intptr_t>c_set_rsa_pubkey(ndata, len(ndata), edata, len(edata))

def sign_digest(intptr_t pkey, char *digest, bytes mdata):
     """Generate a signature of a digest.

     The key is passed to us as intptr_t and we cast it to
     EVP_PKEY *.
     Any key type and digest supported by OpenSSL can be used.
     """
     cdef unsigned char c_buf[1024]
     cdef size_t blen = 1024
     
     blen = c_sign_digest_buf(<EVP_PKEY *>pkey, digest,
                          mdata, len(mdata), c_buf, blen)
     return <bytes>c_buf[:blen]

def verify_digest(intptr_t pkey, char *digest, bytes mdata, bytes sdata):
     """Verify signature using supplied public key and digest."""
     return c_verify_digest(<EVP_PKEY *>pkey, digest,
                            mdata, len(mdata),
                            sdata, len(sdata))
