CASA/c_micasad/lss/CASACrypto.cs

614 lines
21 KiB
C#
Raw Normal View History

/***********************************************************************
*
* Copyright (C) 2005-2006 Novell, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2.1
* of the License.
*
* This library 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
* Library Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail,
* you may find current contact information at www.novell.com.
*
***********************************************************************/
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
#if LINUX
using Mono.Unix;
#endif
using sscs.common;
using sscs.constants;
namespace sscs.crypto
{
public class CASACrypto
{
private const int SALTSIZE = 64;
private const int ITERATION_COUNT = 1000;
private const int HASH_SIZE = 32;
internal static byte[] Generate16ByteKeyFromString(string sTheString)
{
byte[] baKey = new byte[16]; //return value
try
{
Rfc2898DeriveBytes pkcs5 = new Rfc2898DeriveBytes(sTheString, SALTSIZE, ITERATION_COUNT);
baKey = pkcs5.GetBytes(16);
}
catch(Exception e)
{
CSSSLogger.ExpLog(e.ToString());
CSSSLogger.DbgLog("Key generation failed");
baKey = null;
}
return baKey;
}
internal static bool StoreKeySetUsingMasterPasscode(byte[] key,
byte[] IV, byte[] baMasterPasscode, string fileName)
{
bool bRet = false;
FileStream fsEncrypt = null;
CryptoStream csEncrypt = null;
try
{
//Get an encryptor.
RijndaelManaged myRijndael = new RijndaelManaged();
ICryptoTransform encryptor;
encryptor = myRijndael.CreateEncryptor(baMasterPasscode, baMasterPasscode);
//Encrypt the data to a file
fsEncrypt = new FileStream(fileName, FileMode.Create);
// make hidden
File.SetAttributes(fileName, FileAttributes.Hidden);
SHA256 sha = new SHA256Managed();
byte[] hash = sha.ComputeHash(key);
fsEncrypt.Write(hash,0,hash.Length);
fsEncrypt.Flush();
csEncrypt = new CryptoStream(fsEncrypt, encryptor, CryptoStreamMode.Write);
//Write all data to the crypto stream and flush it.
csEncrypt.Write(key, 0, key.Length);
csEncrypt.FlushFinalBlock();
bRet = true;
}
catch(Exception e)
{
CSSSLogger.ExpLog(e.ToString());
CSSSLogger.DbgLog("Unable to store the generated key");
bRet = false;
}
if (csEncrypt != null)
csEncrypt.Close();
if( fsEncrypt != null )
fsEncrypt.Close();
return bRet;
}
internal static byte[] GetKeySetFromFile(byte[] baMasterPasscode,
string fileName )
{
byte[] baSavedKey = null;
FileStream fsDecrypt = null;
CryptoStream csDecrypt = null;
try
{
#if LINUX
UnixFileInfo fsTest = new UnixFileInfo (fileName);
if((fsTest == null) || !(fsTest.Exists) || fsTest.IsSymbolicLink)
#else
if(!File.Exists(fileName))
#endif
{
return null;
}
/* Get a decryptor that uses the same key and IV
* as the encryptor.
*/
RijndaelManaged myRijndael = new RijndaelManaged();
ICryptoTransform decryptor = myRijndael.CreateDecryptor(baMasterPasscode, baMasterPasscode);
//Now decrypt
fsDecrypt = new FileStream(fileName, FileMode.Open);
byte[] storedHash = new byte[32];
fsDecrypt.Read(storedHash,0,storedHash.Length);
csDecrypt = new CryptoStream(fsDecrypt, decryptor, CryptoStreamMode.Read);
baSavedKey = new byte[32];
//Read the data out of the crypto stream.
csDecrypt.Read(baSavedKey, 0, baSavedKey.Length);
SHA256 sha = new SHA256Managed();
byte[] newHash = sha.ComputeHash(baSavedKey);
for( int i = 0 ; i < 32; i++ )
{
if(storedHash[i] != newHash[i])
{
CSSSLogger.DbgLog("Hash doesnot match");
csDecrypt.Close();
fsDecrypt.Close();
return null;
}
}
}
catch(Exception e)
{
CSSSLogger.ExpLog(e.ToString());
CSSSLogger.DbgLog("Unable to get the stored key");
baSavedKey = null;
}
if (csDecrypt != null)
csDecrypt.Close();
if ( fsDecrypt != null )
fsDecrypt.Close();
return baSavedKey;
}
internal static void EncryptDataAndWriteToFile(byte[] xmlData,
byte[] key, string fileName)
{
FileStream fsEncrypt = null;
CryptoStream csEncrypt = null;
try
{
byte[] IV = new byte[16];
for(int z = 0 ; z < 16; z++ )
IV[z] = key[z];
//Get an encryptor.
RijndaelManaged myRijndael = new RijndaelManaged();
ICryptoTransform encryptor = myRijndael.CreateEncryptor(key, IV);
//Encrypt the data to a file
fsEncrypt = new FileStream(fileName, FileMode.Create);
// make hidden
File.SetAttributes(fileName, FileAttributes.Hidden);
SHA256 sha = new SHA256Managed();
byte[] hash = sha.ComputeHash(xmlData);
fsEncrypt.Write(hash,0,hash.Length);
fsEncrypt.Flush();
csEncrypt = new CryptoStream(fsEncrypt, encryptor, CryptoStreamMode.Write);
//Write all data to the crypto stream and flush it.
csEncrypt.Write(xmlData, 0, xmlData.Length);
csEncrypt.FlushFinalBlock();
}
catch(Exception e)
{
CSSSLogger.ExpLog(e.ToString());
CSSSLogger.DbgLog("Encrypting and storing to file failed.");
}
if (csEncrypt != null)
csEncrypt.Close();
if( fsEncrypt != null )
fsEncrypt.Close();
}
internal static byte[] ReadFileAndDecryptData(byte[] key,
string fileName)
{
FileStream fsDecrypt = null;
CryptoStream csDecrypt = null;
try
{
byte[] IV = new byte[16];
for(int z = 0 ; z < 16; z++ )
IV[z] = key[z];
//Get a decryptor that uses the same key and IV as the encryptor.
RijndaelManaged myRijndael = new RijndaelManaged();
ICryptoTransform decryptor = myRijndael.CreateDecryptor(key, IV);
#if LINUX
UnixFileInfo fsTest = new UnixFileInfo (fileName);
if((fsTest == null) || !(fsTest.Exists) || fsTest.IsSymbolicLink)
#else
if(!File.Exists(fileName))
#endif
{
return null;
}
//Now decrypt
fsDecrypt = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
byte[] storedHash = new byte[HASH_SIZE];
fsDecrypt.Read(storedHash,0,storedHash.Length);
csDecrypt = new CryptoStream(fsDecrypt, decryptor, CryptoStreamMode.Read);
if(fsDecrypt.Length < HASH_SIZE )
{
csDecrypt.Close();
fsDecrypt.Close();
return null;
}
ulong fileLen = (ulong)(fsDecrypt.Length - HASH_SIZE);
byte[] fromEncrypt = new byte[fileLen];
//Read the data out of the crypto stream.
int bytesRead = csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
byte[] tmpEncrypt = new byte[bytesRead];
for(int i = 0 ; i < bytesRead; i++ )
tmpEncrypt[i] = fromEncrypt[i];
SHA256 sha = new SHA256Managed();
byte[] newHash = sha.ComputeHash(tmpEncrypt);
for( int i = 0 ; i < 32; i++ )
{
if(storedHash[i] != newHash[i])
{
CSSSLogger.DbgLog("Hash doesnot match");
csDecrypt.Close();
fsDecrypt.Close();
return null;
}
}
csDecrypt.Close();
fsDecrypt.Close();
return tmpEncrypt;
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
if (csDecrypt != null)
{
csDecrypt.Close();
}
if( fsDecrypt != null )
{
fsDecrypt.Close();
}
return null;
}
/* The methods EncryptData() and DecryptData() would be
* required when we use a database to store secrets.
*/
/* Encrypts the data with the key and returns the encrypted buffer.
*/
internal static byte[] EncryptData(byte[] data, byte[] key)
{
try
{
byte[] IV = new byte[16];
int i = 0;
for(i = 0 ; i < 16; i++ )
IV[i] = key[i];
//Get an encryptor.
RijndaelManaged myRijndael = new RijndaelManaged();
ICryptoTransform encryptor = myRijndael.CreateEncryptor(key, IV);
MemoryStream ms1 = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(ms1, encryptor, CryptoStreamMode.Write);
//Write all data to the crypto stream and flush it.
csEncrypt.Write(data, 0, data.Length);
csEncrypt.FlushFinalBlock();
return ms1.ToArray();
}
catch(Exception e)
{
CSSSLogger.ExpLog(e.ToString());
}
return null;
}
/* Decrypts the buffer(encrypted) with the key and returns the
* decrypted data.
*/
internal static byte[] DecryptData(byte[] buffer, byte[] key)
{
try
{
byte[] IV = new byte[16];
for(int i = 0 ; i < 16; i++ )
IV[i] = key[i];
//Get a decryptor that uses the same key and IV as the encryptor.
RijndaelManaged myRijndael = new RijndaelManaged();
ICryptoTransform decryptor = myRijndael.CreateDecryptor(key, IV);
MemoryStream ms1 = new MemoryStream(buffer);
CryptoStream csDecrypt = new CryptoStream(ms1, decryptor, CryptoStreamMode.Read);
byte[] fromEncrypt = new byte[buffer.Length];
//Read the data out of the crypto stream.
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
return fromEncrypt;
}
catch(Exception e)
{
CSSSLogger.ExpLog(e.ToString());
}
return null;
}
/* This method checks if we can get the master passcode by
* decrypting the passwds file ( where we store all possible
* passwds cross-encrypted.
*
* TBD : As we are storing master passcode and the keys in 2
* different files, we need to take care of cases when 1 of the files
* is deleted.
*/
internal static bool CheckIfMasterPasscodeIsAvailable(string desktopPasswd, string fileName)
{
return (File.Exists(fileName));
}
internal static byte[] GetMasterPasscode(string desktopPasswd, string fileName)
{
byte[] mp = DecryptMasterPasscodeUsingString(desktopPasswd, fileName);
return mp;
}
/* TBD - There must be a way, where we establish the integrity of
* the files where we store the keys and master passcode.
* Use a marker ?
*/
// Used to save the MasterPasscode encrypted with Desktop login, etc
internal static void EncryptAndStoreMasterPasscodeUsingString(
byte[] baMasterPasscode,
string passwd,
string fileName)
{
FileStream fsEncrypt = null;
CryptoStream csEncrypt = null;
try
{
if(File.Exists(fileName))
File.Delete(fileName);
byte[] baKey = Generate16ByteKeyFromString(passwd);
//Get an encryptor.
RijndaelManaged myRijndael = new RijndaelManaged();
ICryptoTransform encryptor;
encryptor = myRijndael.CreateEncryptor(baKey, baKey);
//Encrypt the data to a file
fsEncrypt = new FileStream(fileName,FileMode.Create);
// make hidden
File.SetAttributes(fileName, FileAttributes.Hidden);
csEncrypt = new CryptoStream(fsEncrypt, encryptor,
CryptoStreamMode.Write);
//Write all data to the crypto stream and flush it.
csEncrypt.Write(baMasterPasscode, 0, baMasterPasscode.Length);
csEncrypt.FlushFinalBlock();
csEncrypt.Close();
fsEncrypt.Close();
}
catch(Exception e)
{
CSSSLogger.ExpLog(e.ToString());
}
if (csEncrypt != null)
{
csEncrypt.Close();
}
if( fsEncrypt != null )
{
fsEncrypt.Close();
}
}
public static byte[] DecryptMasterPasscodeUsingString(string passwd,
string fileName)
{
FileStream fsDecrypt = null;
CryptoStream csDecrypt = null;
byte[] baSavedMasterPasscode = null;
try
{
byte[] baKey = Generate16ByteKeyFromString(passwd);
/* Get a decryptor that uses the same key and
* IV as the encryptor.
*/
RijndaelManaged myRijndael = new RijndaelManaged();
ICryptoTransform decryptor = myRijndael.CreateDecryptor(baKey,
baKey);
//Now decrypt
#if LINUX
UnixFileInfo fsTest = new UnixFileInfo (fileName);
if((fsTest == null) || !(fsTest.Exists) || fsTest.IsSymbolicLink)
#else
if(!File.Exists(fileName))
#endif
{
return null;
}
fsDecrypt = new FileStream(fileName, FileMode.Open);
csDecrypt = new CryptoStream(fsDecrypt, decryptor,
CryptoStreamMode.Read);
baSavedMasterPasscode = new byte[16];
//Read the data out of the crypto stream.
csDecrypt.Read(baSavedMasterPasscode, 0, 16);
}
catch(Exception e)
{
CSSSLogger.ExpLog(e.ToString());
CSSSLogger.DbgLog("Unable to decrypt master passode");
baSavedMasterPasscode = null;
}
if (csDecrypt != null)
csDecrypt.Close();
if ( fsDecrypt != null )
fsDecrypt.Close();
return baSavedMasterPasscode;
}
internal static byte[] GetMasterPasscodeUsingMasterPasswd(
string mPasswd,
string fileName)
{
byte[] baMasterPasscode;
try
{
if(File.Exists(fileName))
{
/* Decrypt the passcode from the file using master passwd.
* and return the decrypted passcode.
*/
baMasterPasscode = DecryptMasterPasscodeUsingString(mPasswd,
fileName);
return baMasterPasscode;
}
else
return null;
}
catch(Exception e)
{
CSSSLogger.ExpLog(e.ToString());
CSSSLogger.DbgLog("Failed to get master passcode from master password.");
}
return null;
}
internal static byte[] GetMasterPasscodeUsingDesktopPasswd(
string desktopPasswd,
string fileName)
{
byte[] passcode;
try
{
if(File.Exists(fileName))
{
/* Decrypt the passcode from the file using desktop passwd.
* and return the decrypted passcode.
*/
passcode = DecryptMasterPasscodeUsingString(desktopPasswd,
fileName);
return passcode;
}
else
return null;
}
catch(Exception e)
{
CSSSLogger.ExpLog(e.ToString());
CSSSLogger.DbgLog("Failed to get master passcode using desktop passwd");
}
return null;
}
//internal static string GenerateMasterPasscodeUsingDesktopPasswd(
internal static byte[] GenerateMasterPasscodeUsingString(
string desktopPasswd,
string fileName,
string validationFile,
UserIdentifier userId
)
{
try
{
byte[] baPasscode;
// use AES to generate a random 16 byte key;
RijndaelManaged myRijndael = new RijndaelManaged();
myRijndael.KeySize = 128;
//Create a new key and initialization vector.
myRijndael.GenerateKey();
baPasscode = myRijndael.Key;
EncryptAndStoreMasterPasscodeUsingString(baPasscode,
desktopPasswd,
fileName);
EncryptDataAndWriteToFile(
Encoding.Default.GetBytes(
ConstStrings.MICASA_VALIDATION_STRING),
baPasscode,
validationFile);
return baPasscode;
}
catch(Exception e)
{
CSSSLogger.ExpLog(e.ToString());
CSSSLogger.DbgLog("Generation of master passcode failed.");
}
return null;
}
public static bool ValidatePasscode(byte[] baPasscode, string fileName)
{
/* Here we decrpyt a well known string, throw exception
* if not successful
* A well-known string is encrpyted by the Passcode and saved
*/
if ((baPasscode == null) || baPasscode.Length < 1 )
return false;
try
{
byte[] baString = ReadFileAndDecryptData(baPasscode, fileName);
string sString = Encoding.Default.GetString(baString);
char[] trimChars = {'\0'};
sString = sString.TrimEnd(trimChars);
if( ConstStrings.MICASA_VALIDATION_STRING.Equals(sString))
{
return true;
}
else
{
return false;
}
}
catch(Exception e)
{
CSSSLogger.ExpLog(e.ToString());
CSSSLogger.DbgLog("Validation of passcode failed.");
}
return false;
}
}
}