From: Maaartin on
On Apr 27, 1:07 am, "J.D." <degolyer...(a)yahoo.com> wrote:
> On Apr 26, 6:57 pm, Maaartin <grajc...(a)seznam.cz> wrote:
>
> > On Apr 26, 11:56 pm, "J.D." <degolyer...(a)yahoo.com> wrote:
>
> > > Not all hash functions use extensive final processing between the last
> > > input and outputting the digest. For many (e.g. MD4 and MD5) the
> > > digest is simply the last chaining variable.
>
> > You're right. So maybe all modern hash functions do?
>
> Well, most hashes based on the Merkle Damgard construction seem to
> have the digest = the last chaining variable. This includes several
> of the SHA-3 hash function candidates (if I remember correctly, I can
> try to look up a cite if you want).

I did already - only 19 out of 51 first round candidates do, so I was
wrong again:
http://csrc.nist.gov/groups/ST/hash/sha-3/Round1/Feb2009/documents/sha3_classification_forler.pdf

> I like both of those ideas. They are simpler and perhaps more
> effective than my suggestion...

They are simple, and I hope, Brian will be able to evaluate it
somehow, and I hope they'll perform well. Proving anything about
security is too hard, but for hand/card ciphers/hashes the statistical
tests could be appropriate.
From: J.D. on
On Apr 26, 7:35 pm, "J.D." <degolyer...(a)yahoo.com> wrote:
> On Apr 26, 7:22 pm, "J.D." <degolyer...(a)yahoo.com> wrote:
>
>
>
> > On Apr 26, 6:59 pm, "J.D." <degolyer...(a)yahoo.com> wrote:
>
> > > On Apr 26, 5:56 pm, "J.D." <degolyer...(a)yahoo.com> wrote:
>
> > > > Indeed.  Perhaps you (brian) could improve the mix rate by doing
> > > > something like the following:
>
> > > > Split the deck into a red and a black decks by suits.  Each deck would
> > > > have 26 cards, corresponding to each letter in the English alphabet..
> > > > 1) Using the red deck first, use your algorithm (in the link a few
> > > > posts above) adjusted for a deck of length 26 to feed in the first
> > > > letter of the message.
> > > > 2) Take the resulting reordered red deck, and spread them out in a
> > > > line.
> > > > 3) Then place a black card under each red card according to some
> > > > formula:  For example;
> > > > i) interpret each red card as a number, (e.g. ace of hearts = 1),
> > > > ii) then apply an affine function (e.g. f = (7x)+5 mod 26) to the
> > > > number of the given red card,
> > > > iii) and then (interpreting each black card as a number in the same
> > > > range) place the black card corresponding to the output of the
> > > > function under the red card (e.g. f(1) = 12, so under the ace of
> > > > hearts place the queen of clubs, assuming the queen of clubs = 12).
> > > > 4) Then, scoop up the black deck, keeping the cards in the order they
> > > > are placed below the red cards, and use the black deck to absorb the
> > > > second letter of the message (using your algorithm).
> > > > 5) Repeat steps 2-4, except using the black deck along the top and
> > > > placing the red cards below according to the formula.
> > > > ...and continue repeating until all letters of the message are
> > > > absorbed.
>
> > > Some python code (which would tack on to your code in the link):
> > > def convert(deck):
> > >         a = len(deck)
> > >         length = a-1
> > >         tempdeck = list()
> > >         for i in range(length):
> > >                 tempdeck.append(((deck[i]*7)+5)%length)
> > >         return tempdeck
>
> > > So, hashing a message "abc":
> > > #deck = [0....25]
> > > deck = shuffle(deck, 0) #a = 0
> > > deck = convert(deck)
> > > deck = shuffle(deck, 1) #b=1
> > > deck = convert(deck)
> > > deck = shuffle(deck, 2) #c=2
> > > deck = convert(deck)
> > > #and the digest:
> > > print(deck)
>
> > > (Note for testing with shorter decks: while the above 'convert'
> > > function will work for any length of deck, the chosen affine function
> > > (y = 7x+5 mod no. of cards in deck) is intended for decks of 26 cards
> > > -- it might not be appropriate for other deck lengths)
>
> > Oops, function should read:
>
> > def convert(deck):
> >         length = len(deck)
> >         tempdeck = list()
> >         for i in range(length):
> >                 tempdeck.append(((deck[i]*7)+5)%length)
> >         return tempdeck
>
> After some testing, this does not substantially increase the avalanche
> rate, and we still end up with digests for short messages with too
> much order.  For instance, hash of message 3,9,15:
> [16, 21, 0, 5, 10, 15, 20, 14, 25, 4, 9, 19, 13, 24, 3, 8, 18, 6, 23,
> 2, 7, 12, 17, 22, 1, 11]
> And hash of message 3,8,15:
> [21, 6, 0, 5, 10, 15, 20, 14, 25, 4, 9, 19, 16, 24, 3, 8, 13, 18, 23,
> 2, 7, 12, 17, 22, 1, 11]
> That is clearly unacceptable...
>
> Perhaps my idea can still work, but (as described in the addendum to
> my post) by using one out of 26 different affine functions for the
> conversion step, as selected by the latest letter absorbed.  I am
> still learning python (I started yesterday), so I am not sure how to
> do that.  Also, it may be too complicated to efficiently do by hand.
> Maaartin's idea(s) may be more workable...

