Sunday, February 13, 2005

I see requests for CF encryption examples on a regular basis in the newsgroups and the Forums.  Well, here's one I used in an app for storing a password in the registry using the OpenNETCF Encryption stuff :

internal static byte[] GetIV(string keyString)
{
 byte[] bytes = Encoding.Unicode.GetBytes(keyString);
 byte[] iv = new byte[24];
 if(bytes.Length < 24)
 {
  for(int i = 0 ; i < 24 ; i++)
  {
   iv[i] = bytes[i % bytes.Length];
  }
 }
 else
 {
  Array.Copy(bytes, 0, iv, 0, 24);
 }
 return iv;
}
internal static byte[] Encrypt(string toEncrypt, byte[] iv)
{
 TripleDESCryptoServiceProvider des = null;
 try
 {
  des = new TripleDESCryptoServiceProvider();
 }
 catch(Exception)
 {
  MessageBox.Show("The high encryption pack must be installed.  Please install and try again.",
   "Crypto Failure",
   MessageBoxButtons.OK,
   MessageBoxIcon.Exclamation,
   MessageBoxDefaultButton.Button1);
  return null;
 }
 des.Key = PRIVATE_KEY;
 des.IV = iv;
 return des.EncryptValue(Encoding.Unicode.GetBytes(toEncrypt));
}
internal static string Decrypt(byte[] toDecrypt, byte[] iv)
{
 TripleDESCryptoServiceProvider des = null;
 try
 {
  des = new TripleDESCryptoServiceProvider();
 }
 catch(Exception)
 {
  MessageBox.Show("The high encryption pack must be installed.  Please install and try again.",
   "Crypto Failure",
   MessageBoxButtons.OK,
   MessageBoxIcon.Exclamation,
   MessageBoxDefaultButton.Button1);
  return null;
 }
 des.Key = PRIVATE_KEY;
 des.IV = iv;
 byte[] decBytes = des.DecryptValue(toDecrypt);
 return Encoding.Unicode.GetString(decBytes, 0, decBytes.Length);
}
Usage is simple:
byte[] encryptedPwd = AppGlobal.Encrypt(password, GetIV(PUBLIC_KEY));
string  previousPwd = Decrypt(encryptedPwd , GetIV(PUBLIC_KEY));
2/13/2005 7:13:36 AM (Eastern Standard Time, UTC-05:00)  #    Comments [1]  | 
 Wednesday, February 09, 2005

If you've done much with the CF, then you're likely aware that NDOC doen't much like any namespace that's outside of the standard FFW (like the Microsoft.WindowsCE stuff).  While that's been an annoyance for documenting parts of the SDF (leaving holes in the docs), I just ran into a place where it prevents me from generationg any docs - when targeting the TinyCLR.

So, has anyone found anything that can generate decent looking docs without exploding on stuff it doesn't see (or maybe asking for the reference)?  I know NDOC is open source and I could get it working, but I'd rather do actual work.

2/9/2005 11:18:19 PM (Eastern Standard Time, UTC-05:00)  #    Comments [2]  | 
 Tuesday, February 08, 2005

A question came up in todays CF chat about authenticating a CE device to a domain.  While I don't have ready-made CF code for it (though that might be an interesting task) here's a C version:

#include <WINDOWS.H>
#include <SECURITY.H>
#include <SSPI.H>
#define SEC_PACKAGE _T("Microsoft Unified Security Protocol Provider")
typedef struct _AUTH_SEQ {
   BOOL fInitialized;
   BOOL fHaveCredHandle;
   BOOL fHaveCtxtHandle;
   CredHandle hcred;
   struct _SecHandle hctxt;
} AUTH_SEQ, *PAUTH_SEQ;
BOOL GenClientContext(PAUTH_SEQ pAS, PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,
      PVOID pIn, DWORD cbIn, PVOID pOut, PDWORD pcbOut, PBOOL pfDone);
