Skip to content

src/local_secure_storage/impl/Apple.cpp

Namespaces

Name
sgns

Source code

#include "Apple.hpp"

#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>

#include <iostream>

#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>

namespace rj = rapidjson;

namespace sgns
{
    AppleSecureStorage::AppleSecureStorage( std::string identifier ) : identifier_( std::string("SuperGenius_") + identifier ) {}

    outcome::result<rapidjson::Document> AppleSecureStorage::LoadJSON() const
    {
        CFMutableDictionaryRef query = CFDictionaryCreateMutable( kCFAllocatorDefault,
                                                                  0,
                                                                  &kCFTypeDictionaryKeyCallBacks,
                                                                  &kCFTypeDictionaryValueCallBacks );
        CFDictionarySetValue( query, kSecClass, kSecClassGenericPassword );
        CFDictionarySetValue(
            query,
            kSecAttrService,
            CFStringCreateWithCString( kCFAllocatorDefault, identifier_.c_str(), kCFStringEncodingUTF8 ) );
        CFDictionarySetValue(
            query,
            kSecAttrAccount,
            CFStringCreateWithCString( kCFAllocatorDefault, identifier_.c_str(), kCFStringEncodingUTF8 ) );
        CFDictionarySetValue( query, kSecReturnData, kCFBooleanTrue );
        CFDictionarySetValue( query, kSecMatchLimit, kSecMatchLimitOne );

        CFDataRef data   = nullptr;
        auto      status = SecItemCopyMatching( query, reinterpret_cast<CFTypeRef *>( &data ) );

        rj::Document d( rj::Type::kObjectType );

        if ( status == errSecSuccess )
        {
            d.Parse( reinterpret_cast<const char *>( CFDataGetBytePtr( data ) ), CFDataGetLength( data ) );
            CFRelease( data );
        }
        CFRelease( query );

        if ( status == errSecSuccess || status == errSecItemNotFound )
        {
            return d;
        }
        return outcome::failure( std::errc::bad_message );
    }

    outcome::result<void> AppleSecureStorage::SaveJSON( rapidjson::Document document )
    {
        rj::StringBuffer password;
        rj::Writer       writer( password );
        document.Accept( writer );

        CFDataRef binaryData = CFDataCreate( kCFAllocatorDefault,
                                             reinterpret_cast<const UInt8 *>( password.GetString() ),
                                             password.GetLength() );

        CFMutableDictionaryRef query = CFDictionaryCreateMutable( kCFAllocatorDefault,
                                                                  0,
                                                                  &kCFTypeDictionaryKeyCallBacks,
                                                                  &kCFTypeDictionaryValueCallBacks );

        CFDictionarySetValue( query, kSecClass, kSecClassGenericPassword );
        CFDictionarySetValue(
            query,
            kSecAttrService,
            CFStringCreateWithCString( kCFAllocatorDefault, identifier_.c_str(), kCFStringEncodingUTF8 ) );
        CFDictionarySetValue(
            query,
            kSecAttrAccount,
            CFStringCreateWithCString( kCFAllocatorDefault, identifier_.c_str(), kCFStringEncodingUTF8 ) );

        CFMutableDictionaryRef addQuery = CFDictionaryCreateMutableCopy( kCFAllocatorDefault, 0, query );
        CFDictionarySetValue( addQuery, kSecValueData, binaryData );

        auto status = SecItemAdd( addQuery, nullptr );

        if ( status == errSecDuplicateItem )
        {
            CFMutableDictionaryRef updateQuery = CFDictionaryCreateMutable( kCFAllocatorDefault,
                                                                            0,
                                                                            &kCFTypeDictionaryKeyCallBacks,
                                                                            &kCFTypeDictionaryValueCallBacks );
            CFDictionarySetValue( updateQuery, kSecValueData, binaryData );

            status = SecItemUpdate( query, updateQuery );
            CFRelease( updateQuery );
        }

        CFRelease( binaryData );
        CFRelease( addQuery );
        CFRelease( query );

        if ( status == errSecSuccess )
        {
            return outcome::success();
        }
        return outcome::failure( std::errc::bad_message );
    }
}

Updated on 2026-03-04 at 13:10:44 -0800