All right, so I've figured out (I think) how to do this in python.
Though it's not exactly elegant:

def superconvert(deck):
length = len(deck)
tempdeck = list()
if message[0] == 0:
for i in range(length):
tempdeck.append(((deck[i]*5)+1)%length)
return tempdeck
elif message[0] == 1:
for i in range(length):
tempdeck.append(((deck[i]*5)+9)%length)
return tempdeck
elif message[0] == 2:
for i in range(length):
tempdeck.append(((deck[i]*5)+10)%length)
return tempdeck
elif message[0] == 3:
for i in range(length):
tempdeck.append(((deck[i]*5)+23)%length)
return tempdeck
elif message[0] == 4:
for i in range(length):
tempdeck.append(((deck[i]*5)+3)%length)
return tempdeck
elif message[0] == 5:
for i in range(length):
tempdeck.append(((deck[i]*7)+5)%length)
return tempdeck
elif message[0] == 6:
for i in range(length):
tempdeck.append(((deck[i]*7)+11)%length)
return tempdeck
elif message[0] == 7:
for i in range(length):
tempdeck.append(((deck[i]*7)+24)%length)
return tempdeck
elif message[0] == 8:
for i in range(length):
tempdeck.append(((deck[i]*7)+18)%length)
return tempdeck
elif message[0] == 9:
for i in range(length):
tempdeck.append(((deck[i]*11)+2)%length)
return tempdeck
elif message[0] == 10:
for i in range(length):
tempdeck.append(((deck[i]*11)+6)%length)
return tempdeck
elif message[0] == 11:
for i in range(length):
tempdeck.append(((deck[i]*11)+13)%length)
return tempdeck
elif message[0] == 12:
for i in range(length):
tempdeck.append(((deck[i]*11)+18)%length)
return tempdeck
elif message[0] == 13:
for i in range(length):
tempdeck.append(((deck[i]*11)+23)%length)
return tempdeck
elif message[0] == 14:
for i in range(length):
tempdeck.append(((deck[i]*7)+1)%length)
return tempdeck
elif message[0] == 15:
for i in range(length):
tempdeck.append(((deck[i]*7)+14)%length)
return tempdeck
elif message[0] == 16:
for i in range(length):
tempdeck.append(((deck[i]*23)+9)%length)
return tempdeck
elif message[0] == 17:
for i in range(length):
tempdeck.append(((deck[i]*23)+16)%length)
return tempdeck
elif message[0] == 18:
for i in range(length):
tempdeck.append(((deck[i]*23)+19)%length)
return tempdeck
elif message[0] == 19:
for i in range(length):
tempdeck.append(((deck[i]*23)+25)%length)
return tempdeck
elif message[0] == 20:
for i in range(length):
tempdeck.append(((deck[i]*17)+3)%length)
return tempdeck
elif message[0] == 21:
for i in range(length):
tempdeck.append(((deck[i]*17)+7)%length)
return tempdeck
elif message[0] == 22:
for i in range(length):
tempdeck.append(((deck[i]*17)+11)%length)
return tempdeck
elif message[0] == 23:
for i in range(length):
tempdeck.append(((deck[i]*17)+20)%length)
return tempdeck
elif message[0] == 24:
for i in range(length):
tempdeck.append(((deck[i]*17)+15)%length)
return tempdeck
else:
for i in range(length):
tempdeck.append(((deck[i]*23)+1)%length)
return tempdeck