BOOL GenServerContext(PAUTH_SEQ pAS, PVOID pIn, DWORD cbIn, PVOID pOut,
      PDWORD pcbOut, PBOOL pfDone);
BOOL WINAPI SSPLogonUser(LPTSTR szDomain, LPTSTR szUser, LPTSTR szPassword);

TCHAR *lpszUserName = _T("ctacke");
TCHAR *lpszDomainName = _T("mydomain");
TCHAR *lpszPassword = _T("mypassword");

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR lpCmdLine, int nCmShow)
{
 return SSPLogonUser(lpszDomainName, lpszUserName, lpszPassword);
}
BOOL GenClientContext(PAUTH_SEQ pAS, PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,
      PVOID pIn, DWORD cbIn, PVOID pOut, PDWORD pcbOut, PBOOL pfDone) 
{
 DEBUGMSG(TRUE, (_T("+GenServerContext\r\n")));
 SECURITY_STATUS ss;
 TimeStamp       tsExpiry;
 SecBufferDesc   sbdOut;
 SecBuffer       sbOut;
 SecBufferDesc   sbdIn;
 SecBuffer       sbIn;
 ULONG           fContextAttr;
 if (!pAS->fInitialized) 
 {
  DEBUGMSG(TRUE, (_T("Calling AcquireCredentialsHandle...")));
  ss = AcquireCredentialsHandle(NULL, SEC_PACKAGE, 
   SECPKG_CRED_OUTBOUND, NULL, pAuthIdentity, NULL, NULL,
   &pAS->hcred, &tsExpiry);
  
  if (ss < 0) 
  {
   DEBUGMSG(TRUE, (_T("failed with 0x%08x\r\n"), ss));
   return FALSE;
  }
  DEBUGMSG(TRUE, (_T("ok\r\n")));
  if(ss < 0) 
  {
   fprintf(stderr, "AcquireCredentialsHandle failed with %08X\n", ss);
   return FALSE;
  }
  pAS->fHaveCredHandle = TRUE;
 }
   // Prepare output buffer
   sbdOut.ulVersion = 0;
   sbdOut.cBuffers = 1;
   sbdOut.pBuffers = &sbOut;
   sbOut.cbBuffer = *pcbOut;
   sbOut.BufferType = SECBUFFER_TOKEN;
   sbOut.pvBuffer = pOut;
   // Prepare input buffer
   if (pAS->fInitialized)  {
      sbdIn.ulVersion = 0;
      sbdIn.cBuffers = 1;
      sbdIn.pBuffers = &sbIn;
      sbIn.cbBuffer = cbIn;
      sbIn.BufferType = SECBUFFER_TOKEN;
      sbIn.pvBuffer = pIn;
   }
   ss = InitializeSecurityContext(&pAS->hcred, 
         pAS->fInitialized ? &pAS->hctxt : NULL, NULL, 0, 0, 
         SECURITY_NATIVE_DREP, pAS->fInitialized ? &sbdIn : NULL,
         0, &pAS->hctxt, &sbdOut, &fContextAttr, &tsExpiry);
   if (ss < 0)  { 
      // 
      fprintf(stderr, "InitializeSecurityContext failed with %08X\n", ss);
      return FALSE;
   }
   pAS->fHaveCtxtHandle = TRUE;
   // If necessary, complete token
   if (ss == SEC_I_COMPLETE_NEEDED || ss == SEC_I_COMPLETE_AND_CONTINUE) {
     ss = CompleteAuthToken(&pAS->hctxt, &sbdOut);
     if (ss < 0)  {
        fprintf(stderr, "CompleteAuthToken failed with %08X\n", ss);
        return FALSE;
     }
   }
   *pcbOut = sbOut.cbBuffer;
   if (!pAS->fInitialized)
      pAS->fInitialized = TRUE;
   *pfDone = !(ss == SEC_I_CONTINUE_NEEDED 
         || ss == SEC_I_COMPLETE_AND_CONTINUE );
 DEBUGMSG(TRUE, (_T("-GenServerContext\r\n")));
 return TRUE;
}

