Skip to content

Commit

Permalink
Merge pull request #218 from Nitrokey/hmac
Browse files Browse the repository at this point in the history
integration of hmac
  • Loading branch information
daringer authored Mar 4, 2024
2 parents dde248a + 99ed99c commit 9628802
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 12 deletions.
100 changes: 95 additions & 5 deletions nitrokeyapp/secrets_tab/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import binascii
import logging
from base64 import b32decode
from base64 import b32decode, b32encode
from datetime import datetime
from enum import Enum
from random import randbytes
from typing import Callable, Optional

from PySide6.QtCore import Qt, QThread, QTimer, Signal, Slot
Expand All @@ -14,7 +15,7 @@
from nitrokeyapp.qt_utils_mix_in import QtUtilsMixIn
from nitrokeyapp.worker import Worker

from .data import Credential, OtpData, OtpKind
from .data import Credential, OtherKind, OtpData, OtpKind
from .worker import SecretsWorker

# TODO:
Expand Down Expand Up @@ -124,6 +125,7 @@ def __init__(self, info_box: InfoBox, parent: Optional[QWidget] = None) -> None:
icon_refresh = self.get_qicon("OTP_generate.svg")
icon_edit = self.get_qicon("edit.svg")
icon_visibility = self.get_qicon("visibility_off.svg")
icon_generate = self.get_qicon("refresh.svg")

loc = QLineEdit.ActionPosition.TrailingPosition
self.action_username_copy = self.ui.username.addAction(icon_copy, loc)
Expand Down Expand Up @@ -155,6 +157,9 @@ def __init__(self, info_box: InfoBox, parent: Optional[QWidget] = None) -> None:
self.action_otp_edit = self.ui.otp.addAction(icon_edit, loc)
self.action_otp_edit.triggered.connect(self.act_enable_otp_edit)

self.action_hmac_gen = self.ui.otp.addAction(icon_generate, loc)
self.action_hmac_gen.triggered.connect(self.generate_hmac)

self.line_actions = [
self.action_username_copy,
self.action_password_copy,
Expand All @@ -163,6 +168,7 @@ def __init__(self, info_box: InfoBox, parent: Optional[QWidget] = None) -> None:
self.action_otp_copy,
self.action_otp_edit,
self.action_otp_gen,
self.action_hmac_gen,
]
self.line2copy_action = {
self.ui.username: self.action_username_copy,
Expand Down Expand Up @@ -394,13 +400,16 @@ def show_credential(self, credential: Credential) -> None:
self.ui.is_pin_protected.setChecked(credential.protected)
self.ui.is_touch_protected.setChecked(credential.touch_required)

self.hide_hmac_view()

self.hide_otp()
self.ui.algorithm_tab.setCurrentIndex(1)

if credential.otp or credential.other:
self.ui.algorithm_tab.show()
self.ui.algorithm_edit.hide()
self.ui.algorithm_show.show()
self.action_hmac_gen.setVisible(False)

self.ui.otp.show()
self.ui.otp.setReadOnly(True)
Expand All @@ -414,6 +423,12 @@ def show_credential(self, credential: Credential) -> None:
self.action_otp_copy.setVisible(False)
self.action_otp_gen.setVisible(False)

algo = str(credential.other)
if algo == "HMAC":
self.show_hmac_view()
else:
self.hide_hmac_view()

self.action_otp_edit.setVisible(False)
else:
self.ui.algorithm_tab.hide()
Expand Down Expand Up @@ -458,7 +473,6 @@ def edit_credential(self, credential: Credential) -> None:
self.ui.comment.setText(credential.comment.decode(errors="replace"))
else:
self.ui.comment.setText("")

self.ui.name.setReadOnly(False)
self.ui.username.setReadOnly(False)
self.ui.password.setReadOnly(False)
Expand Down Expand Up @@ -487,6 +501,7 @@ def edit_credential(self, credential: Credential) -> None:
str(credential.otp or credential.other)
)
self.ui.select_algorithm.setEnabled(False)
self.action_hmac_gen.setVisible(False)

self.action_otp_copy.setVisible(False)
self.action_otp_gen.setVisible(False)
Expand All @@ -510,11 +525,18 @@ def edit_credential(self, credential: Credential) -> None:

self.check_credential()

if credential.other == OtherKind.HMAC:
self.show_hmac_view()
self.ui.btn_save.setEnabled(False)
else:
self.hide_hmac_view()

def act_enable_otp_edit(self) -> None:
assert self.active_credential
self.active_credential.new_secret = True

self.ui.otp.setReadOnly(False)
self.ui.select_algorithm.setMaxCount(3)
self.ui.select_algorithm.setEnabled(True)
self.ui.otp.setPlaceholderText("<empty>")
self.ui.otp.setText("")
Expand All @@ -523,6 +545,7 @@ def act_enable_otp_edit(self) -> None:

@Slot()
def add_new_credential(self) -> None:

if not self.data:
return

Expand Down Expand Up @@ -563,13 +586,16 @@ def add_new_credential(self) -> None:
self.ui.otp.setReadOnly(False)

self.ui.algorithm_tab.show()
self.ui.select_algorithm.setMaxCount(4)
self.ui.select_algorithm.addItem("HMAC")
self.ui.algorithm_tab.setCurrentIndex(0)
self.ui.algorithm_edit.show()
self.ui.algorithm_show.hide()

self.ui.select_algorithm.show()
self.ui.select_algorithm.setCurrentText("None")
self.ui.select_algorithm.setEnabled(True)
self.action_hmac_gen.setVisible(False)

self.ui.btn_abort.show()
self.ui.btn_delete.hide()
Expand All @@ -586,6 +612,13 @@ def check_credential(self) -> None:

algo = self.ui.select_algorithm.currentText()
if self.ui.select_algorithm.isEnabled():
if algo == "HMAC":
self.show_hmac_view()
if len(otp_secret) != 32:
can_save = False
else:
self.hide_hmac_view()

if algo != "None" and not is_base32(otp_secret):
can_save = False

Expand Down Expand Up @@ -631,6 +664,54 @@ def hide_credential(self) -> None:
self.ui.secrets_list.clearSelection()
self.active_credential = None

def show_hmac_view(self) -> None:

name_hmac = "HmacSlot2"

if self.active_credential is None:
self.action_otp_copy.setVisible(True)
self.action_hmac_gen.setVisible(True)
self.ui.name_label.setText(name_hmac)
self.ui.name.setText(name_hmac)
else:
self.action_hmac_gen.setVisible(False)
self.action_otp_copy.setVisible(False)

self.ui.name.hide()
self.ui.name_label.show()

self.ui.username_label.hide()
self.ui.username.hide()

self.ui.password_label.hide()
self.ui.password.hide()

self.ui.comment_label.hide()
self.ui.comment.hide()

self.ui.is_pin_protection_label.hide()
self.ui.is_pin_protected.hide()

self.ui.is_touch_protection_label.hide()
self.ui.is_touch_protected.hide()

def hide_hmac_view(self) -> None:

self.ui.username_label.show()
self.ui.username.show()

self.ui.password_label.show()
self.ui.password.show()

self.ui.comment_label.show()
self.ui.comment.show()

self.ui.is_pin_protection_label.show()
self.ui.is_pin_protected.show()

self.ui.is_touch_protection_label.show()
self.ui.is_touch_protected.show()

@Slot()
def hide_otp(self) -> None:
self.otp_timeout = None
Expand Down Expand Up @@ -694,11 +775,14 @@ def save_credential(self) -> None:
print("INSERT ERROR MESSAGE HERE - status bar?")
return

