296 lines
9.1 KiB
C++
296 lines
9.1 KiB
C++
/* Copyright (C) 2017 Mike Fleetwood
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* Test PasswordRAMStore
|
|
*
|
|
* NOTE:
|
|
* As well as calling the public API of PasswordRAMStore this unit testing also accesses
|
|
* the private members of PasswordRAMStore and uses knowledge of the implementation,
|
|
* making this white box testing. This is so that the hidden behaviour of zeroing
|
|
* password storing memory before and after use can be tested.
|
|
*
|
|
* WARNING:
|
|
* Each test fixture would normally initialise separate resources to make the tests
|
|
* independent of each other. However the password store is a single long lived shared
|
|
* resource. Therefore, so that each test fixture is independent of all the others, the
|
|
* password store must be returned to it's original state of being empty before each
|
|
* fixture completes.
|
|
*
|
|
* Reference:
|
|
* Google Test, Advanced Guide, Sharing Resources Between Tests in the Same Test Case
|
|
* https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#sharing-resources-between-tests-in-the-same-test-case
|
|
*/
|
|
|
|
#include "PasswordRAMStore.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <string>
|
|
#include <glibmm/ustring.h>
|
|
|
|
namespace GParted
|
|
{
|
|
|
|
// Generate repeatable unique keys
|
|
static const char * gen_key( unsigned int i )
|
|
{
|
|
static char buf[14];
|
|
snprintf( buf, sizeof( buf ), "key%u", i );
|
|
return buf;
|
|
}
|
|
|
|
// Generate repeatable "passwords" exactly 20 characters long
|
|
static const char * gen_passwd( unsigned int i )
|
|
{
|
|
static char buf[21];
|
|
snprintf( buf, sizeof( buf ), "password%03u ", i );
|
|
return buf;
|
|
}
|
|
|
|
static bool mem_is_zero( const char * mem, size_t len )
|
|
{
|
|
while ( len-- > 0 )
|
|
{
|
|
if ( *mem++ != '\0' )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Explicit test fixture class for common setup and sharing of the underlying password
|
|
// store address.
|
|
class PasswordRAMStoreTest : public ::testing::Test
|
|
{
|
|
protected:
|
|
PasswordRAMStoreTest() : looked_up_pw( NULL ) {};
|
|
|
|
static void SetUpTestCase();
|
|
|
|
static void erase_all() { PasswordRAMStore::erase_all(); };
|
|
|
|
static const char * protected_mem;
|
|
|
|
std::string pw;
|
|
const char * looked_up_pw;
|
|
size_t looked_up_len;
|
|
};
|
|
|
|
// Initialise test case class static member.
|
|
const char * PasswordRAMStoreTest::protected_mem = NULL;
|
|
|
|
const size_t ProtectedMemSize = 4096; // [Implementation knowledge: size]
|
|
|
|
// Common test case initialisation recording the underlying password store address.
|
|
void PasswordRAMStoreTest::SetUpTestCase()
|
|
{
|
|
protected_mem = PasswordRAMStore::get_protected_mem();
|
|
ASSERT_TRUE( protected_mem != NULL ) << __func__ << "(): No locked virtual memory for password RAM store";
|
|
}
|
|
|
|
TEST_F( PasswordRAMStoreTest, Initialisation )
|
|
{
|
|
// Test locked memory is initialised with all zeros.
|
|
EXPECT_TRUE( mem_is_zero( protected_mem, ProtectedMemSize ) );
|
|
}
|
|
|
|
TEST_F( PasswordRAMStoreTest, UnknownPasswordLookup )
|
|
{
|
|
// Test lookup of non-existent password fails.
|
|
looked_up_pw = PasswordRAMStore::lookup( "key-unknown" );
|
|
EXPECT_TRUE( looked_up_pw == NULL );
|
|
}
|
|
|
|
TEST_F( PasswordRAMStoreTest, UnknownPasswordErasure )
|
|
{
|
|
// Test erase non-existent password fails.
|
|
EXPECT_FALSE( PasswordRAMStore::erase( "key-unknown" ) );
|
|
}
|
|
|
|
TEST_F( PasswordRAMStoreTest, SinglePassword )
|
|
{
|
|
// Test a single password can be stored, looked up and erased (and zeroed).
|
|
pw = "password";
|
|
EXPECT_TRUE( PasswordRAMStore::store( "key-single", pw.c_str() ) );
|
|
|
|
looked_up_pw = PasswordRAMStore::lookup( "key-single" );
|
|
EXPECT_STREQ( pw.c_str(), looked_up_pw );
|
|
|
|
looked_up_len = strlen( looked_up_pw );
|
|
EXPECT_TRUE( PasswordRAMStore::erase( "key-single" ) );
|
|
EXPECT_TRUE( mem_is_zero( looked_up_pw, looked_up_len ) );
|
|
|
|
EXPECT_TRUE( mem_is_zero( protected_mem, ProtectedMemSize ) );
|
|
}
|
|
|
|
TEST_F( PasswordRAMStoreTest, IdenticalPassword )
|
|
{
|
|
// Test a password can be saved twice with the same key and looked up (and the
|
|
// single password can be erased and zeroed).
|
|
pw = "password";
|
|
EXPECT_TRUE( PasswordRAMStore::store( "key-single", pw.c_str() ) );
|
|
|
|
EXPECT_TRUE( PasswordRAMStore::store( "key-single", pw.c_str() ) );
|
|
|
|
looked_up_pw = PasswordRAMStore::lookup( "key-single" );
|
|
EXPECT_STREQ( pw.c_str(), looked_up_pw );
|
|
|
|
looked_up_len = strlen( looked_up_pw );
|
|
EXPECT_TRUE( PasswordRAMStore::erase( "key-single" ) );
|
|
EXPECT_TRUE( mem_is_zero( looked_up_pw, looked_up_len ) );
|
|
|
|
EXPECT_TRUE( mem_is_zero( protected_mem, ProtectedMemSize ) );
|
|
}
|
|
|
|
TEST_F( PasswordRAMStoreTest, ReplacePassword )
|
|
{
|
|
// Test a password can be saved and looked up, then saved again with a different
|
|
// password using the same key and looked up (and the single replaced password
|
|
// is erased and zeroed).
|
|
pw = "password";
|
|
EXPECT_TRUE( PasswordRAMStore::store( "key-single", pw.c_str() ) );
|
|
|
|
looked_up_pw = PasswordRAMStore::lookup( "key-single" );
|
|
EXPECT_STREQ( pw.c_str(), looked_up_pw );
|
|
|
|
pw = "password2";
|
|
EXPECT_TRUE( PasswordRAMStore::store( "key-single", pw.c_str() ) );
|
|
|
|
looked_up_pw = PasswordRAMStore::lookup( "key-single" );
|
|
EXPECT_STREQ( pw.c_str(), looked_up_pw );
|
|
|
|
looked_up_len = strlen( looked_up_pw );
|
|
EXPECT_TRUE( PasswordRAMStore::erase( "key-single" ) );
|
|
EXPECT_TRUE( mem_is_zero( looked_up_pw, looked_up_len ) );
|
|
|
|
EXPECT_TRUE( mem_is_zero( protected_mem, ProtectedMemSize ) );
|
|
}
|
|
|
|
TEST_F( PasswordRAMStoreTest, OneHundredPasswordsForwards )
|
|
{
|
|
// Test 100, 20 character passwords can be stored, looked up and erased (and
|
|
// zeroed). Passwords are erased forwards (first stored to last stored).
|
|
unsigned int i;
|
|
for ( i = 0 ; i < 100 ; i ++ )
|
|
{
|
|
pw = gen_passwd( i );
|
|
EXPECT_TRUE( PasswordRAMStore::store( gen_key(i), pw.c_str() ) );
|
|
}
|
|
|
|
for ( i = 0 ; i < 100 ; i ++ )
|
|
{
|
|
pw = gen_passwd( i );
|
|
looked_up_pw = PasswordRAMStore::lookup( gen_key(i) );
|
|
EXPECT_STREQ( pw.c_str(), looked_up_pw );
|
|
}
|
|
|
|
for ( i = 0 ; i < 100 ; i ++ )
|
|
{
|
|
pw = gen_passwd( i );
|
|
looked_up_pw = PasswordRAMStore::lookup( gen_key(i) );
|
|
looked_up_len = strlen( looked_up_pw );
|
|
EXPECT_TRUE( PasswordRAMStore::erase( gen_key(i) ) );
|
|
EXPECT_TRUE( mem_is_zero( looked_up_pw, looked_up_len ) );
|
|
}
|
|
|
|
EXPECT_TRUE( mem_is_zero( protected_mem, ProtectedMemSize ) );
|
|
}
|
|
|
|
TEST_F( PasswordRAMStoreTest, OneHundredPasswordsBackwards )
|
|
{
|
|
// Test 100, 20 character passwords can be stored, looked up and erased (and
|
|
// zeroed). Passwords are erased backwards (last stored to first stored).
|
|
unsigned int i;
|
|
for ( i = 0 ; i < 100 ; i ++ )
|
|
{
|
|
pw = gen_passwd( i );
|
|
EXPECT_TRUE( PasswordRAMStore::store( gen_key(i), pw.c_str() ) );
|
|
}
|
|
|
|
for ( i = 0 ; i < 100 ; i ++ )
|
|
{
|
|
pw = gen_passwd( i );
|
|
looked_up_pw = PasswordRAMStore::lookup( gen_key(i) );
|
|
EXPECT_STREQ( pw.c_str(), looked_up_pw );
|
|
}
|
|
|
|
for ( i = 100; i-- > 0 ; )
|
|
{
|
|
pw = gen_passwd( i );
|
|
looked_up_pw = PasswordRAMStore::lookup( gen_key(i) );
|
|
looked_up_len = strlen( looked_up_pw );
|
|
EXPECT_TRUE( PasswordRAMStore::erase( gen_key(i) ) );
|
|
EXPECT_TRUE( mem_is_zero( looked_up_pw, looked_up_len ) );
|
|
}
|
|
|
|
EXPECT_TRUE( mem_is_zero( protected_mem, ProtectedMemSize ) );
|
|
}
|
|
|
|
TEST_F( PasswordRAMStoreTest, LongPassword )
|
|
{
|
|
// Test a 4095 byte password can be stored and looked up (and erased and zeroed).
|
|
// [Implementation knowledge: size]
|
|
pw = std::string( ProtectedMemSize-1, 'X' );
|
|
|
|
EXPECT_TRUE( PasswordRAMStore::store( "key-long", pw.c_str() ) );
|
|
|
|
looked_up_pw = PasswordRAMStore::lookup( "key-long" );
|
|
EXPECT_STREQ( pw.c_str(), looked_up_pw );
|
|
|
|
looked_up_len = strlen( looked_up_pw );
|
|
EXPECT_TRUE( PasswordRAMStore::erase( "key-long" ) );
|
|
EXPECT_TRUE( mem_is_zero( looked_up_pw, looked_up_len ) );
|
|
|
|
EXPECT_TRUE( mem_is_zero( protected_mem, ProtectedMemSize ) );
|
|
}
|
|
|
|
TEST_F( PasswordRAMStoreTest, TooLongPassword )
|
|
{
|
|
// Test a 4096 byte password can't be stored nor looked up or erased.
|
|
// [Implementation knowledge: size]
|
|
std::string pw = std::string( ProtectedMemSize, 'X' );
|
|
|
|
EXPECT_FALSE( PasswordRAMStore::store( "key-too-long", pw.c_str() ) );
|
|
EXPECT_TRUE( mem_is_zero( protected_mem, ProtectedMemSize ) );
|
|
|
|
looked_up_pw = PasswordRAMStore::lookup( "key-too-long" );
|
|
EXPECT_TRUE( looked_up_pw == NULL );
|
|
|
|
EXPECT_FALSE(PasswordRAMStore::erase("key-too-long"));
|
|
EXPECT_TRUE( mem_is_zero( protected_mem, ProtectedMemSize ) );
|
|
}
|
|
|
|
TEST_F( PasswordRAMStoreTest, TotalErasure )
|
|
{
|
|
// Test all passwords are erased (and zeroed using the same code called during
|
|
// password cache destruction).
|
|
unsigned int i;
|
|
for ( i = 0 ; i < 100 ; i ++ )
|
|
{
|
|
pw = gen_passwd( i );
|
|
EXPECT_TRUE( PasswordRAMStore::store( gen_key(i), pw.c_str() ) );
|
|
}
|
|
EXPECT_FALSE( mem_is_zero( protected_mem, ProtectedMemSize ) );
|
|
|
|
PasswordRAMStoreTest::erase_all();
|
|
EXPECT_TRUE( mem_is_zero( protected_mem, ProtectedMemSize ) );
|
|
}
|
|
|
|
} // namespace GParted
|