diff --git a/addresses.py b/addresses.py index 0ad8f5ea..33b87b36 100644 --- a/addresses.py +++ b/addresses.py @@ -100,7 +100,6 @@ def encodeAddress(version,stream,ripe): ripe = ripe[2:] elif ripe[:1] == '\x00': ripe = ripe[1:] - print 'within encodeAddress, length of ripe is:', len(ripe) a = encodeVarint(version) + encodeVarint(stream) + ripe sha = hashlib.new('sha512') sha.update(a) @@ -184,13 +183,10 @@ def decodeAddress(address): return status,addressVersionNumber,streamNumber,data[-24:-4] elif addressVersionNumber == 2: if len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) == 19: - print 'within decode address, lenth I think is 19' return status,addressVersionNumber,streamNumber,'\x00'+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4] elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) == 20: - print 'within decode address, lenth I think is 20' return status,addressVersionNumber,streamNumber,data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4] elif len(data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4]) == 18: - print 'within decode address, lenth I think is 18' return status,addressVersionNumber,streamNumber,'\x00\x00'+data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4] def addBMIfNotPresent(address): diff --git a/bitmessagemain.py b/bitmessagemain.py index f28d7846..c6a8e798 100644 --- a/bitmessagemain.py +++ b/bitmessagemain.py @@ -5,13 +5,14 @@ #Right now, PyBitmessage only support connecting to stream 1. It doesn't yet contain logic to expand into further streams. -softwareVersion = '0.1.6' +softwareVersion = '0.2.0' verbose = 2 maximumAgeOfAnObjectThatIAmWillingToAccept = 216000 #Equals two days and 12 hours. lengthOfTimeToLeaveObjectsInInventory = 237600 #Equals two days and 18 hours. This should be longer than maximumAgeOfAnObjectThatIAmWillingToAccept so that we don't process messages twice. maximumAgeOfObjectsThatIAdvertiseToOthers = 216000 #Equals two days and 12 hours maximumAgeOfNodesThatIAdvertiseToOthers = 10800 #Equals three hours - +storeConfigFilesInSameDirectoryAsProgram = True +userVeryEasyProofOfWorkForTesting = True #If you set this to True while on the normal network, you won't be able to send or sometimes receive messages. import sys try: @@ -69,7 +70,7 @@ class outgoingSynSender(QThread): time.sleep(1) resetTime = int(time.time()) #used below to clear out the alreadyAttemptedConnectionsList periodically so that we will retry connecting to hosts to which we have already tried to connect. while True: - time.sleep(999999)#I'm using this to prevent connections for testing. + #time.sleep(999999)#I'm using this to prevent connections for testing. if len(self.selfInitiatedConnectionList) < 8: #maximum number of outgoing connections = 8 random.seed() HOST, = random.sample(knownNodes[self.streamNumber], 1) @@ -354,7 +355,7 @@ class receiveDataThread(QThread): print 'Inventory (SQL on disk) already has object listed in inv message.' del self.objectsThatWeHaveYetToGet[objectHash] else: - print 'processData function making request for object:', repr(objectHash) + print 'processData function making request for object:', objectHash.encode('hex') self.sendgetdata(objectHash) del self.objectsThatWeHaveYetToGet[objectHash] #It is possible that the remote node doesn't respond with the object. In that case, we'll very likely get it from someone else anyway. break @@ -537,7 +538,6 @@ class receiveDataThread(QThread): message = self.data[readPosition:readPosition+messageLength] readPosition += messageLength signature = self.data[readPosition:readPosition+nLength] - print 'signature', repr(signature) sendersPubkey = rsa.PublicKey(convertStringToInt(nString),convertStringToInt(eString)) #print 'senders Pubkey', sendersPubkey try: @@ -595,13 +595,14 @@ class receiveDataThread(QThread): print 'The time in the msg message is too old. Ignoring it. Time:', embeddedTime return readPosition += 4 - inventoryHash = calculateInventoryHash(self.data[24:self.payloadLength+24]) + streamNumberAsClaimedByMsg, streamNumberAsClaimedByMsgLength = decodeVarint(self.data[readPosition:readPosition+9]) if streamNumberAsClaimedByMsg != self.streamNumber: print 'The stream number encoded in this msg (' + streamNumberAsClaimedByMsg + ') message does not match the stream number on which it was received. Ignoring it.' return readPosition += streamNumberAsClaimedByMsgLength + inventoryHash = calculateInventoryHash(self.data[24:self.payloadLength+24]) inventoryLock.acquire() if inventoryHash in inventory: print 'We have already received this msg message. Ignoring.' @@ -640,10 +641,206 @@ class receiveDataThread(QThread): printLock.release() #This is not an acknowledgement bound for me. See if it is a message bound for me by trying to decrypt it with my private keys. + + for key, cryptorObject in myECAddressHashes.items(): + try: + data = cryptorObject.decrypt(self.data[readPosition:self.payloadLength+24]) + toRipe = key #This is the RIPE hash of my pubkeys. We need this below to compare to the destination_ripe included in the encrypted data. + initialDecryptionSuccessful = True + print 'EC decryption successful using key associated with ripe hash:', key.encode('hex') + break + except Exception, err: + pass + #print 'cryptorObject.decrypt Exception:', err + + if initialDecryptionSuccessful: + #This is a message bound for me. + flushInventory() #so that we won't accidentially receive this message twice if the user restarts Bitmessage violently. + readPosition = 0 + messageVersion, messageVersionLength = decodeVarint(data[readPosition:readPosition+10]) + readPosition += messageVersionLength + if messageVersion != 1: + print 'Cannot understand message versions other than one. Ignoring message.' + return + sendersAddressVersionNumber, sendersAddressVersionNumberLength = decodeVarint(data[readPosition:readPosition+10]) + readPosition += sendersAddressVersionNumberLength + if sendersAddressVersionNumber == 0: + print 'Cannot understand sendersAddressVersionNumber = 0. Ignoring message.' + return + if sendersAddressVersionNumber >= 3: + print 'Sender\'s address version number', sendersAddressVersionNumber, ' not yet supported. Ignoring message.' + return + if len(data) < 170: + print 'Length of the unencrypted data is unreasonably short. Sanity check failed. Ignoring message.' + return + sendersStreamNumber, sendersStreamNumberLength = decodeVarint(data[readPosition:readPosition+10]) + readPosition += sendersStreamNumberLength + behaviorBitfield = data[readPosition:readPosition+4] + print 'behaviorBitfield',behaviorBitfield.encode('hex') + readPosition += 4 + pubSigningKey = '\x04' + data[readPosition:readPosition+64] + readPosition += 64 + pubEncryptionKey = '\x04' + data[readPosition:readPosition+64] + readPosition += 64 + endOfThePublicKeyPosition = readPosition #needed for when we store the pubkey in our database of pubkeys for later use. + if toRipe != data[readPosition:readPosition+20]: + printLock.acquire() + print 'The original sender of this message did not send it to you. Someone is attempting a Surreptitious Forwarding Attack.' + print 'See: http://tools.ietf.org/html/draft-ietf-smime-sender-auth-00' + print 'your toRipe:', toRipe.encode('hex') + print 'embedded destination toRipe:', data[readPosition:readPosition+20].encode('hex') + printLock.release() + return + readPosition += 20 + messageEncodingType, messageEncodingTypeLength = decodeVarint(data[readPosition:readPosition+10]) + readPosition += messageEncodingTypeLength + print 'Message Encoding Type:', messageEncodingType + messageLength, messageLengthLength = decodeVarint(data[readPosition:readPosition+10]) + print 'message length:', messageLength + readPosition += messageLengthLength + message = data[readPosition:readPosition+messageLength] + #print 'First 150 characters of message:', repr(message[:150]) + readPosition += messageLength + ackLength, ackLengthLength = decodeVarint(data[readPosition:readPosition+10]) + readPosition += ackLengthLength + ackData = data[readPosition:readPosition+ackLength] + readPosition += ackLength + positionOfBottomOfAckData = readPosition #needed to mark the end of what is covered by the signature + signatureLength, signatureLengthLength = decodeVarint(data[readPosition:readPosition+10]) + readPosition += signatureLengthLength + signature = data[readPosition:readPosition+signatureLength] + + try: + highlevelcrypto.verify(data[:positionOfBottomOfAckData],signature,pubSigningKey.encode('hex')) + print 'ECDSA verify passed' + except Exception, err: + print 'ECDSA verify failed', err + return + + printLock.acquire() + print 'As a matter of intellectual curiosity, here is the Bitcoin address associated with the keys owned by the other person:', calculateBitcoinAddressFromPubkey(pubSigningKey) + print 'And here is the testnet address:',calculateTestnetAddressFromPubkey(pubSigningKey),'. Be aware that the other person must take their private signing key from Bitmessage and import it into Bitcoin for it to be of any use. Do not use this unless you know what you are doing. Using testnet is recommended as no warranty is offered.' + printLock.release() + #calculate the fromRipe. + sha = hashlib.new('sha512') + sha.update(pubSigningKey+pubEncryptionKey) + ripe = hashlib.new('ripemd160') + ripe.update(sha.digest()) + + #Let's store the public key in case we want to reply to this person. + #We don't have the correct nonce or time (which would let us send out a pubkey message) so we'll just fill it with 1's. We won't be able to send this pubkey to others (without doing the proof of work ourselves, which this program is programmed to not do.) + t = (ripe.digest(),False,'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'+'\xFF\xFF\xFF\xFF'+data[messageVersionLength:endOfThePublicKeyPosition],int(time.time())+2419200) #after one month we may remove this pub key from our database. (2419200 = a month) + sqlLock.acquire() + sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') + sqlSubmitQueue.put(t) + sqlReturnQueue.get() + sqlLock.release() + + blockMessage = False #Gets set to True if the user shouldn't see the message according to black or white lists. + fromAddress = encodeAddress(sendersAddressVersionNumber,sendersStreamNumber,ripe.digest()) + if config.get('bitmessagesettings', 'blackwhitelist') == 'black': #If we are using a blacklist + t = (fromAddress,) + sqlLock.acquire() + sqlSubmitQueue.put('''SELECT label, enabled FROM blacklist where address=?''') + sqlSubmitQueue.put(t) + queryreturn = sqlReturnQueue.get() + sqlLock.release() + for row in queryreturn: + label, enabled = row + if enabled: + print 'Message ignored because address is in blacklist.' + blockMessage = True + else: #We're using a whitelist + t = (fromAddress,) + sqlLock.acquire() + sqlSubmitQueue.put('''SELECT label, enabled FROM whitelist where address=?''') + sqlSubmitQueue.put(t) + queryreturn = sqlReturnQueue.get() + sqlLock.release() + if queryreturn == []: + print 'Message ignored because address not in whitelist.' + blockMessage = True + for row in queryreturn: #It could be in the whitelist but disabled. Let's check. + label, enabled = row + if not enabled: + print 'Message ignored because address in whitelist but not enabled.' + blockMessage = True + + if not blockMessage: + print 'fromAddress:', fromAddress + print 'First 150 characters of message:', repr(message[:150]) + + #Look up the destination address (my address) based on the destination ripe hash. + #I realize that I could have a data structure devoted to this task, or maintain an indexed table + #in the sql database, but I would prefer to minimize the number of data structures this program + #uses. Searching linearly through the user's short list of addresses doesn't take very long anyway. + configSections = config.sections() + for addressInKeysFile in configSections: + if addressInKeysFile <> 'bitmessagesettings': + status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile) + if hash == key: + toAddress = addressInKeysFile + toLabel = config.get(addressInKeysFile, 'label') + if toLabel == '': + toLabel = addressInKeysFile + + if messageEncodingType == 2: + bodyPositionIndex = string.find(message,'\nBody:') + if bodyPositionIndex > 1: + subject = message[8:bodyPositionIndex] + body = message[bodyPositionIndex+6:] + else: + subject = '' + body = message + elif messageEncodingType == 1: + body = message + subject = '' + elif messageEncodingType == 0: + print 'messageEncodingType == 0. Doing nothing with the message. They probably just sent it so that we would store their public key or send their ack data for them.' + else: + body = 'Unknown encoding type.\n\n' + repr(message) + subject = '' + print 'within recmsg, inventoryHash is', inventoryHash.encode('hex') + if messageEncodingType <> 0: + sqlLock.acquire() + t = (inventoryHash,toAddress,fromAddress,subject,int(time.time()),body,'inbox') + sqlSubmitQueue.put('''INSERT INTO inbox VALUES (?,?,?,?,?,?,?)''') + sqlSubmitQueue.put(t) + sqlReturnQueue.get() + sqlLock.release() + self.emit(SIGNAL("displayNewMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),inventoryHash,toAddress,fromAddress,subject,body) + #Now let's send the acknowledgement + #POW, = unpack('>Q',hashlib.sha512(hashlib.sha512(ackData[24:]).digest()).digest()[4:12]) + #if POW <= 2**64 / ((len(ackData[24:])+payloadLengthExtraBytes) * averageProofOfWorkNonceTrialsPerByte): + #print 'The POW is strong enough that this ackdataPayload will be accepted by the Bitmessage network.' + #Currently PyBitmessage only supports sending a message with the acknowledgement in the form of a msg message. But future versions, and other clients, could send any object and this software will relay them. This can be used to relay identifying information, like your public key, through another Bitmessage host in case you believe that your Internet connection is being individually watched. You may pick a random address, hope its owner is online, and send a message with encoding type 0 so that they ignore the message but send your acknowledgement data over the network. If you send and receive many messages, it would also be clever to take someone else's acknowledgement data and use it for your own. Assuming that your message is delivered successfully, both will be acknowledged simultaneously (though if it is not delivered successfully, you will be in a pickle.) + #print 'self.data before:', repr(self.data) + #We'll need to make sure that our client will properly process the ackData; if the packet is malformed, we could clear out self.data and an attacker could use that behavior to determine that we were capable of decoding this message. + ackDataValidThusFar = True + if len(ackData) < 24: + print 'The length of ackData is unreasonably short. Not sending ackData.' + ackDataValidThusFar = False + if ackData[0:4] != '\xe9\xbe\xb4\xd9': + print 'Ackdata magic bytes were wrong. Not sending ackData.' + ackDataValidThusFar = False + if ackDataValidThusFar: + ackDataPayloadLength, = unpack('>L',ackData[16:20]) + if len(ackData)-24 != ackDataPayloadLength: + print 'ackData payload length doesn\'t match the payload length specified in the header. Not sending ackdata.' + ackDataValidThusFar = False + if ackDataValidThusFar: + print 'ackData is valid. Will process it.' + #self.data = self.data[:self.payloadLength+24] + ackData + self.data[self.payloadLength+24:] + self.ackDataThatWeHaveYetToSend.append(ackData) #When we have processed all data, the processData function will pop the ackData out and process it as if it is a message received from our peer. + #print 'self.data after:', repr(self.data) + + + #This section is for my RSA keys (version 1 addresses). If we don't have any version 1 addresses, then it won't matter. + initialDecryptionSuccessful = False infile = cStringIO.StringIO(self.data[readPosition:self.payloadLength+24]) outfile = cStringIO.StringIO() - #print 'len(myAddressHashes.items()):', len(myAddressHashes.items()) - for key, value in myAddressHashes.items(): + #print 'len(myRSAAddressHashes.items()):', len(myRSAAddressHashes.items()) + for key, value in myRSAAddressHashes.items(): try: decrypt_bigfile(infile, outfile, value) #The initial decryption passed though there is a small chance that the message isn't actually for me. We'll need to check that the 20 zeros are present. @@ -686,7 +883,6 @@ class receiveDataThread(QThread): readPosition += sendersELength endOfThePublicKeyPosition = readPosition - messageEncodingType, messageEncodingTypeLength = decodeVarint(data[readPosition:readPosition+10]) readPosition += messageEncodingTypeLength print 'Message Encoding Type:', messageEncodingType @@ -714,7 +910,7 @@ class receiveDataThread(QThread): except Exception, err: print 'verify failed', err if verifyPassed: - #Let's calculate the fromAddress. + #calculate the fromRipe. sha = hashlib.new('sha512') sha.update(sendersN+sendersE) ripe = hashlib.new('ripemd160') @@ -868,6 +1064,8 @@ class receiveDataThread(QThread): #We have received a pubkey def recpubkey(self): + if self.payloadLength < 32: #sanity check + return #We must check to make sure the proof of work is sufficient. if not self.isProofOfWorkSufficient(): print 'Proof of work in pubkey message insufficient.' @@ -884,54 +1082,105 @@ class receiveDataThread(QThread): inventoryLock.release() return + + + readPosition = 24 #for the message header + readPosition += 8 #for the nonce + #bitfieldBehaviors = self.data[readPosition:readPosition+4] The bitfieldBehaviors used to be here + embeddedTime = self.data[readPosition:readPosition+4] + readPosition += 4 #for the time + addressVersion, varintLength = decodeVarint(self.data[readPosition:readPosition+10]) + readPosition += varintLength + streamNumber, varintLength = decodeVarint(self.data[readPosition:readPosition+10]) + readPosition += varintLength + if self.streamNumber != streamNumber: + print 'stream number embedded in this pubkey doesn\'t match our stream number. Ignoring.' + return + objectType = 'pubkey' inventory[inventoryHash] = (objectType, self.streamNumber, self.data[24:self.payloadLength+24], int(time.time())) inventoryLock.release() self.broadcastinv(inventoryHash) self.emit(SIGNAL("incrementNumberOfPubkeysProcessed()")) - readPosition = 24 #for the message header - readPosition += 8 #for the nonce - bitfieldBehaviors = self.data[readPosition:readPosition+4] - readPosition += 4 #for the bitfield of behaviors and features - addressVersion, varintLength = decodeVarint(self.data[readPosition:readPosition+10]) - if addressVersion >= 2: - print 'This version of Bitmessgae cannot handle version', addressVersion,'addresses.' + if addressVersion == 0: + print 'Within recpubkey, addressVersion of zero doesn\'t make sense.' return - readPosition += varintLength - streamNumber, varintLength = decodeVarint(self.data[readPosition:readPosition+10]) - readPosition += varintLength - #ripe = self.data[readPosition:readPosition+20] - #readPosition += 20 #for the ripe hash - nLength, varintLength = decodeVarint(self.data[readPosition:readPosition+10]) - readPosition += varintLength - nString = self.data[readPosition:readPosition+nLength] - readPosition += nLength - eLength, varintLength = decodeVarint(self.data[readPosition:readPosition+10]) - readPosition += varintLength - eString = self.data[readPosition:readPosition+eLength] - readPosition += eLength + if addressVersion >= 3: + printLock.acquire() + print 'This version of Bitmessage cannot handle version', addressVersion,'addresses.' + printLock.release() + return + if addressVersion == 2: + print 'within recpubkey, the self.payloadLength is', self.payloadLength,'. It should be 146.' + if self.payloadLength < 146: #sanity check. This is the minimum possible length. + print 'payloadLength less than 146. Sanity check failed.' + return + bitfieldBehaviors = self.data[readPosition:readPosition+4] + readPosition += 4 + publicSigningKey = self.data[readPosition:readPosition+64] + #Is it possible for a public key to be invalid such that trying to encrypt or sign with it will cause an error? If it is, we should probably test these keys here. + readPosition += 64 + publicEncryptionKey = self.data[readPosition:readPosition+64] + if len(publicEncryptionKey) < 64: + print 'publicEncryptionKey length less than 64. Sanity check failed.' + return + sha = hashlib.new('sha512') + print 'recpubkey hashing this data to make the ripe:', repr('\x04'+publicSigningKey+'\x04'+publicEncryptionKey) + sha.update('\x04'+publicSigningKey+'\x04'+publicEncryptionKey) + ripeHasher = hashlib.new('ripemd160') + ripeHasher.update(sha.digest()) + ripe = ripeHasher.digest() - sha = hashlib.new('sha512') - sha.update(nString+eString) - ripeHasher = hashlib.new('ripemd160') - ripeHasher.update(sha.digest()) - ripe = ripeHasher.digest() + printLock.acquire() + print 'within recpubkey, addressVersion', addressVersion + print 'streamNumber', streamNumber + print 'ripe', ripe.encode('hex') + print 'publicSigningKey in hex:', publicSigningKey.encode('hex') + print 'publicEncryptionKey in hex:', publicEncryptionKey.encode('hex') + printLock.release() - print 'within recpubkey, addressVersion', addressVersion - print 'streamNumber', streamNumber - print 'ripe', repr(ripe) - print 'n=', convertStringToInt(nString) - print 'e=', convertStringToInt(eString) + t = (ripe,True,self.data[24:24+self.payloadLength],int(time.time())+604800) #after one week we may remove this pub key from our database. + sqlLock.acquire() + sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') + sqlSubmitQueue.put(t) + sqlReturnQueue.get() + sqlLock.release() + printLock.acquire() + print 'added foreign pubkey into our database' + printLock.release() + workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) - t = (ripe,True,self.data[24:24+self.payloadLength],int(time.time())+604800) #after one week we may remove this pub key from our database. - sqlLock.acquire() - sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') - sqlSubmitQueue.put(t) - sqlReturnQueue.get() - sqlLock.release() - print 'added foreign pubkey into our database' - workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) + elif addressVersion == 1: + nLength, varintLength = decodeVarint(self.data[readPosition:readPosition+10]) + readPosition += varintLength + nString = self.data[readPosition:readPosition+nLength] + readPosition += nLength + eLength, varintLength = decodeVarint(self.data[readPosition:readPosition+10]) + readPosition += varintLength + eString = self.data[readPosition:readPosition+eLength] + readPosition += eLength + + sha = hashlib.new('sha512') + sha.update(nString+eString) + ripeHasher = hashlib.new('ripemd160') + ripeHasher.update(sha.digest()) + ripe = ripeHasher.digest() + + print 'within recpubkey, addressVersion', addressVersion + print 'streamNumber', streamNumber + print 'ripe', repr(ripe) + print 'n=', convertStringToInt(nString) + print 'e=', convertStringToInt(eString) + + t = (ripe,True,self.data[24:24+self.payloadLength],int(time.time())+604800) #after one week we may remove this pub key from our database. + sqlLock.acquire() + sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') + sqlSubmitQueue.put(t) + sqlReturnQueue.get() + sqlLock.release() + print 'added foreign pubkey into our database' + workerQueue.put(('newpubkey',(addressVersion,streamNumber,ripe))) #We have received a getpubkey message def recgetpubkey(self): @@ -970,12 +1219,88 @@ class receiveDataThread(QThread): #This getpubkey request is valid so far. Forward to peers. broadcastToSendDataQueues((self.streamNumber,'send',self.data[:self.payloadLength+24])) - - if addressVersionNumber > 1: - print 'The addressVersionNumber of the pubkey is too high. Can\'t understand. Ignoring it.' + if addressVersionNumber == 0: + print 'The addressVersionNumber of the pubkey request is zero. That doesn\'t make any sense. Ignoring it.' return - if self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength] in myAddressHashes: - print 'Found getpubkey requested hash in my list of hashes.' + elif addressVersionNumber > 2: + print 'The addressVersionNumber of the pubkey request is too high. Can\'t understand. Ignoring it.' + return + print 'the hash requested in this getpubkey request is:', self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength].encode('hex') + if self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength] in myECAddressHashes: #if this address hash is one of mine + print 'Found getpubkey requested hash in my list of EC hashes.' + #check to see whether we have already calculated the nonce and transmitted this key before + sqlLock.acquire()#released at the bottom of this payload generation section + t = (self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength],) #this prevents SQL injection + sqlSubmitQueue.put('SELECT * FROM pubkeys WHERE hash=?') + sqlSubmitQueue.put(t) + queryreturn = sqlReturnQueue.get() + #print 'queryreturn', queryreturn + if queryreturn == []: + print 'pubkey request is for me but the pubkey is not in our database of pubkeys. Making it.' + payload = pack('>I',(int(time.time()))) #todo: fuzz this time + payload += encodeVarint(2) #Address version number + payload += encodeVarint(streamNumber) + payload += '\x00\x00\x00\x01' #bitfield of features supported by me (see the wiki). + + privSigningKeyBase58 = config.get(encodeAddress(addressVersionNumber,streamNumber,self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength]), 'privsigningkey') + privEncryptionKeyBase58 = config.get(encodeAddress(addressVersionNumber,streamNumber,self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength]), 'privencryptionkey') + + privSigningKeyHex = decodeWalletImportFormat(privSigningKeyBase58).encode('hex') + privEncryptionKeyHex = decodeWalletImportFormat(privEncryptionKeyBase58).encode('hex') + pubSigningKey = highlevelcrypto.privToPub(privSigningKeyHex).decode('hex') + pubEncryptionKey = highlevelcrypto.privToPub(privEncryptionKeyHex).decode('hex') + + #print 'within recgetpubkey' + #print 'pubSigningKey in hex:', pubSigningKey.encode('hex') + #print 'pubEncryptionKey in hex:', pubEncryptionKey.encode('hex') + + payload += pubSigningKey[1:] + payload += pubEncryptionKey[1:] + + #Time to do the POW for this pubkey message + nonce = 0 + trialValue = 99999999999999999999 + target = 2**64 / ((len(payload)+payloadLengthExtraBytes+8) * averageProofOfWorkNonceTrialsPerByte) + print '(For pubkey message) Doing proof of work...' + initialHash = hashlib.sha512(payload).digest() + while trialValue > target: + nonce += 1 + trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8]) + #trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + payload).digest()).digest()[4:12]) + print '(For pubkey message) Found proof of work', trialValue, 'Nonce:', nonce + + payload = pack('>Q',nonce) + payload + t = (self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength],True,payload,int(time.time())+1209600) #after two weeks (1,209,600 seconds), we may remove our own pub key from our database. It will be regenerated and put back in the database if it is requested. + sqlSubmitQueue.put('''INSERT INTO pubkeys VALUES (?,?,?,?)''') + sqlSubmitQueue.put(t) + queryreturn = sqlReturnQueue.get() + + #Now that we have the full pubkey message ready either from making it just now or making it earlier, we can send it out. + t = (self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength],) #this prevents SQL injection + sqlSubmitQueue.put('''SELECT * FROM pubkeys WHERE hash=? AND havecorrectnonce=1''') + sqlSubmitQueue.put(t) + queryreturn = sqlReturnQueue.get() + if queryreturn == []: + sys.stderr.write('Error: pubkey which we just put in our pubkey database suddenly is not there. Is the database malfunctioning?') + sqlLock.release() + return + for row in queryreturn: + hash, havecorrectnonce, payload, timeLastRequested = row + if timeLastRequested < int(time.time())+604800: #if the last time anyone asked about this hash was this week, extend the time. + t = (int(time.time())+604800,hash) + sqlSubmitQueue.put('''UPDATE pubkeys set time=? WHERE hash=?''') + sqlSubmitQueue.put(t) + queryreturn = sqlReturnQueue.get() + + sqlLock.release() + + inventoryHash = calculateInventoryHash(payload) + objectType = 'pubkey' + inventory[inventoryHash] = (objectType, self.streamNumber, payload, int(time.time())) + self.broadcastinv(inventoryHash) + + elif self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength] in myRSAAddressHashes: + print 'Found getpubkey requested hash in my list of RSA hashes.' #check to see whether we have already calculated the nonce and transmitted this key before sqlLock.acquire()#released at the bottom of this payload generation section t = (self.data[36+addressVersionLength+streamNumberLength:56+addressVersionLength+streamNumberLength],) #this prevents SQL injection @@ -1098,7 +1423,7 @@ class receiveDataThread(QThread): try: for i in range(value): hash = self.data[24+lengthOfVarint+(i*32):56+lengthOfVarint+(i*32)] - print 'getdata request for item:', repr(hash), 'length', len(hash) + print 'getdata request for item:', hash.encode('hex'), 'length', len(hash) #print 'inventory is', inventory if hash in inventory: objectType, streamNumber, payload, receivedTime = inventory[hash] @@ -1170,7 +1495,7 @@ class receiveDataThread(QThread): headerData = headerData + pack('>L',len(payload)) headerData = headerData + hashlib.sha512(payload).digest()[:4] printLock.acquire() - print 'broadcasting inv with hash:', repr(hash) + print 'broadcasting inv with hash:', hash.encode('hex') printLock.release() broadcastToSendDataQueues((self.streamNumber, 'send', headerData + payload)) @@ -1606,6 +1931,84 @@ def convertIntToString(n): def convertStringToInt(s): return int(s.encode('hex'), 16) +def decodeWalletImportFormat(WIFstring): + fullString = arithmetic.changebase(WIFstring,58,256) + privkey = fullString[:-4] + if fullString[-4:] != hashlib.sha256(hashlib.sha256(privkey).digest()).digest()[:4]: + sys.stderr.write('Major problem! When trying to decode one of your private keys, the checksum failed. Here is the PRIVATE key: %s\n' % str(WIFstring)) + return "" + else: + #checksum passed + if privkey[0] == '\x80': + return privkey[1:] + else: + sys.stderr.write('Major problem! When trying to decode one of your private keys, the checksum passed but the key doesn\'t begin with hex 80. Here is the PRIVATE key: %s\n' % str(WIFstring)) + return "" + +def reloadMyAddressHashes(): + printLock.acquire() + print 'reloading my address hashes' + printLock.release() + myRSAAddressHashes.clear() + #myPrivateKeys.clear() + configSections = config.sections() + for addressInKeysFile in configSections: + if addressInKeysFile <> 'bitmessagesettings': + isEnabled = config.getboolean(addressInKeysFile, 'enabled') + if isEnabled: + status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile) + if addressVersionNumber == 2: + privEncryptionKey = decodeWalletImportFormat(config.get(addressInKeysFile, 'privencryptionkey')).encode('hex') #returns a simple 32 bytes of information encoded in 64 Hex characters, or null if there was an error + if len(privEncryptionKey) == 64:#It is 32 bytes encoded as 64 hex characters + myECAddressHashes[hash] = highlevelcrypto.makeCryptor(privEncryptionKey) + elif addressVersionNumber == 1: + n = config.getint(addressInKeysFile, 'n') + e = config.getint(addressInKeysFile, 'e') + d = config.getint(addressInKeysFile, 'd') + p = config.getint(addressInKeysFile, 'p') + q = config.getint(addressInKeysFile, 'q') + myRSAAddressHashes[hash] = rsa.PrivateKey(n,e,d,p,q) + +#This function expects that pubkey begin with \x04 +def calculateBitcoinAddressFromPubkey(pubkey): + if len(pubkey)!= 65: + print 'Could not calculate Bitcoin address from pubkey because function was passed a pubkey that was', len(pubkey),'bytes long rather than 65.' + return "error" + ripe = hashlib.new('ripemd160') + sha = hashlib.new('sha256') + sha.update(pubkey) + ripe.update(sha.digest()) + ripeWithProdnetPrefix = '\x00' + ripe.digest() + + checksum = hashlib.sha256(hashlib.sha256(ripeWithProdnetPrefix).digest()).digest()[:4] + binaryBitcoinAddress = ripeWithProdnetPrefix + checksum + numberOfZeroBytesOnBinaryBitcoinAddress = 0 + while binaryBitcoinAddress[0] == '\x00': + numberOfZeroBytesOnBinaryBitcoinAddress += 1 + binaryBitcoinAddress = binaryBitcoinAddress[1:] + base58encoded = arithmetic.changebase(binaryBitcoinAddress,256,58) + return "1"*numberOfZeroBytesOnBinaryBitcoinAddress + base58encoded + +def calculateTestnetAddressFromPubkey(pubkey): + if len(pubkey)!= 65: + print 'Could not calculate Bitcoin address from pubkey because function was passed a pubkey that was', len(pubkey),'bytes long rather than 65.' + return "error" + ripe = hashlib.new('ripemd160') + sha = hashlib.new('sha256') + sha.update(pubkey) + ripe.update(sha.digest()) + ripeWithProdnetPrefix = '\x6F' + ripe.digest() + + checksum = hashlib.sha256(hashlib.sha256(ripeWithProdnetPrefix).digest()).digest()[:4] + binaryBitcoinAddress = ripeWithProdnetPrefix + checksum + numberOfZeroBytesOnBinaryBitcoinAddress = 0 + while binaryBitcoinAddress[0] == '\x00': + numberOfZeroBytesOnBinaryBitcoinAddress += 1 + binaryBitcoinAddress = binaryBitcoinAddress[1:] + base58encoded = arithmetic.changebase(binaryBitcoinAddress,256,58) + return "1"*numberOfZeroBytesOnBinaryBitcoinAddress + base58encoded + + #This thread exists because SQLITE3 is so un-threadsafe that we must submit queries to it and it puts results back in a different queue. They won't let us just use locks. class sqlThread(QThread): def __init__(self, parent = None): @@ -1792,7 +2195,7 @@ class singleWorker(QThread): #We'll need to request the pub key because we don't have it. if not toRipe in neededPubkeys: neededPubkeys[toRipe] = 0 - print 'requesting pubkey:', repr(toRipe) + print 'requesting pubkey:', toRipe.encode('hex') self.requestPubKey(toAddressVersionNumber,toStreamNumber,toRipe) else: print 'We have already requested this pubkey (the ripe hash is in neededPubkeys). We will re-request again soon.' @@ -1812,7 +2215,7 @@ class singleWorker(QThread): del neededPubkeys[toRipe] self.sendMsg(toRipe) else: - print 'We don\'t need this pub key. We didn\'t ask for it. Pubkey hash:', repr(toRipe) + print 'We don\'t need this pub key. We didn\'t ask for it. Pubkey hash:', toRipe.encode('hex') workerQueue.task_done() @@ -1855,11 +2258,11 @@ class singleWorker(QThread): payload += eString payload += messageToTransmit signature = rsa.sign(messageToTransmit,myPrivatekey,'SHA-512') - print 'signature', repr(signature) + #print 'signature', signature.encode('hex') payload += signature - print 'nString', repr(nString) - print 'eString', repr(eString) + #print 'nString', repr(nString) + #print 'eString', repr(eString) nonce = 0 trialValue = 99999999999999999999 @@ -1909,74 +2312,134 @@ class singleWorker(QThread): for row in queryreturn: toaddress, fromaddress, subject, message, ackdata = row ackdataForWhichImWatching[ackdata] = 0 - status,addressVersionNumber,toStreamNumber,hash = decodeAddress(toaddress) - #if hash == toRipe: + toStatus,toAddressVersionNumber,toStreamNumber,toHash = decodeAddress(toaddress) + fromStatus,fromAddressVersionNumber,fromStreamNumber,fromHash = decodeAddress(fromaddress) self.emit(SIGNAL("updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"),ackdata,'Doing work necessary to send the message.') printLock.acquire() print 'Found the necessary message that needs to be sent with this pubkey.' print 'First 150 characters of message:', message[:150] printLock.release() - payload = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #this run of nulls allows the true message receiver to identify his message - payload += '\x01' #Message version. - payload += '\x00\x00\x00\x01' - fromStatus,fromAddressVersionNumber,fromStreamNumber,fromHash = decodeAddress(fromaddress) - payload += encodeVarint(fromAddressVersionNumber) - payload += encodeVarint(fromStreamNumber) - sendersN = convertIntToString(config.getint(fromaddress, 'n')) - payload += encodeVarint(len(sendersN)) - payload += sendersN + if fromAddressVersionNumber == 2: + payload = '\x01' #Message version. + payload += encodeVarint(fromAddressVersionNumber) + payload += encodeVarint(fromStreamNumber) + payload += '\x00\x00\x00\x01' - sendersE = convertIntToString(config.getint(fromaddress, 'e')) - payload += encodeVarint(len(sendersE)) - payload += sendersE + #We need to convert our private keys to public keys in order to include them. + privSigningKeyBase58 = config.get(fromaddress, 'privsigningkey') + privEncryptionKeyBase58 = config.get(fromaddress, 'privencryptionkey') - payload += '\x02' #Type 2 is simple UTF-8 message encoding. - messageToTransmit = 'Subject:' + subject + '\n' + 'Body:' + message - payload += encodeVarint(len(messageToTransmit)) - payload += messageToTransmit + privSigningKeyHex = decodeWalletImportFormat(privSigningKeyBase58).encode('hex') + privEncryptionKeyHex = decodeWalletImportFormat(privEncryptionKeyBase58).encode('hex') - #Later, if anyone impliments clients that don't send the ack_data, then we should probably check here to make sure that the receiver will make use of this ack_data and not attach it if not. - fullAckPayload = self.generateFullAckMessage(ackdata,toStreamNumber) - payload += encodeVarint(len(fullAckPayload)) - payload += fullAckPayload - sendersPrivKey = rsa.PrivateKey(config.getint(fromaddress, 'n'),config.getint(fromaddress, 'e'),config.getint(fromaddress, 'd'),config.getint(fromaddress, 'p'),config.getint(fromaddress, 'q')) + pubSigningKey = highlevelcrypto.privToPub(privSigningKeyHex).decode('hex') + pubEncryptionKey = highlevelcrypto.privToPub(privEncryptionKeyHex).decode('hex') - payload += rsa.sign(payload,sendersPrivKey,'SHA-512') + payload += pubSigningKey[1:] + payload += pubEncryptionKey[1:] + print 'attaching pubSigningKey, length:', len(pubSigningKey), 'should be 64.' - sqlLock.acquire() - sqlSubmitQueue.put('SELECT * FROM pubkeys WHERE hash=?') - sqlSubmitQueue.put((toRipe,)) - queryreturn = sqlReturnQueue.get() - sqlLock.release() + payload += toHash + payload += '\x02' #Type 2 is simple UTF-8 message encoding. + messageToTransmit = 'Subject:' + subject + '\n' + 'Body:' + message + payload += encodeVarint(len(messageToTransmit)) + payload += messageToTransmit + fullAckPayload = self.generateFullAckMessage(ackdata,toStreamNumber) + payload += encodeVarint(len(fullAckPayload)) + payload += fullAckPayload + signature = highlevelcrypto.sign(payload,privSigningKeyHex) + payload += encodeVarint(len(signature)) + payload += signature - for row in queryreturn: - hash, havecorrectnonce, pubkeyPayload, timeLastRequested = row + elif fromAddressVersionNumber == 1: + payload = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #this run of nulls allows the true message receiver to identify his message + payload += '\x01' #Message version. + payload += '\x00\x00\x00\x01' + + payload += encodeVarint(fromAddressVersionNumber) + payload += encodeVarint(fromStreamNumber) - readPosition = 8 #to bypass the nonce - bitfieldBehaviors = pubkeyPayload[8:12] - readPosition += 4 #to bypass the bitfield of behaviors - addressVersion, addressVersionLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) - readPosition += addressVersionLength - streamNumber, streamNumberLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) - readPosition += streamNumberLength - nLength, nLengthLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) - readPosition += nLengthLength - n = convertStringToInt(pubkeyPayload[readPosition:readPosition+nLength]) - readPosition += nLength - eLength, eLengthLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) - readPosition += eLengthLength - e = convertStringToInt(pubkeyPayload[readPosition:readPosition+eLength]) - receiversPubkey = rsa.PublicKey(n,e) + sendersN = convertIntToString(config.getint(fromaddress, 'n')) + payload += encodeVarint(len(sendersN)) + payload += sendersN - infile = cStringIO.StringIO(payload) - outfile = cStringIO.StringIO() - #print 'Encrypting using public key:', receiversPubkey - encrypt_bigfile(infile,outfile,receiversPubkey) + sendersE = convertIntToString(config.getint(fromaddress, 'e')) + payload += encodeVarint(len(sendersE)) + payload += sendersE - encrypted = outfile.getvalue() - infile.close() - outfile.close() + payload += '\x02' #Type 2 is simple UTF-8 message encoding. + messageToTransmit = 'Subject:' + subject + '\n' + 'Body:' + message + payload += encodeVarint(len(messageToTransmit)) + payload += messageToTransmit + + #Later, if anyone impliments clients that don't send the ack_data, then we should probably check here to make sure that the receiver will make use of this ack_data and not attach it if not. + fullAckPayload = self.generateFullAckMessage(ackdata,toStreamNumber) + payload += encodeVarint(len(fullAckPayload)) + payload += fullAckPayload + sendersPrivKey = rsa.PrivateKey(config.getint(fromaddress, 'n'),config.getint(fromaddress, 'e'),config.getint(fromaddress, 'd'),config.getint(fromaddress, 'p'),config.getint(fromaddress, 'q')) + + payload += rsa.sign(payload,sendersPrivKey,'SHA-512') + + #We have assembled the data that will be encrypted. Now let us fetch the recipient's public key out of our database and do the encryption. + + if toAddressVersionNumber == 2: + sqlLock.acquire() + sqlSubmitQueue.put('SELECT * FROM pubkeys WHERE hash=?') + sqlSubmitQueue.put((toRipe,)) + queryreturn = sqlReturnQueue.get() + sqlLock.release() + + for row in queryreturn: + hash, havecorrectnonce, pubkeyPayload, timeLastRequested = row + + readPosition = 8 #to bypass the nonce + readPosition += 4 #to bypass the embedded time + readPosition += 1 #to bypass the address version whose length is definitely 1 + streamNumber, streamNumberLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) + readPosition += streamNumberLength + behaviorBitfield = pubkeyPayload[readPosition:readPosition+4] + readPosition += 4 #to bypass the bitfield of behaviors + #pubSigningKeyBase256 = pubkeyPayload[readPosition:readPosition+64] #We don't use this key for anything here. + readPosition += 64 + pubEncryptionKeyBase256 = pubkeyPayload[readPosition:readPosition+64] + readPosition += 64 + encrypted = highlevelcrypto.encrypt(payload,"04"+pubEncryptionKeyBase256.encode('hex')) + + elif toAddressVersionNumber == 1: + sqlLock.acquire() + sqlSubmitQueue.put('SELECT * FROM pubkeys WHERE hash=?') + sqlSubmitQueue.put((toRipe,)) + queryreturn = sqlReturnQueue.get() + sqlLock.release() + + for row in queryreturn: + hash, havecorrectnonce, pubkeyPayload, timeLastRequested = row + + readPosition = 8 #to bypass the nonce + behaviorBitfield = pubkeyPayload[8:12] + readPosition += 4 #to bypass the bitfield of behaviors + addressVersion, addressVersionLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) + readPosition += addressVersionLength + streamNumber, streamNumberLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) + readPosition += streamNumberLength + nLength, nLengthLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) + readPosition += nLengthLength + n = convertStringToInt(pubkeyPayload[readPosition:readPosition+nLength]) + readPosition += nLength + eLength, eLengthLength = decodeVarint(pubkeyPayload[readPosition:readPosition+10]) + readPosition += eLengthLength + e = convertStringToInt(pubkeyPayload[readPosition:readPosition+eLength]) + receiversPubkey = rsa.PublicKey(n,e) + + infile = cStringIO.StringIO(payload) + outfile = cStringIO.StringIO() + #print 'Encrypting using public key:', receiversPubkey + encrypt_bigfile(infile,outfile,receiversPubkey) + + encrypted = outfile.getvalue() + infile.close() + outfile.close() nonce = 0 trialValue = 99999999999999999999 @@ -2002,9 +2465,9 @@ class singleWorker(QThread): print 'sending inv (within sendmsg function)' payload = '\x01' + inventoryHash headerData = '\xe9\xbe\xb4\xd9' #magic bits, slighly different from Bitcoin's magic bits. - headerData = headerData + 'inv\x00\x00\x00\x00\x00\x00\x00\x00\x00' - headerData = headerData + pack('>L',len(payload)) #payload length. Note that we add an extra 8 for the nonce. - headerData = headerData + hashlib.sha512(payload).digest()[:4] + headerData += 'inv\x00\x00\x00\x00\x00\x00\x00\x00\x00' + headerData += pack('>L',len(payload)) #payload length. Note that we add an extra 8 for the nonce. + headerData += hashlib.sha512(payload).digest()[:4] broadcastToSendDataQueues((toStreamNumber, 'send', headerData + payload)) #Update the status of the message in the 'sent' table to have a 'sent' status @@ -2028,6 +2491,7 @@ class singleWorker(QThread): payload += encodeVarint(addressVersionNumber) payload += encodeVarint(streamNumber) payload += ripe + print 'making request for pubkey with ripe:', ripe.encode('hex') nonce = 0 trialValue = 99999999999999999999 #print 'trial value', trialValue @@ -2128,13 +2592,9 @@ class addressGenerator(QThread): print 'ripe.digest', ripe.digest().encode('hex') if ripe.digest()[:2] == '\x00\x00': address = encodeAddress(2,self.streamNumber,ripe.digest()[2:]) - print 'address has 18 byte ripe:', address elif ripe.digest()[:1] == '\x00': address = encodeAddress(2,self.streamNumber,ripe.digest()[1:]) - print 'address has 19 byte ripe:', address - self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Finished generating address. Writing to keys.dat') - status,addressVersionNumber,streamNumber,hash = decodeAddress(address) - print status,addressVersionNumber,streamNumber,hash.encode('hex') + #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Finished generating address. Writing to keys.dat') #An excellent way for us to store our keys is in Wallet Import Format. Let us convert now. #https://en.bitcoin.it/wiki/Wallet_import_format @@ -2160,11 +2620,73 @@ class addressGenerator(QThread): self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Done generating address') self.emit(SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),self.label,address,str(self.streamNumber)) - + reloadMyAddressHashes() else: #There is something in the deterministicPassphrase variable thus we are going to do this deterministically. - statusbar = 'Generating '+numberOfAddressesToMake + ' new addresses.' + statusbar = 'Generating '+str(self.numberOfAddressesToMake) + ' new addresses.' self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),statusbar) + signingKeyNonce = 0 + encryptionKeyNonce = 1 + for i in range(self.numberOfAddressesToMake): + #This next section is a little bit strange. We're going to generate keys over and over until we + #find one that starts with either \x00 or \x00\x00. Then when we pack them into a Bitmessage address, + #we won't store the \x00 or \x00\x00 bytes thus making the address shorter. + + while True: + potentialPrivSigningKey = hashlib.sha512(self.deterministicPassphrase + encodeVarint(signingKeyNonce)).digest()[:32] + potentialPrivEncryptionKey = hashlib.sha512(self.deterministicPassphrase + encodeVarint(encryptionKeyNonce)).digest()[:32] + potentialPubSigningKey = self.pointMult(potentialPrivSigningKey) + potentialPubEncryptionKey = self.pointMult(potentialPrivEncryptionKey) + #print 'potentialPubSigningKey', potentialPubSigningKey.encode('hex') + #print 'potentialPubEncryptionKey', potentialPubEncryptionKey.encode('hex') + signingKeyNonce += 2 + encryptionKeyNonce += 2 + ripe = hashlib.new('ripemd160') + sha = hashlib.new('sha512') + sha.update(potentialPubSigningKey+potentialPubEncryptionKey) + ripe.update(sha.digest()) + #print 'potential ripe.digest', ripe.digest().encode('hex') + if self.eighteenByteRipe: + if ripe.digest()[:2] == '\x00\x00': + break + else: + if ripe.digest()[:1] == '\x00': + break + + print 'ripe.digest', ripe.digest().encode('hex') + if ripe.digest()[:2] == '\x00\x00': + address = encodeAddress(2,self.streamNumber,ripe.digest()[2:]) + elif ripe.digest()[:1] == '\x00': + address = encodeAddress(2,self.streamNumber,ripe.digest()[1:]) + #self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Finished generating address. Writing to keys.dat') + + #An excellent way for us to store our keys is in Wallet Import Format. Let us convert now. + #https://en.bitcoin.it/wiki/Wallet_import_format + privSigningKey = '\x80'+potentialPrivSigningKey + checksum = hashlib.sha256(hashlib.sha256(privSigningKey).digest()).digest()[0:4] + privSigningKeyWIF = arithmetic.changebase(privSigningKey + checksum,256,58) + print 'privSigningKeyWIF',privSigningKeyWIF + + privEncryptionKey = '\x80'+potentialPrivEncryptionKey + checksum = hashlib.sha256(hashlib.sha256(privEncryptionKey).digest()).digest()[0:4] + privEncryptionKeyWIF = arithmetic.changebase(privEncryptionKey + checksum,256,58) + + try: + config.add_section(address) + print 'self.label', self.label + config.set(address,'label',self.label) + config.set(address,'enabled','true') + config.set(address,'decoy','false') + config.set(address,'privSigningKey',privSigningKeyWIF) + config.set(address,'privEncryptionKey',privEncryptionKeyWIF) + with open(appdata + 'keys.dat', 'wb') as configfile: + config.write(configfile) + + self.emit(SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),self.label,address,str(self.streamNumber)) + except: + print address,'already exists. Not adding it again.' + self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Done generating address') + reloadMyAddressHashes() elif self.addressVersionNumber == 1: statusbar = 'Generating new ' + str(config.getint('bitmessagesettings', 'bitstrength')) + ' bit RSA key. This takes a minute on average. If you want to generate multiple addresses now, you can; they will queue.' @@ -2198,7 +2720,8 @@ class addressGenerator(QThread): self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"),'Done generating address') self.emit(SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),self.label,address,str(self.streamNumber)) - + reloadMyAddressHashes() + def pointMult(self,secret): #passphrase += 'a' #secret = hashlib.sha256(passphrase.encode('utf8')).digest() @@ -2360,9 +2883,23 @@ class NewAddressDialog(QtGui.QDialog): class MyForm(QtGui.QMainWindow): def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) - self.ui = Ui_MainWindow() #Jonathan changed this line - self.ui.setupUi(self) #Jonathan left this line alone + self.ui = Ui_MainWindow() + self.ui.setupUi(self) + #Ask the user if we may delete their old version 1 addresses if they have any. + configSections = config.sections() + for addressInKeysFile in configSections: + if addressInKeysFile <> 'bitmessagesettings': + status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile) + if addressVersionNumber == 1: + displayMsg = "One of your addresses, "+addressInKeysFile+", is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now?" + reply = QtGui.QMessageBox.question(self, 'Message',displayMsg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) + if reply == QtGui.QMessageBox.Yes: + config.remove_section(addressInKeysFile) + with open(appdata + 'keys.dat', 'wb') as configfile: + config.write(configfile) + + #Configure Bitmessage to start on startup (or remove the configuration) based on the setting in the keys.dat file if 'win32' in sys.platform or 'win64' in sys.platform: #Auto-startup for Windows RUN_PATH = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run" @@ -2497,7 +3034,7 @@ class MyForm(QtGui.QMainWindow): self.sqlLookup = sqlThread() self.sqlLookup.start() - self.reloadMyAddressHashes() + reloadMyAddressHashes() self.reloadBroadcastSendersForWhichImWatching() @@ -2873,7 +3410,7 @@ class MyForm(QtGui.QMainWindow): for row in queryreturn: toLabel, = row self.ui.tableWidgetSent.item(i,0).setText(unicode(toLabel,'utf-8')) - + def click_pushButtonSend(self): self.statusBar().showMessage('') toAddresses = str(self.ui.lineEditTo.text()) @@ -2887,7 +3424,9 @@ class MyForm(QtGui.QMainWindow): if toAddress <> '': status,addressVersionNumber,streamNumber,ripe = decodeAddress(toAddress) if status <> 'success': - print 'Status bar!', 'Error: Could not decode', toAddress, ':', status + printLock.acquire() + print 'Status bar:', 'Error: Could not decode', toAddress, ':', status + printLock.release() if status == 'missingbm': self.statusBar().showMessage('Error: Bitmessage addresses start with BM- Please check ' + toAddress) if status == 'checksumfailed': @@ -2897,7 +3436,6 @@ class MyForm(QtGui.QMainWindow): if status == 'versiontoohigh': self.statusBar().showMessage('Error: The address version in '+ toAddress+ ' is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever.') elif fromAddress == '': - print 'Status bar!', 'Error: you must specify a From address.' self.statusBar().showMessage('Error: You must specify a From address. If you don''t have one, go to the ''Your Identities'' tab.') else: toAddress = addBMIfNotPresent(toAddress) @@ -2965,7 +3503,6 @@ class MyForm(QtGui.QMainWindow): self.statusBar().showMessage('Your \'To\' field is empty.') else: #User selected 'Broadcast' if fromAddress == '': - print 'Status bar!', 'Error: you must specify a From address.' self.statusBar().showMessage('Error: You must specify a From address. If you don\'t have one, go to the \'Your Identities\' tab.') else: self.statusBar().showMessage('') @@ -3537,7 +4074,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetYourIdentities.item(currentRow,0).setTextColor(QtGui.QColor(0,0,0)) self.ui.tableWidgetYourIdentities.item(currentRow,1).setTextColor(QtGui.QColor(0,0,0)) self.ui.tableWidgetYourIdentities.item(currentRow,2).setTextColor(QtGui.QColor(0,0,0)) - self.reloadMyAddressHashes() + reloadMyAddressHashes() def on_action_YourIdentitiesDisable(self): currentRow = self.ui.tableWidgetYourIdentities.currentRow() addressAtCurrentRow = self.ui.tableWidgetYourIdentities.item(currentRow,1).text() @@ -3547,7 +4084,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetYourIdentities.item(currentRow,2).setTextColor(QtGui.QColor(128,128,128)) with open(appdata + 'keys.dat', 'wb') as configfile: config.write(configfile) - self.reloadMyAddressHashes() + reloadMyAddressHashes() def on_action_YourIdentitiesClipboard(self): currentRow = self.ui.tableWidgetYourIdentities.currentRow() addressAtCurrentRow = self.ui.tableWidgetYourIdentities.item(currentRow,1).text() @@ -3619,28 +4156,12 @@ class MyForm(QtGui.QMainWindow): newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) self.ui.tableWidgetYourIdentities.setItem(0, 2, newItem) self.rerenderComboBoxSendFrom() - self.reloadMyAddressHashes() def updateStatusBar(self,data): - print 'Status bar!', data + print 'Status bar:', data self.statusBar().showMessage(data) - def reloadMyAddressHashes(self): - print 'reloading my address hashes' - myAddressHashes.clear() - #myPrivateKeys.clear() - configSections = config.sections() - for addressInKeysFile in configSections: - if addressInKeysFile <> 'bitmessagesettings': - isEnabled = config.getboolean(addressInKeysFile, 'enabled') - if isEnabled: - status,addressVersionNumber,streamNumber,hash = decodeAddress(addressInKeysFile) - n = config.getint(addressInKeysFile, 'n') - e = config.getint(addressInKeysFile, 'e') - d = config.getint(addressInKeysFile, 'd') - p = config.getint(addressInKeysFile, 'p') - q = config.getint(addressInKeysFile, 'q') - myAddressHashes[hash] = rsa.PrivateKey(n,e,d,p,q) + def reloadBroadcastSendersForWhichImWatching(self): broadcastSendersForWhichImWatching.clear() @@ -3661,7 +4182,8 @@ class myTableWidgetItem(QTableWidgetItem): sendDataQueues = [] #each sendData thread puts its queue in this list. -myAddressHashes = {} +myRSAAddressHashes = {} +myECAddressHashes = {} #myPrivateKeys = {} inventory = {} #of objects (like msg payloads and pubkey payloads) Does not include protocol headers (the first 24 bytes of each packet). workerQueue = Queue.Queue() @@ -3683,6 +4205,10 @@ neededPubkeys = {} averageProofOfWorkNonceTrialsPerByte = 320 #The amount of work that should be performed (and demanded) per byte of the payload. Double this number to double the work. payloadLengthExtraBytes = 14000 #To make sending short messages a little more difficult, this value is added to the payload length for use in calculating the proof of work target. +if userVeryEasyProofOfWorkForTesting: + averageProofOfWorkNonceTrialsPerByte = averageProofOfWorkNonceTrialsPerByte / 10 + payloadLengthExtraBytes = payloadLengthExtraBytes / 10 + if __name__ == "__main__": #sqlite_version = sqlite3.sqlite_version_info # Check the Major version, the first element in the array @@ -3690,22 +4216,25 @@ if __name__ == "__main__": print 'This program requires sqlite version 3 or higher because 2 and lower cannot store NULL values. I see version:', sqlite3.sqlite_version_info sys.exit() - APPNAME = "PyBitmessage" - from os import path, environ - if sys.platform == 'darwin': - if "HOME" in environ: - appdata = path.join(os.environ["HOME"], "Library/Application support/", APPNAME) + '/' + if not storeConfigFilesInSameDirectoryAsProgram: + APPNAME = "PyBitmessage" + from os import path, environ + if sys.platform == 'darwin': + if "HOME" in environ: + appdata = path.join(os.environ["HOME"], "Library/Application support/", APPNAME) + '/' + else: + print 'Could not find home folder, please report this message and your OS X version to the BitMessage Github.' + sys.exit() + + elif 'win32' in sys.platform or 'win64' in sys.platform: + appdata = path.join(environ['APPDATA'], APPNAME) + '\\' else: - print 'Could not find home folder, please report this message and your OS X version to the BitMessage Github.' - sys.exit() + appdata = path.expanduser(path.join("~", "." + APPNAME + "/")) - elif 'win32' in sys.platform or 'win64' in sys.platform: - appdata = path.join(environ['APPDATA'], APPNAME) + '\\' + if not os.path.exists(appdata): + os.makedirs(appdata) else: - appdata = path.expanduser(path.join("~", "." + APPNAME + "/")) - - if not os.path.exists(appdata): - os.makedirs(appdata) + appdata = "" config = ConfigParser.SafeConfigParser() config.read(appdata + 'keys.dat') @@ -3772,7 +4301,7 @@ if __name__ == "__main__": knownNodes[1][item[4][0]] = (8444,int(time.time())) except: print 'bootstrap8444.bitmessage.org DNS bootstrapping failed.' - + app = QtGui.QApplication(sys.argv) app.setStyleSheet("QStatusBar::item { border: 0px solid black }") myapp = MyForm() diff --git a/newaddressdialog.py b/newaddressdialog.py index 6dd3ca18..7a1284e7 100644 --- a/newaddressdialog.py +++ b/newaddressdialog.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'newaddressdialog.ui' # -# Created: Tue Jan 15 15:21:33 2013 +# Created: Fri Jan 18 13:48:49 2013 # by: PyQt4 UI code generator 4.9.4 # # WARNING! All changes made in this file will be lost! @@ -55,42 +55,42 @@ class Ui_NewAddressDialog(object): self.groupBoxDeterministic.setObjectName(_fromUtf8("groupBoxDeterministic")) self.gridLayout = QtGui.QGridLayout(self.groupBoxDeterministic) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.label_6 = QtGui.QLabel(self.groupBoxDeterministic) - self.label_6.setObjectName(_fromUtf8("label_6")) - self.gridLayout.addWidget(self.label_6, 0, 0, 1, 4) - self.lineEditPassphrase = QtGui.QLineEdit(self.groupBoxDeterministic) - self.lineEditPassphrase.setInputMethodHints(QtCore.Qt.ImhHiddenText|QtCore.Qt.ImhNoAutoUppercase|QtCore.Qt.ImhNoPredictiveText) - self.lineEditPassphrase.setEchoMode(QtGui.QLineEdit.Password) - self.lineEditPassphrase.setObjectName(_fromUtf8("lineEditPassphrase")) - self.gridLayout.addWidget(self.lineEditPassphrase, 1, 0, 1, 7) - self.label_7 = QtGui.QLabel(self.groupBoxDeterministic) - self.label_7.setObjectName(_fromUtf8("label_7")) - self.gridLayout.addWidget(self.label_7, 2, 0, 1, 4) - self.lineEditPassphraseAgain = QtGui.QLineEdit(self.groupBoxDeterministic) - self.lineEditPassphraseAgain.setEchoMode(QtGui.QLineEdit.Password) - self.lineEditPassphraseAgain.setObjectName(_fromUtf8("lineEditPassphraseAgain")) - self.gridLayout.addWidget(self.lineEditPassphraseAgain, 3, 0, 1, 7) - self.label_8 = QtGui.QLabel(self.groupBoxDeterministic) - self.label_8.setObjectName(_fromUtf8("label_8")) - self.gridLayout.addWidget(self.label_8, 4, 0, 1, 7) self.label_9 = QtGui.QLabel(self.groupBoxDeterministic) self.label_9.setObjectName(_fromUtf8("label_9")) - self.gridLayout.addWidget(self.label_9, 5, 0, 1, 1) - spacerItem = QtGui.QSpacerItem(73, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout.addItem(spacerItem, 5, 1, 1, 1) - self.label_10 = QtGui.QLabel(self.groupBoxDeterministic) - self.label_10.setObjectName(_fromUtf8("label_10")) - self.gridLayout.addWidget(self.label_10, 5, 3, 1, 1) - spacerItem1 = QtGui.QSpacerItem(42, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout.addItem(spacerItem1, 5, 4, 1, 2) - self.label_11 = QtGui.QLabel(self.groupBoxDeterministic) - self.label_11.setObjectName(_fromUtf8("label_11")) - self.gridLayout.addWidget(self.label_11, 6, 0, 1, 5) + self.gridLayout.addWidget(self.label_9, 6, 0, 1, 1) + self.label_8 = QtGui.QLabel(self.groupBoxDeterministic) + self.label_8.setObjectName(_fromUtf8("label_8")) + self.gridLayout.addWidget(self.label_8, 5, 0, 1, 3) self.spinBoxNumberOfAddressesToMake = QtGui.QSpinBox(self.groupBoxDeterministic) self.spinBoxNumberOfAddressesToMake.setMinimum(1) self.spinBoxNumberOfAddressesToMake.setProperty("value", 8) self.spinBoxNumberOfAddressesToMake.setObjectName(_fromUtf8("spinBoxNumberOfAddressesToMake")) - self.gridLayout.addWidget(self.spinBoxNumberOfAddressesToMake, 6, 5, 1, 2) + self.gridLayout.addWidget(self.spinBoxNumberOfAddressesToMake, 4, 3, 1, 1) + self.label_6 = QtGui.QLabel(self.groupBoxDeterministic) + self.label_6.setObjectName(_fromUtf8("label_6")) + self.gridLayout.addWidget(self.label_6, 0, 0, 1, 1) + self.label_11 = QtGui.QLabel(self.groupBoxDeterministic) + self.label_11.setObjectName(_fromUtf8("label_11")) + self.gridLayout.addWidget(self.label_11, 4, 0, 1, 3) + spacerItem = QtGui.QSpacerItem(73, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout.addItem(spacerItem, 6, 1, 1, 1) + self.label_10 = QtGui.QLabel(self.groupBoxDeterministic) + self.label_10.setObjectName(_fromUtf8("label_10")) + self.gridLayout.addWidget(self.label_10, 6, 2, 1, 1) + spacerItem1 = QtGui.QSpacerItem(42, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout.addItem(spacerItem1, 6, 3, 1, 1) + self.label_7 = QtGui.QLabel(self.groupBoxDeterministic) + self.label_7.setObjectName(_fromUtf8("label_7")) + self.gridLayout.addWidget(self.label_7, 2, 0, 1, 1) + self.lineEditPassphraseAgain = QtGui.QLineEdit(self.groupBoxDeterministic) + self.lineEditPassphraseAgain.setEchoMode(QtGui.QLineEdit.Password) + self.lineEditPassphraseAgain.setObjectName(_fromUtf8("lineEditPassphraseAgain")) + self.gridLayout.addWidget(self.lineEditPassphraseAgain, 3, 0, 1, 4) + self.lineEditPassphrase = QtGui.QLineEdit(self.groupBoxDeterministic) + self.lineEditPassphrase.setInputMethodHints(QtCore.Qt.ImhHiddenText|QtCore.Qt.ImhNoAutoUppercase|QtCore.Qt.ImhNoPredictiveText) + self.lineEditPassphrase.setEchoMode(QtGui.QLineEdit.Password) + self.lineEditPassphrase.setObjectName(_fromUtf8("lineEditPassphrase")) + self.gridLayout.addWidget(self.lineEditPassphrase, 1, 0, 1, 4) self.formLayout.setWidget(8, QtGui.QFormLayout.LabelRole, self.groupBoxDeterministic) self.groupBox = QtGui.QGroupBox(NewAddressDialog) self.groupBox.setObjectName(_fromUtf8("groupBox")) @@ -103,7 +103,7 @@ class Ui_NewAddressDialog(object): self.newaddresslabel.setObjectName(_fromUtf8("newaddresslabel")) self.gridLayout_2.addWidget(self.newaddresslabel, 1, 0, 1, 2) self.radioButtonMostAvailable = QtGui.QRadioButton(self.groupBox) - self.radioButtonMostAvailable.setChecked(False) + self.radioButtonMostAvailable.setChecked(True) self.radioButtonMostAvailable.setObjectName(_fromUtf8("radioButtonMostAvailable")) self.gridLayout_2.addWidget(self.radioButtonMostAvailable, 2, 0, 1, 2) self.label_3 = QtGui.QLabel(self.groupBox) @@ -155,12 +155,12 @@ class Ui_NewAddressDialog(object): self.radioButtonDeterministicAddress.setText(QtGui.QApplication.translate("NewAddressDialog", "Use a passpharase to make addresses", None, QtGui.QApplication.UnicodeUTF8)) self.checkBoxEighteenByteRipe.setText(QtGui.QApplication.translate("NewAddressDialog", "Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter", None, QtGui.QApplication.UnicodeUTF8)) self.groupBoxDeterministic.setTitle(QtGui.QApplication.translate("NewAddressDialog", "Make deterministic addresses", None, QtGui.QApplication.UnicodeUTF8)) - self.label_6.setText(QtGui.QApplication.translate("NewAddressDialog", "Passphrase", None, QtGui.QApplication.UnicodeUTF8)) - self.label_7.setText(QtGui.QApplication.translate("NewAddressDialog", "Retype passphrase", None, QtGui.QApplication.UnicodeUTF8)) - self.label_8.setText(QtGui.QApplication.translate("NewAddressDialog", "In addition to your passphrase, you must remember these numbers:", None, QtGui.QApplication.UnicodeUTF8)) self.label_9.setText(QtGui.QApplication.translate("NewAddressDialog", "Address version number: 2", None, QtGui.QApplication.UnicodeUTF8)) - self.label_10.setText(QtGui.QApplication.translate("NewAddressDialog", "Stream number: 1", None, QtGui.QApplication.UnicodeUTF8)) + self.label_8.setText(QtGui.QApplication.translate("NewAddressDialog", "In addition to your passphrase, you must remember these numbers:", None, QtGui.QApplication.UnicodeUTF8)) + self.label_6.setText(QtGui.QApplication.translate("NewAddressDialog", "Passphrase", None, QtGui.QApplication.UnicodeUTF8)) self.label_11.setText(QtGui.QApplication.translate("NewAddressDialog", "Number of addresses to make based on your passphrase:", None, QtGui.QApplication.UnicodeUTF8)) + self.label_10.setText(QtGui.QApplication.translate("NewAddressDialog", "Stream number: 1", None, QtGui.QApplication.UnicodeUTF8)) + self.label_7.setText(QtGui.QApplication.translate("NewAddressDialog", "Retype passphrase", None, QtGui.QApplication.UnicodeUTF8)) self.groupBox.setTitle(QtGui.QApplication.translate("NewAddressDialog", "Randomly generate address", None, QtGui.QApplication.UnicodeUTF8)) self.label_2.setText(QtGui.QApplication.translate("NewAddressDialog", "Label (not shown to anyone except you)", None, QtGui.QApplication.UnicodeUTF8)) self.radioButtonMostAvailable.setText(QtGui.QApplication.translate("NewAddressDialog", "Use the most available stream", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/newaddressdialog.ui b/newaddressdialog.ui index 7810f174..e6d1b290 100644 --- a/newaddressdialog.ui +++ b/newaddressdialog.ui @@ -90,52 +90,45 @@ The 'Random Number' option is selected by default but deterministic addresses ha Make deterministic addresses - - - - Passphrase - - - - - - - Qt::ImhHiddenText|Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText - - - QLineEdit::Password - - - - - - - Retype passphrase - - - - - - - QLineEdit::Password - - - - - - - In addition to your passphrase, you must remember these numbers: - - - - + Address version number: 2 - + + + + In addition to your passphrase, you must remember these numbers: + + + + + + + 1 + + + 8 + + + + + + + Passphrase + + + + + + + Number of addresses to make based on your passphrase: + + + + Qt::Horizontal @@ -148,14 +141,14 @@ The 'Random Number' option is selected by default but deterministic addresses ha - + Stream number: 1 - + Qt::Horizontal @@ -168,20 +161,27 @@ The 'Random Number' option is selected by default but deterministic addresses ha - - + + - Number of addresses to make based on your passphrase: + Retype passphrase - - - - 1 + + + + QLineEdit::Password - - 8 + + + + + + Qt::ImhHiddenText|Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText + + + QLineEdit::Password @@ -210,7 +210,7 @@ The 'Random Number' option is selected by default but deterministic addresses ha Use the most available stream - false + true diff --git a/pyelliptic/__init__.py b/pyelliptic/__init__.py index 8e9c9c1a..761d08af 100644 --- a/pyelliptic/__init__.py +++ b/pyelliptic/__init__.py @@ -6,9 +6,11 @@ __version__ = '1.3' __all__ = [ 'OpenSSL', - 'ecc', - 'cipher', - 'hash', + 'ECC', + 'Cipher', + 'hmac_sha256', + 'hmac_sha512', + 'pbkdf2' ] from .openssl import OpenSSL diff --git a/pyelliptic/arithmetic.py b/pyelliptic/arithmetic.py index f28c060d..f54359ba 100644 --- a/pyelliptic/arithmetic.py +++ b/pyelliptic/arithmetic.py @@ -72,7 +72,7 @@ def base10_multiply(a,n): def hex_to_point(h): return (decode(h[2:34],16),decode(h[34:],16)) -def point_to_hex(p): return '04'+encode(p[0],16,32)+encode(p[1],16,32) +def point_to_hex(p): return '04'+encode(p[0],16,64)+encode(p[1],16,64) def multiply(privkey,pubkey): return point_to_hex(base10_multiply(hex_to_point(pubkey),decode(privkey,16))) diff --git a/pyelliptic/highlevelcrypto.py b/pyelliptic/highlevelcrypto.py index ec3dfa23..5acec3c4 100644 --- a/pyelliptic/highlevelcrypto.py +++ b/pyelliptic/highlevelcrypto.py @@ -22,7 +22,7 @@ def encrypt(msg,hexPubkey): # Decrypts message with hex private key def decrypt(msg,hexPrivkey): return makeCryptor(hexPrivkey).decrypt(msg) -# Decrypts message with an existing pyelliptic.ecc.ECC object +# Decrypts message with an existing pyelliptic.ECC.ECC object def decryptFast(msg,cryptor): return cryptor.decrypt(msg) # Signs with hex private key