1 /*
2 Copyright (C) 2011-2014 Yubico AB.  All rights reserved.
3 
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are
6 met:
7 
8    1. Redistributions of source code must retain the above copyright
9       notice, this list of conditions and the following disclaimer.
10 
11    2. Redistributions in binary form must reproduce the above
12       copyright notice, this list of conditions and the following
13       disclaimer in the documentation and/or other materials provided
14       with the distribution.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "oathpage.h"
30 #include "yubikeyutil.h"
31 #include "yubikeyfinder.h"
32 #include "yubikeywriter.h"
33 #include "ui_oathpage.h"
34 #include "ui/helpbox.h"
35 #include "ui/confirmbox.h"
36 
37 #include <QDebug>
38 #include <QSignalMapper>
39 #include <QSettings>
40 #include <QDateTime>
41 #include <QTableWidgetItem>
42 #include <QRadioButton>
43 
44 #define OATH_FIXED_NUMERIC      0
45 #define OATH_FIXED_MODHEX1      1
46 #define OATH_FIXED_MODHEX2      2
47 #define OATH_FIXED_MODHEX       3
48 
49 #define MOVING_FACTOR_ZERO      0
50 #define MOVING_FACTOR_FIXED     1
51 #define MOVING_FACTOR_RAND      2
52 
OathPage(QWidget * parent)53 OathPage::OathPage(QWidget *parent) :
54         QStackedWidget(parent),
55         ui(new Ui::OathPage)
56 {
57     ui->setupUi(this);
58 
59     m_customerPrefix = -1;
60     memset(&m_pubId, 0, sizeof(m_pubId));
61     m_pubIdMUI = 0;
62     m_ykConfig = 0;
63     m_keyPresent = false;
64     clearState();
65 
66     //Connect pages
67     connectPages();
68 
69     //Connect help buttons
70     connectHelpButtons();
71 
72     //Connect other signals and slots
73     connect(YubiKeyFinder::getInstance(), SIGNAL(keyFound(bool, bool*, int)),
74             this, SLOT(keyFound(bool, bool*)));
75 
76     connect(ui->quickWriteConfigBtn, SIGNAL(clicked()),
77             this, SLOT(writeQuickConfig()));
78     connect(ui->advResetBtn, SIGNAL(clicked()),
79             this, SLOT(resetAdvPage()));
80 
81     connect(ui->advMovingFactorSeedTxt, SIGNAL(editingFinished()),
82             this, SLOT(on_advMovingFactorSeedTxt_editingFinished()));
83 
84     connect(ui->advHotpLen6Radio, SIGNAL(clicked()),
85             this, SLOT(hotpLen_clicked()));
86     connect(ui->advHotpLen8Radio, SIGNAL(clicked()),
87             this, SLOT(hotpLen_clicked()));
88     connect(ui->quickHotpLen6Radio, SIGNAL(clicked()),
89             this, SLOT(hotpLen_clicked()));
90     connect(ui->quickHotpLen8Radio, SIGNAL(clicked()),
91             this, SLOT(hotpLen_clicked()));
92 
93     //Load settings
94     loadSettings();
95 
96     ui->advResultsWidget->resizeColumnsToContents();
97 }
98 
~OathPage()99 OathPage::~OathPage() {
100     if(m_ykConfig != 0) {
101         delete m_ykConfig;
102         m_ykConfig = 0;
103     }
104     delete ui;
105 }
106 
107 /*
108  Common
109 */
110 
connectPages()111 void OathPage::connectPages() {
112     //Map the values of the navigation buttons with the indexes of
113     //the stacked widget
114 
115     //Create a QMapper
116     QSignalMapper *mapper = new QSignalMapper(this);
117 
118     //Connect the clicked signal with the QSignalMapper
119     connect(ui->quickBtn, SIGNAL(clicked()), mapper, SLOT(map()));
120     connect(ui->quickBackBtn, SIGNAL(clicked()), mapper, SLOT(map()));
121 
122     connect(ui->advBtn, SIGNAL(clicked()), mapper, SLOT(map()));
123     connect(ui->advBackBtn, SIGNAL(clicked()), mapper, SLOT(map()));
124 
125     //Set a value for each button
126     mapper->setMapping(ui->quickBtn, Page_Quick);
127     mapper->setMapping(ui->quickBackBtn, Page_Base);
128 
129     mapper->setMapping(ui->advBtn, Page_Advanced);
130     mapper->setMapping(ui->advBackBtn, Page_Base);
131 
132     //Connect the mapper to the widget
133     //The mapper will set a value to each button and
134     //set that value to the widget
135     //connect(pageMapper, SIGNAL(mapped(int)), this, SLOT(setCurrentIndex(int)));
136     connect(mapper, SIGNAL(mapped(int)), this, SLOT(setCurrentPage(int)));
137 
138     //Set the current page
139     m_currentPage = 0;
140     setCurrentIndex(Page_Base);
141 }
142 
setCurrentPage(int pageIndex)143 void OathPage::setCurrentPage(int pageIndex) {
144     //Page changed...
145 
146     m_currentPage = pageIndex;
147 
148     switch(pageIndex){
149     case Page_Quick:
150         resetQuickPage();
151         break;
152     case Page_Advanced:
153         resetAdvPage();
154         break;
155     }
156 
157     setCurrentIndex(pageIndex);
158 
159     //Clear state
160     m_keysProgrammedCtr = 0;
161     clearState();
162 }
163 
connectHelpButtons()164 void OathPage::connectHelpButtons() {
165     //Map the values of the help buttons
166 
167     //Create a QMapper
168     QSignalMapper *mapper = new QSignalMapper(this);
169 
170     //Connect the clicked signal with the QSignalMapper
171     connect(ui->quickConfigHelpBtn, SIGNAL(clicked()), mapper, SLOT(map()));
172     connect(ui->quickPubIdHelpBtn, SIGNAL(clicked()), mapper, SLOT(map()));
173     connect(ui->quickHotpLenHelpBtn, SIGNAL(clicked()), mapper, SLOT(map()));
174     connect(ui->quickSecretKeyHelpBtn, SIGNAL(clicked()), mapper, SLOT(map()));
175 
176     connect(ui->advConfigHelpBtn, SIGNAL(clicked()), mapper, SLOT(map()));
177     connect(ui->advParamGenSchemeHelpBtn, SIGNAL(clicked()), mapper, SLOT(map()));
178     connect(ui->advPubIdHelpBtn, SIGNAL(clicked()), mapper, SLOT(map()));
179     connect(ui->advHotpLenHelpBtn, SIGNAL(clicked()), mapper, SLOT(map()));
180     connect(ui->advHotpParamsHelpBtn, SIGNAL(clicked()), mapper, SLOT(map()));
181     connect(ui->advSecretKeyHelpBtn, SIGNAL(clicked()), mapper, SLOT(map()));
182 
183     //Set a value for each button
184     mapper->setMapping(ui->quickConfigHelpBtn, HelpBox::Help_ConfigurationSlot);
185     mapper->setMapping(ui->quickPubIdHelpBtn, HelpBox::Help_OathPublicID);
186     mapper->setMapping(ui->quickHotpLenHelpBtn, HelpBox::Help_HotpLen);
187     mapper->setMapping(ui->quickSecretKeyHelpBtn, HelpBox::Help_SecretKey);
188 
189     mapper->setMapping(ui->advConfigHelpBtn, HelpBox::Help_ConfigurationSlot);
190     mapper->setMapping(ui->advParamGenSchemeHelpBtn, HelpBox::Help_ParameterGeneration);
191     mapper->setMapping(ui->advPubIdHelpBtn, HelpBox::Help_OathPublicID);
192     mapper->setMapping(ui->advHotpLenHelpBtn, HelpBox::Help_HotpLen);
193     mapper->setMapping(ui->advHotpParamsHelpBtn, HelpBox::Help_HotpParam);
194     mapper->setMapping(ui->advSecretKeyHelpBtn, HelpBox::Help_SecretKey);
195 
196     //Connect the mapper
197     connect(mapper, SIGNAL(mapped(int)), this, SIGNAL(showHelp(int)));
198     connect(ui->advConfigProtectionBox, SIGNAL(showHelp(int)), this, SIGNAL(showHelp(int)));
199 }
200 
keyFound(bool found,bool * featuresMatrix)201 void OathPage::keyFound(bool found, bool* featuresMatrix) {
202     if(found) {
203         if(m_state == State_Initial) {
204             ui->quickWriteConfigBtn->setEnabled(true);
205             ui->advWriteConfigBtn->setEnabled(true);
206             ui->advExportConfigBtn->setEnabled(true);
207 
208             if(!featuresMatrix[YubiKeyFinder::Feature_MultipleConfigurations]) {
209                 ui->quickConfigSlot2Radio->setEnabled(false);
210                 ui->advConfigSlot2Radio->setEnabled(false);
211             } else {
212                 ui->quickConfigSlot2Radio->setEnabled(true);
213                 ui->advConfigSlot2Radio->setEnabled(true);
214             }
215 
216             if(!featuresMatrix[YubiKeyFinder::Feature_MovingFactor]) {
217                 ui->advMovingFactorSeedCombo->setEnabled(false);
218                 ui->advMovingFactorSeedTxt->setEnabled(false);
219             }
220 
221             if(!featuresMatrix[YubiKeyFinder::Feature_OathHotp]) {
222                 this->setEnabled(false);
223             }
224 
225             if(this->currentIndex() == Page_Quick) {
226                 resetQuickPrefix();
227             }
228         } else if(this->currentIndex() == Page_Advanced &&
229                   m_state >= State_Programming_Multiple &&
230                   m_keyPresent == false) {
231             if(m_state == State_Programming_Multiple) {
232                 ui->advWriteConfigBtn->setEnabled(true);
233                 ui->advExportConfigBtn->setEnabled(true);
234             } else {
235                 writeAdvConfig(WRITE_CONFIG);
236             }
237         }
238         m_keyPresent = true;
239     } else {
240 
241         ui->quickWriteConfigBtn->setEnabled(false);
242         ui->advWriteConfigBtn->setEnabled(false);
243         ui->advExportConfigBtn->setEnabled(false);
244 
245         m_keyPresent = false;
246 
247         if(m_state == State_Initial) {
248             ui->quickConfigSlot2Radio->setEnabled(true);
249             ui->advConfigSlot2Radio->setEnabled(true);
250 
251             ui->advMovingFactorSeedCombo->setEnabled(true);
252             if(ui->advMovingFactorSeedCombo->currentIndex() == MOVING_FACTOR_ZERO) {
253                 ui->advMovingFactorSeedTxt->setEnabled(false);
254             } else {
255                 ui->advMovingFactorSeedTxt->setEnabled(true);
256             }
257 
258             this->setEnabled(true);
259         } else if(this->currentIndex() == Page_Advanced &&
260                   m_state >= State_Programming_Multiple) {
261             if(m_keysProgrammedCtr > 0 && !m_ready) {
262                 changeAdvConfigParams();
263             }
264         }
265     }
266 }
267 
clearState()268 void OathPage::clearState() {
269     m_state = State_Initial;
270     m_ready = true;
271 
272     if(m_ykConfig != 0) {
273         delete m_ykConfig;
274         m_ykConfig = 0;
275     }
276 }
277 
updatePrefix()278 void OathPage::updatePrefix() {
279     if(m_currentPage == Page_Quick || m_customerPrefix > 0) {
280         //OMP
281         m_pubId[0] = YUBICO_OMP_CODE;
282 
283         //TT
284         m_pubId[1] = OATH_HOTP_CUSTOMER_PREFIX_START +
285                      (unsigned char) (m_customerPrefix / 1000);
286 
287         if(m_customerPrefix > 0) {
288             // make room for the prefix..
289             m_pubIdMUI %= 99999;
290             int mui_part = m_customerPrefix % 1000;
291             m_pubIdMUI += mui_part * 100000;
292         }
293     }
294 
295     QString pubIdTxt;
296     QString muiTxt = QString::number(m_pubIdMUI).rightJustified(8, '0');
297     QString pubIdModhexTxt = YubiKeyUtil::qstrModhexEncode(
298             m_pubId, 2);
299 
300     switch(m_currentPage) {
301     case Page_Quick:
302         ui->quickPrefixTxt->setText(pubIdModhexTxt.left(4));
303         ui->quickMUITxt->setText(muiTxt);
304         break;
305 
306     case Page_Advanced:
307         switch(ui->advPubIdFormatCombo->currentIndex()){
308         case OATH_FIXED_NUMERIC:
309             fixBCD(m_pubId, 2);
310             pubIdTxt = YubiKeyUtil::qstrHexEncode(
311                     m_pubId, 2);
312 
313             ui->advOMPTxt->setText(pubIdTxt.left(2));
314             ui->advTTTxt->setText(pubIdTxt.mid(2, 2));
315             ui->advMUITxt->setText(muiTxt);
316             break;
317 
318         case OATH_FIXED_MODHEX1:
319             fixBCD(m_pubId + 1, 1);
320             pubIdTxt = YubiKeyUtil::qstrHexEncode(
321                     m_pubId, 2);
322 
323             ui->advOMPTxt->setText(pubIdModhexTxt.left(2));
324             ui->advTTTxt->setText(pubIdTxt.mid(2, 2));
325             ui->advMUITxt->setText(muiTxt);
326             break;
327 
328         case OATH_FIXED_MODHEX2:
329             ui->advOMPTxt->setText(pubIdModhexTxt.left(2));
330             ui->advTTTxt->setText(pubIdModhexTxt.mid(2, 2));
331             ui->advMUITxt->setText(muiTxt);
332             break;
333 
334         case OATH_FIXED_MODHEX:
335             unsigned char tempMUI[4];
336             tempMUI[0] = (m_pubIdMUI >> 24) & 0xff;
337             tempMUI[1] = (m_pubIdMUI >> 16) & 0xff;
338             tempMUI[2] = (m_pubIdMUI >> 8) & 0xff;
339             tempMUI[3] = m_pubIdMUI & 0xff;
340             muiTxt = YubiKeyUtil::qstrModhexEncode(tempMUI, 4);
341             ui->advOMPTxt->setText(pubIdModhexTxt.left(2));
342             ui->advTTTxt->setText(pubIdModhexTxt.mid(2, 2));
343             ui->advMUITxt->setText(muiTxt);
344             break;
345         }
346 
347         break;
348     }
349 }
350 
fixBCD(unsigned char * bp,int bcnt)351 void OathPage::fixBCD(unsigned char *bp, int bcnt) {
352     while (bcnt--) {
353         if ((*bp & 0x0f) > 0x09) *bp -= 0x09;
354         if ((*bp & 0xf0) > 0x90) *bp -= 0x90;
355         bp++;
356     }
357 }
358 
loadSettings()359 void OathPage::loadSettings() {
360     QSettings settings;
361     m_customerPrefix = settings.value(SG_CUSTOMER_PREFIX).toInt();
362 
363     if(m_customerPrefix > 0) {
364         ui->advPubIdFormatCombo->setCurrentIndex(OATH_FIXED_MODHEX2);
365     }
366     bool customerPrefixFlag = !(m_customerPrefix > 0);
367 
368     ui->advPubIdFormatCombo->setEnabled(customerPrefixFlag);
369     ui->advOMPTxt->setEnabled(customerPrefixFlag);
370     ui->advTTTxt->setEnabled(customerPrefixFlag);
371 
372     ui->advExportConfigBtn->setVisible(settings.value(SG_EXPORT_PREFERENCE).toBool());
373 
374     bool hotp8 = settings.value(SG_OATH_HOTP8).toBool();
375     ui->advHotpLen6Radio->setChecked(!hotp8);
376     ui->quickHotpLen6Radio->setChecked(!hotp8);
377     ui->advHotpLen8Radio->setChecked(hotp8);
378     ui->quickHotpLen8Radio->setChecked(hotp8);
379 }
380 
381 /*
382  Quick Page handling
383 */
resetQuickPage()384 void OathPage::resetQuickPage() {
385     memset(&m_pubId, 0, sizeof(m_pubId));
386 
387     if(ui->quickConfigSlot1Radio->isChecked()) {
388         ui->quickConfigSlot2Radio->setChecked(true);
389     }
390 
391     ui->quickPubIdCheck->setChecked(true);
392 
393     on_quickResetBtn_clicked();
394 }
395 
on_quickResetBtn_clicked()396 void OathPage::on_quickResetBtn_clicked() {
397     resetQuickPrefix();
398 
399     ui->quickSecretKeyTxt->setText(
400             YubiKeyUtil::generateRandomHex((size_t)KEY_SIZE_OATH * 2));
401     on_quickSecretKeyTxt_editingFinished();
402 }
403 
on_quickHideParams_clicked(bool checked)404 void OathPage::on_quickHideParams_clicked(bool checked) {
405     if(checked) {
406         ui->quickSecretKeyTxt->setEchoMode(QLineEdit::Password);
407     } else {
408         ui->quickSecretKeyTxt->setEchoMode(QLineEdit::Normal);
409     }
410 }
411 
on_quickPubIdCheck_stateChanged(int state)412 void OathPage::on_quickPubIdCheck_stateChanged(int state) {
413     bool disable = (state != 0);
414 
415     ui->quickPrefixTxt->setEnabled(disable);
416     ui->quickMUITxt->setEnabled(disable);
417     ui->quickMUIGenerateBtn->setEnabled(disable);
418 }
419 
resetQuickPrefix()420 void OathPage::resetQuickPrefix() {
421     ui->quickMUITxt->setText(
422             QString::number(YubiKeyFinder::getInstance()->serial()));
423 
424     updateQuickMUI();
425     updatePrefix();
426 }
427 
updateQuickMUI()428 void OathPage::updateQuickMUI() {
429     QString txt = ui->quickMUITxt->text();
430 
431     unsigned char buf[MAX_SIZE];
432     memset(buf, 0, sizeof(buf));
433     size_t bufLen = 0;
434 
435     YubiKeyUtil::qstrClean(&txt, OATH_HOTP_MUI_SIZE * 2, true);
436     YubiKeyUtil::qstrDecDecode(buf, &bufLen, txt);
437 
438     ui->quickMUITxt->setText(txt);
439     memcpy(m_pubId + 2, buf, OATH_HOTP_MUI_SIZE);
440 }
441 
on_quickMUITxt_editingFinished()442 void OathPage::on_quickMUITxt_editingFinished() {
443     updateQuickMUI();
444     updatePrefix();
445 }
446 
on_quickMUIGenerateBtn_clicked()447 void OathPage::on_quickMUIGenerateBtn_clicked() {
448     YubiKeyUtil::generateRandom((unsigned char*)&m_pubIdMUI, sizeof(m_pubIdMUI));
449     m_pubIdMUI %= 99999999;
450     updatePrefix();
451 }
452 
on_quickSecretKeyTxt_editingFinished()453 void OathPage::on_quickSecretKeyTxt_editingFinished() {
454     QString txt = ui->quickSecretKeyTxt->text();
455     YubiKeyUtil::qstrClean(&txt, (size_t)KEY_SIZE_OATH * 2);
456     ui->quickSecretKeyTxt->setText(txt);
457 }
458 
validateQuickSettings()459 bool OathPage::validateQuickSettings() {
460     if(!(ui->quickConfigSlot1Radio->isChecked() ||
461          ui->quickConfigSlot2Radio->isChecked())) {
462         emit showStatusMessage(tr(ERR_CONF_SLOT_NOT_SELECTED), 1);
463         return false;
464     }
465 
466     QSettings settings;
467 
468     //Check if configuration slot 1 is being programmed
469     if (!settings.value(SG_OVERWRITE_CONF_SLOT1).toBool() &&
470         ui->quickConfigSlot1Radio->isChecked()) {
471         //Confirm from client
472         ConfirmBox confirm(this);
473         confirm.setConfirmIndex(ConfirmBox::Confirm_ConfigurationSlot);
474         int ret = confirm.exec();
475 
476         switch (ret) {
477         case 1:     //Yes
478             break;
479         default:    //No
480             return false;
481         }
482     }
483     return true;
484 }
485 
writeQuickConfig()486 void OathPage::writeQuickConfig() {
487     //Clear status
488     emit showStatusMessage(NULL, -1);
489 
490     //Validate settings
491     if(!validateQuickSettings()) {
492         return;
493     }
494 
495     //
496     m_state = State_Programming;
497 
498     //Write configuration
499     ui->quickWriteConfigBtn->setEnabled(false);
500     ui->quickResetBtn->setEnabled(false);
501     ui->quickBackBtn->setEnabled(false);
502 
503     if(m_ykConfig != 0) {
504         delete m_ykConfig;
505         m_ykConfig = 0;
506     }
507     m_ykConfig = new YubiKeyConfig();
508 
509     //Programming mode...
510     m_ykConfig->setProgrammingMode(YubiKeyConfig::Mode_OathHotp);
511 
512     // set serial
513     m_ykConfig->setSerial(QString::number(YubiKeyFinder::getInstance()->serial()));
514 
515     //Configuration slot...
516     int configSlot = 1;
517     if(ui->quickConfigSlot2Radio->isChecked()) {
518         configSlot = 2;
519     }
520     m_ykConfig->setConfigSlot(configSlot);
521 
522     //Public ID...
523     if(ui->quickPubIdCheck->isChecked()) {
524         m_ykConfig->setPubIdTxt(getPublicId(true));
525 
526         //OATH Public ID Type...
527         m_ykConfig->setOathFixedModhex2(true);
528     }
529 
530     //HOTP Len...
531     m_ykConfig->setOathHotp8(ui->quickHotpLen8Radio->isChecked());
532 
533     //HOTP Moving Factor Seed...
534     m_ykConfig->setOathMovingFactorSeed(0);
535 
536     //Secret Key...
537     m_ykConfig->setSecretKeyTxt(ui->quickSecretKeyTxt->text());
538 
539     //Write
540     connect(YubiKeyWriter::getInstance(), SIGNAL(configWritten(bool, const QString &)),
541             this, SLOT(quickConfigWritten(bool, const QString &)));
542 
543     YubiKeyWriter::getInstance()->writeConfig(m_ykConfig);
544 }
545 
quickConfigWritten(bool written,const QString & msg)546 void OathPage::quickConfigWritten(bool written, __attribute__((unused)) const QString &msg) {
547 
548     disconnect(YubiKeyWriter::getInstance(), SIGNAL(configWritten(bool, const QString &)),
549                this, SLOT(quickConfigWritten(bool, const QString &)));
550 
551     ui->quickWriteConfigBtn->setEnabled(true);
552     ui->quickResetBtn->setEnabled(true);
553     ui->quickBackBtn->setEnabled(true);
554 
555     if(written && m_ykConfig != 0) {
556         QString keyDetail("");
557         if(ui->quickPubIdCheck->isChecked()) {
558             QString pubIdTxt = ui->quickPrefixTxt->text() +
559                                ui->quickMUITxt->text().replace(QRegExp("\\s"), QString(""));
560 
561             keyDetail = tr(" (OATH ID: %1)").arg(pubIdTxt);
562         }
563 
564         showStatusMessage(tr(KEY_CONFIGURED).arg(keyDetail), 0);
565     }
566 
567     clearState();
568 }
569 
570 /*
571  Advanced Page handling
572 */
resetAdvPage()573 void OathPage::resetAdvPage() {
574     memset(&m_pubId, 0, sizeof(m_pubId));
575     m_pubIdMUI = 0;
576 
577     bool customerPrefixFlag = !(m_customerPrefix > 0);
578 
579     if(ui->advConfigSlot1Radio->isChecked()) {
580         ui->advConfigSlot2Radio->setChecked(true);
581     }
582 
583     ui->advConfigParamsCombo->setCurrentIndex(0);
584     ui->advAutoProgramKeysCheck->setChecked(false);
585     ui->advProgramMulKeysBox->setChecked(false);
586 
587     ui->advConfigProtectionBox->reset();
588 
589     ui->advPubIdCheck->setChecked(true);
590     if(customerPrefixFlag) {
591         ui->advPubIdFormatCombo->setCurrentIndex(OATH_FIXED_NUMERIC);
592     } else {
593         ui->advPubIdFormatCombo->setCurrentIndex(OATH_FIXED_MODHEX2);
594     }
595     ui->advPubIdFormatCombo->setEnabled(customerPrefixFlag);
596     on_advOMPTxt_editingFinished();
597     ui->advOMPTxt->setEnabled(customerPrefixFlag);
598     on_advTTTxt_editingFinished();
599     ui->advTTTxt->setEnabled(customerPrefixFlag);
600     on_advMUITxt_editingFinished();
601 
602     ui->advMovingFactorSeedCombo->setCurrentIndex(0);
603     ui->advMovingFactorSeedTxt->setText(tr("0"));
604     ui->advMovingFactorSeedTxt->setEnabled(false);
605 
606     ui->advSecretKeyTxt->clear();
607     on_advSecretKeyTxt_editingFinished();
608 
609     ui->advStopBtn->setEnabled(false);
610 }
611 
freezeAdvPage(bool freeze)612 void OathPage::freezeAdvPage(bool freeze) {
613     bool disable = !freeze;
614     ui->advConfigBox->setEnabled(disable);
615     ui->advProgramMulKeysBox->setEnabled(disable);
616     ui->advConfigProtectionBox->setEnabled(disable);
617     ui->advKeyParamsBox->setEnabled(disable);
618 
619     ui->advWriteConfigBtn->setEnabled(disable);
620     ui->advExportConfigBtn->setEnabled(disable);
621     ui->advStopBtn->setEnabled(!disable);
622     ui->advResetBtn->setEnabled(disable);
623     ui->advBackBtn->setEnabled(disable);
624 }
625 
on_advProgramMulKeysBox_clicked(bool checked)626 void OathPage::on_advProgramMulKeysBox_clicked(bool checked) {
627     if(checked) {
628         changeAdvConfigParams();
629     }
630 }
631 
on_advConfigParamsCombo_currentIndexChanged(int index)632 void OathPage::on_advConfigParamsCombo_currentIndexChanged(__attribute__((unused)) int index) {
633     changeAdvConfigParams();
634 }
635 
on_advPubIdCheck_stateChanged(int state)636 void OathPage::on_advPubIdCheck_stateChanged(int state) {
637     bool disable = (state != 0);
638 
639     if(m_customerPrefix <= 0) {
640         ui->advPubIdFormatCombo->setEnabled(disable);
641         ui->advOMPTxt->setEnabled(disable);
642         ui->advTTTxt->setEnabled(disable);
643     }
644 
645     ui->advMUITxt->setEnabled(disable);
646     ui->advMUIGenerateBtn->setEnabled(disable);
647 }
648 
on_advPubIdFormatCombo_currentIndexChanged(int index)649 void OathPage::on_advPubIdFormatCombo_currentIndexChanged(__attribute__((unused)) int index) {
650     updatePrefix();
651 }
652 
updateAdvOMP(int index)653 void OathPage::updateAdvOMP(int index) {
654     QString txt = ui->advOMPTxt->text();
655 
656     unsigned char buf[MAX_SIZE];
657     memset(buf, 0, sizeof(buf));
658     size_t bufLen = 0;
659 
660     if (index == OATH_FIXED_NUMERIC) {
661         YubiKeyUtil::qstrClean(&txt, OATH_HOTP_OMP_SIZE * 2, true);
662         YubiKeyUtil::qstrDecDecode(buf, &bufLen, txt);
663     } else {
664         YubiKeyUtil::qstrModhexClean(&txt, OATH_HOTP_OMP_SIZE * 2);
665         YubiKeyUtil::qstrModhexDecode(buf, &bufLen, txt);
666     }
667     ui->advOMPTxt->setText(txt);
668     memcpy(m_pubId + 0, buf, OATH_HOTP_OMP_SIZE);
669 }
670 
on_advOMPTxt_editingFinished()671 void OathPage::on_advOMPTxt_editingFinished() {
672     updateAdvOMP(ui->advPubIdFormatCombo->currentIndex());
673     updatePrefix();
674 }
675 
updateAdvTT(int index)676 void OathPage::updateAdvTT(int index) {
677     QString txt = ui->advTTTxt->text();
678 
679     unsigned char buf[MAX_SIZE];
680     memset(buf, 0, sizeof(buf));
681     size_t bufLen = 0;
682 
683     if (index == OATH_FIXED_NUMERIC ||
684         index == OATH_FIXED_MODHEX1) {
685         YubiKeyUtil::qstrClean(&txt, OATH_HOTP_TT_SIZE * 2, true);
686         YubiKeyUtil::qstrDecDecode(buf, &bufLen, txt);
687     } else {
688         YubiKeyUtil::qstrModhexClean(&txt, OATH_HOTP_TT_SIZE * 2);
689         YubiKeyUtil::qstrModhexDecode(buf, &bufLen, txt);
690     }
691     ui->advTTTxt->setText(txt);
692     memcpy(m_pubId + 1, buf, OATH_HOTP_TT_SIZE);
693 }
694 
on_advTTTxt_editingFinished()695 void OathPage::on_advTTTxt_editingFinished() {
696     updateAdvTT(ui->advPubIdFormatCombo->currentIndex());
697     updatePrefix();
698 }
699 
updateAdvMUI(int index)700 void OathPage::updateAdvMUI(int index) {
701     QString txt = ui->advMUITxt->text();
702 
703     if (index != OATH_FIXED_MODHEX) {
704         YubiKeyUtil::qstrClean(&txt, OATH_HOTP_MUI_SIZE * 2, true);
705         m_pubIdMUI = txt.toInt();
706     } else {
707         unsigned char buf[MAX_SIZE];
708         memset(buf, 0, sizeof(buf));
709         size_t bufLen = 0;
710 
711         YubiKeyUtil::qstrModhexClean(&txt, OATH_HOTP_MUI_SIZE * 2);
712         YubiKeyUtil::qstrModhexDecode(buf, &bufLen, txt);
713         m_pubIdMUI = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + (buf[3]);
714     }
715 }
716 
on_advMUITxt_editingFinished()717 void OathPage::on_advMUITxt_editingFinished() {
718     updateAdvMUI(ui->advPubIdFormatCombo->currentIndex());
719     updatePrefix();
720 }
721 
on_advMUIGenerateBtn_clicked()722 void OathPage::on_advMUIGenerateBtn_clicked() {
723     YubiKeyUtil::generateRandom((unsigned char*)&m_pubIdMUI, sizeof(m_pubIdMUI));
724     m_pubIdMUI %= 99999999;
725     updatePrefix();
726 }
727 
on_advMovingFactorSeedCombo_currentIndexChanged(int index)728 void OathPage::on_advMovingFactorSeedCombo_currentIndexChanged(int index) {
729     switch(index) {
730     case MOVING_FACTOR_ZERO:
731         ui->advMovingFactorSeedTxt->setText("0");
732         ui->advMovingFactorSeedTxt->setEnabled(false);
733         break;
734     case MOVING_FACTOR_FIXED:
735         ui->advMovingFactorSeedTxt->setEnabled(true);
736         break;
737     case MOVING_FACTOR_RAND:
738         unsigned int tmp;
739         YubiKeyUtil::generateRandom((unsigned char *) &tmp, sizeof(tmp));
740         ui->advMovingFactorSeedTxt->setText(QString::number(tmp));
741         on_advMovingFactorSeedTxt_editingFinished();
742         ui->advMovingFactorSeedTxt->setEnabled(true);
743         break;
744     }
745 }
746 
on_advMovingFactorSeedTxt_editingFinished()747 void OathPage::on_advMovingFactorSeedTxt_editingFinished() {
748     unsigned int val = ui->advMovingFactorSeedTxt->text().toUInt();
749     val = (((val + 8) >> 4UL) & 0xffff) << 4UL;
750     ui->advMovingFactorSeedTxt->setText(QString::number(val));
751 }
752 
on_advSecretKeyTxt_editingFinished()753 void OathPage::on_advSecretKeyTxt_editingFinished() {
754     QString txt = ui->advSecretKeyTxt->text();
755     YubiKeyUtil::qstrClean(&txt, (size_t)KEY_SIZE_OATH * 2);
756     ui->advSecretKeyTxt->setText(txt);
757 }
758 
on_advSecretKeyGenerateBtn_clicked()759 void OathPage::on_advSecretKeyGenerateBtn_clicked() {
760     ui->advSecretKeyTxt->setText(
761             YubiKeyUtil::generateRandomHex((size_t)KEY_SIZE_OATH * 2));
762     ui->advSecretKeyTxt->setCursorPosition(0);
763 }
764 
on_advWriteConfigBtn_clicked()765 void OathPage::on_advWriteConfigBtn_clicked() {
766     emit showStatusMessage(NULL, -1);
767 
768     if(!ui->advProgramMulKeysBox->isChecked()) {
769         m_keysProgrammedCtr = 0;
770     }
771 
772     //Validate settings
773     if(!validateAdvSettings()) {
774         return;
775     }
776 
777     clearState();
778 
779     freezeAdvPage(true);
780 
781     // Change state
782     if(ui->advProgramMulKeysBox->isChecked()) {
783         if(ui->advAutoProgramKeysCheck->isChecked()) {
784             m_keysProgrammedCtr = 0;
785             m_state = State_Programming_Multiple_Auto;
786         } else {
787             m_state = State_Programming_Multiple;
788         }
789     } else {
790         m_keysProgrammedCtr = 0;
791         m_state = State_Programming;
792     }
793 
794     writeAdvConfig(WRITE_CONFIG);
795 }
796 
on_advExportConfigBtn_clicked()797 void OathPage::on_advExportConfigBtn_clicked() {
798     emit showStatusMessage(NULL, -1);
799 
800     //Validate settings
801     if(!validateAdvSettings()) {
802         return;
803     }
804 
805     clearState();
806 
807     freezeAdvPage(true);
808 
809     writeAdvConfig(EXPORT_CONFIG);
810 }
811 
on_advStopBtn_clicked()812 void OathPage::on_advStopBtn_clicked() {
813     ui->advStopBtn->setEnabled(false);
814     m_state = State_Initial;
815     stopAdvConfigWritting();
816 }
817 
stopAdvConfigWritting()818 void OathPage::stopAdvConfigWritting() {
819     qDebug() << "Stopping adv configuration writing...";
820 
821     if(m_state >= State_Programming_Multiple) {
822         ui->advStopBtn->setEnabled(true);
823         return;
824     }
825 
826     m_keysProgrammedCtr = 0;
827     clearState();
828 
829     freezeAdvPage(false);
830     ui->advResetBtn->setEnabled(true);
831 }
832 
changeAdvConfigParams()833 void OathPage::changeAdvConfigParams() {
834     int index = ui->advConfigParamsCombo->currentIndex();
835     int idScheme = GEN_SCHEME_FIXED;
836     int secretScheme = GEN_SCHEME_FIXED;
837 
838     switch(index) {
839     case SCHEME_INCR_ID_RAND_SECRET:
840         //Increment IDs only if in programming mode
841         if(m_state != State_Initial) {
842             idScheme = GEN_SCHEME_INCR;
843         }
844         secretScheme = GEN_SCHEME_RAND;
845         break;
846 
847     case SCHEME_RAND_ALL:
848         idScheme = GEN_SCHEME_RAND;
849         secretScheme = GEN_SCHEME_RAND;
850         break;
851     }
852 
853     //Public ID...
854     if(ui->advPubIdCheck->isChecked()) {
855         switch(idScheme) {
856         case GEN_SCHEME_INCR:
857             {
858                 m_pubIdMUI++;
859                 updatePrefix();
860             }
861             break;
862 
863         case GEN_SCHEME_RAND:
864             YubiKeyUtil::generateRandom((unsigned char*)&m_pubIdMUI, sizeof(m_pubIdMUI));
865             m_pubIdMUI %= 99999999;
866 
867             updatePrefix();
868             break;
869         }
870     }
871 
872     //HOTP Moving Factor Seed...
873     if(ui->advMovingFactorSeedCombo->currentIndex() == MOVING_FACTOR_RAND) {
874         unsigned int tmp;
875         YubiKeyUtil::generateRandom((unsigned char *) &tmp, sizeof(tmp));
876         ui->advMovingFactorSeedTxt->setText(QString::number(tmp));
877         on_advMovingFactorSeedTxt_editingFinished();
878     }
879 
880     //Secret Key...
881     QString secretKeyTxt = YubiKeyUtil::getNextHex(
882             KEY_SIZE_OATH * 2,
883             ui->advSecretKeyTxt->text(), secretScheme);
884     ui->advSecretKeyTxt->setText(secretKeyTxt);
885     on_advSecretKeyTxt_editingFinished();
886     m_ready = true;
887 }
888 
validateAdvSettings()889 bool OathPage::validateAdvSettings() {
890     if(!(ui->advConfigSlot1Radio->isChecked() ||
891          ui->advConfigSlot2Radio->isChecked())) {
892         emit showStatusMessage(tr(ERR_CONF_SLOT_NOT_SELECTED), 1);
893         return false;
894     }
895 
896     QSettings settings;
897 
898     //Check if configuration slot 1 is being programmed
899     if (!settings.value(SG_OVERWRITE_CONF_SLOT1).toBool() &&
900         ui->advConfigSlot1Radio->isChecked() &&
901         m_keysProgrammedCtr == 0) {
902         //Confirm from client
903         ConfirmBox confirm(this);
904         confirm.setConfirmIndex(ConfirmBox::Confirm_ConfigurationSlot);
905         int ret = confirm.exec();
906 
907         switch (ret) {
908         case 1:     //Yes
909             break;
910         default:    //No
911             return false;
912         }
913     }
914 
915     if(!ui->advConfigProtectionBox->checkConfirm()) {
916         return false;
917     }
918 
919     return true;
920 }
921 
getPublicId(bool bcd)922 QString OathPage::getPublicId(bool bcd) {
923   unsigned char pubId[6];
924   memcpy(pubId, m_pubId, 2);
925 
926   if(bcd) {
927     int part = m_pubIdMUI / 1000000;
928     pubId[2] = ((part / 10) << 4) + part % 10;
929     part = m_pubIdMUI / 10000 % 100;
930     pubId[3] = ((part / 10) << 4) + part % 10;
931     part = m_pubIdMUI / 100 % 100;
932     pubId[4] = ((part / 10) << 4) + part % 10;
933     part = m_pubIdMUI % 100;
934     pubId[5] = ((part / 10) << 4) + part % 10;
935   } else {
936     pubId[2] = (m_pubIdMUI >> 24) & 0xff;
937     pubId[3] = (m_pubIdMUI >> 16) & 0xff;
938     pubId[4] = (m_pubIdMUI >> 8) & 0xff;
939     pubId[5] = m_pubIdMUI & 0xff;
940   }
941   QString pubIdTxt = YubiKeyUtil::qstrModhexEncode(pubId, 6);
942   return pubIdTxt;
943 }
944 
writeAdvConfig(int mode)945 void OathPage::writeAdvConfig(int mode) {
946     qDebug() << "Writing configuration...";
947 
948     //Disable stop button while configuration is being written
949     ui->advStopBtn->setEnabled(false);
950 
951 
952     //Write configuration
953     if(m_ykConfig != 0) {
954         qDebug() << "ykConfig destroyed";
955         delete m_ykConfig;
956         m_ykConfig = 0;
957     }
958     m_ykConfig = new YubiKeyConfig();
959 
960     //Programming mode...
961     m_ykConfig->setProgrammingMode(YubiKeyConfig::Mode_OathHotp);
962 
963     // set serial
964     m_ykConfig->setSerial(QString::number(YubiKeyFinder::getInstance()->serial()));
965 
966     //Configuration slot...
967     int configSlot = 1;
968     if(ui->advConfigSlot2Radio->isChecked()) {
969         configSlot = 2;
970     }
971     m_ykConfig->setConfigSlot(configSlot);
972 
973     //Public ID...
974     if(ui->advPubIdCheck->isChecked()) {
975         bool bcd = ui->advPubIdFormatCombo->currentIndex() < 3 ? true : false;
976         m_ykConfig->setPubIdTxt(getPublicId(bcd));
977 
978         //OATH Public ID Type...
979         switch(ui->advPubIdFormatCombo->currentIndex()) {
980         case 1:
981             m_ykConfig->setOathFixedModhex1(true);
982             break;
983         case 2:
984             m_ykConfig->setOathFixedModhex2(true);
985             break;
986         case 3:
987             m_ykConfig->setOathFixedModhex(true);
988             break;
989         }
990     }
991 
992     //HOTP Len...
993     m_ykConfig->setOathHotp8(ui->advHotpLen8Radio->isChecked());
994 
995     //HOTP Moving Factor Seed...
996     if(YubiKeyFinder::getInstance()->checkFeatureSupport(
997             YubiKeyFinder::Feature_MovingFactor)) {
998 
999         m_ykConfig->setOathMovingFactorSeed(
1000                 ui->advMovingFactorSeedTxt->text().toUInt());
1001     }
1002 
1003     //Secret Key...
1004     m_ykConfig->setSecretKeyTxt(ui->advSecretKeyTxt->text());
1005 
1006     //Configuration protection...
1007     m_ykConfig->setCurrentAccessCodeTxt(
1008         ui->advConfigProtectionBox->currentAccessCode());
1009     m_ykConfig->setNewAccessCodeTxt(
1010         ui->advConfigProtectionBox->newAccessCode(),
1011         ui->advConfigProtectionBox->newAccMode());
1012 
1013     if(mode == WRITE_CONFIG) {
1014         //Write
1015         connect(YubiKeyWriter::getInstance(), SIGNAL(configWritten(bool, const QString &)),
1016                 this, SLOT(advConfigWritten(bool, const QString &)));
1017 
1018         YubiKeyWriter::getInstance()->writeConfig(m_ykConfig);
1019     } else if(mode == EXPORT_CONFIG) {
1020         connect(YubiKeyWriter::getInstance(), SIGNAL(configWritten(bool, const QString &)),
1021                 this, SLOT(advConfigExported(bool, const QString &)));
1022 
1023         YubiKeyWriter::getInstance()->exportConfig(m_ykConfig);
1024     }
1025 }
1026 
advConfigWritten(bool written,const QString & msg)1027 void OathPage::advConfigWritten(bool written, const QString &msg) {
1028     disconnect(YubiKeyWriter::getInstance(), SIGNAL(configWritten(bool, const QString &)),
1029                this, SLOT(advConfigWritten(bool, const QString &)));
1030 
1031     QString message;
1032 
1033     if(written && m_ykConfig != 0) {
1034         qDebug() << "Configuration written....";
1035 
1036         m_keysProgrammedCtr++;
1037 
1038         QString keyDetail("");
1039         if(ui->advPubIdCheck->isChecked()) {
1040 
1041             QString pubIdTxt = ui->advOMPTxt->text() +
1042                                ui->advTTTxt->text() +
1043                                ui->advMUITxt->text().replace(QRegExp("\\s"), QString(""));
1044 
1045             keyDetail = tr(" (OATH ID: %1)").arg(pubIdTxt);
1046         }
1047 
1048         if(m_state == State_Programming){
1049             message = tr(KEY_CONFIGURED).arg(keyDetail);
1050         } else {
1051             message = tr("%1. %2").arg(tr(KEY_CONFIGURED).arg(keyDetail)).arg(tr(REMOVE_KEY));
1052         }
1053 
1054         showStatusMessage(message, 0);
1055 
1056         message = tr(KEY_CONFIGURED).arg("");
1057     } else {
1058         qDebug() << "Configuration could not be written....";
1059 
1060         message = msg;
1061     }
1062 
1063     advUpdateResults(written, message);
1064 
1065     m_ready = false;
1066     stopAdvConfigWritting();
1067 }
1068 
advConfigExported(bool written,const QString & msg)1069 void OathPage::advConfigExported(bool written, const QString &msg) {
1070     disconnect(YubiKeyWriter::getInstance(), SIGNAL(configWritten(bool, const QString &)),
1071                this, SLOT(advConfigExported(bool, const QString &)));
1072 
1073     QString message;
1074 
1075     if(written && m_ykConfig != 0) {
1076         qDebug() << "Configuration exported....";
1077 
1078         message = tr(KEY_EXPORTED);
1079 
1080         showStatusMessage(message, 0);
1081     } else {
1082         qDebug() << "Configuration could not be exported....";
1083 
1084         message = msg;
1085     }
1086 
1087     m_ready = false;
1088     stopAdvConfigWritting();
1089 }
1090 
advUpdateResults(bool written,const QString & msg)1091 void OathPage::advUpdateResults(bool written, const QString &msg) {
1092     int row = 0;
1093 
1094     ui->advResultsWidget->insertRow(row);
1095 
1096     //Sr. No....
1097     QTableWidgetItem *srnoItem = new QTableWidgetItem(
1098             tr("%1").arg(ui->advResultsWidget->rowCount()));
1099     if(written) {
1100         srnoItem->setIcon(QIcon(QPixmap(tr(":/res/images/tick.png"))));
1101     } else {
1102         srnoItem->setIcon(QIcon(QPixmap(tr(":/res/images/cross.png"))));
1103     }
1104     srnoItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
1105     ui->advResultsWidget->setItem(row, 0, srnoItem);
1106 
1107 
1108     //Public ID...
1109     QString pubIdTxt;
1110     if(ui->advPubIdCheck->isChecked() && m_ykConfig != 0) {
1111         pubIdTxt = ui->advOMPTxt->text() +
1112                    ui->advTTTxt->text() +
1113                    ui->advMUITxt->text().replace(QRegExp("\\s"), QString(""));
1114     } else {
1115         pubIdTxt = NA;
1116     }
1117     QTableWidgetItem *idItem = new QTableWidgetItem(
1118             tr("%1").arg(pubIdTxt));
1119     idItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
1120     ui->advResultsWidget->setItem(row, 1, idItem);
1121 
1122 
1123     //Status...
1124     QTableWidgetItem *statusItem = new QTableWidgetItem(
1125             tr("%1").arg(msg));
1126     statusItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
1127     ui->advResultsWidget->setItem(row, 2, statusItem);
1128 
1129 
1130     //Timestamp...
1131     QDateTime timstamp = QDateTime::currentDateTime();
1132     QTableWidgetItem *timeItem = new QTableWidgetItem(
1133             tr("%1").arg(timstamp.toString(Qt::SystemLocaleShortDate)));
1134     timeItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
1135     ui->advResultsWidget->setItem(row, 3, timeItem);
1136 
1137 
1138     ui->advResultsWidget->resizeColumnsToContents();
1139     ui->advResultsWidget->resizeRowsToContents();
1140 }
1141 
hotpLen_clicked()1142 void OathPage::hotpLen_clicked() {
1143     QSettings settings;
1144     QRadioButton *button = ui->advHotpLen8Radio;
1145     QRadioButton *button2 = ui->quickHotpLen8Radio;
1146     if(m_currentPage == Page_Quick) {
1147         button = ui->quickHotpLen8Radio;
1148         button2 = ui->advHotpLen8Radio;
1149     }
1150     if(button->isChecked()) {
1151         settings.setValue(SG_OATH_HOTP8, true);
1152     } else {
1153         settings.setValue(SG_OATH_HOTP8, false);
1154     }
1155     button2->toggle();
1156 }
1157 
setCurrentSlot(int slot)1158 void OathPage::setCurrentSlot(int slot) {
1159     if(m_currentPage == Page_Advanced) {
1160         ui->advConfigSlot1Radio->setChecked(slot == 1);
1161         ui->advConfigSlot2Radio->setChecked(slot == 2);
1162     } else if(m_currentPage == Page_Quick) {
1163         ui->quickConfigSlot1Radio->setChecked(slot == 1);
1164         ui->quickConfigSlot2Radio->setChecked(slot == 2);
1165     }
1166 }
1167