Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 70 additions & 14 deletions Source/DECAuthenticatedCipherModesBase.pas
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,21 @@ EDECAuthLengthException = class(EDECException);
/// Base class for authenticated cipher modes (GCM, CCM, future AEAD modes).
/// </summary>
/// <remarks>
/// Lifecycle for multi-call capable modes (e.g. GCM):
/// Lifecycle for authenticated modes (GCM, CCM):
/// <para>
/// Init → set AAD / tag length / expected tag → Encode/Decode* → Done →
/// read CalculatedAuthenticationTag.
/// </para>
/// <para>
/// Done must be called before the calculated authentication tag is valid
/// for multi-call streams. Done is idempotent. After Done, further
/// Encode/Decode raises until Init is called again.
/// Done must be called before CalculatedAuthenticationTag may be read.
/// Reading the tag before Done raises EDECCipherException. Done is
/// idempotent. After Done, further Encode/Decode raises until Init is
/// called again.
/// </para>
/// <para>
/// CCM remains one-shot (single Encode/Decode with full message length);
/// Done still verifies ExpectedAuthenticationTag when set.
/// GCM supports multi-call Encode/Decode. CCM currently remains one-shot
/// (single Encode/Decode with the full message length); the tag is still
/// materialized in Done so both modes share the same lifecycle.
/// </para>
/// </remarks>
TAuthenticatedCipherModesBase = class(TObject)
Expand Down Expand Up @@ -100,6 +102,13 @@ TAuthenticatedCipherModesBase = class(TObject)
/// </summary>
FEncryptionMethod : TEncodeDecodeMethod;

/// <summary>
/// True after Done has materialized the authentication tag. Reading
/// CalculatedAuthenticationTag before this is set raises. Encode/Decode
/// after finalization also raises until Init is called again.
/// </summary>
FFinalized : Boolean;

/// <summary>
/// Defines the length of the resulting authentication value in bit.
/// </summary>
Expand All @@ -120,6 +129,24 @@ TAuthenticatedCipherModesBase = class(TObject)
/// Length of the calculated authentication value in bit
/// </returns>
function GetAuthenticationTagBitLength: UInt32; virtual;
/// <summary>
/// Returns the calculated authentication tag. Raises if Done has not
/// been called yet.
/// </summary>
/// <returns>
/// Calculated authentication tag bytes
/// </returns>
/// <exception cref="EDECCipherException">
/// Raised when the tag is read before Done.
/// </exception>
function GetCalculatedAuthenticationTag: TBytes; virtual;
/// <summary>
/// Raises EDECCipherException when Encode/Decode is attempted after Done.
/// </summary>
/// <exception cref="EDECCipherException">
/// Raised when the mode has already been finalized.
/// </exception>
procedure CheckNotFinalized;
public
/// <summary>
/// Should be called when starting encryption/decryption in order to
Expand Down Expand Up @@ -169,8 +196,9 @@ TAuthenticatedCipherModesBase = class(TObject)

/// <summary>
/// Finalizes the authentication tag after all Encode/Decode calls.
/// Idempotent. Default implementation is a no-op (suitable for modes that
/// already compute the tag inside Encode/Decode, e.g. CCM).
/// Idempotent. Marks the tag as readable via CalculatedAuthenticationTag.
/// Concrete modes that defer tag computation (GCM, CCM) override this
/// to materialize the tag before calling inherited.
/// </summary>
procedure Done; virtual;

Expand All @@ -179,7 +207,8 @@ TAuthenticatedCipherModesBase = class(TObject)
/// the official specification of the standard.
/// </summary>
/// <returns>
/// List of bit lengths
/// List of bit lengths prescribed by the mode specification. If the
/// mode does not prescribe any tag lengths, an empty array is returned.
/// </returns>
function GetStandardAuthenticationTagBitLengths:TStandardBitLengths; virtual;

Expand All @@ -200,11 +229,15 @@ TAuthenticatedCipherModesBase = class(TObject)
read GetAuthenticationTagBitLength
write SetAuthenticationTagLength;
/// <summary>
/// Calculated authentication value. For multi-call modes this is only
/// complete after Done has been called.
/// Calculated authentication value. Valid only after Done has been
/// called. Reading this property before Done raises EDECCipherException
/// so callers follow the Init → Encode/Decode* → Done → tag lifecycle.
/// </summary>
/// <exception cref="EDECCipherException">
/// Raised when the property is read before Done.
/// </exception>
property CalculatedAuthenticationTag : TBytes
read FCalcAuthenticationTag
read GetCalculatedAuthenticationTag
write FCalcAuthenticationTag;

/// <summary>
Expand All @@ -221,15 +254,38 @@ implementation
uses
DECUtil;

resourcestring
sAuthenticationTagNotFinalized =
'Calculated authentication tag is not available before Done has been called';
sAuthenticatedModeAlreadyFinalized =
'Authenticated cipher mode already finalized; call Init before further Encode/Decode';

