basenenc.pp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2021 by Michael Van Canneyt,
  4. member of the Free Pascal development team
  5. Base16,Base32,Base32-hex,Base32-crockford, Base64,Base64url encoding/decoding, with or without padding
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$IFNDEF FPC_DOTTEDUNITS}
  13. unit basenenc;
  14. {$ENDIF FPC_DOTTEDUNITS}
  15. {$mode ObjFPC}{$H+}
  16. interface
  17. {$IFDEF FPC_DOTTEDUNITS}
  18. uses System.SysUtils;
  19. {$ELSE FPC_DOTTEDUNITS}
  20. uses SysUtils;
  21. {$ENDIF FPC_DOTTEDUNITS}
  22. Type
  23. { TAlphabetEncoder }
  24. TReverseAlphabet = Array[0..255] of Byte;
  25. TStandardEncoder = (seBase16,
  26. seBase32,seBase32hex,seBase32CrockFord,
  27. seBase64,seBase64URL);
  28. TAlphabetEncoder = Class (TObject)
  29. protected
  30. Const
  31. StdBits : Array[TStandardEncoder] of Byte = (4,5,5,5,6,6);
  32. StdPads : Array[TStandardEncoder] of Byte = (0,8,8,8,4,4);
  33. StdAlpha : Array[TStandardEncoder] of String = (
  34. '0123456789ABCDEF',
  35. 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
  36. '0123456789ABCDEFGHIJKLMNOPQRSTUV',
  37. '0123456789ABCDEFGHJKMNPQRSTVWXYZ',
  38. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
  39. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_');
  40. Private
  41. FBits : Byte;
  42. FAlphabet : TBytes;
  43. FReverse : TReverseAlphabet;
  44. FPadding : Integer;
  45. class var StdEncoders : Array[TStandardEncoder] of TAlphabetEncoder;
  46. class function GetStdEncoder(AIndex: Integer): TAlphabetEncoder; static;
  47. public
  48. // Construct an encoder with alphabet, bits per letter, padding size in bits
  49. Constructor Create(Const aAlphabet : AnsiString; aBits : Byte; aPadding : Integer); virtual;
  50. // Destroy all standard encoders
  51. Class Destructor Done;
  52. // Create a standard encoder. You must free the result
  53. Class Function CreateStdEncoder(Std: TStandardEncoder): TAlphabetEncoder;
  54. // Encode data in buffer aBuffer with length aLen. If doPad is true, add padding if needed.
  55. Function Encode(aBuffer : PByte; aLen : Cardinal; doPad : Boolean = True) : AnsiString; virtual; overload;
  56. // Encode data in buffer aBuffer. If doPad is true, add padding if needed.
  57. Function Encode(aBuffer : TBytes; doPad : Boolean = True) : AnsiString; overload;
  58. // Encode data in string aBuffer. If doPad is true, add padding if needed.
  59. Function Encode(const aBuffer : AnsiString; doPad : Boolean = True) : AnsiString; overload;
  60. // Decode aSrcBuffer with length aLen.
  61. // Buffer must have enough room. Calculate maximum needed room with GetDecodeLen
  62. Function Decode(const aSrcBuffer : PByte; aLen : Integer; ABuffer : PByte; SkipWhiteSpace : Boolean = True) : Integer; virtual; overload;
  63. // Buffer must have enough room. Calculate maximum needed room with GetDecodeLen
  64. Function Decode(const S : AnsiString; ABuffer : PByte; SkipWhiteSpace : Boolean = True) : Integer; overload;
  65. // Return a buffer with decoded data.
  66. Function Decode(const S : AnsiString; SkipWhiteSpace : Boolean = True) : TBytes; overload;
  67. // Return a buffer with decoded data, starting with buffer.
  68. Function Decode(const aBuffer: PByte; aLen : Integer) : TBytes; overload;
  69. // Get a decoding length for the encoded string S. May be oversized due to padding.
  70. Function GetDecodeLen(const S : AnsiString) : Integer;
  71. // Bits per characters
  72. Property Bits : Byte Read FBits;
  73. // ASCII value of characters
  74. Property Alphabet : TBytes Read FAlphabet;
  75. // Reverse byte->character map
  76. Property Reverse : TReverseAlphabet Read FReverse;
  77. // Bits of padding
  78. Property Padding : Integer Read FPadding;
  79. // Standard encoders.
  80. Class Property Base16 : TAlphabetEncoder Index Ord(seBase16) Read GetStdEncoder;
  81. Class Property Base32 : TAlphabetEncoder Index Ord(seBase32) Read GetStdEncoder;
  82. Class Property Base32Hex : TAlphabetEncoder Index Ord(seBase32Hex) Read GetStdEncoder;
  83. Class Property Base32Crockford : TAlphabetEncoder Index Ord(seBase32Crockford) Read GetStdEncoder;
  84. Class Property Base64 : TAlphabetEncoder Index Ord(seBase64) Read GetStdEncoder;
  85. Class Property Base64URL : TAlphabetEncoder Index Ord(seBase64Url) Read GetStdEncoder;
  86. end;
  87. // Shortcut access to standard encoders.
  88. // Do not free the results !
  89. Function Base16 : TAlphabetEncoder;
  90. Function Base32 : TAlphabetEncoder;
  91. Function Base32Hex : TAlphabetEncoder;
  92. Function Base32Crockford : TAlphabetEncoder;
  93. Function Base64 : TAlphabetEncoder;
  94. Function Base64URL : TAlphabetEncoder;
  95. Function GetStandardEncoder(aEncoder : TStandardEncoder): TAlphabetEncoder;
  96. Function GetRawStringBytes(const S : AnsiString) : TBytes;
  97. Function GetRawStringFromBytes(B : TBytes) : RawByteString;
  98. implementation
  99. Function GetRawStringFromBytes(B : TBytes) : RawByteString;
  100. Var
  101. L : Integer;
  102. begin
  103. Result:='';
  104. L:=Length(B);
  105. SetLength(Result,L);
  106. If L>0 then
  107. Move(B[0],Result[1],L);
  108. end;
  109. Function GetRawStringBytes(Const S : AnsiString) : TBytes;
  110. Var
  111. L : Integer;
  112. begin
  113. Result:=[];
  114. L:=Length(S);
  115. SetLength(Result,L);
  116. If L>0 then
  117. Move(S[1],Result[0],L);
  118. end;
  119. Function TAlphabetEncoder.Encode(aBuffer : TBytes; doPad : Boolean = True) : AnsiString;
  120. begin
  121. Result:=Encode(PByte(aBuffer),Length(aBuffer),DoPad);
  122. end;
  123. function TAlphabetEncoder.Encode(Const aBuffer: AnsiString; doPad : Boolean = True): AnsiString;
  124. begin
  125. Result:=Encode(GetRawStringBytes(aBuffer),DoPad);
  126. end;
  127. Constructor TAlphabetEncoder.Create(const aAlphabet: AnsiString; aBits: Byte; aPadding: Integer);
  128. Var
  129. I : Integer;
  130. begin
  131. if (Length(aAlphabet)<2) or (Length(aAlphabet)>255) then
  132. Raise Exception.Create('Invalid alphabet length');
  133. FBits:=ABits;
  134. FPadding:=aPadding;
  135. SetLength(FAlphaBet,Length(aAlphabet));
  136. Move(aAlphabet[1],FAlphaBet[0],Length(aAlphabet));
  137. for I:=1 to Length(aAlphabet) do
  138. FReverse[Ord(aAlphaBet[i])]:=I;
  139. end;
  140. class destructor TAlphabetEncoder.Done;
  141. Var
  142. Std : TStandardEncoder;
  143. begin
  144. For Std in TStandardEncoder do
  145. FreeAndNil(StdEncoders[Std]);
  146. end;
  147. class function TAlphabetEncoder.CreateStdEncoder(Std : TStandardEncoder) : TAlphabetEncoder;
  148. begin
  149. Result:=TAlphaBetEncoder.Create(StdAlpha[Std],StdBits[Std],StdPads[Std]);
  150. end;
  151. class function TAlphabetEncoder.GetStdEncoder(AIndex: Integer): TAlphabetEncoder; static;
  152. Var
  153. Std : TStandardEncoder;
  154. begin
  155. Std:=TStandardEncoder(aIndex);
  156. if (StdEncoders[Std]=Nil) then
  157. StdEncoders[Std]:=CreateStdEncoder(Std);
  158. Result:=StdEncoders[Std];
  159. end;
  160. function TAlphabetEncoder.Encode(aBuffer: PByte; Alen : Cardinal; doPad : Boolean = True): Ansistring;
  161. var
  162. pSrc, pDest: pByte;
  163. I, Reg, lBits, PadLen,OutLen: integer;
  164. begin
  165. Result:='';
  166. Reg:=0;
  167. lBits:=0;
  168. PadLen:=0;
  169. OutLen:=aLen*8;
  170. OutLen:=(OutLen div Bits)+Ord((OutLen mod Bits) > 0 );
  171. if DoPad and (Padding>0) then
  172. begin
  173. PadLen:=OutLen mod Padding;
  174. if PadLen>0 then
  175. Inc(OutLen,(Padding-PadLen));
  176. end;
  177. SetLength(Result,OutLen);
  178. pSrc:=aBuffer;
  179. pDest:=@Result[1];
  180. for i:=1 to aLen do
  181. begin
  182. Reg:=Reg shl 8;
  183. Reg:=Reg or pSrc^;
  184. Inc(lBits,8);
  185. inc(pSrc);
  186. while (lBits>=Bits) do
  187. begin
  188. Dec(lBits,Bits);
  189. pDest^:=Alphabet[(Reg shr lBits)];
  190. Reg:= Reg-((Reg shr lBits) shl lBits);
  191. inc(pDest);
  192. end;
  193. end;
  194. if (lBits>0) then
  195. begin
  196. pDest^:=Alphabet[Reg shl (Bits-lBits)];
  197. inc(pDest);
  198. end;
  199. if DoPad and (PadLen>0) then
  200. FillChar(pDest^,Padding-PadLen,'=');
  201. end;
  202. Function TAlphabetEncoder.Decode(const aSrcBuffer : PByte; aLen : Integer; ABuffer : PByte;SkipWhiteSpace : Boolean = True) : Integer;
  203. Const
  204. WhiteSpace = [Ord(' '),10,13,9];
  205. var
  206. i, Reg, lBits : Integer;
  207. pSrc, pDest: pByte;
  208. begin
  209. Reg:=0;
  210. lBits:=0;
  211. Result:=0;
  212. while (aLen>0) and (aSrcBuffer[aLen-1]=Ord('=')) do
  213. Dec(aLen);
  214. if Alen=0 then exit;
  215. pSrc:=@aSrcBuffer[0];
  216. pDest:=aBuffer;
  217. I:=1;
  218. While (i<=aLen) do
  219. begin
  220. if SkipWhiteSpace then
  221. begin
  222. While (PSrc^ in WhiteSpace) and (I<=aLen) do
  223. begin
  224. Inc(PSrc);
  225. Inc(I);
  226. end;
  227. if I>aLen then
  228. break;
  229. end;
  230. if Reverse[pSrc^] <= 0 then
  231. break;
  232. Reg:=Reg shl Bits;
  233. Reg:=Reg or (Reverse[pSrc^]-1);
  234. Inc(lBits,Bits);
  235. while (lBits>=8) do
  236. begin
  237. Dec(lBits,8);
  238. pDest^:=byte(Reg shr lBits);
  239. inc(pDest);
  240. end;
  241. inc(pSrc);
  242. Inc(i);
  243. end;
  244. Result:=pDest-aBuffer;
  245. end;
  246. Function TAlphabetEncoder.GetDecodeLen(const S : AnsiString) : Integer;
  247. begin
  248. Result:=(length(s)*Bits) div 8;
  249. end;
  250. function TAlphabetEncoder.Decode(const S: AnsiString; SkipWhiteSpace : Boolean = True): TBytes;
  251. begin
  252. Result:=[];
  253. SetLength(Result,GetDecodeLen(S));
  254. SetLength(Result,Decode(S,PByte(Result),SkipWhiteSpace));
  255. end;
  256. function TAlphabetEncoder.Decode(const aBuffer: PByte; aLen: Integer): TBytes;
  257. begin
  258. Result:=[];
  259. SetLength(Result,(aLen*Bits) div 8);
  260. SetLength(Result,Decode(aBuffer,aLen,PByte(Result)));
  261. end;
  262. Function TAlphabetEncoder.Decode(const S : AnsiString; ABuffer : PByte;SkipWhiteSpace : Boolean = True) : Integer; overload;
  263. begin
  264. Result:=Decode(PByte(S),Length(S),ABuffer,SkipWhiteSpace);
  265. end;
  266. Function Base16 : TAlphabetEncoder;
  267. begin
  268. Result:=TAlphabetEncoder.Base16;
  269. end;
  270. Function Base32 : TAlphabetEncoder;
  271. begin
  272. Result:=TAlphabetEncoder.Base32;
  273. end;
  274. Function Base32Hex : TAlphabetEncoder;
  275. begin
  276. Result:=TAlphabetEncoder.Base32Hex;
  277. end;
  278. Function Base32CrockFord : TAlphabetEncoder;
  279. begin
  280. Result:=TAlphabetEncoder.Base32CrockFord;
  281. end;
  282. Function Base64 : TAlphabetEncoder;
  283. begin
  284. Result:=TAlphabetEncoder.Base64;
  285. end;
  286. Function Base64URL : TAlphabetEncoder;
  287. begin
  288. Result:=TAlphabetEncoder.Base64URL;
  289. end;
  290. Function GetStandardEncoder(aEncoder : TStandardEncoder): TAlphabetEncoder;
  291. begin
  292. Result:=TAlphabetEncoder.GetStdEncoder(Ord(aEncoder));
  293. end;
  294. end.