142 lines
4.7 KiB
Dart
142 lines
4.7 KiB
Dart
import 'dart:typed_data';
|
|
|
|
import 'package:pointycastle/asn1/asn1_parser.dart';
|
|
import 'package:pointycastle/asn1/asn1_encoding_rule.dart';
|
|
import 'package:pointycastle/asn1/asn1_utils.dart';
|
|
|
|
///
|
|
/// Base model for all ASN1Objects
|
|
///
|
|
class ASN1Object {
|
|
///
|
|
/// The BER tag representing this object.
|
|
///
|
|
/// For a list of all supported BER tags take a look in the **Asn1Tags** class.
|
|
///
|
|
int? tag;
|
|
|
|
///
|
|
/// The encoded bytes.
|
|
///
|
|
Uint8List? encodedBytes;
|
|
|
|
///
|
|
/// The value bytes.
|
|
///
|
|
Uint8List? valueBytes;
|
|
|
|
///
|
|
/// The index where the value bytes start. This is the position after the tag + length bytes.
|
|
///
|
|
/// The default value for this field is 2. If the length byte is larger than **127**, the value of this field will increase depending on the length bytes.
|
|
///
|
|
int valueStartPosition = 2;
|
|
|
|
///
|
|
/// Length of the encoded value bytes.
|
|
///
|
|
int? valueByteLength;
|
|
|
|
///
|
|
/// Describes if this ASN1 Object is constructed.
|
|
///
|
|
/// The object is marked as constructed if bit 6 of the [tag] field has value **1**
|
|
///
|
|
bool? isConstructed;
|
|
|
|
int dumpIndent = 2;
|
|
|
|
ASN1Object({this.tag}) {
|
|
if (tag != null) {
|
|
isConstructed = ASN1Utils.isConstructed(tag!);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Creates a new ASN1Object from the given [encodedBytes].
|
|
///
|
|
/// The first byte will be used as the [tag].The field [valueStartPosition] and [valueByteLength] will be calculated on the given [encodedBytes].
|
|
///
|
|
ASN1Object.fromBytes(this.encodedBytes) {
|
|
tag = encodedBytes![0];
|
|
isConstructed = ASN1Utils.isConstructed(tag!);
|
|
valueByteLength = ASN1Utils.decodeLength(encodedBytes!);
|
|
valueStartPosition = ASN1Utils.calculateValueStartPosition(encodedBytes!);
|
|
if (valueByteLength == -1) {
|
|
// Indefinite length, check the last to bytes
|
|
if (ASN1Utils.hasIndefiniteLengthEnding(encodedBytes!)) {
|
|
valueByteLength = encodedBytes!.length - 4;
|
|
}
|
|
}
|
|
valueBytes = Uint8List.view(encodedBytes!.buffer,
|
|
valueStartPosition + encodedBytes!.offsetInBytes, valueByteLength);
|
|
}
|
|
|
|
///
|
|
/// Encode the object to their byte representation.
|
|
///
|
|
/// [encodingRule] defines if the [valueByteLength] should be encoded as indefinite length (0x80) or fixed length with short/long form.
|
|
/// The default is [ASN1EncodingRule.ENCODING_DER] which will automatically decode in definite length with short form.
|
|
///
|
|
/// **Important note**: Subclasses need to override this method and may call this method. If this method is called by a subclass, the subclass has to set the [valueBytes] before calling super.encode().
|
|
///
|
|
Uint8List encode(
|
|
{ASN1EncodingRule encodingRule = ASN1EncodingRule.ENCODING_DER}) {
|
|
if (encodedBytes == null) {
|
|
// Encode the length
|
|
Uint8List lengthAsBytes;
|
|
valueByteLength ??= valueBytes!.length;
|
|
// Check if we have indefinite length or fixed length (short or longform)
|
|
if (encodingRule ==
|
|
ASN1EncodingRule.ENCODING_BER_CONSTRUCTED_INDEFINITE_LENGTH) {
|
|
// Set length to 0x80
|
|
lengthAsBytes = Uint8List.fromList([0x80]);
|
|
// Add 2 to the valueByteLength to handle the 0x00, 0x00 at the end
|
|
//valueByteLength = valueByteLength + 2;
|
|
} else {
|
|
lengthAsBytes = ASN1Utils.encodeLength(valueByteLength!,
|
|
longform:
|
|
encodingRule == ASN1EncodingRule.ENCODING_BER_LONG_LENGTH_FORM);
|
|
}
|
|
// Create the Uint8List with the calculated length
|
|
encodedBytes = Uint8List(1 + lengthAsBytes.length + valueByteLength!);
|
|
// Set the tag
|
|
encodedBytes![0] = tag!;
|
|
// Set the length bytes
|
|
encodedBytes!.setRange(1, 1 + lengthAsBytes.length, lengthAsBytes, 0);
|
|
// Set the value bytes
|
|
encodedBytes!.setRange(
|
|
1 + lengthAsBytes.length, encodedBytes!.length, valueBytes!, 0);
|
|
}
|
|
return encodedBytes!;
|
|
}
|
|
|
|
///
|
|
/// The total length of this object, including its value bytes, the encoded tag and length bytes.
|
|
///
|
|
int get totalEncodedByteLength => valueStartPosition + valueByteLength!;
|
|
|
|
///
|
|
/// Creates a readable dump from the current ASN1Object.
|
|
///
|
|
/// **Important note**: Subclasses need to override this method. If the ASN1Object is constructed and has child elements, dump() has to be called for each child element.
|
|
///
|
|
String dump({int spaces = 0}) {
|
|
var sb = StringBuffer();
|
|
for (var i = 0; i < spaces; i++) {
|
|
sb.write(' ');
|
|
}
|
|
if (tag == 0xA0 || tag == 0xA3) {
|
|
sb.write('[$tag]');
|
|
var parser = ASN1Parser(valueBytes);
|
|
if (parser.hasNext()) {
|
|
var next = parser.nextObject();
|
|
var dump = next.dump(spaces: spaces + dumpIndent);
|
|
sb.write('\n$dump');
|
|
} else {
|
|
sb.write(' (0 elem)');
|
|
}
|
|
}
|
|
return sb.toString();
|
|
}
|
|
}
|