|
@@ -0,0 +1,327 @@
|
|
|
+{
|
|
|
+ This file is part of the Free Pascal run time library.
|
|
|
+ Copyright (c) 2021 by Michael Van Canneyt,
|
|
|
+ member of the Free Pascal development team
|
|
|
+
|
|
|
+ Base16,Base32,Base32-hex,Base32-crockford, Base64,Base64url encoding/decoding, with or without padding
|
|
|
+
|
|
|
+ See the file COPYING.FPC, included in this distribution,
|
|
|
+ for details about the copyright.
|
|
|
+
|
|
|
+ This program is distributed in the hope that it will be useful,
|
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
+
|
|
|
+ **********************************************************************}
|
|
|
+unit basenenc;
|
|
|
+
|
|
|
+{$mode ObjFPC}{$H+}
|
|
|
+
|
|
|
+interface
|
|
|
+
|
|
|
+uses Types, SysUtils;
|
|
|
+
|
|
|
+Type
|
|
|
+
|
|
|
+ { TAlphabetEncoder }
|
|
|
+ TReverseAlphabet = Array[0..255] of Byte;
|
|
|
+ TStandardEncoder = (seBase16,
|
|
|
+ seBase32,seBase32hex,seBase32CrockFord,
|
|
|
+ seBase64,seBase64URL);
|
|
|
+ TAlphabetEncoder = Class (TObject)
|
|
|
+ protected
|
|
|
+ Const
|
|
|
+ StdBits : Array[TStandardEncoder] of Byte = (4,5,5,5,6,6);
|
|
|
+ StdPads : Array[TStandardEncoder] of Byte = (0,8,8,8,4,4);
|
|
|
+ StdAlpha : Array[TStandardEncoder] of String = (
|
|
|
+ '0123456789ABCDEF',
|
|
|
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
|
|
|
+ '0123456789ABCDEFGHIJKLMNOPQRSTUV',
|
|
|
+ '0123456789ABCDEFGHJKMNPQRSTVWZYZ',
|
|
|
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
|
|
|
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_');
|
|
|
+ Private
|
|
|
+ FBits : Byte;
|
|
|
+ FAlphabet : TByteDynArray;
|
|
|
+ FReverse : TReverseAlphabet;
|
|
|
+ FPadding : Integer;
|
|
|
+ class var StdEncoders : Array[TStandardEncoder] of TAlphabetEncoder;
|
|
|
+ class function GetStdEncoder(AIndex: Integer): TAlphabetEncoder; static;
|
|
|
+ public
|
|
|
+ // Construct an encoder with alphabet, bits per letter, padding size in bits
|
|
|
+ Constructor Create(aAlphabet : AnsiString; aBits : Byte; aPadding : Integer); virtual;
|
|
|
+ // Destroy all standard encoders
|
|
|
+ Class Destructor Done;
|
|
|
+ // Create a standard encoder. You must free the result
|
|
|
+ Class Function CreateStdEncoder(Std: TStandardEncoder): TAlphabetEncoder;
|
|
|
+ // Encode data in buffer aBuffer with length aLen. If doPad is true, add padding if needed.
|
|
|
+ Function Encode(aBuffer : PByte; aLen : Cardinal; doPad : Boolean = True) : AnsiString; virtual; overload;
|
|
|
+ // Encode data in buffer aBuffer. If doPad is true, add padding if needed.
|
|
|
+ Function Encode(aBuffer : TBytes; doPad : Boolean = True) : AnsiString; overload;
|
|
|
+ // Encode data in string aBuffer. If doPad is true, add padding if needed.
|
|
|
+ Function Encode(aBuffer : AnsiString; doPad : Boolean = True) : AnsiString; overload;
|
|
|
+ // Decode aSrcBuffer with length aLen.
|
|
|
+ // Buffer must have enough room. Calculate maximum needed room with GetDecodeLen
|
|
|
+ Function Decode(const aSrcBuffer : PByte; aLen : Integer; ABuffer : PByte) : Integer; virtual; overload;
|
|
|
+ // Buffer must have enough room. Calculate maximum needed room with GetDecodeLen
|
|
|
+ Function Decode(const S : AnsiString; ABuffer : PByte) : Integer; overload;
|
|
|
+ // Return a buffer with decoded data.
|
|
|
+ Function Decode(const S : AnsiString) : TBytes; overload;
|
|
|
+ // Return a buffer with decoded data, starting with buffer.
|
|
|
+ Function Decode(const aBuffer: PByte; aLen : Integer) : TBytes; overload;
|
|
|
+ // Get a decoding length for the encoded string S. May be oversized due to padding.
|
|
|
+ Function GetDecodeLen(const S : AnsiString) : Integer;
|
|
|
+ // Bits per characters
|
|
|
+ Property Bits : Byte Read FBits;
|
|
|
+ // ASCII value of characters
|
|
|
+ Property Alphabet : TByteDynArray Read FAlphabet;
|
|
|
+ // Reverse byte->character map
|
|
|
+ Property Reverse : TReverseAlphabet Read FReverse;
|
|
|
+ // Bits of padding
|
|
|
+ Property Padding : Integer Read FPadding;
|
|
|
+ // Standard encoders.
|
|
|
+ Class Property Base16 : TAlphabetEncoder Index Ord(seBase16) Read GetStdEncoder;
|
|
|
+ Class Property Base32 : TAlphabetEncoder Index Ord(seBase32) Read GetStdEncoder;
|
|
|
+ Class Property Base32Hex : TAlphabetEncoder Index Ord(seBase32Hex) Read GetStdEncoder;
|
|
|
+ Class Property Base32Crockford : TAlphabetEncoder Index Ord(seBase32Crockford) Read GetStdEncoder;
|
|
|
+ Class Property Base64 : TAlphabetEncoder Index Ord(seBase64) Read GetStdEncoder;
|
|
|
+ Class Property Base64URL : TAlphabetEncoder Index Ord(seBase64Url) Read GetStdEncoder;
|
|
|
+ end;
|
|
|
+
|
|
|
+// Shortcut access to standard encoders.
|
|
|
+// Do not free the results !
|
|
|
+Function Base16 : TAlphabetEncoder;
|
|
|
+Function Base32 : TAlphabetEncoder;
|
|
|
+Function Base32Hex : TAlphabetEncoder;
|
|
|
+Function Base32Crockford : TAlphabetEncoder;
|
|
|
+Function Base64 : TAlphabetEncoder;
|
|
|
+Function Base64URL : TAlphabetEncoder;
|
|
|
+Function GetStandardEncoder(aEncoder : TStandardEncoder): TAlphabetEncoder;
|
|
|
+
|
|
|
+implementation
|
|
|
+
|
|
|
+Function TAlphabetEncoder.Encode(aBuffer : TBytes; doPad : Boolean = True) : AnsiString;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=Encode(PByte(aBuffer),Length(aBuffer),DoPad);
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+function TAlphabetEncoder.Encode(aBuffer: AnsiString; doPad : Boolean = True): AnsiString;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=Encode(TEncoding.UTF8.GetAnsiBytes(aBuffer),DoPad);
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+Constructor TAlphabetEncoder.Create(aAlphabet: AnsiString; aBits: Byte; aPadding: Integer);
|
|
|
+
|
|
|
+Var
|
|
|
+ I : Integer;
|
|
|
+
|
|
|
+begin
|
|
|
+ if (Length(aAlphabet)<2) or (Length(aAlphabet)>255) then
|
|
|
+ Raise Exception.Create('Invalid alphabet length');
|
|
|
+ FBits:=ABits;
|
|
|
+ FPadding:=aPadding;
|
|
|
+ SetLength(FAlphaBet,Length(aAlphabet));
|
|
|
+ Move(aAlphabet[1],FAlphaBet[0],Length(aAlphabet));
|
|
|
+ for I:=1 to Length(aAlphabet) do
|
|
|
+ FReverse[Ord(aAlphaBet[i])]:=I;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+class destructor TAlphabetEncoder.Done;
|
|
|
+
|
|
|
+Var
|
|
|
+ Std : TStandardEncoder;
|
|
|
+
|
|
|
+begin
|
|
|
+ For Std in TStandardEncoder do
|
|
|
+ FreeAndNil(StdEncoders[Std]);
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+class function TAlphabetEncoder.CreateStdEncoder(Std : TStandardEncoder) : TAlphabetEncoder;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=TAlphaBetEncoder.Create(StdAlpha[Std],StdBits[Std],StdPads[Std]);
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+class function TAlphabetEncoder.GetStdEncoder(AIndex: Integer): TAlphabetEncoder; static;
|
|
|
+
|
|
|
+Var
|
|
|
+ Std : TStandardEncoder;
|
|
|
+
|
|
|
+begin
|
|
|
+ Std:=TStandardEncoder(aIndex);
|
|
|
+ if (StdEncoders[Std]=Nil) then
|
|
|
+ StdEncoders[Std]:=CreateStdEncoder(Std);
|
|
|
+ Result:=StdEncoders[Std];
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+function TAlphabetEncoder.Encode(aBuffer: PByte; Alen : Cardinal; doPad : Boolean = True): Ansistring;
|
|
|
+
|
|
|
+var
|
|
|
+ pSrc, pDest: pByte;
|
|
|
+ I, Reg, lBits, PadLen,OutLen: integer;
|
|
|
+
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:='';
|
|
|
+ Reg:=0;
|
|
|
+ lBits:=0;
|
|
|
+ PadLen:=0;
|
|
|
+ OutLen:=aLen*8;
|
|
|
+ OutLen:=(OutLen div Bits)+Ord((OutLen mod Bits) > 0 );
|
|
|
+ if DoPad and (Padding>0) then
|
|
|
+ begin
|
|
|
+ PadLen:=OutLen mod Padding;
|
|
|
+ if PadLen>0 then
|
|
|
+ Inc(OutLen,(Padding-PadLen));
|
|
|
+ end;
|
|
|
+ SetLength(Result,OutLen);
|
|
|
+ pSrc:=aBuffer;
|
|
|
+ pDest:=@Result[1];
|
|
|
+ for i:=1 to aLen do
|
|
|
+ begin
|
|
|
+ Reg:=Reg shl 8;
|
|
|
+ Reg:=Reg or pSrc^;
|
|
|
+ Inc(lBits,8);
|
|
|
+ inc(pSrc);
|
|
|
+ while (lBits>=Bits) do
|
|
|
+ begin
|
|
|
+ Dec(lBits,Bits);
|
|
|
+ pDest^:=Alphabet[(Reg shr lBits)];
|
|
|
+ Reg:= Reg-((Reg shr lBits) shl lBits);
|
|
|
+ inc(pDest);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ if (lBits>0) then
|
|
|
+ begin
|
|
|
+ pDest^:=Alphabet[Reg shl (Bits-lBits)];
|
|
|
+ inc(pDest);
|
|
|
+ end;
|
|
|
+ if DoPad and (PadLen>0) then
|
|
|
+ FillChar(pDest^,Padding-PadLen,'=');
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+Function TAlphabetEncoder.Decode(const aSrcBuffer : PByte; aLen : Integer; ABuffer : PByte) : Integer;
|
|
|
+
|
|
|
+var
|
|
|
+ i, Reg, lBits : Integer;
|
|
|
+ pSrc, pDest: pByte;
|
|
|
+
|
|
|
+begin
|
|
|
+ Reg:=0;
|
|
|
+ lBits:=0;
|
|
|
+ Result:=0;
|
|
|
+ while (aLen>0) and (aSrcBuffer[aLen-1]=Ord('=')) do
|
|
|
+ Dec(aLen);
|
|
|
+ if Alen=0 then exit;
|
|
|
+ pSrc:=@aSrcBuffer[0];
|
|
|
+ pDest:=aBuffer;
|
|
|
+ for i:=1 to aLen do
|
|
|
+ begin
|
|
|
+ if Reverse[pSrc^] <= 0 then
|
|
|
+ break;
|
|
|
+ Reg:=Reg shl Bits;
|
|
|
+ Reg:=Reg or (Reverse[pSrc^]-1);
|
|
|
+ Inc(lBits,Bits);
|
|
|
+ while (lBits>=8) do
|
|
|
+ begin
|
|
|
+ Dec(lBits,8);
|
|
|
+ pDest^:=Reg shr lBits;
|
|
|
+ inc(pDest);
|
|
|
+ end;
|
|
|
+ inc(pSrc);
|
|
|
+ end;
|
|
|
+ Result:=pDest-aBuffer;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+Function TAlphabetEncoder.GetDecodeLen(const S : AnsiString) : Integer;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=(length(s)*Bits) div 8;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+function TAlphabetEncoder.Decode(const S: AnsiString): TBytes;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=[];
|
|
|
+ SetLength(Result,GetDecodeLen(S));
|
|
|
+ SetLength(Result,Decode(S,PByte(Result)));
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+function TAlphabetEncoder.Decode(const aBuffer: PByte; aLen: Integer): TBytes;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=[];
|
|
|
+ SetLength(Result,(aLen*Bits) div 8);
|
|
|
+ SetLength(Result,Decode(aBuffer,aLen,PByte(Result)));
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+Function TAlphabetEncoder.Decode(const S : AnsiString; ABuffer : PByte) : Integer; overload;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=Decode(PByte(S),Length(S),ABuffer);
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+Function Base16 : TAlphabetEncoder;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=TAlphabetEncoder.Base16;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+Function Base32 : TAlphabetEncoder;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=TAlphabetEncoder.Base32;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+Function Base32Hex : TAlphabetEncoder;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=TAlphabetEncoder.Base32Hex;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+Function Base32CrockFord : TAlphabetEncoder;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=TAlphabetEncoder.Base32CrockFord;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+Function Base64 : TAlphabetEncoder;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=TAlphabetEncoder.Base64;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+Function Base64URL : TAlphabetEncoder;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=TAlphabetEncoder.Base64URL;
|
|
|
+end;
|
|
|
+
|
|
|
+
|
|
|
+Function GetStandardEncoder(aEncoder : TStandardEncoder): TAlphabetEncoder;
|
|
|
+
|
|
|
+begin
|
|
|
+ Result:=TAlphabetEncoder.GetStdEncoder(Ord(aEncoder));
|
|
|
+end;
|
|
|
+
|
|
|
+end.
|
|
|
+
|