/////////////////////////////////////////////////////////////////////////////// 

BOOL GenServerContext(PAUTH_SEQ pAS, PVOID pIn, DWORD cbIn, PVOID pOut,
      PDWORD pcbOut, PBOOL pfDone) 
{
 DEBUGMSG(TRUE, (_T("+GenServerContext\r\n")));
 SECURITY_STATUS ss;
 TimeStamp       tsExpiry;
 SecBufferDesc   sbdOut;
 SecBuffer       sbOut;
 SecBufferDesc   sbdIn;
 SecBuffer       sbIn;
 ULONG           fContextAttr;
 if (!pAS->fInitialized)
 {
  DEBUGMSG(TRUE, (_T("Calling AcquireCredentialsHandle...")));
  ss = AcquireCredentialsHandle(NULL, SEC_PACKAGE, 
   SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &pAS->hcred, 
   &tsExpiry);
  
  if (ss < 0) 
  {
   DEBUGMSG(TRUE, (_T("failed with 0x%08x\r\n"), ss));
   return FALSE;
  }
  DEBUGMSG(TRUE, (_T("ok\r\n")));
  pAS->fHaveCredHandle = TRUE;
 }
 // Prepare output buffer
 sbdOut.ulVersion = 0;
 sbdOut.cBuffers = 1;
 sbdOut.pBuffers = &sbOut;
 sbOut.cbBuffer = *pcbOut;
 sbOut.BufferType = SECBUFFER_TOKEN;
 sbOut.pvBuffer = pOut;
 // Prepare input buffer
 sbdIn.ulVersion = 0;
 sbdIn.cBuffers = 1;
 sbdIn.pBuffers = &sbIn;
 sbIn.cbBuffer = cbIn;
 sbIn.BufferType = SECBUFFER_TOKEN;
 sbIn.pvBuffer = pIn;
 DEBUGMSG(TRUE, (_T("Calling AcceptSecurityContext...")));
 ss = AcceptSecurityContext(&pAS->hcred, 
   pAS->fInitialized ? &pAS->hctxt : NULL, &sbdIn, 0, 
   SECURITY_NATIVE_DREP, &pAS->hctxt, &sbdOut, &fContextAttr, 
   &tsExpiry);
 if (ss < 0) 
 {
  DEBUGMSG(TRUE, (_T("failed with 0x%08x\r\n"), ss));
  return FALSE;
 }
 DEBUGMSG(TRUE, (_T("ok\r\n")));
 pAS->fHaveCtxtHandle = TRUE;
 // If necessary, complete token
 if (ss == SEC_I_COMPLETE_NEEDED || ss == SEC_I_COMPLETE_AND_CONTINUE) 
 {
  DEBUGMSG(TRUE, (_T("Calling CompleteAuthToken...")));
  ss = CompleteAuthToken(&pAS->hctxt, &sbdOut);
  if (ss < 0) 
  {
   DEBUGMSG(TRUE, (_T("failed with 0x%08x\r\n"), ss));
   return FALSE;
  }
  DEBUGMSG(TRUE, (_T("ok\r\n")));
 }
 *pcbOut = sbOut.cbBuffer;
 if (!pAS->fInitialized)
  pAS->fInitialized = TRUE;
 *pfDone = !(ss = SEC_I_CONTINUE_NEEDED 
   || ss == SEC_I_COMPLETE_AND_CONTINUE);
 DEBUGMSG(TRUE, (_T("-GenServerContext\r\n")));
 return TRUE;
}

/////////////////////////////////////////////////////////////////////////////// 