#So the main hash function is:
message = [3,9,15]
deck = [0...25]
for i in range(len(message)):
deck = shuffle(deck, message[0])
deck = superconvert(deck)
del message[0]

Some results....
Digest of message 3,9,15:
[16, 5, 0, 21, 11, 19, 6, 1, 22, 17, 12, 7, 2, 23, 18, 13, 8, 3, 24,
14, 15, 9, 4, 25, 20, 10]
Digest of message 3,8,15:
[12, 22, 7, 18, 3, 14, 25, 10, 21, 6, 17, 2, 13, 24, 9, 20, 5, 16, 1,
23, 15, 8, 0, 19, 4, 11]
Digest of message 3,9,17:
[16, 17, 7, 24, 15, 6, 23, 14, 4, 5, 22, 13, 21, 25, 12, 3, 20, 11, 2,
19, 10, 1, 18, 9, 0, 8]

So this seems to produce a good mixing. On the other hand, it appears
that the selection of affine functions (which I did very quickly and
randomly) may need to be done more carefully, as the interaction
between poorly chosen functions can produce some odd results. e.g.:

Digest of 3,9,16:
[11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 25, 24, 23, 22, 20, 13, 19, 15,
18, 17, 16, 14, 21, 12]

Given this oddity (which seems to indicate a deeper vulnerability),
and the difficulty that all this would entail if done by hand, I don't
think this is a viable addition to your hash-by-hand algorithm. Try
Maaartin's approach(es) instead.
From: Maaartin on
On Apr 27, 2:38 am, "J.D." <degolyer...(a)yahoo.com> wrote:
> Given this oddity (which seems to indicate a deeper vulnerability),
> and the difficulty that all this would entail if done by hand, I don't
> think this is a viable addition to your hash-by-hand algorithm. Try
> Maaartin's approach(es) instead.

It looks like my ideas don't perform well. Especially the "idea for
the data extraction" seems to make no good - it loves to produce short
cycles. I think there's a remedy.

Below my program but I'm a very beginner in python, too:

#creates a deck of n cards in each of 4 shapes
def new_deck(n):
deck = [];
for col in ["c", "d", "h", "s"]:
for n in range(n):
deck = deck + [col + str(n)];
return deck;

#return the numerical value of a card, i.e., a number from 0..12
def card_to_number(card):
return int(card[1:]);

#return true iff the card is black, i.e., for clubs and spades
def card_is_black(card):
c = card[0]
return (c=='c') | (c=='s')

def card_to_letter(card):
c = card[0]
high = 13 if (c=='h') | (c=='s') else 0;
low = card_to_number(card)
return chr(ord('a') + high + low);

def two_color_shuffle(deck):
i = 0
col = card_is_black(deck[0])
while card_is_black(deck[i])==col: i += 1
while card_is_black(deck[i])!=col: i += 1
return deck[i:] + deck[:i]

def four_shapes_shuffle(deck):
i = 0
map = {"c" : False, "d" : False, "h" : False, "s" : False}
found = 0;
while True:
c = deck[i][0]
i += 1
#print i, c, map[c], found
if not map[c]:
map[c] = True
found += 1
if found==4: break
return deck[i:] + deck[:i]


#return a shuffled deck according to "idea for the data extraction";
the output is the last card
#sometimes it suffers from short periods!!!
def output_shuffle(deck):
first = deck[0];
result = deck[1:];
count = card_to_number(deck[0]) + 1 # assuming stack is long enough
#print "<" + str(count) + ">";
#print "=>" + deck[count-1];
return deck[count:] + deck[0:count];

#nearly the original "http://brianpmearns.com/bpm/shufflehash"
def shuffle(deck, letter):
for i in range(len(deck)-1):
if card_to_letter(deck[i]) == letter:
d = deck[i+1]
a = deck[0:i]
b = deck[i+2:]
return b + [deck[i]] + a + [d]
#Edge case, the card we want is the last one.
d = deck[0]
a = deck[1:-1]
return [deck[len(deck)-1]] + a + [d]

### DEMO

def print_deck(prefix, deck):
result = ""
for card in deck: result += " " + card
print prefix + result[1:]

deck = new_deck(13)
print_deck("init ", deck)

print "\n", "input_shuffle";
for letter in "aeiouy":
deck = shuffle(deck, letter)
print_deck(letter + " => ", deck)

