/************************************************* * DER Encoder Source File * * (C) 1999-2006 The Botan Project * *************************************************/ #include #include #include #include #include #include namespace Botan { namespace { /************************************************* * DER encode an ASN.1 type tag * *************************************************/ SecureVector encode_tag(ASN1_Tag type_tag, ASN1_Tag class_tag) { if((class_tag | 0xE0) != 0xE0) throw Encoding_Error("DER_Encoder: Invalid class tag " + to_string(class_tag)); SecureVector encoded_tag; if(type_tag <= 30) encoded_tag.append((byte)(type_tag | class_tag)); else { u32bit blocks = high_bit(type_tag) + 6; blocks = (blocks - (blocks % 7)) / 7; encoded_tag.append(class_tag | 0x1F); for(u32bit k = 0; k != blocks - 1; ++k) encoded_tag.append(0x80 | ((type_tag >> 7*(blocks-k-1)) & 0x7F)); encoded_tag.append(type_tag & 0x7F); } return encoded_tag; } /************************************************* * DER encode an ASN.1 length field * *************************************************/ SecureVector encode_length(u32bit length) { SecureVector encoded_length; if(length <= 127) encoded_length.append((byte)length); else { const u32bit top_byte = significant_bytes(length); encoded_length.append((byte)(0x80 | top_byte)); for(u32bit j = 4-top_byte; j != 4; ++j) encoded_length.append(get_byte(j, length)); } return encoded_length; } /************************************************* * A comparison functor for sorting SET objects * *************************************************/ class DER_Cmp { public: bool operator()(const MemoryRegion&, const MemoryRegion&) const; }; /************************************************* * Compare two encodings, as specified by X.690 * *************************************************/ bool DER_Cmp::operator()(const MemoryRegion& a, const MemoryRegion& b) const { // FIXME: use lexicographical_compare if(a.size() < b.size()) return true; if(a.size() > b.size()) return false; for(u32bit j = 0; j != a.size(); ++j) { if(a[j] < b[j]) return true; if(a[j] > b[j]) return false; } return false; } } /************************************************* * Return the encoded SEQUENCE/SET * *************************************************/ SecureVector DER_Encoder::DER_Sequence::get_contents() { const ASN1_Tag real_class_tag = ASN1_Tag(class_tag | CONSTRUCTED); SecureVector encoded_tag = encode_tag(type_tag, real_class_tag); if(type_tag == SET) { std::sort(set_contents.begin(), set_contents.end(), DER_Cmp()); for(u32bit j = 0; j != set_contents.size(); ++j) contents.append(set_contents[j]); set_contents.clear(); } SecureVector encoded_length = encode_length(contents.size()); SecureVector retval; retval.append(encoded_tag); retval.append(encoded_length); retval.append(contents); contents.destroy(); return retval; } /************************************************* * Add an encoded value to the SEQUENCE/SET * *************************************************/ void DER_Encoder::DER_Sequence::add_bytes(const byte data[], u32bit length) { if(type_tag == SET) set_contents.push_back(SecureVector(data, length)); else contents.append(data, length); } /************************************************* * Return the type and class taggings * *************************************************/ ASN1_Tag DER_Encoder::DER_Sequence::tag_of() const { return ASN1_Tag(type_tag | class_tag); } /************************************************* * DER_Sequence Constructor * *************************************************/ DER_Encoder::DER_Sequence::DER_Sequence(ASN1_Tag t1, ASN1_Tag t2) : type_tag(t1), class_tag(t2) { } /************************************************* * Return the encoded contents * *************************************************/ SecureVector DER_Encoder::get_contents() { if(subsequences.size() != 0) throw Invalid_State("DER_Encoder: Sequence hasn't been marked done"); SecureVector retval; retval = contents; contents.destroy(); return retval; } /************************************************* * Start a new ASN.1 SEQUENCE/SET/EXPLICIT * *************************************************/ DER_Encoder& DER_Encoder::start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag) { subsequences.push_back(DER_Sequence(type_tag, class_tag)); return (*this); } /************************************************* * Finish the current ASN.1 SEQUENCE/SET/EXPLICIT * *************************************************/ DER_Encoder& DER_Encoder::end_cons() { if(subsequences.empty()) throw Invalid_State("DER_Encoder::end_cons: No such sequence"); SecureVector seq = subsequences[subsequences.size()-1].get_contents(); subsequences.pop_back(); raw_bytes(seq); return (*this); } /************************************************* * Start a new ASN.1 EXPLICIT encoding * *************************************************/ DER_Encoder& DER_Encoder::start_explicit(u16bit type_no) { ASN1_Tag type_tag = (ASN1_Tag)type_no; if(type_tag == SET) throw Internal_Error("DER_Encoder.start_explicit(SET); cannot perform"); return start_cons(type_tag, CONTEXT_SPECIFIC); } /************************************************* * Finish the current ASN.1 EXPLICIT encoding * *************************************************/ DER_Encoder& DER_Encoder::end_explicit() { return end_cons(); } /************************************************* * Write raw bytes into the stream * *************************************************/ DER_Encoder& DER_Encoder::raw_bytes(const MemoryRegion& val) { return raw_bytes(val.begin(), val.size()); } /************************************************* * Write raw bytes into the stream * *************************************************/ DER_Encoder& DER_Encoder::raw_bytes(const byte bytes[], u32bit length) { if(subsequences.size()) subsequences[subsequences.size()-1].add_bytes(bytes, length); else contents.append(bytes, length); return (*this); } /************************************************* * Encode a NULL object * *************************************************/ DER_Encoder& DER_Encoder::encode_null() { return add_object(NULL_TAG, UNIVERSAL, 0, 0); } /************************************************* * DER encode a BOOLEAN * *************************************************/ DER_Encoder& DER_Encoder::encode(bool is_true) { return encode(is_true, BOOLEAN, UNIVERSAL); } /************************************************* * DER encode a small INTEGER * *************************************************/ DER_Encoder& DER_Encoder::encode(u32bit n) { return encode(BigInt(n), INTEGER, UNIVERSAL); } /************************************************* * DER encode a small INTEGER * *************************************************/ DER_Encoder& DER_Encoder::encode(const BigInt& n) { return encode(n, INTEGER, UNIVERSAL); } /************************************************* * DER encode an OCTET STRING or BIT STRING * *************************************************/ DER_Encoder& DER_Encoder::encode(const MemoryRegion& bytes, ASN1_Tag real_type) { return encode(bytes.begin(), bytes.size(), real_type, real_type, UNIVERSAL); } /************************************************* * Encode this object * *************************************************/ DER_Encoder& DER_Encoder::encode(const byte bytes[], u32bit length, ASN1_Tag real_type) { return encode(bytes, length, real_type, real_type, UNIVERSAL); } /************************************************* * DER encode a BOOLEAN * *************************************************/ DER_Encoder& DER_Encoder::encode(bool is_true, ASN1_Tag type_tag, ASN1_Tag class_tag) { byte val = is_true ? 0xFF : 0x00; return add_object(type_tag, class_tag, &val, 1); } /************************************************* * DER encode a small INTEGER * *************************************************/ DER_Encoder& DER_Encoder::encode(u32bit n, ASN1_Tag type_tag, ASN1_Tag class_tag) { return encode(BigInt(n), type_tag, class_tag); } /************************************************* * DER encode an INTEGER * *************************************************/ DER_Encoder& DER_Encoder::encode(const BigInt& n, ASN1_Tag type_tag, ASN1_Tag class_tag) { if(n == 0) return add_object(type_tag, class_tag, 0); bool extra_zero = (n.bits() % 8 == 0); SecureVector contents(extra_zero + n.bytes()); BigInt::encode(contents.begin() + extra_zero, n); if(n < 0) { for(u32bit j = 0; j != contents.size(); ++j) contents[j] = ~contents[j]; for(u32bit j = contents.size(); j > 0; --j) if(++contents[j-1]) break; } return add_object(type_tag, class_tag, contents); } /************************************************* * DER encode an OCTET STRING or BIT STRING * *************************************************/ DER_Encoder& DER_Encoder::encode(const MemoryRegion& bytes, ASN1_Tag real_type, ASN1_Tag type_tag, ASN1_Tag class_tag) { return encode(bytes.begin(), bytes.size(), real_type, type_tag, class_tag); } /************************************************* * DER encode an OCTET STRING or BIT STRING * *************************************************/ DER_Encoder& DER_Encoder::encode(const byte bytes[], u32bit length, ASN1_Tag real_type, ASN1_Tag type_tag, ASN1_Tag class_tag) { if(real_type != OCTET_STRING && real_type != BIT_STRING) throw Invalid_Argument("DER_Encoder: Invalid tag for byte/bit string"); if(real_type == BIT_STRING) { SecureVector encoded; encoded.append(0); encoded.append(bytes, length); return add_object(type_tag, class_tag, encoded); } else return add_object(type_tag, class_tag, bytes, length); } /************************************************* * Conditionally write some values to the stream * *************************************************/ DER_Encoder& DER_Encoder::encode_if(bool cond, DER_Encoder& codec) { if(cond) return raw_bytes(codec.get_contents()); return (*this); } /************************************************* * Request for an object to encode itself * *************************************************/ DER_Encoder& DER_Encoder::encode(const ASN1_Object& obj) { obj.encode_into(*this); return (*this); } /************************************************* * Write the encoding of the byte(s) * *************************************************/ DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag, ASN1_Tag class_tag, const byte rep[], u32bit length) { SecureVector encoded_tag = encode_tag(type_tag, class_tag); SecureVector encoded_length = encode_length(length); SecureVector buffer; buffer.append(encoded_tag); buffer.append(encoded_length); buffer.append(rep, length); return raw_bytes(buffer); } /************************************************* * Write the encoding of the byte(s) * *************************************************/ DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag, ASN1_Tag class_tag, const MemoryRegion& rep_buf) { const byte* rep = rep_buf.begin(); const u32bit rep_len = rep_buf.size(); return add_object(type_tag, class_tag, rep, rep_len); } /************************************************* * Write the encoding of the byte(s) * *************************************************/ DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag, ASN1_Tag class_tag, const std::string& rep_str) { const byte* rep = (const byte*)rep_str.c_str(); const u32bit rep_len = rep_str.size(); return add_object(type_tag, class_tag, rep, rep_len); } /************************************************* * Write the encoding of the byte * *************************************************/ DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag, ASN1_Tag class_tag, byte rep) { return add_object(type_tag, class_tag, &rep, 1); } }