Skip to content

genesis_tool/GenesisCeremonyPlatformWindows.cpp

Namespaces

Name
sgns
sgns::trustedpeer
sgns::trustedpeer::genesis_ceremony_platform

Functions

Name
outcome::result< GenesisCeremony::KeyFileStatus > InspectKeyFile(const std::string & path)
outcome::result< std::string > ReadKeyFile(const std::string & path)
int RestrictKeyFileToCurrentUser(const std::string & path)
int RemoveKeyFile(const std::string & path)
ProtectedInputResult ReadProtectedLine(std::istream & input, std::ostream & output, std::string & line)

Defines

Name
NOMINMAX

Functions Documentation

function InspectKeyFile

outcome::result< GenesisCeremony::KeyFileStatus > InspectKeyFile(
    const std::string & path
)

function ReadKeyFile

outcome::result< std::string > ReadKeyFile(
    const std::string & path
)

function RestrictKeyFileToCurrentUser

int RestrictKeyFileToCurrentUser(
    const std::string & path
)

function RemoveKeyFile

int RemoveKeyFile(
    const std::string & path
)

function ReadProtectedLine

ProtectedInputResult ReadProtectedLine(
    std::istream & input,
    std::ostream & output,
    std::string & line
)

Macros Documentation

define NOMINMAX

#define NOMINMAX 

Source code

#include "trustedpeer/genesis_tool/GenesisCeremonyPlatform.hpp"

#define NOMINMAX
#include <windows.h>
#include <aclapi.h>
#include <io.h>

#include <vector>

namespace sgns::trustedpeer::genesis_ceremony_platform
{
    namespace
    {
        constexpr size_t MAX_KEY_FILE_BYTES = 256;

        bool IsOwnerOnlyDacl( PACL dacl, PSID owner )
        {
            ACL_SIZE_INFORMATION information{};
            if ( !dacl || !owner || !GetAclInformation( dacl, &information, sizeof( information ), AclSizeInformation ) )
                return false;
            for ( DWORD index = 0; index < information.AceCount; ++index )
            {
                void *entry = nullptr;
                if ( !GetAce( dacl, index, &entry ) ) return false;
                const auto *header = static_cast<const ACE_HEADER *>( entry );
                if ( ( header->AceFlags & INHERIT_ONLY_ACE ) != 0 || header->AceType == ACCESS_DENIED_ACE_TYPE )
                    continue;
                if ( header->AceType != ACCESS_ALLOWED_ACE_TYPE ) return false;
                const auto *allow = static_cast<const ACCESS_ALLOWED_ACE *>( entry );
                const auto sid = reinterpret_cast<PSID>( const_cast<DWORD *>( &allow->SidStart ) );
                if ( !EqualSid( sid, owner ) && !IsWellKnownSid( sid, WinLocalSystemSid ) &&
                     !IsWellKnownSid( sid, WinBuiltinAdministratorsSid ) )
                    return false;
            }
            return true;
        }

        GenesisCeremony::Error StatusError( const GenesisCeremony::KeyFileStatus &status )
        {
            const auto problem = GenesisCeremony::KeyFileStatusProblem( status );
            return problem ? *problem : GenesisCeremony::Error::SUCCESS;
        }

        outcome::result<std::vector<BYTE>> CurrentUserToken()
        {
            HANDLE token = nullptr;
            if ( !OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &token ) )
                return outcome::failure( GenesisCeremony::Error::KEY_FILE_IO );
            DWORD bytes = 0;
            GetTokenInformation( token, TokenUser, nullptr, 0, &bytes );
            std::vector<BYTE> user( bytes );
            const bool read = bytes != 0 && GetTokenInformation( token, TokenUser, user.data(), bytes, &bytes );
            CloseHandle( token );
            if ( !read ) return outcome::failure( GenesisCeremony::Error::KEY_FILE_IO );
            return user;
        }

        outcome::result<GenesisCeremony::KeyFileStatus> InspectOpenKeyFile( HANDLE file )
        {
            FILE_ATTRIBUTE_TAG_INFO attributes{};
            if ( !GetFileInformationByHandleEx( file, FileAttributeTagInfo, &attributes, sizeof( attributes ) ) )
                return outcome::failure( GenesisCeremony::Error::KEY_FILE_IO );
            const bool symlink = ( attributes.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) != 0;
            const bool regular = !symlink && ( attributes.FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == 0 &&
                                 GetFileType( file ) == FILE_TYPE_DISK;

            PSECURITY_DESCRIPTOR descriptor = nullptr;
            PSID owner = nullptr;
            PACL dacl = nullptr;
            if ( GetSecurityInfo( file,
                                  SE_FILE_OBJECT,
                                  OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
                                  &owner,
                                  nullptr,
                                  &dacl,
                                  nullptr,
                                  &descriptor ) != ERROR_SUCCESS )
                return outcome::failure( GenesisCeremony::Error::KEY_FILE_IO );

            bool owner_matches = false;
            const auto user = CurrentUserToken();
            if ( user.has_value() )
            {
                const auto *token_user = reinterpret_cast<const TOKEN_USER *>( user.value().data() );
                owner_matches = owner && EqualSid( owner, token_user->User.Sid );
            }
            const bool owner_only = owner_matches && IsOwnerOnlyDacl( dacl, owner );
            LocalFree( descriptor );
            return GenesisCeremony::KeyFileStatus{ true, regular, symlink, owner_matches, owner_only ? 0600U : 0U };
        }

        HANDLE OpenKeyFile( const std::string &path )
        {
            return CreateFileA( path.c_str(),
                                GENERIC_READ,
                                FILE_SHARE_READ,
                                nullptr,
                                OPEN_EXISTING,
                                FILE_FLAG_OPEN_REPARSE_POINT,
                                nullptr );
        }
    } // namespace

