From 9d25142d506d1559b134d465ff0434ffbde7dca8 Mon Sep 17 00:00:00 2001 From: Timo Weingärtner Date: Sun, 18 Apr 2021 00:04:19 +0200 Subject: rfc4251.H: use template for fixed-length-types --- rfc4251.H | 160 +++++++++++++++++++------------------------------------------- 1 file changed, 48 insertions(+), 112 deletions(-) (limited to 'rfc4251.H') diff --git a/rfc4251.H b/rfc4251.H index 7d4c0d3..fe8ccd4 100644 --- a/rfc4251.H +++ b/rfc4251.H @@ -10,7 +10,7 @@ * those structs contain the objects in their RFC 4251 representation, * conversions are provided via constructors and cast operators * - * Copyright (C) 2013-2015 Timo Weingärtner + * Copyright (C) 2013-2015,2021 Timo Weingärtner * * This file is part of ssh-agent-filter. * @@ -28,132 +28,69 @@ * along with ssh-agent-filter. If not, see . */ +#include +#include +#include #include #include #include #include #include -#include // ntohl() / htonl() #include #include namespace rfc4251 { -struct byte { - union { - uint8_t value; - char buf[1]; - }; - - byte () = default; - explicit byte (uint8_t v) : value(v) {} - inline explicit byte (std::istream &); - - operator uint8_t () const { return value; } -}; - -inline std::istream & operator>> (std::istream & is, byte & x) { - return is.read(x.buf, sizeof(x.buf)); -} - -inline std::ostream & operator<< (std::ostream & os, byte const & x) { - return os.write(x.buf, sizeof(x.buf)); -} - -inline byte::byte (std::istream & is) { - is >> *this; -} - -struct boolean { - union { - bool value; - char buf[1]; - }; - - boolean () = default; - explicit boolean (uint8_t v) : value(v) {} - inline explicit boolean (std::istream &); - - operator uint8_t () const { return value; } -}; - -inline std::istream & operator>> (std::istream & is, boolean & x) { - return is.read(x.buf, sizeof(x.buf)); -} - -inline std::ostream & operator<< (std::ostream & os, boolean const & x) { - return os.write(x.buf, sizeof(x.buf)); -} - -inline boolean::boolean (std::istream & is) { - is >> *this; -} +namespace internal { + template + constexpr inline auto host_to_network (T value) noexcept { + static_assert(std::is_unsigned_v, "shift is only supported on unsigned"); + + std::array ret; + for (auto it{rbegin(ret)}; it != rend(ret); ++it) { + *it = value & 0xff; + if constexpr (sizeof(T) > 1) value >>= 8; + } + return ret; + } -struct uint32 { - union { - uint32_t value; - char buf[4]; - }; + template + constexpr inline auto network_to_host (std::array const buf) noexcept { + static_assert(std::is_unsigned_v, "shift is only supported on unsigned"); - uint32 () = default; - explicit uint32 (uint32_t v) { value = htonl(v); } - inline explicit uint32 (std::istream &); + T ret{0}; + for (auto it{cbegin(buf)}; it != cend(buf); ++it) { + if constexpr (sizeof(T) > 1) ret <<= 8; + ret |= static_cast(*it); + } + return ret; + } - operator uint32_t () const { return ntohl(value); } -}; + template + struct fixed_length { + static_assert(std::is_unsigned_v, "support for signed types might need more work"); -inline std::istream & operator>> (std::istream & is, uint32 & x) { - return is.read(x.buf, sizeof(x.buf)); -} + std::array buf{}; -inline std::ostream & operator<< (std::ostream & os, uint32 const & x) { - return os.write(x.buf, sizeof(x.buf)); -} + constexpr fixed_length () noexcept = default; + constexpr explicit fixed_length (T const v) noexcept : buf{host_to_network(v)} {} + explicit fixed_length (std::istream & is) { is >> *this; } -inline uint32::uint32 (std::istream & is) { - is >> *this; -} + constexpr operator T () const noexcept { return network_to_host(buf); } -struct uint64 { - union { - uint64_t value; - char buf[8]; + friend std::istream & operator>> (std::istream & is, fixed_length & x) { + return is.read(x.buf.data(), x.buf.size()); + } + friend std::ostream & operator<< (std::ostream & os, fixed_length const & x) { + return os.write(x.buf.data(), x.buf.size()); + } }; - - uint64 () = default; - inline explicit uint64 (uint64_t v); - inline explicit uint64 (std::istream &); - - inline explicit operator uint64_t () const; -}; - -inline uint64::uint64 (uint64_t v) { - for (int_fast8_t i{7}; i >= 0; --i) { - buf[i] = v & 0xff; - v >>= 8; - } -} - -inline uint64::operator uint64_t () const { - uint64_t ret{0}; - for (uint_fast8_t i{0}; i < 8; ++i) { - ret <<= 8; - ret |= static_cast(buf[i]); - } - return ret; } -inline std::istream & operator>> (std::istream & is, uint64 & x) { - return is.read(x.buf, sizeof(x.buf)); -} - -inline std::ostream & operator<< (std::ostream & os, uint64 const & x) { - return os.write(x.buf, sizeof(x.buf)); -} - -inline uint64::uint64 (std::istream & is) { - is >> *this; -} +using byte = internal::fixed_length; +using boolean = internal::fixed_length; +using uint32 = internal::fixed_length; +using uint64 = internal::fixed_length; struct string : boost::totally_ordered { std::vector value; @@ -166,9 +103,9 @@ struct string : boost::totally_ordered { explicit string (mpz_class const & x) : string{x.get_mpz_t()} {} inline explicit string (std::istream &); - size_t size () const { return value.size(); } - char const * data () const { return value.data(); } - char * data () { return value.data(); } + size_t size () const noexcept { return value.size(); } + char const * data () const noexcept { return value.data(); } + char * data () noexcept { return value.data(); } operator std::string () const { return {value.begin(), value.end()}; } operator std::vector () const; @@ -183,8 +120,7 @@ inline string::string (char const * s, size_t l) { inline std::istream & operator>> (std::istream & is, string & s) { s.value.clear(); - uint32 len; - if (is >> len) { + if (uint32_t const len{uint32{is}}; is) { s.value.resize(len); is.read(s.value.data(), len); } -- cgit v1.2.3 From b929b048a1839c3da1cadf7f116b0f767f40267e Mon Sep 17 00:00:00 2001 From: Timo Weingärtner Date: Thu, 22 Apr 2021 23:29:10 +0200 Subject: implement separate rfc4251::(string|mpint|name_list) types --- rfc4251.C | 41 +++++++++---------- rfc4251.H | 116 +++++++++++++++++++++++++++++++---------------------- rfc4251_gmp.C | 51 +++++++++++------------ ssh-agent-filter.C | 14 +++---- 4 files changed, 120 insertions(+), 102 deletions(-) (limited to 'rfc4251.H') diff --git a/rfc4251.C b/rfc4251.C index 5ea0a93..05ef8b8 100644 --- a/rfc4251.C +++ b/rfc4251.C @@ -1,10 +1,10 @@ /* * rfc4251.C -- support for name-list type from RFC 4251, section 5 * - * These are the conversions between an rfc4251::string containing a name-list - * and vector. + * These are the conversions between an rfc4251::name_list and + * std::vector. * - * Copyright (C) 2013,2015 Timo Weingärtner + * Copyright (C) 2013,2015,2021 Timo Weingärtner * * This file is part of ssh-agent-filter. * @@ -26,34 +26,33 @@ namespace rfc4251 { -string::string (std::vector const & v) { +name_list::name_list (std::vector const & v) { for (auto it = v.begin(); it != v.end();) { if (it->size() == 0) throw std::length_error{"name of zero length"}; - if (value.size() + it->size() > std::numeric_limits::max()) - throw std::length_error{"32-bit limit for rfc4251::string exceeded"}; - value.insert(value.end(), it->data(), it->data() + it->size()); + check_length_against_limit(buf.size() + it->size()); + buf.insert(buf.end(), it->data(), it->data() + it->size()); ++it; if (it == v.end()) break; - value.push_back(','); + buf.push_back(','); } } -string::operator std::vector () const { - std::vector ret; - auto name_start = value.begin(); - if (name_start != value.end()) - for (auto it = name_start; ; ++it) { - if (it == value.end() or *it == ',') { - if (it == name_start) - throw std::length_error{"name of zero length"}; - ret.emplace_back(name_start, it); - name_start = it + 1; - } - if (it == value.end()) - break; +name_list::operator std::vector () const { + std::vector ret{}; + if (buf.empty()) return ret; + auto name_start = buf.begin(); + for (auto it = name_start; ; ++it) { + if (it == buf.end() or *it == ',') { + if (it == name_start) + throw std::length_error{"name of zero length"}; + ret.emplace_back(name_start, it); + name_start = it + 1; } + if (it == buf.end()) + break; + } return ret; } diff --git a/rfc4251.H b/rfc4251.H index fe8ccd4..6647d15 100644 --- a/rfc4251.H +++ b/rfc4251.H @@ -5,7 +5,9 @@ * rfc4251::boolean boolean * rfc4251::uint32 uint32 * rfc4251::uint64 uint64 - * rfc4251::string string, incl. mpint and name-list + * rfc4251::string string + * rfc4251::mpint mpint + * rfc4251::name_list name-list * * those structs contain the objects in their RFC 4251 representation, * conversions are provided via constructors and cast operators @@ -85,6 +87,48 @@ namespace internal { return os.write(x.buf.data(), x.buf.size()); } }; + + template + struct variable_length : boost::totally_ordered> { + using length_type = Length_Type; + static_assert(std::is_unsigned_v, "negative lengths are not supported"); + + std::vector buf; + + variable_length () = default; + explicit variable_length (std::istream & is) { is >> *this; } + + size_t size () const noexcept { return buf.size(); } + char const * data () const noexcept { return buf.data(); } + char * data () noexcept { return buf.data(); } + + friend std::istream & operator>> (std::istream & is, variable_length & v) { + if (length_type const len{fixed_length{is}}; is) { + v.buf.clear(); + v.buf.resize(len); + is.read(v.buf.data(), len); + } + return is; + } + friend std::ostream & operator<< (std::ostream & os, variable_length const & v) { + check_length_against_limit(v.buf.size()); + if (os << fixed_length{static_cast(v.buf.size())}) + os.write(v.buf.data(), v.buf.size()); + return os; + } + friend bool operator== (variable_length const & l, variable_length const & r) { + return l.buf == r.buf; + } + friend bool operator< (variable_length const & l, variable_length const & r) { + return l.buf < r.buf; + } + + constexpr static void check_length_against_limit(size_t const length) { + if (length > std::numeric_limits::max()) + throw std::length_error{"numeric limit for length field in rfc4251::variable_length type exceeded"}; + } + + }; } using byte = internal::fixed_length; @@ -92,59 +136,33 @@ using boolean = internal::fixed_length; using uint32 = internal::fixed_length; using uint64 = internal::fixed_length; -struct string : boost::totally_ordered { - std::vector value; - - string () = default; - inline explicit string (char const *, size_t); +struct string : internal::variable_length { + using variable_length::variable_length; + + inline explicit string (char const * s, size_t l) { + check_length_against_limit(l); + buf.insert(buf.end(), s, s + l); + } explicit string (std::string const & s) : string{s.data(), s.size()} {} - explicit string (std::vector const &); - explicit string (mpz_srcptr); - explicit string (mpz_class const & x) : string{x.get_mpz_t()} {} - inline explicit string (std::istream &); - size_t size () const noexcept { return value.size(); } - char const * data () const noexcept { return value.data(); } - char * data () noexcept { return value.data(); } + operator std::string () const { return {buf.cbegin(), buf.cend()}; } +}; - operator std::string () const { return {value.begin(), value.end()}; } +struct name_list : internal::variable_length { + using variable_length::variable_length; + + explicit name_list (std::vector const &); + operator std::vector () const; - operator mpz_class () const; }; -inline string::string (char const * s, size_t l) { - if (l > std::numeric_limits::max()) - throw std::length_error{"32-bit limit for rfc4251::string exceeded"}; - value.insert(value.end(), s, s + l); -} - -inline std::istream & operator>> (std::istream & is, string & s) { - s.value.clear(); - if (uint32_t const len{uint32{is}}; is) { - s.value.resize(len); - is.read(s.value.data(), len); - } - return is; -} - -inline std::ostream & operator<< (std::ostream & os, string const & s) { - if (s.value.size() > std::numeric_limits::max()) - throw std::length_error{"32-bit limit for rfc4251::string exceeded"}; - if (os << uint32{static_cast(s.value.size())}) - os.write(s.value.data(), s.value.size()); - return os; -} - -inline string::string (std::istream & is) { - is >> *this; -} - -inline bool operator== (string const & l, string const & r) { - return l.value == r.value; -} - -inline bool operator< (string const & l, string const & r) { - return l.value < r.value; -} +struct mpint : internal::variable_length { + using variable_length::variable_length; + + explicit mpint (mpz_srcptr); + explicit mpint (mpz_class const & x) : mpint{x.get_mpz_t()} {} + + operator mpz_class () const; +}; } diff --git a/rfc4251_gmp.C b/rfc4251_gmp.C index b4c369b..06b6cf6 100644 --- a/rfc4251_gmp.C +++ b/rfc4251_gmp.C @@ -1,9 +1,9 @@ /* - * rfc4251_gmp.C -- implements mpint/gmp conversions for rfc4251::string + * rfc4251_gmp.C -- implements mpint/mpz conversions for rfc4251::mpint * * these functions need linking against libgmp * - * Copyright (C) 2013,2015 Timo Weingärtner + * Copyright (C) 2013,2015,2021 Timo Weingärtner * * This file is part of ssh-agent-filter. * @@ -25,36 +25,37 @@ namespace rfc4251 { -string::string (mpz_srcptr x) { - if (mpz_sgn(x) == 0) - return; - - auto const import_positive = [] (mpz_srcptr x, std::vector & value) { +mpint::mpint (mpz_srcptr x) { + auto const import_positive = [] (mpz_srcptr x, std::vector & buf) { size_t bits{mpz_sizeinbase(x, 2)}; size_t bytes{(bits + 7) / 8}; size_t extrabyte{(bits % 8) == 0}; // need extra byte if MSB is 1 to keep it non-negative - if (bytes + extrabyte > std::numeric_limits::max()) - throw std::length_error{"32-bit limit for rfc4251::string exceeded"}; - value.resize(bytes + extrabyte); - value[0] = 0; - mpz_export(value.data() + extrabyte, nullptr, 1, 1, 1, 0, x); + check_length_against_limit(bytes + extrabyte); + buf.resize(bytes + extrabyte); + buf[0] = 0; + mpz_export(buf.data() + extrabyte, nullptr, 1, 1, 1, 0, x); }; - if (mpz_sgn(x) == 1) - import_positive(x, value); - else { - // handle two's complement: add 1, invert all bits - mpz_class tmp{x}; - tmp += 1; - import_positive(tmp.get_mpz_t(), value); - for (auto & i : value) - i ^= 0xff; + + switch (mpz_sgn(x)) { + case 0: + return; + case 1: + import_positive(x, buf); + return; + default: + // handle two's complement: add 1, invert all bits + mpz_class tmp{x}; + tmp += 1; + import_positive(tmp.get_mpz_t(), buf); + for (auto & i : buf) + i ^= 0xff; } } -string::operator mpz_class () const { - mpz_class ret; - mpz_import(ret.get_mpz_t(), value.size(), 1, 1, 1, 0, value.data()); - if (mpz_sizeinbase(ret.get_mpz_t(), 2) == value.size() * 8) { // negative +mpint::operator mpz_class () const { + mpz_class ret{}; + mpz_import(ret.get_mpz_t(), buf.size(), 1, 1, 1, 0, buf.data()); + if (mpz_sizeinbase(ret.get_mpz_t(), 2) == buf.size() * 8) { // negative mpz_com(ret.get_mpz_t(), ret.get_mpz_t()); ret += 1; mpz_neg(ret.get_mpz_t(), ret.get_mpz_t()); diff --git a/ssh-agent-filter.C b/ssh-agent-filter.C index 639e55c..c658b62 100644 --- a/ssh-agent-filter.C +++ b/ssh-agent-filter.C @@ -347,14 +347,14 @@ std::optional dissect_auth_data_ssh_cert (rfc4251::string const & data) rfc4251::string nonce{datastream}; std::ostringstream key_to_be_signed{}; if (keytype_str == "ssh-rsa") { - rfc4251::string e{datastream}; - rfc4251::string n{datastream}; + rfc4251::mpint e{datastream}; + rfc4251::mpint n{datastream}; key_to_be_signed << rfc4251::string{keytype_str} << e << n; } else if (keytype_str == "ssh-dss") { - rfc4251::string p{datastream}; - rfc4251::string q{datastream}; - rfc4251::string g{datastream}; - rfc4251::string y{datastream}; + rfc4251::mpint p{datastream}; + rfc4251::mpint q{datastream}; + rfc4251::mpint g{datastream}; + rfc4251::mpint y{datastream}; key_to_be_signed << rfc4251::string{keytype_str} << p << q << g << y; } else if (keytype_str == "ecdsa-sha2-nistp256" || keytype_str == "ecdsa-sha2-nistp384" @@ -474,7 +474,7 @@ string describe_sign_request (rfc4251::string const & data_to_be_signed) { rfc4251::string handle_request (rfc4251::string const & r) { io::stream request{r.data(), r.size()}; rfc4251::string ret; - io::stream>> answer{ret.value}; + io::stream>> answer{ret.buf}; arm(request); arm(answer); rfc4251::byte request_code{request}; -- cgit v1.2.3 From b8b65de84dff999f11bcb3379b576c47faa020b5 Mon Sep 17 00:00:00 2001 From: Timo Weingärtner Date: Sun, 25 Apr 2021 22:06:21 +0200 Subject: rfc4251::string: allow construction from std::initializer_list --- rfc4251.H | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'rfc4251.H') diff --git a/rfc4251.H b/rfc4251.H index 6647d15..41d466f 100644 --- a/rfc4251.H +++ b/rfc4251.H @@ -144,6 +144,10 @@ struct string : internal::variable_length { buf.insert(buf.end(), s, s + l); } explicit string (std::string const & s) : string{s.data(), s.size()} {} + string (std::initializer_list init) { + check_length_against_limit(init.size()); + buf = init; + } operator std::string () const { return {buf.cbegin(), buf.cend()}; } }; -- cgit v1.2.3