BOOL WINAPI SSPLogonUser(LPTSTR szDomain, LPTSTR szUser, LPTSTR szPassword) 
{
 DEBUGMSG(TRUE, (_T("\r\n+SSPLogonUser\r\n")));
 AUTH_SEQ    asServer   = {0};
 AUTH_SEQ    asClient   = {0};
 BOOL        fDone      = FALSE;
 BOOL        fResult    = FALSE;
 DWORD       cbOut      = 0;
 DWORD       cbIn       = 0;
 DWORD       cbMaxToken = 0;
 PVOID       pClientBuf = NULL;
 PVOID       pServerBuf = NULL;
 SecPkgInfo *pSPI       = NULL;
 HMODULE     hModule    = NULL;
 SECURITY_STATUS ss;
 SEC_WINNT_AUTH_IDENTITY ai;
 ULONG packages = 0;
 PSecPkgInfo pPackageInfo = NULL;
 __try 
 {
  EnumerateSecurityPackages(&packages, &pPackageInfo);
  DEBUGMSG(TRUE, (_T("  Available security packages:\r\n")));
  for(UINT i = 0 ; i < packages ; i++)
  {
   DEBUGMSG(TRUE, (_T("\t%s\r\n"), pPackageInfo[i].Name));
  }
  DEBUGMSG(TRUE, (_T("\r\n")));

  DEBUGMSG(TRUE, (_T("Calling FreeContextBuffer...")));
  ss =FreeContextBuffer(pPackageInfo);
  if(ss != SEC_E_OK)
  {
   DEBUGMSG(TRUE, (_T("failed with 0x%08x\r\n"), ss));
   __leave;
  }
  DEBUGMSG(TRUE, (_T("ok\r\n")));
  // Get max token size
  DEBUGMSG(TRUE, (_T("Calling QuerySecurityPackageInfo...")));
  ss = QuerySecurityPackageInfo(SEC_PACKAGE, &pSPI);
  if(ss != SEC_E_OK)
  {
   DEBUGMSG(TRUE, (_T("failed with 0x%08x\r\n"), ss));
   __leave;
  }
  DEBUGMSG(TRUE, (_T("ok\r\n")));
  cbMaxToken = pSPI->cbMaxToken;
  DEBUGMSG(TRUE, (_T("Calling FreeContextBuffer...")));
  ss =FreeContextBuffer(pSPI);
  if(ss != SEC_E_OK)
  {
   DEBUGMSG(TRUE, (_T("failed with 0x%08x\r\n"), ss));
   __leave;
  }
  DEBUGMSG(TRUE, (_T("ok\r\n")));
  // Allocate buffers for client and server messages
  DEBUGMSG(TRUE, (_T("Allocating heaps...")));
  pClientBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbMaxToken);
  pServerBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbMaxToken);
  DEBUGMSG(TRUE, (_T("ok\r\n")));
  // Initialize auth identity structure
  ZeroMemory(&ai, sizeof(ai));
  ai.Domain = szDomain;
  ai.DomainLength = _tcslen(szDomain);
  ai.User = szUser;
  ai.UserLength = _tcslen(szUser);
  ai.Password = szPassword;
  ai.PasswordLength = _tcslen(szPassword);
  ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  // Prepare client message (negotiate) .
  cbOut = cbMaxToken;
  DEBUGMSG(TRUE, (_T("Calling GenClientContext\r\n")));
  if (!GenClientContext(&asClient, &ai, NULL, 0, pClientBuf, &cbOut, &fDone))
   __leave;
  // Prepare server message (challenge) .
  cbIn = cbOut;
  cbOut = cbMaxToken;
  DEBUGMSG(TRUE, (_T("Calling GenServerContext\r\n")));
  if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut, &fDone))
   __leave;
  // Most likely failure: AcceptServerContext fails with SEC_E_LOGON_DENIED
   // in the case of bad szUser or szPassword.
   // Unexpected Result: Logon will succeed if you pass in a bad szUser and 
   // the guest account is enabled in the specified domain.
  // Prepare client message (authenticate) .
  cbIn = cbOut;
  cbOut = cbMaxToken;
  DEBUGMSG(TRUE, (_T("Calling GenClientContext\r\n")));
  if (!GenClientContext(&asClient, &ai, pServerBuf, cbIn, pClientBuf, &cbOut, &fDone))
   __leave;
  // Prepare server message (authentication) .
  cbIn = cbOut;
  cbOut = cbMaxToken;
  DEBUGMSG(TRUE, (_T("Calling GenServerContext\r\n")));
  if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut, &fDone))
   __leave;
  fResult = TRUE;
 } 
 __finally 
 {
  // Clean up resources
  if (asClient.fHaveCtxtHandle)
  {
   DEBUGMSG(TRUE, (_T("Calling DeleteSecurityContext\r\n")));
   DeleteSecurityContext(&asClient.hctxt);
  }
  if (asClient.fHaveCredHandle)
  {
   DEBUGMSG(TRUE, (_T("Calling FreeCredentialsHandle\r\n")));
   FreeCredentialsHandle(&asClient.hcred);
  }
  if (asServer.fHaveCtxtHandle)
  {
   DEBUGMSG(TRUE, (_T("Calling DeleteSecurityContext\r\n")));
   DeleteSecurityContext(&asServer.hctxt);
  }
  if (asServer.fHaveCredHandle)
  {
   DEBUGMSG(TRUE, (_T("Calling FreeCredentialsHandle\r\n")));
   FreeCredentialsHandle(&asServer.hcred);
  }
  DEBUGMSG(TRUE, (_T("Freeing heaps...")));
  HeapFree(GetProcessHeap(), 0, pClientBuf);
  HeapFree(GetProcessHeap(), 0, pServerBuf);
  DEBUGMSG(TRUE, (_T("ok\r\n")));
 }
 DEBUGMSG(TRUE, (_T("-SSPLogonUser\r\n")));
 return fResult;
}
2/8/2005 1:29:26 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  | 
 Friday, February 04, 2005

So I started a amanged wrapper for Pocket Excel some time ago - probably a year and a half now.  I made good progress - got it so you can real and write workbooks, worksheets, cells - just no support for the formulas yet.  Then I got sidetracked with one thing after another. 

Initially I was going to use it as a commercial product to get revenue to support OpenNETCF, but I honestly don't have the time for it.  If I can find someone, or a group of someones willing to put in a final push to finish it (maybe an industrious student looking for thesis material?) I'll donate it to the community at large as an OpenNETCF namespace.

Any takers?

2/4/2005 9:07:39 PM (Eastern Standard Time, UTC-05:00)  #    Comments [1]  | 
 Wednesday, February 02, 2005

Pop quiz, what does this code do?

DS1307 d = new DS1307(Cpu.Pin.GPIO20_VTU_TIO1A, Cpu.Pin.GPIO21_VTU_TIO2A);
d.SetRTC(new DateTime(2005, 2, 2, 21, 22, 0, 0));
for (int i = 0; i < 10; i++)
{
    DateTime dt = d.ReadRTC();
    Debug.Print(dt.Hour + ":" + dt.Minute + ":" + dt.Second);
    System.Threading.Thread.Sleep(2000);
}

Okay, so it's not too exciting, until you realize that what it calls - an I2C driver - is written in C# as well.  Yes, I've achieved a managed code driver.  Next question - what's it run on?  I'll give you a hint - it's not a Pocket PC.

2/2/2005 10:33:41 PM (Eastern Standard Time, UTC-05:00)  #    Comments [4]  | 

We've got a lot of cool new things - many of which are attempts to get your code “prepped” for CF 2.0 before it's even here.  A Serial wrapper, FTP classes...and some other stuff that will excite the hell out of a lot of people.  Just take a look at the top level folders in the OpenNETCF Vault and you'll see what I mean....

2/2/2005 10:25:17 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  |