print "\n", "output_shuffle"
for n in range(8):
deck = output_shuffle(deck)
print_deck(deck[len(deck)-1] + " <= ", deck)


print "\n", "two_color_shuffle"
for n in range(2):
deck = two_color_shuffle(deck)
print_deck("TC ", deck)

print "\n", "four_shapes_shuffle"
for n in range(2):
deck = four_shapes_shuffle(deck)
print_deck("FS ", deck)
From: J.D. on
On Apr 26, 9:58 pm, Maaartin <grajc...(a)seznam.cz> wrote:
> On Apr 27, 2:38 am, "J.D." <degolyer...(a)yahoo.com> wrote:
>
> > Given this oddity (which seems to indicate a deeper vulnerability),
> > and the difficulty that all this would entail if done by hand, I don't
> > think this is a viable addition to your hash-by-hand algorithm.  Try
> > Maaartin's approach(es) instead.
>
> It looks like my ideas don't perform well. Especially the "idea for
> the data extraction" seems to make no good - it loves to produce short
> cycles. I think there's a remedy.
>
> Below my program but I'm a very beginner in python, too:
>
> #creates a deck of n cards in each of 4 shapes
> def new_deck(n):
>         deck = [];
>         for col in ["c", "d", "h", "s"]:
>                 for n in range(n):
>                         deck = deck + [col + str(n)];
>         return deck;
>
> #return the numerical value of a card, i.e., a number from 0..12
> def card_to_number(card):
>         return int(card[1:]);
>
> #return true iff the card is black, i.e., for clubs and spades
> def card_is_black(card):
>         c = card[0]
>         return (c=='c') | (c=='s')
>
> def card_to_letter(card):
>         c = card[0]
>         high = 13 if (c=='h') | (c=='s') else 0;
>         low = card_to_number(card)
>         return chr(ord('a') + high + low);
>
> def two_color_shuffle(deck):
>         i = 0
>         col = card_is_black(deck[0])
>         while card_is_black(deck[i])==col: i += 1
>         while card_is_black(deck[i])!=col: i += 1
>         return deck[i:] + deck[:i]
>
> def four_shapes_shuffle(deck):
>         i = 0
>         map = {"c" : False, "d" : False, "h" : False, "s" : False}
>         found = 0;
>         while True:
>                 c = deck[i][0]
>                 i += 1
>                 #print i, c, map[c], found
>                 if not map[c]:
>                         map[c] = True
>                         found += 1
>                         if found==4: break
>         return deck[i:] + deck[:i]
>
> #return a shuffled deck according to "idea for the data extraction";
> the output is the last card
> #sometimes it suffers from short periods!!!
> def output_shuffle(deck):
>         first = deck[0];
>         result = deck[1:];
>         count = card_to_number(deck[0]) + 1 # assuming stack is long enough
>         #print "<" + str(count) + ">";
>         #print "=>" + deck[count-1];
>         return deck[count:] + deck[0:count];
>
> #nearly the original "http://brianpmearns.com/bpm/shufflehash"
> def shuffle(deck, letter):
>         for i in range(len(deck)-1):
>                 if card_to_letter(deck[i]) == letter:
>                         d = deck[i+1]
>                         a = deck[0:i]
>                         b = deck[i+2:]
>                         return b + [deck[i]] + a + [d]
>         #Edge case, the card we want is the last one.
>         d = deck[0]
>         a = deck[1:-1]
>         return [deck[len(deck)-1]] + a + [d]
>
> ### DEMO
>
> def print_deck(prefix, deck):
>         result = ""
>         for card in deck: result += " " + card
>         print prefix + result[1:]
>
> deck = new_deck(13)
> print_deck("init  ", deck)
>
> print "\n", "input_shuffle";
> for letter in "aeiouy":
>         deck = shuffle(deck, letter)
>         print_deck(letter + "  => ", deck)
>
> print "\n", "output_shuffle"
> for n in range(8):
>         deck = output_shuffle(deck)
>         print_deck(deck[len(deck)-1] + " <= ", deck)
>
> print "\n", "two_color_shuffle"
> for n in range(2):
>         deck = two_color_shuffle(deck)
>         print_deck("TC    ", deck)
>
> print "\n", "four_shapes_shuffle"
> for n in range(2):
>         deck = four_shapes_shuffle(deck)
>         print_deck("FS    ", deck)

Jesus, Maaartin. Isn't it like 4 AM where you are? Go to sleep!
From: bmearns on
Maaartin and J.D., thanks so much for all the great work on this. I
really appreciate all the effort and feedback. It's getting a bit late
here, as well, so I'll take another look through your suggestions
tomorrow.

More responses below. =)


On Apr 26, 5:56 pm, "J.D." <degolyer...(a)yahoo.com> wrote:
> On Apr 26, 4:39 pm, Maaartin <grajc...(a)seznam.cz> wrote:
>
> > On Apr 26, 7:41 pm, bmearns <mearn...(a)gmail.com> wrote:
> > What I liked on your former scheme (and miss now) was the use of card
> > color - this is quite easy and could lead to good mixing. Would you
> > like something like this?
>
> Indeed.  Perhaps you (brian) could improve the mix rate by doing
> something like the following:
>
> Split the deck into a red and a black decks by suits.  Each deck would
> have 26 cards, corresponding to each letter in the English alphabet.
> 1) Using the red deck first, use your algorithm (in the link a few
> posts above) adjusted for a deck of length 26 to feed in the first
> letter of the message.
> 2) Take the resulting reordered red deck, and spread them out in a
> line.
> 3) Then place a black card under each red card according to some
> formula:  For example;
> i) interpret each red card as a number, (e.g. ace of hearts = 1),
> ii) then apply an affine function (e.g. f = (7x)+5 mod 26) to the
> number of the given red card,
> iii) and then (interpreting each black card as a number in the same
> range) place the black card corresponding to the output of the
> function under the red card (e.g. f(1) = 12, so under the ace of
> hearts place the queen of clubs, assuming the queen of clubs = 12).
> 4) Then, scoop up the black deck, keeping the cards in the order they
> are placed below the red cards, and use the black deck to absorb the
> second letter of the message (using your algorithm).
> 5) Repeat steps 2-4, except using the black deck along the top and
> placing the red cards below according to the formula.
> ...and continue repeating until all letters of the message are
> absorbed.

I had considered something vaguely (/vaguely/) similar, especially
with my first algorithm (with the two rows). Essentially, I was
thinking of using the top row as the key for a transposition (sorry,
I'm showing my affinity for classic ciphers, but I guess that's sort
of appropriate in this case). In other words, I would reorder the
bottom row by virtually sorting the top row.
E.g.,
T: 3 5 1 2 4 0
B: 5 3 2 0 1 4
So the bottom would be reordered: 4 2 0 5 1 3

However, after the rather disastrous results of actually trying to
perform my first algorithm, I'm very weary of over complicating it. I
have no doubt that your suggestions would greatly improve the security
of the algorithm, but it doesn't seem very feasible to perform by
hand. Multiplying and taking mod 26 is going to slow things way down,
and is going to be pretty error prone I think.

But I think I did come up with a reasonable way to significantly
improve the security. The algorithm is exactly the same, but after
feeding in the entire input, I re-hash the resulting deck. For
instance, I cut a deck in two (Hearts and Clubs, Spades and Diamonds),
feed my input into one of them. Then, I feed all 26 cards from that
deck into the second deck, and I can take my output from that. I /
think/ that will significantly obscure any sequences, even for fairly
short inputs. And it's pretty reasonable to do since the algorithm is
so quick. So this is really just applying the concept of iterations.

I'm thinking of some tests that I'll hopefully implement and run
tomorrow. I'm going to step through each character in the resulting
deck and see how many of them make a two-card sequence with the next
one. I /think/ that a random permutation will average 1 two-card
sequence, but I'll include this in my testing to see if it's true.
With this test, I can see how many inputs are required for the average
number of two-card sequences per permutation to be the same as for
random permutations. I'll try it with just a straight application, to
see if a reasonable number of inputs can still reach the random-like
threshold, then I'll try it with my above idea (a second iteration) to
see if it brings the minimum number of inputs down at all.

[snip]
>
> Addendum -- if this isn't too complicated already, you could have 26
> _different_ affine functions for step 3, one per letter, and use the
> function A when the message letter just absorbed by the hash was 'a'
> and function B when the letter was 'b', and so on.

I'm afraid it might be too complicated already =).

Thanks, looking forward to more feedback!
-Brian
First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Prev: Dynamic Hill cipher
Next: PE Scrambler