def processBuffer(self, buffer): done = False if (len(buffer) * 8 < self.numObservationBits): print "ERROR: Received an incomplete buffer (%d), expexting (%d)" \ %( ( len( buffer ) * 8 ), self.numObservationBits ) done = True else: part = [] bufferedSamples = BitStream(buffer) numObservationSamples = len( self.spreadingCode) * Receiver.MULTIPLIER for sampleIndex in range(numObservationSamples): sampleValue = bufferedSamples.read(self.bitDepth) for channelIndex in range(self.numberOfChannels - 1): bufferedSamples.read(self.bitDepth) sampleValue = struct.pack("!I", sampleValue) sampleValue = struct.unpack("i", sampleValue)[0] part.append(sampleValue * 1.0) if (part != None and 0 < len(part)): self.detectTrainingSequence(part) self.numProcessedObservations += 1 done = (self.numProcessedObservations >= self.maxNumberOfObservations) return (done)
def extractSymbols(self, symbols, offset): bitPacker = BitPacker() bitStream = BitStream(False, bitPacker) for i in range(offset, len(symbols)): bitPacker.writeByte(symbols[i], 1) data = bitStream.getRawBytes() print "Data (%d):" % (len(data)) for i in range(len(data)): if(i != 0 and i % 8 == 0): print print "0x%02x " % (ord(data[i])), print decodedData = self.decodeData(data) print "Decoded data (%d):" % (len(decodedData)) for i in range(len(decodedData)): if(i != 0 and i % 8 == 0): print print "0x%02x " % (ord(decodedData[i])), print print "Decoded string: '%s'" % (decodedData) decodedSymbols = \ SymbolTracker.toList(self.bitsPerSymbol, decodedData) return(decodedSymbols)
def encode(self, data): if(self.checkParamterIsString(data)): bitPacker = BitPacker() bitStream = BitStream(False, bitPacker) symbolTracker = SymbolTracker( ManchesterEncoder.MESSAGE_LENGTH, data) symbol = symbolTracker.getNextSymbol() while(symbol is not None): if(symbol == 1): bitPacker.writeByte( ManchesterEncoder.HI_SYMBOL, ManchesterEncoder.BLOCK_LENGTH ) elif(symbol == 0): bitPacker.writeByte( ManchesterEncoder.LO_SYMBOL, ManchesterEncoder.BLOCK_LENGTH ) else: print "ERROR: Received unknown symbol (%d)." % (symbol) symbol = symbolTracker.getNextSymbol() return(bitStream.getRawBytes()) else: return(None)
def decode(self, data, errorPositions=None): if(self.codec and self.checkParamterIsString(data)): bitPacker = BitPacker() bitStream = BitStream(False, bitPacker) symbolTracker = BitStream(False, data) numberOfBlocks = \ int( math.ceil( float(symbolTracker.getSize()) / float(self.blockLength) ) ) for i in range(numberOfBlocks): blockValues = [0 for j in range(self.numberOfBlockSymbols)] buffer = symbolTracker.readBytes(self.blockLength) for j in range(len(buffer)): index = (i * self.numberOfBlockSymbols) + j blockValues[j] = ord(buffer[j]) if ( errorPositions is not None and index < len(errorPositions) and errorPositions[index] ): blockValues[j] = -1 message = \ self.codec.RSDecode( blockValues, ( self.numberOfBlockSymbols - self.numberOfMessageSymbols ) ) if(message is not None): map( lambda x: bitPacker.writeByte( x, ReedSolomonEncoder.SYMBOL_SIZE_IN_BITS ), message[0: self.numberOfMessageSymbols] ) else: for i in range(self.numberOfMessageSymbols): bitPacker.writeByte( 0, ReedSolomonEncoder.SYMBOL_SIZE_IN_BITS ) return(bitStream.getRawBytes()) else: print "ERROR: Codec was not initialized." return(None)
def test_bit_extension(self): """ The real test function. I don't know how it works, but I think it will be cool :return: dunno """ bit_stream = BitStream([]) test1 = bit_stream.extend_count(3) test2 = bit_stream.extend_count(4) self.assertEqual(len(test1), 8) self.assertEqual(len(test2), 16)
def decode(self, fname): self._qr.decode(fname) bits = BitStream() print(self._qr.data) binData = base64.b64decode(self._qr.data) for uint8 in binData: byte = ord(uint8) bits.write(8, byte) print(bits) self._processPolygons(bits)
def record(self, device): if ( \ device.hasAppropriateStream ( \ CAHAL_DEVICE_INPUT_STREAM, \ self.numberOfChannels, \ self.bitDepth, \ self.sampleRate \ ) \ ): flags = \ CAHAL_AUDIO_FORMAT_FLAGISSIGNEDINTEGER \ | CAHAL_AUDIO_FORMAT_FLAGISPACKED BaseRecorder.bitPacker = BitPacker() BaseRecorder.lock = _threading.Lock() BaseRecorder.semaphore = _threading.Semaphore(0) self.signal = BitStream(BaseRecorder.bitPacker) if ( start_recording ( \ device.struct, \ CAHAL_AUDIO_FORMAT_LINEARPCM, \ self.numberOfChannels, \ self.sampleRate, \ self.bitDepth, \ bufferSamples, \ flags \ ) \ ): print "Starting recording..." self.processObservations() print "Stopping recording..." cahal_stop_recording() print "Stopped recording." else: print "ERROR: Could not start recording." else: print "ERROR: Could not find an appropriate stream." BaseRecorder.bitPacker = None BaseRecorder.lock = None BaseRecorder.semaphore = None
def encode(self, data): if(self.codec and self.checkParamterIsString(data)): bitPacker = BitPacker() bitStream = BitStream(False, bitPacker) symbolTracker = BitStream(False, data) numberOfBlocks = \ int( math.ceil( float(symbolTracker.getSize()) / float(self.messageLength) ) ) for i in range(numberOfBlocks): blockValues = [chr(0) for i in range(self.numberOfMessageSymbols)] buffer = symbolTracker.readBytes(self.messageLength) for i in range(len(buffer)): blockValues[i] = buffer[i] codeword = \ self.codec.RSEncode( blockValues, ( self.numberOfBlockSymbols - self.numberOfMessageSymbols ) ) map( lambda x: bitPacker.writeByte( x, ReedSolomonEncoder.SYMBOL_SIZE_IN_BITS ), codeword ) return(bitStream.getRawBytes()) else: print "ERROR: Codec was not initialized." return(None)
def encrypt_text(self, text, pad_to=None, task_monitor=None): """ Encrypts the given string into a ciphertext object. Arguments: text::string -- A string to encrypt. pad_to::int -- Minimum size (in bytes) of the resulting ciphertext. Data will be padded before encryption to match this size. task_monitor::TaskMonitor -- A task monitor for this task. Returns: ciphertext:Ciphertext -- A ciphertext object encapsulating the encrypted data. """ bitstream = BitStream() bitstream.put_string(text) return self.encrypt_bitstream(bitstream, pad_to, task_monitor)
def transmit( self, device, message ): if ( \ device.hasAppropriateStream ( \ CAHAL_DEVICE_OUTPUT_STREAM, \ self.numberOfChannels, \ self.bitDepth, \ self.sampleRate \ ) \ ): print "Preparing signal for message: %s." %( message ) symbolTracker = BitStream( message ) bitPacker = BitPacker() numSymbols = symbolTracker.getSize() / self.bitsPerSymbol numSamples = numSymbols * self.samplesPerSymbol duration = math.ceil( numSamples / self.sampleRate ) print "Buffering up to 5 seconds of audio." numSymbolsToRead = \ math.ceil( self.sampleRate / self.samplesPerSymbol * 5 ) print "Symbols to read per iteration is 0x%x." %( numSymbolsToRead ) hasRemaining = \ self.bufferSymbols ( symbolTracker, bitPacker, numSymbolsToRead ) self.sendSignal ( device, symbolTracker, bitPacker, duration, hasRemaining, numSymbolsToRead ) else: print "ERROR: Could not find an appropriate stream."
def _encrypted_data_as_bitstream(self): """ Returns the contents of this ciphertext as a BitStream object. This includes only the encrypted data (gamma and delta components), not the nbits and public key fingerprint metadata. The components are encoded alternating as follows: [gamma[0], delta[0], gamma[1], delta[1], ...] with each component represented as a nbits long number. Returns: bitstream::BitStream -- The gamma and delta components of this ciphertext as a bitstream. """ bitstream = BitStream() for i in range(0, self.get_length()): bitstream.put_num(self.gamma[i], self.nbits) bitstream.put_num(self.delta[i], self.nbits) return bitstream
def processBuffer(self, buffer): done = False if len(buffer) * 8 < self.numObservationBits: print "ERROR: Received an incomplete buffer (%d), expexting (%d)" % ( (len(buffer) * 8), self.numObservationBits, ) done = True else: part = [] bufferedSamples = BitStream(buffer) for sampleIndex in range(self.numObservationSamples): sampleValue = bufferedSamples.read(self.bitDepth) for channelIndex in range(self.numberOfChannels - 1): bufferedSamples.read(self.bitDepth) sampleValue = struct.pack("!I", sampleValue) sampleValue = struct.unpack("i", sampleValue)[0] part.append(sampleValue * 1.0) if 0 < len(part): if self.applyFilter: part = python_filter_signal(self.widebandFilter, part) if 0 == (self.numProcessedObservations % int(self.graphPeriod)): self.graphFFT(part) self.calculateEnergy(part) self.numProcessedObservations += 1 done = self.numProcessedObservations >= self.numObservations return done
def decode(self, data, errorPositions=None): if(self.checkParamterIsString(data)): bitPacker = BitPacker() bitStream = BitStream(False, bitPacker) symbolTracker = SymbolTracker(ManchesterEncoder.BLOCK_LENGTH, data) symbol = symbolTracker.getNextSymbol() errorBitPositions = [] while(symbol is not None): if(symbol == ManchesterEncoder.HI_SYMBOL): bitPacker.writeByte(1, 1) elif(symbol == ManchesterEncoder.LO_SYMBOL): bitPacker.writeByte(0, 1) else: print \ "WARN: Received unknown symbol (0x%x) at position %d."\ % (symbol, bitStream.getSize()) errorBitPositions.append(bitStream.getSize()) bitPacker.writeByte(1, 1) symbol = symbolTracker.getNextSymbol() buffer = bitStream.getRawBytes() if(errorPositions is not None): for i in range(len(buffer)): errorPositions.append(False) for bitPosition in errorBitPositions: bytePosition = int(math.floor(bitPosition / 8)) errorPositions[bytePosition] = True return(buffer) else: return(None)
def processBuffer( self, buffer ): done = False if( len( buffer ) * 8 < self.numObservationBits ): print "ERROR: Received an incomplete buffer (%d), expexting (%d)" \ %( ( len( buffer ) * 8 ), self.numObservationBits ) done = True else: part = [] bufferedSamples = BitStream( buffer ) for sampleIndex in range( self.numObservationSamples ): sampleValue = bufferedSamples.read( self.bitDepth ) for channelIndex in range( self.numberOfChannels - 1 ): bufferedSamples.read( self.bitDepth ) sampleValue = struct.pack( "!I", sampleValue ) sampleValue = struct.unpack( "i", sampleValue )[ 0 ] part.append( sampleValue * 1.0 ) if( 0 < len( part ) ): if( self.applyFilter ): part = python_filter_signal( self.widebandFilter, part ) if( 0 == ( self.numProcessedObservations % int( self.graphPeriod ) ) ): self.graphFFT( part ) self.calculateEnergy( part ) self.numProcessedObservations += 1 done = ( self.numProcessedObservations >= self.numObservations ) return( done )
def transmit(self, device, message): if ( \ device.hasAppropriateStream ( \ CAHAL_DEVICE_OUTPUT_STREAM, \ self.numberOfChannels, \ self.bitDepth, \ self.sampleRate \ ) \ ): print "Preparing signal for message: %s." % (message) symbolTracker = BitStream(message) bitPacker = BitPacker() numSymbols = symbolTracker.getSize() / self.bitsPerSymbol numSamples = numSymbols * self.samplesPerSymbol duration = math.ceil(numSamples / self.sampleRate) print "Buffering up to 5 seconds of audio." numSymbolsToRead = \ math.ceil( self.sampleRate / self.samplesPerSymbol * 5 ) print "Symbols to read per iteration is 0x%x." % (numSymbolsToRead) hasRemaining = \ self.bufferSymbols ( symbolTracker, bitPacker, numSymbolsToRead ) self.sendSignal(device, symbolTracker, bitPacker, duration, hasRemaining, numSymbolsToRead) else: print "ERROR: Could not find an appropriate stream."
def decode(self, data, errorPositions=None): if (self.codec and self.checkParamterIsString(data)): bitPacker = BitPacker() bitStream = BitStream(False, bitPacker) symbolTracker = BitStream(False, data) numberOfBlocks = \ int( math.ceil( float(symbolTracker.getSize()) / float(self.blockLength) ) ) for i in range(numberOfBlocks): blockValues = [0 for j in range(self.numberOfBlockSymbols)] buffer = symbolTracker.readBytes(self.blockLength) for j in range(len(buffer)): index = (i * self.numberOfBlockSymbols) + j blockValues[j] = ord(buffer[j]) if (errorPositions is not None and index < len(errorPositions) and errorPositions[index]): blockValues[j] = -1 message = \ self.codec.RSDecode( blockValues, ( self.numberOfBlockSymbols - self.numberOfMessageSymbols ) ) if (message is not None): map( lambda x: bitPacker.writeByte( x, ReedSolomonEncoder.SYMBOL_SIZE_IN_BITS), message[0:self.numberOfMessageSymbols]) else: for i in range(self.numberOfMessageSymbols): bitPacker.writeByte( 0, ReedSolomonEncoder.SYMBOL_SIZE_IN_BITS) return (bitStream.getRawBytes()) else: print "ERROR: Codec was not initialized." return (None)
def sendSignal(self, device, symbolTracker, bitPacker, duration, hasRemaining, numSymbolsToRead): flags = \ CAHAL_AUDIO_FORMAT_FLAGISSIGNEDINTEGER \ | CAHAL_AUDIO_FORMAT_FLAGISPACKED Transmitter.signal = BitStream(bitPacker) Transmitter.lock = _threading.Lock() print "Duration is %d seconds." % (duration) if ( start_playback ( \ device.struct, \ CAHAL_AUDIO_FORMAT_LINEARPCM, \ self.numberOfChannels, \ self.sampleRate, \ self.bitDepth, \ playback, \ flags \ ) \ ): startTime = calendar.timegm(time.gmtime()) while (hasRemaining): hasRemaining = \ self.bufferSymbols( symbolTracker, bitPacker, numSymbolsToRead ) endTime = calendar.timegm(time.gmtime()) duration -= int(endTime - startTime) if (0 > duration): duation = 0 duration = struct.unpack("I", struct.pack("i", duration))[0] cahal_sleep(duration * 1000) if (cahal_stop_playback()): print "Transmit stopped." else: print "ERROR: Could not stop playback." else: print "ERROR: Could not start playing." Transmitter.lock = None
def record( self, device ): if ( \ device.hasAppropriateStream ( \ CAHAL_DEVICE_INPUT_STREAM, \ self.numberOfChannels, \ self.bitDepth, \ self.sampleRate \ ) \ ): flags = \ CAHAL_AUDIO_FORMAT_FLAGISSIGNEDINTEGER \ | CAHAL_AUDIO_FORMAT_FLAGISPACKED BaseRecorder.bitPacker = BitPacker() BaseRecorder.lock = _threading.Lock() BaseRecorder.semaphore = _threading.Semaphore( 0 ) self.signal = BitStream( BaseRecorder.bitPacker ) if ( start_recording ( \ device.struct, \ CAHAL_AUDIO_FORMAT_LINEARPCM, \ self.numberOfChannels, \ self.sampleRate, \ self.bitDepth, \ bufferSamples, \ flags \ ) \ ): print "Starting recording..." self.processObservations() print "Stopping recording..." cahal_stop_recording() print "Stopped recording." else: print "ERROR: Could not start recording." else: print "ERROR: Could not find an appropriate stream." BaseRecorder.bitPacker = None BaseRecorder.lock = None BaseRecorder.semaphore = None
def encode(self, data): if (self.codec and self.checkParamterIsString(data)): bitPacker = BitPacker() bitStream = BitStream(False, bitPacker) symbolTracker = BitStream(False, data) numberOfBlocks = \ int( math.ceil( float(symbolTracker.getSize()) / float(self.messageLength) ) ) for i in range(numberOfBlocks): blockValues = [ chr(0) for i in range(self.numberOfMessageSymbols) ] buffer = symbolTracker.readBytes(self.messageLength) for i in range(len(buffer)): blockValues[i] = buffer[i] codeword = \ self.codec.RSEncode( blockValues, ( self.numberOfBlockSymbols - self.numberOfMessageSymbols ) ) map( lambda x: bitPacker.writeByte( x, ReedSolomonEncoder.SYMBOL_SIZE_IN_BITS), codeword) return (bitStream.getRawBytes()) else: print "ERROR: Codec was not initialized." return (None)
def verify(self, original_collection, shuffled_collection): """ Verifies that original_collection and shuffled_collection are equivalent as proven by this ShufflingProof object. If this method returns true, then we have proof that both collections contain encryptions of the same collection of plaintexts, save for the negligible probability (for a correct security parameter configured in params.py) that the zero-knowledge proof has been faked. Otherwise we gain no information about the two collections, other than they are not shown to be equivalent by this particular proof. ShufflingProof is a zero-knowledge proof: the result of this verification or the information within the ShufflingProof object for which two collections pass this verification, provide no information as to the association of particular ciphertexts in original_collection with particular ciphertexts of shuffled_collection. Arguments: original_collection::CiphertextCollection -- The original collection of ciphertexts. shuffled_collection::CiphertextCollection -- Another collection for which we wish to know if the current ShufflingProof object demonstrates equivalence with original_collection. Returns: result::bool -- True if this proof shows both collections to be equivalent. False otherwise. """ # Get the security parameter P with which the proof was originally # created. This is reflect in the length of self._collections and # self._mappings. assert len(self._collections) == len(self._mappings), \ "The length of the private properties self._collections and " \ "self._mappings of ShufflingProof must always be the same." security_parameter = len(self._collections) # Get the security parameter specified in params minimum_allowed_security_parameter = params.SHUFFLING_PROOF_SECURITY_PARAMETER # Check that the security parameter is <= 256, since that is the most # bits of challenge we can currently have. Also check that P is at least 1 if(minimum_allowed_security_parameter < 1 or minimum_allowed_security_parameter > 256): raise ValueError("Security parameter for shuffling proof " \ "(params.SHUFFLING_PROOF_SECURITY_PARAMETER) is out of " \ "range. The security parameter must be between 1 and 256, "\ "its current value is %d. If you have set " \ "CUSTOM_SHUFFLING_PROOF_SECURITY_PARAMETER in the file " \ "params.py, please correct this value or set it to None, " \ "in order for the security parameter to be decided based " \ "on the global SECURITY_LEVEL of plonevotecryptolib. It " \ "is recommended that " \ "CUSTOM_SHUFFLING_PROOF_SECURITY_PARAMETER be always set " \ "to None for deployment operation of plonevotecryptolib." \ % security_parameter) # Check that the security parameter for which the proof was generated # is correct and meets the security standards declared in params.py if(security_parameter < minimum_allowed_security_parameter or security_parameter < 1 or security_parameter > 256): raise InvalidShuffilingProofError("The security parameter for " \ "(which the current proof was created is %d. This is " \ "(either an incorrect value (outside of the [1,256] " \ "(range) or not secure enough to meet the standards set " \ "(in the configuration of params.py (< %d). The proof " \ "(is thus invalid." \ % (security_parameter, minimum_allowed_security_parameter)) # Generate the challenge challenge = \ self._generate_challenge(original_collection, shuffled_collection) # Verify that the challenge corresponds to the stored one if(challenge != self._challenge): return False # Get the challenge as a BitStream for easier manipulation challenge_bits = BitStream() challenge_bits.put_hex(challenge) challenge_bits.seek(0) # back to the beginning of the stream # For each of the first P bits in the stream for i in range(0, security_parameter): bit = challenge_bits.get_num(1) if(bit == 0): # verify that self._mapping[i] maps original_collection into # self._collections[i] if(not self._mappings[i].verify(original_collection, self._collections[i])): return False elif(bit == 1): # verify that self._mapping[i] self._collections[i] into # self._collections[i] if(not self._mappings[i].verify(self._collections[i], shuffled_collection)): return False else: assert False, "We took a single bit, its value must be either "\ "0 or 1." # If we made it so far, the proof is correct # (each mapping is in accordance to the challenge and valid) return True
def new(cls, original_collection, shuffled_collection, mapping): """ Constructs a new proof of equivalence between original_collection and shuffled_collection. This method should not be used outside of plonevotecryptolib.Mixnet. Consider using CiphertextCollection.shuffle_with_proof() instead. The given CiphertextCollectionMapping must be a valid mapping between original_collection and shuffled_collection. Arguments: original_collection::CiphertextCollection -- The original collection to be shuffled. shuffled_collection::CiphertextCollection -- The shuffled collection resulting from applying mapping to original_collection. mapping::CiphertextCollectionMapping -- The mapping between original_collection and shuffled_collection. Returns: proof::ShufflingProof -- The zero-knowledge proof of equivalence between original_collection and shuffled_collection. Throws: InvalidCiphertextCollectionMappingError -- If mapping is not a valid mapping between original_collection and shuffled_collection. ValueError -- If params.SHUFFLING_PROOF_SECURITY_PARAMETER is within an invalid range. """ # Check that we have a valid mapping between the two collections: if(not mapping.verify(original_collection, shuffled_collection)): raise InvalidCiphertextCollectionMappingError( \ "mapping was expected to be a CiphertextCollectionMapping "\ "object representing a valid mapping between "\ "original_collection and shuffled_collection. However, "\ "mapping.verify(original_collection, shuffled_collection) "\ "returns False. A zero-knowledge proof of equivalence "\ "between two shuffled collections cannot be constructed "\ "without first having a valid mapping between them.") # Get the security parameter P security_parameter = params.SHUFFLING_PROOF_SECURITY_PARAMETER # Check that the security parameter is <= 256, since that is the most # bits of challenge we can currently have. Also check that P is at least 1 if(security_parameter < 1 or security_parameter > 256): raise ValueError("Security parameter for shuffling proof " \ "(params.SHUFFLING_PROOF_SECURITY_PARAMETER) is out of " \ "range. The security parameter must be between 1 and 256, "\ "its current value is %d. If you have set " \ "CUSTOM_SHUFFLING_PROOF_SECURITY_PARAMETER in the file " \ "params.py, please correct this value or set it to None, " \ "in order for the security parameter to be decided based " \ "on the global SECURITY_LEVEL of plonevotecryptolib. It " \ "is recommended that " \ "CUSTOM_SHUFFLING_PROOF_SECURITY_PARAMETER be always set " \ "to None for deployment operation of plonevotecryptolib." \ % security_parameter) # Construct a new empty proof proof = ShufflingProof() # Populate proof._collections with P random shuffles of # original_collection. We save each mapping for now in proof._mappings. # (ie. every mapping in proof._mappings[i] will initially be from the # original collection into proof._collections[i]) # generate new mappings in parallel pool = multiprocessing.Pool() async_params = [original_collection] * security_parameter generate_mappings_pool = pool.map_async(new_collection_mapping, async_params) for new_mapping in generate_mappings_pool.get(99999999): proof._mappings.append(new_mapping) proof._collections.append(new_mapping.apply(original_collection)) pool.close() pool.join() # Generate the challenge proof._challenge = \ proof._generate_challenge(original_collection, shuffled_collection) # Get the challenge as a BitStream for easier manipulation challenge_bits = BitStream() challenge_bits.put_hex(proof._challenge) challenge_bits.seek(0) # back to the beginning of the stream # For each of the first P bits in the stream for i in range(0, security_parameter): ## print "Processing challenge bit %d" % i # replace with TaskMonitor API calls bit = challenge_bits.get_num(1) if(bit == 0): # Do nothing. # proof._mappings[i] is already a mapping from # original_collection unto proof._collections[i] pass elif(bit == 1): # Change proof._mappings[i] to be a mapping from # proof._collections[i] unto shuffled_collection, using # CiphertextCollectionMapping.rebase(...) # rebase(O->D, O->C_{i}) => C_{i}->D rebased_mapping = mapping.rebase(proof._mappings[i]) # Replace O->C_{i} with C_{i}->D proof._mappings[i] = rebased_mapping else: assert False, "We took a single bit, its value must be either "\ "0 or 1." # return the proof object return proof
class BaseRecorder: bitPacker = None lock = None semaphore = None numObservationBits = None def __init__ ( self, bitDepth, numberOfChannels, sampleRate, widebandFirstStopband, widebandFirstPassband, widebandSecondPassband, widebandSecondStopband, passbandAttenuation, stopbandAttenuation, outputFileName, filter, writeOutput ): self.signal = None self.bitDepth = bitDepth self.numberOfChannels = numberOfChannels self.sampleRate = int( sampleRate ) self.widebandFirstStopband = widebandFirstStopband self.widebandFirstPassband = widebandFirstPassband self.widebandSecondPassband = widebandSecondPassband self.widebandSecondStopband = widebandSecondStopband self.passbandAttenuation = passbandAttenuation self.stopbandAttenuation = stopbandAttenuation self.writeOutput = writeOutput self.applyFilter = filter if( self.writeOutput ): self.outputFileName = outputFileName else: self.outputFileName = None if( self.applyFilter ): self.widebandFilter = \ python_initialize_kaiser_filter ( self.widebandFirstStopband, self.widebandFirstPassband, self.widebandSecondPassband, self.widebandSecondStopband, self.passbandAttenuation, self.stopbandAttenuation, int( self.sampleRate ) ) else: self.widebandFilter = None def record( self, device ): if ( \ device.hasAppropriateStream ( \ CAHAL_DEVICE_INPUT_STREAM, \ self.numberOfChannels, \ self.bitDepth, \ self.sampleRate \ ) \ ): flags = \ CAHAL_AUDIO_FORMAT_FLAGISSIGNEDINTEGER \ | CAHAL_AUDIO_FORMAT_FLAGISPACKED BaseRecorder.bitPacker = BitPacker() BaseRecorder.lock = _threading.Lock() BaseRecorder.semaphore = _threading.Semaphore( 0 ) self.signal = BitStream( BaseRecorder.bitPacker ) if ( start_recording ( \ device.struct, \ CAHAL_AUDIO_FORMAT_LINEARPCM, \ self.numberOfChannels, \ self.sampleRate, \ self.bitDepth, \ bufferSamples, \ flags \ ) \ ): print "Starting recording..." self.processObservations() print "Stopping recording..." cahal_stop_recording() print "Stopped recording." else: print "ERROR: Could not start recording." else: print "ERROR: Could not find an appropriate stream." BaseRecorder.bitPacker = None BaseRecorder.lock = None BaseRecorder.semaphore = None def processObservations( self ): done = False while( not done ): buffer = None nBitsToRead = self.getNumberOfBitsToRead() BaseRecorder.semaphore.acquire( True ) BaseRecorder.lock.acquire( True ) availableData = self.signal.getSize() BaseRecorder.lock.release() #print "Available %d, waiting for %d." %( availableData, nBitsToRead ) while( availableData >= nBitsToRead and not done ): BaseRecorder.lock.acquire( True ) buffer = self.signal.read( nBitsToRead ) BaseRecorder.lock.release() if( None != buffer ): done = self.processBuffer( buffer ) nBitsToRead = self.getNumberOfBitsToRead() BaseRecorder.lock.acquire( True ) availableData = self.signal.getSize() BaseRecorder.lock.release() #print "Available %d, waiting for %d." %( availableData, nBitsToRead ) def getNumberOfBitsToRead( self ): return( 0 ) def processbuffer( self, buffer ): return( True ) def __del__( self ): if( self.writeOutput ): WAVRecorder.saveSignalToWAV ( self.signal, self.outputFileName, self.numberOfChannels, self.bitDepth, self.sampleRate ) if( self.signal ): self.signal = None if( self.widebandFilter ): csignal_destroy_passband_filter( self.widebandFilter ) BaseRecorder.bitPacker = None BaseRecorder.lock = None BaseRecorder.semaphore = None BaseRecorder.numObservationBits = None
def decompress_video(self, decompress_path): with open(decompress_path, "wb") as write_stream: #Get the bitstream to read the compression read_stream = BitStream(self.file_path) read_stream.set_offset(len(self.header) * 8) #Write the header for the video write_stream.write(self.header.encode()) write_stream.write("FRAME\n".encode()) #This marks the start of decompression number_of_numbers = 0 array_of_nums = [] counter = 0 counter_bits = 0 while read_stream.read_bits(5000): got_number = self.gomby.add_bits(read_stream.get_bit_array()) read_stream.delete_bits(5000) counter_bits += 1 if got_number: nums = self.gomby.decode_nums() number_of_numbers += len(nums) array_of_nums += nums print("Decompressed", number_of_numbers) print("Need to get", self.frame.limit_to_convert) if number_of_numbers >= self.frame.limit_to_convert: counter += 1 print("Decompressing frame: ", counter) array_of_nums = self.frame.set_frame_by_array( array_of_nums) y, u, v = self.frame.decompress_frame(self.decompress_mode) write_stream.write(y.astype(np.uint8)) write_stream.write(u.astype(np.uint8)) write_stream.write(v.astype(np.uint8)) write_stream.write("FRAME\n".encode()) number_of_numbers -= self.frame.limit_to_convert print("Finished writing decompressed frame, now only have", number_of_numbers) num_bits = read_stream.read_allbits() got_number = self.gomby.add_bits(read_stream.get_bit_array()) if got_number: nums = self.gomby.decode_nums() array_of_nums += nums ## At this point we have all of the frame saved in memory, we will now start to divide it in frames to proceed to decompressing array_of_nums = self.frame.set_frame_by_array(array_of_nums) y, u, v = self.frame.decompress_frame(self.decompress_mode) write_stream.write(y.astype(np.uint8)) write_stream.write(u.astype(np.uint8)) write_stream.write(v.astype(np.uint8)) print("Finished writing decompressed frame") #girar print("Finished writing decompressed frame")
def decrypt_to_bitstream(self, ciphertext, task_monitor=None, force=False): """ Decrypts the given ciphertext into a bitstream. If the bitstream was originally encrypted with PublicKey.encrypt_X(), then this method returns a bitstream following the format described in Note 001 of the Ciphertext.py file: [size (64 bits) | message (size bits) | padding (X bits) ] Arguments: ciphertext::Ciphertext -- An encrypted Ciphertext object. task_monitor::TaskMonitor -- A task monitor for this task. force:bool -- Set this to true if you wish to force a decryption attempt, even when the ciphertext's stored public key fingerprint does not match that of the public key associated with this private key. Returns: bitstream::Bitstream -- A bitstream containing the unencrypted data. Throws: IncompatibleCiphertextError -- The given ciphertext does not appear to be decryptable with the selected private key. """ # Check that the public key fingerprint stored in the ciphertext # matches the public key associated with this private key. if(not force): if(ciphertext.nbits != self.cryptosystem.get_nbits()): raise IncompatibleCiphertextError("The given ciphertext is " \ "not decryptable with the selected private key: " \ "incompatible cryptosystem/key sizes.") if(ciphertext.pk_fingerprint != self.public_key.get_fingerprint()): raise IncompatibleCiphertextError("The given ciphertext is " \ "not decryptable with the selected private key: " \ "public key fingerprint mismatch.") # We read and decrypt the ciphertext block by block # See "Handbook of Applied Cryptography" Algorithm 8.18 bitstream = BitStream() block_size = self.cryptosystem.get_nbits() - 1 prime = self.cryptosystem.get_prime() key = self._key # Check if we have a task monitor and register with it if(task_monitor != None): # One tick per block ticks = ciphertext.get_length() decrypt_task_mon = \ task_monitor.new_subtask("Decrypt data", expected_ticks = ticks) for gamma, delta in ciphertext: assert max(gamma, delta) < 2**(block_size + 1), \ "The ciphertext object includes blocks larger than the " \ "expected block size." m = (pow(gamma, prime - 1 - key, prime) * delta) % prime bitstream.put_num(m, block_size) if(task_monitor != None): decrypt_task_mon.tick() return bitstream
def encrypt_bitstream(self, bitstream, pad_to=None, task_monitor=None): """ Encrypts the given bitstream into a ciphertext object. Arguments: bitstream::BitStream-- A stream of bits to encrypt (see BitStream utility class). pad_to::int -- Minimum size (in bytes) of the resulting ciphertext. Data will be padded before encryption to match this size. task_monitor::TaskMonitor -- A task monitor for this task. Returns: ciphertext:Ciphertext -- A ciphertext object encapsulating the encrypted data. """ random = StrongRandom() ## PART 1 # First, format the bitstream as per Ciphertext.py Note 001, # previous to encryption. # [size (64 bits) | message (size bits) | padding (X bits) ] ## formated_bitstream = BitStream() # The first 64 encode the size of the actual data in bits SIZE_BLOCK_LENGTH = 64 size_in_bits = bitstream.get_length() if(size_in_bits >= 2**SIZE_BLOCK_LENGTH): raise ValueError("The size of the bitstream to encrypt is larger " \ "than 16 Exabits. The current format for " \ "PloneVote ciphertext only allows encrypting a " \ "maximum of 16 Exabits of information.") formated_bitstream.put_num(size_in_bits, SIZE_BLOCK_LENGTH) # We then copy the contents of the original bitstream bitstream.seek(0) formated_bitstream.put_bitstream_copy(bitstream) # Finally, we append random data until we reach the desired pad_to # length unpadded_length = formated_bitstream.get_length() if(pad_to != None and (pad_to * 8) > unpadded_length): full_length = pad_to * 8 else: full_length = unpadded_length padding_left = full_length - unpadded_length while(padding_left > 1024): padding_bits = random.randint(1, 2**1024) formated_bitstream.put_num(padding_bits,1024) padding_left -= 1024 if(padding_left > 0): padding_bits = random.randint(1, 2**padding_left) formated_bitstream.put_num(padding_bits, padding_left) padding_left = 0 ## PART 2 # We encrypt the formated bitsteam using ElGamal into a Ciphertext # object. # See "Handbook of Applied Cryptography" Algorithm 8.18 ## # block_size is the size of each block of bits to encrypt # since we can only encrypt messages in [0, p - 1] # we should use (nbits - 1) as the block size, where # 2**(nbits - 1) < p < 2**nbits block_size = self.cryptosystem.get_nbits() - 1 prime = self.cryptosystem.get_prime() generator = self.cryptosystem.get_generator() # We pull data from the bitstream one block at a time and encrypt it formated_bitstream.seek(0) ciphertext = \ Ciphertext(self.cryptosystem.get_nbits(), self.get_fingerprint()) plaintext_bits_left = formated_bitstream.get_length() # Check if we have a task monitor and register with it if(task_monitor != None): # We will do two tick()s per block to encrypt: one for generating # the gamma component of the ciphertext block and another for the # delta component (those are the two time intensive steps, # because of exponentiation). ticks = math.ceil((1.0 * plaintext_bits_left) / block_size) * 2 encrypt_task_mon = \ task_monitor.new_subtask("Encrypt data", expected_ticks = ticks) while(plaintext_bits_left > 0): # get next block (message, m, etc) to encrypt if(plaintext_bits_left >= block_size): block = formated_bitstream.get_num(block_size) plaintext_bits_left -= block_size else: block = formated_bitstream.get_num(plaintext_bits_left) # Encrypt as if the stream was filled with random data past its # end, this avoids introducing a 0's gap during decryption to # bitstream displacement = block_size - plaintext_bits_left block = block << displacement padding = random.randint(0, 2**displacement - 1) assert (padding / 2**displacement == 0), \ "padding should be at most displacement bits long" block = block | padding plaintext_bits_left = 0 # Select a random integer k, 1 <= k <= p − 2 k = random.randint(1, prime - 2) # Compute gamma and delta gamma = pow(generator, k, prime) if(task_monitor != None): encrypt_task_mon.tick() delta = (block * pow(self._key, k, prime)) % prime if(task_monitor != None): encrypt_task_mon.tick() # Add this encrypted data portion to the ciphertext object ciphertext.append(gamma, delta) # return the ciphertext object return ciphertext
class Ciphertext: """ An object representing encrypted PloneVote data. Ciphertext objects are created by PublicKey encrypt operations and decrypted through PrivateKey decrypt methods (or through ThresholdDecryptionCombinator if the data was encrypted with a threshold public key and all partial decryptions are available). This class can also be store to and loaded from file using the PloneVote armored ciphertext XML format. Attributes: nbits::int -- Size in bits of the cryptosystem/public key used to encrypt this ciphertext. pk_fingerprint::string -- A fingerprint of the public key used to encrypt this ciphertext. This fingerprint can then be compared with the result from PublicKey.get_fingerprint() to check for compatibility with a given key pair or threshold public key. gamma::long[] delta::long[] -- : This two attributes should only be accessed by key classes within PloneVoteCryptoLib. See "Handbook of Applied Cryptography" Algorithm 8.18 for the meaning of the variables. An array is used because the encrypted data might be longer than the cryptosystem's bit size. """ def to_dict(self): return {'a': self.gamma, 'b': self.delta} def get_length(self): """ Returns the length, in blocks, of the ciphertext. """ assert len(self.gamma) == len(self.delta), "Each gamma component of " \ "the ciphertext must correspond " \ " to one delta component." return len(self.gamma) def __getitem__(self, i): """ Makes this object indexable. Returns: (gamma, delta)::(long, long) -- Returns the gamma, delta pair representing a particular block of the encrypted data. Use ciphertext[i] for block i. """ return (self.gamma[i], self.delta[i]) def __iter__(self): """ Return an iterator (CiphertextIterator) for the current ciphertext. """ return CiphertextIterator(self) def __eq__(self, other): """ Implements Ciphertext equality. Two ciphertexts are equal if they have the same bit size, public key fingerprint and list of gamma and delta components. A ciphertext is not equal to any object of a different type. """ if (isinstance(other, Ciphertext) and (other.nbits == self.nbits) and (other.pk_fingerprint == self.pk_fingerprint) and (other.gamma == self.gamma) and (other.delta == self.delta)): return True else: return False def __ne__(self, other): """ Implements Ciphertext inequality. See __eq__(...) for the definition of Ciphertext equality, inequality its is negation. """ return not self.__eq__(other) def get_fingerprint(self): """ Gets a fingerprint of the current ciphertext. A ciphertext fingerprint is generated as a SHA-256 hash of the ciphertext, block by block. Returns: fingerprint::string -- A SHA-256 hexdigest providing a fingerprint of the current ciphertext. """ fingerprint = sha256() for (gamma, delta) in self: fingerprint.update(hex(gamma)) fingerprint.update(hex(delta)) return fingerprint.hexdigest() def __init__(self, nbits, public_key_fingerprint): """ Create an empty ciphertext object. Arguments: nbits::int -- Size in bits of the cryptosystem/public key used to encrypt this ciphertext. public_key_fingerprint::string -- The fingerprint of the public key used to encrypt this data. """ self.gamma = [] self.delta = [] self.nbits = nbits self.pk_fingerprint = public_key_fingerprint def append(self, gamma, delta): """ Used internally by PublicKey. This method adds an encrypted block of data with its gamma and delta components from ElGamal (see HoAC Alg. 8.18). """ self.gamma.append(gamma) self.delta.append(delta) def _encrypted_data_as_bitstream(self): """ Returns the contents of this ciphertext as a BitStream object. This includes only the encrypted data (gamma and delta components), not the nbits and public key fingerprint metadata. The components are encoded alternating as follows: [gamma[0], delta[0], gamma[1], delta[1], ...] with each component represented as a nbits long number. Returns: bitstream::BitStream -- The gamma and delta components of this ciphertext as a bitstream. """ bitstream = BitStream() for i in range(0, self.get_length()): bitstream.put_num(self.gamma[i], self.nbits) bitstream.put_num(self.delta[i], self.nbits) return bitstream def _encrypted_data_as_base64(self): """ Returns the contents of this ciphertext as a base64 string. This includes only the encrypted data (gamma and delta components), not the nbits and public key fingerprint metadata. """ bitstream = self._encrypted_data_as_bitstream() bitstream.seek(0) length = bitstream.get_length() assert length % 8 == 0, \ "The ciphertext data must be a multiple of eight bits in size." return bitstream.get_base64(length) def to_file(self, filename, SerializerClass=serialize.XMLSerializer): """ Saves this ciphertext to a file. Arguments: filename::string -- The path to the file in which to store the serialized Ciphertext object. SerializerClass::class -- The class that provides the serialization. XMLSerializer by default. Must inherit from serialize.BaseSerializer and provide an adequate serialize_to_file method. Note that often the same class used to serialize the data must be used to deserialize it. (see utilities/serialize.py documentation for more information) """ # Create a new serializer object for the Ciphertext structure definition serializer = SerializerClass(Ciphertext_serialize_structure_definition) # Generate a serializable data dictionary matching the definition: data = { "PloneVoteCiphertext": { "nbits": str(self.nbits), "PKFingerprint": self.pk_fingerprint, "EncryptedData": self._encrypted_data_as_base64() } } # Use the serializer to store the data to file serializer.serialize_to_file(filename, data) @classmethod def from_file(cls, filename, SerializerClass=serialize.XMLSerializer): """ Loads an instance of Ciphertext from the given file. Arguments: filename::string -- The name of a file containing the ciphertext in serialized form. SerializerClass::class -- The class that provides the deserialization. XMLSerializer by default. Must inherit from serialize.BaseSerializer and provide an adequate deserialize_from_file method. Note that often the same class used to serialize the data must be used to deserialize it. (see utilities/serialize.py documentation for more information) Throws: InvalidPloneVoteCryptoFileError -- If the file is not a valid PloneVoteCryptoLib stored ciphertext file. """ # Create a new serializer object for the Ciphertext structure definition serializer = SerializerClass(Ciphertext_serialize_structure_definition) # Deserialize the Ciphertext instance from file try: data = serializer.deserialize_from_file(filename) except serialize.InvalidSerializeDataError, e: # Convert the exception to an InvalidPloneVoteCryptoFileError raise InvalidPloneVoteCryptoFileError(filename, "File \"%s\" does not contain a valid ciphertext. The " \ "following error occurred while trying to deserialize the " \ "file contents: %s" % (filename, str(e))) # Get the values from the deserialized data try: nbits = int(data["PloneVoteCiphertext"]["nbits"]) except ValueError: raise InvalidPloneVoteCryptoFileError(filename, "File \"%s\" does not contain a valid ciphertext. The " \ "stored value for nbits is not a valid (decimal) integer." \ % filename) fingerprint_str = data["PloneVoteCiphertext"]["PKFingerprint"] enc_data_str = data["PloneVoteCiphertext"]["EncryptedData"] # Construct a new Ciphertext object with the given nbits and fingerprint ciphertext = cls(nbits, fingerprint_str) # Load the encrypted data bitstream = BitStream() bitstream.put_base64(enc_data_str) bitstream.seek(0) length = bitstream.get_length() # number of gamma and delta blocks in the bitstream: blocks = length / (nbits * 2) for i in range(0, blocks): gamma_val = bitstream.get_num(nbits) delta_val = bitstream.get_num(nbits) ciphertext.append(gamma_val, delta_val) # Return the ciphertext return ciphertext
def new(cls, original_collection, shuffled_collection, mapping): """ Constructs a new proof of equivalence between original_collection and shuffled_collection. This method should not be used outside of plonevotecryptolib.Mixnet. Consider using CiphertextCollection.shuffle_with_proof() instead. The given CiphertextCollectionMapping must be a valid mapping between original_collection and shuffled_collection. Arguments: original_collection::CiphertextCollection -- The original collection to be shuffled. shuffled_collection::CiphertextCollection -- The shuffled collection resulting from applying mapping to original_collection. mapping::CiphertextCollectionMapping -- The mapping between original_collection and shuffled_collection. Returns: proof::ShufflingProof -- The zero-knowledge proof of equivalence between original_collection and shuffled_collection. Throws: InvalidCiphertextCollectionMappingError -- If mapping is not a valid mapping between original_collection and shuffled_collection. ValueError -- If params.SHUFFLING_PROOF_SECURITY_PARAMETER is within an invalid range. """ # Check that we have a valid mapping between the two collections: if (not mapping.verify(original_collection, shuffled_collection)): raise InvalidCiphertextCollectionMappingError( \ "mapping was expected to be a CiphertextCollectionMapping "\ "object representing a valid mapping between "\ "original_collection and shuffled_collection. However, "\ "mapping.verify(original_collection, shuffled_collection) "\ "returns False. A zero-knowledge proof of equivalence "\ "between two shuffled collections cannot be constructed "\ "without first having a valid mapping between them.") # Get the security parameter P security_parameter = params.SHUFFLING_PROOF_SECURITY_PARAMETER # Check that the security parameter is <= 256, since that is the most # bits of challenge we can currently have. Also check that P is at least 1 if (security_parameter < 1 or security_parameter > 256): raise ValueError("Security parameter for shuffling proof " \ "(params.SHUFFLING_PROOF_SECURITY_PARAMETER) is out of " \ "range. The security parameter must be between 1 and 256, "\ "its current value is %d. If you have set " \ "CUSTOM_SHUFFLING_PROOF_SECURITY_PARAMETER in the file " \ "params.py, please correct this value or set it to None, " \ "in order for the security parameter to be decided based " \ "on the global SECURITY_LEVEL of plonevotecryptolib. It " \ "is recommended that " \ "CUSTOM_SHUFFLING_PROOF_SECURITY_PARAMETER be always set " \ "to None for deployment operation of plonevotecryptolib." \ % security_parameter) # Construct a new empty proof proof = ShufflingProof() # Populate proof._collections with P random shuffles of # original_collection. We save each mapping for now in proof._mappings. # (ie. every mapping in proof._mappings[i] will initially be from the # original collection into proof._collections[i]) # generate new mappings in parallel pool = multiprocessing.Pool() async_params = [original_collection] * security_parameter generate_mappings_pool = pool.map_async(new_collection_mapping, async_params) for new_mapping in generate_mappings_pool.get(99999999): proof._mappings.append(new_mapping) proof._collections.append(new_mapping.apply(original_collection)) pool.close() pool.join() # Generate the challenge proof._challenge = \ proof._generate_challenge(original_collection, shuffled_collection) # Get the challenge as a BitStream for easier manipulation challenge_bits = BitStream() challenge_bits.put_hex(proof._challenge) challenge_bits.seek(0) # back to the beginning of the stream # For each of the first P bits in the stream for i in range(0, security_parameter): ## print "Processing challenge bit %d" % i # replace with TaskMonitor API calls bit = challenge_bits.get_num(1) if (bit == 0): # Do nothing. # proof._mappings[i] is already a mapping from # original_collection unto proof._collections[i] pass elif (bit == 1): # Change proof._mappings[i] to be a mapping from # proof._collections[i] unto shuffled_collection, using # CiphertextCollectionMapping.rebase(...) # rebase(O->D, O->C_{i}) => C_{i}->D rebased_mapping = mapping.rebase(proof._mappings[i]) # Replace O->C_{i} with C_{i}->D proof._mappings[i] = rebased_mapping else: assert False, "We took a single bit, its value must be either "\ "0 or 1." # return the proof object return proof
def compress_video(self, compress_path, mode="JPEG-1"): if mode not in ["JPEG-" + str(i) for i in range(1, 8)] and mode != "JPEG-LS": print("Invalid mode") return None with open(self.file_path, "rb") as stream: line = stream.readline() gomby = Golomb(4) bit_stream = BitStream() header = self.header[:-1] + " G" + str( gomby.encoding_parameter) + " M" + mode + "\n" with open(compress_path, "wb") as file_path: file_path.write(header.encode()) counter = 0 while True: start_time = time() if counter > 2: break counter += 1 print("Compressing frame: ", counter) try: line = stream.readline() except ValueError: print("Finished compressing") break line = stream.readline() self.frame.set_frame(stream) start = time() compressed_frame = self.frame.compress_frame(mode) print("Compressed in ", time() - start) y = compressed_frame[0] u = compressed_frame[1] v = compressed_frame[2] for x in np.nditer(y): bit_stream.add_to_bit_array(gomby.encode(int(x))) #bit_stream.write_allbits(compress_path) print("Finished compressing Y") for x in np.nditer(u): bit_stream.add_to_bit_array(gomby.encode(int(x))) #bit_stream.write_allbits(compress_path) print("Finished compressing U") for x in np.nditer(v): bit_stream.add_to_bit_array(gomby.encode(int(x))) bit_stream.write_allbits(compress_path) print("Finished compressing in", time() - start_time) bit_stream.close(compress_path)
class BaseRecorder: bitPacker = None lock = None semaphore = None numObservationBits = None def __init__(self, bitDepth, numberOfChannels, sampleRate, widebandFirstStopband, widebandFirstPassband, widebandSecondPassband, widebandSecondStopband, passbandAttenuation, stopbandAttenuation, outputFileName, filter, writeOutput): self.signal = None self.bitDepth = bitDepth self.numberOfChannels = numberOfChannels self.sampleRate = int(sampleRate) self.widebandFirstStopband = widebandFirstStopband self.widebandFirstPassband = widebandFirstPassband self.widebandSecondPassband = widebandSecondPassband self.widebandSecondStopband = widebandSecondStopband self.passbandAttenuation = passbandAttenuation self.stopbandAttenuation = stopbandAttenuation self.writeOutput = writeOutput self.applyFilter = filter if (self.writeOutput): self.outputFileName = outputFileName else: self.outputFileName = None if (self.applyFilter): self.widebandFilter = \ python_initialize_kaiser_filter ( self.widebandFirstStopband, self.widebandFirstPassband, self.widebandSecondPassband, self.widebandSecondStopband, self.passbandAttenuation, self.stopbandAttenuation, int( self.sampleRate ) ) else: self.widebandFilter = None def record(self, device): if ( \ device.hasAppropriateStream ( \ CAHAL_DEVICE_INPUT_STREAM, \ self.numberOfChannels, \ self.bitDepth, \ self.sampleRate \ ) \ ): flags = \ CAHAL_AUDIO_FORMAT_FLAGISSIGNEDINTEGER \ | CAHAL_AUDIO_FORMAT_FLAGISPACKED BaseRecorder.bitPacker = BitPacker() BaseRecorder.lock = _threading.Lock() BaseRecorder.semaphore = _threading.Semaphore(0) self.signal = BitStream(BaseRecorder.bitPacker) if ( start_recording ( \ device.struct, \ CAHAL_AUDIO_FORMAT_LINEARPCM, \ self.numberOfChannels, \ self.sampleRate, \ self.bitDepth, \ bufferSamples, \ flags \ ) \ ): print "Starting recording..." self.processObservations() print "Stopping recording..." cahal_stop_recording() print "Stopped recording." else: print "ERROR: Could not start recording." else: print "ERROR: Could not find an appropriate stream." BaseRecorder.bitPacker = None BaseRecorder.lock = None BaseRecorder.semaphore = None def processObservations(self): done = False while (not done): buffer = None nBitsToRead = self.getNumberOfBitsToRead() BaseRecorder.semaphore.acquire(True) BaseRecorder.lock.acquire(True) availableData = self.signal.getSize() BaseRecorder.lock.release() #print "Available %d, waiting for %d." %( availableData, nBitsToRead ) while (availableData >= nBitsToRead and not done): BaseRecorder.lock.acquire(True) buffer = self.signal.read(nBitsToRead) BaseRecorder.lock.release() if (None != buffer): done = self.processBuffer(buffer) nBitsToRead = self.getNumberOfBitsToRead() BaseRecorder.lock.acquire(True) availableData = self.signal.getSize() BaseRecorder.lock.release() #print "Available %d, waiting for %d." %( availableData, nBitsToRead ) def getNumberOfBitsToRead(self): return (0) def processbuffer(self, buffer): return (True) def __del__(self): if (self.writeOutput): WAVRecorder.saveSignalToWAV(self.signal, self.outputFileName, self.numberOfChannels, self.bitDepth, self.sampleRate) if (self.signal): self.signal = None if (self.widebandFilter): csignal_destroy_passband_filter(self.widebandFilter) BaseRecorder.bitPacker = None BaseRecorder.lock = None BaseRecorder.semaphore = None BaseRecorder.numObservationBits = None
def verify(self, original_collection, shuffled_collection): """ Verifies that original_collection and shuffled_collection are equivalent as proven by this ShufflingProof object. If this method returns true, then we have proof that both collections contain encryptions of the same collection of plaintexts, save for the negligible probability (for a correct security parameter configured in params.py) that the zero-knowledge proof has been faked. Otherwise we gain no information about the two collections, other than they are not shown to be equivalent by this particular proof. ShufflingProof is a zero-knowledge proof: the result of this verification or the information within the ShufflingProof object for which two collections pass this verification, provide no information as to the association of particular ciphertexts in original_collection with particular ciphertexts of shuffled_collection. Arguments: original_collection::CiphertextCollection -- The original collection of ciphertexts. shuffled_collection::CiphertextCollection -- Another collection for which we wish to know if the current ShufflingProof object demonstrates equivalence with original_collection. Returns: result::bool -- True if this proof shows both collections to be equivalent. False otherwise. """ # Get the security parameter P with which the proof was originally # created. This is reflect in the length of self._collections and # self._mappings. assert len(self._collections) == len(self._mappings), \ "The length of the private properties self._collections and " \ "self._mappings of ShufflingProof must always be the same." security_parameter = len(self._collections) # Get the security parameter specified in params minimum_allowed_security_parameter = params.SHUFFLING_PROOF_SECURITY_PARAMETER # Check that the security parameter is <= 256, since that is the most # bits of challenge we can currently have. Also check that P is at least 1 if (minimum_allowed_security_parameter < 1 or minimum_allowed_security_parameter > 256): raise ValueError("Security parameter for shuffling proof " \ "(params.SHUFFLING_PROOF_SECURITY_PARAMETER) is out of " \ "range. The security parameter must be between 1 and 256, "\ "its current value is %d. If you have set " \ "CUSTOM_SHUFFLING_PROOF_SECURITY_PARAMETER in the file " \ "params.py, please correct this value or set it to None, " \ "in order for the security parameter to be decided based " \ "on the global SECURITY_LEVEL of plonevotecryptolib. It " \ "is recommended that " \ "CUSTOM_SHUFFLING_PROOF_SECURITY_PARAMETER be always set " \ "to None for deployment operation of plonevotecryptolib." \ % security_parameter) # Check that the security parameter for which the proof was generated # is correct and meets the security standards declared in params.py if (security_parameter < minimum_allowed_security_parameter or security_parameter < 1 or security_parameter > 256): raise InvalidShuffilingProofError("The security parameter for " \ "(which the current proof was created is %d. This is " \ "(either an incorrect value (outside of the [1,256] " \ "(range) or not secure enough to meet the standards set " \ "(in the configuration of params.py (< %d). The proof " \ "(is thus invalid." \ % (security_parameter, minimum_allowed_security_parameter)) # Generate the challenge challenge = \ self._generate_challenge(original_collection, shuffled_collection) # Verify that the challenge corresponds to the stored one if (challenge != self._challenge): return False # Get the challenge as a BitStream for easier manipulation challenge_bits = BitStream() challenge_bits.put_hex(challenge) challenge_bits.seek(0) # back to the beginning of the stream # For each of the first P bits in the stream for i in range(0, security_parameter): bit = challenge_bits.get_num(1) if (bit == 0): # verify that self._mapping[i] maps original_collection into # self._collections[i] if (not self._mappings[i].verify(original_collection, self._collections[i])): return False elif (bit == 1): # verify that self._mapping[i] self._collections[i] into # self._collections[i] if (not self._mappings[i].verify(self._collections[i], shuffled_collection)): return False else: assert False, "We took a single bit, its value must be either "\ "0 or 1." # If we made it so far, the proof is correct # (each mapping is in accordance to the challenge and valid) return True
def decrypt_to_bitstream(self, ciphertext, task_monitor=None, force=False): """ Decrypts the given ciphertext into a bitstream. If the bitstream was originally encrypted with PublicKey.encrypt_X(), then this method returns a bitstream following the format described in Note 001 of the Ciphertext.py file: [size (64 bits) | message (size bits) | padding (X bits) ] Arguments: ciphertext::Ciphertext -- An encrypted Ciphertext object. task_monitor::TaskMonitor -- A task monitor for this task. force:bool -- Set this to true if you wish to force a decryption attempt, even when the ciphertext's stored public key fingerprint does not match that of the public key associated with this private key. Returns: bitstream::Bitstream -- A bitstream containing the unencrypted data. Throws: IncompatibleCiphertextError -- The given ciphertext does not appear to be decryptable with the selected private key. """ # Check that the public key fingerprint stored in the ciphertext # matches the public key associated with this private key. if (not force): if (ciphertext.nbits != self.cryptosystem.get_nbits()): raise IncompatibleCiphertextError("The given ciphertext is " \ "not decryptable with the selected private key: " \ "incompatible cryptosystem/key sizes.") if (ciphertext.pk_fingerprint != self.public_key.get_fingerprint()): raise IncompatibleCiphertextError("The given ciphertext is " \ "not decryptable with the selected private key: " \ "public key fingerprint mismatch.") # We read and decrypt the ciphertext block by block # See "Handbook of Applied Cryptography" Algorithm 8.18 bitstream = BitStream() block_size = self.cryptosystem.get_nbits() - 1 prime = self.cryptosystem.get_prime() key = self._key # Check if we have a task monitor and register with it if (task_monitor != None): # One tick per block ticks = ciphertext.get_length() decrypt_task_mon = \ task_monitor.new_subtask("Decrypt data", expected_ticks = ticks) for gamma, delta in ciphertext: assert max(gamma, delta) < 2**(block_size + 1), \ "The ciphertext object includes blocks larger than the " \ "expected block size." m = (pow(gamma, prime - 1 - key, prime) * delta) % prime bitstream.put_num(m, block_size) if (task_monitor != None): decrypt_task_mon.tick() return bitstream
def encrypt_bitstream(self, bitstream, pad_to=None, task_monitor=None): """ Encrypts the given bitstream into a ciphertext object. Arguments: bitstream::BitStream-- A stream of bits to encrypt (see BitStream utility class). pad_to::int -- Minimum size (in bytes) of the resulting ciphertext. Data will be padded before encryption to match this size. task_monitor::TaskMonitor -- A task monitor for this task. Returns: ciphertext:Ciphertext -- A ciphertext object encapsulating the encrypted data. """ random = StrongRandom() ## PART 1 # First, format the bitstream as per Ciphertext.py Note 001, # previous to encryption. # [size (64 bits) | message (size bits) | padding (X bits) ] ## formated_bitstream = BitStream() # The first 64 encode the size of the actual data in bits SIZE_BLOCK_LENGTH = 64 size_in_bits = bitstream.get_length() if (size_in_bits >= 2**SIZE_BLOCK_LENGTH): raise ValueError("The size of the bitstream to encrypt is larger " \ "than 16 Exabits. The current format for " \ "PloneVote ciphertext only allows encrypting a " \ "maximum of 16 Exabits of information.") formated_bitstream.put_num(size_in_bits, SIZE_BLOCK_LENGTH) # We then copy the contents of the original bitstream bitstream.seek(0) formated_bitstream.put_bitstream_copy(bitstream) # Finally, we append random data until we reach the desired pad_to # length unpadded_length = formated_bitstream.get_length() if (pad_to != None and (pad_to * 8) > unpadded_length): full_length = pad_to * 8 else: full_length = unpadded_length padding_left = full_length - unpadded_length while (padding_left > 1024): padding_bits = random.randint(1, 2**1024) formated_bitstream.put_num(padding_bits, 1024) padding_left -= 1024 if (padding_left > 0): padding_bits = random.randint(1, 2**padding_left) formated_bitstream.put_num(padding_bits, padding_left) padding_left = 0 ## PART 2 # We encrypt the formated bitsteam using ElGamal into a Ciphertext # object. # See "Handbook of Applied Cryptography" Algorithm 8.18 ## # block_size is the size of each block of bits to encrypt # since we can only encrypt messages in [0, p - 1] # we should use (nbits - 1) as the block size, where # 2**(nbits - 1) < p < 2**nbits block_size = self.cryptosystem.get_nbits() - 1 prime = self.cryptosystem.get_prime() generator = self.cryptosystem.get_generator() # We pull data from the bitstream one block at a time and encrypt it formated_bitstream.seek(0) ciphertext = \ Ciphertext(self.cryptosystem.get_nbits(), self.get_fingerprint()) plaintext_bits_left = formated_bitstream.get_length() # Check if we have a task monitor and register with it if (task_monitor != None): # We will do two tick()s per block to encrypt: one for generating # the gamma component of the ciphertext block and another for the # delta component (those are the two time intensive steps, # because of exponentiation). ticks = math.ceil((1.0 * plaintext_bits_left) / block_size) * 2 encrypt_task_mon = \ task_monitor.new_subtask("Encrypt data", expected_ticks = ticks) while (plaintext_bits_left > 0): # get next block (message, m, etc) to encrypt if (plaintext_bits_left >= block_size): block = formated_bitstream.get_num(block_size) plaintext_bits_left -= block_size else: block = formated_bitstream.get_num(plaintext_bits_left) # Encrypt as if the stream was filled with random data past its # end, this avoids introducing a 0's gap during decryption to # bitstream displacement = block_size - plaintext_bits_left block = block << displacement padding = random.randint(0, 2**displacement - 1) assert (padding / 2**displacement == 0), \ "padding should be at most displacement bits long" block = block | padding plaintext_bits_left = 0 # Select a random integer k, 1 <= k <= p − 2 k = random.randint(1, prime - 2) # Compute gamma and delta gamma = pow(generator, k, prime) if (task_monitor != None): encrypt_task_mon.tick() delta = (block * pow(self._key, k, prime)) % prime if (task_monitor != None): encrypt_task_mon.tick() # Add this encrypted data portion to the ciphertext object ciphertext.append(gamma, delta) # return the ciphertext object return ciphertext