blob: 214c8b2972f8de526a3756072e15360a5396bb7b [file]
import 'dart:typed_data';
import 'package:pointycastle/asn1/asn1_tags.dart';
///
/// Utils class holding different methods to ease the handling of ANS1Objects and their byte representation.
///
class ASN1Utils {
///
/// Calculates the start position of the value bytes for the given [encodedBytes].
///
/// It will return 2 if the **length byte** is less than or equal 127 or the length calculate on the **length byte** value.
/// This will throw a [RangeError] if the given [encodedBytes] has length < 2.
///
static int calculateValueStartPosition(Uint8List encodedBytes) {
// TODO tag length can be >1
var length = encodedBytes[1];
if (length <= 0x7F) {
return 2;
} else {
return 2 + (length & 0x7F);
}
}
///
/// Calculates the length of the **value bytes** for the given [encodedBytes].
///
/// Will return **-1** if the length byte equals **0x80**. Throws an [ArgumentError] if the length could not be calculated for the given [encodedBytes].
///
static int decodeLength(Uint8List encodedBytes) {
var valueStartPosition = 2;
var length = encodedBytes[1];
if (length <= 0x7F) {
return length;
}
if (length == 0x80) {
return -1;
}
if (length > 127) {
var length = encodedBytes[1] & 0x7F;
var numLengthBytes = length;
length = 0;
for (var i = 0; i < numLengthBytes; i++) {
length <<= 8;
length |= (encodedBytes[valueStartPosition++] & 0xFF);
}
return length;
}
throw ArgumentError('Could not calculate the length from the given bytes.');
}
///
/// Encode the given [length] to byte representation.
///
static Uint8List encodeLength(int length, {bool longform = false}) {
Uint8List e;
if (length <= 127 && longform == false) {
e = Uint8List(1);
e[0] = length;
} else {
var x = Uint32List(1);
x[0] = length;
var y = Uint8List.view(x.buffer);
// Skip null bytes
var num = 3;
while (y[num] == 0) {
--num;
}
e = Uint8List(num + 2);
e[0] = 0x80 + num + 1;
for (var i = 1; i < e.length; ++i) {
e[i] = y[num--];
}
}
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;
}
static bool isASN1Tag(int i) {
return ASN1Tags.TAGS.contains(i);
}
///
/// 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;
}
}