Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

integration of hmac #218

Merged
merged 1 commit into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
daringer marked this conversation as resolved.
Show resolved Hide resolved

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
Loading