Skip to content

Commit

Permalink
add pin protocol 1 support
Browse files Browse the repository at this point in the history
  • Loading branch information
dangfan committed Jan 15, 2024
1 parent 36de307 commit 689695e
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 44 deletions.
2 changes: 1 addition & 1 deletion lib/fido2.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
library fido2;

export 'src/ctap2/base.dart';
export 'src/utils/string.dart';
export 'src/ctap2/pin.dart';
12 changes: 6 additions & 6 deletions lib/src/cose.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ class ES256 extends CoseKey {

ES256(Map<int, dynamic> coseKeyParams) : super(coseKeyParams);

static ES256 fromPublicKey(List<int> publicKey) {
static ES256 fromPublicKey(List<int> x, List<int> y) {
return ES256({
1: 2,
3: ES256.algorithm,
-1: 1,
-2: publicKey.sublist(1, 33),
-3: publicKey.sublist(33),
-2: x,
-3: y,
});
}

Expand All @@ -72,13 +72,13 @@ class EcdhEsHkdf256 extends CoseKey {

EcdhEsHkdf256(Map<int, dynamic> coseKeyParams) : super(coseKeyParams);

static EcdhEsHkdf256 fromPublicKey(List<int> publicKey) {
static EcdhEsHkdf256 fromPublicKey(List<int> x, List<int> y) {
return EcdhEsHkdf256({
1: 2,
3: EcdhEsHkdf256.algorithm,
-1: 1,
-2: publicKey.sublist(0, 32),
-3: publicKey.sublist(32),
-2: x,
-3: y,
});
}

Expand Down
66 changes: 66 additions & 0 deletions lib/src/ctap2/pin.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import 'package:convert/convert.dart';
import 'package:cryptography/cryptography.dart';
import 'package:elliptic/ecdh.dart';
import 'package:elliptic/elliptic.dart';
import 'package:fido2/fido2.dart';
import 'package:fido2/src/cose.dart';
import 'package:quiver/collection.dart';

class EncapsulateResult {
final CoseKey coseKey;
final List<int> sharedSecret;

EncapsulateResult(this.coseKey, this.sharedSecret);
}

abstract class PinProtocol {
Future<EncapsulateResult> encapsulate(CoseKey peerCoseKey);

Future<List<int>> encrypt(List<int> key, List<int> plaintext);

Future<List<int>> decrypt(List<int> key, List<int> ciphertext);

Future<List<int>> authenticate(List<int> key, List<int> message);

Future<bool> verify(List<int> key, List<int> message, List<int> signature);
}

class PinProtocolV1 extends PinProtocol {
@override
Future<EncapsulateResult> encapsulate(CoseKey peerCoseKey) async {
final ec = getP256();
final priv = ec.generatePrivateKey();
final pub = priv.publicKey;
final pubBytes = hex.decode(pub.toHex().substring(2));
final keyAgreement = EcdhEsHkdf256.fromPublicKey(pubBytes.sublist(0, 32), pubBytes.sublist(32, 64));
final sharedSecret = computeSecret(priv, ec.hexToPublicKey('04${hex.encode(peerCoseKey[-2] + peerCoseKey[-3])}'));
return EncapsulateResult(keyAgreement, await sharedSecret);
}

@override
Future<List<int>> encrypt(List<int> key, List<int> plaintext) async {
final algorithm = AesCbc.with128bits(macAlgorithm: MacAlgorithm.empty, paddingAlgorithm: PaddingAlgorithm.zero);
final secretBox = await algorithm.encrypt(plaintext, secretKey: SecretKeyData(key), nonce: List.filled(16, 0));
return secretBox.cipherText;
}

@override
Future<List<int>> decrypt(List<int> key, List<int> ciphertext) async {
final algorithm = AesCbc.with128bits(macAlgorithm: MacAlgorithm.empty, paddingAlgorithm: PaddingAlgorithm.zero);
return await algorithm.decrypt(SecretBox(ciphertext, nonce: List.filled(16, 0), mac: Mac.empty), secretKey: SecretKeyData(key));
}

@override
Future<List<int>> authenticate(List<int> key, List<int> message) async {
final algorithm = Hmac.sha256();
final mac = await algorithm.calculateMac(message, secretKey: SecretKeyData(key));
return mac.bytes;
}

@override
Future<bool> verify(List<int> key, List<int> message, List<int> signature) async {
final algorithm = Hmac.sha256();
final mac = await algorithm.calculateMac(message, secretKey: SecretKeyData(key));
return listsEqual(mac.bytes, signature);
}
}
23 changes: 0 additions & 23 deletions lib/src/utils/string.dart

This file was deleted.

3 changes: 3 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ environment:
dependencies:
cbor: ^6.1.1
convert: ^3.0.1
cryptography: ^2.7.0
elliptic: ^0.3.10
quiver: ^3.2.1

dev_dependencies:
lints: ^2.1.0
Expand Down
25 changes: 11 additions & 14 deletions test/fido2_base_test.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:convert/convert.dart';
import 'package:fido2/fido2.dart';
import 'package:fido2/src/cose.dart' as cose;

import 'package:fido2/src/cose.dart';
import 'package:test/test.dart';

void main() {
Expand All @@ -11,7 +11,7 @@ void main() {
});

test('Response', () {
var response = hexStringToList(
var response = hex.decode(
'AD0183665532465F5632684649444F5F325F30684649444F5F325F3102846863726564426C6F626B6372656450726F746563746B686D61632D7365637265746C6C61726765426C6F624B65790350244EB29EE0904E4981FE1F20F8D3B8F404A662726BF568637265644D676D74F569636C69656E7450696EF46A6C61726765426C6F6273F56E70696E557641757468546F6B656EF5706D616B654372656455764E6F74527164F5051905140682010207080818460982636E6663637573620A83A263616C672664747970656A7075626C69632D6B6579A263616C672764747970656A7075626C69632D6B6579A263616C67382F64747970656A7075626C69632D6B65790B1910000E18C90F1820');
var info = Ctap2.parseGetInfoResponse(response);
expect(info.versions, equals(['U2F_V2', 'FIDO_2_0', 'FIDO_2_1']));
Expand All @@ -21,31 +21,28 @@ void main() {

group('ClientPin', () {
test('Request1', () {
var request = Ctap2.makeClientPinRequest(ClientPinRequest(
subCommand: ClientPinSubCommand.getKeyAgreement.value,
pinUvAuthProtocol: 2));
expect(request, equals(hexStringToList('06A201020202')));
var request = Ctap2.makeClientPinRequest(ClientPinRequest(subCommand: ClientPinSubCommand.getKeyAgreement.value, pinUvAuthProtocol: 2));
expect(request, equals(hex.decode('06A201020202')));
});

test('Request2', () {
var request = Ctap2.makeClientPinRequest(ClientPinRequest(
subCommand: ClientPinSubCommand.setPin.value,
pinUvAuthProtocol: 2,
keyAgreement: cose.EcdhEsHkdf256.fromPublicKey(hexStringToList(
'9950CCD8C524DBAAB6D5ED7E4256B72A647920445DCA51DA5F1B2A6AEB9AAB1880CC342ABC60C6FD1E8101CB3AA1D34B43CAFA6C3CA5403D70DEC1C72EC637FD')),
pinUvAuthParam: hexStringToList(
'9941B629D9BAB9C8C578D5E7A3AE6201B7A2F90F02B238AA2674F4A976C17FF3'),
newPinEnc: hexStringToList(
keyAgreement: EcdhEsHkdf256.fromPublicKey(hex.decode('9950CCD8C524DBAAB6D5ED7E4256B72A647920445DCA51DA5F1B2A6AEB9AAB18'),
hex.decode('80CC342ABC60C6FD1E8101CB3AA1D34B43CAFA6C3CA5403D70DEC1C72EC637FD')),
pinUvAuthParam: hex.decode('9941B629D9BAB9C8C578D5E7A3AE6201B7A2F90F02B238AA2674F4A976C17FF3'),
newPinEnc: hex.decode(
'75E69079A080945600397CC32ABE3B5CFD61C1BBBAD4CE71396EBB64D51D0198CC9D6FF8EBD14A6C9A134BE717CEBFB1CB25815B3AD0080DCC7414D8604DF1729E89EA54B1277DC701077C6ED5B8512A'),
));
expect(
request,
equals(hexStringToList(
equals(hex.decode(
'06A50102020303A5010203381820012158209950CCD8C524DBAAB6D5ED7E4256B72A647920445DCA51DA5F1B2A6AEB9AAB1822582080CC342ABC60C6FD1E8101CB3AA1D34B43CAFA6C3CA5403D70DEC1C72EC637FD0458209941B629D9BAB9C8C578D5E7A3AE6201B7A2F90F02B238AA2674F4A976C17FF305585075E69079A080945600397CC32ABE3B5CFD61C1BBBAD4CE71396EBB64D51D0198CC9D6FF8EBD14A6C9A134BE717CEBFB1CB25815B3AD0080DCC7414D8604DF1729E89EA54B1277DC701077C6ED5B8512A')));
});

test('Response1', () {
var response = hexStringToList(
var response = hex.decode(
'A101A50102033818200121582064E75C1E36EF6C3C17F609014D96D048BEB6793CD34823358E44A599B4DD2291225820235BD52FAEB2A3599F10D38EFB58E65BE58AE67AF118BF1BC528FA4B090EE763');
var clientPinResponse = Ctap2.parseClientPinResponse(response);
expect(clientPinResponse.keyAgreement![1], equals(2));
Expand Down
58 changes: 58 additions & 0 deletions test/fido2_test_pin.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import 'package:convert/convert.dart';
import 'package:elliptic/ecdh.dart';
import 'package:elliptic/elliptic.dart';
import 'package:fido2/fido2.dart';
import 'package:fido2/src/cose.dart';
import 'package:fido2/src/ctap2/pin.dart';
import 'package:test/test.dart';

void main() {
group('Protocol 1', () {
test('encapsulate', () async {
final ec = getP256();
final priv = ec.generatePrivateKey();
final pub = priv.publicKey;
final pubBytes = hex.decode(pub.toHex().substring(2));
final peerCoseKey = EcdhEsHkdf256.fromPublicKey(pubBytes.sublist(0, 32), pubBytes.sublist(32, 64));

PinProtocolV1 pinProtocol = PinProtocolV1();
EncapsulateResult result = await pinProtocol.encapsulate(peerCoseKey);
final sharedSecret = computeSecret(priv, ec.hexToPublicKey('04${hex.encode(result.coseKey[-2] + result.coseKey[-3])}'));
expect(sharedSecret, equals(result.sharedSecret));
});

test('encrypt', () async {
final key = hex.decode('000102030405060708090a0b0c0d0e0f');
final plaintext = hex.decode('00112233445566778899aabbccddeeff');
final ciphertext = hex.decode('69c4e0d86a7b0430d8cdb78070b4c55a');
PinProtocolV1 pinProtocol = PinProtocolV1();
expect(await pinProtocol.encrypt(key, plaintext), equals(ciphertext));
});

test('decrypt', () async {
final key = hex.decode('000102030405060708090a0b0c0d0e0f');
final plaintext = hex.decode('00112233445566778899aabbccddeeff');
final ciphertext = hex.decode('69c4e0d86a7b0430d8cdb78070b4c55a');
PinProtocolV1 pinProtocol = PinProtocolV1();
expect(await pinProtocol.decrypt(key, ciphertext), equals(plaintext));
});

test('authenticate', () async {
final key = hex.decode('000102030405060708090a0b0c0d0e0f');
final message = hex.decode('00112233445566778899aabbccddeeff');
final signature = hex.decode('32cd28477b88c12e515b0e1fd7330d19616a4a51f6c502d64fe6a93fe7f786fa');
PinProtocolV1 pinProtocol = PinProtocolV1();
expect(await pinProtocol.authenticate(key, message), equals(signature));
});

test('verify', () async {
final key = hex.decode('000102030405060708090a0b0c0d0e0f');
final message = hex.decode('00112233445566778899aabbccddeeff');
final signature = hex.decode('32cd28477b88c12e515b0e1fd7330d19616a4a51f6c502d64fe6a93fe7f786fa');
final signatureFalse = hex.decode('32cd28477b88c12e515b0e1fd7330d19616a4a51f6c502d64fe6a93fe7f786fb');
PinProtocolV1 pinProtocol = PinProtocolV1();
expect(await pinProtocol.verify(key, message, signature), equals(true));
expect(await pinProtocol.verify(key, message, signatureFalse), equals(false));
});
});
}

0 comments on commit 689695e

Please sign in to comment.