/* $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;
}