Skip to content

security/test_message_signing.cpp

Unit tests for MessageSigning — verify, tamper rejection, replay protection. More...

Functions

Name
TEST(MessageSigning , VerifyValidSignature )
TEST(MessageSigning , VerifyTamperedPayload )
TEST(MessageSigning , VerifyWrongKey )
TEST(MessageSigning , VerifyEmptySignature )
TEST(MessageSigning , VerifyTruncatedSignature )
TEST(MessageSigning , VerifyAndStripValid )
TEST(MessageSigning , VerifyAndStripExpiredTimestamp )
TEST(MessageSigning , GenerateNonce_Returns32ByteHexString )
TEST(MessageSigning , GenerateNonce_IsUnique )
TEST(MessageSigning , AttachSignature_ValidPayload_AppendsSigField )
TEST(MessageSigning , VerifyAndStrip_MalformedJson_ReturnsFalse )
TEST(MessageSigning , VerifyAndStrip_EmptyPayload_ReturnsFalse )

Detailed Description

Unit tests for MessageSigning — verify, tamper rejection, replay protection.

Date: 2026-05-28 GSD Executor

Functions Documentation

function TEST

TEST(
    MessageSigning ,
    VerifyValidSignature 
)

function TEST

TEST(
    MessageSigning ,
    VerifyTamperedPayload 
)

function TEST

TEST(
    MessageSigning ,
    VerifyWrongKey 
)

function TEST

TEST(
    MessageSigning ,
    VerifyEmptySignature 
)

function TEST

TEST(
    MessageSigning ,
    VerifyTruncatedSignature 
)

function TEST

TEST(
    MessageSigning ,
    VerifyAndStripValid 
)

function TEST

TEST(
    MessageSigning ,
    VerifyAndStripExpiredTimestamp 
)

function TEST

TEST(
    MessageSigning ,
    GenerateNonce_Returns32ByteHexString 
)

function TEST

TEST(
    MessageSigning ,
    GenerateNonce_IsUnique 
)

function TEST

TEST(
    MessageSigning ,
    AttachSignature_ValidPayload_AppendsSigField 
)

function TEST

TEST(
    MessageSigning ,
    VerifyAndStrip_MalformedJson_ReturnsFalse 
)

function TEST

TEST(
    MessageSigning ,
    VerifyAndStrip_EmptyPayload_ReturnsFalse 
)

Source code

#include "security/message_signing.hpp"
#include "security/node_identity.hpp"
#include <gtest/gtest.h>

#include <iomanip>
#include <sstream>
#include <string>
#include <vector>

using namespace sgns::neoswarm;
using namespace sgns::neoswarm::security;

namespace
{
    std::string PubKeyToHex( const NodeIdentity::PubKey& key )
    {
        std::ostringstream oss;
        for ( auto b : key )
        {
            oss << std::hex << std::setw( 2 ) << std::setfill( '0' ) << static_cast<int>( b );
        }
        return oss.str();
    }
} // namespace

// =======================================================================
// Signature Verification
// =======================================================================

TEST( MessageSigning, VerifyValidSignature )
{
    NodeIdentity ident;
    ASSERT_TRUE( ident.Generate().has_value() );

    const std::string pubKeyHex = PubKeyToHex( ident.GetPublicKey() );
    MessageSigning signer( ident );

    const std::string payload = R"({"msg":"hello"})";
    auto sig = signer.Sign( payload );
    ASSERT_TRUE( sig.has_value() );
    ASSERT_FALSE( sig.value().empty() );

    EXPECT_TRUE( MessageSigning::Verify( payload, sig.value(), pubKeyHex ) );
}

TEST( MessageSigning, VerifyTamperedPayload )
{
    NodeIdentity ident;
    ASSERT_TRUE( ident.Generate().has_value() );

    const std::string pubKeyHex = PubKeyToHex( ident.GetPublicKey() );
    MessageSigning signer( ident );

    const std::string payload = R"({"msg":"hello"})";
    auto sig = signer.Sign( payload );
    ASSERT_TRUE( sig.has_value() );

    const std::string tampered = R"({"msg":"world"})";
    EXPECT_FALSE( MessageSigning::Verify( tampered, sig.value(), pubKeyHex ) );
}

TEST( MessageSigning, VerifyWrongKey )
{
    NodeIdentity identA;
    NodeIdentity identB;
    ASSERT_TRUE( identA.Generate().has_value() );
    ASSERT_TRUE( identB.Generate().has_value() );

    const std::string pubKeyB = PubKeyToHex( identB.GetPublicKey() );
    MessageSigning signerA( identA );

    const std::string payload = R"({"msg":"hello"})";
    auto sig = signerA.Sign( payload );
    ASSERT_TRUE( sig.has_value() );

    EXPECT_FALSE( MessageSigning::Verify( payload, sig.value(), pubKeyB ) );
}

