[Streams] Verschlüsselung - Welche Facette?
-
Und wie sieht's mit Padding aus? Kann man feststellen wann die Nachricht zu ende ist bzw. der Stream zerstört wird?
-
> Und wie sieht's mit Padding aus? Kann man feststellen wann die Nachricht zu ende ist bzw. der Stream zerstört wird?
Wie meinst du das?
@ Werner Salomon: Ja, so ähnlich habe ich es mir vorgestellt. Aber ich kann nun mal nicht immer einen filebuf gebrauchen. Leider finde ich in den Referenzen nirgends, ob und wann ein Streampuffer eine bestimmte Facette nutzt. Was mache ich z.B., wenn ich mit einem stringbuf arbeiten will? Stringbuf scheint keine nützliche Facette für diesen Zweck zu benutzen.
-
Jodocus schrieb:
Ja, so ähnlich habe ich es mir vorgestellt. Aber ich kann nun mal nicht immer einen filebuf gebrauchen. Leider finde ich in den Referenzen nirgends, ob und wann ein Streampuffer eine bestimmte Facette nutzt. Was mache ich z.B., wenn ich mit einem stringbuf arbeiten will? Stringbuf scheint keine nützliche Facette für diesen Zweck zu benutzen.
Bei stringbuf ist lt. Standard überhaupt keine Facette garantiert. Warum auch? schreibt doch eh' nur in den Speicher.
Ansonsten bleibt immer noch der Vorschlag von SeppJ - mit dem geht es in jeden Streambuf.cooky451 schrieb:
Und wie sieht's mit Padding aus? Kann man feststellen wann die Nachricht zu ende ist bzw. der Stream zerstört wird?
Bei einer XOR-Verschlüsselung ??

keine Ahnung wie Du das meinst.Ich habe in meinem Listing oben noch das binary hinzugefügt. Sonst kann es bei bestimmten Byte-Konstellationen zu Fehlern bei der Windows-internen Code-Konvertierung kommen. Dies führt dann letztlich zu Lesefehlern.
Gruß
Werner
-
Werner Salomon schrieb:
cooky451 schrieb:
Und wie sieht's mit Padding aus? Kann man feststellen wann die Nachricht zu ende ist bzw. der Stream zerstört wird?
Bei einer XOR-Verschlüsselung ??

keine Ahnung wie Du das meinst.Na ja Jodocus' Eingangspost sah mir so aus als sollte die Verschlüsselung auch irgendwie genutzt werden. Und das wird dann wohl kein XOR mehr sein. Was ja auch nicht schlimm wäre, aber es gibt eben nicht nur Stromverschlüsselungen und statt CTR will man ja vielleicht auch mal CBC nutzen, insofern war meine Frage ob man das überhaupt über diese Methode realisieren könnte.
-
cooky451 schrieb:
Werner Salomon schrieb:
cooky451 schrieb:
Und wie sieht's mit Padding aus? Kann man feststellen wann die Nachricht zu ende ist bzw. der Stream zerstört wird?
Bei einer XOR-Verschlüsselung ??

keine Ahnung wie Du das meinst.Na ja Jodocus' Eingangspost sah mir so aus als sollte die Verschlüsselung auch irgendwie genutzt werden. Und das wird dann wohl kein XOR mehr sein. Was ja auch nicht schlimm wäre, aber es gibt eben nicht nur Stromverschlüsselungen und statt CTR will man ja vielleicht auch mal CBC nutzen, insofern war meine Frage ob man das überhaupt über diese Methode realisieren könnte.
Da hilft dann auch wieder die Implementierung auf streambuf-Ebene. Dann weiß man, der Datenstrom ist zu Ende, wenn der streambuf stirbt.
-
cooky451 schrieb:
Werner Salomon schrieb:
cooky451 schrieb:
Und wie sieht's mit Padding aus? Kann man feststellen wann die Nachricht zu ende ist bzw. der Stream zerstört wird?
Bei einer XOR-Verschlüsselung ??

