src/ASN1Writer.h
branchv_0
changeset 1 e9b4f3a9e436
parent 0 f4f9cdf7ed59
child 2 d1b39a9bd591
equal deleted inserted replaced
0:f4f9cdf7ed59 1:e9b4f3a9e436
    42 private:
    42 private:
    43 	ostream& output;
    43 	ostream& output;
    44 	wstring_convert<codecvt_utf8<wchar_t>> convertor; // only UTF8String is supported
    44 	wstring_convert<codecvt_utf8<wchar_t>> convertor; // only UTF8String is supported
    45 	int sequenceLevel = 0;
    45 	int sequenceLevel = 0;
    46 
    46 
    47 	void xxx_indent() {
    47 	enum TagClass : uint8_t {
    48 		// FIXME: remove
    48 		/** The type is native to ASN.1 */
    49 		for (int i = 0; i < sequenceLevel; i++) output << "  ";
    49 		Universal = 0,
       
    50 		/** The type is only valid for one specific application */
       
    51 		Application = 1,
       
    52 		/** Meaning of this type depends on the context (such as within a sequence, set or choice) */
       
    53 		ContextSpecific = 2,
       
    54 		/** Defined in private specifications */
       
    55 		Private = 3,
       
    56 	};
       
    57 
       
    58 	enum PC : uint8_t {
       
    59 		/** The contents octets directly encode the element value. */
       
    60 		Primitive = 0,
       
    61 		/** The contents octets contain 0, 1, or more element encodings.  */
       
    62 		Constructed = 1,
       
    63 	};
       
    64 
       
    65 	enum UniversalType : uint16_t {
       
    66 		Boolean = 1,
       
    67 		Integer = 2,
       
    68 		OctetString = 4,
       
    69 		Null = 5,
       
    70 		ObjectIdentifier = 6,
       
    71 		Real = 9,
       
    72 		UTF8String = 12,
       
    73 		RelativeObjectIdentifier = 13,
       
    74 		Sequence = 16,
       
    75 	};
       
    76 
       
    77 	void writeIdentifier(TagClass tagClass, PC pc, uint16_t tagNumber) {
       
    78 		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
       
    79 		uint8_t tag = 0;
       
    80 		tag |= tagClass << 6;
       
    81 		tag |= pc << 5;
       
    82 		tag |= tagNumber;
       
    83 		output.put(tag);
       
    84 	}
       
    85 
       
    86 	/**
       
    87 	 * @param der
       
    88 	 * @param length
       
    89 	 */
       
    90 	template<typename N, typename = typename std::enable_if<std::is_unsigned<N>::value, N>::type>
       
    91 	void writeLength(N length) {
       
    92 		if (length < 128) {
       
    93 			output.put(length);
       
    94 		} else {
       
    95 			for (int i = sizeof (N) - 1; i >= 0; i--) {
       
    96 				uint8_t b = length >> (i * 8);
       
    97 				if (b || i == 0) {
       
    98 					output.put((i + 1) | 0x80);
       
    99 					for (; i >= 0; i--) {
       
   100 						b = length >> (i * 8);
       
   101 						output.put(b);
       
   102 					}
       
   103 					break;
       
   104 				}
       
   105 			}
       
   106 		}
       
   107 	}
       
   108 
       
   109 	static bool isLittleEndian() {
       
   110 		int test = 1;
       
   111 		return (*(char *) &test);
       
   112 	}
       
   113 
       
   114 	static bool isBigEndian() {
       
   115 		return !isLittleEndian();
    50 	}
   116 	}
    51 
   117 
    52 public:
   118 public:
    53 
   119 
    54 	ASN1Writer(std::ostream& output) : output(output) {
   120 	ASN1Writer(std::ostream& output) : output(output) {
    55 	}
   121 	}
    56 
   122 
    57 	virtual ~ASN1Writer() {
   123 	virtual ~ASN1Writer() {
    58 		if (sequenceLevel) output << "Unable to close ASN1Writer because there are not ended sequences." << endl; // FIXME: better error handling
       
    59 		output.flush();
   124 		output.flush();
    60 	}
   125 	}
    61 
   126 
       
   127 	/**
       
   128 	 * Just check whether all sequences are closed. If not, throws RelpipeCLIException.
       
   129 	 */
       
   130 	void end() {
       
   131 		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
       
   132 	}
       
   133 
    62 	void writeStartSequence() {
   134 	void writeStartSequence() {
    63 		xxx_indent();
       
    64 		output << "sequence start" << endl;
       
    65 
       
    66 		sequenceLevel++;
   135 		sequenceLevel++;
       
   136 		output.put('\x30');
       
   137 		output.put('\x80');
    67 	}
   138 	}
    68 
   139 
    69 	void writeEndSequence() {
   140 	void writeEndSequence() {
    70 		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
   141 		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
    71 
       
    72 		sequenceLevel--;
   142 		sequenceLevel--;
    73 
   143 		output.put('\x00');
    74 		xxx_indent();
   144 		output.put('\x00');
    75 		output << "sequence end" << endl;
       
    76 	}
   145 	}
    77 
   146 
    78 	void writeBoolean(const boolean_t& value) {
   147 	void writeBoolean(const boolean_t& value) {
    79 		xxx_indent();
   148 		output.put('\x01');
    80 		output << "boolean" << (value ? "true" : "false") << endl;
   149 		output.put('\x01');
       
   150 		output.put(value ? '\xFF' : '\x00');
       
   151 
    81 	}
   152 	}
    82 
   153 
    83 	void writeInteger(const integer_t& value) {
   154 	void writeInteger(const integer_t& value) {
    84 		xxx_indent();
   155 		uint8_t* end = (uint8_t*) & value;
    85 		output << "integer: " << value << endl;
   156 		uint8_t* start = end + sizeof (value) - 1;
       
   157 		uint8_t* current;
       
   158 		int direction = -1;
       
   159 
       
   160 		if (isBigEndian()) {
       
   161 			std::swap(end, start);
       
   162 			direction = -direction;
       
   163 		}
       
   164 
       
   165 		// move current pointer to the first valuable octet
       
   166 		for (current = start; current != end; current += direction) {
       
   167 			switch (*current) {
       
   168 				case 0x00: if ((*(current + direction) & 0x80) == 0) continue;
       
   169 					break;
       
   170 				case 0xFF: if ((*(current + direction) & 0x80) != 0) continue;
       
   171 					break;
       
   172 			}
       
   173 			break;
       
   174 		}
       
   175 
       
   176 		std::vector<char> v;
       
   177 		writeIdentifier(TagClass::Universal, PC::Primitive, UniversalType::Integer);
       
   178 		writeLength((size_t) (end > current ? end - current : current - end) + 1);
       
   179 		for (end += direction; current != end; current += direction) output.put(*current);
    86 	}
   180 	}
    87 
   181 
    88 	void writeString(const string_t& value) {
   182 	void writeString(const string_t& value) {
    89 		xxx_indent();
   183 		// TODO: empty string → null?
    90 		output << "string: " << convertor.to_bytes(value) << endl;
   184 		std::string bytes = convertor.to_bytes(value);
       
   185 		writeIdentifier(TagClass::Universal, PC::Primitive, UniversalType::UTF8String);
       
   186 		writeLength(bytes.length());
       
   187 		output << bytes;
    91 	}
   188 	}
    92 
   189 
    93 };
   190 };
    94 
   191 
    95 }
   192 }