{ TAuthenticatedCipherModesBase }

function TAuthenticatedCipherModesBase.GetAuthenticationTagBitLength: UInt32;
begin
Result := FCalcAuthenticationTagLength shl 3;
end;

function TAuthenticatedCipherModesBase.GetCalculatedAuthenticationTag: TBytes;
begin
if not FFinalized then
raise EDECCipherException.CreateRes(@sAuthenticationTagNotFinalized);

Result := FCalcAuthenticationTag;
end;

procedure TAuthenticatedCipherModesBase.CheckNotFinalized;
begin
if FFinalized then
raise EDECCipherException.CreateRes(@sAuthenticatedModeAlreadyFinalized);
end;

function TAuthenticatedCipherModesBase.GetStandardAuthenticationTagBitLengths: TStandardBitLengths;
begin
// No prescribed lengths at this abstraction: return an empty array rather
// than a dummy 0-entry so callers can distinguish "none specified" from a
// specified length of 0 bits.
SetLength(Result, 0);
end;

Expand All @@ -250,12 +306,12 @@ procedure TAuthenticatedCipherModesBase.Init(EncryptionMethod : TEncodeDecodeMet
end;

FEncryptionMethod := EncryptionMethod;
FFinalized := False;
end;

procedure TAuthenticatedCipherModesBase.Done;
begin
// Default: no deferred finalization (CCM computes the tag in Encode/Decode).
// Streaming modes such as GCM override this to materialize the tag.
FFinalized := True;
end;

procedure TAuthenticatedCipherModesBase.SetAuthenticationTagLength(const Value: UInt32);
Expand Down
10 changes: 5 additions & 5 deletions Source/DECCipherFormats.pas
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{*****************************************************************************
{*****************************************************************************
The DEC team (see file NOTICE.txt) licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
Expand Down Expand Up @@ -732,8 +732,8 @@ function TDECFormattedCipher.EncodeBytes(const Source: TBytes): TBytes;
if Length(Result) > 0 then
Encode(Source[0], Result[0], Length(Source))
else
if (FMode = cmGCM) then
EncodeGCM(nil, nil, 0);
if (FMode = cmGCM) or (FMode = cmCCM) then
EncodeAuthenticated(nil, nil, 0);
end;

begin
Expand All @@ -755,8 +755,8 @@ function TDECFormattedCipher.DecodeBytes(const Source: TBytes): TBytes;
Decode(Source[0], Result[0], Length(Source));
end
else
if (FMode = cmGCM) then
DecodeGCM(nil, nil, 0);
if (FMode = cmGCM) or (FMode = cmCCM) then
DecodeAuthenticated(nil, nil, 0);

if not (FPaddingClass = nil) then
Result := FPaddingClass.RemovePadding(Result, Context.BlockSize);
Expand Down
20 changes: 12 additions & 8 deletions Source/DECCipherInterface.pas
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{*****************************************************************************
{*****************************************************************************
The DEC team (see file NOTICE.txt) licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
Expand Down Expand Up @@ -665,11 +665,12 @@ interface
/// </summary>
/// <returns>
/// Result of the authentication. Raises an EDECCipherException if this is
/// called for a cipher mode not supporting authentication.
/// called for a cipher mode not supporting authentication, or if Done
/// has not been called yet.
/// </returns>
/// <exception cref="EDECCipherException">
/// Exception raised if called for a cipher mode not supporting
/// authentication.
/// authentication, or if the tag is read before Done.
/// </exception>
function GetCalcAuthenticatonResult: TBytes;
/// <summary>
Expand Down Expand Up @@ -705,8 +706,10 @@ interface
/// the official specification of the standard.
/// </summary>
/// <returns>
/// List of bit lengths. If the cipher mode used is not an authenticated
/// one, the array will just contain a single value of 0.
/// List of bit lengths prescribed by the authenticated mode. If the
/// cipher mode used is not an authenticated one, the array will just
/// contain a single value of 0. If an authenticated mode does not
/// prescribe tag lengths, an empty array is returned.
/// </returns>
function GetStandardAuthenticationTagBitLengths:TStandardBitLengths;

Expand Down Expand Up @@ -756,12 +759,13 @@ interface
/// <summary>
/// Some block chaining modes have the ability to authenticate the message
/// in addition to encrypting it. This property contains the generated
/// authentication tag. Raises an EDECCipherException if this is
/// called for a cipher mode not supporting authentication.
/// authentication tag. Call Done before reading it; reading the tag
/// before Done raises EDECCipherException. Raises an EDECCipherException
/// if this is called for a cipher mode not supporting authentication.
/// </summary>
/// <exception cref="EDECCipherException">
/// Exception raised if called for a cipher mode not supporting
/// authentication.
/// authentication, or if the tag is read before Done.
/// </exception>
property CalculatedAuthenticationResult : TBytes
read GetCalcAuthenticatonResult;
Expand Down
Loading