{
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.