module ItsGenCert_Functions {
    
  // LibIts
  import from IEEE1609dot2BaseTypes language "ASN.1:1997" all;
  import from IEEE1609dot2 language "ASN.1:1997" all;
  import from EtsiTs103097Module language "ASN.1:1997" all;
    
  // LibItsSecurity
  import from LibItsSecurity_EncdecDeclarations all;
  import from LibItsSecurity_TypesAndValues all;
  import from LibItsSecurity_Templates all;
  import from LibItsSecurity_Functions all;
  import from LibItsSecurity_Pixits all;
    
  // AtsGenCert
  import from ItsGenCert_TypeAndValues all;
  import from ItsGenCert_Templates all;
  import from ItsGenCert_Pics all;
    
  function f_generate_signing_keys(
                                   in certificate_params p_certificate_params,
                                   inout certificate_details p_certificate_details
                                   ) return integer {
    if (p_certificate_params.curve == e_nist_p256) {
      f_generate_key_pair_nistp256(p_certificate_details.private_key, p_certificate_details.public_key_x, p_certificate_details.public_key_y);
    } else if (p_certificate_params.curve == e_brainpool_p256) {
      f_generate_key_pair_brainpoolp256(p_certificate_details.private_key, p_certificate_details.public_key_x, p_certificate_details.public_key_y);
    } else if (p_certificate_params.curve == e_brainpool_p256) {
      f_generate_key_pair_brainpoolp384(p_certificate_details.private_key, p_certificate_details.public_key_x, p_certificate_details.public_key_y);
    } else {
      log("f_generate_signing_keys: Unsupported curve");
      return -1;
    }
        
    log("f_generate_signing_keys: p_certificate_details.certificate = ", p_certificate_details.certificate);
        
    return 0;
  } // End of function f_generate_signing_keys
    
  function f_fill_certificate(
                              in certificate_params p_certificate_params,
                              inout certificate_details p_certificate_details
                              ) return integer {
  p_certificate_details.certificate := valueof(
                                               m_etsiTs103097Certificate(
                                                                         m_issuerIdentifier_self(p_certificate_params.hash_algorithm),
                                                                         m_toBeSignedCertificate(
                                                                                                 { name := p_certificate_params.certificate_id },
                                                                                                 m_verificationKeyIndicator_verificationKey(
                                                                                                                                            m_publicVerificationKey_ecdsaNistP256(
                                                                                                                                                                                  m_eccP256CurvePoint_uncompressed(
                                                                                                                                                                                                                   p_certificate_details.public_key_x,
                                                                                                                                                                                                                   p_certificate_details.public_key_y
                                                                                                                                                                                                                   ))),
                                                                                                 p_certificate_params.validity_period,
                                                                                                 p_certificate_params.region,
                                                                                                 p_certificate_params.app_permissions
                                                                                                 )));
    if (not(ispresent(p_certificate_params.issuing_permissions))) { // all required
    p_certificate_details.certificate.toBeSigned.certIssuePermissions := { valueof(m_psidGroupPermissions(m_subjectPermissions_all)) };
    } else {
      // TODO
      log("f_fill_certificate: To be done");
      return -1;
    }
        
    log("f_fill_certificate: p_certificate_details.certificate = ", p_certificate_details.certificate);
        
    return 0;
  } // End of function f_fill_certificate
    
