/* $Id: t_dibigdRSASign.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 $
*/

/* Sign data using RSA PKCS-1_5 standard */

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

/* Hard code the RSA key values */
// Alice's public key
BigDigit n = 
    "0xB4F489E85838163E4D9A7F4F92B628"
    "D234E47C2BF84DF03FA5D3BF28BA596C"
    "938012BD2C23B02368EEC1D46B8C5052"
    "5E493BA56A635F0B00DC0E463ADCF416"
    "97E0C82ECEC6C55C17BAB85875995A57"
    "985C52A5407243EF79D0C173FDEBCF80"
    "FF7AC15FB3E13B40C16D99DF8AD38B3E"
    "03C4C234ED760D15410E96CFE09143F6"
    "1B1CF29AC3B691A6164EEB774ABC82A9"
    "E1C415B645901E2EACC938C6B2090B70"
    "B35ABB92D90D051DB0657E480A379C17"
    "56091615F88EB9C7C29FE210B1AAA9A3"
    "57777FD8D291BB40579C37BF37E883CF"
    "75F4CC4A23D1FA9782AE1259C0B49EC1"
    "B5972C97B754CBDBBD08E804CA446256"
    "8905420A90ED734353EAF749E396C24B"
    "3B";
BigDigit e(0x10001);
// Alice's private key
BigDigit d = 
    "0x14A0F60C6F40D6EEFBAB7BB7A765831F"
    "7CEE793B62AA0693F2EF173BE8E1388F"
    "FE1F3A17F5EA3C0C827F6FF1B76EBC07"
    "C9F875EC916F2C7D736E0B27E5B82125"
    "3CF7AFE25C659E65DECAEA315F2DF338"
    "C82441CE282DECCC7F20615296AC8801"
    "02A3ADD47F538BC276A0A9366B29AA04"
    "5AF7657A415024D6A44CF6B3714C0545"
    "08A8212C009AAC744721FA4B78DD7E64"
    "BC0A26ECB0B50C7884E9129F84C12E6F"
    "F2C63474B0638766CE1686EA9F79149D"
    "61B3C1E851123645E569B1369D68A777"
    "2A860D73595C7DF9B0759B3F845B3BC6"
    "69B05CA3269689E1D66CC7A83D7F06A7"
    "891DCF0C3F8B9E0DDC958087BFB95B65"
    "43D35C4506EEA24903C8360C615EA1CD";
BigDigit p = 
    "0xFBE5A858185199CE744AB52137C3FD"
    "AB52A4D8216C71D8F433E46E7C3DCB13"
    "64CF3D1E3E1A64EA0B9AE7476EB4D01E"
    "5B9135C1E85C423ADABB347153EC0FB2"
    "2564E2DFB3E4924DE3445F10F9EE68F2"
    "CAE7E957AE4E70D73599224098C5F68D"
    "643C9DD9CEB963DC73927A7CDBD36AED"
    "1AED904E69E2879C0F175CBDC662294C"
    "F5";
BigDigit q = 
    "0xB7E712A2EDFD89986B6C618DB5B5F4"
    "6E0D49BA12384C5BA6FB1969C8F485C5"
    "F131A3D79B3BA03118BEA48F4C9741E8"
    "F01BFED67075B8FD981E2A8F5EFAC759"
    "01FCDEC63BDEF83BC50FFCF747914EDD"
    "BB27F99D1AB59AB23EC8614756C183DF"
    "5DEB73E6A461D29C0B6D3B7BA2113124"
    "F5645A339C2EAA360A629CD4EE0F3E19"
    "6F";
BigDigit dP = 
    "0x0E4377DFDD28A9BBEE0BD8EBD39B8E7E"
    "A8045B94B0EE5569A69295CB0538BD95"
    "64C2C236A409F8D2C567B93CC4925055"
    "C108393DAE13B5FF72C04A7685147272"
    "F99E7B2A55940F4302391BDDFB29082E"
    "83294C64BB5E5A6157957FF0E6ACCA5E"
    "D5ABDE8C7D24F173BDE49FB356EA6418"
    "B541099FA74B24841A3F8F236FA5850D";
BigDigit dQ = 
    "0x30D9301A171D416F3B198ED015743F78"
    "FC0E995E3D960AC24FF07028F0CA97C9"
    "79660BDF2FF75D133590807FD7ADAA32"
    "1D3D93B84B8CD7CF9033BDE17CBB76E9"
    "10421C65CAD4250C3332FE7C041CEEC8"
    "C7D585E5925785012838AEBA5CACAAA8"
    "47E28161289AC8D8402B8CAB7FE58DEB"
    "A0BC54F9276C0EEEB342F54C46C03801";
BigDigit qInv = 
    "0xB70462890C24DC196A10D172A7273C"
    "D10D69C69DD4EDC8878BC9A78B490D71"
    "3A9EE7B63FDA8F2763D2129EDD467DF3"
    "2CEFD6107D15D345D126D92B7AA02848"
    "F42BF98FBE0B1D95E48045457CED8DAB"
    "FBB04B34D991E3E4F87A0F268E16B407"
    "68919811032C8797480469EF1197CF5E"
    "C815FB76EB4E3402E3459153AE0D7C0A"
    "E7";

