I've recently been working on an interesting problem for detecting clock changes in Windows Mobile 5.0.
The problem I’ve been trying to solve is this: lets say you have a business operation that takes place at date/time x and the data is only valid for y minutes past that point: so that beyond x+y the content or data generated by the business process cannot be accessed any more. Sounds pretty straight forward except that y is likely to be a number of days and the business process (or even the device) will be restarted in that time.
The initial solution might be to take the file time stamp x, add y minutes and ensure its greater than 'now' date/ time. That will work so long as the system date / time remains unchanged, but in Windows Mobile there is no way of knowing if the system date changes... well that’s not entirely true: there is a system notification that occurs when the date changes, but if you register for that event and receive notification, by the time you get the notification the clock has already changed and there is no way of knowing by how much it changed!
I don’t really want to stop people from changing the time on the device, but I need to know how much the time has changed since the content was retrieved in order to accurately calculate when the content expires. So the validity test should really be 'now' < x + y + delta where delta is the cumulative time changes to the clock. So what I am really looking at is some way of implementing a "linear clock" rather than a "secure clock", and a linear clock is something I can use to determine elapsed time.
So how can it be done in Windows Mobile 5.0? The design I’m working to is pretty straight forward and relies on a couple of premises:
1> The clock cannot be altered outside the OS (e.g. through the bootloader)
2> Protection of the clock is only required from UnTrusted applications – e.g. trusted applications are trusted not to mess with the reg keys, and notification events.
The principals of the design are based on the GetTickCount API that returns the number of ticks (milliseconds in WM 5.0) that the OS has been running.
1> Set an event to fire when the system starts
2> Set an event to fire when the clock is changed
3> Set an event to fire every 2^32 milliseconds (minus a small buffer for error)
1> When the system start event fires take the time and tick count and store them in a secure registry location.
2> When the periodic timer fires take the time and tick count and overwrite the secure reg location
3> When the time is changed:
a. Read the stored time and tick count in the registry
b. Add the tick count to the stored time and take the delta with ‘now’
c. Read the existing delta from the registry and add the two together
d. Store the delta back to the registry
An app can then calculate the linear time by adding the delta value held in the registry to the current clock value. Alternatively the application could register for State Notify broker of the delta registry key and then it will receive an event when the clock changes, and again can calculate the linear time.
From a security perspective the SecureClock app needs to be signed and running in the Trusted code group otherwise it cant update the registry.
These are the important bits of my test implementation:
// register for device wake up
if (0==CeRunAppAtEvent(buff, NOTIFICATION_EVENT_WAKEUP))
DEBUGMSG(TRUE, (TEXT("Failed to set Wakeup event in SecureClock register\r\n")));
// Now register for time change
DEBUGMSG(TRUE, (TEXT("Failed to set TimeChange event in SecureClock register\r\n")));
Unregister step – if the whole things needs to be pulled out:
// Clear all entries for this app.
if (0==CeRunAppAtEvent(buff, NOTIFICATION_EVENT_NONE))
DEBUGMSG(TRUE, (TEXT("Failed to clear Wakeup event in SecureClock Unregister\r\n")));
Device startup and periodic timer:
// Clear registry info and write time and system ticks
DWORD ticks = 0;
if (!GetTimeAndTicks(&sysTime, &ticks))
if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\SecureClock"), 0, TEXT("SecureClock DEMO"),REG_OPTION_NON_VOLATILE, 0,0,&hKey, &dispo))
DEBUGMSG(TRUE, (TEXT("Error creating the reg key\r\n")));
// Set run option next execution
// Setup next run time.
// Convert NOW to filetime
DEBUGMSG(TRUE, (TEXT("Failed to convert system time to file time\r\n")));
// build trigger time
__int64 base = 0xFFFF0000; //set it to 2^32 miliseconds - 0xFFFF (allow a 66 second error - timer granulrity is OEM defined but defaults to 10 seconds)
base = base * 10000; // turn the mili's to 100 nano's
// Add Base time to Now
// Stuff the 2 FILETIMEs into their own __int64s.
__int64 t1 = TempNowFileTime.dwHighDateTime;
t1 <<= 32;
t1 |= TempNowFileTime.dwLowDateTime;
// Write the tick count and time (as filetime
if (ERROR_SUCCESS!= RegSetValueEx(hKey, TEXT("TickSync"), 0,REG_DWORD, (BYTE*)&ticks, sizeof(ticks)))
DEBUGMSG(TRUE, (TEXT("Failed to set TickSync\r\n")));
if (ERROR_SUCCESS!= RegSetValueEx(hKey, TEXT("TimeSync"), 0,REG_BINARY, (BYTE*)&t1, sizeof(t1)))
DEBUGMSG(TRUE, (TEXT("Failed to set TimeSync\r\n")));
if (ERROR_SUCCESS!= RegFlushKey(hKey))
DEBUGMSG(TRUE, (TEXT("Failed to flush registry\r\n")));
if (ERROR_SUCCESS!= RegCloseKey(hKey))
DEBUGMSG(TRUE, (TEXT("Failed to close reg key\r\n")));
hKey = 0;
// Add the 64bit ints.
t1 = t1 + base;
// Set it back to the file time
TempNowFileTime.dwHighDateTime = (long)(t1>>32);
TempNowFileTime.dwLowDateTime = (long)(t1);
// Convert back to system time
DEBUGMSG(TRUE, (TEXT("Failed to convert to Systime\r\n")));
DEBUGMSG(TRUE, (TEXT("Failed to call CeRunAppAtTime\r\n")));
And last but not least the time change function:
// Time has changed. Calculate delta and store in the registry
// take new time, now ticks
DWORD NowTicks = 0;
DWORD StoredTicks = 0;
if (!GetTimeAndTicks(&NewTime, &NowTicks))
// calc diff ticks from the registry and now ticks
DWORD size = sizeof(DWORD);
if (ERROR_SUCCESS!=RegQueryValueEx(hKey, TEXT("TickSync"), 0, &type, (BYTE*)&StoredTicks, &size))
DEBUGMSG(TRUE, (TEXT("Failed to read TickSync\r\n")));
size = sizeof(OldTime);
if (ERROR_SUCCESS!=RegQueryValueEx(hKey, TEXT("TimeSync"), 0,&type, (BYTE*)&OldTime, &size))
DEBUGMSG(TRUE, (TEXT("Failed to read TimeSync\r\n")));
DWORD TickDifference = 0;
if (StoredTicks >NowTicks)
TickDifference = NowTicks + (0xFFFFFFFF - StoredTicks);
TickDifference = NowTicks-StoredTicks;
// add diff ticks to stored time
__int64 base = TickDifference;
base = base * 10000; // turn the mili's to 100 nanos
OldTime = OldTime + base;
// calc difference from now time
DEBUGMSG(TRUE, (TEXT("Failed to convert SysTime to FileTime\r\n")));
__int64 t2 = TempNewFileTime.dwHighDateTime;
t2 <<= 32;
t2 |= TempNewFileTime.dwLowDateTime;
__int64 timediff = OldTime-t2; // Calc the difference in nano seconds (SIGNED!)
// store the difference
__int64 regDelta = 0;
size = sizeof(regDelta);
if (ERROR_SUCCESS!=RegQueryValueEx(hKey, TEXT("TimeDelta"), 0, &type, (BYTE*)®Delta, &size))
DEBUGMSG(TRUE, (TEXT("No TimeDelta found - continuing \r\n")));
//NO ERROR - just reset the delta to 0;
regDelta = 0;
if (ERROR_SUCCESS!=RegSetValueEx(hKey, TEXT("TimeDelta"), 0, type, (BYTE*)®Delta, sizeof(regDelta)))
DEBUGMSG(TRUE, (TEXT("Failed to set TimeDelta \r\n")));
// Re-synch the reg keys
If you want to see the whole thing and try it out, grab the ZIP (with a test harness) here
Usual code disclaimer applies.