Files
proxypin/lib/network/util/cert/der.dart
2024-10-25 17:57:20 +08:00

435 lines
11 KiB
Dart

import 'dart:typed_data';
import 'dart:convert';
import 'package:proxypin/network/util/byte_buf.dart';
import 'package:pointycastle/asn1.dart';
import 'package:pointycastle/src//utils.dart';
class DerValue {
/// Tag value indicating an ASN.1 "INTEGER" value.
static const int tagInteger = 0x02;
/// Tag value indicating an ASN.1 "OCTET STRING" value.
static const int tagOctetString = 0x04;
int tag;
final Uint8List value;
final ByteBuf buffer;
late DerInputStream data;
DerValue(this.tag, this.value, {ByteBuf? buffer}) : buffer = buffer ?? ByteBuf(value) {
data = DerInputStream(this.buffer);
}
factory DerValue.fromBytes(Uint8List bytes) {
return DerValue.getDerValue(ByteBuf(bytes));
}
factory DerValue.getDerValue(ByteBuf inStream) {
var tag = inStream.read();
int length = DerInputStream.getLength(inStream);
var buffer = inStream.dup();
buffer.truncate(length);
var value = inStream.readBytes(length);
return DerValue(tag, value, buffer: buffer);
}
Uint8List toByteArray() {
DerOutputStream out = DerOutputStream();
encode(out);
return out.toByteArray();
}
void encode(DerOutputStream out) {
out.writeByte(tag);
out.writeLength(value.length);
out.writeBytes(value);
}
/// Returns true iff the CONSTRUCTED bit is set in the type tag.
bool isConstructed() {
return ((tag & 0x020) == 0x020);
}
bool isConstructedTag(int constructedTag) {
if (!isConstructed()) {
return false;
}
return ((tag & 0x01f) == constructedTag);
}
Uint8List getOctetString() {
if (tag != tagOctetString && !isConstructedTag(tagOctetString)) {
throw Exception("DerValue.getOctetString, not an Octet String: $tag");
}
if (isConstructed()) {
while (data.buffer.isReadable()) {
return data.getOctetString();
}
}
return value;
}
//get oid
ASN1ObjectIdentifier getOID() {
if (tag != 0x06) {
throw Exception('DER input, Object Identifier tag error');
}
int length = value.length;
int first = value[0] ~/ 40;
int second = value[0] % 40;
int oid = first * 40 + second;
for (int i = 1; i < length; i++) {
int byte = value[i];
if (byte < 128) {
oid = oid * 128 + byte;
} else {
oid = oid * 128 + (byte & 0x7F);
}
}
return ASN1ObjectIdentifier.fromIdentifierString(oid.toString());
}
DerInputStream toDerInputStream() {
return data;
}
@override
String toString() {
return 'DerValue(tag: $tag, value: ${base64.encode(value)})';
}
}
class DerOutputStream {
final BytesBuilder _builder = BytesBuilder();
void writeByte(int byte) {
_builder.addByte(byte);
}
void writeLength(int length) {
if (length < 128) {
_builder.addByte(length);
} else {
int numBytes = (length.bitLength + 7) >> 3;
_builder.addByte(0x80 | numBytes);
for (int i = numBytes - 1; i >= 0; i--) {
_builder.addByte((length >> (8 * i)) & 0xFF);
}
}
}
void writeBytes(Uint8List bytes) {
_builder.add(bytes);
}
Uint8List toByteArray() {
return _builder.toBytes();
}
}
class DerInputStream {
final ByteBuf buffer;
DerInputStream(this.buffer);
factory DerInputStream.fromBytes(Uint8List data) {
return DerInputStream(ByteBuf(data));
}
static int getLength(ByteBuf inStream) {
int length = inStream.read();
if (length & 0x80 == 0) {
return length;
}
int numBytes = length & 0x7F;
length = 0;
for (int i = 0; i < numBytes; i++) {
length = (length << 8) | inStream.read();
}
return length;
}
int getInteger() {
if (buffer.read() != DerValue.tagInteger) {
throw Exception("DER input, Integer tag error");
}
var length = getLength(buffer);
return decodeBigInt(buffer.readBytes(length)).toInt();
}
List<DerValue> getSequence(int startLen) {
int tag = buffer.read();
if (tag != 0x30) {
// SEQUENCE tag
throw Exception('Sequence tag error');
}
int length = getLength(buffer);
Uint8List sequenceData = buffer.readBytes(length);
DerInputStream sequenceStream = DerInputStream.fromBytes(sequenceData);
List<DerValue> values = [];
while (sequenceStream.buffer.isReadable()) {
int valueTag = sequenceStream.buffer.read();
int valueLength = getLength(sequenceStream.buffer);
Uint8List valueData = sequenceStream.buffer.readBytes(valueLength);
values.add(DerValue(valueTag, valueData));
}
return values;
}
ASN1ObjectIdentifier getOID() {
var oid = ASN1ObjectIdentifier.fromBytes(buffer.bytes);
buffer.read();
var length = getLength(buffer);
buffer.skipBytes(length);
return oid;
}
DerValue getDerValue() {
return DerValue.getDerValue(buffer);
}
/// Returns an ASN.1 OCTET STRING from the input stream.
Uint8List getOctetString() {
if (buffer.read() != DerValue.tagOctetString) {
throw Exception("DER input not an octet string");
}
int length = getLength(buffer);
return buffer.readBytes(length);
}
}
class DerIndefLenConverter {
static const int LEN_LONG = 0x80; // bit 8 set
static const int LEN_MASK = 0x7f; // bits 7 - 1
late Uint8List data;
late Uint8List newData;
int newDataPos = 0, dataPos = 0, dataSize = 0, index = 0;
int unresolved = 0;
List<Object> ndefsList = [];
int numOfTotalLenBytes = 0;
static bool isEOC(Uint8List data, int pos) {
return data[pos] == 0 && data[pos + 1] == 0;
}
static bool isLongForm(int lengthByte) {
return (lengthByte & LEN_LONG) == LEN_LONG;
}
static bool isIndefinite(int lengthByte) {
return (isLongForm(lengthByte) && ((lengthByte & LEN_MASK) == 0));
}
void parseTag() {
if (isEOC(data, dataPos)) {
int numOfEncapsulatedLenBytes = 0;
var elem;
int index;
for (index = ndefsList.length - 1; index >= 0; index--) {
elem = ndefsList[index];
if (elem is int) {
break;
} else {
numOfEncapsulatedLenBytes += (elem as Uint8List).length - 3;
}
}
if (index < 0) {
throw Exception("EOC does not have matching indefinite-length tag");
}
int sectionLen = dataPos - (elem as int) + numOfEncapsulatedLenBytes;
Uint8List sectionLenBytes = getLengthBytes(sectionLen);
ndefsList[index] = sectionLenBytes;
unresolved--;
numOfTotalLenBytes += (sectionLenBytes.length - 3);
}
dataPos++;
}
void writeTag() {
while (dataPos < dataSize) {
if (isEOC(data, dataPos)) {
dataPos += 2;
} else {
newData[newDataPos++] = data[dataPos++];
break;
}
}
}
int parseLength() {
if (dataPos == dataSize) {
return 0;
}
int lenByte = data[dataPos++] & 0xff;
if (isIndefinite(lenByte)) {
ndefsList.add(dataPos);
unresolved++;
return 0;
}
int curLen = 0;
if (isLongForm(lenByte)) {
lenByte &= LEN_MASK;
if (lenByte > 4) {
throw Exception("Too much data");
}
if ((dataSize - dataPos) < (lenByte + 1)) {
return -1;
}
for (int i = 0; i < lenByte; i++) {
curLen = (curLen << 8) + (data[dataPos++] & 0xff);
}
if (curLen < 0) {
throw Exception("Invalid length bytes");
}
} else {
curLen = (lenByte & LEN_MASK);
}
return curLen;
}
void writeLengthAndValue() {
if (dataPos == dataSize) {
return;
}
int curLen = 0;
int lenByte = data[dataPos++] & 0xff;
if (isIndefinite(lenByte)) {
Uint8List lenBytes = ndefsList[index++] as Uint8List;
newData.setRange(newDataPos, newDataPos + lenBytes.length, lenBytes);
newDataPos += lenBytes.length;
} else {
if (isLongForm(lenByte)) {
lenByte &= LEN_MASK;
for (int i = 0; i < lenByte; i++) {
curLen = (curLen << 8) + (data[dataPos++] & 0xff);
}
if (curLen < 0) {
throw Exception("Invalid length bytes");
}
} else {
curLen = (lenByte & LEN_MASK);
}
writeLength(curLen);
writeValue(curLen);
}
}
void writeLength(int curLen) {
if (curLen < 128) {
newData[newDataPos++] = curLen;
} else if (curLen < (1 << 8)) {
newData[newDataPos++] = 0x81;
newData[newDataPos++] = curLen;
} else if (curLen < (1 << 16)) {
newData[newDataPos++] = 0x82;
newData[newDataPos++] = (curLen >> 8);
newData[newDataPos++] = curLen;
} else if (curLen < (1 << 24)) {
newData[newDataPos++] = 0x83;
newData[newDataPos++] = (curLen >> 16);
newData[newDataPos++] = (curLen >> 8);
newData[newDataPos++] = curLen;
} else {
newData[newDataPos++] = 0x84;
newData[newDataPos++] = (curLen >> 24);
newData[newDataPos++] = (curLen >> 16);
newData[newDataPos++] = (curLen >> 8);
newData[newDataPos++] = curLen;
}
}
Uint8List getLengthBytes(int curLen) {
Uint8List lenBytes;
int index = 0;
if (curLen < 128) {
lenBytes = Uint8List(1);
lenBytes[index++] = curLen;
} else if (curLen < (1 << 8)) {
lenBytes = Uint8List(2);
lenBytes[index++] = 0x81;
lenBytes[index++] = curLen;
} else if (curLen < (1 << 16)) {
lenBytes = Uint8List(3);
lenBytes[index++] = 0x82;
lenBytes[index++] = (curLen >> 8);
lenBytes[index++] = curLen;
} else if (curLen < (1 << 24)) {
lenBytes = Uint8List(4);
lenBytes[index++] = 0x83;
lenBytes[index++] = (curLen >> 16);
lenBytes[index++] = (curLen >> 8);
lenBytes[index++] = curLen;
} else {
lenBytes = Uint8List(5);
lenBytes[index++] = 0x84;
lenBytes[index++] = (curLen >> 24);
lenBytes[index++] = (curLen >> 16);
lenBytes[index++] = (curLen >> 8);
lenBytes[index++] = curLen;
}
return lenBytes;
}
void writeValue(int curLen) {
newData.setRange(newDataPos, newDataPos + curLen, data, dataPos);
dataPos += curLen;
newDataPos += curLen;
}
Uint8List? convertBytes(Uint8List indefData) {
data = indefData;
dataPos = 0;
dataSize = data.length;
while (dataPos < dataSize) {
if (dataPos + 2 > dataSize) {
return null;
}
parseTag();
int len = parseLength();
if (len < 0) {
return null;
}
dataPos += len;
if (dataPos < 0) {
throw Exception("Data overflow");
}
if (unresolved == 0) {
break;
}
}
if (unresolved != 0) {
return null;
}
int unused = dataSize - dataPos;
dataSize = dataPos;
newData = Uint8List(dataSize + numOfTotalLenBytes + unused);
dataPos = 0;
newDataPos = 0;
index = 0;
while (dataPos < dataSize) {
writeTag();
writeLengthAndValue();
}
newData.setRange(dataSize + numOfTotalLenBytes, newData.length, data, dataSize);
return newData;
}
}