/*---------------------------------------------------------------------------*/
/* jwk.h                                                                     */
/* Copyright (c) innovaphone 2022                                            */
/* Authors: Adriaan Nieß                                                     */
/*                                                                           */
/* JSON Web Key (JWK). See RFC-7517                                          */
/*---------------------------------------------------------------------------*/

#ifndef _JWK_H_
#define _JWK_H_

NAMESPACE_BEGIN

enum jwa_alg {
    JWA_NONE,
    JWA_HS256,
    JWA_HS384,
    JWA_HS512,
    JWA_RS256,
    JWA_RS384,
    JWA_RS512,
    JWA_ES256,
    JWA_ES384,
    JWA_ES512,
    JWA_PS256,
    JWA_PS384,
    JWA_PS512,
    JWA_UNKNOWN
};

class jwa {
public:
    static const char * alg_to_str(jwa_alg alg);
    static jwa_alg alg_from_str(const char * str);
};

class jose {
public:
    static dword create_jws(const char * jws_header, const char * jws_protected_header, const char * jws_payload, class jwk * jwk, jwa_alg alg, char * jws);
    static bool verify_jws(const char * jws, class jwk * jwk, jwa_alg alg);
    static bool parse_jws(const char * jws, char * jws_header, char * jws_protected_header, char * jws_payload);

    // static bool encrypte_jwe(...);
    // static bool decrypt_jwe(...);

    static dword encode_compact_jws(const char * jws, char * compact_jws);
    static dword decode_compact_jws(const char * compact_jws, char * jws);
};

/** Key type */
enum jwk_key {
    JWK_KTY_NONE,
    JWK_KTY_EC,
    JWK_KTY_RSA,
    JWK_KTY_UNKNOWN
};

/** Elliptic curve type */
enum jwk_crv {
    JWK_CRV_NONE,
    JWK_CRV_P256,
    JWK_CRV_UNKNOWN
};

/** Usage type */
enum jwk_use {
    JWK_USE_NONE,
    JWK_USE_SIG,
    JWK_USE_ENC
};

/**
 * JWK base class
 *
 * Supported algorithmes: ES256
 */
class jwk {
public:
    jwk_key kty;
    jwk_use use;
    jwk_crv crv;
    jwa_alg alg;
    
    // kty attribute is used as discriminator
    union {
        struct {
            char x[32];
            char y[32];
            char d[32];
        } p256;
        struct {
            char n[256];
            char e[3];
            char d[256];
            char p[256];
            char q[256];
            char dp[256];
            char dq[256];
            char qi[256];
        } rs256;
    } params;

    static bool is_algorithm_supported(jwa_alg alg);

    jwk();
    ~jwk();

    // Encode/decode to/from strings
    bool decode(const char * buffer);
    dword encode(char * buffer, bool include_optional_parameters);
    dword encode_public_key(char * buffer, bool include_optional_parameters);
    dword encode_private_key(char * buffer, bool include_optional_parameters);

    // Encode/decode to/from JSON objects
    bool decode(json_io* json, word base, const char * name);
    dword encode(json_io* json, word base, const char * name, bool include_optional_parameters);
    dword encode_public_key(json_io* json, word base, const char * name, bool include_optional_parameters);
    dword encode_private_key(json_io* json, word base, const char * name, bool include_optional_parameters);

    void clear();

    // TODO: document that signature is binary!
    bool sign(char * signature, const char * msg, size_t msg_len);
    bool sign(char * signature, const char * msg, size_t msg_len, jwa_alg alg);

    // TODO: document that signature is binary!
    bool verify(const char * signature, const char * msg, size_t msg_len);
    bool verify(const char * signature, const char * msg, size_t msg_len, jwa_alg alg);

    /** Return the size of signatures to be generated by this JWK in bytes. */
    dword get_signature_size();

    // Initialize/generate keys
    void gen_es256_keys();
    void set_es256_keys(const byte * params);
   // void gen_rs256_keys();
private:
    dword encode_impl(char * buffer, bool serialize_public_key, bool serialize_private_key, bool include_optional_parameters);
    dword encode_impl(json_io* json, word base, const char * name, bool serialize_public_key, bool serialize_private_key, bool include_optional_parameters);
    bool sign_es256(char * signature, const char * msg, size_t msg_len);
    bool verify_es256(const char * signature, const char * msg, size_t msg_len);
};

NAMESPACE_END

#endif
