Skip to content

Deferred: OTP counter byte-order depends on a little-endian host #18

Description

@darianmiller

Tracking ticket for a known, intentionally deferred assumption -- not a bug to
fix now.

The RFC 4226 counter is built by writing the Int64 in native memory order and
then reversing the bytes:

// Source/radRTL.ByteArrayUtils.pas:29-33
function ConvertToByteArray(const pValue:Int64):TBytes;
begin
  SetLength(Result, SizeOf(Int64));
  PInt64(@Result[0])^ := pValue;   // native order == little-endian on current targets
end;

// Source/radRTL.HOTP.pas:72
vData := ReverseByteArray(ConvertToByteArray(pCounterValue));  // reversed to big-endian

This produces correct big-endian output only because every current Delphi target
is little-endian
(Win32/Win64/Linux x64/macOS/iOS/Android are all LE). On a
hypothetical big-endian target the reverse step would flip already-correct bytes
and produce a wrong counter. The dependency is implicit -- nothing in the code
fails or warns on a BE host.

Decision: deferred. Changing working, RFC-vector-verified logic to guard
against a platform that does not exist for Delphi is not worth the churn. This
ticket exists so the assumption is findable if that ever changes, rather than
living only as a buried code comment. The author's blog article
(radprogrammer.com) also details why the reversal step is done this way.

Trigger to revisit: Embarcadero ships (or the project adopts) a big-endian
Delphi target, or this code is ported to a toolchain/runtime where big-endian is
possible.

Reference fix when/if triggered (host-independent, also removes ReverseByteArray
from the OTP path):

function ConvertToBigEndianBytes(const pValue:Int64):TBytes;
var
  i:Integer;
begin
  SetLength(Result, SizeOf(Int64));
  for i := 0 to 7 do
    Result[i] := Byte(UInt64(pValue) shr (56 - i * 8)); // Result[0]=MSB .. Result[7]=LSB
end;

Shifts act on the value, not the memory layout, so the output is identical on LE
and BE hosts. RFC 4226/6238 vector tests are the safety net for any future swap.

Acceptance criteria (only if the trigger occurs):

  • Counter byte-order is produced host-independently (shift-based conversion) and no longer relies on native memory layout + reversal.
  • RFC 4226/6238 vector tests pass on the big-endian target.
  • ReverseByteArray is removed from the OTP path (or retained only if still used elsewhere).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions