Skip to content

Commit

Permalink
Challenge-response dialog started...
Browse files Browse the repository at this point in the history
  • Loading branch information
etotheipi committed Feb 26, 2012
1 parent bc95958 commit ea8f588
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 21 deletions.
2 changes: 2 additions & 0 deletions ArmoryQt.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,9 @@ def chngDev(b):
actSetModeDev.setChecked(True)

actOpenTools = self.createAction('&Calculator', lambda: DlgECDSACalc(self,self).exec_())
actOwnership = self.createAction('&Prove Ownership', lambda: DlgOwnership(self,self).exec_())
self.menusList[MENUS.Tools].addAction(actOpenTools)
self.menusList[MENUS.Tools].addAction(actOwnership)


reactor.callLater(0.1, self.execIntroDialog)
Expand Down
Binary file added img/arrow_down32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/arrow_left32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/arrow_right32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/arrow_up32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
256 changes: 235 additions & 21 deletions qtdialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,8 +537,13 @@ def __init__(self, wlt, usermode=USERMODE.Standard, parent=None, main=None):
self.wltAddrView.setSelectionMode(QTableView.SingleSelection)
self.wltAddrView.horizontalHeader().setStretchLastSection(True)
self.wltAddrView.verticalHeader().setDefaultSectionSize(20)
self.wltAddrView.setMinimumWidth(800)
initialColResize(self.wltAddrView, [0.2, 0.4, 64, 80, 0.3])
self.wltAddrView.setMinimumWidth(550)
self.wltAddrView.setMinimumHeight(150)
iWidth = tightSizeStr(self.wltAddrView, 'Imported')[0]
initialColResize(self.wltAddrView, [0.25, 0.3, 64, iWidth*1.1, 0.3])

self.wltAddrView.sizeHint = lambda: QSize(700, 225)
self.wltAddrView.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)

self.wltAddrView.setContextMenuPolicy(Qt.CustomContextMenu)
self.wltAddrView.customContextMenuRequested.connect(self.showContextMenu)
Expand Down Expand Up @@ -708,7 +713,7 @@ def createVBoxSeparator():
layout.addWidget(optFrame, 1, 4, 8, 2)
self.setLayout(layout)

self.setWindowTitle('Wallet Details')
self.setWindowTitle('Wallet Properties')


#############################################################################
Expand Down Expand Up @@ -6298,9 +6303,9 @@ def __init__(self, parent=None, main=None):
self.btnWltData = QPushButton('Get Keys From Wallet')
self.btnClearFrm = QPushButton('Clear')
self.btnCalcKeys = QPushButton('Calculate')
self.btnClearFrm.setEnabled(False)
self.btnCalcKeys.setEnabled(False)
self.btnWltData.setEnabled(False)
#self.btnClearFrm.setEnabled(False)
#self.btnCalcKeys.setEnabled(False)
#self.btnWltData.setEnabled(False)
self.connect(self.btnWltData, SIGNAL('clicked()'), self.getOtherData)
self.connect(self.btnClearFrm, SIGNAL('clicked()'), self.clearFormData)
self.connect(self.btnCalcKeys, SIGNAL('clicked()'), self.keyWaterfall)
Expand All @@ -6318,8 +6323,8 @@ def __init__(self, parent=None, main=None):
self.btnSignMsg = QPushButton('Sign Message')
self.btnChallenge = QPushButton('Make Challenge')
self.btnVerify = QPushButton('Verify Signature')
self.btnSignMsg.setEnabled(False)
self.btnVerify.setEnabled(False)
#self.btnSignMsg.setEnabled(False)
#self.btnVerify.setEnabled(False)
self.lblSigResult = QRichLabel('')

self.connect(self.btnSignMsg, SIGNAL('clicked()'), self.signMsg)
Expand Down Expand Up @@ -6415,7 +6420,7 @@ def __init__(self, parent=None, main=None):