TEST( MessageSigning, VerifyEmptySignature )
{
    NodeIdentity ident;
    ASSERT_TRUE( ident.Generate().has_value() );

    const std::string pubKeyHex = PubKeyToHex( ident.GetPublicKey() );

    EXPECT_FALSE( MessageSigning::Verify( "payload", {}, pubKeyHex ) );
}

TEST( MessageSigning, VerifyTruncatedSignature )
{
    NodeIdentity ident;
    ASSERT_TRUE( ident.Generate().has_value() );

    const std::string pubKeyHex = PubKeyToHex( ident.GetPublicKey() );

    std::vector<uint8_t> truncated = { 0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01 };
    EXPECT_FALSE( MessageSigning::Verify( "payload", truncated, pubKeyHex ) );
}

// =======================================================================
// Nonce + Timestamp Replay Protection
// =======================================================================

TEST( MessageSigning, VerifyAndStripValid )
{
    NodeIdentity ident;
    ASSERT_TRUE( ident.Generate().has_value() );

    const std::string pubKeyHex = PubKeyToHex( ident.GetPublicKey() );
    MessageSigning signer( ident );

    const std::string original = R"({"msg":"hello"})";
    std::string payload = signer.AttachSignature( original );

    EXPECT_NE( payload, original );
    EXPECT_TRUE( MessageSigning::VerifyAndStrip( payload, pubKeyHex ) );
    EXPECT_EQ( payload, original );
}

TEST( MessageSigning, VerifyAndStripExpiredTimestamp )
{
    NodeIdentity ident;
    ASSERT_TRUE( ident.Generate().has_value() );

    const std::string pubKeyHex = PubKeyToHex( ident.GetPublicKey() );
    MessageSigning signer( ident );

    std::string payload = signer.AttachSignature( R"({"msg":"test"})" );

    auto tsPos = payload.rfind( ",\"ts\":" );
    ASSERT_NE( tsPos, std::string::npos );

    uint64_t oldTs = MessageSigning::CurrentTimestampMs() - 61000;
    auto tsVal = payload.find_first_of( "0123456789", tsPos + 6 );
    auto tsEnd = payload.find_first_of( ",}", tsVal );
    ASSERT_NE( tsVal, std::string::npos );
    ASSERT_NE( tsEnd, std::string::npos );

    payload.replace( tsVal, tsEnd - tsVal, std::to_string( oldTs ) );

    EXPECT_FALSE( MessageSigning::VerifyAndStrip( payload, pubKeyHex ) );
}

TEST( MessageSigning, GenerateNonce_Returns32ByteHexString )
{
    std::string nonce = MessageSigning::GenerateNonce();
    EXPECT_EQ( nonce.size(), 64 );
    for ( char c : nonce )
    {
        EXPECT_TRUE( ( c >= '0' && c <= '9' ) || ( c >= 'a' && c <= 'f' ) );
    }
}

TEST( MessageSigning, GenerateNonce_IsUnique )
{
    std::string n1 = MessageSigning::GenerateNonce();
    std::string n2 = MessageSigning::GenerateNonce();
    EXPECT_NE( n1, n2 );
}

TEST( MessageSigning, AttachSignature_ValidPayload_AppendsSigField )
{
    NodeIdentity ident;
    ASSERT_TRUE( ident.Generate().has_value() );
    MessageSigning signer( ident );

    std::string payload = R"({"op":"test","data":"hello"})";
    std::string signedPayload = signer.AttachSignature( payload );
    EXPECT_NE( signedPayload.find( "\"sig\"" ), std::string::npos );
    EXPECT_NE( signedPayload.find( "\"nonce\"" ), std::string::npos );
}

TEST( MessageSigning, VerifyAndStrip_MalformedJson_ReturnsFalse )
{
    NodeIdentity ident;
    ASSERT_TRUE( ident.Generate().has_value() );
    std::string pubKeyHex = PubKeyToHex( ident.GetPublicKey() );

    std::string payload = "not json";
    EXPECT_FALSE( MessageSigning::VerifyAndStrip( payload, pubKeyHex ) );
}

TEST( MessageSigning, VerifyAndStrip_EmptyPayload_ReturnsFalse )
{
    NodeIdentity ident;
    ASSERT_TRUE( ident.Generate().has_value() );
    std::string pubKeyHex = PubKeyToHex( ident.GetPublicKey() );
    std::string payload;

    EXPECT_FALSE( MessageSigning::VerifyAndStrip( payload, pubKeyHex ) );
}

Updated on 2026-07-25 at 22:56:57 +0000