{ Visit www.counterpane.com/solitaire.html for details! solitaire concept by Bruce Schneier (counterpane) programming in Pascal by Dave Boschma (Borland TP7 used for testing) } program solitair; uses dos,crt; const JokerA = 53; JokerB = 54; var deck : array[1..54] of shortint; keystream : array[1..255] of shortint; message : string[255]; key : string[255]; function mod26 (input : shortint) : shortint; begin repeat if (input < 1) then input := input + 26; if (input > 26) then input := input - 26; until (input >= 1) and (input <= 26); mod26 := input; end; function char2sint(ch : char) : shortint; begin if (upcase(ch) >= upcase('A')) and (upcase(ch) <= upcase('Z')) then char2sint := ord(upcase(ch))-ord(upcase('A'))+1 else if (ch = ' ') then char2sint := 32 else begin writeln('Error! A non-letter was found: '+ch); halt(1); end; end; function sint2char(input : shortint) : char; begin if (input < 1) or (input > 26) then input := mod26(input); sint2char := chr(ord('A') + input - 1); end; procedure movedown(card : shortint); var place : shortint; dumpint : shortint; begin {step 1: find card} place := 1; while (deck[place] <> card) do inc(place); if place = 54 then {step 2a: card is last card: place behind first} begin for dumpint := 53 downto 2 do deck[dumpint+1] := deck[dumpint]; deck[2] := card; end {step 2b: card is not last card: move card down one card} else begin deck[place] := deck[place+1]; deck[place+1] := card; end; end; procedure swap1with3; var copydeck : array[1..54] of shortint; placejoker1, placejoker2 : shortint; movecount : shortint; copycount : shortint; begin {step 3a: find first joker (JokerA or JokerB)} placejoker1 := 1; while (deck[placejoker1] < JokerA) do inc(placejoker1); {step 3b: find second joker} placejoker2 := placejoker1 + 1; while (deck[placejoker2] < JokerA) do inc(placejoker2); {step 3c: make exact copy of deck} for copycount := 1 to 54 do copydeck[copycount] := deck[copycount]; copycount := 1; {step 3d: move all cards from section3 to front of deck} if placejoker2 < 54 then for movecount := (placejoker2+1) to 54 do begin deck[copycount] := copydeck[movecount]; inc(copycount); end; {step 3e: move all cards from section2 behind section3} for movecount := placejoker1 to placejoker2 do begin deck[copycount] := copydeck[movecount]; inc(copycount); end; {step 3f: move all cards from section1 behind section2} if placejoker1 > 1 then for movecount := 1 to (placejoker1-1) do begin deck[copycount] := copydeck[movecount]; inc(copycount); end; end; procedure printdeck; var count : shortint; begin for count := 1 to 54 do case deck[count] of 1..13: write('C'); 14..26: write('D'); 27..39: write('H'); 40..52: write('S'); 53..54: write('J'); else write('X'); end; writeln; for count := 1 to 54 do case deck[count] of 1..52: case (deck[count] mod 13) of 0: write('K'); 1: write('A'); 2..9: write((deck[count] mod 13)); 10: write('0'); 11: write('J'); 12: write('Q'); end; 53 : write('a'); 54 : write('b'); else write('X'); end; writeln; end; procedure countcut(count : shortint); var copydeck : array[1..54] of shortint; movecount : shortint; copycount : shortint; begin {step 4a: make exact copy of deck} for copycount := 1 to 54 do copydeck[copycount] := deck[copycount]; copycount := 1; {step 4b: move all cards from section2 to front of deck} for movecount := (count+1) to 53 do begin deck[copycount] := copydeck[movecount]; inc(copycount); end; {step 4c: move all cards from section1 behind section2} for movecount := 1 to count do begin deck[copycount] := copydeck[movecount]; inc(copycount); end; end; procedure keydeck(passphrase : string); var passcount : shortint; begin if length(passphrase) < 1 then exit; for passcount := 1 to length(passphrase) do begin {step 1: move Joker A one cards down} movedown(JokerA); {step 2: move Joker B two cards down} movedown(JokerB); movedown(JokerB); {step 3: swap section 1 and section 3} swap1with3; {step 4: count cut deck and place before last card using last card} if (deck[54] < JokerA) then countcut(deck[54]) else countcut(53); {if lastcard is a joker} {step 5: count cut deck and place before last card using passphrase} countcut(char2sint(passphrase[passcount])); end; end; procedure initdeck; var count: shortint; begin for count := 1 to 54 do deck[count] := count; end; procedure makekeystream(count :integer); var streamcount : integer; found : integer; begin streamcount := 1; write('Keystream: '); repeat {step 1: move Joker A one cards down} movedown(JokerA); {step 2: move Joker B two cards down} movedown(JokerB); movedown(JokerB); {step 3: swap section 1 and section 3} swap1with3; {step 4: count cut deck and place before last card using last cards value} if (deck[54] < JokerA) then countcut(deck[54]) else countcut(53); {if lastcard is a joker} {step 5: find next key by counting from top using value of top card} if (deck[1] < JokerA) then found := deck[deck[1]+1] else found := deck[54]; {if topcard is a joker} case found of 1..52: begin write(found); keystream[streamcount] := found; inc(streamcount); end; 53..54: write('(53)'); end; write(' '); until streamcount > count; writeln; end; function encrypt(originalmsg : string): string; var dumpstr,msg : string[255]; msgcount : integer; msgarr : array[1..255] of shortint; begin {step 1: eliminate spaces and find nonletters} dumpstr := ''; msg := originalmsg; for msgcount := 1 to length(msg) do case upcase(msg[msgcount]) of 'A'..'Z': dumpstr := dumpstr + upcase(msg[msgcount]); else; {do nothing: invallid character} end; msg := dumpstr; {step 2: make length of message a multiple of five} case (length(msg) mod 5) of 0: ; {do nothing} 1: msg := msg + 'XXXX'; {add four extra character} 2: msg := msg + 'XXX'; {add three extra character} 3: msg := msg + 'XX'; {add two extra character} 4: msg := msg + 'X'; {add one extra character} end; {step 3: convert to an array of numbers} for msgcount := 1 to length(msg) do msgarr[msgcount] := char2sint(msg[msgcount]); {step 4: create keydeck} initdeck; keydeck(key); writeln; writeln('Deck:'); printdeck; makekeystream(length(msg)); {step 5: encrypt!} for msgcount := 1 to length(msg) do msgarr[msgcount] := msgarr[msgcount]+keystream[msgcount]; {step 6: convert encrypted array to encrypte string} for msgcount := 1 to length(msg) do msg[msgcount] := sint2char(msgarr[msgcount]); {step 7: make blocks of 5 characters} dumpstr := ''; for msgcount := 1 to length(msg) do if ((msgcount mod 5) = 0) then dumpstr := dumpstr + msg[msgcount] + ' ' else dumpstr := dumpstr + msg[msgcount]; encrypt := dumpstr; end; function decrypt(originalmsg : string): string; var dumpstr,msg : string[255]; msgcount : integer; msgarr : array[1..255] of shortint; begin {step 1: eliminate spaces} dumpstr := ''; msg := originalmsg; for msgcount := 1 to length(msg) do case upcase(msg[msgcount]) of 'A'..'Z': dumpstr := dumpstr + upcase(msg[msgcount]); else; {do nothing: invallid character} end; msg := dumpstr; {step 2: convert to an array of numbers} for msgcount := 1 to length(msg) do msgarr[msgcount] := char2sint(msg[msgcount]); {step 3: create a keydeck} initdeck; keydeck(key); writeln; writeln('Deck:'); printdeck; makekeystream(length(msg)); {step 4: decrypt!} for msgcount := 1 to length(msg) do msgarr[msgcount] := msgarr[msgcount]-keystream[msgcount]; {step 5: convert encrypted array to encrypte string} for msgcount := 1 to length(msg) do msg[msgcount] := sint2char(msgarr[msgcount]); {step 6: make blocks of 5 characters} dumpstr := ''; for msgcount := 1 to length(msg) do if ((msgcount mod 5) = 0) then dumpstr := dumpstr + msg[msgcount] + ' ' else dumpstr := dumpstr + msg[msgcount]; decrypt := dumpstr; end; begin clrscr; writeln('Solitaire Encryption/Decryption v1.0'); writeln; message := 'a sample message to demonstrate the algorithm of Solitaire'; key := 'Cryptonomicon'; {Leave empty for null-deck} writeln('Original message: '+message); writeln('Key: '+key); message := encrypt(message); writeln('Encrypted as: '+message); message := decrypt(message); writeln('Decrypted as: '+message); end.