using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Text; using System.Diagnostics; namespace PhoneHome { internal class CheckActiveTwo { internal string ProductName = String.Empty; internal string ProductVersion = String.Empty; internal string SerialNumber = String.Empty; private string _LastGeneratedKey = String.Empty; /// /// /// /// /// /// internal CheckActiveTwo(string ProductName, string ProductVersion, string SerialNumber) { if (!String.IsNullOrEmpty(ProductName) && !String.IsNullOrEmpty(ProductVersion) && !String.IsNullOrEmpty(SerialNumber)) { this.ProductName = ProductName; this.ProductVersion = ProductVersion; this.SerialNumber = SerialNumber; } else { throw new ArgumentException("ProductName, ProductVersion, and SerialNumber can't be blank"); } } /// /// /// /// internal CheckActiveTwo(string strEncGeneratedString) { if (IsValidEncKey(strEncGeneratedString)) { _LastGeneratedKey = strEncGeneratedString; } else { throw new ArgumentException("Not a valid Enc String"); } } #region Static Internal Utilities /// /// Perform checksum on string /// /// /// Checksum internal static int PerformChecksum(string strAboutToBeChecksummed) { if (!String.IsNullOrEmpty(strAboutToBeChecksummed)) { int nChecksum = 0; for (int i = 0; i < strAboutToBeChecksummed.Length; ++i) { if (Char.IsDigit(strAboutToBeChecksummed[i])) nChecksum = nChecksum + int.Parse(strAboutToBeChecksummed[i].ToString()); } return nChecksum; } return 0; } /// /// Dash a String /// /// internal static string MakeIntoDashSeperatedString(string strAboutToBeDashed, int DashEveryNthCharacter) { if (!String.IsNullOrEmpty(strAboutToBeDashed)) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < strAboutToBeDashed.Length; i++) { if ((i != 0) && ((i % DashEveryNthCharacter) == 0)) sb.Append("-"); sb.Append(strAboutToBeDashed[i]); } return sb.ToString(); } return String.Empty; } /// /// Undash a String /// /// internal static string MakeIntoDashUnseperatedString(string strAboutToBeUndashed) { if (!String.IsNullOrEmpty(strAboutToBeUndashed)) return strAboutToBeUndashed.Replace("-", ""); return String.Empty; } #endregion #region Internal Methods /// /// Generate a new Key to use - the key can be used to verify the serial number /// /// a new Key internal string GenerateNewKey() { // GenerateString For User to send string KeyValue = MakeKey(); string EncrKey = EncodeShuffle(KeyValue); _LastGeneratedKey = MakeIntoDashSeperatedString(EncrKey, 4); // For Debugging string UnEncoded = DecodeShuffle(EncrKey); if (KeyValue != UnEncoded) { // something is terribly wrong with the Encryption Debug.Assert(false); } return _LastGeneratedKey; } /// /// The function should really be called 'GeneratedPIN' but for reverse engineering /// purpose, keeping them guessin * security by obscurity * /// /// internal string GeneraledPiM() { if (!String.IsNullOrEmpty(_LastGeneratedKey)) { int KeyChecksum = PerformChecksum(MakeKey()); int ShuffledChecksum = PerformChecksum(_LastGeneratedKey); string Result = KeyChecksum.ToString() + ShuffledChecksum.ToString(); if (Result.Length < 4) { StringBuilder sb = new StringBuilder(Result); int nRemainder = 4 - Result.Length; while (nRemainder > 0) { sb.Append("7"); nRemainder--; } Result = sb.ToString(); } Result = Result.Substring(0, 4); int nHour = DateTime.Now.ToUniversalTime().Hour; nHour = (nHour <= 6) ? (nHour + 3) : (nHour - 2); string strHour = String.Format("{0}", (nHour < 10) ? ("8" + nHour.ToString()) : (nHour.ToString())); string fourdigitPin = (strHour[1] + Result[1].ToString() + strHour[0] + Result[3].ToString()); int nChecksumPin = PerformChecksum(fourdigitPin); string strChecksumLastDigit = nChecksumPin.ToString()[nChecksumPin.ToString().Length - 1].ToString(); return fourdigitPin + strChecksumLastDigit; } else { GenerateNewKey(); return GeneraledPiM(); } } /// /// /// /// /// /// /// /// true, if successful, false otherwise internal bool RetrieveValues(out DateTime dtStamp, out string ProductName, out string ProductVersion, out string SerialNumber) { dtStamp = DateTime.MinValue; ProductName = String.Empty; ProductVersion = String.Empty; SerialNumber = String.Empty; if (!String.IsNullOrEmpty(_LastGeneratedKey)) { string Undashed = MakeIntoDashUnseperatedString(_LastGeneratedKey); if (UnmakeKey(DecodeShuffle(Undashed), out dtStamp, out ProductName, out ProductVersion, out SerialNumber)) { this.ProductName = ProductName; this.ProductVersion = ProductVersion; this.SerialNumber = SerialNumber; return true; } } return false; } #endregion #region Private Key Generation Functions private bool ContainsOnlyDigits(string strToCheckForDigits) { if (!String.IsNullOrEmpty(strToCheckForDigits)) { for (int i = 0; i < strToCheckForDigits.Length; ++i) { if (!Char.IsDigit(strToCheckForDigits[i])) return false; } return true; } return false; } /// /// Check to see if the Generated string being passed in is a valid generated string /// /// /// true if valid, false otherwise private bool IsValidEncKey(string strEncGeneratedString) { string Undashed = MakeIntoDashUnseperatedString(strEncGeneratedString); DateTime dt; string pName, pVersion, pSerial; if (UnmakeKey(DecodeShuffle(Undashed), out dt, out pName, out pVersion, out pSerial)) return true; return false; } /// /// Make a Key to send across (all the info the auth needs) /// /// Key with needed Info private string MakeKey() { //string dtShortTest = DateTime.Now.ToUniversalTime().ToShortDateString().Replace("/", ""); DateTime dtUniversal = DateTime.Now.ToUniversalTime(); string dtMonth = (dtUniversal.Month > 9) ? String.Format("{0}", dtUniversal.Month) : String.Format("0{0}", dtUniversal.Month); string dtDay = (dtUniversal.Day > 9) ? String.Format("{0}", dtUniversal.Day) : String.Format("0{0}", dtUniversal.Day); string dtYear = dtUniversal.Year.ToString(); string dtShort = String.Format("{0}{1}{2}", dtMonth, dtDay, dtYear); string ProductId = ProductName.Substring(0, 1); // should always be 'M' or 'L' // so we use "LMXYZ" string strKey = dtShort + "Z" + ProductId + "Y" + ProductVersion.Split('.')[0] + "X" + SerialNumber; return strKey; } /// /// Unmake a key * Don't even know why i wrote this, prob. won't end up using this * /// /// true if successful, false otheriwise private bool UnmakeKey(string strAboutToBeUnkeyed, out DateTime dtStamp, out string ProductName, out string ProductVersion, out string SerialNumber) { dtStamp = DateTime.MinValue; ProductName = ""; ProductVersion = ""; SerialNumber = ""; //string strKey = dtShort + "Z" + ProductId + "Y" + ProductVersion.Split('.')[0] + "X" + SerialNumber; //0123456Z try { if (!String.IsNullOrEmpty(strAboutToBeUnkeyed)) { int nZIndex = strAboutToBeUnkeyed.IndexOf("Z"); int nYIndex = strAboutToBeUnkeyed.IndexOf("Y"); int nXIndex = strAboutToBeUnkeyed.IndexOf("X"); if (nZIndex == -1 || nYIndex == -1 || nXIndex == -1) return false; // dtShort string strDT = strAboutToBeUnkeyed.Substring(0, nZIndex); strDT = String.Format("{0}/{1}/{2}", strDT.Substring(0, 2), strDT.Substring(2, 2), strDT.Substring(4)); dtStamp = DateTime.Parse(strDT); // ProductId string ProductId = strAboutToBeUnkeyed.Substring(nZIndex + 1, 1); if (ProductId == "L") ProductName = "Lytec"; else if (ProductId == "M") ProductName = "Medisoft"; else return false; // ProductVersion string strProductVersion = strAboutToBeUnkeyed.Substring(nYIndex + 1, (nXIndex - nYIndex - 1)); if (!String.IsNullOrEmpty(strProductVersion) && ContainsOnlyDigits(strProductVersion)) ProductVersion = strProductVersion; else return false; // Serial Number SerialNumber = strAboutToBeUnkeyed.Substring(nXIndex + 1); return !String.IsNullOrEmpty(SerialNumber) && ContainsOnlyDigits(SerialNumber); } } catch (Exception) { /* ignore */ } return false; } /// /// * Simple Encoder * /// /// /// private string EncodeShuffle(string strAboutToBeEncodeShuffled) { const string Char_CodeLib = "ABCDEFGHIJKNOPQRSTUVW"; //20 - W is never used const string Char_CodeLibExcluded = "LMXYZ"; //5 if (!String.IsNullOrEmpty(strAboutToBeEncodeShuffled)) { List ResultStr = new List(strAboutToBeEncodeShuffled); int nCount = ResultStr.Count; // Every N'th Digit do something for (int i = 0; i < nCount; i = i + 3) { char c = ResultStr[i]; if (char.IsDigit(c)) { int nChar = int.Parse(c.ToString()); ResultStr[i] = Char_CodeLib[nChar]; // 0..9 } } // Every N'th Digit do something for (int i = 0; i < nCount; i = i + 4) { char c = ResultStr[i]; if (char.IsDigit(c)) { int nChar = int.Parse(c.ToString()); ResultStr[i] = Char_CodeLib[nChar + 10]; // 10..19 } } // Add Randomness to the end of the string Random random = new Random(); int nRand = random.Next(1, 9); // Perform a Random Shift * So that the code ALWAYS looks different from use to use * for (int i = 0; i < nCount; i = i + 2) { char c = ResultStr[i]; if (char.IsLetter(c) && !Char_CodeLibExcluded.Contains(c.ToString())) { int nIndexShifted = Char_CodeLib.IndexOf(c) + nRand; int nIndexShiftedAdj = nIndexShifted % 21; ResultStr[i] = Char_CodeLib[nIndexShiftedAdj]; // 0..20 } } // Perform another Random Swap * So that the code ALWAYS looks different from use to use * for (int i = 0; i < nCount; i = i + nRand) { char c = ResultStr[i]; int nOpposite = nCount - i - 1; char o = ResultStr[nOpposite]; if (char.IsLetter(c) && !Char_CodeLibExcluded.Contains(c.ToString()) && char.IsLetter(o) && !Char_CodeLibExcluded.Contains(o.ToString())) { // swap ResultStr[i] = o; ResultStr[nOpposite] = c; } } // Perform a Reversal for (int i = 0; i < (nCount / 2); ++i) { char N1 = ResultStr[i]; char N2 = ResultStr[nCount - 1 - i]; // swap ResultStr[i] = N2; ResultStr[nCount - 1 - i] = N1; } // Add the Randomness to the string for proper decoding to occur ResultStr.Add(Char.Parse(nRand.ToString())); // And Return return new String(ResultStr.ToArray()); } return String.Empty; } /// /// * Simple Decoder * /// /// /// private string DecodeShuffle(string strAboutToBeDecodeShuffled) { const string Char_CodeLib = "ABCDEFGHIJKNOPQRSTUVW"; //20 const string Char_CodeLibExcluded = "LMXYZ"; //5 try { if (!String.IsNullOrEmpty(strAboutToBeDecodeShuffled)) { List ResultStr = new List(strAboutToBeDecodeShuffled); // retrieve Randomness Factor char cLast = ResultStr[ResultStr.Count - 1]; ResultStr.RemoveAt(ResultStr.Count - 1); int nCount = ResultStr.Count; int nRand = int.Parse(cLast.ToString()); // Perform a Reversal for (int i = 0; i < (nCount / 2); ++i) { char N1 = ResultStr[i]; char N2 = ResultStr[nCount - 1 - i]; // swap ResultStr[i] = N2; ResultStr[nCount - 1 - i] = N1; } // Perform another Random Swap * So that the code ALWAYS looks different from use to use * for (int i = 0; i < nCount; i = i + nRand) { char c = ResultStr[i]; int nOpposite = nCount - i - 1; char o = ResultStr[nOpposite]; if (char.IsLetter(c) && !Char_CodeLibExcluded.Contains(c.ToString()) && char.IsLetter(o) && !Char_CodeLibExcluded.Contains(o.ToString())) { // swap ResultStr[i] = o; ResultStr[nOpposite] = c; } } // Perform a Random Shift * So that the code ALWAYS looks different from use to use * for (int i = 0; i < nCount; i = i + 2) { char c = ResultStr[i]; if (char.IsLetter(c) && !Char_CodeLibExcluded.Contains(c.ToString())) { int nIndexShifted = Char_CodeLib.IndexOf(c) - nRand; int nIndexShiftedAdj = (nIndexShifted < 0)? 21 + nIndexShifted : nIndexShifted; ResultStr[i] = Char_CodeLib[nIndexShiftedAdj]; // 0..20 } } // Every N'th Digit do something for (int i = 0; i < nCount; i = i + 4) { char c = ResultStr[i]; if (char.IsLetter(c) && !Char_CodeLibExcluded.Contains(c.ToString())) { int nIndex = Char_CodeLib.IndexOf(c); if (nIndex >= 10) { nIndex = nIndex - 10; if (nIndex >= 0 && nIndex <= 9) ResultStr[i] = Char.Parse(nIndex.ToString()); // 11..19 } } } // Every N'th Digit do something for (int i = 0; i < nCount; i = i + 3) { char c = ResultStr[i]; if (char.IsLetter(c)) { int nIndex = Char_CodeLib.IndexOf(c); if (nIndex >= 0 && nIndex <= 9) ResultStr[i] = Char.Parse(nIndex.ToString()); // 1..9 } } // And Return return new String(ResultStr.ToArray()); } } catch (Exception) { /* ignore */ } return String.Empty; } #endregion } }