keine Ahnung wie Du das meinst.Na ja Jodocus' Eingangspost sah mir so aus als sollte die Verschlüsselung auch irgendwie genutzt werden. Und das wird dann wohl kein XOR mehr sein. Was ja auch nicht schlimm wäre, aber es gibt eben nicht nur Stromverschlüsselungen und statt CTR will man ja vielleicht auch mal CBC nutzen, insofern war meine Frage ob man das überhaupt über diese Methode realisieren könnte.
Ach so. Ja codecvt stellt alles zur Verfügung, falls ich das richtig verstanden habe. Du kannst eine beliebige Anzahl von Zeichen in eine beliebig andere Anzahl konvertieren. Padding geht wohl mit unshift - wenn wir das gleiche meinen. Wobei der Stream dabei nicht zerstört wird. Man kann dann aber kontrollieren wann die Konvertierung bzw. (En)Codierung eines Pakets von Bytes abgeschlossen ist.
Gruß
Werner
-
Warum willst du unbedingt IOStreams benutzen? Diese API wurde vor 20 Jahren designed und ist dementsprechend eklig.
-
So, ich hab mich jetzt mal rangemacht und habe das rausbekommen:
#include <iostream> #include <locale> #include <sstream> #include <string> #include <type_traits> #include <vector> template <typename Ch, typename Traits = std::char_traits<Ch>> class crypto : public std::locale::facet { bool crypt; public: crypto(bool c, int i = 0) : std::locale::facet(i), crypt(c) { } ~crypto() { } virtual std::basic_string<Ch, Traits> encrypt(const std::basic_string<Ch, Traits>&) const = 0; virtual std::basic_string<Ch, Traits> decrypt(const std::basic_string<Ch, Traits>&) const = 0; bool do_crypt() const { return crypt; } static std::locale::id id; }; template <typename Ch, typename Traits> std::locale::id crypto<Ch, Traits>::id; class crypto_base64 : public crypto<char> { static const std::string base64_chars; public: crypto_base64(bool do_crypt) : crypto<char>(do_crypt) { } std::string encrypt(const std::string& s) const { std::string encoded; unsigned index = 0; auto num_bytes = s.length(); for(; index + 2 < s.length(); index += 3) { encoded.push_back(base64_chars[s[index] >> 2]); encoded.push_back(base64_chars[((s[index] & 3) << 4) + (s[index + 1] >> 4)]); encoded.push_back(base64_chars[((s[index + 1] & 15) << 2) + (s[index + 2] >> 6)]); encoded.push_back(base64_chars[s[index + 2] & 63]); } if(num_bytes - index == 2) { encoded.push_back(base64_chars[s[index] >> 2]); encoded.push_back(base64_chars[((s[index] & 3) << 4) + (s[index + 1] >> 4)]); encoded.push_back(base64_chars[((s[index + 1] & 15) << 2) + (0 >> 6)]); encoded.push_back('='); } else if(num_bytes - index == 1) { encoded.push_back(base64_chars[s[index] >> 2]); encoded.push_back(base64_chars[((s[index] & 3) << 4) + (0 >> 4)]); encoded += "=="; } return encoded; } std::string decrypt(const std::string& s) const { std::vector<unsigned> bytes(s.length()); for(std::size_t n = 0; n < bytes.size(); ++n) { if(s[n] == '=') bytes[n] = 0; else bytes[n] = base64_chars.find(s[n]); } std::string decoded; for(std::size_t n = 0; n + 3 < bytes.size(); n += 4) { decoded.push_back((bytes[n] << 2) + (bytes[n + 1] >> 4)); decoded.push_back((bytes[n + 1] << 4) + (bytes[n + 2] >> 2)); decoded.push_back((bytes[n + 2] << 6) + bytes[n + 3]); } return decoded; } }; const std::string crypto_base64::base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; template <typename Ch, typename Traits = std::char_traits<Ch>> class crypto_xor : public crypto<Ch, Traits> { const char* sbegin_; std::size_t ssize_; public: crypto_xor(bool do_crypt, const char* begin = 0, std::size_t size = 0) : crypto<Ch, Traits>(do_crypt), sbegin_(begin), ssize_(size) { } std::basic_string<Ch, Traits> encrypt(const std::basic_string<Ch, Traits>& s) const { if(!ssize_) return s; std::basic_string<Ch, Traits> crypt = s; int index = -1; for(std::size_t n = 0; n != s.size(); ++n) { ++index; if(index == ssize_) index = 0; crypt[n] = crypt[n] ^ sbegin_[index]; } return crypt; } std::basic_string<Ch, Traits> decrypt(const std::basic_string<Ch, Traits>& s) const { return encrypt(s); } }; template <typename Ch, typename Traits = std::char_traits<Ch>> class crypt_buffer : public std::basic_streambuf<Ch, Traits> { std::basic_streambuf<Ch, Traits>* buf_; public: crypt_buffer(std::basic_streambuf<Ch, Traits>* buf) : buf_(buf) { } protected: std::streamsize xsputn(const Ch* ptr, std::streamsize count) { std::string str(ptr, static_cast<unsigned>(count)); if(std::has_facet<crypto<Ch, Traits>>(getloc())) { const crypto<Ch, Traits>& facet = std::use_facet<crypto<Ch, Traits>>(getloc()); str = facet.do_crypt() ? facet.encrypt(str) : facet.decrypt(str); } pubimbue(std::locale()); return buf_->sputn(str.c_str(), str.length()) == str.length() ? count : 0; } std::streamsize xsgetn(Ch* ptr, std::streamsize count) { std::streamsize ret = buf_->sgetn(ptr, count); std::basic_string<Ch, Traits> str(ptr, static_cast<std::size_t>(count)); if(std::has_facet<crypto<Ch, Traits>>(getloc())) { const crypto<Ch, Traits>& facet = std::use_facet<crypto<Ch, Traits>>(getloc()); str = facet.do_crypt() ? facet.encrypt(str) : facet.decrypt(str); } std::streamsize to_copy = count > str.length() ? str.length() : count; Traits::copy(ptr, str.c_str(), static_cast<std::size_t>(to_copy)); return to_copy; } }; std::ios& as_base64(std::ios& ios) { ios.imbue(std::locale(std::locale(), new crypto_base64(true))); return ios; } std::ios& from_base64(std::ios& ios) { ios.imbue(std::locale(std::locale(), new crypto_base64(false))); return ios; } struct xor_helper { xor_helper(const std::string& s, bool c) : salt(s), do_crypt(c) { } template <typename Ch, typename Traits> std::basic_ostream<Ch, Traits>& function(std::basic_ostream<Ch, Traits>& ios) { ios.imbue(std::locale(std::locale(), new crypto_xor<Ch, Traits>(do_crypt, salt.c_str(), salt.size()))); return ios; } const std::string& salt; bool do_crypt; }; xor_helper as_xor(const std::string& salt) { return xor_helper(salt, true); } xor_helper from_xor(const std::string& salt) { return xor_helper(salt, false); } template <typename Ch, typename Traits> std::basic_ostream<Ch, Traits>& operator <<(std::basic_ostream<Ch, Traits>& ios, xor_helper& xorh) { return xorh.function(ios); }Testprogramm:
int main() { std::streambuf* old = std::cout.rdbuf(new crypt_buffer<char>(std::cout.rdbuf())); std::cout << as_base64 << "Hallo, Welt!" << from_base64 << "SGFsbG8gV2VsdCE="; std::cout << as_xor("x") << "Hallo, Welt!"; delete std::cout.rdbuf(old); }Das Manipulator-Interface geht ganz gut. Nur zwei Dinge stören spontant:
~~1. Eigentlich gilt der Manipulator nur für die nächste Aktion, also habe ich pubimbue() benutzt, um das Locale zurückzusetzen. Leider ergibt has_facet<crypt> immernoch true.
~~
2. operator >>() mit istream benutzt scheinbar nicht sgetn(), sodass meine xsgetn()-Überladung nicht aufgerufen wird. Woran liegt das?
-
So, ich habe das erste Problem gelöst (pubimbue() statt buf_->pubimbue(), logisch).
Für das zweite Problem habe ich noch keine Idee, da ich scheinbar underflow() und uflow() definieren muss. Aber wie soll ich einen Base64-Algorithmus durchführen, wenn ich noch nicht einmal alle Zeichen habe?Außerdem funktioniert meine Lösung nicht mit std::stringstream, weil std::stringstream::rdbuf() doch tatsächlich std::basic_ios<>::rdbuf() überdeckt und dadurch inbesondere std::stringstream::rdbuf(std::streambuf*) nicht exisitiert. Das sieht für mich wie ein Bug (entweder in Visual Studio oder gar im Standard selbst) aus. Warum sollte ich den Streampuffer von Stringstreams nicht beeinflussen können?
-
Jodocus schrieb:
Für das zweite Problem habe ich noch keine Idee, da ich scheinbar underflow() und uflow() definieren muss. Aber wie soll ich einen Base64-Algorithmus durchführen, wenn ich noch nicht einmal alle Zeichen habe?
Hallo Jodocus,
Ja - Du musst mindestens eine der beiden Methoden in Deinem streambuf implementieren, damit Du auch alle Zeichen erwischst. Die Implementierung von xgetn ist dann gar nicht notwendig. Für die Ausgabe gilt das genauso für overflow bzw. xputn.
Und wenn man z.B. es in underflow einbaut, so muss man sich bei der Konvertierung immer den Zustand merken, wo man sich gerade befindet. Dann kann man auch Zeichen für Zeichen umsetzen. Ist natürlich ein wenig aufwendiger zu implementieren.Jodocus schrieb:
Außerdem funktioniert meine Lösung nicht mit std::stringstream, weil std::stringstream::rdbuf() doch tatsächlich std::basic_ios<>::rdbuf() überdeckt und dadurch insbesondere std::stringstream::rdbuf(std::streambuf*) nicht existiert. Das sieht für mich wie ein Bug (entweder in Visual Studio oder gar im Standard selbst) aus. Warum sollte ich den Streampuffer von Stringstreams nicht beeinflussen können?
Nein - das ist kein Bug, das ist Standard. In diesem Fall würde ich auch nur den basic_stringbuf in Kombination mit einem nacktem std::istream benutzen. Dann hast Du alles, was Du brauchst - eben als zwei Klassen und nicht als eine.
Ich habe mich mal hingesetzt und versucht eine codecvt-Facette für die base64-Konvertierung zu basteln.
#ifndef CPLUSPLUSFORUM_CODECVT_BASE64_H_ #define CPLUSPLUSFORUM_CODECVT_BASE64_H_ #include <locale> // std::codecvt #include <cassert> #include <string> // char_traits namespace base64_detail { // 0 1 2 3 4 5 6 // 0123456789012345678901234567890123456789012345678901234567890123 const char tab[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; template< typename S > char write( S& state, const char*& src ) { const int shift = ((state & 0x300) >> 7) + 2; const char res = tab[(state & 0x3f) | static_cast< unsigned char >( *src ) >> shift]; if( ((state += 0x100) &= 0x300) != 0 ) state |= (*src++ << (6 - shift)) & 0x3f; return res; } template< typename S > void read( S& state, char from, char*& to ) { const char* q = std::char_traits< char >::find( tab, 64, from ); if( q ) { const int c = q - tab; const int shift = ((state & 0x300) >> 7) + 2; // e{2,4,6,8} if( shift > 2 ) *to++ = (state & 0xff) | (c >> (8 - shift)); state = ((state + 0x100) & 0x300) | ((c << shift) & 0xff); } } } template< typename S = std::mbstate_t > class base64 : public std::codecvt< char, char, S > { typedef std::codecvt< char, char, S > base_type; public: explicit base64( std::size_t refs = 0 ) : base_type( refs ) {} protected: // -- out: 3 char -> 4 Byte virtual result do_out( state_type& state, const intern_type* from, const intern_type* from_end, const intern_type*& from_next, extern_type* to, extern_type* to_end, extern_type*& to_next ) const { for( ; from != from_end && to != to_end; ++to ) *to = base64_detail::write( state, from ); from_next = from; to_next = to; return ok; } virtual result do_unshift( state_type& state, extern_type* to, extern_type* to_end, extern_type*& to_next) const { const char* padding = "\0"; for( const char* src = padding; state != 0 && to != to_end && src != padding; ++to ) *to = base64_detail::write( state, src ); to_next = to; return ok; } // -- in: 4 Byte -> 3 char virtual result do_in( state_type& state, const extern_type* from, const extern_type* from_end, const extern_type*& from_next, intern_type* to, intern_type* to_end, intern_type*& to_next ) const { for( ; from != from_end && to != to_end; ++from ) base64_detail::read( state, *from, to ); from_next = from; to_next = to; return ok; } virtual int do_encoding() const /*noexcept*/ { return -1; // Returns: -1 if the encoding of the externT sequence is state-dependent } virtual bool do_always_noconv() const /*noexcept*/ { return false; } }; #endif // CPLUSPLUSFORUM_CODECVT_BASE64_H_wobei noch die Methoden do_length und do_max_length fehlen. Aber da diese Methode von der basic_filebuf-Implementierung beim Visual Studio gar nicht aufgerufen werden, habe ich mir das gespart. Abgesehen davon habe ich selten für so wenig Code so lange gebraucht - nun gut.
Die Anwendung ist dann wie gehabt:
#include "codecvt_base64.hpp" // (s.o.) #include <fstream> #include <string> #include <iostream> int main() { using namespace std; { // -- Schreiben ofstream out("test.dat", ios_base::binary ); out.imbue( locale( out.getloc(), new base64<> ) ); out << "Hallo Welt" << "\n"; for( int i0=1, i1=0; i1 < 100; i0 += i1, swap( i0, i1 ) ) out << i1 << "\n"; } { // -- Lesen ifstream in("test.dat", ios_base::binary ); if( !in.is_open() ) { cerr << "Fehler beim Oeffnen der Datei" << endl; return -2; } cout << "\nDatei decodiert:" << endl; in.imbue( locale( in.getloc(), new base64<> ) ); string ueberschrift; if( getline( in, ueberschrift ) ) { cout << ueberschrift << endl; for( int zahl; in >> zahl; ) cout << zahl << " "; cout << endl; } if( !in.eof() ) { cerr << "Fehler beim Lesen" << endl; } } }Sag' uns bitte noch, warum es ausgerechnet ein stringstream sein muss. Schickst Du die Bytes anschließend über einen Socket oder dergleichen?
Gruß
Werner
-
Entschuldige Werner, ich dachte, der Thread wäre tot. Danke für deine Mühe, durch diesen Code muss ich mich erst einmal durchbeißen. (Auch wenn per Google zu Themen wie codecvt kaum noch etwas anderes als Referenzen findet).
> Sag' uns bitte noch, warum es ausgerechnet ein stringstream sein muss. Schickst Du die Bytes anschließend über einen Socket oder dergleichen?
Ja, ich wollte es für jeden Zweck nutzbar haben. Auf jeden Fall nicht nur für Dateien.