--- 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<codecvt_utf8<wchar_t>> 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<typename N, typename = typename std::enable_if<std::is_unsigned<N>::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<char> 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;
}
};