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

WIP #5

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package net.archethic.yubikit_android.methods

import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel

class Connect : MethodHandler {
override fun handle(call: MethodCall, result: MethodChannel.Result) {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package net.archethic.yubikit_android.methods

import androidx.annotation.NonNull
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel

interface MethodHandler {
fun handle(@NonNull call: MethodCall, @NonNull result: MethodChannel.Result);
}
2 changes: 1 addition & 1 deletion example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3

COCOAPODS: 1.11.3
COCOAPODS: 1.12.0
4 changes: 3 additions & 1 deletion example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -205,6 +205,7 @@
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
Expand Down Expand Up @@ -241,6 +242,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
Expand Down
2 changes: 1 addition & 1 deletion example/lib/components/capabilities_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class CapabilitiesText extends StatelessWidget {

Future<String> capabilitiesString() async {
try {
final capabilities = await yubikitPlugin.general.deviceCapabilities;
final capabilities = await yubikitPlugin.connection.deviceCapabilities;
return 'nfc : ${capabilities.nfc}, wired : ${capabilities.wired}';
} on PlatformException {
return 'Failed to get device capabilities';
Expand Down
14 changes: 11 additions & 3 deletions example/lib/components/generate_key_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ class GenerateKeyButton extends StatelessWidget {
Widget build(BuildContext context) => ActionButton(
text: 'Generate key',
onPressed: () async {
final publicKey = await yubikitPlugin.piv.generateKey(
pin: "123456",
managementKey: PivManagementKey.fromString(
final connection = await yubikitPlugin.connection.connect(
timeout: const Duration(seconds: 15),
);
final piv = await connection.pivSession;

await piv.verifyPin("123456");
await piv.authenticate(
PivManagementKey.fromString(
"010203040506070801020304050607080102030405060708",
keyType: PivManagementKeyType.tripleDES,
),
);

final publicKey = await piv.generateKey(
pinPolicy: PivPinPolicy.defaultPolicy,
type: PivKeyType.eccp256,
touchPolicy: PivTouchPolicy.defaultPolicy,
Expand Down
8 changes: 6 additions & 2 deletions example/lib/components/piv_calculate_secret_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ class PivCalculateSecretButton extends StatelessWidget {
Widget build(BuildContext context) => ActionButton(
text: 'Calculate secret',
onPressed: () async {
final secret = await yubikitPlugin.piv.calculateSecret(
final connection = await yubikitPlugin.connection.connect();
final piv = await connection.pivSession;

piv.verifyPin("123456");

final secret = await piv.calculateSecret(
slot: PivSlot.authentication,
pin: "123456",
peerPublicKey: """
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAElqeFrBCjtonol5ksKYCuXf+alUTI
Expand Down
8 changes: 5 additions & 3 deletions example/lib/components/piv_read_cert_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ class PivReadCertButton extends StatelessWidget {
Widget build(BuildContext context) => ActionButton(
text: 'Read certificate',
onPressed: () async {
final certificate = await yubikitPlugin.piv.getCertificate(
pin: "123456",
final connection = await yubikitPlugin.connection.connect();
final piv = await connection.pivSession;
await piv.verifyPin("123456");
final publicKey = await piv.getCertificate(
slot: PivSlot.signature,
);
return String.fromCharCodes(certificate);
return String.fromCharCodes(publicKey);
},
);
}
1 change: 0 additions & 1 deletion ios/Classes/handlers/PivCalculateSecret.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ class PivCalculateSecretHandler: Handler {

return
}

pivSession.verifyPin(pin) { retries, verifyPinError in
guard verifyPinError == nil else {
context.failure(
Expand Down
18 changes: 18 additions & 0 deletions lib/src/domain/protocol/connection/protocol.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:yubidart/yubidart.dart';

abstract class Connection {
Future<PivProtocol> get pivSession;

Future<OTPProtocol> get otpSession;
}

abstract class ConnectionProtocol {
/// Looks at the device capabilities (connectivity mainly)
Future<DeviceCapabilities> get deviceCapabilities;

Future<Connection> connect({
Duration timeout = const Duration(minutes: 1),
});

Future<void> disconnect();
}
6 changes: 0 additions & 6 deletions lib/src/domain/protocol/general/protocol.dart

This file was deleted.

18 changes: 10 additions & 8 deletions lib/src/domain/protocol/piv/protocol.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ import 'package:yubidart/src/domain/model/piv/slot.dart';
import 'package:yubidart/src/domain/model/piv/touch_policy.dart';

abstract class PivProtocol {
/// Verifies the PIN code.
///
/// [pin] The pin. Default pin code is 123456.
Future<PivProtocol> verifyPin(String pin);

/// Authenticates with the management key
///
/// [managementKey] The management key. Default value is 000102030405060708090A0B0C0D0E0F1011121314151617.
Future<PivProtocol> authenticate(PivManagementKey managementKey);

/// Generates a new key pair within the YubiKey.
/// This method requires authentication and pin verification.
///
Expand All @@ -16,8 +26,6 @@ abstract class PivProtocol {
/// TouchPolicy.CACHED requires support for touch cached, available on YubiKey 4.3 or later.
/// This method is thread safe and can be invoked from any thread (main or a background thread).
///
/// [pin] The pin. Default pin code is 123456.
/// [managementKey] The management key. Default is 010203040506070801020304050607080102030405060708.
/// [slot] The slot to generate the new key in.
/// [type] Which algorithm is used for key generation.
/// [pinPolicy] The PIN policy for using the private key.
Expand All @@ -27,8 +35,6 @@ abstract class PivProtocol {
///
/// Throws a YKFailure
Future<Uint8List> generateKey({
required String pin,
required PivManagementKey managementKey,
required PivSlot slot,
required PivKeyType type,
required PivPinPolicy pinPolicy,
Expand All @@ -37,28 +43,24 @@ abstract class PivProtocol {

/// Reads the X.509 certificate stored in the specified slot on the YubiKey.
///
/// [pin] The pin. Default pin code is 123456.
/// [slot] : The slot where the certificate is stored.
///
/// Returns certificate instance
///
/// Throws a YKFailure
Future<Uint8List> getCertificate({
required String pin,
required PivSlot slot,
});

/// Perform an ECDH operation with a given public key to compute a shared secret.
///
/// [pin] The pin. Default pin code is 123456.
/// [slot] The slot containing the private EC key to use.
/// [peerPublicKey] The peer public key for the operation. This is an EllipticCurve encryption public key in PEM format.
///
/// Returns the shared secret, comprising the x-coordinate of the ECDH result point.
///
/// Throws a YKFailure
Future<Uint8List> calculateSecret({
required String pin,
required PivSlot slot,
required String peerPublicKey,
});
Expand Down
2 changes: 1 addition & 1 deletion lib/src/domain/protocol/protocol.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export 'general/protocol.dart';
export 'connection/protocol.dart';
export 'otp/otp.dart';
export 'piv/protocol.dart';
12 changes: 3 additions & 9 deletions lib/src/domain/yubidart_platform_interface.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'package:yubidart/src/domain/protocol/general/protocol.dart';
import 'package:yubidart/src/domain/protocol/piv/protocol.dart';
import 'package:yubidart/src/domain/protocol/connection/protocol.dart';

abstract class YubidartPlatform extends PlatformInterface {
/// Constructs a [YubidartPlatform].
Expand All @@ -15,9 +14,7 @@ abstract class YubidartPlatform extends PlatformInterface {
/// Defaults to MethodChannelYubidart.
static YubidartPlatform get instance => _instance;

PivProtocol get piv;

GeneralProtocol get general;
ConnectionProtocol get connection;

/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [YubidartPlatform] when
Expand All @@ -30,8 +27,5 @@ abstract class YubidartPlatform extends PlatformInterface {

class EmptyYubidartPlatformImplementation implements YubidartPlatform {
@override
GeneralProtocol get general => throw UnimplementedError();

@override
PivProtocol get piv => throw UnimplementedError();
ConnectionProtocol get connection => throw UnimplementedError();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import 'package:flutter/services.dart';
import 'package:yubidart/src/domain/model/failure/failure.dart';
import 'package:yubidart/src/domain/model/general/device_capabilities.dart';
import 'package:yubidart/src/domain/protocol/connection/protocol.dart';
import 'package:yubidart/src/domain/protocol/otp/otp.dart';
import 'package:yubidart/src/domain/protocol/piv/protocol.dart';
import 'package:yubidart/src/infrastructure/protocol/otp/default_otp_protocol.dart';
import 'package:yubidart/src/infrastructure/protocol/otp/yubicloud_client.dart';
import 'package:yubidart/src/infrastructure/protocol/piv/default_piv_protocol.dart';

class DefaultConnection implements Connection {
@override
Future<PivProtocol> get pivSession async => DefaultPivProtocol();

@override
Future<OTPProtocol> get otpSession async =>
DefaultOTPProtocol(yubicloudClient: YubicloudClient());
}

class DefaultConnectionProtocol implements ConnectionProtocol {
/// The method channel used to interact with the native platform.
// @foundation.visibleForTesting
final methodChannel = const MethodChannel('net.archethic/yubidart');

@override
Future<DeviceCapabilities> get deviceCapabilities => YKFailure.guard(
() async {
final supportsNFCScanning =
await methodChannel.invokeMethod<bool>('supportsNFCScanning');
final supportsISO7816NFCTags =
await methodChannel.invokeMethod<bool>('supportsISO7816NFCTags');
final supportsMFIAccessoryKey =
await methodChannel.invokeMethod<bool>('supportsMFIAccessoryKey');

if (supportsNFCScanning == null ||
supportsISO7816NFCTags == null ||
supportsMFIAccessoryKey == null) {
throw YKFailure.other();
}

return DeviceCapabilities(
nfc: supportsNFCScanning || supportsISO7816NFCTags,
wired: supportsMFIAccessoryKey,
);
},
);

@override
Future<Connection> connect({
Duration timeout = const Duration(minutes: 1),
}) {
return YKFailure.guard(
() async {
await methodChannel.invokeMethod(
'connect',
);
return DefaultConnection();
},
);
}

@override
Future<void> disconnect() {
return YKFailure.guard(
() async {
await methodChannel.invokeMethod(
'disconnect',
);
},
);
}
}

This file was deleted.

Loading
Loading