kind, otp_secret, secret = None, None, None
kind, otherKind, otp_secret, secret = None, None, None, None
# only save otp, if enabled
if self.ui.select_algorithm.isEnabled():
try:
kind = OtpKind.from_str(kind_str)
if kind_str == "HMAC" or kind_str == "REVERSE_HOTP":
otherKind = OtherKind.from_str(kind_str)
else:
kind = OtpKind.from_str(kind_str)
otp_secret = self.ui.otp.text()
secret = parse_base32(otp_secret)
except RuntimeError:
Expand All @@ -707,6 +791,7 @@ def save_credential(self) -> None:
cred = Credential(
id=name.encode(),
otp=kind,
other=otherKind,
login=username.encode(),
password=password.encode(),
comment=comment.encode(),
Expand Down Expand Up @@ -734,3 +819,8 @@ def generate_otp(self) -> None:
def uncheck_checkbox(self, uncheck: bool) -> None:
if uncheck:
self.ui.is_protected.setChecked(False)

@Slot()
def generate_hmac(self) -> None:
secret = b32encode(randbytes(20))
self.ui.otp.setText(secret.decode())
15 changes: 15 additions & 0 deletions nitrokeyapp/secrets_tab/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ class OtherKind(Enum):
def __str__(self) -> str:
return self.name

def raw_kind(self) -> RawKind:
if self == OtherKind.HMAC:
return RawKind.Hmac
elif self == OtherKind.REVERSE_HOTP:
return RawKind.HotpReverse
else:
raise RuntimeError(f"Unexpected OTP kind: {self}")

@classmethod
def from_str(cls, s: str) -> "OtherKind":
for kind in OtherKind:
if kind.name == s:
return kind
raise RuntimeError(f"Unexpected Other kind: {kind}")


Kind = Union[OtpKind, OtherKind]

Expand Down
5 changes: 4 additions & 1 deletion nitrokeyapp/secrets_tab/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,10 @@ def add_credential(self, successful: bool = True) -> None:
pin_based_encryption=self.credential.protected,
)

if self.credential.other:
reg_data["secret"] = self.secret
reg_data["kind"] = self.credential.other.raw_kind()

if self.credential.otp:
reg_data["secret"] = self.secret
reg_data["kind"] = self.credential.otp.raw_kind()
Expand All @@ -406,7 +410,6 @@ def add_credential(self, successful: bool = True) -> None:
reg_data["password"] = self.credential.password
if self.credential.comment:
reg_data["metadata"] = self.credential.comment

try:
secrets.register(**reg_data) # type: ignore [arg-type]
except SecretsAppException as e:
Expand Down
17 changes: 11 additions & 6 deletions nitrokeyapp/ui/secrets_tab.ui
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="__username_label">
<widget class="QLabel" name="username_label">
<property name="text">
<string>Username:</string>
</property>
Expand Down Expand Up @@ -279,7 +279,7 @@
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="__password_label">
<widget class="QLabel" name="password_label">
<property name="text">
<string>Password:</string>
</property>
Expand Down Expand Up @@ -311,7 +311,7 @@
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="__comment_label">
<widget class="QLabel" name="comment_label">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
Expand Down Expand Up @@ -339,7 +339,7 @@
<item row="5" column="0">
<widget class="QStackedWidget" name="algorithm_tab">
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<widget class="QWidget" name="algorithm_edit">
<layout class="QVBoxLayout" name="verticalLayout_5">
Expand Down Expand Up @@ -410,6 +410,11 @@
<string>HOTP</string>
</property>
</item>
<item>
<property name="text">
<string>HMAC</string>
</property>
</item>
</widget>
</item>
</layout>
Expand Down Expand Up @@ -478,7 +483,7 @@
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="__is_pin_protection_label">
<widget class="QLabel" name="is_pin_protection_label">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
Expand Down Expand Up @@ -507,7 +512,7 @@
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="__is_touch_protection_label">
<widget class="QLabel" name="is_touch_protection_label">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
Expand Down

0 comments on commit 9628802

Please sign in to comment.