imgPlus = QImageLabel('img/plus_orange.png')
imgTimes = QImageLabel('img/asterisk_orange.png')
imgDown = QImageLabel('img/arrow_down.png')
imgDown = QImageLabel('img/arrow_down32.png')
#imgDown.setMaximumSize(32,32)

ssLayout = QGridLayout()
Expand All @@ -6438,7 +6443,7 @@ def __init__(self, parent=None, main=None):
#frmSS.setFrameStyle(STYLE_SUNKEN)
#frmSS.setLayout(ssLayout)
tabEcc.setLayout(ssLayout)
tabWidget.addTab(tabEcc, 'Elliptic Curve')
#tabWidget.addTab(tabEcc, 'Elliptic Curve')


calcLayout = QHBoxLayout()
Expand All @@ -6453,10 +6458,10 @@ def __init__(self, parent=None, main=None):
#############################################################################
def keyTextEdited(self, txtIndex):
notEmpty = not self.formIsEmpty()
self.btnClearFrm.setEnabled(notEmpty)
self.btnCalcKeys.setEnabled(notEmpty)
self.btnSignMsg.setEnabled(notEmpty)
self.btnVerify.setEnabled(notEmpty)
#self.btnClearFrm.setEnabled(notEmpty)
#self.btnCalcKeys.setEnabled(notEmpty)
#self.btnSignMsg.setEnabled(notEmpty)
#self.btnVerify.setEnabled(notEmpty)

if not isinstance(txtIndex, (list,tuple)):
txtIndex = [txtIndex]
Expand All @@ -6479,7 +6484,7 @@ def msgTextChanged(self):
def formIsEmpty(self):
totalEmpty = [0 if len(str(a.text()))>0 else 1 for a in self.keyTxtList]
allEmpty = not sum(totalEmpty)!=0
self.btnSignMsg.setEnabled(not allEmpty)
#self.btnSignMsg.setEnabled(not allEmpty)
return allEmpty

#############################################################################
Expand Down Expand Up @@ -6564,15 +6569,30 @@ def keyWaterfall(self):
return
elif len(addrB58)>0:
try:
raw25byte = base58_to_binary(addrB58)
if len(raw25byte)!=25:
QMessageBox.critical(self, 'Invalid Address', \
'The Bitcoin address supplied is invalid.', QMessageBox.Ok)
return
data,chk = raw25byte[:21], raw25byte[21:]
fixedData = verifyChecksum(data,chk)
if fixedData!=data:
if len(fixedData)==0:
QMessageBox.critical(self, 'Invalid Address', \
'The Bitcoin address has an error in it. Please double-check '
'that it was entered properly.', QMessageBox.Ok)
return
self.txtAddr.setText(hash160_to_addrStr(fixedData[1:]))

a160Bin = addrStr_to_hash160(addrB58)
self.txtHash.setText(binary_to_hex(a160Bin))
except:
QMessageBox.critical(self, 'Invalid Address Data', \
'Address data is not recognized!', QMessageBox.Ok)
return

self.btnSignMsg.setEnabled( len(privBin)>0 )
self.btnVerify.setEnabled( len(pubfBin)>0 )
#self.btnSignMsg.setEnabled( len(privBin)>0 )
#self.btnVerify.setEnabled( len(pubfBin)>0 )

for txt in self.keyTxtList:
txt.setCursorPosition(0)
Expand All @@ -6581,6 +6601,7 @@ def keyWaterfall(self):



#############################################################################
def checkIfAddrIsOurs(self, addr160):
wltID = self.main.getWalletForAddr160(addr160)
if wltID=='':
Expand All @@ -6592,6 +6613,7 @@ def checkIfAddrIsOurs(self, addr160):
return wltID


#############################################################################
def getOtherData(self):
''' Look in your wallets for the address, fill in pub/priv keys '''

Expand Down Expand Up @@ -6636,9 +6658,6 @@ def clearFormData(self):
wdgt.setText('')
self.lblPrivType.setText('')
self.lblTopMid.setText('')
self.btnWltData.setEnabled(False)
self.btnSignMsg.setEnabled(False)
self.btnVerify.setEnabled(False)

#############################################################################
def privSwitch(self):
Expand All @@ -6649,7 +6668,7 @@ def privSwitch(self):
#############################################################################
def makeChallenge(self):
rnd = SecureBinaryData().GenerateRandom(16)
msgToBeSigned = 'SignThisMessage_%s' % rnd.toHexStr()
msgToBeSigned = 'SignThisMessage%s' % rnd.toHexStr()
self.txtMsg.setText(msgToBeSigned)


Expand Down Expand Up @@ -6726,6 +6745,201 @@ def verifyMsg(self):



################################################################################
class DlgOwnership(QDialog):
def __init__(self, parent=None, main=None):
super(DlgOwnership, self).__init__(parent)


self.radioCreateChallenge = QRadioButton('I want someone to prove they own an address')
self.radioCreateResponse = QRadioButton('I need to prove that I own an address')
self.radioVerifyResponse = QRadioButton('Some gave me a proof-of-ownership I need to verify')
btngrp = QButtonGroup(self)
btngrp.addButton(self.radioCreateChallenge)
btngrp.addButton(self.radioCreateResponse)
btngrp.addButton(self.radioVerifyResponse)
btngrp.setExclusive(True)
self.radioCreateChallenge.setChecked(True)

self.connect(self.radioCreateChallenge, SIGNAL('clicked(bool)'), self.changeStack)
self.connect(self.radioCreateResponse, SIGNAL('clicked(bool)'), self.changeStack)
self.connect(self.radioVerifyResponse, SIGNAL('clicked(bool)'), self.changeStack)


## Create the stacked widget
self.challengeStack = QStackedWidget()
dispFont = GETFONT('Var', 8)
w,h = relaxedSizeNChar(dispFont, 40)
wmin = 250

## Create-Challenge options
ccWidget = QWidget()
ccLayout = QGridLayout()

self.txtCCDescr = QRichLabel( \
'Since all transactions are public but identities are not tied to '
'the addresses, you may need to "challenge" someone to prove that '
'they own a particular Bitcoin address (perhaps you received money '
'from that address, and the other person claims it was them who '
'sent it to you). Enter the address to be verified, and then click '
'the button to create a "challenge." The challenge can be emailed '
'to the other party')
self.txtCCAddress = QLineEdit()
self.txtCCAddress.setMinimumWidth(wmin)
self.txtCCAddress.sizeHint = lambda: QSize(w,h)
self.txtCCAddress.setFont(dispFont)

# Only advanced users should be able to specify a custom string
self.txtCCCustomStr = QLineEdit()
self.txtCCCustomStr.setMinimumWidth(wmin)
self.txtCCCustomStr.sizeHint = lambda: QSize(w,h)
self.txtCCCustomStr.setFont(dispFont)
self.txtCCCustomStr.setEnabled(False)

self.txtCCChallenge = QTextEdit()
self.txtCCChallenge.setMinimumWidth(wmin)
self.txtCCChallenge.sizeHint = lambda: QSize(w,int(3.1*h))
self.txtCCChallenge.setFont(dispFont)

self.lblCCcopied = QRichLabel('')
self.btnCCCreate = QPushButton('Create Challenge')


c1 = self.makeChallenge('1'+'abc'*11, \
'Feb 28, 12:38.1839083290 UTC ' + 'BitcoinXYZ'*12, \
'\xab\xcd\x12\x34'*8,
'\xab\xcd\x12\x34'*8)
c2 = self.makeChallenge('1'+'abc'*11, \
'Feb 28, 12:38.1839083290 UTC ' + 'BitcoinXYZ'*12)

print c1, '\n\n', c2, '\n\n'

print self.readChallenge(c1), '\n\n'
print self.readChallenge(c2), '\n\n'





