Download the file
  1. #!/usr/bin/env python
  2.  
  3. # This program is free software: you can redistribute it and/or modify it
  4. # under the terms of the GNU Affero General Public License as published by the
  5. # Free Software Foundation, either version 3 of the License, or (at your
  6. # option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful, but WITHOUT
  9. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. # more details.
  12. #
  13. # You should have received a copy of the GNU Affero General Public License
  14. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  15.  
  16. # Inspired by Jeff Connelly
  17. # Matthieu Weber, 2011
  18.  
  19. import time, sha, sys, socket
  20. from optparse import OptionParser
  21.  
  22. parser = OptionParser()
  23.  
  24. parser.add_option("-l",
  25. help="How much data should be output (use 'b' suffix for bits, 'K' for kByte, 'M' for MByte)",
  26. type="string",
  27. dest="data_length")
  28.  
  29. parser.add_option("-o",
  30. metavar="FILE",
  31. help="Output file (default is the standard output)",
  32. action="store",
  33. type="string",
  34. dest="output_file")
  35.  
  36. parser.add_option("-x",
  37. help="Output hexadecimal numbers instead of raw data",
  38. action="store_true",
  39. dest="hex",
  40. default=False)
  41.  
  42. parser.add_option("-q",
  43. help="Quiet mode",
  44. action="store_true",
  45. dest="quiet",
  46. default=False)
  47.  
  48. parser.add_option("-e",
  49. metavar="FLOAT",
  50. help="Number of bits of entropy per byte (default is %default)",
  51. action="store",
  52. type="float",
  53. dest="entropy",
  54. default=5.0)
  55.  
  56. #parser.add_option("-r",
  57. # metavar="INT",
  58. # help="Serial port's bitrate (default is %default)",
  59. # action="store",
  60. # type="int",
  61. # dest="rate",
  62. # default=57600)
  63. #
  64. #parser.add_option("-d",
  65. # metavar="DEVICE",
  66. # help="Serial port device (default is %default)",
  67. # action="store",
  68. # type="string",
  69. # dest="device",
  70. # default="/dev/ttyUSB0")
  71.  
  72. (options, args) = parser.parse_args()
  73.  
  74. entropy_per_byte = options.entropy
  75. hash_len = sha.digest_size
  76. read_len = int((hash_len * 8) / entropy_per_byte)
  77.  
  78. if options.data_length == None:
  79. parser.exit(1, "Error: You must specify a data length\n")
  80.  
  81. data_length = options.data_length
  82. if data_length[-1] == 'b':
  83. data_length = int(data_length[:-1])
  84. if data_length % 8 != 0:
  85. parser.exit(1, "Error: data length must be a multiple of 8 bits\n")
  86. else:
  87. data_length /= 8
  88. elif data_length[-1] == 'K':
  89. data_length = int(data_length[:-1])*1024
  90. elif data_length[-1] == 'M':
  91. data_length = int(data_length[:-1])*1024*1024
  92. else:
  93. try:
  94. data_length = int(data_length)
  95. except ValueError:
  96. parser.exit(1, "Error: '%s' is not a valid data length\n" % data_length)
  97.  
  98. output_count = 0
  99. output_file = sys.stdout
  100. if options.output_file:
  101. output_file = open(options.output_file, "w")
  102.  
  103. try:
  104. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  105. sock.connect(("localhost", 9876))
  106. sock_file = sock.makefile("rb") # buffered
  107. except socket.error:
  108. parser.exit(1, "Error: Failed to connect to button_box_server\n")
  109.  
  110.  
  111. start_time = time.time()
  112. next_check_time = start_time+1
  113.  
  114. cont = True
  115. while cont and (data_length == None or output_count < data_length):
  116. block = ""
  117. while len(block) < read_len:
  118. try:
  119. sock.send("Q")
  120. # expect to receive 'Qx\n' where x is a random byte
  121. buf = sock_file.readline()
  122. while not buf[0] == 'Q':
  123. buf = sock_file.readline()
  124. block += buf[1] # get the random byte
  125. except KeyboardInterrupt:
  126. cont = False
  127. break # exit the inner loop
  128. if not cont:
  129. break # exit the outer loop
  130.  
  131. to_write = hash_len
  132. if output_count + hash_len > data_length:
  133. to_write = data_length - output_count
  134. if options.hex:
  135. hash = sha.new(block).hexdigest()
  136. output_file.write(hash[:to_write*2])
  137. else:
  138. hash = sha.new(block).digest()
  139. output_file.write(hash[:to_write])
  140. output_file.flush()
  141.  
  142. output_count += to_write
  143. if not options.quiet and time.time() > next_check_time and output_count > 0:
  144. next_check_time = time.time()+1
  145. elapsed = time.time() - start_time
  146. eta = elapsed / (float(output_count) / data_length) - elapsed
  147. sys.stderr.write("\r%9d bytes (%3d%%), ETA: %02d:%02d (%.3f kbps)" % (output_count, 100.0 * output_count/data_length, eta/60, eta%60, 8*output_count/(1000.0*elapsed)))
  148. sys.stderr.flush()
  149.  
  150. if options.hex:
  151. output_file.write("\n")
  152. if not options.quiet:
  153. elapsed_time = time.time() - start_time
  154. sys.stderr.write("\nElapsed time: %02d:%02d, rate: %.3f kbps, %d bytes written\n" %
  155. (elapsed_time/60, elapsed_time % 60, 8*output_count/(1000.0*elapsed_time), output_count))
  156.