Solitaire Cipher

January 18, 2011

Today’s exercise is straight forward but tedious; I will never admit the number of off-by-one errors I made while writing it. We will represent cards by the numbers 1 to 54, in bridge order, and letters by the numbers 1 to 26; it might be easier in both cases to use zero-based indices, because the modular arithmetic is more natural, but we will retain the conventions used by Schneier. A deck is a list of fifty-four card indices in positions 0 to 53. The function that converts a card number to its name is not needed elsewhere, but is given below because it is useful during debugging:

(define (name idx)
  (define (pips idx)
    (case (modulo (- idx 1) 13)
      ((0) "ace") ((1) "deuce") ((2) "trey")
      ((3) "four") ((4) "five") ((5) "six")
      ((6) "seven") ((7) "eight") ((8) "nine")
      ((9) "ten") ((10) "jack") ((11) "queen")
      ((12) "king")))
  (define (suit idx)
    (case (quotient (- idx 1) 13)
      ((0) "clubs") ((1) "diamonds")
      ((2) "hearts") ((3) "spades")))
  (if (= idx 53) "little joker"
    (if (= idx 54) "big joker"
      (string-append (pips idx) " of " (suit idx)))))

The three functions shown below initialize a deck in bridge order, look up the value (card number) of a card at a particular deck position, and find the index in the deck of a given card number:

(define (init-deck) (range 1 55))

(define value list-ref) ; 1 <= value <= 54

(define (index deck val) ; 0 <= index <= 53
  (let loop ((deck deck) (idx 0))
    (if (= (car deck) val) idx
      (loop (cdr deck) (+ idx 1)))))

The keystream generator takes a deck and produces a new deck in four steps. There is nothing hard about these functions, but all of the + 1 operations and the counts of the various deck positions are easy to get wrong:

(define (down-one deck idx)
  (if (= idx 53)
      (append (take 1 deck)
              (list (value deck 53))
              (take 52 (drop 1 deck)))
      (append (take idx deck)
              (list (value deck (+ idx 1)))
              (list (value deck idx))
              (drop (+ idx 2) deck))))

(define (down-two deck idx)
  (cond ((= idx 53)
          (append (take 2 deck)
                  (list (value deck 53))
                  (take 51 (drop 2 deck))))
        ((= idx 52)
          (append (take 1 deck)
                  (list (value deck 52))
                  (take 51 (drop 1 deck))
                  (list (value deck 53))))
        (else (append (take idx deck)
                      (take 2 (drop (+ idx 1) deck))
                      (list (value deck idx))
                      (drop (+ idx 3) deck)))))

(define (triple-cut deck)
  (let* ((j1 (index deck 53))
         (j2 (index deck 54))
         (lo (min j1 j2))
         (hi (max j1 j2)))
    (append (drop (+ hi 1) deck)
            (drop lo (take (+ hi 1) deck))
            (take lo deck))))

(define (count-cut deck idx)
  (append (take (- 53 idx)
            (drop idx deck))
          (take idx deck)
          (list (value deck 53))))

(define (next-deck deck)
  (let* ((d1 (down-one deck (index deck 53)))
         (d2 (down-two d1 (index d1 54)))
         (d3 (triple-cut d2)))
    (count-cut d3 (min (value d3 53) 53))))

The key is pointed to by the top card in the deck. Either joker is represented as 53. If the key card is in the range 27 to 52, it is reduced by 26:

(define (card deck)
  (let ((c (value deck (min (value deck 0) 53))))
    (if (< 26 c 53) (- c 26) c)))

The procedure for keying the deck is given below. Schneier’s description of the operation is confusing. The thing to remember is that the deck is manipulated one time more than the number of characters in the key:

(define (key-deck key)
  (let loop ((ks (prep key)) (deck (next-deck (init-deck))))
    (if (null? ks) deck
      (loop (cdr ks) (next-deck (count-cut deck (numb (car ks))))))))

Given what we already have, encrypt and decrypt are simple. The keystream and input text are processed in parallel, except that an extra element of the keystream is generated whenever a joker appears. The arithmetic to add and subtract letters is computed by the k and p variables:

