# HG changeset patch # User František Kučera # Date 1553906345 -3600 # Node ID e9b4f3a9e4364a87efb8f7e2a5e19ced711327c0 # Parent f4f9cdf7ed593bd0d72b2f2a21a70bfe3f838408 first working version with ASN.1 BER output diff -r f4f9cdf7ed59 -r e9b4f3a9e436 src/ASN1Handler.h --- a/src/ASN1Handler.h Sat Mar 30 01:12:42 2019 +0100 +++ b/src/ASN1Handler.h Sat Mar 30 01:39:05 2019 +0100 @@ -129,6 +129,7 @@ if (valueCount) asn1Writer->writeEndSequence(); if (relationCount) asn1Writer->writeEndSequence(); asn1Writer->writeEndSequence(); + asn1Writer->end(); } }; diff -r f4f9cdf7ed59 -r e9b4f3a9e436 src/ASN1Writer.h --- a/src/ASN1Writer.h Sat Mar 30 01:12:42 2019 +0100 +++ b/src/ASN1Writer.h Sat Mar 30 01:39:05 2019 +0100 @@ -44,9 +44,75 @@ wstring_convert> convertor; // only UTF8String is supported int sequenceLevel = 0; - void xxx_indent() { - // FIXME: remove - for (int i = 0; i < sequenceLevel; i++) output << " "; + enum TagClass : uint8_t { + /** The type is native to ASN.1 */ + Universal = 0, + /** The type is only valid for one specific application */ + Application = 1, + /** Meaning of this type depends on the context (such as within a sequence, set or choice) */ + ContextSpecific = 2, + /** Defined in private specifications */ + Private = 3, + }; + + enum PC : uint8_t { + /** The contents octets directly encode the element value. */ + Primitive = 0, + /** The contents octets contain 0, 1, or more element encodings. */ + Constructed = 1, + }; + + enum UniversalType : uint16_t { + Boolean = 1, + Integer = 2, + OctetString = 4, + Null = 5, + ObjectIdentifier = 6, + Real = 9, + UTF8String = 12, + RelativeObjectIdentifier = 13, + Sequence = 16, + }; + + void writeIdentifier(TagClass tagClass, PC pc, uint16_t tagNumber) { + if (tagNumber > 30) throw cli::RelpipeCLIException(L"Tag numbers higher than 30 are currently unsupported.", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); // should not happen, error in the program // TODO: support tag numbers > 30 + uint8_t tag = 0; + tag |= tagClass << 6; + tag |= pc << 5; + tag |= tagNumber; + output.put(tag); + } + + /** + * @param der + * @param length + */ + template::value, N>::type> + void writeLength(N length) { + if (length < 128) { + output.put(length); + } else { + for (int i = sizeof (N) - 1; i >= 0; i--) { + uint8_t b = length >> (i * 8); + if (b || i == 0) { + output.put((i + 1) | 0x80); + for (; i >= 0; i--) { + b = length >> (i * 8); + output.put(b); + } + break; + } + } + } + } + + static bool isLittleEndian() { + int test = 1; + return (*(char *) &test); + } + + static bool isBigEndian() { + return !isLittleEndian(); } public: @@ -55,39 +121,70 @@ } virtual ~ASN1Writer() { - if (sequenceLevel) output << "Unable to close ASN1Writer because there are not ended sequences." << endl; // FIXME: better error handling output.flush(); } + /** + * Just check whether all sequences are closed. If not, throws RelpipeCLIException. + */ + void end() { + if (sequenceLevel) throw cli::RelpipeCLIException(L"Unable to close ASN1Writer because there are not ended sequences.", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); // should not happen, error in the program + } + void writeStartSequence() { - xxx_indent(); - output << "sequence start" << endl; - sequenceLevel++; + output.put('\x30'); + output.put('\x80'); } void writeEndSequence() { if (sequenceLevel == 0) throw cli::RelpipeCLIException(L"Unable to writeEndSequence() if not sequence is currently started.", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); // should not happen, error in the program - sequenceLevel--; - - xxx_indent(); - output << "sequence end" << endl; + output.put('\x00'); + output.put('\x00'); } void writeBoolean(const boolean_t& value) { - xxx_indent(); - output << "boolean" << (value ? "true" : "false") << endl; + output.put('\x01'); + output.put('\x01'); + output.put(value ? '\xFF' : '\x00'); + } void writeInteger(const integer_t& value) { - xxx_indent(); - output << "integer: " << value << endl; + uint8_t* end = (uint8_t*) & value; + uint8_t* start = end + sizeof (value) - 1; + uint8_t* current; + int direction = -1; + + if (isBigEndian()) { + std::swap(end, start); + direction = -direction; + } + + // move current pointer to the first valuable octet + for (current = start; current != end; current += direction) { + switch (*current) { + case 0x00: if ((*(current + direction) & 0x80) == 0) continue; + break; + case 0xFF: if ((*(current + direction) & 0x80) != 0) continue; + break; + } + break; + } + + std::vector v; + writeIdentifier(TagClass::Universal, PC::Primitive, UniversalType::Integer); + writeLength((size_t) (end > current ? end - current : current - end) + 1); + for (end += direction; current != end; current += direction) output.put(*current); } void writeString(const string_t& value) { - xxx_indent(); - output << "string: " << convertor.to_bytes(value) << endl; + // TODO: empty string → null? + std::string bytes = convertor.to_bytes(value); + writeIdentifier(TagClass::Universal, PC::Primitive, UniversalType::UTF8String); + writeLength(bytes.length()); + output << bytes; } };