1
################################################################################
3
# Copyright (C) 2011-2014, Armory Technologies, Inc. #
4
# Distributed under the GNU Affero General Public License (AGPL v3) #
5
# See LICENSE or http://www.gnu.org/licenses/agpl.html #
7
################################################################################
8
from PyQt4 import Qt, QtCore
9
from PyQt4.QtCore import *
10
from PyQt4.QtGui import *
12
from armorycolors import htmlColor
13
from jasvet import ASv0, ASv1B64, ASv1CS, verifySignature, readSigBlock
14
from qtdefines import *
15
from qtdialogs import MIN_PASSWD_WIDTH, DlgPasswd3, createAddrBookButton,\
17
from armoryengine.ArmoryUtils import isASCII
18
from announcefetch import ANNOUNCE_SIGN_PUBKEY
20
class MessageSigningVerificationDialog(ArmoryDialog):
22
def __init__(self, parent=None, main=None):
23
super(MessageSigningVerificationDialog, self).__init__(parent, main)
24
layout = QVBoxLayout()
25
self.setWindowTitle("Message Signing/Verification")
26
self.setWindowIcon(QIcon( self.main.iconfile))
27
self.setMinimumWidth(600)
29
tabbedPanel = QTabWidget()
30
messageSigningTab = MessageSigningWidget(parent, main)
31
bareSignatureVerificationTab = BareSignatureVerificationWidget(parent, main)
32
signedMsgBlockVerificationTab = SignedMessageBlockVerificationWidget(parent, main)
33
tabbedPanel.addTab(messageSigningTab, "Sign Message")
34
tabbedPanel.addTab(bareSignatureVerificationTab, "Verify Bare Signature")
35
tabbedPanel.addTab(signedMsgBlockVerificationTab, "Verify Signed Message Block")
36
layout.addWidget(tabbedPanel)
38
self.goBackButton = QPushButton("Done")
39
actionButtonBox = QDialogButtonBox()
40
actionButtonBox.addButton(self.goBackButton, QDialogButtonBox.RejectRole)
41
layout.addWidget(actionButtonBox)
43
self.setLayout(layout)
44
self.connect(self.goBackButton, SIGNAL('clicked()'), \
45
self, SLOT('reject()'))
47
def clearFields(self):
48
self.addressLineEdit.setText('')
49
self.messageTextEdit.setPlainText('')
50
self.signatureDisplay.setPlainText('')
55
class MessageSigningWidget(QWidget):
57
def __init__(self, parent=None, main=None):
58
super(MessageSigningWidget, self).__init__(parent)
60
signMessageLayout = QGridLayout()
61
self.setMinimumWidth(800)
63
# Pick an Address in Row 0 of the grid layout
64
addressLabel = QLabel('Sign with Address:')
65
self.addressLineEdit = QLineEdit()
66
self.addressBookButton = createAddrBookButton(self, self.addressLineEdit, None,
67
selectMineOnly=True, showLockboxes=False)
68
signMessageLayout.addWidget(addressLabel, 0, 0)
69
signMessageLayout.addWidget(self.addressLineEdit, 0, 1)
70
signMessageLayout.addWidget(self.addressBookButton, 0, 2)
72
# Create a message in Row 1
73
messageLabel = QLabel("Message to sign:")
74
self.messageTextEdit = QTextEdit()
75
self.messageTextEdit.setAcceptRichText(False)
76
self.messageTextEdit.setStyleSheet("font: 9pt \"Courier\";")
77
signMessageLayout.addWidget(messageLabel, 1, 0)
78
signMessageLayout.addWidget(self.messageTextEdit, 1, 1, 1, 2)
81
# Create a row with just a sign message button
83
self.bareSigButton = QPushButton('Bare Signature (Bitcoin-Qt Compatible)')
84
self.base64SigButton = QPushButton('Base64 Signature')
85
self.clearSigButton = QPushButton('Clearsign Signature')
86
sigButtonFrame = makeHorizFrame([self.bareSigButton,\
87
self.base64SigButton,\
90
signMessageLayout.addWidget(sigButtonFrame, 2, 1, 1, 3)
92
# Create a Signature display
93
signatureLabel = QLabel('Message Signature:')
94
self.signatureDisplay = QTextEdit()
95
self.signatureDisplay.setReadOnly(True)
96
self.signatureDisplay.setStyleSheet("font: 9pt \"Courier\"; background-color: #bbbbbb;")
97
signMessageLayout.addWidget(signatureLabel, 3, 0)
98
signMessageLayout.addWidget(self.signatureDisplay, 3, 1, 1, 2)
100
self.copySignatureButton = QPushButton("Copy Signature")
101
self.clearFieldsButton = QPushButton("Clear All")
103
buttonFrame = makeHorizFrame([self.copySignatureButton, self.clearFieldsButton,'Stretch'])
104
signMessageLayout.addWidget(buttonFrame, 4, 1, 1, 3)
106
self.setLayout(signMessageLayout)
107
self.connect(self.bareSigButton, SIGNAL('clicked()'), \
108
self.bareSignMessage)
109
self.connect(self.base64SigButton, SIGNAL('clicked()'), \
110
self.base64SignMessage)
111
self.connect(self.clearSigButton, SIGNAL('clicked()'), \
112
self.clearSignMessage)
113
self.connect(self.copySignatureButton, SIGNAL('clicked()'), \
115
self.connect(self.clearFieldsButton, SIGNAL('clicked()'), \
118
def getPrivateKeyFromAddrInput(self):
119
atype, addr160 = addrStr_to_hash160(str(self.addressLineEdit.text()))
121
LOGWARN('P2SH address requested')
122
walletId = self.main.getWalletForAddr160(addr160)
123
wallet = self.main.walletMap[walletId]
124
if wallet.useEncryption and wallet.isLocked:
125
# Target wallet is encrypted...
126
unlockdlg = DlgUnlockWallet(wallet, self, self.main, 'Unlock Wallet to Import')
127
if not unlockdlg.exec_():
128
QMessageBox.critical(self, 'Wallet is Locked', \
129
'Cannot import private keys without unlocking wallet!', \
132
return wallet.addrMap[addr160].binPrivKey32_Plain.toBinStr()
134
def bareSignMessage(self):
135
messageText = str(self.messageTextEdit.toPlainText())
136
if not isASCII(messageText):
137
QMessageBox.warning(self, 'Non ASCII Text', 'Message to sign must be ASCII', QMessageBox.Ok)
140
privateKey = self.getPrivateKeyFromAddrInput()
142
signature = ASv0(privateKey, messageText)
143
self.signatureDisplay.setPlainText(signature['b64-signature'])
145
QMessageBox.warning(self, 'Private Key Not Known', 'The private key is not known for this address.', QMessageBox.Ok)
147
QMessageBox.warning(self, 'Invalid Address', 'The signing address is invalid.', QMessageBox.Ok)
150
def base64SignMessage(self):
151
messageText = str(self.messageTextEdit.toPlainText())
152
if not isASCII(messageText):
153
QMessageBox.warning(self, 'Non ASCII Text', 'Message to sign must be ASCII', QMessageBox.Ok)
156
privateKey = self.getPrivateKeyFromAddrInput()
158
signature = ASv1B64(self.getPrivateKeyFromAddrInput(), messageText)
159
self.signatureDisplay.setPlainText(signature)
161
QMessageBox.warning(self, 'Private Key Not Known', 'The private key is not known for this address.', QMessageBox.Ok)
163
QMessageBox.warning(self, 'Invalid Address', 'The signing address is invalid.', QMessageBox.Ok)
166
def clearSignMessage(self):
167
messageText = str(self.messageTextEdit.toPlainText())
168
if not isASCII(messageText):
169
QMessageBox.warning(self, 'Non ASCII Text', 'Message to sign must be ASCII', QMessageBox.Ok)
172
privateKey = self.getPrivateKeyFromAddrInput()
174
QMessageBox.warning(self, 'Invalid Address', 'The signing address is invalid.', QMessageBox.Ok)
177
signature = ASv1CS(privateKey, messageText)
178
self.signatureDisplay.setPlainText(signature)
180
QMessageBox.warning(self, 'Private Key Not Known', 'The private key is not known for this address.', QMessageBox.Ok)
182
def copySignature(self):
183
clipb = QApplication.clipboard()
185
clipb.setText(str(self.signatureDisplay.toPlainText()))
187
def clearFields(self):
188
self.addressLineEdit.setText('')
189
self.messageTextEdit.setPlainText('')
190
self.signatureDisplay.setPlainText('')
192
# Intended to be a base class
193
class SignatureVerificationWidget(QWidget):
195
def __init__(self, parent=None, main=None):
196
super(SignatureVerificationWidget, self).__init__(parent)
198
self.signMessageLayout = QGridLayout()
199
self.setMinimumWidth(800)
201
self.verifySignatureButton = QPushButton("Verify Signature")
202
self.clearFieldsButton = QPushButton("Clear All")
204
self.lblSigResult = QRichLabel('', doWrap=False)
205
buttonFrame = makeHorizFrame([self.verifySignatureButton, self.clearFieldsButton,\
206
'Stretch', self.lblSigResult])
207
self.signMessageLayout.addWidget(buttonFrame, 3, 1, 1, 2)
209
self.setLayout(self.signMessageLayout)
210
self.connect(self.verifySignatureButton, SIGNAL('clicked()'), \
211
self.verifySignature)
212
self.connect(self.clearFieldsButton, SIGNAL('clicked()'), \
215
# To be implemented by child classes
216
def verifySignature(self):
219
def clearFields(self):
220
self.lblSigResult.setText('')
222
def displayVerifiedBox(self, addrB58, messageString):
223
atihash160 = hash160(hex_to_binary(ANNOUNCE_SIGN_PUBKEY))
225
if addrB58==hash160_to_addrStr(atihash160):
226
addrDisp = '<b>Armory Technologies, Inc.</b>'
227
if CLI_OPTIONS.testAnnounceCode:
229
<font color="%s"><b>Armory Technologies, Inc.
230
(testing key)</b></font> has signed the following
231
block of text:<br>""") % htmlColor('TextGreen')
234
<font color="%s"><b>Armory Technologies, Inc.</b></font>
235
has signed the following block of text:<br>""") % \
236
htmlColor('TextGreen')
239
The owner of the following Bitcoin address...
242
<font face="Courier" size=4 color="#000060"><b>%s</b></font>
245
... has produced a <b><u>valid</u></b> signature for
246
the following message:<br>
250
msg = messageString.replace('\r\n','\n')
251
msg = ' ' + '<br> '.join(msg.split('\n'))
252
# The user will be able to see the entire message
253
# in the Message Signing/Verification dialog
254
msg = '<br>'.join([line[:60]+ '...'*(len(line)>60) for line in msg.split('<br>')][:12])
255
MsgBoxCustom(MSGBOX.Good, tr('Verified!'), tr("""
259
<font face="Courier" color="#000060"><b>%s</b></font>
262
<b>Please</b> make sure that the address above (%s...) matches the
263
exact address you were expecting. A valid signature is meaningless
265
from a recognized address!""") % (ownerStr, msg, addrB58[:10]))
266
self.lblSigResult.setText(\
267
'<font color="green">Valid Signature by %s</font>' % addrDisp)
269
self.displayInvalidSignatureMessage()
271
def displayInvalidSignatureMessage(self):
272
MsgBoxCustom(MSGBOX.Error, 'Invalid Signature!', \
273
'The supplied signature <b>is not valid</b>!')
274
self.lblSigResult.setText('<font color="red">Invalid Signature!</font>')
276
class BareSignatureVerificationWidget(SignatureVerificationWidget):
278
def __init__(self, parent=None, main=None):
279
super(BareSignatureVerificationWidget, self).__init__(parent, main)
280
# Pick an Address in Row 0 of the grid layout
281
addressLabel = QLabel('Signing Address:')
282
self.addressLineEdit = QLineEdit()
283
self.addressBookButton = createAddrBookButton(self, self.addressLineEdit, None,
284
selectMineOnly=True, showLockboxes=False)
285
self.signMessageLayout.addWidget(addressLabel, 0, 0)
286
self.signMessageLayout.addWidget(self.addressLineEdit, 0, 1)
287
self.signMessageLayout.addWidget(self.addressBookButton, 0, 2)
289
# Create a message text box
290
messageLabel = QLabel("Signed Message:")
291
self.messageTextEdit = QTextEdit()
292
self.messageTextEdit.setAcceptRichText(False)
293
self.messageTextEdit.setStyleSheet("font: 9pt \"Courier\";")
294
self.signMessageLayout.addWidget(messageLabel, 1, 0)
295
self.signMessageLayout.addWidget(self.messageTextEdit, 1, 1)
296
# Create a Signature display
297
signatureLabel = QLabel('Signature:')
298
self.signatureTextEdit = QTextEdit()
299
self.signatureTextEdit.setStyleSheet("font: 9pt \"Courier\";")
300
self.signMessageLayout.addWidget(signatureLabel, 2, 0)
301
self.signMessageLayout.addWidget(self.signatureTextEdit, 2, 1)
303
def verifySignature(self):
304
messageString = str(self.messageTextEdit.toPlainText())
306
addrB58 = verifySignature(str(self.signatureTextEdit.toPlainText()), \
307
messageString, 'v0', ord(ADDRBYTE))
308
if addrB58 == str(self.addressLineEdit.text()):
309
self.displayVerifiedBox(addrB58, messageString)
311
self.displayInvalidSignatureMessage()
313
self.displayInvalidSignatureMessage()
317
def clearFields(self):
318
super(BareSignatureVerificationWidget, self).clearFields()
319
self.addressLineEdit.setText('')
320
self.messageTextEdit.setPlainText('')
321
self.signatureTextEdit.setPlainText('')
323
class SignedMessageBlockVerificationWidget(SignatureVerificationWidget):
325
def __init__(self, parent=None, main=None):
326
super(SignedMessageBlockVerificationWidget, self).__init__(parent, main)
327
# Create a Signature display
328
signatureLabel = QLabel('Signed Message Block:')
329
self.signedMessageBlockTextEdit = QTextEdit()
330
self.signedMessageBlockTextEdit.setStyleSheet("font: 9pt \"Courier\";")
331
self.signedMessageBlockTextEdit.setAcceptRichText(False)
332
self.signMessageLayout.addWidget(signatureLabel, 0, 0)
333
self.signMessageLayout.addWidget(self.signedMessageBlockTextEdit, 0, 1)
335
# Create a message in Row 1
336
messageLabel = QLabel("Message:")
337
self.messageTextEdit = QTextEdit()
338
self.messageTextEdit.setAcceptRichText(False)
339
self.messageTextEdit.setReadOnly(True)
340
self.messageTextEdit.setStyleSheet("font: 9pt \"Courier\"; background-color: #bbbbbb;")
341
self.signMessageLayout.addWidget(messageLabel, 1, 0)
342
self.signMessageLayout.addWidget(self.messageTextEdit, 1, 1, 1, 2)
345
def verifySignature(self):
347
sig, msg = readSigBlock(str(self.signedMessageBlockTextEdit.toPlainText()))
348
addrB58 = verifySignature(sig, msg, 'v1', ord(ADDRBYTE) )
349
self.displayVerifiedBox(addrB58, msg)
350
self.messageTextEdit.setPlainText(msg)
352
self.displayInvalidSignatureMessage()
355
def clearFields(self):
356
super(SignedMessageBlockVerificationWidget, self).clearFields()
357
self.signedMessageBlockTextEdit.setPlainText('')
358
self.messageTextEdit.setPlainText('')