From 5078cf4a8651da5f414f508b2cee29f45083e455 Mon Sep 17 00:00:00 2001 From: Ugochukwu Mmaduekwe Date: Thu, 28 May 2026 21:41:39 +0100 Subject: [PATCH] DerInteger enhancements --- CryptoLib/src/Asn1/ClpAsn1Objects.pas | 82 ++++++++++++++----- .../src/Interfaces/Asn1/ClpIAsn1Objects.pas | 36 +++++++- 2 files changed, 96 insertions(+), 22 deletions(-) diff --git a/CryptoLib/src/Asn1/ClpAsn1Objects.pas b/CryptoLib/src/Asn1/ClpAsn1Objects.pas index 5f3f8c01..36a2c323 100644 --- a/CryptoLib/src/Asn1/ClpAsn1Objects.pas +++ b/CryptoLib/src/Asn1/ClpAsn1Objects.pas @@ -2578,8 +2578,12 @@ Meta = class sealed(TAsn1UniversalType, IAsn1UniversalType) /// function GetValue(): TBigInteger; /// - /// Get the positive BigInteger value. + /// Force the ASN.1 INTEGER encoding to be interpreted as unsigned. /// + /// + /// In some cases positive values get crammed into a space that's not quite big enough... + /// NB: The BigInteger constructor tolerates any redundant sign bytes (per 'AllowUnsafeInteger'). + /// function GetPositiveValue(): TBigInteger; /// /// Check if this integer has a specific Int32 value. @@ -2598,26 +2602,50 @@ Meta = class sealed(TAsn1UniversalType, IAsn1UniversalType) /// function GetIntValueExact(): Int32; /// - /// Get positive Int32 value, throwing if out of range. + /// Force the ASN.1 INTEGER encoding to be interpreted as unsigned. /// + /// + /// In some cases positive values get crammed into a space that's not quite big enough... + /// function GetIntPositiveValueExact(): Int32; /// /// Get Int64 value, throwing if out of range. /// function GetLongValueExact(): Int64; /// + /// Force the ASN.1 INTEGER encoding to be interpreted as unsigned. + /// + /// + /// In some cases positive values get crammed into a space that's not quite big enough... + /// + function GetLongPositiveValueExact(): Int64; + /// /// Try to get Int32 value, returning false if out of range. /// function TryGetIntValueExact(out AValue: Int32): Boolean; /// - /// Try to get positive Int32 value, returning false if out of range. + /// Force the ASN.1 INTEGER encoding to be interpreted as unsigned. /// + /// + /// In some cases positive values get crammed into a space that's not quite big enough... + /// function TryGetIntPositiveValueExact(out AValue: Int32): Boolean; /// /// Try to get Int64 value, returning false if out of range. /// function TryGetLongValueExact(out AValue: Int64): Boolean; /// + /// Force the ASN.1 INTEGER encoding to be interpreted as unsigned. + /// + /// + /// In some cases positive values get crammed into a space that's not quite big enough... + /// + function TryGetLongPositiveValueExact(out AValue: Int64): Boolean; + /// + /// Whether the first significant encoding byte has the sign bit set. + /// + function GetIsNegative(): Boolean; + /// /// Check if bytes are malformed (invalid INTEGER encoding). /// class function IsMalformed(const ABytes: TCryptoLibByteArray): Boolean; static; @@ -2642,6 +2670,8 @@ Meta = class sealed(TAsn1UniversalType, IAsn1UniversalType) property IntValueExact: Int32 read GetIntValueExact; property IntPositiveValueExact: Int32 read GetIntPositiveValueExact; property LongValueExact: Int64 read GetLongValueExact; + property LongPositiveValueExact: Int64 read GetLongPositiveValueExact; + property IsNegative: Boolean read GetIsNegative; end; /// @@ -12659,33 +12689,27 @@ function TDerInteger.HasValue(const AX: TBigInteger): Boolean; end; function TDerInteger.GetIntValueExact(): Int32; -var - LCount: Int32; begin - LCount := System.Length(FBytes) - FStart; - if LCount > 4 then + if not TryGetIntValueExact(Result) then raise EArithmeticCryptoLibException.Create('ASN.1 Integer out of int range'); - Result := TDerInteger.IntValue(FBytes, FStart, SignExtSigned); end; function TDerInteger.GetIntPositiveValueExact(): Int32; -var - LCount: Int32; begin - LCount := System.Length(FBytes) - FStart; - if (LCount > 4) or ((LCount = 4) and ((FBytes[FStart] and $80) <> 0)) then + if not TryGetIntPositiveValueExact(Result) then raise EArithmeticCryptoLibException.Create('ASN.1 Integer out of positive int range'); - Result := TDerInteger.IntValue(FBytes, FStart, SignExtUnsigned); end; function TDerInteger.GetLongValueExact(): Int64; -var - LCount: Int32; begin - LCount := System.Length(FBytes) - FStart; - if LCount > 8 then + if not TryGetLongValueExact(Result) then raise EArithmeticCryptoLibException.Create('ASN.1 Integer out of long range'); - Result := TDerInteger.LongValue(FBytes, FStart, SignExtSigned); +end; + +function TDerInteger.GetLongPositiveValueExact(): Int64; +begin + if not TryGetLongPositiveValueExact(Result) then + raise EArithmeticCryptoLibException.Create('ASN.1 Integer out of positive long range'); end; function TDerInteger.TryGetIntValueExact(out AValue: Int32): Boolean; @@ -12708,7 +12732,7 @@ function TDerInteger.TryGetIntPositiveValueExact(out AValue: Int32): Boolean; LCount: Int32; begin LCount := System.Length(FBytes) - FStart; - if (LCount > 4) or ((LCount = 4) and ((FBytes[FStart] and $80) <> 0)) then + if (LCount > 4) or ((LCount = 4) and IsNegative) then begin AValue := 0; Result := False; @@ -12718,6 +12742,21 @@ function TDerInteger.TryGetIntPositiveValueExact(out AValue: Int32): Boolean; Result := True; end; +function TDerInteger.TryGetLongPositiveValueExact(out AValue: Int64): Boolean; +var + LCount: Int32; +begin + LCount := System.Length(FBytes) - FStart; + if (LCount > 8) or ((LCount = 8) and IsNegative) then + begin + AValue := 0; + Result := False; + Exit; + end; + AValue := TDerInteger.LongValue(FBytes, FStart, SignExtSigned); + Result := True; +end; + function TDerInteger.TryGetLongValueExact(out AValue: Int64): Boolean; var LCount: Int32; @@ -12733,6 +12772,11 @@ function TDerInteger.TryGetLongValueExact(out AValue: Int64): Boolean; Result := True; end; +function TDerInteger.GetIsNegative(): Boolean; +begin + Result := (FBytes[FStart] and $80) <> 0; +end; + function TDerInteger.GetEncoding(AEncoding: Int32): IAsn1Encoding; begin Result := TPrimitiveEncoding.Create(TAsn1Tags.Universal, TAsn1Tags.Integer, FBytes); diff --git a/CryptoLib/src/Interfaces/Asn1/ClpIAsn1Objects.pas b/CryptoLib/src/Interfaces/Asn1/ClpIAsn1Objects.pas index 206a455f..e3fa1eda 100644 --- a/CryptoLib/src/Interfaces/Asn1/ClpIAsn1Objects.pas +++ b/CryptoLib/src/Interfaces/Asn1/ClpIAsn1Objects.pas @@ -506,8 +506,12 @@ interface /// function GetValue(): TBigInteger; /// - /// Get the positive BigInteger value. + /// Force the ASN.1 INTEGER encoding to be interpreted as unsigned. /// + /// + /// In some cases positive values get crammed into a space that's not quite big enough... + /// NB: The BigInteger constructor tolerates any redundant sign bytes (per 'AllowUnsafeInteger'). + /// function GetPositiveValue(): TBigInteger; /// /// Check if this integer has a specific Int32 value. @@ -526,31 +530,57 @@ interface /// function GetIntValueExact(): Int32; /// - /// Get positive Int32 value, throwing if out of range. + /// Force the ASN.1 INTEGER encoding to be interpreted as unsigned. /// + /// + /// In some cases positive values get crammed into a space that's not quite big enough... + /// function GetIntPositiveValueExact(): Int32; /// /// Get Int64 value, throwing if out of range. /// function GetLongValueExact(): Int64; /// + /// Force the ASN.1 INTEGER encoding to be interpreted as unsigned. + /// + /// + /// In some cases positive values get crammed into a space that's not quite big enough... + /// + function GetLongPositiveValueExact(): Int64; + /// /// Try to get Int32 value, returning false if out of range. /// function TryGetIntValueExact(out AValue: Int32): Boolean; /// - /// Try to get positive Int32 value, returning false if out of range. + /// Force the ASN.1 INTEGER encoding to be interpreted as unsigned. /// + /// + /// In some cases positive values get crammed into a space that's not quite big enough... + /// function TryGetIntPositiveValueExact(out AValue: Int32): Boolean; /// /// Try to get Int64 value, returning false if out of range. /// function TryGetLongValueExact(out AValue: Int64): Boolean; + /// + /// Force the ASN.1 INTEGER encoding to be interpreted as unsigned. + /// + /// + /// In some cases positive values get crammed into a space that's not quite big enough... + /// + function TryGetLongPositiveValueExact(out AValue: Int64): Boolean; + /// + /// Whether the first significant encoding byte has the sign bit set. + /// + function GetIsNegative(): Boolean; property Bytes: TCryptoLibByteArray read GetBytes; property Value: TBigInteger read GetValue; property PositiveValue: TBigInteger read GetPositiveValue; property IntValueExact: Int32 read GetIntValueExact; property IntPositiveValueExact: Int32 read GetIntPositiveValueExact; property LongValueExact: Int64 read GetLongValueExact; + property LongPositiveValueExact: Int64 read GetLongPositiveValueExact; + property IsNegative: Boolean read GetIsNegative; end; ///