(define (encrypt key plain-text)
  (let* ((p (prep plain-text)) (len (modulo (length p) 5))
         (ps (append p (make-list (if (zero? len) 0 (- 5 len)) #\X))))
    (let loop ((ps ps) (deck (key-deck key)) (cs '()))
      (if (null? ps) (list->string (fives (reverse cs)))
        (if (< 52 (card deck)) (loop ps (next-deck deck) cs)
          (let* ((k (+ (numb (car ps)) (card deck)))
                 (c (letr (if (< 26 k) (- k 26) k))))
            (loop (cdr ps) (next-deck deck) (cons c cs))))))))

(define (decrypt key cipher-text)
  (let loop ((cs (prep cipher-text)) (deck (key-deck key)) (ps '()))
    (if (null? cs) (list->string (reverse ps))
      (if (< 52 (card deck)) (loop cs (next-deck deck) ps)
        (let* ((k (- (numb (car cs)) (card deck)))
               (p (letr (if (< k 1) (+ k 26) k))))
          (loop (cdr cs) (next-deck deck) (cons p ps)))))))

We used several utility functions. Prep eliminates non-alphabetic characters from an input string and converts it to upper case. Numb and letr translate A=1 … Z=26. Fives breaks its input into five-character blocks:

(define (prep str)
  (map char-upcase
    (filter char-alphabetic?
      (string->list str))))

(define (numb letr) (- (char->integer letr) 64))
(define (letr numb) (integer->char (+ numb 64)))

(define (fives text)
  (if (< (length text) 6) text
    (append (take 5 text) (list #\space) (fives (drop 5 text)))))

For testing, we use sol-equal? to check if two strings are equal after converting them to upper case and eliminating non-alphabetic characters and trailing nulls, rand-string to generate random strings for keys and plaintext, and a modified version of the assert macro that uses sol-equal? instead of equal?:

(define (sol-equal? s1 s2)
  (define (del-x s)
    (list->string (reverse
      (drop-while (lambda (c) (char=? c #\X))
        (reverse s)))))
  (let ((p1 (del-x (prep s1))) (p2 (del-x (prep s2))))
    (string=? p1 p2)))

(define (rand-string n)
  (let loop ((n n) (cs '()))
    (if (zero? n) (list->string cs)
      (let* ((r (randint 27))
             (c (if (zero? r) #\space (letr r))))
        (loop (- n 1) (cons c cs))))))

Then the test performs Schneier’s three sample encryptions, and does a hundred round-trip checks through encryption and decryption with random keys and plaintext:

(define (sol-test)
  (assert (encrypt "" "AAAAAAAAAA") "EXKYI ZSGEH")
  (assert (decrypt "" "EXKYI ZSGEH") "AAAAAAAAAA")
  (assert (encrypt "FOO" "AAAAAAAAAAAAAAA") "ITHZU JIWGR FARMW")
  (assert (decrypt "FOO" "ITHZU JIWGR FARMW") "AAAAAAAAAAAAAAA")
  (assert (encrypt "CRYPTONOMICON" "SOLITAIRE") "KIRAK SFJAN")
  (assert (decrypt "CRYPTONOMICON" "KIRAK SFJAN") "SOLITAIRE")
  (do ((i 0 (+ i 1))) ((= i 100))
    (let ((key (rand-string 80)) (plain-text (rand-string 1000)))
      (assert (decrypt key (encrypt key plain-text)) plain-text))))

Here it is in action:

> (encrypt "CRYPTONOMICON" "SOLITAIRE")
"KIRAK SFJAN"

We used take, drop, drop-while, range, filter, make-list from the Standard Prelude; testing also requires randint and a modified version of assert. You can run the program at http://programmingpraxis.codepad.org/PDJGeldx.

About these ads

Pages: 1 2

9 Responses to “Solitaire Cipher”

  1. Dave Webb said

    My Python solution:

    def movedown(deck,entry,move):
        """Move a card down a number of cards in the deck"""
        newindex = deck.index(entry) + move
        if newindex >= len(deck):
            newindex -= len(deck) - 1
        deck.remove(entry)
        deck.insert(newindex,entry)
    
    def countcut(deck,count):
        """a counted cut, based on the number of the bottom card in
        the deck, moves the top count cards to just above the bottom
        card"""
        deck[-1:1] = deck[:count]
        del(deck[:count])
    
    def joker(card):
        """Is the card a joker?"""
        return card in ('A','B')
    
    def step(deck):
        """Perform a step on the deck returning the key"""
    
        #  A joker is moved one card down the deck, wrapping around the
        #  end of the deck if necessary
        movedown(deck,'A',1)
        
        # the B joker is moved two cards down the deck, again wrapping
        # around the deck if necessary.
        movedown(deck,'B',2)
    
        # a triple-cut swaps all the cards above the highest joker in the
        # deck with all the cards below the lowest joker in the deck,
        # leaving the two jokers and the cards between them in place.
        indexa = deck.index('A')
        indexb = deck.index('B')
    
        if indexb > indexa:
            topindex, botindex = indexa, indexb
        else:
            topindex, botindex = indexb, indexa
    
        deck.extend(deck[topindex:botindex + 1])
        deck.extend(deck[:topindex])
        del(deck[:botindex + 1])
    
        # Fourth, a counted cut, based on the number of the bottom card in
        # the deck. the cards are numbered 1 to 52 in bridge order with
        # ace low to king high in each suit, clubs, diamonds, hearts,
        # spades, and either joker counting as 53
        count = deck[-1]
        
        if joker(count):
            count = 53
        
        countcut(deck,count)
    
        # Then look at the top card in the deck and count down the given
        # number to determine the current key card.
        count = deck[0]
        
        if joker(count):
            count = 53
        
        return deck[count]
    
    def keydeck(deck,key):
        # For each character in the key, perform a single step then do a
        # counted cut on the number of the current character, with
        # A=1...Z=26
        for count in [ord(c) - 64 for c in key.upper()]:
            step(deck)
            countcut(deck,count)
    
    def solitare(plaintext,key="",decrypt=False):
        """encrypt and decrypt using the solitare cyper"""
    
        # Make uppercase and remove spaces
        plaintext = plaintext.upper().replace(' ','')
    
        # The plain-text has nulls (the letter X) added to the end to make
        # the message length a multiple of five
        pad = 5 - len(plaintext) % 5
        if pad != 5:
            plaintext += 'X'
    
        # Create a deck in bridge order
        deck = range(1,53) + ['A','B']
        keydeck(deck,key)
     
        ciphertext = ''
        count = 0
    
        for c in plaintext:
            # the cipher-text is split into five-character blocks for
            # convenience.
            if count == 5:
                ciphertext += ' '
                count = 0
    
            key = step(deck)
    
            # the jokers are skipped
            while joker(key):
                key = step(deck)
            
            # each character is added (for encryption) or subtracted (for
            # decryption) from the current text character, wrapping around
            # the alphabet as necessary
            e = ord(c)
    
            if (decrypt):
                e -= key
                while e < ord('A'):
                    e += 26
            else:
                e += key
                while e > ord('Z'):
                    e -= 26
                count += 1
    
            ciphertext += chr(e)
    
        return ciphertext
    
    print solitare("AAAAAAAAAA")
    print solitare("AAAAAAAAAAAAAAA","FOO")
    print solitare("SOLITAIRE","CRYPTONOMICON")
    
    print solitare("EXKYI ZSGEH",decrypt=True)
    print solitare("ITHZU JIWGR FARMW","FOO",decrypt=True)
    print solitare("KIRAK SFJAN","CRYPTONOMICON",decrypt=True)
    
  2. Dave Webb said

    Line 84 should be:

    plaintext += 'X' * pad
    

    and not:

    plaintext += 'X'
    
  3. Mike said

    My python version.

    
    from itertools import chain, ifilter, imap, izip
    
    A = 53
    B = 54
    N_CARDS = 54
    ORD_A = ord('A')
    
    class Deck(list):
        def __init__(self, key=''):
            list.__init__(self, range(1,55))
    
            self.step()
            for ch in key:
                self.counted_cut(ord(ch) - ORD_A + 1)
                self.step()
    
        def move(self, card, distance):
            fm = self.index(card)
            to = fm + distance
            if to >= N_CARDS:
                to -= (N_CARDS-1)
    
            self.insert(to, self.pop(fm))
    
        def triple_cut(self):
            a = self.index(A)
            b = self.index(B)
            s, e = (a, b+1) if a < b else (b, a+1)
            self[:] = self[e:] + self[s:e] + self[:s]
    
        def counted_cut(self, count):
            self[:] = self[count:-1] + self[:count] + self[-1:]
    
        def step(self):
            self.move(A, 1)
            self.move(B, 2)
            self.triple_cut()
            self.counted_cut(min(self[-1], A))
    
        def __iter__(self):
            while True:
                ndx = min(self[0], A)
    
                if self[ndx] < A:
                    yield self[ndx]
                        
                self.step()
    
    
    def encrypt(text, key=''):
        encode = lambda c,k: chr((ord(c) - ORD_A + k)%26 + ORD_A)
            
        padded_text = chain(text, ['X']*(-len(text)%5))
        
        cyphertext =  imap(encode, padded_text, Deck(key))
        
        groups = imap(''.join, izip(*[cyphertext]*5))
        
        return ' '.join(groups)
    
    
    def decrypt(text, key=''):
        decode = lambda c,k: chr((ord(c) - ORD_A - k)%26 + ORD_A)
    
        letters = (c for c in text if c.isupper())
    
        return ''.join(imap(decode, letters, Deck(key)))
    
    
  4. Veer said

    My attempt in scheme.

    (define (joker? n) (or (= n joker1) (= n joker2)))
    (define joker1 53)
    (define joker2 54)
    
    ;utils
    (define (find-elem i v pred?)
      (cond
        [(= i (vector-length v)) (error 'oops)]
        [(pred? (vector-ref v i)) i]
        [else (find-elem (+ i 1) v pred?)]))
    
    (define (string->nums s)
      (let ([l (string->list (string-upcase s))])
        (map (lambda (c) (mod (mod (char->integer c) 64) 26)) l)))
    
    (define (nums->string nl)
      (map (lambda (n)
             (if (= 0 n) #\Z (integer->char (+ n 64)))) nl))
    
    
    ;start
    (define (shift v index end op)
      (let ([val (vector-ref v index)])
        (let loop ([index index])
          (cond
            [(= index end) (vector-set! v index val)]
            [else (vector-set! v index (vector-ref v (op index 1))) 
                  (loop (op index 1))]))))
    
    (define (mov v i n )
      (let ([index (mod i (vector-length v))])
        (let ([end-index (mod (+ index n) (vector-length v))])
          (if (<= index end-index)
              (shift v index end-index +)
              (shift v index (+ 1 end-index) -)))))
    
    (define (count-cut v n ei)
      (let ([si (- n 1)]) 
        (let loop ([si si] [ei ei])
          (cond
            [(< si 0) v]
            [(zero? si) (shift v si ei +)]
            [else (shift v si ei +)
                  (loop (- si 1) (- ei 1))]))))
    
    (define (triple-cut v)
      (let ([joker1 (find-elem 0 v joker?)])        
        (count-cut v joker1 (- (vector-length v) 1))
          (let ([joker2 (find-elem 1 v joker?)])
            (count-cut v (+ joker2 1) (- (vector-length v) (+ 1 joker1)))
            v)))
    
    (define (move-jokers deck)
      (mov deck (find-elem 0 deck (lambda (n) (= n joker1))) 1)
      (mov deck (find-elem 0 deck (lambda (n) (= n joker2))) 2))
    
    (define (steps deck)
      (move-jokers deck)
      (triple-cut deck)
      (let ([last-num (vector-ref deck (- (vector-length deck) 1))])
        (count-cut deck (min 53 last-num) (- (vector-length deck) 2))))
    
    (define (key-deck key deck)
      (for-each (lambda (i)
                  (steps deck)
                  (count-cut deck i (- (vector-length deck) 2)))
                key))
    
    (define (enc-dec msg key op)
    
      (define (get-next deck)
        (steps deck)
        (let ([n (vector-ref deck (min 53 (vector-ref deck 0)))])
          (if (>= n 53) (get-next deck) n)))
      
      (let ([msg (string->nums msg)]
            [key (string->nums key)]
            [deck (build-vector 54 (lambda (i) (+ i 1)))])
        (key-deck key deck)
        
        (list->string
         (nums->string (map (lambda (m) (mod (op m (get-next deck)) 26)) msg)))))
            
    (define (enc msg key)
      (enc-dec msg key +))
    
    (define (dec msg key)
      (enc-dec msg key -))
      
    
    (define (test)
      (and
       (equal? (enc "AAAAAAAAAA" "" ) "EXKYIZSGEH")
       (equal? (enc "AAAAAAAAAAAAAAA" "FOO" ) "ITHZUJIWGRFARMW")
       (equal? (enc "SOLITAIRE" "CRYPTONOMICON" ) "KIRAKSFJA")
       
       (equal? (dec "EXKYIZSGEH" "") "AAAAAAAAAA")
       (equal? (dec "ITHZUJIWGRFARMW" "FOO") "AAAAAAAAAAAAAAA")
       (equal? (dec "KIRAKSFJA" "CRYPTONOMICON")  "SOLITAIRE")))
    
    
  5. Veer said

    Another attempt using pattern matching.

    (define DECK (build-list 54 (lambda (i) (+ i 1))))
    (define joker1 53)
    (define joker2 54)
    (define (joker? j) (or (equal? j joker1) (equal? j joker2)))
    
    (define (to-num c)
      (modulo
       (- (char->integer (char-upcase c)) 64) 26))
    
    (define (to-char n)
      (let ([n (modulo n 26)])
        (if (zero? n) #\Z (integer->char (+ n 64)))))
    
    (define (eq=? val1)
      (lambda (val2)
        (= val1 val2)))
    
    (define (move-joker v joker)
      (match v
        [(list a x ... (? (eq=? joker)) ) (append (list a joker) x)]
        [(list x ... (? (eq=? joker)) b y ...) (append x (list b joker) y)]))
    
    (define (move-jokers v)
      (move-joker (move-joker (move-joker v joker1) joker2) joker2))
    
    (define (triple-cut v)
      (match v
        [(list x ... (? joker? j1) y ... (? joker? j2) z ... ) (append z (list j1) y (list j2) x)]))
    
    (define (cut v n)
      (let ([val (list-ref v (- n 1))])
        (match v
          [(list x ... (? (eq=? val) b) z ... c) (append z x (list b c))])))
            
    (define (step v)
      (let ([v (triple-cut (move-jokers v))])
        (cut v (min 53 (list-ref v (- (length v) 1))))))
    
    (define (key-deck v key)
      (match key
        [(list ) v]
        [(list a b ...) (key-deck (cut (step v) (to-num a)) b) ]))
                
    (define (enc-dec msg deck op)
      (let ([deck (step deck)])
        (let ([n (list-ref deck (min 53 (first deck)))])
            (cond
              [(empty? msg) empty]
              [(joker? n) (enc-dec msg deck op)]
              [else (cons (to-char (op (to-num (first msg)) n))
                          (enc-dec (rest msg) deck op))]))))
    
    (define (enc msg key)
      (let ([deck (key-deck DECK (string->list key))])
        (list->string (enc-dec (string->list msg) deck +))))
    
    (define (dec msg key)
      (let ([deck (key-deck DECK (string->list key))])
        (list->string (enc-dec (string->list msg) deck -))))
    
  6. Here is v. 0.0.1 of my writing of this algorithm. This is not neat and pretty, merely functional. The first block of code is solitaire.py. It executes when called on the console (only tested on UNIX-like systems) and must be passed arguments to work. There is a class and list of functions in solitaire.py. Deck class handles all the stuff you’d expect a deck in solitaire to handle: pushing cards, popping cards, getting characters, advancing state, and a built-in make. The functions in solitaire.py are mainly for loading and unloading deck states and to operate the command-line form.

    Advpass option for command line operation is a demonstration of nested decks, basically a deck of decks. The deck class is flexible enough to take characters, strings, binary blobs, functions, or anything else that counts as an object as a card’s representation.

    I wrote pyrand.py to interface to /dev/urandom and serve me up tasty 32-bit unsigned random integers. Replace in code with your own solution as I suspect pyrand is the slow spot in the shuffle routines in solitaire.py…

    This is the first time this code has left this system, consider it licensed under BSD by:
    Daniel Duffield
    LucianSolaris@gmail.com

    https://www.facebook.com/LucianSolaris

    solitaire.py:

    #!/usr/bin/python2.7
    
    import pyrand
    import sys
    import os
    import re
    
    #class declaration -
    class Deck(object):
    
     #constructor
     def __init__(self):
      self.__deck=[]
      self.__numCards=0
      self.__lockState=0
      self.__debug=0
      self.__pushLockState=0
    
     def printDebug(self, inString):
      if self.__debug==1:
       print inString
    
     def pushCard(self, inTuple):
      if not self.__lockState and self.__pushLockState!=2:
       self.__pushLockState=1
       self.__numCards+=1
       self.__deck.reverse()
       self.__deck.append(inTuple)
       self.__deck.reverse()
      else:
       raise Exception('(EE): Deck feature locked in current state, check programming!')
    
     def pushAlpha(self, inObj):
      if not self.__lockState and self.__pushLockState!=1:
       self.__pushLockState=2
       self.__numCards+=1
       self.__deck.reverse()
       self.__deck.append((self.__numCards,inObj))
       self.__deck.reverse()
      else:
       raise Exception('(EE): Deck feature locked in current state, check programming!')
    
     def pushJoker(self, inJoker):
      if not self.__lockState and self.__pushLockState!=1:
       self.__pushLockState=2
       if inJoker<=2 and inJoker>=1:
        self.__deck.reverse()
        self.__deck.append((0-inJoker,None))
        self.__deck.reverse()
       else:
        raise Exception('(EE): Joker count cannot exceed 2 nor be less than 0')
      else:
       raise Exception('(EE): Deck feature locked in current state, check programming!')
    
     def shuffleVal(self, inCount):
      if not self.__lockState:
       for j in range(inCount):
        deck2=[]
        tmp=None
        offset=-1
        for i in range(len(self.__deck)):
         tmp=(i,self.__deck.pop(pyrand.urandomGet(0,2**16,1)[0]%len(self.__deck)))
         if tmp[1][1]==None:
          deck2.append((tmp[1][0],None))
          offset+=1
         else:
          deck2.append((tmp[0]-offset,tmp[1][1]))
        self.__deck=deck2
      else:
       raise Exception('(EE): Deck locked before accessing protected feature')
    
     def shuffleDeck(self, inCount):
      if not self.__lockState:
       deck2=[]
       for ij in range(inCount):
        for i in range(len(self.__deck)):
         deck2.append(self.__deck.pop(pyrand.urandomGet(0,2**16,1)[0]%len(self.__deck)))
        self.__deck=deck2
      else:
       raise Exception('(EE): Deck locked before accessing protected feature')
    
     def popState(self):
      for i in range(len(self.__deck)):
       yield (i+1,self.__deck[i][0],self.__deck[i][1])
    
     def lockDeck(self):
      try:
       if self.__deck.index((-1,None))+1 and self.__deck.index((-2,None))+1:
        self.__lockState=1
       else:
        raise Exception('(EE): Unknown Joker error')
      except:
       raise Exception('(EE): Lacking one or more Jokers at lock')
    
     def makeDeck(self, inAlphaString, inShuffleCount):
      if not self.__lockState:
       for i in inAlphaString:
        self.pushAlpha(i)
       self.pushJoker(1)
       self.pushJoker(2)
       self.shuffleDeck(inShuffleCount)
      else:
       raise Exception('(EE): Deck locked before accessing protected feature')
    
     def advanceState(self):
      if self.__lockState:
       deck2=[]
       smJokerLoc=self.__deck.index((-1,None))
       bgJokerLoc=self.__deck.index((-2,None))
       if smJokerLoc==len(self.__deck)-1:
        deck2=self.__deck[0:1]+self.__deck[smJokerLoc:smJokerLoc+1]+self.__deck[1:smJokerLoc]
       else:
        deck2=self.__deck[:smJokerLoc]+self.__deck[smJokerLoc+1:smJokerLoc+2]+self.__deck[smJokerLoc:smJokerLoc+1]+self.__deck[smJokerLoc+2:]
       self.__deck=deck2
       deck2=[]
       smJokerLoc=self.__deck.index((-1,None))
       bgJokerLoc=self.__deck.index((-2,None))
       if bgJokerLoc==len(self.__deck)-1:
        deck2=self.__deck[0:2]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[2:bgJokerLoc]
       elif bgJokerLoc==len(self.__deck)-2:
        deck2=self.__deck[0:1]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[1:bgJokerLoc]+self.__deck[bgJokerLoc+1:bgJokerLoc+2]
       else:
        deck2=self.__deck[:bgJokerLoc]+self.__deck[bgJokerLoc+1:bgJokerLoc+3]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[bgJokerLoc+3:]
       self.__deck=deck2
       deck2=[]
       smJokerLoc=self.__deck.index((-1,None))
       bgJokerLoc=self.__deck.index((-2,None))
       if smJokerLoc<bgJokerLoc:
        deck2=self.__deck[bgJokerLoc+1:]+self.__deck[smJokerLoc:smJokerLoc+1]+self.__deck[smJokerLoc+1:bgJokerLoc]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[:smJokerLoc]
        self.__deck=deck2
       else:
        tmp=smJokerLoc
        smJokerLoc=bgJokerLoc
        bgJokerLoc=tmp
        deck2=self.__deck[bgJokerLoc+1:]+self.__deck[smJokerLoc:smJokerLoc+1]+self.__deck[smJokerLoc+1:bgJokerLoc]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[:smJokerLoc]
        self.__deck=deck2
       deck2=[]
       smJokerLoc=self.__deck.index((-1,None))
       bgJokerLoc=self.__deck.index((-2,None))
       if self.__deck[len(self.__deck)-1][0]<0:
        deck2.append(self.__deck[:])
       else:
        deck2.append(self.__deck[self.__deck[len(self.__deck)-1][0]:-1]+self.__deck[:self.__deck[len(self.__deck)-1][0]]+self.__deck[-1:])
       self.__deck=deck2[0]
      else:
       raise Exception('(EE): Deck not locked before accessing protected feature')
    
     def getChars(self, inSize):
      i=0
      while i<inSize:
       self.advanceState()
       if self.__deck[0][0]<0:
        yieldChar=self.__deck[-2][1]
       else:
        yieldChar=self.__deck[self.__deck[0][0]-1][1]
       if type(yieldChar)!=type(None):
        i+=1
        yield yieldChar
    
     def getKey(self, inSize):
      outString=''
      for i in self.getChars(inSize):
       outString=outString+i
      return outString
    
    alphaDict={
    'complex':'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\'\",.<>`~[]{}/=\?+|;:-_',
    'dvkextended':'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\'\",.<>/=?+;:-_',
    'extended':'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\'\",.<>[]{}/?;:',
    'standard':'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
    'lowercase':'abcdefghijklmnopqrstuvwxyz',
    'lowercasenums':'abcdefghijklmnopqrstuvwxyz0123456789',
    'uppercase':'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
    'uppercasenums':'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
    'numbers':'0123456789',
    'solitaire':'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ',
    'stdcrypt':'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-'
    }
    
    def loadState(inDeck, inFilename):
     file=open(inFilename,'rU')
     if 'PYNTIFEX_DECK\n' not in file:
      raise Exception('(EE): Attempted to load deck file lacking magic string')
     for i in file:
      try:
       imatch=re.search('\((\d+),\s(-?\d+),\s\'(.+)\'\)', i)
       if imatch.groups()[2]=='None':
        inDeck.pushCard((int(imatch.groups()[1]),None))
       else:
        inDeck.pushCard((int(imatch.groups()[1]),imatch.groups()[2]))
      except:
       if i!='PYNTIFEX_DECK':
        raise Exception('(EE): The following line cannot be processed: '+str(i+1)+' in file: '+inFilename)
     file.close()
     inDeck.lockDeck()
    
    def saveState(inDeck, inFilename):
     outFile=open(inFilename,'w')
     outFile.truncate(0)
     outFile.write('PYNTIFEX_DECK\n')
     for i in inDeck.popState():
      if i[1]<0:
       i=(i[0],i[1],'None')
      outFile.write(str(i)+'\n')
     outFile.flush()
     outFile.close()
    
    def makeDeckFile():
     alphabet=''
     outFile=''
     shuffleCount=100
     mixValues=0
     deck=Deck()
     try:
      alphabet=sys.argv[sys.argv.index('--alphastr')+1]
     except ValueError:
      try:
       alphabet=sys.argv[sys.argv.index('--alpha')+1]
       alphabet=alphaDict.get(alphabet)
       if alphabet==None:
        print '\nBad selection on --alpha, please refer to --help'
        sys.exit(1)
      except ValueError:
       print '\nBad options, should be: <command> make [--alpha <Name> | --alphastr <String>] --outfile <PathToOutputFile> [--shuffle <Integer>] [--mix-values]'
       sys.exit(1)
     try:
      outFile=sys.argv[sys.argv.index('--outfile')+1]
      try:
       shuffleCount=int(sys.argv[sys.argv.index('--shuffle')+1])
       if shuffleCount<10:
        print '\nFor security reasons, please make --shuffle 10 or larger'
        sys.exit(1)
      except ValueError:
       pass
      if '--mix-values' in sys.argv:
       mixValues=1
     except ValueError:
      print '\nBad options, should be: <command> make [--alpha <Name> | --alphastr <String>] --outfile <PathToOutputFile> [--shuffle <Integer>] [--mix-values]'
      sys.exit(1)
     deck.makeDeck(alphabet, shuffleCount)
     if mixValues==1:
      deck.shuffleVal(shuffleCount)
     deck.lockDeck()
     try:
      saveState(deck,outFile)
     except:
      print '\nError writing file to path:'
      print outFile
      sys.exit(1)
     print '\nDeck outputted successfully to:'
     print outFile
     sys.exit(0)
    
    def printKeyString():
     inFile=''
     outFile=''
     keyString=''
     stringLength=0
     deck=Deck()
     try:
      inFile=sys.argv[sys.argv.index('--infile')+1]
      stringLength=int(sys.argv[sys.argv.index('--length')+1])
      try:
       outFile=sys.argv[sys.argv.index('--outfile')+1]
      except ValueError:
       pass
     except:
      print '\nBad options, should be: <command> key --infile <PathToFile> --length <Integer> [--outfile <PathToFile>] [--quiet]'
      sys.exit(1)
     try:
      loadState(deck,inFile)
     except:
      print '\nError opening deck file:'
      print inFile
      sys.exit(1)
     deck.lockDeck()
     print '',
     for i in deck.getChars(stringLength):
      print '\b'+i,
     if outFile=='':
      try:
       deck.saveState(inFile)
      except:
       if '--quiet' not in sys.argv:
        print '\nError saving deck file:'
        print inFile
       sys.exit(1)
     else:
      try:
       deck.saveState(outFile)
      except:
       if '--quiet' not in sys.argv:
        print '\nError saving deck file:'
        print outFile
       sys.exit(1)
     sys.exit(0)
    
    def printPassString():
     shuffleCount=100
     alphabet=''
     stringLength=0
     try:
      stringLength=int(sys.argv[sys.argv.index('--length')+1])
     except ValueError:
      '\nBad options, should be: <command> pass [--alpha <Name> | --alphastr <String>] --length <Integer> [--shuffle <Integer>]'
     deck=Deck()
     try:
      alphabet=sys.argv[sys.argv.index('--alphastr')+1]
     except ValueError:
      try:
       alphabet=sys.argv[sys.argv.index('--alpha')+1]
       alphabet=alphaDict.get(alphabet)
       if alphabet==None:
        print '\nBad selection on --alpha, please refer to --help'
        sys.exit(1)
      except ValueError:
       print '\nBad options, should be: <command> pass [--alpha <Name> | --alphastr <String>] --length <Integer> [--shuffle <Integer>]'
       sys.exit(1)
     try:
      shuffleCount=int(sys.argv[sys.argv.index('--shuffle')+1])
      if shuffleCount<100:
       print '\nFor security reasons, please make --shuffle 100 or larger'
       sys.exit(1)
     except ValueError:
      pass
     deck.makeDeck(alphabet*3, shuffleCount)
     deck.shuffleVal(shuffleCount)
     deck.lockDeck()
     for i in deck.getChars(stringLength):
      print '\b'+i,
     sys.exit(0) 
    
    def printAdvPassString():
     if '--quiet' not in sys.argv:
      print '\nUnofficial function selected.  Advpass can take a'
      print ' really long time to output.  Output latency is pro-'
      print ' portional to (2x --count) x (--shuffle) x (--alpha len)'
      print ' length having little impact...'
     shuffleCount=100
     alphabet=''
     stringLength=0
     deckCount=1
     try:
      deckCount=int(sys.argv[sys.argv.index('--count')+1])
      stringLength=int(sys.argv[sys.argv.index('--length')+1])
     except ValueError:
      print '\nBad options, should be: <command> advpass [--alpha <Name> | --alphastr <String>] --length <Integer> --count <Integer> [--shuffle <Integer>]'
      sys.exit(1)
     deck=Deck()
     try:
      alphabet=sys.argv[sys.argv.index('--alphastr')+1]
     except ValueError:
      try:
       alphabet=sys.argv[sys.argv.index('--alpha')+1]
       alphabet=alphaDict.get(alphabet)
       if alphabet==None:
        print '\nBad selection on --alpha, please refer to --help'
        sys.exit(1)
      except ValueError:
       print '\nBad options, should be: <command> pass [--alpha <Name> | --alphastr <String>] --length <Integer> [--shuffle <Integer>]'
       sys.exit(1)
     try:
      shuffleCount=int(sys.argv[sys.argv.index('--shuffle')+1])
      if shuffleCount<10:
       print '\nFor security reasons, please make --shuffle 10 or larger'
       sys.exit(1)
     except ValueError:
      pass
     if '--quiet' not in sys.argv:
      print '\n Initializing Meta-deck...\n  ',
     for i in range(deckCount):
      deck2=Deck()
      deck2.makeDeck(alphabet,shuffleCount)
      deck2.lockDeck()
      deck.pushAlpha(deck2)
     deck.pushJoker(1)
     deck.pushJoker(2)
     deck.shuffleDeck(shuffleCount)
     deck.shuffleVal(shuffleCount)
     deck.lockDeck()
     if '--quiet' not in sys.argv:
      print '\n Compiling password string...\n'
     print '',
     for j in deck.getChars(stringLength):
      for i in j.getKey(1):
       print '\b'+i,
     sys.exit(0) 
    
    #main function -
    def main():
     if '--help' in sys.argv or len(sys.argv)==1:
      print '\nOptions:'
      print ' <command> func --args args'
      print '\n Function \'make\':'
      print '  Makes a Pontifex deck.  Options are:'
      print '  --alpha String    | Pre-made alphabet to use (below)'
      print '  --alphastr String | User-provided alphabet (in single quotes, mutually exclusive with --alpha)'
      print '  --outfile Path    | Path to output deck file'
      print '  --shuffle Integer | [Optional] Shuffle count (100 Default)'
      print '  --mix-values      | [Optional] Mixes up alphabet value assignments'
      print '\n Function \'key\':'
      print '  Outputs a key string for use with a tableau.  Options are:'
      print '  --length Integer  | Output length'
      print '  --infile Path     | Deck file to use'
      print '  --outfile Path    | [Optional] Deck output path (Don\'t write endstate to infile)'
      print '                    |   (not recommended for sensitive uses)'
      print '  --quiet           | [Optional] Suppress soft errors'
      print '\n Function \'pass\':'
      print '  Outputs a complex password based on these options:'
      print '  --length Integer  | Password length'
      print '  --alpha String    | Pre-made alphabet string name (below)'
      print '  --alphastr String | User-provided alphabet (in single quotes, mutually exclusive with --alpha)'
      print '  --shuffle Integer | [Optional] Shuffle count (100 Default)'
      print '\n Function \'advpass\':'
      print '  Outputs a complex password, like pass, but with this additional option:'
      print '  --count           | Number of decks in \'meta\' deck.'
      print '\n  (advpass is more of a test than a function, therefore it\'s unsupported)'
      print '\n Available pre-made alphabets:'
      for i in alphaDict.keys():
       print '  '+i+': '+alphaDict.get(i)
     elif sys.argv[1]=='make':
      makeDeckFile()
     elif sys.argv[1]=='key':
      printKeyString()
     elif sys.argv[1]=='pass':
      printPassString()
     elif sys.argv[1]=='advpass':
      printAdvPassString()
     else:
      print '\nNo useable Options.  --help for info.'
      sys.exit(1)
    
    #launch boilerplate -
    if __name__ == '__main__':
     try:
      main()
     except KeyboardInterrupt:
      print '\n Program execution halted prematurely via keyboard (^C)'
    

    pyrand.py:

    #!/usr/bin/python2.7
    
    import os
    import struct
    import sys
    
    randomDevice=open('/dev/random','rb')
    urandomDevice=open('/dev/urandom','rb')
    UINT32_MAX=0xffffffff
    
    def randomDeviceBytes(inLength):
     return randomDevice.read(inLength)
    
    def urandomDeviceBytes(inLength):
     return urandomDevice.read(inLength)
    
    def convertBytesToInt(inBytes):
     return struct.unpack('I',inBytes)[0]
    
    def makeAcceptableInt(inLower, inUpper, inInt):
     if inLower>=0 and inUpper>inLower and inInt>=0:
      return int((((inUpper-inLower)/float(UINT32_MAX))+inLower)*inInt)
    
    def randomGet(inLower, inUpper, inCount):
     if inLower>=0 and inUpper>inLower and inCount>0:
      return [makeAcceptableInt(inLower, inUpper, convertBytesToInt(randomDeviceBytes(4))) for _ in range(inCount)]
     else:
      raise Exception('(EE): Bad arguments for randomGet()')
    
    def urandomGet(inLower, inUpper, inCount):
     if inLower>=0 and inUpper>inLower and inCount>0:
      return [makeAcceptableInt(inLower, inUpper, convertBytesToInt(urandomDeviceBytes(4))) for _ in range(inCount)]
     else:
      raise Exception('(EE): Bad arguments for urandomGet()')
    
    def main():
     args=[None]
     if len(sys.argv)==4 and long(sys.argv[1])<long(sys.argv[2]) and long(sys.argv[3])>=0:
      for i in sys.argv[1:]:
       args.append(int(i))
      print '\nurandom Output:'
      for i in urandomGet(args[1],args[2],args[3]):
       print i,
      print '\n\nrandom Output:'
      for i in randomGet(args[1],args[2],args[3]):
       print i,
      print '\n\nDebug Check Complete...'
     else:
      print '(EE): Error in arguments.  Usage: ./command Lower Upper Count'
    
    
    if __name__=='__main__':
     main()
    
  7. A bug was discovered in the functions pushCard(), pushAlpha(), and pushJoker(). They caused the deck to be loaded in the wrong order. The corrected code is below.

    solitaire.py: (v. 0.0.2)

    #!/usr/bin/python2.7
    
    import pyrand
    import sys
    import os
    import re
    
    #class declaration -
    class Deck(object):
    
     #constructor
     def __init__(self):
      self.__deck=[]
      self.__numCards=0
      self.__lockState=0
      self.__debug=0
      self.__pushLockState=0
    
     def printDebug(self, inString):
      if self.__debug==1:
       print inString
    
     def pushCard(self, inTuple):
      if not self.__lockState and self.__pushLockState!=2:
       self.__pushLockState=1
       self.__numCards+=1
       self.__deck.append(inTuple)
      else:
       raise Exception('(EE): Deck feature locked in current state, check programming!')
    
     def pushAlpha(self, inObj):
      if not self.__lockState and self.__pushLockState!=1:
       self.__pushLockState=2
       self.__numCards+=1
       self.__deck.append((self.__numCards,inObj))
      else:
       raise Exception('(EE): Deck feature locked in current state, check programming!')
    
     def pushJoker(self, inJoker):
      if not self.__lockState and self.__pushLockState!=1:
       self.__pushLockState=2
       if inJoker<=2 and inJoker>=1:
        self.__deck.append((0-inJoker,None))
       else:
        raise Exception('(EE): Joker count cannot exceed 2 nor be less than 0')
      else:
       raise Exception('(EE): Deck feature locked in current state, check programming!')
    
     def shuffleVal(self, inCount):
      if not self.__lockState:
       for j in range(inCount):
        deck2=[]
        tmp=None
        offset=-1
        for i in range(len(self.__deck)):
         tmp=(i,self.__deck.pop(pyrand.urandomGet(0,2**16,1)[0]%len(self.__deck)))
         if tmp[1][1]==None:
          deck2.append((tmp[1][0],None))
          offset+=1
         else:
          deck2.append((tmp[0]-offset,tmp[1][1]))
        self.__deck=deck2
      else:
       raise Exception('(EE): Deck locked before accessing protected feature')
    
     def shuffleDeck(self, inCount):
      if not self.__lockState:
       deck2=[]
       for ij in range(inCount):
        for i in range(len(self.__deck)):
         deck2.append(self.__deck.pop(pyrand.urandomGet(0,2**16,1)[0]%len(self.__deck)))
        self.__deck=deck2
      else:
       raise Exception('(EE): Deck locked before accessing protected feature')
    
     def popState(self):
      for i in range(len(self.__deck)):
       yield (i+1,self.__deck[i][0],self.__deck[i][1])
    
     def lockDeck(self):
      try:
       if self.__deck.index((-1,None))+1 and self.__deck.index((-2,None))+1:
        self.__lockState=1
       else:
        raise Exception('(EE): Unknown Joker error')
      except:
       raise Exception('(EE): Lacking one or more Jokers at lock')
    
     def makeDeck(self, inAlphaString, inShuffleCount):
      if not self.__lockState:
       for i in inAlphaString:
        self.pushAlpha(i)
       self.pushJoker(1)
       self.pushJoker(2)
       self.shuffleDeck(inShuffleCount)
      else:
       raise Exception('(EE): Deck locked before accessing protected feature')
    
     def advanceState(self):
      if self.__lockState:
       deck2=[]
       smJokerLoc=self.__deck.index((-1,None))
       bgJokerLoc=self.__deck.index((-2,None))
       if smJokerLoc==len(self.__deck)-1:
        deck2=self.__deck[0:1]+self.__deck[smJokerLoc:smJokerLoc+1]+self.__deck[1:smJokerLoc]
       else:
        deck2=self.__deck[:smJokerLoc]+self.__deck[smJokerLoc+1:smJokerLoc+2]+self.__deck[smJokerLoc:smJokerLoc+1]+self.__deck[smJokerLoc+2:]
       self.__deck=deck2
       deck2=[]
       smJokerLoc=self.__deck.index((-1,None))
       bgJokerLoc=self.__deck.index((-2,None))
       if bgJokerLoc==len(self.__deck)-1:
        deck2=self.__deck[0:2]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[2:bgJokerLoc]
       elif bgJokerLoc==len(self.__deck)-2:
        deck2=self.__deck[0:1]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[1:bgJokerLoc]+self.__deck[bgJokerLoc+1:bgJokerLoc+2]
       else:
        deck2=self.__deck[:bgJokerLoc]+self.__deck[bgJokerLoc+1:bgJokerLoc+3]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[bgJokerLoc+3:]
       self.__deck=deck2
       deck2=[]
       smJokerLoc=self.__deck.index((-1,None))
       bgJokerLoc=self.__deck.index((-2,None))
       if smJokerLoc<bgJokerLoc:
        deck2=self.__deck[bgJokerLoc+1:]+self.__deck[smJokerLoc:smJokerLoc+1]+self.__deck[smJokerLoc+1:bgJokerLoc]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[:smJokerLoc]
        self.__deck=deck2
       else:
        tmp=smJokerLoc
        smJokerLoc=bgJokerLoc
        bgJokerLoc=tmp
        deck2=self.__deck[bgJokerLoc+1:]+self.__deck[smJokerLoc:smJokerLoc+1]+self.__deck[smJokerLoc+1:bgJokerLoc]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[:smJokerLoc]
        self.__deck=deck2
       deck2=[]
       smJokerLoc=self.__deck.index((-1,None))
       bgJokerLoc=self.__deck.index((-2,None))
       if self.__deck[len(self.__deck)-1][0]<0:
        deck2.append(self.__deck[:])
       else:
        deck2.append(self.__deck[self.__deck[len(self.__deck)-1][0]:-1]+self.__deck[:self.__deck[len(self.__deck)-1][0]]+self.__deck[-1:])
       self.__deck=deck2[0]
      else:
       raise Exception('(EE): Deck not locked before accessing protected feature')
    
     def getChars(self, inSize):
      i=0
      while i<inSize:
       self.advanceState()
       if self.__deck[0][0]<0:
        yieldChar=self.__deck[-2][1]
       else:
        yieldChar=self.__deck[self.__deck[0][0]-1][1]
       if type(yieldChar)!=type(None):
        i+=1
        yield yieldChar
    
     def getKey(self, inSize):
      outString=''
      for i in self.getChars(inSize):
       outString=outString+i
      return outString
    
    alphaDict={
    'complex':'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\'\",.<>`~[]{}/=\?+|;:-_',
    'dvkextended':'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\'\",.<>/=?+;:-_',
    'extended':'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\'\",.<>[]{}/?;:',
    'standard':'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
    'lowercase':'abcdefghijklmnopqrstuvwxyz',
    'lowercasenums':'abcdefghijklmnopqrstuvwxyz0123456789',
    'uppercase':'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
    'uppercasenums':'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
    'numbers':'0123456789',
    'solitaire':'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ',
    'stdcrypt':'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-'
    }
    
    def loadState(inDeck, inFilename):
     file=open(inFilename,'rU')
     if 'PYNTIFEX_DECK\n' not in file:
      raise Exception('(EE): Attempted to load deck file lacking magic string')
     for i in file:
      try:
       imatch=re.search('\((\d+),\s(-?\d+),\s\'(.+)\'\)', i)
       if imatch.groups()[2]=='None':
        inDeck.pushCard((int(imatch.groups()[1]),None))
       else:
        inDeck.pushCard((int(imatch.groups()[1]),imatch.groups()[2]))
      except:
       if i!='PYNTIFEX_DECK':
        raise Exception('(EE): The following line cannot be processed: '+str(i+1)+' in file: '+inFilename)
     file.close()
     inDeck.lockDeck()
    
    def saveState(inDeck, inFilename):
     outFile=open(inFilename,'w')
     outFile.truncate(0)
     outFile.write('PYNTIFEX_DECK\n')
     for i in inDeck.popState():
      if i[1]<0:
       i=(i[0],i[1],'None')
      outFile.write(str(i)+'\n')
     outFile.flush()
     outFile.close()
    
    def makeDeckFile():
     alphabet=''
     outFile=''
     shuffleCount=100
     mixValues=0
     deck=Deck()
     try:
      alphabet=sys.argv[sys.argv.index('--alphastr')+1]
     except ValueError:
      try:
       alphabet=sys.argv[sys.argv.index('--alpha')+1]
       alphabet=alphaDict.get(alphabet)
       if alphabet==None:
        print '\nBad selection on --alpha, please refer to --help'
        sys.exit(1)
      except ValueError:
       print '\nBad options, should be: <command> make [--alpha <Name> | --alphastr <String>] --outfile <PathToOutputFile> [--shuffle <Integer>] [--mix-values]'
       sys.exit(1)
     try:
      outFile=sys.argv[sys.argv.index('--outfile')+1]
      try:
       shuffleCount=int(sys.argv[sys.argv.index('--shuffle')+1])
       if shuffleCount<10:
        print '\nFor security reasons, please make --shuffle 10 or larger'
        sys.exit(1)
      except ValueError:
       pass
      if '--mix-values' in sys.argv:
       mixValues=1
     except ValueError:
      print '\nBad options, should be: <command> make [--alpha <Name> | --alphastr <String>] --outfile <PathToOutputFile> [--shuffle <Integer>] [--mix-values]'
      sys.exit(1)
     deck.makeDeck(alphabet, shuffleCount)
     if mixValues==1:
      deck.shuffleVal(shuffleCount)
     deck.lockDeck()
     try:
      saveState(deck,outFile)
     except:
      print '\nError writing file to path:'
      print outFile
      sys.exit(1)
     print '\nDeck outputted successfully to:'
     print outFile
     sys.exit(0)
    
    def printKeyString():
     inFile=''
     outFile=''
     keyString=''
     stringLength=0
     deck=Deck()
     try:
      inFile=sys.argv[sys.argv.index('--infile')+1]
      stringLength=int(sys.argv[sys.argv.index('--length')+1])
      try:
       outFile=sys.argv[sys.argv.index('--outfile')+1]
      except ValueError:
       pass
     except:
      print '\nBad options, should be: <command> key --infile <PathToFile> --length <Integer> [--outfile <PathToFile>] [--quiet]'
      sys.exit(1)
     try:
      loadState(deck,inFile)
     except:
      print '\nError opening deck file:'
      print inFile
      sys.exit(1)
     deck.lockDeck()
     print ''
     print '',
     for i in deck.getChars(stringLength):
      print '\b'+i,
     if outFile=='':
      try:
       deck.saveState(inFile)
      except:
       if '--quiet' not in sys.argv:
        print '\nError saving deck file:'
        print inFile
       sys.exit(1)
     else:
      try:
       deck.saveState(outFile)
      except:
       if '--quiet' not in sys.argv:
        print '\nError saving deck file:'
        print outFile
       sys.exit(1)
     sys.exit(0)
    
    def printPassString():
     shuffleCount=100
     alphabet=''
     stringLength=0
     try:
      stringLength=int(sys.argv[sys.argv.index('--length')+1])
     except ValueError:
      '\nBad options, should be: <command> pass [--alpha <Name> | --alphastr <String>] --length <Integer> [--shuffle <Integer>]'
     deck=Deck()
     try:
      alphabet=sys.argv[sys.argv.index('--alphastr')+1]
     except ValueError:
      try:
       alphabet=sys.argv[sys.argv.index('--alpha')+1]
       alphabet=alphaDict.get(alphabet)
       if alphabet==None:
        print '\nBad selection on --alpha, please refer to --help'
        sys.exit(1)
      except ValueError:
       print '\nBad options, should be: <command> pass [--alpha <Name> | --alphastr <String>] --length <Integer> [--shuffle <Integer>]'
       sys.exit(1)
     try:
      shuffleCount=int(sys.argv[sys.argv.index('--shuffle')+1])
      if shuffleCount<100:
       print '\nFor security reasons, please make --shuffle 100 or larger'
       sys.exit(1)
     except ValueError:
      pass
     deck.makeDeck(alphabet*3, shuffleCount)
     deck.shuffleVal(shuffleCount)
     deck.lockDeck()
     for i in deck.getChars(stringLength):
      print '\b'+i,
     sys.exit(0) 
    
    def printAdvPassString():
     if '--quiet' not in sys.argv:
      print '\nUnofficial function selected.  Advpass can take a'
      print ' really long time to output.  Output latency is pro-'
      print ' portional to (2x --count) x (--shuffle) x (--alpha len)'
      print ' length having little impact...'
     shuffleCount=100
     alphabet=''
     stringLength=0
     deckCount=1
     try:
      deckCount=int(sys.argv[sys.argv.index('--count')+1])
      stringLength=int(sys.argv[sys.argv.index('--length')+1])
     except ValueError:
      print '\nBad options, should be: <command> advpass [--alpha <Name> | --alphastr <String>] --length <Integer> --count <Integer> [--shuffle <Integer>]'
      sys.exit(1)
     deck=Deck()
     try:
      alphabet=sys.argv[sys.argv.index('--alphastr')+1]
     except ValueError:
      try:
       alphabet=sys.argv[sys.argv.index('--alpha')+1]
       alphabet=alphaDict.get(alphabet)
       if alphabet==None:
        print '\nBad selection on --alpha, please refer to --help'
        sys.exit(1)
      except ValueError:
       print '\nBad options, should be: <command> pass [--alpha <Name> | --alphastr <String>] --length <Integer> [--shuffle <Integer>]'
       sys.exit(1)
     try:
      shuffleCount=int(sys.argv[sys.argv.index('--shuffle')+1])
      if shuffleCount<10:
       print '\nFor security reasons, please make --shuffle 10 or larger'
       sys.exit(1)
     except ValueError:
      pass
     if '--quiet' not in sys.argv:
      print '\n Initializing Meta-deck...\n  ',
     for i in range(deckCount):
      deck2=Deck()
      deck2.makeDeck(alphabet,shuffleCount)
      deck2.lockDeck()
      deck.pushAlpha(deck2)
     deck.pushJoker(1)
     deck.pushJoker(2)
     deck.shuffleDeck(shuffleCount)
     deck.shuffleVal(shuffleCount)
     deck.lockDeck()
     if '--quiet' not in sys.argv:
      print '\n Compiling password string...\n'
     print '',
     for j in deck.getChars(stringLength):
      for i in j.getKey(1):
       print '\b'+i,
     sys.exit(0) 
    
    #main function -
    def main():
     if '--help' in sys.argv or len(sys.argv)==1:
      print '\nOptions:'
      print ' <command> func --args args'
      print '\n Function \'make\':'
      print '  Makes a Pontifex deck.  Options are:'
      print '  --alpha String    | Pre-made alphabet to use (below)'
      print '  --alphastr String | User-provided alphabet (in single quotes, mutually exclusive with --alpha)'
      print '  --outfile Path    | Path to output deck file'
      print '  --shuffle Integer | [Optional] Shuffle count (100 Default)'
      print '  --mix-values      | [Optional] Mixes up alphabet value assignments'
      print '\n Function \'key\':'
      print '  Outputs a key string for use with a tableau.  Options are:'
      print '  --length Integer  | Output length'
      print '  --infile Path     | Deck file to use'
      print '  --outfile Path    | [Optional] Deck output path (Don\'t write endstate to infile)'
      print '                    |   (not recommended for sensitive uses)'
      print '  --quiet           | [Optional] Suppress soft errors'
      print '\n Function \'pass\':'
      print '  Outputs a complex password based on these options:'
      print '  --length Integer  | Password length'
      print '  --alpha String    | Pre-made alphabet string name (below)'
      print '  --alphastr String | User-provided alphabet (in single quotes, mutually exclusive with --alpha)'
      print '  --shuffle Integer | [Optional] Shuffle count (100 Default)'
      print '\n Function \'advpass\':'
      print '  Outputs a complex password, like pass, but with this additional option:'
      print '  --count           | Number of decks in \'meta\' deck.'
      print '\n  (advpass is more of a test than a function, therefore it\'s unsupported)'
      print '\n Available pre-made alphabets:'
      for i in alphaDict.keys():
       print '  '+i+': '+alphaDict.get(i)
     elif sys.argv[1]=='make':
      makeDeckFile()
     elif sys.argv[1]=='key':
      printKeyString()
     elif sys.argv[1]=='pass':
      printPassString()
     elif sys.argv[1]=='advpass':
      printAdvPassString()
     else:
      print '\nNo useable Options.  --help for info.'
      sys.exit(1)
    
    #launch boilerplate -
    if __name__ == '__main__':
     try:
      main()
     except KeyboardInterrupt:
      print '\n Program execution halted prematurely via keyboard (^C)'
    
  8. A couple more bugs squashed, one involving returning the last card counted instead of the next one after the last one counted. Now operates to real-life version.

    Build deck file that resembles the standard hand-held algorithm by inputting:

    ./solitaire.py make --alpha solitaire --outfile ../sandbox/soldeck001.deck --shuffle 100
    

    Generate a key with:

    ./solitaire.py key --infile soldeck001.deck --length 1000 --outfile soldeck001.out.deck
    

    Then open soldeck001.deck in a text editor. The second and third values are the count values (the number the card represents, used for counting) and face values (characters). Model your real deck after this one and check for yourself!

    I’m sure many of those who read this thread have heard of Pontifex, or the Solitaire cipher. This is my attempt to implement a general form of that algorithm which can accept as face values any type of data, including more decks. This python module even allows for a deck of decks! As written, it is meant to operate just like the card algorithm, so if you wanted to you could print up code books of what your ‘field agents’ are carrying decks of cards to generate! This module also has two password generating routines, one which uses the solitaire deck simply, and another that is a play on the deck of decks idea.

    Here is v. 0.0.3 of my writing of this algorithm. This is not neat and pretty, merely functional. The first block of code is solitaire.py. It executes when called on the console (only tested on UNIX-like systems) and must be passed arguments to work. There is a class and list of functions in solitaire.py. Deck class handles all the stuff you’d expect a deck in solitaire to handle: pushing cards, popping cards, getting characters, advancing state, and a built-in make. The functions in solitaire.py are mainly for loading and unloading deck states and to operate the command-line form.

    Advpass option for command line operation is a demonstration of nested decks, basically a deck of decks. The deck class is flexible enough to take characters, strings, binary blobs, functions, or anything else that counts as an object as a card’s representation.

    I wrote pyrand.py to interface to /dev/urandom and serve me up tasty 32-bit unsigned random integers. Replace in code with your own solution as I suspect pyrand is the slow spot in the shuffle routines in solitaire.py.

    I decided to write this because I wanted to start writing python code where I implement quantum computing proof cryptographic algorithms and protocols. I think OpenPGP is sorely lacking in post-quantum computing foresight at this point and warez has to get out there to handle this threat.

    Yea yea, I know it’s sloppy code. I wrote it to work, and I may work on cleaning it up and making it faster and more general. In the event I don’t, here’s a little present for y’all:

    This is the first time this code has left this system, consider it licensed under BSD by:
    Daniel Duffield
    LucianSolaris@gmail.com

    https://www.facebook.com/LucianSolaris

    Output of `./solitaire.py –help`:

    Options:
     <command> func --args args
    
     Function 'make':
      Makes a Pontifex deck.  Options are:
      --alpha String    | Pre-made alphabet to use (below)
      --alphastr String | User-provided alphabet (in single quotes, mutually exclusive with --alpha)
      --outfile Path    | Path to output deck file
      --shuffle Integer | [Optional] Shuffle count (100 Default)
      --mix-values      | [Optional] Mixes up alphabet value assignments
    
     Function 'key':
      Outputs a key string for use with a tableau.  Options are:
      --length Integer  | Output length
      --infile Path     | Deck file to use
      --outfile Path    | [Optional] Deck output path (Don't write endstate to infile)
                        |   (not recommended for sensitive uses)
      --quiet           | [Optional] Suppress soft errors
    
     Function 'pass':
      Outputs a complex password based on these options:
      --length Integer  | Password length
      --alpha String    | Pre-made alphabet string name (below)
      --alphastr String | User-provided alphabet (in single quotes, mutually exclusive with --alpha)
      --shuffle Integer | [Optional] Shuffle count (100 Default)
    
     Function 'advpass':
      Outputs a complex password, like pass, but with this additional option:
      --count           | Number of decks in 'meta' deck.
    
      (advpass is more of a test than a function, therefore it's unsupported)
    
     Available pre-made alphabets:
      extended: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'",.<>[]{}/?;:
      uppercase: ABCDEFGHIJKLMNOPQRSTUVWXYZ
      uppercasenums: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
      standard: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
      dvkextended: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'",.<>/=?+;:-_
      numbers: 0123456789
      lowercase: abcdefghijklmnopqrstuvwxyz
      stdcrypt: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-
      lowercasenums: abcdefghijklmnopqrstuvwxyz0123456789
      complex: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'",.<>`~[]{}/=\?+|;:-_
      solitaire: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ
    

    solitaire.py:

    #!/usr/bin/python2.7
    
    import pyrand
    import sys
    import os
    import re
    
    #class declaration -
    class Deck(object):
    
     #constructor
     def __init__(self):
      self.__deck=[]
      self.__numCards=0
      self.__lockState=0
      self.__debug=0
      self.__pushLockState=0
    
     def printDebug(self, inString):
      if self.__debug==1:
       print inString
    
     def pushCard(self, inTuple):
      if not self.__lockState and self.__pushLockState!=2:
       self.__pushLockState=1
       self.__numCards+=1
       self.__deck.append(inTuple)
      else:
       raise Exception('(EE): Deck feature locked in current state, check programming!')
    
     def pushAlpha(self, inObj):
      if not self.__lockState and self.__pushLockState!=1:
       self.__pushLockState=2
       self.__numCards+=1
       self.__deck.append((self.__numCards,inObj))
      else:
       raise Exception('(EE): Deck feature locked in current state, check programming!')
    
     def pushJoker(self, inJoker):
      if not self.__lockState and self.__pushLockState!=1:
       self.__pushLockState=2
       if inJoker<=2 and inJoker>=1:
        self.__deck.append((0-inJoker,None))
       else:
        raise Exception('(EE): Joker count cannot exceed 2 nor be less than 0')
      else:
       raise Exception('(EE): Deck feature locked in current state, check programming!')
    
     def shuffleVal(self, inCount):
      if not self.__lockState:
       for j in range(inCount):
        deck2=[]
        tmp=None
        offset=-1
        for i in range(len(self.__deck)):
         tmp=(i,self.__deck.pop(pyrand.urandomGet(0,2**16,1)[0]%len(self.__deck)))
         if tmp[1][1]==None:
          deck2.append((tmp[1][0],None))
          offset+=1
         else:
          deck2.append((tmp[0]-offset,tmp[1][1]))
        self.__deck=deck2
        self.shuffleDeck(10)
      else:
       raise Exception('(EE): Deck locked before accessing protected feature')
    
     def shuffleDeck(self, inCount):
      if not self.__lockState:
       deck2=[]
       for ij in range(inCount):
        for i in range(len(self.__deck)):
         deck2.append(self.__deck.pop(pyrand.urandomGet(0,2**16,1)[0]%len(self.__deck)))
        self.__deck=deck2
      else:
       raise Exception('(EE): Deck locked before accessing protected feature')
    
     def popState(self):
      for i in range(len(self.__deck)):
       yield (i+1,self.__deck[i][0],self.__deck[i][1])
    
     def lockDeck(self):
      try:
       if self.__deck.index((-1,None))+1 and self.__deck.index((-2,None))+1:
        self.__lockState=1
       else:
        raise Exception('(EE): Unknown Joker error')
      except:
       raise Exception('(EE): Lacking one or more Jokers at lock')
    
     def makeDeck(self, inAlphaString, inShuffleCount):
      if not self.__lockState:
       for i in inAlphaString:
        self.pushAlpha(i)
       self.pushJoker(1)
       self.pushJoker(2)
       self.shuffleDeck(inShuffleCount)
      else:
       raise Exception('(EE): Deck locked before accessing protected feature')
    
     def advanceState(self):
      if self.__lockState:
       deck2=[]
       smJokerLoc=self.__deck.index((-1,None))
       bgJokerLoc=self.__deck.index((-2,None))
       if smJokerLoc==len(self.__deck)-1:
        deck2=self.__deck[0:1]+self.__deck[smJokerLoc:smJokerLoc+1]+self.__deck[1:smJokerLoc]
       else:
        deck2=self.__deck[:smJokerLoc]+self.__deck[smJokerLoc+1:smJokerLoc+2]+self.__deck[smJokerLoc:smJokerLoc+1]+self.__deck[smJokerLoc+2:]
       self.__deck=deck2
       deck2=[]
       smJokerLoc=self.__deck.index((-1,None))
       bgJokerLoc=self.__deck.index((-2,None))
       if bgJokerLoc==len(self.__deck)-1:
        deck2=self.__deck[0:2]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[2:bgJokerLoc]
       elif bgJokerLoc==len(self.__deck)-2:
        deck2=self.__deck[0:1]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[1:bgJokerLoc]+self.__deck[bgJokerLoc+1:bgJokerLoc+2]
       else:
        deck2=self.__deck[:bgJokerLoc]+self.__deck[bgJokerLoc+1:bgJokerLoc+3]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[bgJokerLoc+3:]
       self.__deck=deck2
       deck2=[]
       smJokerLoc=self.__deck.index((-1,None))
       bgJokerLoc=self.__deck.index((-2,None))
       if smJokerLoc<bgJokerLoc:
        deck2=self.__deck[bgJokerLoc+1:]+self.__deck[smJokerLoc:smJokerLoc+1]+self.__deck[smJokerLoc+1:bgJokerLoc]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[:smJokerLoc]
        self.__deck=deck2
       else:
        tmp=smJokerLoc
        smJokerLoc=bgJokerLoc
        bgJokerLoc=tmp
        deck2=self.__deck[bgJokerLoc+1:]+self.__deck[smJokerLoc:smJokerLoc+1]+self.__deck[smJokerLoc+1:bgJokerLoc]+self.__deck[bgJokerLoc:bgJokerLoc+1]+self.__deck[:smJokerLoc]
        self.__deck=deck2
       deck2=[]
       smJokerLoc=self.__deck.index((-1,None))
       bgJokerLoc=self.__deck.index((-2,None))
       if self.__deck[len(self.__deck)-1][0]<0:
        deck2.append(self.__deck[:])
       else:
        deck2.append(self.__deck[self.__deck[len(self.__deck)-1][0]:-1]+self.__deck[:self.__deck[len(self.__deck)-1][0]]+self.__deck[-1:])
       self.__deck=deck2[0]
      else:
       raise Exception('(EE): Deck not locked before accessing protected feature')
    
     def getChars(self, inSize):
      i=0
      while i<inSize:
       self.advanceState()
       if self.__deck[0][0]<0:
        yieldChar=self.__deck[-2][1]
       else:
        yieldChar=self.__deck[self.__deck[0][0]][1]
       if type(yieldChar)!=type(None):
        i+=1
        yield yieldChar
    
     def getKey(self, inSize):
      outString=''
      for i in self.getChars(inSize):
       outString=outString+i
      return outString
    
    alphaDict={
    'complex':'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\'\",.<>`~[]{}/=\?+|;:-_',
    'dvkextended':'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\'\",.<>/=?+;:-_',
    'extended':'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\'\",.<>[]{}/?;:',
    'standard':'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
    'lowercase':'abcdefghijklmnopqrstuvwxyz',
    'lowercasenums':'abcdefghijklmnopqrstuvwxyz0123456789',
    'uppercase':'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
    'uppercasenums':'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
    'numbers':'0123456789',
    'solitaire':'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ',
    'stdcrypt':'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-'
    }
    
    def loadState(inDeck, inFilename):
     file=open(inFilename,'rU')
     if 'PYNTIFEX_DECK\n' not in file:
      raise Exception('(EE): Attempted to load deck file lacking magic string')
     for i in file:
      try:
       imatch=re.search('\((\d+),\s(-?\d+),\s\'(.+)\'\)', i)
       if imatch.groups()[2]=='None':
        inDeck.pushCard((int(imatch.groups()[1]),None))
       else:
        inDeck.pushCard((int(imatch.groups()[1]),imatch.groups()[2]))
      except:
       if i!='PYNTIFEX_DECK':
        raise Exception('(EE): The following line cannot be processed: '+str(i+1)+' in file: '+inFilename)
     file.close()
     inDeck.lockDeck()
    
    def saveState(inDeck, inFilename):
     outFile=open(inFilename,'w')
     outFile.truncate(0)
     outFile.write('PYNTIFEX_DECK\n')
     for i in inDeck.popState():
      if i[1]<0:
       i=(i[0],i[1],'None')
      outFile.write(str(i)+'\n')
     outFile.flush()
     outFile.close()
    
    def makeDeckFile():
     alphabet=''
     outFile=''
     shuffleCount=100
     mixValues=0
     deck=Deck()
     try:
      alphabet=sys.argv[sys.argv.index('--alphastr')+1]
     except ValueError:
      try:
       alphabet=sys.argv[sys.argv.index('--alpha')+1]
       alphabet=alphaDict.get(alphabet)
       if alphabet==None:
        print '\nBad selection on --alpha, please refer to --help'
        sys.exit(1)
      except ValueError:
       print '\nBad options, should be: <command> make [--alpha <Name> | --alphastr <String>] --outfile <PathToOutputFile> [--shuffle <Integer>] [--mix-values]'
       sys.exit(1)
     try:
      outFile=sys.argv[sys.argv.index('--outfile')+1]
      try:
       shuffleCount=int(sys.argv[sys.argv.index('--shuffle')+1])
       if shuffleCount<10:
        print '\nFor security reasons, please make --shuffle 10 or larger'
        sys.exit(1)
      except ValueError:
       pass
      if '--mix-values' in sys.argv:
       mixValues=1
     except ValueError:
      print '\nBad options, should be: <command> make [--alpha <Name> | --alphastr <String>] --outfile <PathToOutputFile> [--shuffle <Integer>] [--mix-values]'
      sys.exit(1)
     deck.makeDeck(alphabet, shuffleCount)
     if mixValues==1:
      deck.shuffleVal(shuffleCount)
     deck.lockDeck()
     try:
      saveState(deck,outFile)
     except:
      print '\nError writing file to path:'
      print outFile
      sys.exit(1)
     print '\nDeck outputted successfully to:'
     print outFile
     sys.exit(0)
    
    def printKeyString():
     inFile=''
     outFile=''
     keyString=''
     stringLength=0
     deck=Deck()
     try:
      inFile=sys.argv[sys.argv.index('--infile')+1]
      stringLength=int(sys.argv[sys.argv.index('--length')+1])
      try:
       outFile=sys.argv[sys.argv.index('--outfile')+1]
      except ValueError:
       pass
     except:
      print '\nBad options, should be: <command> key --infile <PathToFile> --length <Integer> [--outfile <PathToFile>] [--quiet]'
      sys.exit(1)
     try:
      loadState(deck,inFile)
     except:
      print '\nError opening deck file:'
      print inFile
      sys.exit(1)
     deck.lockDeck()
     print ''
     print '',
     for i in deck.getChars(stringLength):
      print '\b'+i,
     if outFile=='':
      try:
       saveState(deck,inFile)
      except:
       if '--quiet' not in sys.argv:
        print '\nError saving deck file:'
        print inFile
       sys.exit(1)
     else:
      try:
       saveState(deck,outFile)
      except:
       if '--quiet' not in sys.argv:
        print '\nError saving deck file:'
        print outFile
       sys.exit(1)
     sys.exit(0)
    
    def printPassString():
     shuffleCount=100
     alphabet=''
     stringLength=0
     try:
      stringLength=int(sys.argv[sys.argv.index('--length')+1])
     except ValueError:
      '\nBad options, should be: <command> pass [--alpha <Name> | --alphastr <String>] --length <Integer> [--shuffle <Integer>]'
     deck=Deck()
     try:
      alphabet=sys.argv[sys.argv.index('--alphastr')+1]
     except ValueError:
      try:
       alphabet=sys.argv[sys.argv.index('--alpha')+1]
       alphabet=alphaDict.get(alphabet)
       if alphabet==None:
        print '\nBad selection on --alpha, please refer to --help'
        sys.exit(1)
      except ValueError:
       print '\nBad options, should be: <command> pass [--alpha <Name> | --alphastr <String>] --length <Integer> [--shuffle <Integer>]'
       sys.exit(1)
     try:
      shuffleCount=int(sys.argv[sys.argv.index('--shuffle')+1])
      if shuffleCount<100:
       print '\nFor security reasons, please make --shuffle 100 or larger'
       sys.exit(1)
     except ValueError:
      pass
     deck.makeDeck(alphabet*3, shuffleCount)
     deck.shuffleVal(shuffleCount)
     deck.lockDeck()
     for i in deck.getChars(stringLength):
      print '\b'+i,
     sys.exit(0) 
    
    def printAdvPassString():
     if '--quiet' not in sys.argv:
      print '\nUnofficial function selected.  Advpass can take a'
      print ' really long time to output.  Output latency is pro-'
      print ' portional to (2x --count) x (--shuffle) x (--alpha len)'
      print ' length having little impact...'
     shuffleCount=100
     alphabet=''
     stringLength=0
     deckCount=1
     try:
      deckCount=int(sys.argv[sys.argv.index('--count')+1])
      stringLength=int(sys.argv[sys.argv.index('--length')+1])
     except ValueError:
      print '\nBad options, should be: <command> advpass [--alpha <Name> | --alphastr <String>] --length <Integer> --count <Integer> [--shuffle <Integer>]'
      sys.exit(1)
     deck=Deck()
     try:
      alphabet=sys.argv[sys.argv.index('--alphastr')+1]
     except ValueError:
      try:
       alphabet=sys.argv[sys.argv.index('--alpha')+1]
       alphabet=alphaDict.get(alphabet)
       if alphabet==None:
        print '\nBad selection on --alpha, please refer to --help'
        sys.exit(1)
      except ValueError:
       print '\nBad options, should be: <command> pass [--alpha <Name> | --alphastr <String>] --length <Integer> [--shuffle <Integer>]'
       sys.exit(1)
     try:
      shuffleCount=int(sys.argv[sys.argv.index('--shuffle')+1])
      if shuffleCount<10:
       print '\nFor security reasons, please make --shuffle 10 or larger'
       sys.exit(1)
     except ValueError:
      pass
     if '--quiet' not in sys.argv:
      print '\n Initializing Meta-deck...\n  ',
     for i in range(deckCount):
      deck2=Deck()
      deck2.makeDeck(alphabet,shuffleCount)
      deck2.lockDeck()
      deck.pushAlpha(deck2)
     deck.pushJoker(1)
     deck.pushJoker(2)
     deck.shuffleDeck(shuffleCount)
     deck.shuffleVal(shuffleCount)
     deck.lockDeck()
     if '--quiet' not in sys.argv:
      print '\n Compiling password string...\n'
     print '',
     for j in deck.getChars(stringLength):
      for i in j.getKey(1):
       print '\b'+i,
     sys.exit(0) 
    
    #main function -
    def main():
     if '--help' in sys.argv or len(sys.argv)==1:
      print '\nOptions:'
      print ' <command> func --args args'
      print '\n Function \'make\':'
      print '  Makes a Pontifex deck.  Options are:'
      print '  --alpha String    | Pre-made alphabet to use (below)'
      print '  --alphastr String | User-provided alphabet (in single quotes, mutually exclusive with --alpha)'
      print '  --outfile Path    | Path to output deck file'
      print '  --shuffle Integer | [Optional] Shuffle count (100 Default)'
      print '  --mix-values      | [Optional] Mixes up alphabet value assignments'
      print '\n Function \'key\':'
      print '  Outputs a key string for use with a tableau.  Options are:'
      print '  --length Integer  | Output length'
      print '  --infile Path     | Deck file to use'
      print '  --outfile Path    | [Optional] Deck output path (Don\'t write endstate to infile)'
      print '                    |   (not recommended for sensitive uses)'
      print '  --quiet           | [Optional] Suppress soft errors'
      print '\n Function \'pass\':'
      print '  Outputs a complex password based on these options:'
      print '  --length Integer  | Password length'
      print '  --alpha String    | Pre-made alphabet string name (below)'
      print '  --alphastr String | User-provided alphabet (in single quotes, mutually exclusive with --alpha)'
      print '  --shuffle Integer | [Optional] Shuffle count (100 Default)'
      print '\n Function \'advpass\':'
      print '  Outputs a complex password, like pass, but with this additional option:'
      print '  --count           | Number of decks in \'meta\' deck.'
      print '\n  (advpass is more of a test than a function, therefore it\'s unsupported)'
      print '\n Available pre-made alphabets:'
      for i in alphaDict.keys():
       print '  '+i+': '+alphaDict.get(i)
     elif sys.argv[1]=='make':
      makeDeckFile()
     elif sys.argv[1]=='key':
      printKeyString()
     elif sys.argv[1]=='pass':
      printPassString()
     elif sys.argv[1]=='advpass':
      printAdvPassString()
     else:
      print '\nNo useable Options.  --help for info.'
      sys.exit(1)
    
    #launch boilerplate -
    if __name__ == '__main__':
     try:
      main()
     except KeyboardInterrupt:
      print '\n Program execution halted prematurely via keyboard (^C)'
    

    pyrand.py:

    #!/usr/bin/python2.7
    
    import os
    import struct
    import sys
    
    randomDevice=open('/dev/random','rb')
    urandomDevice=open('/dev/urandom','rb')
    UINT32_MAX=0xffffffff
    
    def randomDeviceBytes(inLength):
     return randomDevice.read(inLength)
    
    def urandomDeviceBytes(inLength):
     return urandomDevice.read(inLength)
    
    def convertBytesToInt(inBytes):
     return struct.unpack('I',inBytes)[0]
    
    def makeAcceptableInt(inLower, inUpper, inInt):
     if inLower>=0 and inUpper>inLower and inInt>=0:
      return int((((inUpper-inLower)/float(UINT32_MAX))+inLower)*inInt)
    
    def randomGet(inLower, inUpper, inCount):
     if inLower>=0 and inUpper>inLower and inCount>0:
      return [makeAcceptableInt(inLower, inUpper, convertBytesToInt(randomDeviceBytes(4))) for _ in range(inCount)]
     else:
      raise Exception('(EE): Bad arguments for randomGet()')
    
    def urandomGet(inLower, inUpper, inCount):
     if inLower>=0 and inUpper>inLower and inCount>0:
      return [makeAcceptableInt(inLower, inUpper, convertBytesToInt(urandomDeviceBytes(4))) for _ in range(inCount)]
     else:
      raise Exception('(EE): Bad arguments for urandomGet()')
    
    def main():
     args=[None]
     if len(sys.argv)==4 and long(sys.argv[1])<long(sys.argv[2]) and long(sys.argv[3])>=0:
      for i in sys.argv[1:]:
       args.append(int(i))
      print '\nurandom Output:'
      for i in urandomGet(args[1],args[2],args[3]):
       print i,
      print '\n\nrandom Output:'
      for i in randomGet(args[1],args[2],args[3]):
       print i,
      print '\n\nDebug Check Complete...'
     else:
      print '(EE): Error in arguments.  Usage: ./command Lower Upper Count'
    
    
    if __name__=='__main__':
     main()
    

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 629 other followers

%d bloggers like this: