unit ECCProcess; (* This file is part of Synopse framework. Synopse framework. Copyright (C) 2022 Arnaud Bouchez Synopse Informatique - https://synopse.info *** BEGIN LICENSE BLOCK ***** Version: MPL 1.1/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is Synopse framework. The Initial Developer of the Original Code is Arnaud Bouchez. Portions created by the Initial Developer are Copyright (C) 2022 the Initial Developer. All Rights Reserved. Contributor(s): - Nicolas Marchand (MC) Alternatively, the contents of this file may be used under the terms of either the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the LGPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of either the GPL or the LGPL, and not to allow others to use your version of this file under the terms of the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the MPL, the GPL or the LGPL. ***** END LICENSE BLOCK ***** *) interface {$I Synopse.inc} uses SysUtils, Classes, SynCommons, SynTable, SynEcc, SynCrypto; /// end-user command to create a new private/public key file // - as used in the ECC.dpr command-line sample project function ECCCommandNew(const AuthPrivKey: TFileName; const AuthPassword: RawUTF8; AuthPasswordRounds: integer; const Issuer: RawUTF8; StartDate: TDateTime; ExpirationDays: integer; const SavePassword: RawUTF8; SavePassordRounds, SplitFiles: integer): TFileName; /// end-user command to create a renew a .private file password // - as used in the ECC.dpr command-line sample project function ECCCommandRekey(const AuthPrivKey: TFileName; const AuthPassword: RawUTF8; AuthPasswordRounds: integer; const SavePassword: RawUTF8; SavePassordRounds: integer): TFileName; /// end-user command to sign a file using a private key file // - as used in the ECC.dpr command-line sample project function ECCCommandSignFile(const FileToSign, AuthPrivKey: TFileName; const AuthPassword: RawUTF8; AuthPasswordRounds: integer; const MetaNameValuePairs: array of const): TFileName; /// end-user command to verify a file signature // - as used in the ECC.dpr command-line sample project function ECCCommandVerifyFile(const FileToVerify, AuthPubKey: TFileName; const AuthBase64: RawUTF8): TECCValidity; /// end-user command to create a .inc pascal source file from a private key file // - ready to be included within the executable binary as private secret // - as used in the ECC.dpr command-line sample project function ECCCommandSourceFile(const AuthPrivKey: TFileName; const AuthPassword: RawUTF8; AuthPasswordRounds: integer; const ConstName, Comment, PassWord: RawUTF8): TFileName; /// end-user command to create a .json base-64 text array from a set of public key files // - ready to be included e.g. as settings of any server // - ECCCommandChainCertificates(['*']) will create a 'chain.ca' of all // public key files in the current folder // - as used in the ECC.dpr command-line sample project function ECCCommandChainCertificates(const CertFiles: array of RawUTF8): TFileName; /// end-user command to display the json information from a .private file // - as used in the ECC.dpr command-line sample project function ECCCommandInfoPrivFile(const AuthPrivKey: TFileName; const AuthPassword: RawUTF8; AuthPasswordRounds: integer): RawUTF8; /// end-user command to encrypt a file with the .synecc format // - as used in the ECC.dpr command-line sample project procedure ECCCommandCryptFile(const FileToCrypt, DestFile, AuthPubKey: TFileName; const AuthBase64, AuthSerial, Password: RawUTF8; PasswordRounds: integer; Algo: TECIESAlgo=ecaUnknown); /// end-user command to decrypt a .synecc file // - as used in the ECC.dpr command-line sample project // - if AuthPrivKey is not set, it will search for the stored TECCCertificate.Serial function ECCCommandDecryptFile(const FileToDecrypt, DestFile, AuthPrivKey: TFileName; const AuthPassword: RawUTF8; AuthPasswordRounds: integer; const DecryptPassword: RawUTF8; DecryptPasswordRounds: integer; Signature: PECCSignatureCertifiedContent; MetaData: PRawJSON): TECCDecrypt; /// end-user command to verify a .synecc file signature, after decryption // - as used in the ECC.dpr command-line sample project // - the supplied signature can be retrieved from ECCCommandDecryptFile() function ECCCommandVerifyDecryptedFile(const FileToVerify: TFileName; const Signature: TECCSignatureCertifiedContent): TECCValidity; /// end-user command to initialize local cheat.private/.public files // - as used in the ECC.dpr command-line sample project // - those files will let new/rekey commands create a .cheat file, encrypted // using the master cheat.public key, so that the password of the generated // .private key file could be retrieved using the cheat.private key and its // password/round, via ECCCommandCheat (ECC cheat) // - it may be convenient to remember a single password instead of several, // and security will be enhanced by the fact that cheat.private stays hidden function ECCCommandCheatInit(const Issuer, CheatPassword: RawUTF8; CheatRounds: integer): TFileName; /// end-user command to display a .private password as stored in its .cheat file // - as used in the ECC.dpr command-line sample project // - using the master password/rounds of the local cheat.private key, as // generated by ECCCommandCheatInit (ECC cheatinit) function ECCCommandCheat(const PrivateFile: TFileName; const CheatPassword: RawUTF8; CheatRounds: integer; out authpass: RawUTF8; out authround: integer): RawUTF8; /// end-user command to encrypt a file with the symetric .synaead format // - will use symetric encryption via AES-256-CFB/PKCS7 over PBKDF2_HMAC_SHA256 // - as used in the ECC.dpr command-line sample project procedure AEADCommandCryptFile(const FileToCrypt, DestFile: TFileName; const Password, PasswordSalt: RawUTF8; PasswordRounds: integer); /// end-user command to decrypt a symetric .synaead file // - will use symetric encryption via AES-256-CFB/PKCS7 over PBKDF2_HMAC_SHA256 // - as used in the ECC.dpr command-line sample project procedure AEADCommandDecryptFile(const FileToDecrypt, DestFile: TFileName; const Password, PasswordSalt: RawUTF8; PasswordRounds: integer); type /// the actions implemented by ECCCommand() // - as used in the ECC.dpr command-line sample project // - retrieved from the command line as first parameter TECCCommand = ( ecHelp, ecNew, ecRekey, ecSign, ecVerify, ecSource, ecInfoPriv, ecChain, ecChainAll, ecCrypt, ecDecrypt, ecInfoCrypt, ecAeadCrypt, ecAeadDecrypt, ecCheatInit, ecCheat); /// the result code returned by ECCCommand() // - as used in the ECC.dpr command-line sample project TECCCommandError = ( eccSuccess, eccUnknownCommand, eccValidationError, eccError, eccException); /// execute the encryption process corresponding to the command line options // - as used in the ECC.dpr sample project // - returns the ExitCode expected value (0=eccSuccess) function ECCCommand(cmd: TECCCommand; const sw: ICommandLine): TECCCommandError; const CHEAT_FILEEXT = '.cheat'; CHEAT_FILEMASTER = 'cheat'; CHEAT_ROUNDS = 100000; CHEAT_SPLIT = 100; AEAD_FILEEXT = '.synaead'; DEFAULT_AEADROUNDS = 60000; implementation procedure CreateCheatFile(secret: TECCCertificateSecret; const SavePassword: RawUTF8; SavePasswordRounds: integer); var json,bin: RawByteString; master: TECCCertificate; fn: TFileName; begin master := TECCCertificate.Create; try if master.FromFile(CHEAT_FILEMASTER) then try if SavePasswordRounds=DEFAULT_ECCROUNDS then json := SavePassword else json := JSONEncode(['pass',SavePassword,'rounds',SavePasswordRounds]); bin := TAESPRNG.Main.AFSplit(pointer(json)^,length(json),CHEAT_SPLIT); fn := UTF8ToString(secret.Serial)+CHEAT_FILEEXT; FileFromString(master.Encrypt(bin),fn); finally FillZero(json); FillZero(bin); end; finally master.Free; end; end; function ECCCommandNew(const AuthPrivKey: TFileName; const AuthPassword: RawUTF8; AuthPasswordRounds: integer; const Issuer: RawUTF8; StartDate: TDateTime; ExpirationDays: integer; const SavePassword: RawUTF8; SavePassordRounds, SplitFiles: integer): TFileName; var auth,new: TECCCertificateSecret; begin if AuthPrivKey='' then auth := nil else auth := TECCCertificateSecret.CreateFromSecureFile(AuthPrivKey,AuthPassword,AuthPasswordRounds); try // generate pair new := TECCCertificateSecret.CreateNew(auth,Issuer,ExpirationDays,StartDate); try // save private key as .private password-protected binary file new.SaveToSecureFiles(SavePassword,'.',SplitFiles,64,SavePassordRounds); CreateCheatFile(new,SavePassword,SavePassordRounds); // save public key as .public JSON file result := ChangeFileExt(new.SaveToSecureFileName,ECCCERTIFICATEPUBLIC_FILEEXT); new.ToFile(result); finally new.Free; end; finally auth.Free; end; end; function ECCCommandRekey(const AuthPrivKey: TFileName; const AuthPassword: RawUTF8; AuthPasswordRounds: integer; const SavePassword: RawUTF8; SavePassordRounds: integer): TFileName; var auth: TECCCertificateSecret; begin auth := TECCCertificateSecret.CreateFromSecureFile(AuthPrivKey,AuthPassword,AuthPasswordRounds); try auth.SaveToSecureFile(SavePassword,'.',64,SavePassordRounds); CreateCheatFile(auth,SavePassword,SavePassordRounds); result := auth.SaveToSecureFileName; finally auth.Free; end; end; function ECCCommandSignFile(const FileToSign, AuthPrivKey: TFileName; const AuthPassword: RawUTF8; AuthPasswordRounds: integer; const MetaNameValuePairs: array of const): TFileName; var auth: TECCCertificateSecret; begin auth := TECCCertificateSecret.CreateFromSecureFile(AuthPrivKey,AuthPassword,AuthPasswordRounds); try result := auth.SignFile(FileToSign,MetaNameValuePairs); finally auth.Free; end; end; function ECCCommandSourceFile(const AuthPrivKey: TFileName; const AuthPassword: RawUTF8; AuthPasswordRounds: integer; const ConstName, Comment, PassWord: RawUTF8): TFileName; var auth: TECCCertificateSecret; begin auth := TECCCertificateSecret.CreateFromSecureFile(AuthPrivKey,AuthPassword,AuthPasswordRounds); try result := AuthPrivKey+'.inc'; FileFromString(auth.SaveToSource(ConstName,Comment,Password),result); finally auth.Free; end; end; function ECCCommandVerifyFile(const FileToVerify, AuthPubKey: TFileName; const AuthBase64: RawUTF8): TECCValidity; var content: RawByteString; auth: TECCCertificate; cert: TECCSignatureCertified; begin content := StringFromFile(FileToVerify); if content='' then raise EECCException.CreateUTF8('File not found: %',[FileToVerify]); cert := TECCSignatureCertified.CreateFromFile(FileToVerify); try if not cert.Check then begin result := ecvInvalidSignature; exit; end; auth := TECCCertificate.Create; try if auth.FromAuth(AuthPubKey,AuthBase64,cert.AuthoritySerial) then result := cert.Verify(auth,pointer(content),length(content)) else result := ecvUnknownAuthority; finally auth.Free; end; finally cert.Free; end; end; function ECCCommandVerifyDecryptedFile(const FileToVerify: TFileName; const Signature: TECCSignatureCertifiedContent): TECCValidity; var content: RawByteString; auth: TECCCertificate; cert: TECCSignatureCertified; begin content := StringFromFile(FileToVerify); if content='' then raise EECCException.CreateUTF8('File not found: %',[FileToVerify]); cert := TECCSignatureCertified.CreateFrom(Signature); try auth := TECCCertificate.Create; try result := ecvUnknownAuthority; if auth.FromAuth('','',cert.AuthoritySerial) then result := cert.Verify(auth,pointer(content),length(content)); finally auth.Free; end; finally cert.Free; end; end; procedure ECCCommandCryptFile(const FileToCrypt, DestFile, AuthPubKey: TFileName; const AuthBase64, AuthSerial, Password: RawUTF8; PasswordRounds: integer; Algo: TECIESAlgo); var content: RawByteString; auth: TECCCertificate; begin content := StringFromFile(FileToCrypt); if content='' then raise EECCException.CreateUTF8('File not found: %',[FileToCrypt]); auth := TECCCertificate.Create; try if not auth.FromAuth(AuthPubKey,AuthBase64,AuthSerial) then raise EECCException.Create('No public key'); if not auth.EncryptFile(FileToCrypt,DestFile,Password,PasswordRounds,Algo,true) then raise EECCException.CreateUTF8('EncryptFile failed for %',[FileToCrypt]); finally auth.Free; FillZero(content); end; end; function ECCCommandDecryptFile(const FileToDecrypt, DestFile, AuthPrivKey: TFileName; const AuthPassword: RawUTF8; AuthPasswordRounds: integer; const DecryptPassword: RawUTF8; DecryptPasswordRounds: integer; Signature: PECCSignatureCertifiedContent; MetaData: PRawJSON): TECCDecrypt; var auth: TECCCertificateSecret; head: TECIESHeader; priv: TFileName; begin auth := TECCCertificateSecret.Create; try result := ecdNoPrivateKey; if FileExists(AuthPrivKey) then priv := AuthPrivKey else begin if not ECIESHeaderFile(FileToDecrypt,head) then exit; priv := UTF8ToString(ECCText(head.recid)); if not ECCKeyFileFind(priv,true) then exit; // not found local .private from header end; if not auth.LoadFromSecureFile(priv,AuthPassword,AuthPasswordRounds) then exit; result := auth.DecryptFile(FileToDecrypt,DestFile, DecryptPassword,DecryptPasswordRounds,Signature,MetaData); finally auth.Free; end; end; function ECCCommandChainCertificates(const CertFiles: array of RawUTF8): TFileName; var n,i: integer; files: TFileNameDynArray; begin result := ''; n := length(CertFiles); if n=0 then exit; if (n=1) and (CertFiles[0]='*') then begin files := FindFilesDynArrayToFileNames( FindFiles('.','*'+ECCCERTIFICATEPUBLIC_FILEEXT)); result := 'chain'+ECCCERTIFICATES_FILEEXT; end else begin SetLength(files,n); for i := 0 to n-1 do begin files[i] := UTF8ToString(CertFiles[i]); if not ECCKeyFileFind(files[i],false) then exit; end; result := format('chain%d'+ECCCERTIFICATES_FILEEXT,[GetTickCount64]); end; with TECCCertificateChain.CreateFromFiles(files) do try if ValidateItems<>nil then begin result := ''; raise EECCException.Create('Some of the certificates are invalid'); end; SaveToFile(result); finally Free; end; end; function ECCCommandInfoPrivFile(const AuthPrivKey: TFileName; const AuthPassword: RawUTF8; AuthPasswordRounds: integer): RawUTF8; var auth: TECCCertificateSecret; begin auth := TECCCertificateSecret.CreateFromSecureFile(AuthPrivKey,AuthPassword,AuthPasswordRounds); try result := JSONReformat(VariantSaveJSON(auth.ToVariant)) finally auth.Free; end; end; function ECCCommandCheatInit(const Issuer, CheatPassword: RawUTF8; CheatRounds: integer): TFileName; var new: TECCCertificateSecret; priv: RawByteString; begin if FileExists(CHEAT_FILEMASTER+ECCCERTIFICATEPUBLIC_FILEEXT) or FileExists(CHEAT_FILEMASTER+ECCCERTIFICATESECRET_FILEEXT) then raise EECCException.Create(CHEAT_FILEMASTER+' file already exist'); // generate pair new := TECCCertificateSecret.CreateNew(nil,Issuer); try // save private key as cheat.private password-protected binary file priv := new.SaveToSecureBinary(CheatPassword,128,CheatRounds); FileFromString(priv,CHEAT_FILEMASTER+ECCCERTIFICATESECRET_FILEEXT); // save public key as mastercheat.public JSON file result := CHEAT_FILEMASTER+ECCCERTIFICATEPUBLIC_FILEEXT; JSONReformatToFile(VariantSaveJSON(new.ToVariant),result); finally new.Free; FillZero(priv); end; end; function ECCCommandCheat(const PrivateFile: TFileName; const CheatPassword: RawUTF8; CheatRounds: integer; out authpass: RawUTF8; out authround: integer): RawUTF8; var bin,split,json: RawByteString; master: TECCCertificateSecret; doc: TDocVariantData; fn: TFileName; res: TECCDecrypt; begin fn := ChangeFileExt(PrivateFile,CHEAT_FILEEXT); bin := StringFromFile(fn); if bin='' then raise EECCException.CreateUTF8('Unknown file %',[fn]); master := TECCCertificateSecret.CreateFromSecureFile( CHEAT_FILEMASTER,CheatPassword,CheatRounds); try res := master.Decrypt(bin,split); if res<>ecdDecrypted then raise EECCException.CreateUTF8('% on %',[ToText(res)^,fn]); json := TAESPRNG.AFUnsplit(split,CHEAT_SPLIT); if json='' then raise EECCException.CreateUTF8('Incorrect file %',[fn]); if not doc.InitJSON(json) then doc.InitObject(['pass',json,'rounds',DEFAULT_ECCROUNDS],JSON_OPTIONS_FAST); authpass := doc.U['pass']; authround := doc.I['rounds']; result := doc.ToJSON('','',jsonHumanReadable); finally master.Free; FillZero(json); end; end; procedure AEADProcess(Encrypt: boolean; var Source: RawByteString; const DestFileName: TFileName; const Password, PasswordSalt: RawUTF8; PasswordRounds: integer); var aeskey: THash256; dst: RawByteString; begin try PBKDF2_HMAC_SHA256(Password,PasswordSalt,PasswordRounds,aeskey,'salt'); try dst := TAESCFBCRC.MACEncrypt(Source,aeskey,Encrypt); try if dst='' then raise EECCException.CreateUTF8('MACEncrypt failed for %',[DestFileName]); if not FileFromString(dst,DestFileName) then raise EECCException.CreateUTF8('FileFromString failed for %',[DestFileName]); finally FillZero(dst); end; finally FillZero(aeskey); end; finally FillZero(Source); end; end; procedure AEADCommandCryptFile(const FileToCrypt, DestFile: TFileName; const Password, PasswordSalt: RawUTF8; PasswordRounds: integer); var plain: RawByteString; dest: TFileName; begin plain := StringFromFile(FileToCrypt); if plain='' then raise EECCException.CreateUTF8('File not found: %',[FileToCrypt]); if DestFile='' then dest := FileToCrypt+AEAD_FILEEXT else dest := DestFile; AEADProcess({encrypt=}true,plain,dest,Password,PasswordSalt,PasswordRounds); end; procedure AEADCommandDecryptFile(const FileToDecrypt, DestFile: TFileName; const Password, PasswordSalt: RawUTF8; PasswordRounds: integer); var encrypted: RawByteString; dest: TFileName; begin encrypted := StringFromFile(FileToDecrypt); if encrypted='' then raise EECCException.CreateUTF8('File not found: %',[FileToDeCrypt]); if DestFile='' then dest := GetFileNameWithoutExt(FileToDecrypt) else dest := DestFile; AEADProcess({encrypt=}false,encrypted,dest,Password,PasswordSalt,PasswordRounds); end; function ECCCommand(cmd: TECCCommand; const sw: ICommandLine): TECCCommandError; procedure WriteVerif(verif: TECCValidity; const filename: TFileName; const sw: ICommandLine); var res: string; begin res := SysUtils.LowerCase(GetCaptionFromEnum(TypeInfo(TECCValidity),ord(verif))); if verif in ECC_VALIDSIGN then sw.Text(' % file verified as %.',[filename,res],ccLightGreen) else begin sw.Text(' % file verification failure: % (%).',[filename,res,ord(verif)],ccLightRed); result := eccValidationError; end; end; procedure WritePassword(const privfile: TFileName; const pass: RawUTF8; rounds: integer); var a: TECDHEAuth; privkey: RawUTF8; begin if privfile='' then exit; sw.Text('Corresponding TSynPersistentWithPassword.ComputePassword:',[]); sw.Text(' encryption %',[TSynPersistentWithPassword.ComputePassword(pass)],ccLightBlue); privkey := StringToUTF8(copy(GetFileNameWithoutExt(privfile),1,8)); for a := low(a) to high(a) do sw.Text(' % %',[ToText(a)^,TECDHEProtocol.FromKeyCompute( privkey,pass,rounds,'',a)],ccLightBlue); sw.Text('',[]); end; var issuer, authpass, savepass, constname, comment, json: RawUTF8; meta: RawJSON; start: TDateTime; authrounds, days, saverounds, splitfiles: integer; msg: string; origfile,auth,newfile,jsonfile: TFileName; algo: TECIESAlgo; decrypt: TECCDecrypt; decryptsign: TECCSignatureCertifiedContent; begin result := eccSuccess; if sw=nil then raise EECCException.Create('ECCCommand(nil)'); try try case cmd of ecNew: begin repeat auth := sw.AsString('Auth','', 'Enter the first chars of the .private file name of the signing authority.'#13#10+ 'Will create a self-signed certificate if left void.'); until (auth='') or ECCKeyFileFind(auth,true) or sw.NoPrompt; if auth<>'' then begin sw.Text('Will use: %'#13#10,[ExtractFileName(auth)]); authpass := sw.AsUTF8('AuthPass','', 'Enter the PassPhrase of this .private file.'); authrounds := sw.AsInt('AuthRounds',DEFAULT_ECCROUNDS, 'Enter the PassPhrase iteration rounds of this .private file.'); end else authrounds := 0; issuer := sw.AsUTF8('Issuer',ExeVersion.User, 'Enter Issuer identifier text.'#13#10'Will be truncated to 15-20 ascii-7 chars.'); start := sw.AsDate('Start',NowUTC, 'Enter the YYYY-MM-DD start date of its validity.'#13#10+ '0 will create a never-expiring certificate.'); if start<=0 then days := 0 else days := sw.AsInt('Days',365,'Enter the number of days of its validity.'); repeat savepass := sw.AsUTF8('NewPass',TAESPRNG.Main.RandomPassword(12), 'Enter a private PassPhrase for the new key (at least 8 chars long).'#13#10+ 'Save this in a safe place: if you forget it, the key will be useless!'); until (length(savepass)>=8) or sw.NoPrompt; repeat saverounds := sw.AsInt('NewRounds',DEFAULT_ECCROUNDS, 'Enter the PassPhrase iteration rounds for the new key (at least 1000).'#13#10+ 'The higher, the safer, but will demand more computation time.'); until (saverounds>=1000) or sw.NoPrompt; splitfiles := 1; {repeat splitfiles := sw.AsInt('SplitFiles',1, 'Into how many files the private key should be parceled out.'); until (splitfiles>0) or sw.NoPrompt;} newfile := EccCommandNew( auth,authpass,authrounds,issuer,start,days,savepass,saverounds,splitfiles); WritePassword(newfile,savepass,saverounds); if newfile<>'' then begin newfile := newfile+'/.private'; if FileExists(ChangeFileExt(newfile, CHEAT_FILEEXT)) then newfile := newfile+('/'+CHEAT_FILEEXT); end; end; ecRekey: begin repeat auth := sw.AsString('Auth','', 'Enter the first chars of the .private certificate file name.'); until ECCKeyFileFind(auth,true) or sw.NoPrompt; sw.Text('Will use: %'#13#10,[ExtractFileName(auth)]); authpass := sw.AsUTF8('AuthPass','', 'Enter the PassPhrase of this .private file.'); authrounds := sw.AsInt('AuthRounds',DEFAULT_ECCROUNDS, 'Enter the PassPhrase iteration rounds of this .private file.'); repeat savepass := sw.AsUTF8('NewPass',TAESPRNG.Main.RandomPassword(12), 'Enter a NEW private PassPhrase for the key (at least 8 chars long).'#13#10+ 'Save this in a safe place: if you forget it, the key will be useless!'); until (length(savepass)>=8) or sw.NoPrompt; repeat saverounds := sw.AsInt('NewRounds',DEFAULT_ECCROUNDS, 'Enter the NEW PassPhrase iteration rounds for the key (at least 1000).'#13#10+ 'The higher, the safer, but will demand more computation time.'); until (saverounds>=1000) or sw.NoPrompt; newfile := EccCommandRekey(auth,authpass,authrounds,savepass,saverounds); WritePassword(newfile,savepass,saverounds); if FileExists(ChangeFileExt(newfile,CHEAT_FILEEXT)) then newfile := newfile+('/'+CHEAT_FILEEXT); end; ecSign: begin repeat origfile := sw.AsString('File','','Enter the name of the file to be signed.'); until FileExists(origfile) or sw.NoPrompt; repeat auth := sw.AsString('Auth','', 'Enter the first chars of the .private file name of the signing authority.'); until ECCKeyFileFind(auth,true) or sw.NoPrompt; sw.Text('Will use: %'#13#10,[ExtractFileName(auth)]); authpass := sw.AsUTF8('Pass','', 'Enter the PassPhrase of this .private file.'); authrounds := sw.AsInt('Rounds',DEFAULT_ECCROUNDS, 'Enter the PassPhrase iteration rounds of this .private file.'); newfile := EccCommandSignFile(origfile,auth,authpass,authrounds,[]); end; ecVerify: begin repeat origfile := sw.AsString('File','','Enter the name of the file to be verified.'); until sw.NoPrompt or (FileExists(origfile) and FileExists(origfile+ECCCERTIFICATESIGN_FILEEXT)); WriteVerif(ECCCommandVerifyFile(origfile,sw.AsString('auth','',''),''),origfile,sw); end; ecSource: begin repeat auth := sw.AsString('Auth','', 'Enter the first chars of the .private certificate file name.'); until ECCKeyFileFind(auth,true) or sw.NoPrompt; sw.Text('Will use: %'#13#10,[ExtractFileName(auth)]); authpass := sw.AsUTF8('Pass','', 'Enter the PassPhrase of this .private file.'); authrounds := sw.AsInt('Rounds',DEFAULT_ECCROUNDS, 'Enter the PassPhrase iteration rounds of this .private file.'); constname := sw.AsUTF8('Const','', 'Enter the variable name to define the const in source.'); comment := sw.AsUTF8('Comment','', 'Enter some optional comment to identify this private key.'); newfile := EccCommandSourceFile(auth,authpass,authrounds,constname,comment, TAESPRNG.Main.RandomPassword(24)); end; ecInfoPriv: begin repeat auth := sw.AsString('Auth','', 'Enter the first chars of the .private certificate file name.'); until ECCKeyFileFind(auth,true) or sw.NoPrompt; if not sw.NoPrompt then sw.Text('Will use: %'#13#10,[ExtractFileName(auth)]); authpass := sw.AsUTF8('Pass','', 'Enter the PassPhrase of this .private file.'); authrounds := sw.AsInt('Rounds',DEFAULT_ECCROUNDS, 'Enter the PassPhrase iteration rounds of this .private file.'); json := ECCCommandInfoPrivFile(auth,authpass,authrounds); sw.Text('%',[json]); jsonfile := sw.AsString('Json','',''); if jsonfile <> '' then FileFromString(json,jsonfile); if not sw.NoPrompt then WritePassword(auth,authpass,authrounds); end; ecChain: newfile := ECCCommandChainCertificates(sw.AsArray); ecChainAll: newfile := ECCCommandChainCertificates(['*']); ecCrypt: begin repeat origfile := sw.AsString('File','','Enter the name of the file to be encrypted.'); until FileExists(origfile) or sw.NoPrompt; repeat newfile := SysUtils.Trim(sw.AsString('Out',origfile+ENCRYPTED_FILEEXT, 'Enter the name of the encrypted file')); until (newfile <> '') or sw.NoPrompt; repeat auth := sw.AsString('Auth','', 'Enter the first chars of the .public file name of the encryption authority.'); until ECCKeyFileFind(auth,false) or sw.NoPrompt; sw.Text('Will use: %'#13#10,[ExtractFileName(auth)]); authpass := sw.AsUTF8('SaltPass','salt','Enter the optional PassPhrase to be used for encryption.'); authrounds := sw.AsInt('SaltRounds',DEFAULT_ECCROUNDS, 'Enter the PassPhrase iteration rounds.'); algo := TECIESAlgo(sw.AsEnum('Algo', '0', TypeInfo(TECIESAlgo), '')); ECCCommandCryptFile(origfile,newfile,auth,'','',authpass,authrounds,algo); end; ecInfoCrypt: begin repeat origfile := sw.AsString('File','','Enter the name of the encrypted file.'); until FileExists(origfile) or sw.NoPrompt; newfile := sw.AsString('RawFile','',''); json := JSONReformat(ECIESHeaderText(origfile,newfile)); sw.Text('%',[json]); jsonfile := sw.AsString('Json','',''); if jsonfile <> '' then FileFromString(json,jsonfile); end; ecDecrypt: begin repeat origfile := sw.AsString('File','','Enter the name of the file to be decrypted.'); until FileExists(origfile) or sw.NoPrompt; repeat newfile := SysUtils.Trim(sw.AsString('Out',GetFileNameWithoutExt(origfile)+'.2', 'Enter the name of the decrypted file')); until (newfile <> '') or sw.NoPrompt; authpass := sw.AsUTF8('AuthPass','', 'Enter the PassPhrase of the associated .private file.'); authrounds := sw.AsInt('AuthRounds',DEFAULT_ECCROUNDS, 'Enter the PassPhrase iteration rounds of this .private file.'); savepass := sw.AsUTF8('SaltPass','salt','Enter the optional PassPhrase to be used for decryption.'); saverounds := sw.AsInt('SaltRounds',DEFAULT_ECCROUNDS, 'Enter the PassPhrase iteration rounds.'); decrypt := ECCCommandDecryptFile(origfile,newfile, sw.AsString('Auth','',''),authpass,authrounds,savepass,saverounds,@decryptsign,@meta); msg := SysUtils.LowerCase(GetCaptionFromEnum(TypeInfo(TECCDecrypt),ord(decrypt))); if decrypt in ECC_VALIDDECRYPT then begin if decrypt=ecdDecryptedWithSignature then WriteVerif(ECCCommandVerifyDecryptedFile(newfile,decryptsign),newfile,sw); if meta<>'' then sw.Text(' % file meta = %',[origfile,meta],ccGreen); sw.Text(' % file %.',[origfile,msg],ccLightGreen); end else begin sw.Text(' % file decryption failure: % (%).',[origfile,msg,ord(decrypt)],ccLightRed); result := eccError; end; end; ecAeadCrypt: begin repeat origfile := sw.AsString('File','','Enter the name of the file to be encrypted.'); until FileExists(origfile) or sw.NoPrompt; repeat newfile := SysUtils.Trim(sw.AsString('Out',origfile+AEAD_FILEEXT, 'Enter the name of the encrypted file')); until (newfile <> '') or sw.NoPrompt; authpass := sw.AsUTF8('Pass','', 'Enter the PassPhrase to be used for encryption.'); savepass := sw.AsUTF8('Salt','salt','Enter the optional PassPhrase to be used for encryption.'); authrounds := sw.AsInt('Rounds',DEFAULT_AEADROUNDS, 'Enter the PassPhrase iteration rounds.'); AEADCommandCryptFile(origfile,newfile,authpass,savepass, authrounds); end; ecAeadDecrypt: begin repeat origfile := sw.AsString('File','','Enter the name of the file to be decrypted.'); until FileExists(origfile) or sw.NoPrompt; repeat newfile := SysUtils.Trim(sw.AsString('Out',GetFileNameWithoutExt(origfile)+'.2', 'Enter the name of the decrypted file')); until (newfile <> '') or sw.NoPrompt; authpass := sw.AsUTF8('Pass','','Enter the PassPhrase to be used for decryption.'); savepass := sw.AsUTF8('Salt','salt','Enter the optional PassPhrase to be used for decryption.'); authrounds := sw.AsInt('Rounds',DEFAULT_AEADROUNDS,'Enter the PassPhrase iteration rounds.'); AEADCommandDecryptFile(origfile,newfile,authpass,savepass,authrounds); sw.Text(' % file decrypted.',[origfile],ccLightGreen); end; ecCheatInit: begin issuer := sw.AsUTF8('Issuer',ExeVersion.User, 'Enter Issuer identifier text of the master cheat keys.'#13#10+ 'Will be truncated to 15-20 ascii-7 chars.'); repeat savepass := sw.AsUTF8('NewPass',TAESPRNG.Main.RandomPassword(12), 'Enter a private PassPhrase for the master cheat.private key (at least 8 chars).'#13#10+ 'Save this in a safe place: if you forget it, the key will be useless!'); until (length(savepass)>=8) or sw.NoPrompt; repeat saverounds := sw.AsInt('NewRounds',CHEAT_ROUNDS, 'Enter iteration rounds for the mastercheat.private key (at least 100000).'); until (saverounds>=CHEAT_ROUNDS) or sw.NoPrompt; newfile := ECCCommandCheatInit(issuer,savepass,saverounds); if newfile<>'' then newfile := newfile+'/.private'; end; ecCheat: begin repeat auth := sw.AsString('Auth','', 'Enter the first chars of the .private certificate file name.'); until ECCKeyFileFind(auth,true) or sw.NoPrompt; if not sw.NoPrompt then sw.Text('Will use: %'#13#10,[ExtractFileName(auth)]); savepass := sw.AsUTF8('AuthPass','', 'Enter the PassPhrase of the master cheat.private file.'); saverounds := sw.AsInt('AuthRounds',CHEAT_ROUNDS, 'Enter the PassPhrase iteration rounds of the cheat.private file.'); sw.Text('%',[ECCCommandCheat(auth,savepass,saverounds,authpass,authrounds)]); if not sw.NoPrompt then WritePassword(auth,authpass,authrounds); end; else result := eccUnknownCommand; end; except on E: Exception do begin if not sw.NoPrompt then ConsoleShowFatalException(E,false); newfile := ''; result := eccException; end; end; finally if not sw.NoPrompt then begin FillcharFast(pointer(authpass)^,length(authpass),0); FillcharFast(pointer(savepass)^,length(savepass),0); end; end; if (newfile<>'') and (result=eccSuccess) and not(cmd in [ecInfoCrypt]) then sw.Text(' % file created.',[newfile],ccWhite); sw.TextColor(ccLightGray); end; end.