mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-26 10:33:15 +03:00
PIV: Validate subject.
This commit is contained in:
parent
33eccbb53c
commit
cd006085a6
@ -42,6 +42,7 @@ from ykman.piv import (
|
||||
generate_self_signed_certificate,
|
||||
generate_csr,
|
||||
generate_chuid,
|
||||
parse_rfc4514_string,
|
||||
)
|
||||
from ykman.util import (
|
||||
parse_certificates,
|
||||
@ -233,6 +234,30 @@ class PivNode(RpcNode):
|
||||
def slots(self):
|
||||
return SlotsNode(self.session)
|
||||
|
||||
@action(closes_child=False)
|
||||
def examine_file(self, params, event, signal):
|
||||
data = bytes.fromhex(params.pop("data"))
|
||||
password = params.pop("password", None)
|
||||
try:
|
||||
private_key, certs = _parse_file(data, password)
|
||||
return dict(
|
||||
status=True,
|
||||
password=password is not None,
|
||||
private_key=bool(private_key),
|
||||
certificates=len(certs),
|
||||
)
|
||||
except InvalidPasswordError:
|
||||
logger.debug("Invalid or missing password", exc_info=True)
|
||||
return dict(status=False)
|
||||
|
||||
@action(closes_child=False)
|
||||
def validate_rfc4514(self, params, event, signal):
|
||||
try:
|
||||
parse_rfc4514_string(params.pop("data"))
|
||||
return dict(status=True)
|
||||
except ValueError:
|
||||
return dict(status=False)
|
||||
|
||||
|
||||
def _slot_for(name):
|
||||
return SLOT(int(name, base=16))
|
||||
@ -310,22 +335,6 @@ class SlotsNode(RpcNode):
|
||||
return SlotNode(self.session, slot, metadata, certificate, self.refresh)
|
||||
return super().create_child(name)
|
||||
|
||||
@action
|
||||
def examine_file(self, params, event, signal):
|
||||
data = bytes.fromhex(params.pop("data"))
|
||||
password = params.pop("password", None)
|
||||
try:
|
||||
private_key, certs = _parse_file(data, password)
|
||||
return dict(
|
||||
status=True,
|
||||
password=password is not None,
|
||||
private_key=bool(private_key),
|
||||
certificates=len(certs),
|
||||
)
|
||||
except InvalidPasswordError:
|
||||
logger.debug("Invalid or missing password", exc_info=True)
|
||||
return dict(status=False)
|
||||
|
||||
|
||||
class SlotNode(RpcNode):
|
||||
def __init__(self, session, slot, metadata, certificate, refresh):
|
||||
@ -413,7 +422,9 @@ class SlotNode(RpcNode):
|
||||
pin_policy = PIN_POLICY(params.pop("pin_policy", PIN_POLICY.DEFAULT))
|
||||
touch_policy = TOUCH_POLICY(params.pop("touch_policy", TOUCH_POLICY.DEFAULT))
|
||||
subject = params.pop("subject")
|
||||
generate_type = GENERATE_TYPE(params.pop("generate_type", GENERATE_TYPE.CERTIFICATE))
|
||||
generate_type = GENERATE_TYPE(
|
||||
params.pop("generate_type", GENERATE_TYPE.CERTIFICATE)
|
||||
)
|
||||
public_key = self.session.generate_key(
|
||||
self.slot, key_type, pin_policy, touch_policy
|
||||
)
|
||||
|
@ -380,9 +380,7 @@ class _DesktopPivSlotsNotifier extends PivSlotsNotifier {
|
||||
|
||||
@override
|
||||
Future<PivExamineResult> examine(String data, {String? password}) async {
|
||||
final result = await _session.command('examine_file', target: [
|
||||
'slots',
|
||||
], params: {
|
||||
final result = await _session.command('examine_file', params: {
|
||||
'data': data,
|
||||
'password': password,
|
||||
});
|
||||
@ -394,6 +392,14 @@ class _DesktopPivSlotsNotifier extends PivSlotsNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> validateRfc4514(String value) async {
|
||||
final result = await _session.command('validate_rfc4514', params: {
|
||||
'data': value,
|
||||
});
|
||||
return result['status'];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PivImportResult> import(SlotId slot, String data,
|
||||
{String? password,
|
||||
|
@ -463,6 +463,7 @@
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"l_invalid_rfc4514": "Invalid RFC4514 string",
|
||||
|
||||
"@_piv_slots": {},
|
||||
"s_slot_display_name": "{name} ({hexid})",
|
||||
|
@ -50,6 +50,7 @@ final pivSlotsProvider = AsyncNotifierProvider.autoDispose
|
||||
abstract class PivSlotsNotifier
|
||||
extends AutoDisposeFamilyAsyncNotifier<List<PivSlot>, DevicePath> {
|
||||
Future<PivExamineResult> examine(String data, {String? password});
|
||||
Future<bool> validateRfc4514(String value);
|
||||
Future<(SlotMetadata?, String?)> read(SlotId slot);
|
||||
Future<PivGenerateResult> generate(
|
||||
SlotId slot,
|
||||
|
@ -42,6 +42,7 @@ class GenerateKeyDialog extends ConsumerStatefulWidget {
|
||||
|
||||
class _GenerateKeyDialogState extends ConsumerState<GenerateKeyDialog> {
|
||||
String _subject = '';
|
||||
bool _invalidSubject = true;
|
||||
GenerateType _generateType = defaultGenerateType;
|
||||
KeyType _keyType = defaultKeyType;
|
||||
late DateTime _validFrom;
|
||||
@ -71,36 +72,47 @@ class _GenerateKeyDialogState extends ConsumerState<GenerateKeyDialog> {
|
||||
actions: [
|
||||
TextButton(
|
||||
key: keys.saveButton,
|
||||
onPressed: _generating || _subject.isEmpty
|
||||
onPressed: _generating || _invalidSubject
|
||||
? null
|
||||
: () async {
|
||||
setState(() {
|
||||
_generating = true;
|
||||
});
|
||||
|
||||
Function()? close;
|
||||
final pivNotifier =
|
||||
ref.read(pivSlotsProvider(widget.devicePath).notifier);
|
||||
final withContext = ref.read(withContextProvider);
|
||||
|
||||
if (!await pivNotifier.validateRfc4514(_subject)) {
|
||||
setState(() {
|
||||
_generating = false;
|
||||
});
|
||||
_invalidSubject = true;
|
||||
return;
|
||||
}
|
||||
|
||||
void Function()? close;
|
||||
final PivGenerateResult result;
|
||||
try {
|
||||
close = showMessage(
|
||||
context,
|
||||
l10n.l_generating_private_key,
|
||||
duration: const Duration(seconds: 30),
|
||||
close = await withContext<void Function()>(
|
||||
(context) async => showMessage(
|
||||
context,
|
||||
l10n.l_generating_private_key,
|
||||
duration: const Duration(seconds: 30),
|
||||
));
|
||||
result = await pivNotifier.generate(
|
||||
widget.pivSlot.slot,
|
||||
_keyType,
|
||||
parameters: switch (_generateType) {
|
||||
GenerateType.certificate =>
|
||||
PivGenerateParameters.certificate(
|
||||
subject: _subject,
|
||||
validFrom: _validFrom,
|
||||
validTo: _validTo),
|
||||
GenerateType.csr =>
|
||||
PivGenerateParameters.csr(subject: _subject),
|
||||
},
|
||||
);
|
||||
result = await ref
|
||||
.read(pivSlotsProvider(widget.devicePath).notifier)
|
||||
.generate(
|
||||
widget.pivSlot.slot,
|
||||
_keyType,
|
||||
parameters: switch (_generateType) {
|
||||
GenerateType.certificate =>
|
||||
PivGenerateParameters.certificate(
|
||||
subject: _subject,
|
||||
validFrom: _validFrom,
|
||||
validTo: _validTo),
|
||||
GenerateType.csr =>
|
||||
PivGenerateParameters.csr(subject: _subject),
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
close?.call();
|
||||
}
|
||||
@ -127,17 +139,21 @@ class _GenerateKeyDialogState extends ConsumerState<GenerateKeyDialog> {
|
||||
autofocus: true,
|
||||
key: keys.subjectField,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: l10n.s_subject,
|
||||
),
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: l10n.s_subject,
|
||||
errorText: _subject.isNotEmpty && _invalidSubject
|
||||
? l10n.l_invalid_rfc4514
|
||||
: null),
|
||||
textInputAction: TextInputAction.next,
|
||||
enabled: !_generating,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
if (value.isEmpty) {
|
||||
_subject = '';
|
||||
_invalidSubject = true;
|
||||
} else {
|
||||
_subject = value.contains('=') ? value : 'CN=$value';
|
||||
_invalidSubject = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user