Hi Everyone,

Android, Apple and Microsoft provide protected storage. Protected Storage 
is OS help with saving secrets. Android and Apple call it KeyChain, while 
Microsoft has called it a few names over the years, including Protected 
Storage, PStore, and DPAPI. There's also a separate MS credential store.

I have some classes that provide sources, filters and sinks for the OS's 
protected storage. They were built over the years and they are copy/paste 
drop-ins when a project has platform security integration requirements. An 
example of DPAPI is shown below.

My question is, should these be added to the library? Would folks use them 
if they were available?

Another option is to place them on the Patch Page so they can be downloaded 
on demand. The PEM Pack is there, and I see it in the wild on occasion.

What do you guys think?

Jeff

====================

Usage:

std::string s1 = "This is a test", s2, s3;
byte pword[] = "super secret";
size_t psize = 12;
DpapiParameters params(pword, psize);

StringSource(s1, true, new DpapiEncryptor(params, new HexEncoder(new 
StringSink(s2))));
StringSource(s2, true, new HexDecoder(new DpapiDecryptor(params, new 
StringSink(s3))));
std::cout << "Message: " << s1 << std::endl;
std::cout << "Entropy: " << pword << std::endl;
std::cout << "Encrypted: " << s2 << std::endl;
std::cout << "Decrypted: " << s3 << std::endl;

s2.clear();
DpapiEncryptor dpapi(params, new HexEncoder(new StringSink(s2)));
dpapi.Put((const byte*)s1.data(), s1.size());
dpapi.MessageEnd();

std::cout << "Test: " << s2;

====================

Classes:

enum DpapiFlags
{
    LOCAL_MACHINE = 0x04,
    UI_FORBIDDEN = 0x01,
    AUDIT = 0x10,
    DEFAULT_FLAGS = LOCAL_MACHINE | UI_FORBIDDEN
};

struct DpapiParameters
{
    DpapiParameters()
        : m_secret(NULLPTR), m_length(0), m_description(NULLPTR), 
m_flags(DEFAULT_FLAGS) {}

    DpapiParameters(const char *password, const char *description = 
NULLPTR, DpapiFlags flags=DpapiFlags::DEFAULT_FLAGS)
        : m_secret(reinterpret_cast<const byte*>(password)), 
m_length(std::strlen(password)), m_description(description), m_flags(flags) 
{}

    DpapiParameters(const byte *secret, size_t secretLength, const char 
*description = NULLPTR, DpapiFlags flags=DpapiFlags::DEFAULT_FLAGS)
        : m_secret(secret), m_length(secretLength), 
m_description(description), m_flags(flags) {}

    const byte * m_secret;
    size_t m_length;
    const char * m_description;
    DpapiFlags m_flags;
};

template <bool T_Encryption>
class DpapiFilter : public FilterWithBufferedInput
{
public:
    virtual ~DpapiFilter() {}

    DpapiFilter(const DpapiParameters &params, BufferedTransformation 
*attachment)
        : m_params(params), FilterWithBufferedInput(256, SIZE_MAX, 0, 
attachment) {}

    size_t Put2(const byte *inString, size_t length, int messageEnd, bool 
blocking);
    void FirstPut(const byte *inString);
    void LastPut(const byte *inString, size_t length);

private:
    const DpapiParameters &m_params;
    ByteQueue m_data;
};

typedef DpapiFilter<true>  DpapiEncryptor;
typedef DpapiFilter<false> DpapiDecryptor;

====================

#include <Windows.h>
#include <Wincrypt.h>
#pragma comment(lib, "crypt32")

template <bool T_Encryption>
void DpapiFilter<T_Encryption>::FirstPut(const byte *inString)
{
    // Buffer incoming message
    if (inString)
        m_data.Put(inString, m_firstSize);
}

template <bool T_Encryption>
void DpapiFilter<T_Encryption>::LastPut(const byte *inString, size_t length)
{
    if (inString && length)
        m_data.Put(inString, length);
}