    outcome::result<GenesisCeremony::KeyFileStatus> InspectKeyFile( const std::string &path )
    {
        const HANDLE file = OpenKeyFile( path );
        if ( file == INVALID_HANDLE_VALUE ) return outcome::failure( GenesisCeremony::Error::KEY_FILE_IO );
        auto status = InspectOpenKeyFile( file );
        CloseHandle( file );
        return status;
    }

    outcome::result<std::string> ReadKeyFile( const std::string &path )
    {
        const HANDLE file = OpenKeyFile( path );
        if ( file == INVALID_HANDLE_VALUE ) return outcome::failure( GenesisCeremony::Error::KEY_FILE_IO );
        const auto status = InspectOpenKeyFile( file );
        if ( status.has_error() )
        {
            CloseHandle( file );
            return outcome::failure( status.error() );
        }
        const auto status_error = StatusError( status.value() );
        if ( status_error != GenesisCeremony::Error::SUCCESS )
        {
            CloseHandle( file );
            return outcome::failure( status_error );
        }
        std::string value( MAX_KEY_FILE_BYTES + 1, '\0' );
        DWORD bytes_read = 0;
        const BOOL read = ReadFile( file, value.data(), static_cast<DWORD>( value.size() ), &bytes_read, nullptr );
        const BOOL closed = CloseHandle( file );
        if ( !read || !closed || bytes_read > MAX_KEY_FILE_BYTES )
            return outcome::failure( GenesisCeremony::Error::KEY_FILE_IO );
        value.resize( bytes_read );
        TrimLineEnding( value );
        return value;
    }

    int RestrictKeyFileToCurrentUser( const std::string &path )
    {
        const auto user = CurrentUserToken();
        if ( user.has_error() ) return -1;
        const auto *token_user = reinterpret_cast<const TOKEN_USER *>( user.value().data() );

        EXPLICIT_ACCESSA access{};
        access.grfAccessPermissions = GENERIC_READ | GENERIC_WRITE;
        access.grfAccessMode = SET_ACCESS;
        access.grfInheritance = NO_INHERITANCE;
        access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
        access.Trustee.ptstrName = static_cast<LPSTR>( token_user->User.Sid );

        PACL dacl = nullptr;
        if ( SetEntriesInAclA( 1, &access, nullptr, &dacl ) != ERROR_SUCCESS ) return -1;
        const auto result = SetNamedSecurityInfoA( const_cast<LPSTR>( path.c_str() ),
                                                   SE_FILE_OBJECT,
                                                   OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION |
                                                       PROTECTED_DACL_SECURITY_INFORMATION,
                                                   token_user->User.Sid,
                                                   nullptr,
                                                   dacl,
                                                   nullptr );
        LocalFree( dacl );
        return result == ERROR_SUCCESS ? 0 : -1;
    }

    int RemoveKeyFile( const std::string &path )
    {
        return DeleteFileA( path.c_str() ) ? 0 : -1;
    }

    ProtectedInputResult ReadProtectedLine( std::istream &input, std::ostream &output, std::string &line )
    {
        if ( &input != &std::cin )
            return std::getline( input, line ) ? ProtectedInputResult::SUCCESS : ProtectedInputResult::READ_FAILED;
        const HANDLE console = GetStdHandle( STD_INPUT_HANDLE );
        DWORD original = 0;
        if ( ::_isatty( ::_fileno( stdin ) ) == 0 || console == INVALID_HANDLE_VALUE ||
             !GetConsoleMode( console, &original ) ||
             !SetConsoleMode( console, original & ~static_cast<DWORD>( ENABLE_ECHO_INPUT ) ) )
            return ProtectedInputResult::NOT_A_TERMINAL;
        const bool read = static_cast<bool>( std::getline( input, line ) );
        SetConsoleMode( console, original );
        output << '\n';
        return read ? ProtectedInputResult::SUCCESS : ProtectedInputResult::READ_FAILED;
    }
} // namespace sgns::trustedpeer::genesis_ceremony_platform

Updated on 2026-09-25 at 15:46:12 +0000