Skip to content

Commit

Permalink
Thanks!
Browse files Browse the repository at this point in the history
Merge branch 'Ephenodrom-master' into master
  • Loading branch information
mwcw committed Sep 11, 2020
2 parents 0d595c6 + 865fde4 commit 167aa6a
Show file tree
Hide file tree
Showing 39 changed files with 2,898 additions and 89 deletions.
22 changes: 22 additions & 0 deletions lib/asn1.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
library asn1;

export 'asn1/asn1_encoding_rule.dart';
export 'asn1/asn1_object.dart';
export 'asn1/asn1_parser.dart';
export 'asn1/asn1_tags.dart';
export 'asn1/asn1_utils.dart';
export 'asn1/primitives/asn1_bit_string.dart';
export 'asn1/primitives/asn1_boolean.dart';
export 'asn1/primitives/asn1_enumerated.dart';
export 'asn1/primitives/asn1_ia5_string.dart';
export 'asn1/primitives/asn1_integer.dart';
export 'asn1/primitives/asn1_null.dart';
export 'asn1/primitives/asn1_object_identifier.dart';
export 'asn1/primitives/asn1_octet_string.dart';
export 'asn1/primitives/asn1_printable_string.dart';
export 'asn1/primitives/asn1_sequence.dart';
export 'asn1/primitives/asn1_set.dart';
export 'asn1/primitives/asn1_utc_time.dart';
export 'asn1/primitives/asn1_utf8_string.dart';
export 'asn1/unsupported_asn1_encoding_rule_exception.dart';
export 'asn1/unsupported_asn1_tag_exception.dart';
16 changes: 16 additions & 0 deletions lib/asn1/asn1_encoding_rule.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
enum ASN1EncodingRule {
/// Normal DER encoding rules
ENCODING_DER,

/// BER encoding where the length is described in a long form
ENCODING_BER_LONG_LENGTH_FORM,

/// BER Constructed encoding with definite length
ENCODING_BER_CONSTRUCTED,

/// BER encoding with padded bits to make the length of the value bytes a multiple of eight. Only used for ASN1BitString
ENCODING_BER_PADDED,

/// BER Constructed encoding with indefinite length
ENCODING_BER_CONSTRUCTED_INDEFINITE_LENGTH
}
45 changes: 42 additions & 3 deletions lib/asn1/asn1_object.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:typed_data';

import 'package:pointycastle/asn1/asn1_encoding_rule.dart';
import 'package:pointycastle/asn1/asn1_utils.dart';

///
Expand Down Expand Up @@ -35,7 +36,18 @@ class ASN1Object {
///
int valueByteLength;

ASN1Object({this.tag});
///
/// 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;

ASN1Object({this.tag}) {
if (tag != null) {
isConstructed = ASN1Utils.isConstructed(tag);
}
}

///
/// Creates a new ASN1Object from the given [encodedBytes].
Expand All @@ -44,23 +56,45 @@ class ASN1Object {
///
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() {
Uint8List encode(
{ASN1EncodingRule encodingRule = ASN1EncodingRule.ENCODING_DER}) {
if (encodedBytes == null) {
// Encode the length
Uint8List lengthAsBytes;
valueByteLength ??= valueBytes.length;
lengthAsBytes = ASN1Utils.encodeLength(valueByteLength);
// 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
Expand All @@ -73,4 +107,9 @@ class ASN1Object {
}
return encodedBytes;
}

///
/// The total length of this object, including its value bytes, the encoded tag and length bytes.
///
int get totalEncodedByteLength => valueStartPosition + valueByteLength;
}
138 changes: 138 additions & 0 deletions lib/asn1/asn1_parser.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
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_boolean.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_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 curren 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));
if (_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);
ASN1Object obj;
if (isConstructed) {
obj = _createConstructed(tag, subBytes);
} else {
obj = _createPrimitive(tag, subBytes);
}

// Update the position
_position = _position + obj.totalEncodedByteLength;
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 0xA0:
case 0xA1:
case 0xA2:
case 0xA3:
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);
default:
throw UnsupportedASN1TagException(tag);
}
}
}
18 changes: 18 additions & 0 deletions lib/asn1/asn1_tags.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@ class ASN1Tags {
/// Decimal 3
static const int BIT_STRING = 0x03;

/// Decimal 35
static const int BIT_STRING_CONSTRUCTED = 0x23;

/// Decimal 4
static const int OCTET_STRING = 0x04;

/// Decimal 36
static const int OCTET_STRING_CONSTRUCTED = 0x24;

/// Decimal 5
static const int NULL = 0x05;

Expand All @@ -29,6 +35,9 @@ class ASN1Tags {
/// Decimal 12
static const int UTF8_STRING = 0x0c;

/// Decimal 44
static const int UTF8_STRING_CONSTRUCTED = 0x2C;

/// Decimal 48
static const int SEQUENCE = 0x30;

Expand All @@ -47,15 +56,24 @@ class ASN1Tags {
/// Decimal 19
static const int PRINTABLE_STRING = 0x13;

/// Decimal 51
static const int PRINTABLE_STRING_CONSTRUCTED = 0x33;

/// Decimal 20
static const int T61_STRING = 0x14;

/// Decimal 52
static const int T61_STRING_CONSTRUCTED = 0x34;

/// Decimal 21
static const int VIDEOTEX_STRING = 0x15;

/// Decimal 22
static const int IA5_STRING = 0x16;

/// Decimal 54
static const int IA5_STRING_CONSTRUCTED = 0x36;

/// Decimal 23
static const int UTC_TIME = 0x17;

Expand Down
39 changes: 37 additions & 2 deletions lib/asn1/asn1_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ class ASN1Utils {
///
/// Encode the given [length] to byte representation.
///
static Uint8List encodeLength(int length) {
static Uint8List encodeLength(int length, {bool longform = false}) {
Uint8List e;
if (length <= 127) {
if (length <= 127 && longform == false) {
e = Uint8List(1);
e[0] = length;
} else {
Expand All @@ -73,4 +73,39 @@ class ASN1Utils {
}
return e;
}

///
/// Checks if the given int [i] is constructed according to <https://www.bouncycastle.org/asn1_layman_93.txt> section 3.2.
///
/// The Identifier octets (represented by the given [i]) is marked as constructed if bit 6 has the value **1**.
///
/// Example with the IA5 String tag:
///
/// 0x36 = 0 0 1 1 0 1 1 0
///
/// 0x16 = 0 0 0 1 0 1 1 0
/// ```
/// ASN1Utils.isConstructed(0x36); // true
/// ASN1Utils.isConstructed(0x16); // false
/// ```
///
///
static bool isConstructed(int i) {
// Shift bits
var newNum = i >> (6 - 1);
// Check if bit is set to 1
return (newNum & 1) == 1;
}

///
/// Checks if the given [bytes] ends with 0x00, 0x00
///
static bool hasIndefiniteLengthEnding(Uint8List bytes) {
var last = bytes.elementAt(bytes.length - 1);
var lastMinus1 = bytes.elementAt(bytes.length - 2);
if (last == 0 && lastMinus1 == 0) {
return true;
}
return false;
}
}
Loading

0 comments on commit 167aa6a

Please sign in to comment.