Ian Branch 128 Posted November 28, 2020 Hi Team, D10.4.1. 32bit Apps. I have the need to disguise a Date in a table, currently stored as a Date field, so the actual date cannot be read by any normal User/Utility. Ideally, something to encrypt and decrypt as required. Doesn't need to be super secret, it's just to deter the casual 'hacker'. Currently the Date is stored in a Date field but this is not compulsory. Having said that, it will need to encrypted from a TDateTime (Now), and decrypted back to a TDateTime variable. Thoughts, suggestions, recommendations appreciated. Regards & TIA, Ian Share this post Link to post
FPiette 385 Posted November 28, 2020 A Delphi TDateTime is just a floating point number given the number of day from Dec 31, 1899 (That date is 0). You can store the DateTime as a float or if your are not interested in the time, you can store Trunc(MyDateTime) as an integer and then convert that integer back to TDateTime using a cast. Of you want to obfuscate it a little bit more, you can add a constant before storing and subtract it after reading. And there are many other obvious way to obfuscate a TDateTime once you understand it is a simple floating point number: you can do whatever math you like on it before storing and do the inverse math when reading back. You lust pay attention to the precision of the math to not loose time when computing (Maye be use Extended and do some testing). Share this post Link to post
Guest Posted November 29, 2020 (edited) On 11/28/2020 at 1:11 AM, Ian Branch said: Hi Team, D10.4.1. 32bit Apps. Thoughts, suggestions, recommendations appreciated. First, we must analyze some possibilities, regarding the table and the specific field in question: - If the table is accessed (whatever the intention: read, write, delete, etc ...) by other applications, then all of them should become aware of this new approach: the values of the column "Date" will have their encrypted data. I think this will be a great challenge for you, and even making the new approach impossible. NOTE: Fields of the type: numbered values will not accept such a conversion, because there is a natural barrier here: the range of possible values! Besides how to perform the calculations, etc ... A field of type Date or Time, has its internal representation, in the Database, as a numeric value. Being: the whole part for the days, and the fraction part for the time. This time, it is therefore possible to make calculations with fields of type TDateTime, for example. Check how dates and times are stored in your Database tables. Some systems will work with encrypted data, always in "string" format, and when they are needed, decryption will be done "on demand"! However, this will take many process cycles on the CPU, and, depending on your hardware and number of records, it will be unfeasible! Others adopt encryption of the Database file, or even the entire disk, which makes things a little better in performance! However, there is always a price to pay! Let's think about the following situation: 1) The table is only accessed by your application! Exclusively! 2) The field with the date to be encrypted can be exchanged for another field, for example, a field of type, however, of type "Calculated" 3) Your table is stored in a Database SQL, for example, in Interbase, Firebird, MySQL, etc ... If it is stored in a file, it should be possible without too many problems! For example, the old Paradox data tables were stored in files, with their own structure. Let's go step-by-step: 1) in the table, create a new field, of type "string", with the necessary size to store the encrypted text (the date). This size is related to the type of encryption chosen by you. Test with a date and see what "string" is generated by your encryption. We will call it "myEncriptedDate" NOTE: if your table is in a Database, the best choice would be to create a "stored procedure" to perform the actions. So, you let the Database server do the work for you, and your application just reads or asks to save the values in the table. Now, if you want, you can do this in your client application. 2) to take advantage of the data in your table, you must do a procedure to encrypt the existing dates, and save it in the new "myEncriptedDate" field. 3) now, create a new field of type "Calculated" with the same definitions as your original date field. If the original field is of the TDateTime type, then the calculated field must be of the TDateTime type. And so! We will call it "mySecretDateDecrypted". This field will be of the on-the-fly type, that is, the data will only exist in it when your application opens the table. Otherwise, nothing will exist in it! 3.1) in the "OnCalcFields" event of the Dataset component, for example, FDQuery, you do all the reverse action. In other words, here you take the value of the "myEncriptedDate" field (an encripted string), and revert to a date type value, and, assign this value to the calculated "mySecretDateDecrypted" field. This will be done for all records in the table, and, transparently to the user. 3.2) as always, by encrypting / decrypting data you will always have a price to pay! The performance! - (even in great hardware) 4) to know if everything is as expected, it's simple: use any visual component to access the "mySecretDateDecrypted" field. Of course, this field is READ-ONLY, so when you need to save or update a date you should do this, however, aiming at the "myEncriptedDate" field as something! What will reflect in the "mySecretDateDecrypted" field. 5) if everything went as expected: the original dates were encrypted and saved as "string" in the "myEncriptedDate" field, and, you can see the decrypted dates through the "mySecretDateDecrypted" field, in any data-aware component, then it would be interesting to "delete" the original column with the dates, as it is most needed in the system. here, a sample in code -- BUT IT'S JUST A IDEA! implementation {$R *.dfm} function fncEncryptDateTimeToStringField(lDTcurrent: TDateTime): string; begin // // Encrypt this DateTime... // result := 'encrypted text...'; // how size this value? How size of your "mySecretTablemyEncrytedDate"? end; function fncDecryptMyStringWithDateTime(lText: string): TDateTime; begin // // Decrypt this text... // result := MyDateTime_Resulted; end; procedure TForm2.mySecretTableCalcFields(DataSet: TDataSet); begin // // all record will processed in your table! // mySecretTablemySecretDate.AsDateTime := fncDecryptMyStringWithDateTime(mySecretTablemySecretDate.AsString); end; procedure TForm2.btn_EncryptMyDateTimesClick(Sender: TObject); begin // // FIND A WAY BETTER! // // Is always necessary open a transaction before any activity on the table!!! Else, you'll have a "AV" // myDatabase.StartTranscation; // mySecretTable.Open; // // always use a "try except end" block to "CANCEL" the transaction, if some erro // try // while not mySecretTable.Eof do // this way, will be very "hard" if you have many many record... you can use FDQuery and SQL instruction or other way! begin // // mode Edit mySecretTable.Edit; // // mySecretTable_SOURCE_DATETIME is my source to DateTime values ok? mySecretTablemyEncrytedDate.AsString := fncEncryptDateTimeToStringField(mySecretTable_SOURCE_DATETIME.asDateTime); // // post and commit data mySecretTable.Post; // mySecretTable.Next; end; // // commit on Database myDatabase.Commit; // except on E: Exception do begin // show error myDatabase.RollBack; end; end; // mySecretTable.Close; // end; hug Edited November 29, 2020 by Guest Share this post Link to post
Guest Posted November 29, 2020 One of the most fast and small algorithm is XXTea, like this one https://github.com/xxtea/xxtea-delphi Now it is implemented n many library and will fit your need, there is many implementation in many libraries, also exists in EurekaLog. Use it like this uses System.SysUtils, XXTEA in 'XXTEA.pas', Base64 in 'Base64.pas'; var CurrentTime,DecryptedDateTime: TDateTime; NowBytes, Key, EncDateBytes,DecDateBytes: TBytes; begin CurrentTime := Now; SetLength(NowBytes, SizeOf(CurrentTime)); PDateTime(@NowBytes[0])^ := CurrentTime; // or use only two lines skipping the use of CurretTime //SetLength(NowBytes, SizeOf(Now)); //PDateTime(@NowBytes[0])^ := Now; Key := BytesOf('123456'); EncDateBytes := XXTEA.Encrypt(NowBytes,Key); Writeln('Cuurrent DateTime = '#9+DateTimeToStr(CurrentTime)); Writeln('EncryptedString = '#9+Base64.Encode(EncDateBytes)); DecDateBytes := XXTEA.Decrypt(EncDateBytes,Key); DecryptedDateTime := PDateTime(@DecDateBytes[0])^; Writeln('Decrypted DateTime = '#9+DateTimeToStr(DecryptedDateTime)); Readln; end. Few things to point to 1) You can use the Base64 or simply use the encrypted binary form. 2) The Key can be fixed like the above example, or you can use some another entry from the other available data (eg, orderNumber), or better to combine one fixed with an entry to generate the key, XXTEA is fast and does need key expansion, perfect for short strings or small data. 3) As i said if the license of the above library is not suitable, and you have free time for some fun then head to Wikipedia and implement it on your own ! https://en.wikipedia.org/wiki/XXTEA 4) You also can replace XXTea with RC4, or any simple and fast encryption that does not need key expansion. Share this post Link to post
Ian Branch 128 Posted November 29, 2020 emailx45 & Kas, Thank you for your suggestions. Both appreciated. I do use Eurekalog so that is a good option. Not to waste an opportunity to learn I will try both methodologies and see how they go. Thank you again, Regards, Ian Share this post Link to post
Ian Branch 128 Posted November 29, 2020 Update - I have gone a combined route using the field as suggested by emailx45 and TEA, via EurekaLog, pointed to by Kas. Thanks Guys, Appreciated, Ian Share this post Link to post
Guest Posted November 29, 2020 (edited) 6 hours ago, Ian Branch said: Update - I have gone a combined route using the field as suggested by emailx45 and TEA, via EurekaLog, pointed to by Kas. Thanks Guys, Appreciated, Ian try this "other" my sample RAD Studio 10.3.3 Arch (Rio) VCL project for test source type TfrmFormMain = class(TForm) Edit1: TEdit; Edit2: TEdit; mmMemoEncryptedText: TMemo; ListBox1: TListBox; Label1: TLabel; Label2: TLabel; Label3: TLabel; procedure FormCreate(Sender: TObject); procedure ListBox1Click(Sender: TObject); private procedure prcEncryp_Decrypt_DateTime(lDTvalue: TDateTime); // just for easy access to components on Form!!! public { Public declarations } end; var frmFormMain: TfrmFormMain; implementation {$R *.dfm} { -- Simple way using just "Base64" convertion and reverting it! -- To more complex, you can use others tecnnics to encrypt, zip, etc... or "all" togheter! } uses System.NetEncoding; procedure TfrmFormMain.FormCreate(Sender: TObject); var lNewDateTimeValues: TDateTime; i : Integer; begin Randomize; // for i := 0 to 9 do begin lNewDateTimeValues := now + (Random(365) + (Random(86400000) / 100000000)); // days.milliseconds // ListBox1.Items.Add(DateTimeToStr(lNewDateTimeValues)); end; // ListBox1.ItemIndex := Random(ListBox1.Items.Count); // Edit1.Text := ListBox1.Items[ListBox1.ItemIndex]; // ListBox1Click(Self); end; procedure TfrmFormMain.ListBox1Click(Sender: TObject); begin if (ListBox1.ItemIndex > -1) then begin Edit1.Text := ListBox1.Items[ListBox1.ItemIndex]; // // BAD PLACE to do it, ok? Just for show quickly // // here, im not treating any "Exception" ok? ... do it! // no validation, no nothing... then, do it!!! // prcEncryp_Decrypt_DateTime(StrToDateTime(Edit1.Text)); end; end; procedure TfrmFormMain.prcEncryp_Decrypt_DateTime(lDTvalue: TDateTime); var lMyTArrayBytes: TArray<Byte>; lTextEncrypted: string; begin // // here, im not treating any "Exception" ok? ... do it! // no validation, no nothing... then, do it!!! // lMyTArrayBytes := BytesOf(DateTimeToStr(lDTvalue)); // lTextEncrypted := TNetEncoding.Base64.EncodeBytesToString(lMyTArrayBytes); // mmMemoEncryptedText.Text := Format('%s [size: %d]', [ { } lTextEncrypted, { } lTextEncrypted.Length { } ]); // lMyTArrayBytes := TNetEncoding.Base64.DecodeStringToBytes(lTextEncrypted); // Edit2.Text := TEncoding.Default.GetString(lMyTArrayBytes); end; end. hug Edited November 29, 2020 by Guest Share this post Link to post