1419 lines
45 KiB
ObjectPascal
1419 lines
45 KiB
ObjectPascal
{******************************************************************************}
|
|
{ }
|
|
{ Library: Fundamentals 5.00 }
|
|
{ File name: cASN1.pas }
|
|
{ File version: 5.05 }
|
|
{ Description: Abstract Syntax Notation One (ASN.1) }
|
|
{ BER (Basic Encoding Routines) }
|
|
{ }
|
|
{ Copyright: Copyright (c) 2010-2020, David J Butler }
|
|
{ All rights reserved. }
|
|
{ Redistribution and use in source and binary forms, with }
|
|
{ or without modification, are permitted provided that }
|
|
{ the following conditions are met: }
|
|
{ Redistributions of source code must retain the above }
|
|
{ copyright notice, this list of conditions and the }
|
|
{ following disclaimer. }
|
|
{ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND }
|
|
{ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED }
|
|
{ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED }
|
|
{ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A }
|
|
{ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL }
|
|
{ THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, }
|
|
{ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR }
|
|
{ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
|
|
{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF }
|
|
{ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) }
|
|
{ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER }
|
|
{ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING }
|
|
{ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE }
|
|
{ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE }
|
|
{ POSSIBILITY OF SUCH DAMAGE. }
|
|
{ }
|
|
{ Github: https://github.com/fundamentalslib }
|
|
{ E-mail: fundamentals.library at gmail.com }
|
|
{ }
|
|
{ References: }
|
|
{ }
|
|
{ ITU-T Rec. X.690 (07/2002) }
|
|
{ http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf }
|
|
{ http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One }
|
|
{ http://en.wikipedia.org/wiki/Basic_Encoding_Rules }
|
|
{ http://luca.ntop.org/Teaching/Appunti/asn1.html }
|
|
{ http://www.obj-sys.com/asn1tutorial/node10.html }
|
|
{ [OID registry] http://www.alvestrand.no/objectid/top.html }
|
|
{ [OID registry] http://oid-info.com/get/1 }
|
|
{ }
|
|
{ Revision history: }
|
|
{ }
|
|
{ 2010/11/08 0.01 Initial development: encoding routines. }
|
|
{ 2010/11/23 0.02 Initial development: decoding routines. }
|
|
{ 2011/04/02 0.03 Compilable with Delphi 5. }
|
|
{ 2018/07/17 5.04 Revised for Fundamentals 5. }
|
|
{ 2018/08/12 5.05 String type changes. }
|
|
{ }
|
|
{ Supported compilers: }
|
|
{ }
|
|
{ Delphi 2010-10.4 Win32/Win64 5.05 2020/06/02 }
|
|
{ Delphi 10.2-10.4 Linux64 5.05 2020/06/02 }
|
|
{ FreePascal 3.0.4 Win64 5.05 2020/06/02 }
|
|
{ }
|
|
{ Todo: }
|
|
{ - Parser to str repr }
|
|
{ - Parser to dom }
|
|
{******************************************************************************}
|
|
|
|
{$INCLUDE ..\flcInclude.inc}
|
|
|
|
{$IFDEF DEBUG}
|
|
{$IFDEF TEST}
|
|
{$DEFINE ASN1_TEST}
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
unit flcASN1;
|
|
|
|
interface
|
|
|
|
uses
|
|
{ System }
|
|
SysUtils,
|
|
|
|
{ Fundamentals }
|
|
flcStdTypes;
|
|
|
|
|
|
|
|
{ ASN.1 errors }
|
|
type
|
|
EASN1 = class(Exception);
|
|
|
|
|
|
|
|
{ Type identifier octet }
|
|
{ }
|
|
{ Class Bit 8 Bit 7 }
|
|
{ universal 0 0 }
|
|
{ application 0 1 }
|
|
{ context-specific 1 0 }
|
|
{ private 1 1 }
|
|
{ }
|
|
{ Bit 6 = P/C (Primitive/Constructed) }
|
|
const
|
|
ASN1_ID_END_OF_CONTENT = $00;
|
|
ASN1_ID_BOOLEAN = $01;
|
|
ASN1_ID_INTEGER = $02;
|
|
ASN1_ID_BIT_STRING = $03;
|
|
ASN1_ID_OCTET_STRING = $04;
|
|
ASN1_ID_NULL = $05;
|
|
ASN1_ID_OBJECT_IDENTIFIER = $06;
|
|
ASN1_ID_OBJECT_DESCRIPTOR = $07;
|
|
ASN1_ID_EXTERNAL = $08;
|
|
ASN1_ID_REAL = $09;
|
|
ASN1_ID_ENUMERATED = $0A;
|
|
ASN1_ID_EMBEDDED_PDV = $0B;
|
|
ASN1_ID_UTF8STRING = $0C; // ISO10646-1 (Unicode, UTF8)
|
|
ASN1_ID_RELATIVE_OID = $0D;
|
|
ASN1_ID_NUMERICSTRING = $12; // ASCII
|
|
ASN1_ID_PRINTABLESTRING = $13; // ASCII (A..Z, a..z, 0..9, ' = ( ) + , - . / : ?) (Excludes @ & _)
|
|
ASN1_ID_T61STRING = $14; // CCITT T.61 (Teletex) (ASCII, 8 bit, escape sequences)
|
|
ASN1_ID_VIDEOTEXSTRING = $15; // CCITT T.100/T.101
|
|
ASN1_ID_IA5STRING = $16; // International Alphabet 5 (ASCII)
|
|
ASN1_ID_UTCTIME = $17;
|
|
ASN1_ID_GENERALIZEDTIME = $18;
|
|
ASN1_ID_GRAPHICSTRING = $19;
|
|
ASN1_ID_VISIBLESTRING = $1A; // ISO646 (ASCII)
|
|
ASN1_ID_GENERALSTRING = $1B;
|
|
ASN1_ID_UNIVERSALSTRING = $1C; // ISO10646-1 (Unicode, UCS4)
|
|
ASN1_ID_CHARACTERSTRING = $1D;
|
|
ASN1_ID_BMPSTRING = $1E; // ISO10646-1 (Unicode, UCS2)
|
|
ASN1_ID_SEQUENCE = $30;
|
|
ASN1_ID_SET = $31;
|
|
|
|
ASN1_ID_CONSTRUCTED = $20;
|
|
ASN1_ID_APPLICATION = $40;
|
|
ASN1_ID_CONTEXT_SPECIFIC = $80;
|
|
ASN1_ID_PRIVATE = $C0;
|
|
|
|
ASN1_ID_CONSTR_APPLICATION = ASN1_ID_CONSTRUCTED or ASN1_ID_APPLICATION;
|
|
ASN1_ID_CONSTR_CONTEXT_SPECIFIC = ASN1_ID_CONSTRUCTED or ASN1_ID_CONTEXT_SPECIFIC;
|
|
ASN1_ID_CONSTR_PRIVATE = ASN1_ID_CONSTRUCTED or ASN1_ID_PRIVATE;
|
|
|
|
|
|
|
|
{ Object identifiers }
|
|
|
|
type
|
|
TASN1ObjectIdentifier = array of Integer;
|
|
|
|
procedure ASN1OIDInit(var A: TASN1ObjectIdentifier; const B: array of Integer);
|
|
function ASN1OIDToStrB(const A: TASN1ObjectIdentifier): RawByteString;
|
|
function ASN1OIDToStr(const A: TASN1ObjectIdentifier): String;
|
|
function ASN1OIDEqual(const A: TASN1ObjectIdentifier; const B: array of Integer): Boolean;
|
|
|
|
|
|
|
|
{ OIDs for common hash and cipher algorithms }
|
|
|
|
const
|
|
// PKCS-1
|
|
OID_PKCS_1 : array[0..5] of Integer = (1, 2, 840, 113549, 1, 1);
|
|
OID_RSA : array[0..6] of Integer = (1, 2, 840, 113549, 1, 1, 1);
|
|
OID_RSA_PKCS1_MD2 : array[0..6] of Integer = (1, 2, 840, 113549, 1, 1, 2);
|
|
OID_RSA_PKCS1_MD5 : array[0..6] of Integer = (1, 2, 840, 113549, 1, 1, 4);
|
|
OID_RSA_PKCS1_SHA1 : array[0..6] of Integer = (1, 2, 840, 113549, 1, 1, 5);
|
|
OID_RSA_OAEP : array[0..6] of Integer = (1, 2, 840, 113549, 1, 1, 7);
|
|
OID_RSA_MGF1 : array[0..6] of Integer = (1, 2, 840, 113549, 1, 1, 8);
|
|
|
|
// PKCS#3
|
|
OID_PKCS_3 : array[0..5] of Integer = (1, 2, 840, 113549, 1, 3);
|
|
OID_DHKeyAgreement : array[0..6] of Integer = (1, 2, 840, 113549, 1, 3, 1);
|
|
|
|
// PKCS-9/Signatures/SMIME/alg
|
|
OID_PKCS_9 : array[0..5] of Integer = (1, 2, 840, 113549, 1, 9);
|
|
OID_3DES_wrap : array[0..8] of Integer = (1, 2, 840, 113549, 1, 9, 16, 3, 6);
|
|
OID_RC2_wrap : array[0..8] of Integer = (1, 2, 840, 113549, 1, 9, 16, 3, 7);
|
|
OID_HMAC_3DES_wrap : array[0..8] of Integer = (1, 2, 840, 113549, 1, 9, 16, 3, 11);
|
|
OID_HMAC_AES_wrap : array[0..8] of Integer = (1, 2, 840, 113549, 1, 9, 16, 3, 12);
|
|
|
|
// PKCS#2
|
|
OID_MD2 : array[0..5] of Integer = (1, 2, 840, 113549, 2, 2);
|
|
OID_MD4 : array[0..5] of Integer = (1, 2, 840, 113549, 2, 4);
|
|
OID_MD5 : array[0..5] of Integer = (1, 2, 840, 113549, 2, 5);
|
|
OID_HMAC_SHA1 : array[0..5] of Integer = (1, 2, 840, 113549, 2, 7);
|
|
OID_HMAC_SHA224 : array[0..5] of Integer = (1, 2, 840, 113549, 2, 8);
|
|
OID_HMAC_SHA256 : array[0..5] of Integer = (1, 2, 840, 113549, 2, 9);
|
|
OID_HMAC_SHA384 : array[0..5] of Integer = (1, 2, 840, 113549, 2, 10);
|
|
OID_HMAC_SHA512 : array[0..5] of Integer = (1, 2, 840, 113549, 2, 11);
|
|
|
|
// rsadsi/encryptionalgorithm
|
|
OID_RC2_CBC : array[0..5] of Integer = (1, 2, 840, 113549, 3, 2);
|
|
OID_RC2_ECB : array[0..5] of Integer = (1, 2, 840, 113549, 3, 3);
|
|
OID_RC4 : array[0..5] of Integer = (1, 2, 840, 113549, 3, 4);
|
|
OID_RC4_MAC : array[0..5] of Integer = (1, 2, 840, 113549, 3, 5);
|
|
OID_DES_CBC : array[0..5] of Integer = (1, 2, 840, 113549, 3, 6);
|
|
OID_DES_EDE3_CBC : array[0..5] of Integer = (1, 2, 840, 113549, 3, 7);
|
|
OID_RC5_CBC : array[0..5] of Integer = (1, 2, 840, 113549, 3, 8);
|
|
OID_RC5_CBCPad : array[0..5] of Integer = (1, 2, 840, 113549, 3, 9);
|
|
|
|
// X9-57
|
|
OID_DSA : array[0..5] of Integer = (1, 2, 840, 10040, 4, 1);
|
|
OID_DSA_SHA1 : array[0..5] of Integer = (1, 2, 840, 10040, 4, 3);
|
|
|
|
// secsig
|
|
OID_SHA1 : array[0..5] of Integer = (1, 3, 14, 3, 2, 26);
|
|
|
|
|
|
|
|
{ Encode }
|
|
|
|
function ASN1EncodeLength(const Len: Integer): RawByteString;
|
|
function ASN1EncodeObj(const TypeID: Byte; const Data: RawByteString): RawByteString;
|
|
|
|
function ANS1EncodeEndOfContent: RawByteString;
|
|
function ASN1EncodeNull: RawByteString;
|
|
function ASN1EncodeBoolean(const A: Boolean): RawByteString;
|
|
function ASN1EncodeDataInteger8(const A: ShortInt): RawByteString;
|
|
function ASN1EncodeDataInteger16(const A: SmallInt): RawByteString;
|
|
function ASN1EncodeDataInteger24(const A: Int32): RawByteString;
|
|
function ASN1EncodeDataInteger32(const A: Int32): RawByteString;
|
|
function ASN1EncodeDataInteger64(const A: Int64): RawByteString;
|
|
function ASN1EncodeInteger8(const A: ShortInt): RawByteString;
|
|
function ASN1EncodeInteger16(const A: SmallInt): RawByteString;
|
|
function ASN1EncodeInteger24(const A: Int32): RawByteString;
|
|
function ASN1EncodeInteger32(const A: Int32): RawByteString;
|
|
function ASN1EncodeInteger64(const A: Int64): RawByteString;
|
|
function ASN1EncodeIntegerBuf(const A; const Size: Integer): RawByteString;
|
|
function ASN1EncodeIntegerBufStr(const A: RawByteString): RawByteString;
|
|
function ASN1EncodeEnumerated(const A: Int64): RawByteString;
|
|
function ASN1EncodeBitString(const A: RawByteString; const UnusedBits: Byte): RawByteString;
|
|
function ASN1EncodeOctetString(const A: RawByteString): RawByteString;
|
|
function ASN1EncodeInt32AsOctetString(const A: Int32): RawByteString;
|
|
function ASN1EncodeUTF8String(const A: RawByteString): RawByteString;
|
|
function ASN1EncodeIA5String(const A: RawByteString): RawByteString;
|
|
function ASN1EncodeVisibleString(const A: RawByteString): RawByteString;
|
|
function ASN1EncodeNumericString(const A: RawByteString): RawByteString;
|
|
function ASN1EncodePrintableString(const A: RawByteString): RawByteString;
|
|
function ASN1EncodeTeletexString(const A: RawByteString): RawByteString;
|
|
function ASN1EncodeUniversalString(const A: UnicodeString): RawByteString;
|
|
function ASN1EncodeBMPString(const A: UnicodeString): RawByteString;
|
|
function ASN1EncodeUTCTime(const A: TDateTime): RawByteString;
|
|
function ASN1EncodeGeneralizedTime(const A: TDateTime): RawByteString;
|
|
function ASN1EncodeOID(const OID: array of Integer): RawByteString;
|
|
function ASN1EncodeSequence(const A: RawByteString): RawByteString;
|
|
function ASN1EncodeSet(const A: RawByteString): RawByteString;
|
|
function ASN1EncodeContextSpecific(const I: Integer; const A: RawByteString): RawByteString;
|
|
|
|
|
|
|
|
{ Decode }
|
|
|
|
function ASN1DecodeLength(const Buf; const Size: Integer; var Len: Integer): Integer;
|
|
function ASN1DecodeObjHeader(const Buf; const Size: Integer;
|
|
var TypeID: Byte; var Len: Integer; var Data: Pointer): Integer;
|
|
function ASN1TypeIsConstructedType(const TypeID: Byte): Boolean;
|
|
function ASN1TypeIsContextSpecific(const TypeID: Byte; var Idx: Integer): Boolean;
|
|
|
|
function ASN1DecodeDataBoolean(const Buf; const Size: Integer; var A: Boolean): Integer;
|
|
function ASN1DecodeDataInteger32(const Buf; const Size: Integer; var A: Int32): Integer;
|
|
function ASN1DecodeDataInteger64(const Buf; const Size: Integer; var A: Int64): Integer;
|
|
function ASN1DecodeDataIntegerBuf(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeDataBitString(const Buf; const Size: Integer; var A: RawByteString; var UnusedBits: Byte): Integer;
|
|
function ASN1DecodeDataRawByteString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeDataOctetString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeDataIA5String(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeDataVisibleString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeDataNumericString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeDataPrintableString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeDataTeletexString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeDataUTF8String(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeDataUniversalString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeDataBMPString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeDataOID(const Buf; const Size: Integer; var A: TASN1ObjectIdentifier): Integer;
|
|
function ASN1DecodeDataUTCTime(const Buf; const Size: Integer; var A: TDateTime): Integer;
|
|
function ASN1DecodeDataGeneralizedTime(const Buf; const Size: Integer; var A: TDateTime): Integer;
|
|
|
|
function ASN1DecodeBoolean(const TypeID: Byte; const DataBuf; const DataSize: Integer; var A: Boolean): Integer;
|
|
function ASN1DecodeInteger32(const TypeID: Byte; const DataBuf; const DataSize: Integer; var A: Int32): Integer;
|
|
function ASN1DecodeInteger64(const TypeID: Byte; const DataBuf; const DataSize: Integer; var A: Int64): Integer;
|
|
function ASN1DecodeIntegerBuf(const TypeID: Byte; const DataBuf; const DataSize: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeBitString(const TypeID: Byte; const DataBuf; const DataSize: Integer; var A: RawByteString; var UnusedBits: Byte): Integer;
|
|
function ASN1DecodeString(const TypeID: Byte; const DataBuf; const DataSize: Integer; var A: RawByteString): Integer;
|
|
function ASN1DecodeOID(const TypeID: Byte; const DataBuf; const DataSize: Integer; var A: TASN1ObjectIdentifier): Integer;
|
|
function ASN1DecodeTime(const TypeID: Byte; const DataBuf; const DataSize: Integer; var A: TDateTime): Integer;
|
|
|
|
type
|
|
TASN1ParseProc =
|
|
procedure (const TypeID: Byte; const DataBuf; const DataSize: Integer;
|
|
const ObjectIdx: Integer; const CallerData: NativeInt);
|
|
|
|
function ASN1Parse(
|
|
const Buf; const Size: Integer;
|
|
const ParseProc: TASN1ParseProc;
|
|
const CallerData: NativeInt): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ Test }
|
|
{ }
|
|
{$IFDEF ASN1_TEST}
|
|
procedure Test;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
{ Fundamentals }
|
|
flcUtils,
|
|
flcUTF,
|
|
flcUnicodeCodecs;
|
|
|
|
|
|
|
|
{ Errors }
|
|
|
|
const
|
|
SErr_EncodeError = 'Encode error';
|
|
|
|
SErr_DecodeError = 'Decode error';
|
|
SErr_DecodeInvalidType = 'Decode error: Invalid type';
|
|
SErr_DecodeConversionError = 'Decode error: Invalid type conversion';
|
|
|
|
|
|
|
|
{ Constants }
|
|
|
|
const
|
|
MinInt8 = -128;
|
|
MaxInt8 = 127;
|
|
MinInt16 = -32768;
|
|
MaxInt16 = 32767;
|
|
MinInt24 = -8388608;
|
|
MaxInt24 = 8388607;
|
|
MinInt32 = Low(Int32); // -2147483648;
|
|
MaxInt32 = 2147483647;
|
|
|
|
|
|
|
|
{ Types }
|
|
|
|
{$IFDEF DELPHI5}
|
|
type
|
|
PByte = ^Byte;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ Utilities }
|
|
|
|
{$IFDEF DELPHI5}
|
|
function TryStrToInt(const S: AnsiString; var I: Integer): Boolean;
|
|
var Error : Integer;
|
|
begin
|
|
Val(S, I, Error);
|
|
Result := Error = 0;
|
|
end;
|
|
|
|
function TryStrToInt64(const S: AnsiString; var I: Int64): Boolean;
|
|
var Error : Integer;
|
|
begin
|
|
Val(S, I, Error);
|
|
Result := Error = 0;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ Object identifiers }
|
|
|
|
procedure ASN1OIDInit(var A: TASN1ObjectIdentifier; const B: array of Integer);
|
|
var L, I : Integer;
|
|
begin
|
|
L := Length(B);
|
|
SetLength(A, L);
|
|
for I := 0 to L - 1 do
|
|
A[I] := B[I];
|
|
end;
|
|
|
|
function ASN1OIDToStrB(const A: TASN1ObjectIdentifier): RawByteString;
|
|
var I : Integer;
|
|
S : RawByteString;
|
|
begin
|
|
SetLength(S, 0);
|
|
for I := 0 to Length(A) - 1 do
|
|
begin
|
|
if I > 0 then
|
|
S := S + '.';
|
|
S := S + IntToStringB(A[I]);
|
|
end;
|
|
Result := S;
|
|
end;
|
|
|
|
function ASN1OIDToStr(const A: TASN1ObjectIdentifier): String;
|
|
var I : Integer;
|
|
S : String;
|
|
begin
|
|
SetLength(S, 0);
|
|
for I := 0 to Length(A) - 1 do
|
|
begin
|
|
if I > 0 then
|
|
S := S + '.';
|
|
S := S + IntToStr(A[I]);
|
|
end;
|
|
Result := S;
|
|
end;
|
|
|
|
function ASN1OIDEqual(const A: TASN1ObjectIdentifier; const B: array of Integer): Boolean;
|
|
var L, M, I : Integer;
|
|
begin
|
|
L := Length(A);
|
|
M := Length(B);
|
|
Result := False;
|
|
if L <> M then
|
|
exit;
|
|
for I := 0 to L - 1 do
|
|
if A[I] <> B[I] then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
|
|
|
|
|
|
{ Encode }
|
|
|
|
function ASN1EncodeLength_Short(const Len: Integer): RawByteString;
|
|
begin
|
|
Assert(Len >= 0);
|
|
Assert(Len <= 127);
|
|
|
|
SetLength(Result, 1);
|
|
Result := ByteChar(Len);
|
|
end;
|
|
|
|
function ASN1EncodeLength_Long(const Len: Integer): RawByteString;
|
|
var N, I : Integer;
|
|
B : array[0..3] of Byte;
|
|
begin
|
|
Assert(Len >= 0);
|
|
|
|
// count bytes required to represent Len
|
|
N := 0;
|
|
I := Len;
|
|
repeat
|
|
B[N] := I mod $100;
|
|
I := I div $100;
|
|
Inc(N);
|
|
until I = 0;
|
|
// set result
|
|
SetLength(Result, N + 1);
|
|
Result[1] := AnsiChar(N or $80);
|
|
for I := 0 to N - 1 do
|
|
Result[2 + I] := AnsiChar(B[N - I - 1]);
|
|
end;
|
|
|
|
function ASN1EncodeLength(const Len: Integer): RawByteString;
|
|
begin
|
|
if Len < $80 then
|
|
Result := ASN1EncodeLength_Short(Len)
|
|
else
|
|
Result := ASN1EncodeLength_Long(Len);
|
|
end;
|
|
|
|
function ASN1EncodeObj(const TypeID: Byte; const Data: RawByteString): RawByteString;
|
|
begin
|
|
Result :=
|
|
AnsiChar(TypeID) +
|
|
ASN1EncodeLength(Length(Data)) +
|
|
Data;
|
|
end;
|
|
|
|
function ANS1EncodeEndOfContent: RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_END_OF_CONTENT, '');
|
|
end;
|
|
|
|
function ASN1EncodeNull: RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_NULL, '');
|
|
end;
|
|
|
|
function ASN1EncodeBoolean(const A: Boolean): RawByteString;
|
|
begin
|
|
if A then
|
|
Result := ASN1EncodeObj(ASN1_ID_BOOLEAN, #$FF)
|
|
else
|
|
Result := ASN1EncodeObj(ASN1_ID_BOOLEAN, #$00);
|
|
end;
|
|
|
|
function ASN1EncodeDataInteger8(const A: ShortInt): RawByteString;
|
|
begin
|
|
Result := AnsiChar(Byte(A));
|
|
end;
|
|
|
|
function ASN1EncodeDataInteger16(const A: SmallInt): RawByteString;
|
|
var D : RawByteString;
|
|
begin
|
|
if (A >= MinInt8) and (A <= MaxInt8) then
|
|
Result := ASN1EncodeDataInteger8(A)
|
|
else
|
|
begin
|
|
SetLength(D, 2);
|
|
Move(A, D[1], 2);
|
|
Result := D;
|
|
end;
|
|
end;
|
|
|
|
function ASN1EncodeDataInteger24(const A: Int32): RawByteString;
|
|
var D : RawByteString;
|
|
begin
|
|
if (A >= MinInt16) and (A <= MaxInt16) then
|
|
Result := ASN1EncodeDataInteger16(A)
|
|
else
|
|
begin
|
|
SetLength(D, 3);
|
|
Move(A, D[1], 3);
|
|
Result := D;
|
|
end;
|
|
end;
|
|
|
|
function ASN1EncodeDataInteger32(const A: Int32): RawByteString;
|
|
var D : RawByteString;
|
|
begin
|
|
if (A >= MinInt24) and (A <= MaxInt24) then
|
|
Result := ASN1EncodeDataInteger24(A)
|
|
else
|
|
begin
|
|
SetLength(D, 4);
|
|
Move(A, D[1], 4);
|
|
Result := D;
|
|
end;
|
|
end;
|
|
|
|
function ASN1EncodeDataInteger64(const A: Int64): RawByteString;
|
|
var D : RawByteString;
|
|
F : Byte;
|
|
B : array[0..7] of Byte;
|
|
I, L : Integer;
|
|
begin
|
|
Move(A, B[0], 8);
|
|
if B[7] and $80 <> 0 then
|
|
F := $FF
|
|
else
|
|
F := $00;
|
|
L := 0;
|
|
for I := 7 downto 1 do
|
|
if B[I] <> F then
|
|
begin
|
|
L := I;
|
|
break;
|
|
end;
|
|
if ((F = $00) and (B[L] and $80 <> 0)) or
|
|
((F = $FF) and (B[L] and $80 = 0)) then
|
|
Inc(L);
|
|
Inc(L);
|
|
SetLength(D, L);
|
|
Move(B[0], D[1], L);
|
|
Result := D;
|
|
end;
|
|
|
|
function ASN1EncodeInteger8(const A: ShortInt): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_INTEGER, ASN1EncodeDataInteger8(A));
|
|
end;
|
|
|
|
function ASN1EncodeInteger16(const A: SmallInt): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_INTEGER, ASN1EncodeDataInteger16(A));
|
|
end;
|
|
|
|
function ASN1EncodeInteger24(const A: Int32): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_INTEGER, ASN1EncodeDataInteger24(A));
|
|
end;
|
|
|
|
function ASN1EncodeInteger32(const A: Int32): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_INTEGER, ASN1EncodeDataInteger32(A));
|
|
end;
|
|
|
|
function ASN1EncodeInteger64(const A: Int64): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_INTEGER, ASN1EncodeDataInteger64(A));
|
|
end;
|
|
|
|
function ASN1EncodeIntegerBuf(const A; const Size: Integer): RawByteString;
|
|
var D : RawByteString;
|
|
begin
|
|
Assert(Size > 0);
|
|
|
|
SetLength(D, Size);
|
|
Move(A, D[1], Size);
|
|
Result := ASN1EncodeObj(ASN1_ID_INTEGER, D);
|
|
end;
|
|
|
|
function ASN1EncodeIntegerBufStr(const A: RawByteString): RawByteString;
|
|
begin
|
|
Assert(A <> '');
|
|
|
|
Result := ASN1EncodeObj(ASN1_ID_INTEGER, A);
|
|
end;
|
|
|
|
function ASN1EncodeEnumerated(const A: Int64): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_ENUMERATED, ASN1EncodeDataInteger64(A));
|
|
end;
|
|
|
|
function ASN1EncodeBitString(const A: RawByteString; const UnusedBits: Byte): RawByteString;
|
|
begin
|
|
if A = '' then
|
|
Result := ASN1EncodeObj(ASN1_ID_BIT_STRING, AnsiChar(#0))
|
|
else
|
|
Result := ASN1EncodeObj(ASN1_ID_BIT_STRING, AnsiChar(UnusedBits) + A);
|
|
end;
|
|
|
|
function ASN1EncodeOctetString(const A: RawByteString): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_OCTET_STRING, A);
|
|
end;
|
|
|
|
function ASN1EncodeInt32AsOctetString(const A: Int32): RawByteString;
|
|
var S : RawByteString;
|
|
I : Integer;
|
|
F : Int32;
|
|
begin
|
|
SetLength(S, 4);
|
|
F := A;
|
|
for I := 0 to 3 do
|
|
begin
|
|
S[4 - I] := AnsiChar(F mod 256);
|
|
F := F div 256;
|
|
end;
|
|
Result := ASN1EncodeOctetString(S);
|
|
end;
|
|
|
|
function ASN1EncodeUTF8String(const A: RawByteString): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_UTF8STRING, A);
|
|
end;
|
|
|
|
function ASN1EncodeIA5String(const A: RawByteString): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_IA5STRING, A);
|
|
end;
|
|
|
|
function ASN1EncodeVisibleString(const A: RawByteString): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_VISIBLESTRING, A);
|
|
end;
|
|
|
|
function ASN1EncodeNumericString(const A: RawByteString): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_NUMERICSTRING, A);
|
|
end;
|
|
|
|
function ASN1EncodePrintableString(const A: RawByteString): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_PRINTABLESTRING, A);
|
|
end;
|
|
|
|
function ASN1EncodeTeletexString(const A: RawByteString): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_T61STRING, A);
|
|
end;
|
|
|
|
function ASN1EncodeUniversalString(const A: UnicodeString): RawByteString;
|
|
var S : RawByteString;
|
|
begin
|
|
S := UTF16ToEncodingU(TUCS4LECodec, A);
|
|
Result := ASN1EncodeObj(ASN1_ID_UNIVERSALSTRING, S);
|
|
end;
|
|
|
|
function ASN1EncodeBMPString(const A: UnicodeString): RawByteString;
|
|
var S : RawByteString;
|
|
begin
|
|
S := UTF16ToEncodingU(TUCS2Codec, A);
|
|
Result := ASN1EncodeObj(ASN1_ID_UNIVERSALSTRING, S);
|
|
end;
|
|
|
|
function ASN1EncodeUTCTime(const A: TDateTime): RawByteString;
|
|
begin
|
|
{$IFDEF StringIsUnicode}
|
|
Result := ASN1EncodeObj(ASN1_ID_UTCTIME, RawByteString(FormatDateTime('YYMMDDHHNNSS', A)) + 'Z');
|
|
{$ELSE}
|
|
Result := ASN1EncodeObj(ASN1_ID_UTCTIME, FormatDateTime('YYMMDDHHNNSS', A) + 'Z');
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function ASN1EncodeGeneralizedTime(const A: TDateTime): RawByteString;
|
|
begin
|
|
{$IFDEF StringIsUnicode}
|
|
Result := ASN1EncodeObj(ASN1_ID_GENERALIZEDTIME, RawByteString(FormatDateTime('YYYYMMDDHHNNSS', A)) + 'Z');
|
|
{$ELSE}
|
|
Result := ASN1EncodeObj(ASN1_ID_GENERALIZEDTIME, FormatDateTime('YYYYMMDDHHNNSS', A) + 'Z');
|
|
{$ENDIF}
|
|
end;
|
|
|
|
// OID implementation limits
|
|
const
|
|
MAX_BER_OIDPart_Len = 6; // 6 = max 42-bit part, 10 = max 70-bit part
|
|
MAX_BER_OID_Length = 512;
|
|
MAX_OID_Parts = 64;
|
|
|
|
type
|
|
TOIDPartEnc = record
|
|
Len : Byte;
|
|
Enc : array[0..MAX_BER_OIDPart_Len - 1] of Byte;
|
|
end;
|
|
|
|
procedure ASN1BEREncodeOIDPart(const OIDPart: Integer; var Enc: TOIDPartEnc);
|
|
var N, A, B, C : Integer;
|
|
begin
|
|
N := 0;
|
|
A := OIDPart;
|
|
repeat
|
|
B := A div 128;
|
|
C := A mod 128;
|
|
if N > 0 then
|
|
C := C or 128;
|
|
if N >= MAX_BER_OIDPart_Len then
|
|
raise EASN1.Create(SErr_EncodeError);
|
|
Enc.Enc[N] := Byte(C);
|
|
Inc(N);
|
|
A := B;
|
|
until A = 0;
|
|
Enc.Len := N;
|
|
end;
|
|
|
|
// BER Encode OID
|
|
function ASN1EncodeOID(const OID: array of Integer): RawByteString;
|
|
var L, I, N, J, K : Integer;
|
|
A, B, C : Integer;
|
|
E : TOIDPartEnc;
|
|
Buf : array[0..MAX_BER_OID_Length - 1] of Byte;
|
|
S : RawByteString;
|
|
begin
|
|
Result := '';
|
|
L := Length(OID);
|
|
if L < 2 then
|
|
raise EASN1.Create(SErr_EncodeError);
|
|
A := OID[0];
|
|
B := OID[1];
|
|
C := A * 40 + B;
|
|
if C > $FF then
|
|
raise EASN1.Create(SErr_EncodeError);
|
|
N := 1;
|
|
Buf[0] := C;
|
|
for I := 2 to L - 1 do
|
|
begin
|
|
A := OID[I];
|
|
ASN1BEREncodeOIDPart(A, E);
|
|
K := E.Len;
|
|
if N + K - 1 >= MAX_BER_OID_Length then
|
|
raise EASN1.Create(SErr_EncodeError);
|
|
for J := 0 to K - 1 do
|
|
Buf[N + J] := E.Enc[K - J - 1];
|
|
Inc(N, K);
|
|
end;
|
|
SetLength(S, N);
|
|
Move(Buf[0], S[1], N);
|
|
Result := ASN1EncodeObj(ASN1_ID_OBJECT_IDENTIFIER, S);
|
|
end;
|
|
|
|
function ASN1EncodeSequence(const A: RawByteString): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_SEQUENCE, A);
|
|
end;
|
|
|
|
function ASN1EncodeSet(const A: RawByteString): RawByteString;
|
|
begin
|
|
Result := ASN1EncodeObj(ASN1_ID_SET, A);
|
|
end;
|
|
|
|
function ASN1EncodeContextSpecific(const I: Integer; const A: RawByteString): RawByteString;
|
|
begin
|
|
Assert(I >= 0);
|
|
Assert(I <= 31);
|
|
Result := ASN1EncodeObj(ASN1_ID_CONSTR_CONTEXT_SPECIFIC + I, A);
|
|
end;
|
|
|
|
|
|
|
|
{ Decode }
|
|
|
|
function ASN1DecodeLength(const Buf; const Size: Integer; var Len: Integer): Integer;
|
|
var P : PByte;
|
|
L, I : Byte;
|
|
begin
|
|
if Size <= 0 then
|
|
raise EASN1.Create(SErr_DecodeError); // buffer too small
|
|
P := @Buf;
|
|
L := P^;
|
|
if L < $80 then
|
|
begin
|
|
Len := L;
|
|
Result := 1;
|
|
exit;
|
|
end;
|
|
L := L and $7F;
|
|
if Size < L + 1 then
|
|
raise EASN1.Create(SErr_DecodeError); // buffer too small
|
|
if L > 3 then
|
|
raise EASN1.Create(SErr_DecodeError); // size too big
|
|
Len := 0;
|
|
for I := 0 to L - 1 do
|
|
begin
|
|
Inc(P);
|
|
Len := (Len shl 8) or P^;
|
|
end;
|
|
Result := L + 1;
|
|
end;
|
|
|
|
function ASN1DecodeObjHeader(const Buf; const Size: Integer;
|
|
var TypeID: Byte; var Len: Integer; var Data: Pointer): Integer;
|
|
var P : PByte;
|
|
L : Integer;
|
|
begin
|
|
Assert(Assigned(@Buf));
|
|
if Size < 2 then
|
|
raise EASN1.Create(SErr_DecodeError); // buffer too small
|
|
P := @Buf;
|
|
TypeID := P^;
|
|
Inc(P);
|
|
L := ASN1DecodeLength(P^, Size - 1, Len);
|
|
Inc(P, L);
|
|
Data := P;
|
|
Result := L + 1;
|
|
end;
|
|
|
|
function ASN1TypeIsConstructedType(const TypeID: Byte): Boolean;
|
|
begin
|
|
Result := TypeID and ASN1_ID_CONSTRUCTED <> 0;
|
|
end;
|
|
|
|
function ASN1TypeIsContextSpecific(const TypeID: Byte; var Idx: Integer): Boolean;
|
|
begin
|
|
if TypeID and ASN1_ID_CONTEXT_SPECIFIC <> 0 then
|
|
begin
|
|
Idx := TypeID and $1F;
|
|
Result := True;
|
|
end
|
|
else
|
|
begin
|
|
Idx := -1;
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function ASN1DecodeDataBoolean(const Buf; const Size: Integer; var A: Boolean): Integer;
|
|
begin
|
|
if Size <> 1 then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
A := PByte(@Buf)^ <> 0;
|
|
Result := 1;
|
|
end;
|
|
|
|
function ASN1DecodeDataInteger32(const Buf; const Size: Integer; var A: Int32): Integer;
|
|
var P : PByte;
|
|
L, I : Integer;
|
|
B : array[0..3] of Byte;
|
|
E : Byte;
|
|
begin
|
|
P := @Buf;
|
|
L := Size;
|
|
if (L <= 0) or (L > 4) then
|
|
raise EASN1.Create(SErr_DecodeError); // integer too big for Int32
|
|
Move(P^, B[0], L);
|
|
if L < 4 then
|
|
begin
|
|
// extend sign
|
|
if B[L - 1] >= $80 then
|
|
E := $FF
|
|
else
|
|
E := $00;
|
|
for I := L to 3 do
|
|
B[I] := E;
|
|
end;
|
|
Move(B[0], A, 4);
|
|
Result := L;
|
|
end;
|
|
|
|
function ASN1DecodeDataInteger64(const Buf; const Size: Integer; var A: Int64): Integer;
|
|
var P : PByte;
|
|
L, I : Integer;
|
|
B : array[0..7] of Byte;
|
|
E : Byte;
|
|
begin
|
|
P := @Buf;
|
|
L := Size;
|
|
if (L <= 0) or (L > 8) then
|
|
raise EASN1.Create(SErr_DecodeError); // integer too big for Int64
|
|
Move(P^, B[0], L);
|
|
if L < 8 then
|
|
begin
|
|
// extend sign
|
|
if B[L - 1] >= $80 then
|
|
E := $FF
|
|
else
|
|
E := $00;
|
|
for I := L to 7 do
|
|
B[I] := E;
|
|
end;
|
|
Move(B[0], A, 8);
|
|
Result := L;
|
|
end;
|
|
|
|
function ASN1DecodeDataIntegerBuf(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
var L : Integer;
|
|
begin
|
|
L := Size;
|
|
SetLength(A, L);
|
|
if L > 0 then
|
|
Move(Buf, A[1], L);
|
|
Result := L;
|
|
end;
|
|
|
|
function ASN1DecodeDataBitString(const Buf; const Size: Integer; var A: RawByteString; var UnusedBits: Byte): Integer;
|
|
var P : PByte;
|
|
L : Integer;
|
|
F : Byte;
|
|
begin
|
|
P := @Buf;
|
|
L := Size;
|
|
if L <= 0 then
|
|
raise EASN1.Create(SErr_DecodeError); // invalid size
|
|
F := P^;
|
|
if F > 7 then
|
|
raise EASN1.Create(SErr_DecodeError); // invalid UnusedBits
|
|
if L = 1 then
|
|
begin
|
|
if F <> 0 then
|
|
raise EASN1.Create(SErr_DecodeError); // invalid size
|
|
A := '';
|
|
UnusedBits := 0;
|
|
Result := 1;
|
|
exit;
|
|
end;
|
|
Inc(P);
|
|
Dec(L);
|
|
SetLength(A, L);
|
|
if L > 0 then
|
|
Move(P^, A[1], L);
|
|
UnusedBits := F;
|
|
Result := L + 1;
|
|
end;
|
|
|
|
function ASN1DecodeDataRawByteString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
var L : Integer;
|
|
begin
|
|
L := Size;
|
|
SetLength(A, L);
|
|
if L > 0 then
|
|
Move(Buf, A[1], L);
|
|
Result := L;
|
|
end;
|
|
|
|
function ASN1DecodeDataOctetString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
begin
|
|
Result := ASN1DecodeDataRawByteString(Buf, Size, A);
|
|
end;
|
|
|
|
function ASN1DecodeDataIA5String(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
begin
|
|
Result := ASN1DecodeDataRawByteString(Buf, Size, A);
|
|
end;
|
|
|
|
function ASN1DecodeDataVisibleString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
begin
|
|
Result := ASN1DecodeDataRawByteString(Buf, Size, A);
|
|
end;
|
|
|
|
function ASN1DecodeDataNumericString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
begin
|
|
Result := ASN1DecodeDataRawByteString(Buf, Size, A);
|
|
end;
|
|
|
|
function ASN1DecodeDataPrintableString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
begin
|
|
Result := ASN1DecodeDataRawByteString(Buf, Size, A);
|
|
end;
|
|
|
|
function ASN1DecodeDataTeletexString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
begin
|
|
Result := ASN1DecodeDataRawByteString(Buf, Size, A);
|
|
end;
|
|
|
|
function ASN1DecodeDataUTF8String(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
begin
|
|
Result := ASN1DecodeDataRawByteString(Buf, Size, A);
|
|
end;
|
|
|
|
function ASN1DecodeDataUniversalString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
begin
|
|
A := UnicodeStringToUTF8String(EncodingToUTF16U(TUCS4LECodec, @Buf, Size));
|
|
Result := Size;
|
|
end;
|
|
|
|
function ASN1DecodeDataBMPString(const Buf; const Size: Integer; var A: RawByteString): Integer;
|
|
begin
|
|
A := UnicodeStringToUTF8String(EncodingToUTF16U(TUCS2Codec, @Buf, Size));
|
|
Result := Size;
|
|
end;
|
|
|
|
function ASN1DecodeOIDPart(const Buf; const Size: Integer; var Part: Integer): Integer;
|
|
var P : PByte;
|
|
L, C : Integer;
|
|
A : Byte;
|
|
R : Boolean;
|
|
V : Int64;
|
|
begin
|
|
P := @Buf;
|
|
L := Size;
|
|
C := 1;
|
|
V := 0;
|
|
repeat
|
|
if L < 0 then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
if C > MAX_BER_OIDPart_Len then
|
|
raise EASN1.Create(SErr_DecodeError); // too many bytes in this OID part
|
|
A := P^;
|
|
R := A and 128 = 0;
|
|
V := (V shl 7) or (A and 127);
|
|
Inc(P);
|
|
Dec(L);
|
|
Inc(C);
|
|
until R;
|
|
if V > MaxInt32 then
|
|
raise EASN1.Create(SErr_DecodeError); // value too large
|
|
Part := V;
|
|
Result := Size - L;
|
|
end;
|
|
|
|
function ASN1DecodeDataOID(const Buf; const Size: Integer; var A: TASN1ObjectIdentifier): Integer;
|
|
var P : PByte;
|
|
L, N, C, I : Integer;
|
|
F : Byte;
|
|
Parts : array[0..MAX_OID_Parts - 1] of Integer;
|
|
begin
|
|
P := @Buf;
|
|
L := Size;
|
|
if L < 1 then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
F := P^;
|
|
Parts[0] := F div 40;
|
|
Parts[1] := F mod 40;
|
|
Inc(P);
|
|
Dec(L);
|
|
N := 2;
|
|
while L > 0 do
|
|
begin
|
|
if N >= MAX_OID_Parts then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
C := ASN1DecodeOIDPart(P^, L, Parts[N]);
|
|
Inc(P, C);
|
|
Dec(L, C);
|
|
Inc(N);
|
|
end;
|
|
SetLength(A, N);
|
|
for I := 0 to N - 1 do
|
|
A[I] := Parts[I];
|
|
Result := Size - L;
|
|
end;
|
|
|
|
function ASN1DecodeDataUTCTime(const Buf; const Size: Integer; var A: TDateTime): Integer;
|
|
var D : RawByteString;
|
|
YYYY, YY, MM, DD, HH, NN, SS : Integer;
|
|
begin
|
|
if Size < 6 then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
SetLength(D, Size);
|
|
Move(Buf, D[1], Size);
|
|
if not TryStringToIntB(Copy(D, 1, 2), YY) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
if not TryStringToIntB(Copy(D, 3, 2), MM) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
if not TryStringToIntB(Copy(D, 5, 2), DD) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
HH := 0;
|
|
NN := 0;
|
|
SS := 0;
|
|
if Size >= 10 then
|
|
begin
|
|
if not TryStringToIntB(Copy(D, 7, 2), HH) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
if not TryStringToIntB(Copy(D, 9, 2), NN) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
if Size >= 12 then
|
|
if not TryStringToIntB(Copy(D, 11, 2), SS) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
end;
|
|
if YY <= 49 then
|
|
YYYY := 2000 + YY
|
|
else
|
|
YYYY := 1900 + YY;
|
|
A := EncodeDate(YYYY, MM, DD) +
|
|
EncodeTime(HH, NN, SS, 0);
|
|
Result := Size;
|
|
end;
|
|
|
|
function ASN1DecodeDataGeneralizedTime(const Buf; const Size: Integer; var A: TDateTime): Integer;
|
|
var D : RawByteString;
|
|
YYYY, MM, DD, HH, NN, SS : Integer;
|
|
begin
|
|
if Size < 8 then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
SetLength(D, Size);
|
|
Move(Buf, D[1], Size);
|
|
if not TryStringToIntB(Copy(D, 1, 4), YYYY) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
if not TryStringToIntB(Copy(D, 5, 2), MM) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
if not TryStringToIntB(Copy(D, 7, 2), DD) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
HH := 0;
|
|
NN := 0;
|
|
SS := 0;
|
|
if Size >= 12 then
|
|
begin
|
|
if not TryStringToIntB(Copy(D, 9, 2), HH) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
if not TryStringToIntB(Copy(D, 11, 2), NN) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
if Size >= 14 then
|
|
if not TryStringToIntB(Copy(D, 13, 2), SS) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
end;
|
|
A := EncodeDate(YYYY, MM, DD) +
|
|
EncodeTime(HH, NN, SS, 0);
|
|
Result := Size;
|
|
end;
|
|
|
|
function ASN1DecodeBoolean(const TypeID: Byte; const DataBuf; const DataSize: Integer; var A: Boolean): Integer;
|
|
var I : Int64;
|
|
begin
|
|
case TypeID of
|
|
ASN1_ID_BOOLEAN : Result := ASN1DecodeDataBoolean(DataBuf, DataSize, A);
|
|
ASN1_ID_INTEGER :
|
|
begin
|
|
Result := ASN1DecodeDataInteger64(DataBuf, DataSize, I);
|
|
A := I <> 0;
|
|
end;
|
|
else
|
|
raise EASN1.Create(SErr_DecodeInvalidType);
|
|
end;
|
|
end;
|
|
|
|
function ASN1DecodeInteger32(const TypeID: Byte; const DataBuf; const DataSize: Integer;
|
|
var A: Int32): Integer;
|
|
var I : Int64;
|
|
begin
|
|
Result := ASN1DecodeInteger64(TypeID, DataBuf, DataSize, I);
|
|
if (I > High(Int32)) or (I < Low(Int32)) then
|
|
raise EASN1.Create(SErr_DecodeError);
|
|
A := I;
|
|
end;
|
|
|
|
function ASN1DecodeInteger64(const TypeID: Byte; const DataBuf; const DataSize: Integer;
|
|
var A: Int64): Integer;
|
|
var S : RawByteString;
|
|
begin
|
|
case TypeID of
|
|
ASN1_ID_INTEGER : Result := ASN1DecodeDataInteger64(DataBuf, DataSize, A);
|
|
ASN1_ID_OCTET_STRING,
|
|
ASN1_ID_UTF8STRING,
|
|
ASN1_ID_NUMERICSTRING,
|
|
ASN1_ID_PRINTABLESTRING,
|
|
ASN1_ID_VISIBLESTRING,
|
|
ASN1_ID_T61STRING,
|
|
ASN1_ID_UNIVERSALSTRING,
|
|
ASN1_ID_BMPSTRING :
|
|
begin
|
|
Result := ASN1DecodeString(TypeID, DataBuf, DataSize, S);
|
|
if not TryStringToInt64B(S, A) then
|
|
raise EASN1.Create(SErr_DecodeConversionError);
|
|
end;
|
|
else
|
|
raise EASN1.Create(SErr_DecodeInvalidType);
|
|
end;
|
|
end;
|
|
|
|
function ASN1DecodeIntegerBuf(const TypeID: Byte; const DataBuf; const DataSize: Integer;
|
|
var A: RawByteString): Integer;
|
|
begin
|
|
case TypeID of
|
|
ASN1_ID_INTEGER : Result := ASN1DecodeDataIntegerBuf(DataBuf, DataSize, A);
|
|
else
|
|
raise EASN1.Create(SErr_DecodeInvalidType);
|
|
end;
|
|
end;
|
|
|
|
function ASN1DecodeBitString(const TypeID: Byte; const DataBuf; const DataSize: Integer; var A: RawByteString; var UnusedBits: Byte): Integer;
|
|
begin
|
|
case TypeID of
|
|
ASN1_ID_BIT_STRING : Result := ASN1DecodeDataBitString(DataBuf, DataSize, A, UnusedBits);
|
|
else
|
|
raise EASN1.Create(SErr_DecodeInvalidType);
|
|
end;
|
|
end;
|
|
|
|
type
|
|
TStringParseData = record
|
|
Delim : RawByteString;
|
|
Str : RawByteString;
|
|
end;
|
|
PStringParseData = ^TStringParseData;
|
|
|
|
procedure StringParseProc(const TypeID: Byte; const DataBuf; const DataSize: Integer;
|
|
const ObjectIdx: Integer; const CallerData: NativeInt);
|
|
var S : RawByteString;
|
|
D : PStringParseData;
|
|
begin
|
|
ASN1DecodeString(TypeID, DataBuf, DataSize, S);
|
|
D := Pointer(CallerData);
|
|
if (ObjectIdx > 0) and (D^.Delim <> '') then
|
|
D^.Str := D^.Str + D^.Delim;
|
|
D^.Str := D^.Str + S;
|
|
end;
|
|
|
|
function ASN1DecodeString(const TypeID: Byte; const DataBuf; const DataSize: Integer;
|
|
var A: RawByteString): Integer;
|
|
var I : Int64;
|
|
B : TASN1ObjectIdentifier;
|
|
D : TStringParseData;
|
|
K : Byte;
|
|
begin
|
|
if ASN1TypeIsConstructedType(TypeID) then
|
|
begin
|
|
D.Delim := ' ';
|
|
Result := ASN1Parse(DataBuf, DataSize, StringParseProc, NativeInt(@D));
|
|
A := D.Str;
|
|
end
|
|
else
|
|
case TypeID of
|
|
ASN1_ID_OCTET_STRING : Result := ASN1DecodeDataOctetString(DataBuf, DataSize, A);
|
|
ASN1_ID_UTF8STRING : Result := ASN1DecodeDataUTF8String(DataBuf, DataSize, A);
|
|
ASN1_ID_NUMERICSTRING : Result := ASN1DecodeDataNumericString(DataBuf, DataSize, A);
|
|
ASN1_ID_PRINTABLESTRING : Result := ASN1DecodeDataPrintableString(DataBuf, DataSize, A);
|
|
ASN1_ID_VISIBLESTRING : Result := ASN1DecodeDataVisibleString(DataBuf, DataSize, A);
|
|
ASN1_ID_T61STRING : Result := ASN1DecodeDataTeletexString(DataBuf, DataSize, A);
|
|
ASN1_ID_UNIVERSALSTRING : Result := ASN1DecodeDataUniversalString(DataBuf, DataSize, A);
|
|
ASN1_ID_BMPSTRING : Result := ASN1DecodeDataBMPString(DataBuf, DataSize, A);
|
|
ASN1_ID_BIT_STRING : Result := ASN1DecodeDataBitString(DataBuf, DataSize, A, K);
|
|
ASN1_ID_IA5STRING : Result := ASN1DecodeDataIA5String(DataBuf, DataSize, A);
|
|
ASN1_ID_INTEGER :
|
|
begin
|
|
Result := ASN1DecodeDataInteger64(DataBuf, DataSize, I);
|
|
A := IntToStringB(I);
|
|
end;
|
|
ASN1_ID_OBJECT_IDENTIFIER :
|
|
begin
|
|
Result := ASN1DecodeDataOID(DataBuf, DataSize, B);
|
|
A := ASN1OIDToStrB(B);
|
|
end;
|
|
else
|
|
raise EASN1.Create(SErr_DecodeInvalidType);
|
|
end;
|
|
end;
|
|
|
|
function ASN1DecodeOID(const TypeID: Byte; const DataBuf; const DataSize: Integer;
|
|
var A: TASN1ObjectIdentifier): Integer;
|
|
begin
|
|
case TypeID of
|
|
ASN1_ID_OBJECT_IDENTIFIER : Result := ASN1DecodeDataOID(DataBuf, DataSize, A);
|
|
else
|
|
raise EASN1.Create(SErr_DecodeInvalidType);
|
|
end;
|
|
end;
|
|
|
|
function ASN1DecodeTime(const TypeID: Byte; const DataBuf; const DataSize: Integer; var A: TDateTime): Integer;
|
|
begin
|
|
case TypeID of
|
|
ASN1_ID_UTCTIME : Result := ASN1DecodeDataUTCTime(DataBuf, DataSize, A);
|
|
ASN1_ID_GENERALIZEDTIME : Result := ASN1DecodeDataGeneralizedTime(DataBuf, DataSize, A);
|
|
else
|
|
raise EASN1.Create(SErr_DecodeInvalidType);
|
|
end;
|
|
end;
|
|
|
|
function ASN1Parse(const Buf; const Size: Integer; const ParseProc: TASN1ParseProc; const CallerData: NativeInt): Integer;
|
|
var P : PByte;
|
|
L, N, T : Integer;
|
|
ObjIdx : Integer;
|
|
TypeID : Byte;
|
|
Len : Integer;
|
|
Data : Pointer;
|
|
begin
|
|
P := @Buf;
|
|
L := Size;
|
|
if (L < 0) or not Assigned(P) then
|
|
raise EASN1.Create(SErr_DecodeError); // invalid buffer
|
|
if L = 0 then
|
|
begin
|
|
// nothing to parse
|
|
Result := 0;
|
|
exit;
|
|
end;
|
|
// iterate top level ASN.1 objects in buffer
|
|
ObjIdx := 0;
|
|
while L > 0 do
|
|
begin
|
|
N := ASN1DecodeObjHeader(P^, L, TypeID, Len, Data);
|
|
T := N + Len;
|
|
if T > L then
|
|
raise EASN1.Create(SErr_DecodeError); // invalid length encoded in header
|
|
ParseProc(TypeID, Data^, Len, ObjIdx, CallerData);
|
|
Inc(P, T);
|
|
Dec(L, T);
|
|
Inc(ObjIdx);
|
|
end;
|
|
Result := Size - L;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Test }
|
|
{ }
|
|
{$IFDEF ASN1_TEST}
|
|
{$ASSERTIONS ON}
|
|
procedure TestParseProc(
|
|
const TypeID: Byte; const DataBuf; const DataSize: Integer;
|
|
const ObjectIdx: Integer; const CallerData: NativeInt);
|
|
var I : Int64;
|
|
S : RawByteString;
|
|
begin
|
|
case CallerData of
|
|
0 : case ObjectIdx of
|
|
0 : begin
|
|
Assert(TypeID = ASN1_ID_SEQUENCE);
|
|
Assert(ASN1Parse(DataBuf, DataSize, TestParseProc, 1) = DataSize);
|
|
end;
|
|
else
|
|
Assert(False);
|
|
end;
|
|
1 : case ObjectIdx of
|
|
0 : begin
|
|
Assert(TypeID = ASN1_ID_INTEGER);
|
|
ASN1DecodeInteger64(TypeID, DataBuf, DataSize, I);
|
|
Assert(I = 123);
|
|
end;
|
|
1 : begin
|
|
Assert(TypeID = ASN1_ID_PRINTABLESTRING);
|
|
ASN1DecodeString(TypeID, DataBuf, DataSize, S);
|
|
Assert(S = 'ABC');
|
|
end;
|
|
else
|
|
Assert(False);
|
|
end;
|
|
else
|
|
Assert(False);
|
|
end;
|
|
end;
|
|
|
|
procedure Test;
|
|
var S : RawByteString;
|
|
L, I, J : Integer;
|
|
D : TASN1ObjectIdentifier;
|
|
begin
|
|
Assert(ASN1EncodeLength(0) = #$00);
|
|
Assert(ASN1EncodeLength(1) = #$01);
|
|
Assert(ASN1EncodeLength($7F) = #$7F);
|
|
Assert(ASN1EncodeLength($80) = #$81#$80);
|
|
Assert(ASN1EncodeLength($FF) = #$81#$FF);
|
|
Assert(ASN1EncodeLength($100) = #$82#$01#$00);
|
|
|
|
Assert(ASN1EncodeOID(OID_3DES_wrap) = #$06#$0b#$2a#$86#$48#$86#$f7#$0d#$01#$09#$10#$03#$06);
|
|
Assert(ASN1EncodeOID(OID_RC2_wrap) = #$06#$0b#$2a#$86#$48#$86#$f7#$0d#$01#$09#$10#$03#$07);
|
|
|
|
S := RawByteString(#$2a#$86#$48#$86#$f7#$0d#$01#$09#$10#$03#$06);
|
|
L := Length(S);
|
|
Assert(ASN1DecodeDataOID(S[1], L, D) = L);
|
|
Assert(Length(D) = 9);
|
|
Assert((D[0] = 1) and (D[1] = 2) and (D[2] = 840) and (D[3] = 113549) and
|
|
(D[4] = 1) and (D[5] = 9) and (D[6] = 16) and (D[7] = 3) and (D[8] = 6));
|
|
Assert(ASN1OIDToStrB(D) = '1.2.840.113549.1.9.16.3.6');
|
|
|
|
S := RawByteString(#$2a#$86#$48#$86#$f7#$0d#$03#$06);
|
|
L := Length(S);
|
|
Assert(ASN1DecodeDataOID(S[1], L, D) = L);
|
|
Assert(Length(D) = 6);
|
|
Assert((D[0] = 1) and (D[1] = 2) and (D[2] = 840) and (D[3] = 113549) and
|
|
(D[4] = 3) and (D[5] = 6));
|
|
|
|
Assert(ASN1EncodeInteger32(0) = #$02#$01#$00);
|
|
Assert(ASN1EncodeInteger32(1) = #$02#$01#$01);
|
|
Assert(ASN1EncodeInteger32(-1) = #$02#$01#$FF);
|
|
Assert(ASN1EncodeInteger32(-$80) = #$02#$01#$80);
|
|
Assert(ASN1EncodeInteger32(-$81) = #$02#$02#$7F#$FF);
|
|
Assert(ASN1EncodeInteger32(-$FF) = #$02#$02#$01#$FF);
|
|
Assert(ASN1EncodeInteger32($7F) = #$02#$01#$7F);
|
|
Assert(ASN1EncodeInteger32($80) = #$02#$02#$80#$00);
|
|
Assert(ASN1EncodeInteger32($FF) = #$02#$02#$FF#$00);
|
|
|
|
for I := -512 to 512 do
|
|
begin
|
|
S := ASN1EncodeInteger32(I);
|
|
Assert(S = ASN1EncodeInteger64(I));
|
|
L := Length(S);
|
|
Assert(ASN1DecodeDataInteger32(S[3], L - 2, J) = L - 2);
|
|
Assert(J = I);
|
|
end;
|
|
|
|
S :=
|
|
ASN1EncodeSequence(
|
|
ASN1EncodeInteger32(123) +
|
|
ASN1EncodePrintableString('ABC')
|
|
);
|
|
L := Length(S);
|
|
Assert(L > 0);
|
|
Assert(ASN1Parse(S[1], L, @TestParseProc, 0) = L);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
end.
|
|
|