Suche einfach zu implementierende symmetrische Verschlüsselung



  • Ich verwende momentan das hier:

    output = salt concat (input xor keyA) xor hash(keyB xor salt)
    Wobei keyA und keyB garantiert länger als input sind, salt sind zufällige Bytes und das Ergebnis des xor ist jeweils so lange wie der linke Operand.

    Wie sicher ist das? Wobei hier "100% sicher" für mich bedeuten würde, dass ein Angreifer den Plaintext mindestens einer verschlüsselten Nachricht kennen muss und zusätzlich einen Aufwand von bis zu 2^{Länge des Hashwertes} betreiben müsste, um Teile von keyA zu ermitteln und dann 2^{Länge des Salts}, um jede weitere Nachricht zu entschlüsseln.



  • Machst du das hier? (Klammern zur Verdeutlichung)

    output = (salt || (input ^ keyA)) ^ hash(keyB ^ salt)
    

    Nur so aus Interesse, welchen Angriff würdest du erwarten bei

    output = input ^ hash(key ^ salt)
    

    (Angenommen natürlich salt ändert sich für jeden Block.)



  • Nicht ganz, ich stelle den salt der Ausgabe einfach unverändert voran. Entsprechend würde er sich auch nicht für jeden Block ändern und man könnte bei der zweiten Variante leicht sehen, ob es Wiederholungen in der Ursprungsnachricht gab.



  • ;Fred schrieb:

    Entsprechend würde er sich auch nicht für jeden Block ändern und man könnte bei der zweiten Variante leicht sehen, ob es Wiederholungen in der Ursprungsnachricht gab.

    Das ist auch bei der ersten Variante der Fall wenn salt sich nicht für jeden Block ändert.

    Mein Vorschlag wäre das hier:

    encrypt(message, key, iv = generate_random_iv())
    {
      rng_state = hash(iv) ^ hash(key);
      output;
    
      for (size_t i = 0; i != message.number_of_blocks; ++i)
      {
        output[i] = message[i] ^ first_half_of(rng_state);
        rng_state = hash(rng_state);
      }
    
      return output, iv;
    }
    

    Für hash könnte man dann z.B. SHA-256 benutzen und damit hätte man dann 128-bit Blöcke. Das sollte nach meinem Wissen nach* sicher sein in dem Sinne, dass man nicht vom plaintext + ciphertext + iv auf den Key schließen kann. (Und natürlich nicht vom ciphertext + iv auf den plaintext.) Je nach Situation reicht das allerdings nicht, in Netzwerkprotokollen willst du z.B. in so gut wie jedem Fall auch noch Integrity und Authenticity haben, aber wofür genau du das brauchst hast du ja nicht erklärt. Das mit den Blöcken ist so auch ungünstig, da bräuchtest du nämlich padding, vielleicht willst du dir aus dem Schema lieber einen prng bauen und immer die erste Hälfte der state Bytes direkt zurückgeben. Noch besser würde ich finden einfach SHA-3 zu benutzen, wenn du den sponge implementierst hast du quasi automatisch einen geigneten prng, zudem hat SHA-3 einige Schwächen von SHA-2 nicht mehr. (Weshalb der Algorithmus für HMAC für SHA-3 auch trivial ist, im Gegensatz zu dem für SHA-2.)

    * Also dem Wissen nach von irgendeinem noname aus einem deutschen C++ Forum der eigentlich von nichts eine Ahnung hat, die Standardantwort lautet also: Nimm Crypto++! 🤡

    Edit: Mir ist vorhin eingefallen dass ich vor ein paar Monaten mal was passendes geschrieben habe. Aber:

    1. Habe ich keine Ahnung von Krypto.
    2. Ist es wie gesagt Monate her dass ich das geschrieben habe.
    3. Habe ich es nie ordentlich getestet.
    4. Der Key den du dem encrypter gibst sollte u.U. key || iv sein, wenn mehrmals mit dem gleichen Key verschlüsselte Nachrichten unterschiedliche Plaintexte haben sollen.

    Ist also eher mit Vorsicht zu genießen, und vielleicht als Beispiel. https://ideone.com/z3zfkd

    Edit2: Mir fällt gerade auf so eine "spoiler" Funktion wäre ganz nice für's Forum.

    #ifndef KECCAK_DETAIL_HPP
    #define KECCAK_DETAIL_HPP
    
    #include <cstddef>
    #include <cstdint>
    #include <cstring>
    #include <array>
    
    namespace keccak
    {
    	namespace detail
    	{
    		template <unsigned long long A, unsigned long long B>
    		struct static_max
    		{
    			static const auto value = A > B ? A : B;
    		};
    
    		template <unsigned N, unsigned C = 0>
    		struct static_log2
    		{
    			static const unsigned value = static_log2<N / 2, C + 1>::value;
    		};
    
    		template <unsigned C>
    		struct static_log2<1, C>
    		{
    			static const unsigned value = C;
    		};
    
    		template <unsigned C>
    		struct static_log2<0, C>
    		{
    			static_assert(C >= 1, "Log2 result is not an integer.");
    			static_assert(C < 1, "Log2 result is not an integer.");
    		};
    
    		template <typename StateType, typename LaneType>
    		void round(StateType& s, LaneType round_constant);
    
    		template <unsigned Rounds, typename StateType>
    		void keccak_f(StateType& state);
    
    		template <typename LaneType, std::size_t Capacity>
    		class keccak_state
    		{
    		public:
    			typedef LaneType lane_type;
    			typedef lane_type plane_type[5];
    			typedef std::array<plane_type, 5> state_type;
    
    			static const std::size_t byte_bits = 8;
    
    			static const auto width = sizeof(lane_type) * byte_bits;
    			static const auto capacity = Capacity;
    			static const auto rate = 25 * width - capacity;
    
    			static const auto byte_width = width / byte_bits;
    			static const auto byte_capacity = capacity / byte_bits;
    			static const auto byte_rate = rate / byte_bits;
    
    			static const auto rounds = 12 + 2 * static_log2<width>::value;
    
    			static_assert(width % byte_bits == 0, "Sub-byte hacks are not supported.");
    			static_assert(capacity % byte_bits == 0, "Sub-byte hacks are not supported.");
    			static_assert(rate % byte_bits == 0, "Sub-byte hacks are not supported.");
    
    			static_assert(rate % width == 0, "Rate / width combination impossible.");
    			static_assert(byte_rate % byte_width == 0, "Rate / width combination not supported.");
    
    			static_assert(capacity < 25 * width, "Capacity too high.");
    
    		private:
    			state_type state_;
    
    		public:
    			keccak_state()
    				: state_()
    			{}
    
    			const plane_type& operator [] (std::size_t i) const
    			{
    				return state_[i];
    			}
    
    			plane_type& operator [] (std::size_t i)
    			{
    				return state_[i];
    			}
    
    			const lane_type* data() const
    			{
    				return state_.data()[0];
    			}
    
    			lane_type* data()
    			{
    				return state_.data()[0];
    			}
    
    			void transform()
    			{
    				keccak_f<rounds>(state_);
    			}
    		};
    
    		template <typename StateType>
    		class sponge_absorber
    		{
    		public:
    			typedef StateType state_type;
    			typedef typename state_type::lane_type lane_type;
    
    		private:
    			state_type state_;
    			std::size_t bytes_absorbed_;
    
    		public:
    			sponge_absorber()
    				: bytes_absorbed_()
    			{}
    
    			sponge_absorber(const void* data, std::size_t size)
    				: bytes_absorbed_()
    			{
    				(*this)(data, size);
    			}
    
    			void operator () (const void* data, std::size_t size)
    			{
    				auto data_bytes = static_cast<const std::uint8_t*>(data);
    
    				while (size != 0)
    				{
    					while (bytes_absorbed_ == 0 && size >= state_type::byte_rate)
    					{
    						for (std::size_t i = 0; i != state_type::byte_rate / state_type::byte_width; ++i)
    						{
    							lane_type lane;
    							std::memcpy(&lane, data_bytes + i * state_type::byte_width, state_type::byte_width);
    							*(state_[0] + i) ^= lane;
    						}
    
    						data_bytes += state_type::byte_rate;
    						size -= state_type::byte_rate;
    
    						state_.transform();
    					}
    
    					if (size != 0)
    					{
    						reinterpret_cast<std::uint8_t*>(state_.data())[bytes_absorbed_] ^= *data_bytes;
    
    						++data_bytes;
    						--size;
    
    						if (++bytes_absorbed_ == state_type::byte_rate)
    						{
    							state_.transform();
    							bytes_absorbed_ = 0;
    						}
    					}
    				}
    			}
    
    			std::size_t bytes_absorbed() const
    			{
    				return bytes_absorbed_;
    			}
    
    			const state_type& internal_state() const
    			{
    				return state_;
    			}
    
    			state_type& internal_state()
    			{
    				return state_;
    			}
    		};
    
    		template <typename StateType>
    		class sponge_squeezer
    		{
    		public:
    			typedef StateType state_type;
    			typedef typename state_type::lane_type lane_type;
    
    		private:
    			state_type state_;
    			std::size_t bytes_squeezed_;
    
    		public:
    			sponge_squeezer(sponge_absorber<state_type> absorber)
    				: state_(absorber.internal_state())
    				, bytes_squeezed_()
    			{
    				reinterpret_cast<std::uint8_t*>(state_.data())[absorber.bytes_absorbed()] ^= 1u;
    				reinterpret_cast<std::uint8_t*>(state_.data())[state_type::byte_rate - 1] ^= 128u;
    				state_.transform();
    			}
    
    			void operator () (void* buf, std::size_t size)
    			{
    				auto buf_bytes = static_cast<std::uint8_t*>(buf);
    
    				while (size != 0)
    				{
    					while (bytes_squeezed_ == 0 && size >= state_type::byte_rate)
    					{
    						std::memcpy(buf_bytes, state_.data(), state_type::byte_rate);
    
    						buf_bytes += state_type::byte_rate;
    						size -= state_type::byte_rate;
    
    						state_.transform();
    					}
    
    					if (size != 0)
    					{
    						const auto copy_size = std::min(size, state_type::byte_rate - bytes_squeezed_);
    						const auto state_bytes = reinterpret_cast<std::uint8_t*>(state_.data());
    
    						std::memcpy(buf_bytes, state_bytes + bytes_squeezed_, copy_size);
    
    						buf_bytes += copy_size;
    						size -= copy_size;
    						bytes_squeezed_ += copy_size;
    
    						if (bytes_squeezed_ == state_type::byte_rate)
    						{
    							state_.transform();
    							bytes_squeezed_ = 0;
    						}
    					}
    				}
    			}
    
    			std::size_t bytes_squeezed() const
    			{
    				return bytes_squeezed_;
    			}
    
    			const state_type& internal_state() const
    			{
    				return state_;
    			}
    
    			state_type& internal_state()
    			{
    				return state_;
    			}
    		};
    
    		const std::uint64_t round_constants[24] =
    		{
    			0x0000000000000001ull,
    			0x0000000000008082ull,
    			0x800000000000808Aull,
    			0x8000000080008000ull,
    			0x000000000000808Bull,
    			0x0000000080000001ull,
    			0x8000000080008081ull,
    			0x8000000000008009ull,
    			0x000000000000008Aull,
    			0x0000000000000088ull,
    			0x0000000080008009ull,
    			0x000000008000000Aull,
    			0x000000008000808Bull,
    			0x800000000000008Bull,
    			0x8000000000008089ull,
    			0x8000000000008003ull,
    			0x8000000000008002ull,
    			0x8000000000000080ull,
    			0x000000000000800Aull,
    			0x800000008000000Aull,
    			0x8000000080008081ull,
    			0x8000000000008080ull,
    			0x0000000080000001ull,
    			0x8000000080008008ull,
    		};
    
    		template <unsigned Rounds, typename StateType>
    		void keccak_f(StateType& state)
    		{
    			for (unsigned i = 0; i != Rounds; ++i)
    			{
    				round(state, round_constants[i]);
    			}
    		}
    
    		template <unsigned N, typename Word>
    		Word shl(Word w)
    		{
    			return w << (N);
    		}
    
    		template <unsigned N, typename Word>
    		Word shr(Word w)
    		{
    			return w >> (N);
    		}
    
    		template <unsigned N, typename Word>
    		Word rotr(Word w)
    		{
    			return shr<N>(w) | shl<sizeof(Word) * 8 - N>(w);
    		}
    
    		template <unsigned N, typename Word>
    		Word rotl(Word w)
    		{
    			return shl<N>(w) | shr<sizeof(Word) * 8 - N>(w);
    		}
    
    		template <typename Word>
    		Word rotr(Word w, unsigned n)
    		{
    			return (w >> n) | (w << (sizeof(Word) * 8 - n));
    		}
    
    		template <typename Word>
    		Word rotl(Word w, unsigned n)
    		{
    			return (w << n) | (w >> (sizeof(Word) * 8 - n));
    		}
    
    		typedef unsigned rotation_offset_type;
    
    		const rotation_offset_type roffset_0_0 = 0;
    		const rotation_offset_type roffset_0_1 = 36;
    		const rotation_offset_type roffset_0_2 = 3;
    		const rotation_offset_type roffset_0_3 = 41;
    		const rotation_offset_type roffset_0_4 = 18;
    
    		const rotation_offset_type roffset_1_0 = 1;
    		const rotation_offset_type roffset_1_1 = 44;
    		const rotation_offset_type roffset_1_2 = 10;
    		const rotation_offset_type roffset_1_3 = 45;
    		const rotation_offset_type roffset_1_4 = 2;
    
    		const rotation_offset_type roffset_2_0 = 62;
    		const rotation_offset_type roffset_2_1 = 6;
    		const rotation_offset_type roffset_2_2 = 43;
    		const rotation_offset_type roffset_2_3 = 15;
    		const rotation_offset_type roffset_2_4 = 61;
    
    		const rotation_offset_type roffset_3_0 = 28;
    		const rotation_offset_type roffset_3_1 = 55;
    		const rotation_offset_type roffset_3_2 = 25;
    		const rotation_offset_type roffset_3_3 = 21;
    		const rotation_offset_type roffset_3_4 = 56;
    
    		const rotation_offset_type roffset_4_0 = 27;
    		const rotation_offset_type roffset_4_1 = 20;
    		const rotation_offset_type roffset_4_2 = 39;
    		const rotation_offset_type roffset_4_3 = 8;
    		const rotation_offset_type roffset_4_4 = 14;
    
    		template <typename StateType, typename LaneType>
    		void round(StateType& s, LaneType round_constant)
    		{
    			auto c0 = s[0][0] ^ s[1][0] ^ s[2][0] ^ s[3][0] ^ s[4][0];
    			auto c1 = s[0][1] ^ s[1][1] ^ s[2][1] ^ s[3][1] ^ s[4][1];
    			auto c2 = s[0][2] ^ s[1][2] ^ s[2][2] ^ s[3][2] ^ s[4][2];
    			auto c3 = s[0][3] ^ s[1][3] ^ s[2][3] ^ s[3][3] ^ s[4][3];
    			auto c4 = s[0][4] ^ s[1][4] ^ s[2][4] ^ s[3][4] ^ s[4][4];
    
    			auto d0 = c4 ^ rotl<1>(c1);
    			auto d1 = c0 ^ rotl<1>(c2);
    			auto d2 = c1 ^ rotl<1>(c3);
    			auto d3 = c2 ^ rotl<1>(c4);
    			auto d4 = c3 ^ rotl<1>(c0);
    
    			s[0][0] ^= d0;
    			s[0][1] ^= d1;
    			s[0][2] ^= d2;
    			s[0][3] ^= d3;
    
    			auto s00 = (s[0][0]); /* rotl<roffset_0_0> */
    			auto s20 = rotl<roffset_1_0>(s[0][1]);
    			auto s40 = rotl<roffset_2_0>(s[0][2]);
    			auto s10 = rotl<roffset_3_0>(s[0][3]);
    
    			s[0][4] ^= d4;
    			s[1][0] ^= d0;
    			s[1][1] ^= d1;
    			s[1][2] ^= d2;
    
    			auto s30 = rotl<roffset_4_0>(s[0][4]);
    			auto s31 = rotl<roffset_0_1>(s[1][0]);
    			auto s01 = rotl<roffset_1_1>(s[1][1]);
    			auto s21 = rotl<roffset_2_1>(s[1][2]);
    
    			s[1][3] ^= d3;
    			s[1][4] ^= d4;
    			s[2][0] ^= d0;
    			s[2][1] ^= d1;
    
    			auto s41 = rotl<roffset_3_1>(s[1][3]);
    			auto s11 = rotl<roffset_4_1>(s[1][4]);
    			auto s12 = rotl<roffset_0_2>(s[2][0]);
    			auto s32 = rotl<roffset_1_2>(s[2][1]);
    
    			s[2][2] ^= d2;
    			s[2][3] ^= d3;
    			s[2][4] ^= d4;
    			s[3][0] ^= d0;
    
    			auto s02 = rotl<roffset_2_2>(s[2][2]);
    			auto s22 = rotl<roffset_3_2>(s[2][3]);
    			auto s42 = rotl<roffset_4_2>(s[2][4]);
    			auto s43 = rotl<roffset_0_3>(s[3][0]);
    
    			s[3][1] ^= d1;
    			s[3][2] ^= d2;
    			s[3][3] ^= d3;
    			s[3][4] ^= d4;
    
    			auto s13 = rotl<roffset_1_3>(s[3][1]);
    			auto s33 = rotl<roffset_2_3>(s[3][2]);
    			auto s03 = rotl<roffset_3_3>(s[3][3]);
    			auto s23 = rotl<roffset_4_3>(s[3][4]);
    
    			s[4][0] ^= d0;
    			s[4][1] ^= d1;
    			s[4][2] ^= d2;
    			s[4][3] ^= d3;
    
    			auto s24 = rotl<roffset_0_4>(s[4][0]);
    			auto s44 = rotl<roffset_1_4>(s[4][1]);
    			auto s14 = rotl<roffset_2_4>(s[4][2]);
    			auto s34 = rotl<roffset_3_4>(s[4][3]);
    
    			s[4][4] ^= d4;
    			auto s04 = rotl<roffset_4_4>(s[4][4]);
    
    			// 
    			s[0][0] = s00 ^ (~s01 & s02);
    			s[0][1] = s01 ^ (~s02 & s03);
    			s[0][2] = s02 ^ (~s03 & s04);
    			s[0][3] = s03 ^ (~s04 & s00);
    			s[0][4] = s04 ^ (~s00 & s01);
    
    			s[1][0] = s10 ^ (~s11 & s12);
    			s[1][1] = s11 ^ (~s12 & s13);
    			s[1][2] = s12 ^ (~s13 & s14);
    			s[1][3] = s13 ^ (~s14 & s10);
    			s[1][4] = s14 ^ (~s10 & s11);
    
    			s[2][0] = s20 ^ (~s21 & s22);
    			s[2][1] = s21 ^ (~s22 & s23);
    			s[2][2] = s22 ^ (~s23 & s24);
    			s[2][3] = s23 ^ (~s24 & s20);
    			s[2][4] = s24 ^ (~s20 & s21);
    
    			s[3][0] = s30 ^ (~s31 & s32);
    			s[3][1] = s31 ^ (~s32 & s33);
    			s[3][2] = s32 ^ (~s33 & s34);
    			s[3][3] = s33 ^ (~s34 & s30);
    			s[3][4] = s34 ^ (~s30 & s31);
    
    			s[4][0] = s40 ^ (~s41 & s42);
    			s[4][1] = s41 ^ (~s42 & s43);
    			s[4][2] = s42 ^ (~s43 & s44);
    			s[4][3] = s43 ^ (~s44 & s40);
    			s[4][4] = s44 ^ (~s40 & s41);
    
    			s[0][0] ^= round_constant;
    		}
    	}
    }
    
    #endif
    
    #ifndef KECCAK_HPP
    #define KECCAK_HPP
    
    #include <array>
    #include <cstdint>
    #include <limits>
    
    //#include "keccak_detail.hpp"
    
    namespace keccak
    {
    	template <std::size_t CollisionResistance, std::size_t PreimageResistance>
    	class basic_hasher
    	{
    	public:
    		static const auto collision_resistance = CollisionResistance;
    		static const auto preimage_resistance = PreimageResistance;
    
    		static const auto capacity = detail::static_max<collision_resistance * 2, preimage_resistance * 2>::value;
    		static const auto hash_size = detail::static_max<collision_resistance * 2 / 8, preimage_resistance / 8>::value;
    
    		typedef detail::keccak_state<std::uint64_t, capacity> state_type;
    
    		typedef std::array<std::uint8_t, hash_size> hash_type;		
    
    	private:
    		detail::sponge_absorber<state_type> absorb_;
    
    	public:
    		void update(const void* data, std::size_t size)
    		{
    			absorb_(data, size);
    		}
    
    		void finish(void* buf, std::size_t size)
    		{
    			detail::sponge_squeezer<state_type> squeeze(std::move(absorb_));
    			squeeze(buf, size);
    			*this = basic_hasher();
    		}
    
    		hash_type finish()
    		{
    			hash_type hash;
    			finish(hash.data(), hash.size());
    			return hash;
    		}
    	};
    
    	typedef basic_hasher<112, 224> sha3_244_hasher;
    	typedef basic_hasher<128, 256> sha3_256_hasher;
    	typedef basic_hasher<192, 384> sha3_384_hasher;
    	typedef basic_hasher<256, 512> sha3_512_hasher;
    
    	typedef basic_hasher<112, 112> s112_hasher;
    	typedef basic_hasher<128, 128> s128_hasher;
    	typedef basic_hasher<192, 192> s192_hasher;
    	typedef basic_hasher<256, 256> s256_hasher;
    
    	template <std::size_t SecurityStrength>
    	class basic_stream_crypter
    	{
    	public:
    		static const auto security_strength = SecurityStrength;
    
    		typedef detail::keccak_state<std::uint64_t, security_strength * 2> state_type;
    		typedef typename state_type::lane_type lane_type;
    
    	private:
    		detail::sponge_squeezer<state_type> squeeze_;
    
    	public:
    		basic_stream_crypter(const void* key, std::size_t keylen)
    			: squeeze_(detail::sponge_absorber<state_type>(key, keylen))
    		{}
    
    		void operator () (void* buf, const void* data, std::size_t size)
    		{
    			auto dptr = static_cast<const std::uint8_t*>(data);
    			auto bptr = static_cast<std::uint8_t*>(buf);
    			auto msize = size / sizeof(lane_type);
    			auto rsize = size % sizeof(lane_type);
    
    			auto shift = apply<lane_type>(bptr, dptr, msize);
    			apply<std::uint8_t>(bptr + shift, dptr + shift, rsize);
    		}
    
    	private:
    		template <typename LaneType>
    		std::size_t apply(void* buf, const void* data, std::size_t size)
    		{
    			auto dptr = static_cast<const std::uint8_t*>(data);
    			auto bptr = static_cast<std::uint8_t*>(buf);
    
    			for (auto i = 0; i != size; ++i)
    			{
    				LaneType l1, l2;
    
    				squeeze_(&l1, sizeof l1);
    				std::memcpy(&l2, dptr + i * sizeof(LaneType), sizeof l2);
    
    				l1 ^= l2;
    
    				std::memcpy(bptr + i * sizeof(LaneType), &l1, sizeof l1);
    			}
    
    			return size * sizeof(LaneType);
    		}
    	};
    }
    
    #endif
    
    #include <iostream>
    #include <string>
    
    void dump(const void* memory, std::size_t size)
    {
    	static const char table[256][3] = 
    	{
    		"00", "01", "02", "03", "04", "05", "06", "07", 
    		"08", "09", "0a", "0b", "0c", "0d", "0e", "0f", 
    		"10", "11", "12", "13", "14", "15", "16", "17", 
    		"18", "19", "1a", "1b", "1c", "1d", "1e", "1f", 
    		"20", "21", "22", "23", "24", "25", "26", "27", 
    		"28", "29", "2a", "2b", "2c", "2d", "2e", "2f", 
    		"30", "31", "32", "33", "34", "35", "36", "37", 
    		"38", "39", "3a", "3b", "3c", "3d", "3e", "3f", 
    		"40", "41", "42", "43", "44", "45", "46", "47", 
    		"48", "49", "4a", "4b", "4c", "4d", "4e", "4f", 
    		"50", "51", "52", "53", "54", "55", "56", "57", 
    		"58", "59", "5a", "5b", "5c", "5d", "5e", "5f", 
    		"60", "61", "62", "63", "64", "65", "66", "67", 
    		"68", "69", "6a", "6b", "6c", "6d", "6e", "6f", 
    		"70", "71", "72", "73", "74", "75", "76", "77", 
    		"78", "79", "7a", "7b", "7c", "7d", "7e", "7f", 
    		"80", "81", "82", "83", "84", "85", "86", "87", 
    		"88", "89", "8a", "8b", "8c", "8d", "8e", "8f", 
    		"90", "91", "92", "93", "94", "95", "96", "97", 
    		"98", "99", "9a", "9b", "9c", "9d", "9e", "9f", 
    		"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", 
    		"a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", 
    		"b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", 
    		"b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", 
    		"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", 
    		"c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", 
    		"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", 
    		"d8", "d9", "da", "db", "dc", "dd", "de", "df", 
    		"e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", 
    		"e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", 
    		"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", 
    		"f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff", 
    	};
    
    	const unsigned char* p = static_cast<const unsigned char*>(memory);
    
    	for (; size--; ++p)
    		std::cout << table[*p];
    
    	std::cout << '\n';
    }
    
    int main()
    {
    	std::cout << "Key: ";
    	std::string key;
    	std::getline(std::cin, key);
    
    	keccak::basic_stream_crypter<128> encrypt(key.data(), key.size());
    	keccak::basic_stream_crypter<128> decrypt(key.data(), key.size());
    
    	std::string msg;
    	while (std::cout << "Message: " && std::getline(std::cin, msg))
    	{
    		std::string enc(msg.size(), ' ');
    		encrypt(&enc[0], msg.data(), msg.size());
    		std::cout << "Encrypted: ";
    		dump(enc.data(), enc.size());
    
    		std::string dec(msg.size(), ' ');
    
    		decrypt(&dec[0], enc.data(), enc.size());
    		std::cout << "Decrypted: " << dec << '\n';
    	}
    }
    


  • Wozu soll das gut sein? Wenn dein Key sowieso länger ist als die zu verschlüsselnde Nachricht und nicht wiederverwendet wird(!) reicht auch ein einfaches xor. Wenn nicht, nimm lieber gleich was vernünftiges.



  • hasht schrieb:

    Wozu soll das gut sein? Wenn dein Key sowieso länger ist als die zu verschlüsselnde Nachricht und nicht wiederverwendet wird(!) reicht auch ein einfaches xor. Wenn nicht, nimm lieber gleich was vernünftiges.

    Ich denke was er sagen wollte ist dass der Key >= Input für einen Block ist, nicht größer als die gesamte Nachricht.



  • hasht schrieb:

    Wozu soll das gut sein? Wenn dein Key sowieso länger ist als die zu verschlüsselnde Nachricht und nicht wiederverwendet wird(!) reicht auch ein einfaches xor. Wenn nicht, nimm lieber gleich was vernünftiges.

    Wo steht was davon dass er den Key nicht wiederverwenden will?



  • Also, der Key ist tatsächlich länger als alle Nachrichten und es wird natürlich der gleiche Key immer wieder für alle Nachrichten verwendet. Wenn ich den Key für jede Nachricht sicher übertragen könnte, dann würde ich stattdessen gleich die Nachricht selbst übertragen.

    Der Ansatz, die erste Hälfte zu nutzen und das alles wieder mit sich selbst zu verhashen, der gefällt mir sehr gut. Ist sehr einfach, sieht aber auf den ersten Blick viel sicherer aus als mein Vorschlag, dem ich durchaus zutraue, noch einige versteckte Schwachstellen zu haben. Insbesondere die Tatsache, dass ein Angreifer für jedes Bit, dass er von hash(keyB ^ salt) errät, mehrere Bits von keyA bestimmen kann, macht mir etwas Sorgen. Auch wenn es mir zunächst so erscheint, dass er trotzdem 2^Hashlänge Kombinationen ausprobieren muss. Aber wenn das so sicher wäre, würde man vermutlich keine komplexen Verschlüsselungsverfahren wie Twofish und sonstiges entwerfen.



  • ;Fred schrieb:

    Suche einfach zu implementierende symmetrische Verschlüsselung

    Ok, was ist denn Dein Threat-Modell? Was kann der Angreifer?

    ;Fred schrieb:

    Ich verwende momentan das hier:

    output = salt concat (input xor keyA) xor hash(keyB xor salt)
    Wobei keyA und keyB garantiert länger als input sind, salt sind zufällige Bytes und das Ergebnis des xor ist jeweils so lange wie der linke Operand.

    Ich nehme an xor bindet stärker als concat 🙂

    Wenn Du hier 'ne gute Hashfunktion nimmst, ist keyA überflüssig. Und Dein "salt" würde man eher "nonce" nennen, weil es eine "number used once" ist, die nicht mehrfach für denselben Schlüssel benutzt werden darf. Dein Input ist also hochstens so lang wie die Ausgabe von hash(.)?

    Dein Ansatz entspricht jedenfalls einer Strom-Chiffre. Und da gibt's ja schon einiges. Das Rad muss man nicht neu erfinden. Zum Beispiel ist ChaCha 'ne nette Strom-Chiffre, die recht einfach zu implementieren ist. Referenzcode in C, der public domain ist, gibt's AFAIK auch.

    Allerdings bietet so eine Strom-Chiffre allein keine Authentifizierung an und das will man heutzutage schon meistens haben (authenticated encryption). Ggf brauchst du so'was auch. Kommt drauf an, wie du das wo einbauen willst und gegen welche Angreifer Du dich schützen willst.



  • ;Fred, was hast du denn eigentlich vor? Ggf kann man dir bessere Tipps geben, wenn man weiß, was Du vorhast.



  • ;Fred schrieb:

    Aber wenn das so sicher wäre, würde man vermutlich keine komplexen Verschlüsselungsverfahren wie Twofish und sonstiges entwerfen.

    Das ist so sicher™* (also mein Vorschlag), es ist halt nur eine Streamverschlüsselungund keine Blockverschlüsselung.

    Dein Vorschlag ist wie gesagt nicht sicher, wenn sich salt nicht für jeden Block ändert. Und wenn doch, dann ist es sinnlos und zu viel Arbeit.

    * Ich bin mir sehr sicher dass es theoretisch sicher ist, praktisch gibt's natürlich immer Implementierungs und/oder Protokoll-Fehler.


Anmelden zum Antworten