  function f_generate_certificate(
                                  in certificate_params p_certificate_params,
                                  inout certificate_details p_certificate_details
                                  ) return integer {
    var bitstring v_enc_msg;
    var octetstring v_signature;
        
    // Encode it ==> Get octetstring
  v_enc_msg := encvalue(p_certificate_details.certificate.toBeSigned);
        
    // Sign the certificate using ECDSA/SHA-256 (NIST p-256)
    if (p_certificate_params.curve == e_nist_p256) {
    v_signature := f_signWithEcdsaNistp256WithSha256(bit2oct(v_enc_msg), p_certificate_details.private_key);
    p_certificate_details.certificate.signature_ := valueof(
                                                            m_signature_ecdsaNistP256(
                                                                                      m_ecdsaP256Signature(
                                                                                                           m_eccP256CurvePoint_x_only(
                                                                                                                                      substr(v_signature, 0, 32)
                                                                                                                                      ),
                                                                                                           substr(v_signature, 32, 32)
                                                                                                           )
                                                                                      ));
    } else if (p_certificate_params.curve == e_brainpool_p256) {
    v_signature := f_signWithEcdsaBrainpoolp256WithSha256(bit2oct(v_enc_msg), p_certificate_details.private_key);
    p_certificate_details.certificate.signature_ := valueof(
                                                            m_signature_ecdsaBrainpoolP256r1(
                                                                                             m_ecdsaP256Signature(
                                                                                                                  m_eccP256CurvePoint_x_only(
                                                                                                                                             substr(v_signature, 0, 32)
                                                                                                                                             ),
                                                                                                                  substr(v_signature, 32, 32)
                                                                                                                  )
                                                                                             ));
    } else if (p_certificate_params.curve == e_brainpool_p256) {
    v_signature := f_signWithEcdsaBrainpoolp384WithSha384(bit2oct(v_enc_msg), p_certificate_details.private_key);
    p_certificate_details.certificate.signature_ := valueof(m_signature_ecdsaBrainpoolP384r1(
                                                                                             m_ecdsaP384Signature(
                                                                                                                  m_eccP384CurvePoint_x_only(
                                                                                                                                             substr(v_signature, 0, 48)
                                                                                                                                             ),
                                                                                                                  substr(v_signature, 48, 48)
                                                                                                                  )
                                                                                             ));
    } else {
      log("f_generate_certificate: Unsupported curve");
      return -1;
    }
        
    log("f_generate_certificate: p_certificate_details = ", p_certificate_details);
        
    return 0;
  } // End of function f_generate_certificate
    
  function f_finalyse_certificate(
                                  in certificate_params p_certificate_params,
                                  in certificate_details_list p_certificate_details_list,
                                  inout certificate_details p_certificate_details
                                  ) return integer {
    // Set the issuer
    if (p_certificate_params.certificate_id != p_certificate_params.signed_by) { 
      // Find the issuer in the list
      var boolean v_found := false;
      for (var integer v_counter := 0; v_counter < lengthof(p_certificate_details_list); v_counter := v_counter + 1) {
        if (p_certificate_details_list[v_counter].certificate_id == p_certificate_params.signed_by) {
          v_found := true;
          p_certificate_details.issuer := p_certificate_details_list[v_counter].hashid8;
          if (p_certificate_params.hash_algorithm == sha256) {
            p_certificate_details.certificate.issuer.sha256AndDigest := p_certificate_details_list[v_counter].hashid8;
          } else if (p_certificate_params.hash_algorithm == sha384) {
            p_certificate_details.certificate.issuer.sha384AndDigest := p_certificate_details_list[v_counter].hashid8;
          } else {
            log("f_finalyse_certificate: Unsupported hash algorithm");
            return -1;
          }
          break;
        }
      } // End of 'for' statement
      if (v_found == false) {
        log("f_finalyse_certificate: issuer not found");
        return -1;
      }
    }
    // Encode the certificate
    p_certificate_details.enc_cert := bit2oct(encvalue(p_certificate_details.certificate));
    // Calculate the HashedId8
    if (p_certificate_params.hash_algorithm == sha256) {
      p_certificate_details.hashid8 := f_HashedId8FromSha256(f_hashWithSha256(p_certificate_details.enc_cert));
    } else {
      p_certificate_details.hashid8 := f_HashedId8FromSha384(f_hashWithSha384(p_certificate_details.enc_cert));
    }
    if (p_certificate_params.certificate_id == p_certificate_params.signed_by) { // Root certificate
      log("f_finalyse_certificate: Root certificate");
      p_certificate_details.issuer := p_certificate_details.hashid8
      }
        
    log("f_finalyse_certificate: p_certificate_details = ", p_certificate_details);
        
    return 0;
  } // End of function f_finalyse_certificate
    
  function f_store_certificates(
                                in certificate_details_list p_certificate_details_list
                                ) return integer {
    fx_loadCertificates(PX_CERTIFICATE_POOL_PATH, "certificates");
    for (var integer v_counter := 0; v_counter < lengthof(p_certificate_details_list); v_counter := v_counter + 1) {
      fx_store_certificate(
                           p_certificate_details_list[v_counter].certificate_id,
                           p_certificate_details_list[v_counter].enc_cert,
                           p_certificate_details_list[v_counter].private_key,
                           p_certificate_details_list[v_counter].public_key_x,
                           p_certificate_details_list[v_counter].public_key_y,
                           p_certificate_details_list[v_counter].hashid8,
                           p_certificate_details_list[v_counter].issuer);
    } // End of 'for' statement
    
    return 0;
  } // End of function f_store_certificates
  
} // End of module ItsGenCert_Functions 