void do_test() {
    cout << "Sign data using RSA PKCS-1_5 standard." << endl;
    cout << "======================================" << endl;
    std::string msg ="abc";    /* Default message */

    // Alice signs a message 'abc' using her private key.
    cout << "Alice signs a message..." << endl;
    cout << "Message to sign is '" << msg << "' " << endl;
    size_t nbits = n.bitlen();
    cout << "Alice's RSA key is " << nbits << " bits long" << endl;

    /* Create a PKCS#1 v1.5 EMSA message block in octet format */
    /*
    |<------------(klen bytes)---------->|
    +--+--+-----------+--+---------------+
    |00|01|  PADDING  |00|      T        |
    +--+--+-----------+--+---------------+
    Where the padding is made up of _at least_ eight bytes all of value 0xFF,
    and T is the ASN.1 DER encoding of type DigestInfo of the message digest.
    See <https://datatracker.ietf.org/doc/html/rfc8017#section-9.2>
    */
    /* We use the SHA-256 message digest and hardcode the value of T 
    T = 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H
    H = SHA-256('abc') = ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad
    In practice you will need to compute the message digest separately. For more details see
    <https://di-mgt.com.au/rsa_alg.html#signpkcs1>
    */
    // digestAlgorithm for SHA-256
    std::vector<unsigned char> tvec = Bvec::from_hex("3031300d060960864801650304020105000420");
    // digest value of SHA-256('abc')
    std::vector<unsigned char> hvec = Bvec::from_hex("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
    // Concatenate vectors
    tvec.insert(tvec.end(), hvec.begin(), hvec.end());
    size_t tlen = tvec.size();
    Bvec::print_hex("Message digest SHA-256(msg): ", hvec, "");

    /* How big is the key in octets (8-bit bytes)? */
    size_t klen = (nbits + 7) / 8;
    /* Set up encoded message block of klen bytes */
    std::vector<unsigned char> block(klen);
    /* Set fixed byte values */
    block.at(0) = 0x00;
    block.at(1) = 0x01;
    block.at(klen - tlen - 1) = 0x00;
    // Length of padding string
    size_t padlen = klen - tlen - 3;
    assert(padlen >= 8);
    // Fill with bytes of value 0xFF
    for (size_t i = 0; i < padlen; i++) {
        block.at(i + 2) = 0xFF;
    }
    // Copy the octet values of T
    for (size_t i = 0; i < tlen; i++) {
        block.at(padlen + 3 + i) = tvec.at(i);
    }
    Bvec::print_hex("block, m=", block);

    /* Convert block byte array to a big integer m */
    BigDigit m = from_octets(block);

    /* Sign s = m^d mod n */
    clock_t start = clock();
    BigDigit s = m.mod_exp(d, n);
    clock_t finish = clock();
    s.printhex("s=");
    double interval = (double)(finish - start) / CLOCKS_PER_SEC;
    cout << "Signing by modular exponentiation took " << interval << " seconds" << endl;

    /* Sign using CRT method - Ref: PKCS #1/RFC8017 */
    cout << "Signing by CRT method..." << endl;
    start = clock();
    BigDigit m_1 = m.mod_exp(dP, p);
    BigDigit m_2 = m.mod_exp(dQ, q);
    BigDigit h = qInv.mod_mult(m_1.mod_sub(m_2, p), p);
    BigDigit s1 = m_2 + h * q;
    s1.printhex("s=");
    finish = clock();
    interval = (double)(finish - start) / CLOCKS_PER_SEC;
    cout << "Signing by CRT took " << interval << " seconds" << endl;
    assert(s1 == s);

    /* Check verify m1 = c^e mod n */
    cout << "About to verify..." << endl;
    BigDigit m1 = s.mod_exp(e, n);
    m1.printhex("m'=");
    bool ok = (m1 == m);
    cout << "Verification " << (ok ? "OK" : "FAILED!") << endl;
    assert(m1 == m);

    /* Extract the message digest from the output block of exactly klen bytes */
    block = m1.to_octets(klen);
    Bvec::print_hex(block);
    assert(block.at(0) == 0x00);
    assert(block.at(1) == 0x01);
    size_t i;
    for (i = 2; i < block.size(); i++) {
        // Look for zero separating byte
        if (block.at(i) == 0x00)
            break;
    }
    if (i < klen) {  // Found zero byte
        // Copy the DigestInfo data
        std::vector<unsigned char> digest_info;
        size_t nchars = klen - i - 1;
        for (size_t j = 0; j < nchars; j++) {
            digest_info.push_back(block.at(i + 1 + j));
        }
        cout << "Extracted DigestInfo: \n";
        Bvec::print_hex(digest_info, "");
        cout << "Original T: \n";
        Bvec::print_hex(tvec, "");
        // Check matches the value of T we made earlier
        assert(tvec == digest_info);
    }
    else {
        cout << "ERROR: failed to find DigestInfo in decrypted block" << 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;
}