template <bool T_Encryption>
size_t DpapiFilter<T_Encryption>::Put2(const byte *inString, size_t length, 
int messageEnd, bool blocking)
{
    CRYPTOPP_UNUSED(blocking); CRYPTOPP_UNUSED(messageEnd);
    CRYPTOPP_ASSERT(!messageEnd || (messageEnd && !length));
    CRYPTOPP_COMPILE_ASSERT(DpapiFlags::LOCAL_MACHINE == 
CRYPTPROTECT_LOCAL_MACHINE);
    CRYPTOPP_COMPILE_ASSERT(DpapiFlags::UI_FORBIDDEN == 
CRYPTPROTECT_UI_FORBIDDEN);
    CRYPTOPP_COMPILE_ASSERT(DpapiFlags::AUDIT == CRYPTPROTECT_AUDIT);

    // Buffer incoming message
    if (inString && length)
        m_data.Put(inString, length);

    if (messageEnd-- == 0)
        return 0;

    DATA_BLOB in, ent, out = {0, NULL};
    SecByteBlock temp(static_cast<size_t>(m_data.MaxRetrievable()));
    m_data.Get(temp, temp.size());

    in.pbData = reinterpret_cast<BYTE*>(temp.BytePtr());
    in.cbData = static_cast<DWORD>(temp.SizeInBytes());

    ent.pbData = const_cast<BYTE*>(reinterpret_cast<const 
BYTE*>(m_params.m_secret));
    ent.cbData = static_cast<DWORD>(m_params.m_length);

    if (T_Encryption == true)
    {
        std::wstring desc(m_params.m_description ? 
StringWiden(m_params.m_description) : L"");
        const wchar_t* dptr = desc.empty() ? NULLPTR : desc.c_str();

        BOOL bResult = CryptProtectData(&in, dptr, &ent, NULL, NULL, 
m_params.m_flags, &out);
        if (bResult == FALSE)
        {
            const DWORD dwError = GetLastError();
            throw InvalidArgument("CryptProtectData() call failed with 
error " + IntToString(dwError));
        }
    }
    else
    {
        BOOL bResult = CryptUnprotectData(&in, NULL, &ent, NULL, NULL, 
m_params.m_flags, &out);
        if (bResult == FALSE)
        {
            // It appears ERROR_INVALID_DATA is returned for all crypto 
related
            //  failures, including bad password, tamper data and tamper 
mac.
            const DWORD dwError = GetLastError();
            throw InvalidArgument("CryptUnprotectData() call failed with 
error " + IntToString(dwError));
        }
    }

    AttachedTransformation()->Put2(out.pbData, out.cbData, messageEnd, 
blocking);
    LocalFree(out.pbData);

    AttachedTransformation()->MessageEnd();

    return 0;
}

template class DpapiFilter<true>;
template class DpapiFilter<false>;

====================

And here is what a small message is encrypted and mac'd to:

String: This is a test
Entropy: super secret
Encrypted: 
01000000D08C9DDF0115D1118C7A00C04FC297EB010000009B53121C755FD94384293
2EA67C1EF7F04000000020000000000106600000001000020000000F34C1BA7A9A7B2A0C823E52BF
F69716FDF9390948CE9BF4ED8744426E6F560CC000000000E80000000020000200000002DD36C0E6
6E9BA76B9C605CC371C69380A3EE164DD750372906736CD25290C4710000000E792981D0A56CD0EA
4F307CF40499AE840000000AB2675B720015C1AA414FFB46B707B55F3FA3D9BB3EA4BA14C7BD5E11
EF16A80C75FBFB68F6C79514B125AF3AB6F52B2113D5B0AF4A690A099E56A67770708D1

-- 
-- 
You received this message because you are subscribed to the "Crypto++ Users" 
Google Group.
To unsubscribe, send an email to cryptopp-users-unsubscr...@googlegroups.com.
More information about Crypto++ and this group is available at 
http://www.cryptopp.com.
--- 
You received this message because you are subscribed to the Google Groups 
"Crypto++ Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to cryptopp-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to