| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- unit ISSigFunc;
- {
- Inno Setup
- Copyright (C) 1997-2025 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- Functions for creating/verifying .issig signatures and importing/exporting
- text-based keys
- }
- interface
- uses
- Windows, SysUtils, Classes, ECDSA, SHA256;
- type
- TISSigVerifySignatureResult = (vsrSuccess, vsrMalformed, vsrKeyNotFound,
- vsrBad);
- TISSigImportKeyResult = (ikrSuccess, ikrMalformed, ikrNotPrivateKey);
- TISSigVerifySignatureFileMissingErrorProc = reference to procedure(const Filename: String);
- TISSigVerifySignatureSigFileMissingErrorProc = reference to procedure(const Filename, SigFilename: String);
- TISSigVerifySignatureVerificationFailedErrorProc = reference to procedure(const Filename, SigFilename: String; const VerifyResult: TISSigVerifySignatureResult);
- { Preferred, hardened functions for loading/saving .issig and key file text }
- function ISSigLoadTextFromFile(const AFilename: String): String;
- procedure ISSigSaveTextToFile(const AFilename, AText: String);
- function ISSigCreateSignatureText(const AKey: TECDSAKey;
- const AFileName: String; const AFileSize: Int64; const AFileHash: TSHA256Digest): String;
- function ISSigVerifySignatureText(const AAllowedKeys: array of TECDSAKey;
- const AText: String; out AFileName: String; out AFileSize: Int64;
- out AFileHash: TSHA256Digest): TISSigVerifySignatureResult; overload;
- function ISSigVerifySignatureText(const AAllowedKeys: array of TECDSAKey;
- const AText: String; out AFileName: String; out AFileSize: Int64;
- out AFileHash: TSHA256Digest; out AKeyUsedID: String): TISSigVerifySignatureResult; overload;
- function ISSigVerifySignature(const AFilename: String; const AAllowedKeys: array of TECDSAKey;
- out AExpectedFileName: String; out AExpectedFileSize: Int64; out AExpectedFileHash: TSHA256Digest;
- out AKeyUsedID: String;
- const AFileMissingErrorProc: TISSigVerifySignatureFileMissingErrorProc;
- const ASigFileMissingErrorProc: TISSigVerifySignatureSigFileMissingErrorProc;
- const AVerificationFailedErrorProc: TISSigVerifySignatureVerificationFailedErrorProc): Boolean; overload;
- function ISSigVerifySignature(const AFilename: String; const AAllowedKeys: array of TECDSAKey;
- out AExpectedFileName: String; out AExpectedFileSize: Int64; out AExpectedFileHash: TSHA256Digest;
- const AFileMissingErrorProc: TISSigVerifySignatureFileMissingErrorProc;
- const ASigFileMissingErrorProc: TISSigVerifySignatureSigFileMissingErrorProc;
- const AVerificationFailedErrorProc: TISSigVerifySignatureVerificationFailedErrorProc): Boolean; overload;
- procedure ISSigExportPrivateKeyText(const AKey: TECDSAKey;
- var APrivateKeyText: String);
- procedure ISSigExportPublicKeyText(const AKey: TECDSAKey;
- var APublicKeyText: String);
- procedure ISSigConvertPublicKeyToStrings(const APublicKey: TECDSAPublicKey;
- out APublicX, APublicY: String);
- function ISSigParsePrivateKeyText(const AText: String;
- out APrivateKey: TECDSAPrivateKey): TISSigImportKeyResult;
- function ISSigParsePublicKeyText(const AText: String;
- out APublicKey: TECDSAPublicKey): TISSigImportKeyResult;
- function ISSigImportKeyText(const AKey: TECDSAKey; const AText: String;
- const ANeedPrivateKey: Boolean): TISSigImportKeyResult;
- function ISSigImportPublicKey(const AKey: TECDSAKey;
- const AKeyID, APublicX, APublicY: String): TISSigImportKeyResult;
- procedure ISSigCheckValidKeyID(const AKeyID: String);
- procedure ISSigCheckValidPublicXOrY(const APublicXOrY: String);
- function ISSigIsValidKeyIDForPublicXY(const AKeyID, APublicX, APublicY: String): Boolean;
- function ISSigCalcStreamHash(const AStream: TStream): TSHA256Digest;
- var
- ISSigExt: String = '.issig';
- implementation
- uses
- StringScanner;
- const
- ISSigTextFileLengthLimit = 2500;
- NonControlASCIICharsSet = [#32..#126];
- UTF8HighCharsSet = [#128..#244];
- AllHighCharsSet = [#128..#255];
- DigitsSet = ['0'..'9'];
- HexDigitsSet = DigitsSet + ['a'..'f'];
- function ECDSAInt256ToString(const Value: TECDSAInt256): String;
- begin
- Result := SHA256DigestToString(TSHA256Digest(Value));
- end;
- function ECDSAInt256FromString(const S: String): TECDSAInt256;
- begin
- TSHA256Digest(Result) := SHA256DigestFromString(S);
- end;
- function CalcHashToSign(const AIncludeFileNameAndTag: Boolean; const AFileName: String; const AFileSize: Int64;
- const AFileHash: TSHA256Digest; const AFileTag: String): TSHA256Digest;
- procedure SHA256UpdateWithString(var Context: TSHA256Context; const S: String);
- begin
- const U = UTF8String(S);
- const N = UInt32(Length(U));
- SHA256Update(Context, N, SizeOf(N));
- if N > 0 then
- SHA256Update(Context, Pointer(U)^, N*SizeOf(U[1]));
- end;
- begin
- var Context: TSHA256Context;
- SHA256Init(Context);
- if AIncludeFileNameAndTag then begin
- SHA256UpdateWithString(Context, AFileName);
- SHA256UpdateWithString(Context, AFileTag);
- end;
- SHA256Update(Context, AFileSize, SizeOf(AFileSize));
- SHA256Update(Context, AFileHash, SizeOf(AFileHash));
- Result := SHA256Final(Context);
- end;
- function CalcKeyID(const APublicKey: TECDSAPublicKey): TSHA256Digest;
- begin
- Result := SHA256Buf(APublicKey, SizeOf(APublicKey));
- end;
- function ConsumeLineValue(var SS: TStringScanner; const AIdent: String;
- var AValue: String; const AMinValueLength, AMaxValueLength: Integer;
- const AAllowedChars: TSysCharSet; const AAllowAllCharsAboveFF: Boolean = False;
- const ARequireQuotes: Boolean = False): Boolean;
- begin
- Result := False;
- if SS.Consume(AIdent) and SS.Consume(' ') and (not ARequireQuotes or SS.Consume('"')) then
- if SS.ConsumeMultiToString(AAllowedChars, AValue, AAllowAllCharsAboveFF,
- AMinValueLength, AMaxValueLength) >= AMinValueLength then begin
- if not ARequireQuotes or SS.Consume('"') then begin
- { CRLF and LF line breaks are allowed (but not CR) }
- SS.Consume(#13);
- Result := SS.Consume(#10);
- end;
- end;
- end;
- function ISSigLoadTextFromFile(const AFilename: String): String;
- { Reads the specified file's contents into a string. This is intended only for
- loading .issig and key files. If the file appears to be invalid (e.g., if
- it is too large or contains invalid characters), then an empty string is
- returned, which will be reported as malformed when it is processed by
- ISSigVerifySignatureText or ISSigImportKeyText. }
- begin
- var U: UTF8String;
- SetLength(U, ISSigTextFileLengthLimit + 1);
- const F = TFileStream.Create(AFilename, fmOpenRead or fmShareDenyWrite);
- try
- const BytesRead = F.Read(U[Low(U)], Length(U));
- if BytesRead >= Length(U) then
- Exit('');
- SetLength(U, BytesRead);
- finally
- F.Free;
- end;
- { Defense-in-depth: Reject any non-CRLF control characters up front, as well
- as any byte values that are never used in UTF-8 encoding }
- for var C in U do
- if not CharInSet(C, [#10, #13] + NonControlASCIICharsSet + UTF8HighCharsSet) then
- Exit('');
- { Do round-trip check to catch invalid sequences }
- const UTF16Text = String(U);
- if UTF8String(UTF16Text) <> U then
- Exit('');
- Result := UTF16Text;
- end;
- procedure ISSigSaveTextToFile(const AFilename, AText: String);
- begin
- const F = TFileStream.Create(AFilename, fmCreate or fmShareExclusive);
- try
- const U = UTF8String(AText);
- if U <> '' then
- F.WriteBuffer(U[Low(U)], Length(U));
- finally
- F.Free;
- end;
- end;
- function ISSigCreateSignatureText(const AKey: TECDSAKey;
- const AFileName: String; const AFileSize: Int64; const AFileHash: TSHA256Digest): String;
- begin
- const AFileTag = ''; { Should be a parameter in the future }
- { Ensure unverifiable signature files can't be created accidentally }
- const UTF8FileName = UTF8String(AFileName);
- if Length(UTF8FileName) > 1000 then
- raise Exception.Create('File name is too long');
- if String(UTF8FileName) <> AFileName then
- raise Exception.Create('File name contains invalid surrogate pairs');
- for var C in UTF8FileName do
- if not (C in ((NonControlASCIICharsSet - ['"']) + UTF8HighCharsSet)) then { Note this rejects quotes }
- raise Exception.Create('File name contains invalid characters');
- { File size is limited to 16 digits (enough for >9 EB) }
- if (AFileSize < 0) or (AFileSize > {$IF CompilerVersion >= 36.0} 9_999_999_999_999_999 {$ELSE} 9999999999999999 {$ENDIF} ) then
- raise Exception.Create('File size out of range');
- var PublicKey: TECDSAPublicKey;
- AKey.ExportPublicKey(PublicKey);
- const HashToSign = CalcHashToSign(True, AFileName, AFileSize, AFileHash, AFileTag);
- var Sig: TECDSASignature;
- AKey.SignHash(HashToSign, Sig);
- Result := Format(
- 'format issig-v2'#13#10 +
- 'file-name "%s"'#13#10 +
- 'file-size %d'#13#10 +
- 'file-hash %s'#13#10 +
- 'file-tag "%s"'#13#10 +
- 'key-id %s'#13#10 +
- 'sig-r %s'#13#10 +
- 'sig-s %s'#13#10,
- [AFileName,
- AFileSize,
- SHA256DigestToString(AFileHash),
- AFileTag,
- SHA256DigestToString(CalcKeyID(PublicKey)),
- ECDSAInt256ToString(Sig.Sig_r),
- ECDSAInt256ToString(Sig.Sig_s)]);
- end;
- function ISSigVerifySignatureText(const AAllowedKeys: array of TECDSAKey;
- const AText: String; out AFileName: String; out AFileSize: Int64;
- out AFileHash: TSHA256Digest; out AKeyUsedID: String): TISSigVerifySignatureResult;
- function FormatToVersion(const Format: String; out Version: Integer): Boolean;
- begin
- if Format = 'issig-v1' then begin
- Version := 1;
- Exit(True);
- end else if Format = 'issig-v2' then begin
- Version := 2;
- Exit(True);
- end else
- Exit(False);
- end;
- var
- TextValues: record
- Format, FileName, FileSize, FileHash, FileTag, KeyID, Sig_r, Sig_s: String;
- Version: Integer;
- end;
- begin
- { To be extra safe, clear the "out" parameters just in case the caller isn't
- properly checking the function result }
- AFileName := '';
- AFileSize := -1;
- FillChar(AFileHash, SizeOf(AFileHash), 0);
- var AFileTag := ''; { Should be a parameter in the future }
- AKeyUsedID := '';
- if Length(AText) > ISSigTextFileLengthLimit then
- Exit(vsrMalformed)
- else if Length(AAllowedKeys) = 0 then
- Exit(vsrKeyNotFound);
- var SS := TStringScanner.Create(AText);
- if not ConsumeLineValue(SS, 'format', TextValues.Format, 8, 8, NonControlASCIICharsSet) or
- not FormatToVersion(TextValues.Format, TextValues.Version) or
- ((TextValues.Version >= 2) and not ConsumeLineValue(SS, 'file-name', TextValues.FileName, 0, MaxInt,
- (NonControlASCIICharsSet - ['"']) + AllHighCharsSet, True, True)) or
- not ConsumeLineValue(SS, 'file-size', TextValues.FileSize, 1, 16, DigitsSet) or
- not ConsumeLineValue(SS, 'file-hash', TextValues.FileHash, 64, 64, HexDigitsSet) or
- ((TextValues.Version >= 2) and not ConsumeLineValue(SS, 'file-tag', TextValues.FileTag, 0, MaxInt,
- (NonControlASCIICharsSet - ['"']) + AllHighCharsSet, True, True)) or
- not ConsumeLineValue(SS, 'key-id', TextValues.KeyID, 64, 64, HexDigitsSet) or
- not ConsumeLineValue(SS, 'sig-r', TextValues.Sig_r, 64, 64, HexDigitsSet) or
- not ConsumeLineValue(SS, 'sig-s', TextValues.Sig_s, 64, 64, HexDigitsSet) or
- not SS.ReachedEnd then
- Exit(vsrMalformed);
- { Don't allow leading zeros on file-size }
- if (Length(TextValues.FileSize) > 1) and
- (TextValues.FileSize[Low(TextValues.FileSize)] = '0') then
- Exit(vsrMalformed);
- { Find the key that matches the key ID }
- var KeyUsed: TECDSAKey := nil;
- const KeyID = SHA256DigestFromString(TextValues.KeyID);
- for var K in AAllowedKeys do begin
- var PublicKey: TECDSAPublicKey;
- K.ExportPublicKey(PublicKey);
- if SHA256DigestsEqual(KeyID, CalcKeyID(PublicKey)) then begin
- KeyUsed := K;
- Break;
- end;
- end;
- if KeyUsed = nil then
- Exit(vsrKeyNotFound);
- AKeyUsedID := TextValues.KeyID;
- const UnverifiedFileName = TextValues.FileName;
- const UnverifiedFileSize = StrToInt64(TextValues.FileSize);
- const UnverifiedFileHash = SHA256DigestFromString(TextValues.FileHash);
- const UnverifiedFileTag = TextValues.FileTag;
- const HashToSign = CalcHashToSign(TextValues.Version >= 2, UnverifiedFileName,
- UnverifiedFileSize, UnverifiedFileHash, UnverifiedFileTag);
- var Sig: TECDSASignature;
- Sig.Sig_r := ECDSAInt256FromString(TextValues.Sig_r);
- Sig.Sig_s := ECDSAInt256FromString(TextValues.Sig_s);
- if KeyUsed.VerifySignature(HashToSign, Sig) then begin
- AFileName := UnverifiedFileName;
- AFileSize := UnverifiedFileSize;
- AFileHash := UnverifiedFileHash;
- AFileTag := UnverifiedFileTag;
- Result := vsrSuccess;
- end else
- Result := vsrBad;
- end;
- function ISSigVerifySignatureText(const AAllowedKeys: array of TECDSAKey;
- const AText: String; out AFileName: String; out AFileSize: Int64;
- out AFileHash: TSHA256Digest): TISSigVerifySignatureResult;
- begin
- var KeyUsedID: String;
- Result := ISSigVerifySignatureText(AAllowedKeys, AText, AFileName, AFileSize, AFileHash, KeyUsedID);
- end;
- function ISSigVerifySignature(const AFilename: String; const AAllowedKeys: array of TECDSAKey;
- out AExpectedFileName: String; out AExpectedFileSize: Int64; out AExpectedFileHash: TSHA256Digest;
- out AKeyUsedID: String;
- const AFileMissingErrorProc: TISSigVerifySignatureFileMissingErrorProc;
- const ASigFileMissingErrorProc: TISSigVerifySignatureSigFileMissingErrorProc;
- const AVerificationFailedErrorProc: TISSigVerifySignatureVerificationFailedErrorProc): Boolean;
-
- function NewFileExists(const Name: String): Boolean;
- { Returns True if the specified file exists.
- This function is better than Delphi's FileExists function because it works
- on files in directories that don't have "list" permission. There is, however,
- one other difference: FileExists allows wildcards, but this function does
- not. }
- begin
- var Attr := GetFileAttributes(PChar(Name));
- Result := (Attr <> INVALID_FILE_ATTRIBUTES) and (Attr and faDirectory = 0);
- end;
- begin
- if Assigned(AFileMissingErrorProc) and not NewFileExists(AFilename) then begin
- AFileMissingErrorProc(AFilename);
- Exit(False);
- end;
- const SigFilename = AFilename + ISSigExt;
- if not NewFileExists(SigFilename) then begin
- if Assigned(ASigFileMissingErrorProc) then
- ASigFileMissingErrorProc(AFilename, SigFilename);
- Exit(False);
- end;
- const SigText = ISSigLoadTextFromFile(SigFilename);
- const VerifyResult = ISSigVerifySignatureText(AAllowedKeys, SigText,
- AExpectedFileName, AExpectedFileSize, AExpectedFileHash, AKeyUsedID);
- Result := VerifyResult = vsrSuccess;
- if not Result and Assigned(AVerificationFailedErrorProc) then
- AVerificationFailedErrorProc(AFilename, SigFilename, VerifyResult);
- end;
- function ISSigVerifySignature(const AFilename: String; const AAllowedKeys: array of TECDSAKey;
- out AExpectedFileName: String; out AExpectedFileSize: Int64; out AExpectedFileHash: TSHA256Digest;
- const AFileMissingErrorProc: TISSigVerifySignatureFileMissingErrorProc;
- const ASigFileMissingErrorProc: TISSigVerifySignatureSigFileMissingErrorProc;
- const AVerificationFailedErrorProc: TISSigVerifySignatureVerificationFailedErrorProc): Boolean;
- begin
- var KeyUsedID: String;
- Result := ISSigVerifySignature(AFilename, AAllowedKeys, AExpectedFileName, AExpectedFileSize,
- AExpectedFileHash, KeyUsedID, AFileMissingErrorProc, ASigFileMissingErrorProc,
- AVerificationFailedErrorProc);
- end;
- procedure ISSigExportPrivateKeyText(const AKey: TECDSAKey;
- var APrivateKeyText: String);
- begin
- var PrivateKey: TECDSAPrivateKey;
- try
- AKey.ExportPrivateKey(PrivateKey);
- APrivateKeyText := Format(
- 'format issig-private-key'#13#10 +
- 'key-id %s'#13#10 +
- 'public-x %s'#13#10 +
- 'public-y %s'#13#10 +
- 'private-d %s'#13#10,
- [SHA256DigestToString(CalcKeyID(PrivateKey.PublicKey)),
- ECDSAInt256ToString(PrivateKey.PublicKey.Public_x),
- ECDSAInt256ToString(PrivateKey.PublicKey.Public_y),
- ECDSAInt256ToString(PrivateKey.Private_d)]);
- finally
- PrivateKey.Clear;
- end;
- end;
- procedure ISSigExportPublicKeyText(const AKey: TECDSAKey;
- var APublicKeyText: String);
- begin
- var PublicKey: TECDSAPublicKey;
- try
- AKey.ExportPublicKey(PublicKey);
- APublicKeyText := Format(
- 'format issig-public-key'#13#10 +
- 'key-id %s'#13#10 +
- 'public-x %s'#13#10 +
- 'public-y %s'#13#10,
- [SHA256DigestToString(CalcKeyID(PublicKey)),
- ECDSAInt256ToString(PublicKey.Public_x),
- ECDSAInt256ToString(PublicKey.Public_y)]);
- finally
- PublicKey.Clear;
- end;
- end;
- procedure ISSigConvertPublicKeyToStrings(const APublicKey: TECDSAPublicKey;
- out APublicX, APublicY: String);
- begin
- APublicX := ECDSAInt256ToString(APublicKey.Public_x);
- APublicY := ECDSAInt256ToString(APublicKey.Public_y);
- end;
- function InternalParseKeyText(const AText: String;
- out APrivateKey: TECDSAPrivateKey;
- const ANeedPrivateKey: Boolean): TISSigImportKeyResult;
- var
- TextValues: record
- Format, KeyID, Public_x, Public_y, Private_d: String;
- end;
- begin
- Result := ikrMalformed;
- if Length(AText) > ISSigTextFileLengthLimit then
- Exit;
- var SS := TStringScanner.Create(AText);
- if not ConsumeLineValue(SS, 'format', TextValues.Format, 16, 17, NonControlASCIICharsSet) then
- Exit;
- var HasPrivateKey := False;
- if TextValues.Format = 'issig-private-key' then
- HasPrivateKey := True
- else if TextValues.Format = 'issig-public-key' then
- { already False }
- else
- Exit;
- if not ConsumeLineValue(SS, 'key-id', TextValues.KeyID, 64, 64, HexDigitsSet) or
- not ConsumeLineValue(SS, 'public-x', TextValues.Public_x, 64, 64, HexDigitsSet) or
- not ConsumeLineValue(SS, 'public-y', TextValues.Public_y, 64, 64, HexDigitsSet) then
- Exit;
- if HasPrivateKey then
- if not ConsumeLineValue(SS, 'private-d', TextValues.Private_d, 64, 64, HexDigitsSet) then
- Exit;
- if not SS.ReachedEnd then
- Exit;
- APrivateKey.Clear; { just because Private_d isn't always set }
- APrivateKey.PublicKey.Public_x := ECDSAInt256FromString(TextValues.Public_x);
- APrivateKey.PublicKey.Public_y := ECDSAInt256FromString(TextValues.Public_y);
- { Verify that the key ID is correct for the public key values }
- if not SHA256DigestsEqual(SHA256DigestFromString(TextValues.KeyID),
- CalcKeyID(APrivateKey.PublicKey)) then
- Exit;
- if ANeedPrivateKey then begin
- if not HasPrivateKey then
- Exit(ikrNotPrivateKey);
- APrivateKey.Private_d := ECDSAInt256FromString(TextValues.Private_d);
- end;
- Result := ikrSuccess;
- end;
- function ISSigParsePrivateKeyText(const AText: String;
- out APrivateKey: TECDSAPrivateKey): TISSigImportKeyResult;
- begin
- Result := InternalParseKeyText(AText, APrivateKey, True);
- end;
- function ISSigParsePublicKeyText(const AText: String;
- out APublicKey: TECDSAPublicKey): TISSigImportKeyResult;
- begin
- var PrivateKey: TECDSAPrivateKey; { only PublicKey part is used }
- Result := InternalParseKeyText(AText, PrivateKey, False);
- if Result = ikrSuccess then
- APublicKey := PrivateKey.PublicKey;
- end;
- function ISSigImportKeyText(const AKey: TECDSAKey; const AText: String;
- const ANeedPrivateKey: Boolean): TISSigImportKeyResult;
- begin
- var PrivateKey: TECDSAPrivateKey;
- try
- Result := InternalParseKeyText(AText, PrivateKey, ANeedPrivateKey);
- if Result = ikrSuccess then begin
- if ANeedPrivateKey then
- AKey.ImportPrivateKey(PrivateKey)
- else
- AKey.ImportPublicKey(PrivateKey.PublicKey);
- end;
- finally
- PrivateKey.Clear;
- end;
- end;
- function ISSigImportPublicKey(const AKey: TECDSAKey;
- const AKeyID, APublicX, APublicY: String): TISSigImportKeyResult;
- begin
- var Publickey: TECDSAPublickey;
- PublicKey.Public_x := ECDSAInt256FromString(APublicX);
- PublicKey.Public_y := ECDSAInt256FromString(APublicY);
- if AKeyID <> '' then begin
- { Verify that the key ID is correct for the public key values }
- if not SHA256DigestsEqual(SHA256DigestFromString(AKeyID),
- CalcKeyID(PublicKey)) then
- Exit(ikrMalformed);
- end;
- AKey.ImportPublicKey(PublicKey);
- Result := ikrSuccess;
- end;
- procedure ISSigCheckValidKeyID(const AKeyID: String);
- begin
- SHA256DigestFromString(AKeyID);
- end;
- procedure ISSigCheckValidPublicXOrY(const APublicXOrY: String);
- begin
- ECDSAInt256FromString(APublicXOrY);
- end;
- function ISSigIsValidKeyIDForPublicXY(const AKeyID, APublicX, APublicY: String): Boolean;
- begin
- var PublicKey: TECDSAPublicKey;
- PublicKey.Public_x := ECDSAInt256FromString(APublicX);
- PublicKey.Public_y := ECDSAInt256FromString(APublicY);
- Result := SHA256DigestsEqual(SHA256DigestFromString(AKeyID),
- CalcKeyID(PublicKey));
- end;
- function ISSigCalcStreamHash(const AStream: TStream): TSHA256Digest;
- var
- Buf: array[0..$FFFF] of Byte;
- begin
- var Context: TSHA256Context;
- SHA256Init(Context);
- while True do begin
- const BytesRead = Cardinal(AStream.Read(Buf, SizeOf(Buf)));
- if BytesRead = 0 then
- Break;
- SHA256Update(Context, Buf, BytesRead);
- end;
- Result := SHA256Final(Context);
- end;
- end.
|