def changeStack(self, dummy):
"""
Dummy argument is only to take the bool from 'clicked(bool)', but we
don't actually use it
"""
if self.radioCreateResponse.isChecked():
self.challengeStack.setCurrentIndex(1)
elif self.radioVerifyResponse.isChecked():
self.challengeStack.setCurrentIndex(2)
else:
# We put this last to be default in case somehow nothing is checked
self.challengeStack.setCurrentIndex(0)


def readChallenge(self, fullPacket):
addrB58, challengeStr, pubkey, sig = '','','',''
lines = fullPacket.split('\n')
readingChallenge, readingPub, readingSig = False, False, False
for i in range(len(lines)):
s = lines[i].strip()

# ADDRESS
if s.startswith('Addr'):
addrB58 = s.split(':')[-1].strip()

# CHALLENGE STRING
if s.startswith('Chal') or readingChallenge:
readingChallenge = True
if s.startswith('Pub') or s.startswith('Sig') or ('END-CHAL' in s):
readingChallenge = False
else:
# Challenge string needs to be exact, grab what's between the
# double quotes, no newlines
iq1 = s.index('"') + 1
iq2 = s.index('"', iq1)
challengeStr += s[iq1:iq2]

# PUBLIC KEY
if s.startswith('Pub') or readingPub:
readingPub = True
if s.startswith('Sig') or ('END-CHAL' in s):
readingPub = False
else:
pubkey += s.split(':')[-1].strip().replace(' ','')

# SIGNATURE
if s.startswith('Sig') or readingSig:
readingSig = True
if 'END-CHAL' in s:
readingSig = False
else:
sig += s.split(':')[-1].strip().replace(' ','')


if len(pubkey)>0:
try:
print pubkey, len(pubkey)
pubkey = hex_to_binary(pubkey)
if len(pubkey) not in (32, 33, 64, 65): raise
except:
QMessageBox.critical(self, 'Bad Public Key', \
'Public key data was not recognized', QMessageBox.Ok)
pubkey = ''

if len(sig)>0:
try:
sig = hex_to_binary(sig)
except:
QMessageBox.critical(self, 'Bad Signature', \
'Signature data is malformed!', QMessageBox.Ok)
sig = ''

return addrB58, challengeStr, pubkey, sig


def makeChallenge(self, addrB58, challengeStr, binPubkey='', binSig=''):
s = '-----BEGIN-CHALLENGE--------------------------------\n'

### Address ###
s += 'Address: %s\n' % addrB58

lineWid = 32
### Challenge ###
nChallengeLines = (len(challengeStr)-1)/lineWid + 1
for i in range(nChallengeLines):
cLine = 'Challenge: "%s"\n' if i==0 else ' "%s"\n'
s += cLine % challengeStr[i*lineWid:(i+1)*lineWid]

if len(binPubkey)>0:
hexPub = binary_to_hex(binPubkey)
prefix = ' '
if len(binPubkey)%32==1:
prefix,hexPub = hexPub[:2], hexPub[2:]

nPubLines = (len(hexPub)-1)/lineWid + 1
for i in range(nPubLines):
idx0, idx1 = i*lineWid, (i+1)*lineWid
if i==0:
s += 'PublicKey: %s %s\n' % (prefix, hexPub[idx0:idx1])
else:
s += ' %s\n' % ( hexPub[idx0:idx1])

if len(binSig)>0:
hexSig = binary_to_hex(binSig)
prefix = ' '
if len(binSig)%32==1:
prefix,hexSig = hexSig[:2], hexSig[2:]

nSigLines = (len(hexSig)-1)/lineWid + 1
for i in range(nSigLines):
idx0, idx1 = i*lineWid, (i+1)*lineWid
if i==0:
s += 'Signature: %s %s\n' % (prefix, hexSig[idx0:idx1])
else:
s += ' %s\n' % ( hexSig[idx0:idx1])

s += '-----END-CHALLENGE----------------------------------'
return s


################################################################################
################################################################################
Expand Down

0 comments on commit ea8f588

Please sign in to comment.