163 lines
5.4 KiB
Dart
163 lines
5.4 KiB
Dart
import 'dart:typed_data';
|
|
|
|
import 'package:pointycastle/asn1/asn1_object.dart';
|
|
import 'package:pointycastle/asn1/asn1_tags.dart';
|
|
import 'package:pointycastle/asn1/asn1_utils.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_bit_string.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_bmp_string.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_boolean.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_generalized_time.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_ia5_string.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_integer.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_null.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_object_identifier.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_octet_string.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_printable_string.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_sequence.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_set.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_teletext_string.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_utc_time.dart';
|
|
import 'package:pointycastle/asn1/primitives/asn1_utf8_string.dart';
|
|
import 'package:pointycastle/asn1/unsupported_asn1_tag_exception.dart';
|
|
|
|
///
|
|
/// The ASN1Parser to parse bytes into ASN1 Objects
|
|
///
|
|
class ASN1Parser {
|
|
///
|
|
/// The bytes to parse
|
|
///
|
|
final Uint8List? bytes;
|
|
|
|
///
|
|
/// The current position in the byte array.
|
|
///
|
|
/// The inital value is 0.
|
|
///
|
|
int _position = 0;
|
|
|
|
ASN1Parser(this.bytes);
|
|
|
|
///
|
|
/// Returns true if there is still an object to parse. Otherwise false.
|
|
///
|
|
bool hasNext() {
|
|
return _position < bytes!.length;
|
|
}
|
|
|
|
///
|
|
/// Parses the next object in the [bytes].
|
|
///
|
|
ASN1Object nextObject() {
|
|
// Get the current tag in the list bytes
|
|
var tag = bytes![_position];
|
|
|
|
// Get the length of the value bytes for the current object
|
|
var length = ASN1Utils.decodeLength(bytes!.sublist(_position));
|
|
|
|
var valueStartPosition =
|
|
ASN1Utils.calculateValueStartPosition(bytes!.sublist(_position));
|
|
|
|
var isIndefiniteLength = false;
|
|
|
|
if (length == -1) {
|
|
length = ASN1Utils.calculateIndefiniteLength(bytes!, _position) + 2;
|
|
isIndefiniteLength = true;
|
|
} else if (bytes!.length - _position > length + valueStartPosition) {
|
|
length = length + valueStartPosition;
|
|
} else {
|
|
length = bytes!.length - _position;
|
|
}
|
|
|
|
// Create new view from the bytes
|
|
var offset = _position + bytes!.offsetInBytes;
|
|
var subBytes = Uint8List.view(bytes!.buffer, offset, length);
|
|
|
|
// Parse the view and the tag to an ASN1Object
|
|
var isConstructed = ASN1Utils.isConstructed(tag);
|
|
var isPrimitive = (0xC0 & tag) == 0;
|
|
//var isApplication = (0x40 & tag) != 0;
|
|
|
|
ASN1Object obj;
|
|
if (isConstructed) {
|
|
obj = _createConstructed(tag, subBytes);
|
|
} else if (isPrimitive) {
|
|
obj = _createPrimitive(tag, subBytes);
|
|
} else {
|
|
// create a vanilla object
|
|
obj = ASN1Object.fromBytes(subBytes);
|
|
}
|
|
|
|
// Update the position
|
|
_position =
|
|
_position + obj.totalEncodedByteLength + (isIndefiniteLength ? 2 : 0);
|
|
return obj;
|
|
}
|
|
|
|
///
|
|
/// Creates a constructed ASN1Object depending on the given [tag] and [bytes]
|
|
///
|
|
ASN1Object _createConstructed(int tag, Uint8List bytes) {
|
|
switch (tag) {
|
|
case ASN1Tags.SEQUENCE: // sequence
|
|
return ASN1Sequence.fromBytes(bytes);
|
|
case ASN1Tags.SET:
|
|
return ASN1Set.fromBytes(bytes);
|
|
case ASN1Tags.IA5_STRING_CONSTRUCTED:
|
|
return ASN1IA5String.fromBytes(bytes);
|
|
case ASN1Tags.BIT_STRING_CONSTRUCTED:
|
|
return ASN1BitString.fromBytes(bytes);
|
|
case ASN1Tags.OCTET_STRING_CONSTRUCTED:
|
|
return ASN1OctetString.fromBytes(bytes);
|
|
case ASN1Tags.PRINTABLE_STRING_CONSTRUCTED:
|
|
return ASN1PrintableString.fromBytes(bytes);
|
|
case ASN1Tags.T61_STRING_CONSTRUCTED:
|
|
return ASN1TeletextString.fromBytes(bytes);
|
|
case 0xA0:
|
|
case 0xA1:
|
|
case 0xA2:
|
|
case 0xA3:
|
|
case 0xA4:
|
|
return ASN1Object.fromBytes(bytes);
|
|
default:
|
|
throw UnsupportedASN1TagException(tag);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Creates a primitive ASN1Object depending on the given [tag] and [bytes]
|
|
///
|
|
ASN1Object _createPrimitive(int tag, Uint8List bytes) {
|
|
switch (tag) {
|
|
case ASN1Tags.OCTET_STRING:
|
|
return ASN1OctetString.fromBytes(bytes);
|
|
case ASN1Tags.UTF8_STRING:
|
|
return ASN1UTF8String.fromBytes(bytes);
|
|
case ASN1Tags.IA5_STRING:
|
|
return ASN1IA5String.fromBytes(bytes);
|
|
case ASN1Tags.INTEGER:
|
|
case ASN1Tags.ENUMERATED:
|
|
return ASN1Integer.fromBytes(bytes);
|
|
case ASN1Tags.BOOLEAN:
|
|
return ASN1Boolean.fromBytes(bytes);
|
|
case ASN1Tags.OBJECT_IDENTIFIER:
|
|
return ASN1ObjectIdentifier.fromBytes(bytes);
|
|
case ASN1Tags.BIT_STRING:
|
|
return ASN1BitString.fromBytes(bytes);
|
|
case ASN1Tags.NULL:
|
|
return ASN1Null.fromBytes(bytes);
|
|
case ASN1Tags.PRINTABLE_STRING:
|
|
return ASN1PrintableString.fromBytes(bytes);
|
|
case ASN1Tags.UTC_TIME:
|
|
return ASN1UtcTime.fromBytes(bytes);
|
|
case ASN1Tags.T61_STRING:
|
|
return ASN1TeletextString.fromBytes(bytes);
|
|
case ASN1Tags.GENERALIZED_TIME:
|
|
return ASN1GeneralizedTime.fromBytes(bytes);
|
|
case ASN1Tags.BMP_STRING:
|
|
return ASN1BMPString.fromBytes(bytes);
|
|
default:
|
|
throw UnsupportedASN1TagException(tag);
|
|
}
|
|
}
|
|
}
|