/* $Id: t_dibigdDSA.cpp $ */

/*
* Copyright (C) 2026 David Ireland, D.I. Management Services Pty Limited
* <https://di-mgt.com.au/contact/> <https://di-mgt.com.au/bigdigits.html>
* SPDX-License-Identifier: MPL-2.0
*
* Last updated:
* $Date: 2026-05-05 02:02 $
* $Revision: 1.0.1 $
* $Author: dai $
*/

/* EXAMPLE OF THE DSA from APPENDIX 5 FIPS PUB 186-2 
   "DIGITAL SIGNATURE STANDARD (DSS)", 27 January 2000 */

#ifdef NDEBUG
#undef NDEBUG
#endif
#include <iostream>
//#include <string>
#include <stdexcept>
#include <assert.h>
#include "dibigd.hpp"
using std::cout;
using std::endl;
using namespace dibigd;

void do_test() {
    cout << "EXAMPLE OF THE DSA from APPENDIX 5 FIPS PUB 186-2 'DIGITAL SIGNATURE STANDARD'" << endl;
    cout << "==============================================================================" << endl;
    // Initialize parameters from hex (NB no spaces permitted)
    BigDigit p("0x8df2a494492276aa3d25759bb06869cbeac0d83afb8d0cf7"
        "cbb8324f0d7882e5d0762fc5b7210eafc2e9adac32ab7aac"
        "49693dfbf83724c2ec0736ee31c80291");
    BigDigit q("0xc773218c737ec8ee993b4f2ded30f48edace915f");
    BigDigit g("0x626d027839ea0a13413163a55b4cb500299d5522956cefcb"
        "3bff10f399ce2c2e71cb9de5fa24babf58e5b79521925c9c"
        "c42e9f6f464b088cc572af53e6d78802");
    BigDigit x("0x2070b3223dba372fde1c0ffc7b2e3b498b260614");
    BigDigit k("0x358dad571462710f50e254cf1a376b2bdeaadfbf");
    /* M = ASCII form of "abc" (See FIPS PUB 180-1, Appendix A)
        (SHA-1)(M) = <pre-computed value> */
    BigDigit hashm("0xa9993e364706816aba3e25717850c26c9cd0d89d");
    p.printhex("p=");
    //cout << "p is " << (p.isprime() ? "prime" : "NOT PRIME") << endl;
    /* Compute public key y = g^x mod p */
    BigDigit y = g.mod_exp(x, p);
    y.printhex("public key, y=\n");

    /* COMPUTATION BY SIGNER */
    /* (p, q, g) are public, common values
    x is signer's private key
    k is a random integer 0 < k < q
    Keep (x, k) secret
    */
    cout << "Signing message 'abc' using private key x..." << endl;
    /* Compute k' = k^-1 mod q */
    BigDigit k1 = k.mod_inv(q);
    k1.printhex("k^-1=");
    /* Compute y = g^x mod p */
    BigDigit r = g.mod_exp(k, p) % q;
    r.printhex("r=");
    /* Compute s = (k^-1(SHA-1(M) + xr)) mod q */
    BigDigit s = k1.mod_mult(x.mod_mult(r, q) + hashm, q);
    s.printhex("s=");
    assert(r == from_str("0x8bac1ab66410435cb7181f95b16ab97c92b341c0"));
    assert(s == from_str("0x41e2345f1f56df2458f426d155b4ba2db6dcd8c8"));
    cout << "signature is (r,s)." << endl;

    /* VERIFICATION BY RECEIVER */
    /* (p, q, g) are public, common values
    y is signer's public key
    (r,s) = signature(M)
    Receiver receives (M', r', s').
    Receiver recomputes SHA-1(M') for received message M'
    */
    cout << "Verifying signature using public key y..." << endl;
    /* Check 0 < r' < q and 0 < s' < q */
    assert(q > r);
    assert(q > s);
    /* Compute w = s^-1 mod q */
    BigDigit w = s.mod_inv(q);
    w.printhex("w=");
    /* Compute u1 = ((SHA-1(M'))w) mod q */
    BigDigit u1 = hashm.mod_mult(w, q);
    u1.printhex("u1=");
    /* Compute u2 = ((r')w) mod q */
    BigDigit u2 = r.mod_mult(w, q);
    u2.printhex("u2=");
    /* Compute g^u1 mod p */
    BigDigit gg = g.mod_exp(u1, p);
    gg.printhex("g^u1 mod p=\n");
    /* Compute y^u2 mod p */
    BigDigit yy = y.mod_exp(u2, p);
    yy.printhex("y^u2 mod p=\n");
    /* Compute v = (((g^u1)(y^u2)) mod p) mod q */
    BigDigit v = gg.mod_mult(yy, p) % q;
    v.printhex("v=");
    cout << "Signature is verified if v == r" << endl;
    if (v == r) {
        cout << "Signature verified OK." << endl;
    }
    else {
        cout << "Signature verification FAILED!" << endl;
    }
}

int main() {
    /* MSVC memory leak checking stuff */
#if _MSC_VER >= 1100
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);
#endif

    /* Catch any exceptions */
    try {
        do_test();
    }
    catch (const std::exception& e) {
        // Handle standard exceptions with a specific message
        std::cerr << "Standard exception: " << e.what() << std::endl;
    } 
    catch (...) {
        std::cerr << "Caught unknown exception." << std::endl;
    }

    cout << endl << "ALL DONE." << endl << endl;

    return 0;
}