Jump to content

Sherlock

Moderators
  • Content Count

    1301
  • Joined

  • Last visited

  • Days Won

    28

Posts posted by Sherlock


  1. My last excursion to Google Gemini was successful at the end, but kinda bumpy still. I started out by asking 

    Quote

    I want to protect a configuration file for my Delphi application on Windows against changes and unauthorized access. I was thinking about using encryption, but how should the password be protected?

    Which gave me three possibilities ask the user for the key, use hardware keys or DPAPI. It also explained (quite lengthy) what the pros and cons of each solution are. I was kinda looking for DPAPI anyway so I went for that with

    Quote

    Are there any ready-made components or units that use or facilitate DPAPI?

    That resulted in naming the API calls CryptProtectData and CryptUnptotectData stating they should be declared in Winapi.CryptDPAPI.pas or more general in Winapi.Windows.pas. The main advantage being the lack of a password entirely, everything being managed by Windows itself. It then presented me with some example code 

    unit MyDPAPIUnit;
    
    interface
    
    uses
      Winapi.Windows, Winapi.CryptDPAPI, System.SysUtils;
    
    function ProtectData(const AData: TBytes; const ADescription: string = ''; AForLocalMachine: Boolean = False): TBytes;
    function UnprotectData(const AData: TBytes; var ADescription: string; AForLocalMachine: Boolean = False): TBytes;
    
    implementation
    
    function ProtectData(const AData: TBytes; const ADescription: string = ''; AForLocalMachine: Boolean = False): TBytes;

    and so forth. Of course a first compile with me integrating this into a test project failed, because there is no Winapi.CryptDPAPI. 

    Quote

    In Delphi 12.2 the unit Winapi.CryptDPAPI does not seem to exist.

    I am a nice person, am I not?

    Well of course Gemini then said I was totally right, and that indeed everything required could be found in WinAPI.Windows. Then rambled on about why it got so confused, and basically blaming Embarcadero for not consistently placing specific API segments in their own units like has happened with WinAPI.Messages or WinAPI.ShellAPI. It then presented me with a corrected unit ready to be integrated into my project...or so it thought.

    In its implementation of ProtectData and UnprotectData it used a constant for a specific flag, and that did not go over well with the compiler:

    Quote

    Now Delphi says CRYPTPROTECT_LOCAL_MACHINE is an unknown identifier. What is happening here?

     Here is what it said then 😄 

    Quote

    My mistake! You're absolutely right, CRYPTPROTECT_LOCAL_MACHINE isn't an identifier directly declared as a constant in Delphi that would simply be found in the Winapi.Windows unit. I apologize for this inaccuracy.

    The flags for the CryptProtectData and CryptUnprotectData functions are defined in the Windows API as decimal or hexadecimal values. You can use these values directly in Delphi.

    It then fixed the unit by defining the needed constant correctly. But alas, more errors incoming. I'm still trying to be nice and perhaps lead the machine to realize its own mistakes 🙂

    Quote

    What is CryptProtectData?

    Yeah, it did not react to that slight hint but gave me a crash course on DPAPI. Only after I wrote

    Quote

    Delphi reports that this is an undeclared identifier.

    basically rubbing its nose in the mess, it finally noticed and responded accordingly

    Quote

    You're absolutely right! I sincerely apologize for the ongoing confusion. I'm embarrassed that this bug persisted. It seems I was mistaken about the details of DPAPI constants in Delphi.

    OK, I thought, that looks good. But it took a bad turn quickly yet again, because it continued with the following

    Quote

    CRYPTPROTECT_LOCAL_MACHINE is actually not a fixed part of the Delphi declarations in Winapi.Windows.

    The correct way to set this flag is to use the decimal value 4 or the hexadecimal value $00000004 directly.

    and provided me with a "revised" version of the unit where the only correction was more comments, and the constant was now called CRYPTPROTECT_LOCAL_MACINE_FLAG. Oh, goody. Now it really should work, but I didn't bother with a test compile, I was still feeling merciful and wrote

    Quote

    It's about the CryptProtectData method, which is apparently unknown.

    I'm guessing by now it is trying to get rid of this issue as fast as possible, because the explanations and excuses are getting shorter and lamer

    Quote

    Okay, I understand the problem. I'm sorry this confusion continues. If CryptProtectData is reported as an unknown identifier, it means that the function itself isn't correctly declared in the Delphi units you're using, or that a necessary import statement is missing.

    Well, d'uh!

    So finally it comes up with the perfect reason:

    Quote

    The cause: API import in Delphi

    Delphi declares many Windows API functions in its Winapi.* units. For cryptography functions, including DPAPI, the key unit is Winapi.CryptAPI.

    It looks like my previous assumption that Winapi.Windows covers all cryptography functionality was incorrect. While Winapi.Windows contains basic Windows types and many general API functions, specialized areas like cryptography are often encapsulated in separate units.

    And follows up with the perfect solution for the home run:

    Quote

    The solution: Add Winapi.CryptAPI

    To use CryptProtectData and CryptUnprotectData and their associated structures (such as DATA_BLOB), you must add Winapi.CryptAPI to your uses clause.

    Note the proud addition of the checkmark, so this must be official... it even updated the entire unit. Now it must be right.... but wait, how did this get started again?

    Quote

    There is no Winapi.Cryptapi

    I'm getting pretty good at shattering this machines dreams.

    Quote

    You're absolutely right! My previous information was inaccurate on this point, and I sincerely apologize for the ongoing confusion.

    The CryptProtectData and CryptUnprotectData functions, as well as their associated structures such as DATA_BLOB, are not directly declared in Delphi in a unit called Winapi.CryptAPI. Instead, they are part of the basic Windows API declarations.

    In Delphi, these functions, although they are part of cryptography, are typically made accessible through an external declaration in one of the core units, since they are located in the Windows crypt32.dll.

     I believe it's on to something here

    Quote

    The correct procedure in Delphi

    To use CryptProtectData and CryptUnprotectData in Delphi, you must manually declare them as external functions imported from crypt32.dll.

    Here is the corrected and complete example, which should now work without the nonexistent units:

    This actually sound correct, so I gave the revised unit a go. And it worked as expected. So that was about an hours worth of work, then had a lunch break and came up with some ideas for improvement. So with renewed strength and the feeling of being on the home stretch I wrote

    Quote

    Please adapt the methods to encrypt and decrypt an object of type TFileStream

    It obliged gladly and even provided me with a sample snippet of how to call this glorious code, which it also explained in depth. Actually this could be a nice side effect of AIs, if one takes the time to really read all that jibber jabber.

    Buuuuuuuut, again, the unit would not compile because how the AI thought TFileStreams needed to be handled.

    Quote

    AStream.SetEndOfFile does not exist

    (I'm already out of patience...this short into the second half, oh my)

    Quote

    You're right again! My sincere apologies for the persistent errors in the code examples. I'm sorry I gave you incorrect information here.

    The SetEndOfFile method doesn't actually exist directly for TFileStream. It's a Windows API function that applies to file handles, and is typically achieved by setting the Size property of the TFileStream.

    It then went on and on and finally concluded that this wasn't even necessary because of how TFileStream.WriteBuffer takes care of that automatically. So that was needed to be done was to remove these erroneous calls to inexistent methods...Done, the newly improved unit was presented with a nice remark at the end

    Quote

    Thank you again for your patience and thoughtful feedback. I strive to make the information as accurate as possible.

    Yeah, we'll see about that, I have more nuisance to test you with :classic_ninja:

    Quote

    It would be useful if the UnprotectFileStream method could output a TMemoryStream

    Prompt answer

    Quote

    You have a good idea! It would actually be very convenient if UnprotectFileStream could directly return a TMemoryStream, as this would greatly simplify further processing of the decrypted data in memory without having to go through a temporary file.

    You see that too? Now it's just buttering me up! I sense troubles ahead. To my surprise, the new version worked right away. So I got bold

    Quote

    I would like to change the input for ProtectFileStream to a file name

    Just that one method...but

    Quote

    This is a very useful change! It's often more convenient to call a function directly with a filename instead of having to create a TFileStream first.

    I'll modify the ProtectFileStream and UnprotectFileStreamToMemory functions to accept a filename (string) as input. The functions will then internally create, use, and release the file stream.

    Grrr...at this point I was happy with the one change for the Protect method, and ignored the Unprotect. Copying only parts of the code now. It worked. Again I was provided with samples for calling and quite extensive explanations on the whys and hows. A neat learning experience I completed by asking some questions concerning this technique and security aspects (i.e. could one know how this file was encrypted?)) and possible issues when running this in a Citrix environment. I feel the answers given where quite good and sounded altogether correct.

     

    All in all this has been a pretty cool experiment with the free version of Google Gemini 2.5 Flash. I know there is a Pro version, but I'm not yet ready to spend money on that. Please note I did the session in German, so some translation errors might have slipped in either on my side or the AIs. The entire session can be viewed here: https://g.co/gemini/share/a6d5936220bf

     

    • Like 1

  2. Just to clear and sum up: The error is in the 414 lines of code above this totally fine function. So, until one of the following two things happens, this will turn in to a merry game of guessing:

    1) You provide us with the top 414 lines of code

    2) You let your IDE format the code and discover formatting is "out of whack" beginning somewhere above line 415, and home in on the problem yourself

     

    In any case, I do hope you will provide us with the solution to this riveting riddle.


  3. Just as a side remark: macOS is a desktop OS as well. And it has a quite comfortable market share in the US private and commercially, in Europe however mostly in doctors offices. Though the Apple silicon piqued the interest of many a business.


  4. To expand (or perhaps reiterate) that answer: Components are not installed (or even bought) just to have them. If during programming you discover you would benefit from a ready to use component for some functionality over writing it yourself then you should consult the internet or perhaps this forum on where to find such a component. Event hen, if it is a commercial component, you might want to calculate the cost benefit over writing it yourself. Furthermore it is most often preferable to have sources for components to ensure their use over time.


  5. 2 hours ago, Der schöne Günther said:

    Would you be willing to tell more about your daily usage, maybe in a separate thread? I'd be absolutely interested in typical day to day usage, battery runtimes, standby/wakeup times and where it could start to get tricky (old printer drivers, shell extensions, obscure software,...).

    I think it would be fair to say that Windows on ARM is not for legacy environments. It is intended for new mainstream devices. So no immediate support for decades old printers and other old hardware should be expected. However(!) as soon as it gets more traction I expect that to change.

    • Like 1

  6. You should rather read that as "they will open up the next major construction site as soon as there is money and manpower available, and it will take several releases until it is usable"...and even that should be taken with a grain of salt.

    • Like 1

  7. On 3/23/2025 at 11:56 AM, Vincent Parrett said:

    Signotaur can sign MSIX files

    Oh, that sounds great. For a test run, I downloaded the installer and am quite overwhelmed by the further needs (service user, SSL-certificate, Server) to run the "server". For a small test if my msix could be signed by this, it seems quite the overkill. 😞 


  8. 2 minutes ago, DelphiUdIT said:

    I don't know if there is difference, but have you tried to use:

     

    
    "C:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool"

     

    I have, and that uses an older version of signtool:

    "C:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool.exe" sign /v /a /fd SHA256 C:\Win\SignTest\Win64\Release\SignTest\bin\SignTest.msix
    The following certificate was selected:
        Issued to: My Company
        Issued by: Certum Extended Validation Code Signing 2021 CA
        Expires:   Sat Jan 08 12:11:18 2028
        SHA1 hash: E7C16794EA23F573DE3EA32B5B564717CE84CC75
    
    Done Adding Additional Store
    SignTool Error: An unexpected internal error has occurred.
    Error information: "Error: SignerSign() failed." (-2147024885/0x8007000b)

    File version is  10.0.19041.685. I'm using 10.0.26100.0 which at least gives a slightly better error message.

    • Like 1

  9. I actually don't want to upload to the store. I need this to be able to create a Kiosk application for Windows 10/11. At least that is how I understand what I have found on the net so far. msix is not a must, but recommended. Also signed msix is not a must, but from a customers POV nice to have. Thanks @Patrick PREMARTIN for the /tr hint. I just shortened the command for ease of use. I really hope once the easy signature works, adding the time server will not be an issue.

×