
/*---------------------------------------------------------------------------*/
/* certificates.h															 */
/* copyright (c) innovaphone 2017                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

typedef enum {
    CERTIFICATE_OK = 0,
    CERTIFICATE_PARSING_FAILED,
    CERTIFICATE_PUBLIC_KEY_INVALID,
    CERTIFICATE_PRIVATE_KEY_INVALID,
    CERTIFICATE_CHAIN_INVALID,
    CERTIFICATE_LOAD_FAILED
} certificate_error_t;

inline const char * CertificateErrorTypeToStr(certificate_error_t error)
{
    const char * ret = "<unkown>";
    switch (error) {
    case CERTIFICATE_OK: ret = "certificateOk"; break;
    case CERTIFICATE_PARSING_FAILED: ret = "certificateParsingFailed"; break;
    case CERTIFICATE_PUBLIC_KEY_INVALID: ret = "certificatePublicKeyInvalid"; break;
    case CERTIFICATE_PRIVATE_KEY_INVALID: ret = "certificatePrivateKeyInvalid"; break;
    case CERTIFICATE_CHAIN_INVALID: ret = "certificateChainInvalid"; break;
    case CERTIFICATE_LOAD_FAILED: ret = "certificateLoadFailed"; break;
        break;
    }
    return ret;
}

typedef enum {
    CERTIFICATE_PEM = 0
} certificate_type_t;

typedef enum {
    FINGERPRINT_MD5,
    FINGERPRINT_SHA1,
    FINGERPRINT_SHA224,
    FINGERPRINT_SHA256,
    FINGERPRINT_SHA384,
    FINGERPRINT_SHA512
} fingerprint_t;

typedef enum {
    CERTIFICAFTE_KEY_LENGTH_0 = 0,
    CERTIFICAFTE_KEY_LENGTH_2048 = 2048,
    CERTIFICAFTE_KEY_LENGTH_3072 = 3072,
    CERTIFICAFTE_KEY_LENGTH_4096 = 4096
} certificate_key_length_t;

typedef enum {
    CERTIFICAFTE_CSR_TYPE_RSA_2048 = 0,
    CERTIFICAFTE_CSR_TYPE_RSA_3072,
    CERTIFICAFTE_CSR_TYPE_RSA_4096,
    CERTIFICAFTE_CSR_TYPE_EC_SECP256R1,
    CERTIFICAFTE_CSR_TYPE_EC_SECP384R1,
    CERTIFICAFTE_CSR_TYPE_LAST
} certificate_csr_type_t;

inline const char * CertificateCsrTypeToStr(certificate_csr_type_t keyType)
{
    const char * ret = "<unkown>";
    switch (keyType) {
    case CERTIFICAFTE_CSR_TYPE_RSA_2048: ret = "2048"; break;
    case CERTIFICAFTE_CSR_TYPE_RSA_3072: ret = "3072"; break;
    case CERTIFICAFTE_CSR_TYPE_RSA_4096: ret = "4096"; break;
    case CERTIFICAFTE_CSR_TYPE_EC_SECP256R1: ret = "secp256r1"; break;
    case CERTIFICAFTE_CSR_TYPE_EC_SECP384R1: ret = "secp384r1"; break;
    case CERTIFICAFTE_CSR_TYPE_LAST:
        break;
    }
    return ret;
}

inline certificate_csr_type_t CertificateCsrTypeToEnum(const char * csrType)
{
    certificate_csr_type_t ret = CERTIFICAFTE_CSR_TYPE_LAST;
    if (csrType && *csrType) {
        if (strcmp(csrType, CertificateCsrTypeToStr(CERTIFICAFTE_CSR_TYPE_RSA_2048)) == 0) ret = CERTIFICAFTE_CSR_TYPE_RSA_2048;
        else if (strcmp(csrType, CertificateCsrTypeToStr(CERTIFICAFTE_CSR_TYPE_RSA_3072)) == 0) ret = CERTIFICAFTE_CSR_TYPE_RSA_3072;
        else if (strcmp(csrType, CertificateCsrTypeToStr(CERTIFICAFTE_CSR_TYPE_RSA_4096)) == 0) ret = CERTIFICAFTE_CSR_TYPE_RSA_4096;
        else if (strcmp(csrType, CertificateCsrTypeToStr(CERTIFICAFTE_CSR_TYPE_EC_SECP256R1)) == 0) ret = CERTIFICAFTE_CSR_TYPE_EC_SECP256R1;
        else if (strcmp(csrType, CertificateCsrTypeToStr(CERTIFICAFTE_CSR_TYPE_EC_SECP384R1)) == 0) ret = CERTIFICAFTE_CSR_TYPE_EC_SECP384R1;
    }
    return ret;
}

typedef enum {
    CERTIFICAFTE_KEY_TYPE_RSA = 0,
    CERTIFICAFTE_KEY_TYPE_EC
} certificate_key_type_t;

inline const char * CertificateKeyTypeToStr(certificate_key_type_t keyType)
{
    const char * ret = "<unkown type>";
    switch (keyType) {
    case CERTIFICAFTE_KEY_TYPE_RSA: ret = "RSA"; break;
    case CERTIFICAFTE_KEY_TYPE_EC: ret = "EC"; break;
    }
    return ret;
}

inline certificate_key_type_t CertificateKeyTypeToEnum(const char * keyType)
{
    certificate_key_type_t ret = CERTIFICAFTE_KEY_TYPE_RSA;
    if (keyType && *keyType) {
        if (strcmp(keyType, CertificateKeyTypeToStr(CERTIFICAFTE_KEY_TYPE_RSA)) == 0) ret = CERTIFICAFTE_KEY_TYPE_RSA;
        else if (strcmp(keyType, CertificateKeyTypeToStr(CERTIFICAFTE_KEY_TYPE_EC)) == 0) ret = CERTIFICAFTE_KEY_TYPE_EC;
    }
    return ret;
}

class ICertificateSigningRequest {
public:
    static ICertificateSigningRequest * Create(const char * commonName, certificate_key_length_t keyLength);
    static ICertificateSigningRequest * Create(const char * commonName, certificate_csr_type_t csrType);

    virtual ~ICertificateSigningRequest() {};
    virtual void AddDnsName(const char * dnsName) = 0;
    virtual certificate_key_length_t GetKeyLength() = 0;
    virtual certificate_csr_type_t GetKeyType() = 0;
    virtual const char * GetCommonName() = 0;
    virtual const char * GetPrivateKeyPEM() = 0;
    virtual const char * GetRequestPEM() = 0;
};

class ICertificateInformation {
public:
    static ICertificateInformation * Create();

    virtual ~ICertificateInformation() {};
    virtual const char * GetCommonName() = 0;
    virtual const char * GetIssuer() = 0;
    virtual const char * GetChainCertificate(size_t index) = 0;
    virtual const char * GetDnsName(size_t index) = 0;
    virtual const char * GetIpAddress(size_t index) = 0;
    virtual const char * GetFingerprintSHA256() = 0;
    virtual const char * GetSignatureAlgorithm() = 0;
    virtual int GetKeyLength() = 0;
    virtual certificate_key_type_t GetKeyType() = 0;
    virtual size_t GetChainDepth() = 0;
    virtual size_t GetDnsNamesCount() = 0;
    virtual size_t GetIpAddressesCount() = 0;
    virtual long64 GetNotBefore() = 0;
    virtual long64 GetNotAfter() = 0;
};

class ICertificates {
public:
    static certificate_error_t ValidateCertificate(class IInstanceLog * log, const byte * cert, size_t certLen, class ICertificateInformation * info = nullptr, bool privateKeyRequired = true, bool verifyChain = true);
    static bool CreateCertificateSigningRequest(class IInstanceLog * log, class ICertificateSigningRequest * iCsr);
    static byte * ConvertPEMToDER(class IInstanceLog * log, const char * pem, size_t & outLength);
    static char * ConvertDERToPEM(class IInstanceLog * log, const byte * der, size_t derLength);
    virtual byte * CertificateFingerprint(class IInstanceLog * log, const byte * cert, size_t certLen, size_t * length, fingerprint_t type = FINGERPRINT_SHA256) = 0;
    virtual byte * CreateCertificate(class IInstanceLog * log, size_t * certLen, certificate_type_t type = CERTIFICATE_PEM) = 0;
};
