Important!

Blog moved to https://blog.apdu.fr/

I moved my blog from https://ludovicrousseau.blogspot.com/ to https://blog.apdu.fr/ . Why? I wanted to move away from Blogger (owne...

Saturday, October 23, 2021

PCSC sample in Flutter/Dart

Here is a new PC/SC sample in Dart language I promised in PC/SC sample in different languages.

Fabien Rousseau wrote a PC/SC wrapper for Dart (the language) and Flutter (the UI toolkit).

The library is at https://pub.dev/packages/flutter_pcsc. It is available for GNU/Linux, macOS and Windows.

The source code is at https://github.com/fabienrousseau/flutter_pcsc and uses the MIT license. The current version is 0.0.4.

Fabien also provides an sample code at https://github.com/fabienrousseau/flutter_pcsc/blob/main/flutter_pcsc/example/lib/main.dart. I reused this code for my example bellow.

 

Installation

I just cloned the flutter_pcsc git repository.

You also need to install the flutter SDK to build and run the application. But if you use Flutter I imagine your already have the SDK installed.


Source code

You can use flutter create test to create a sample application with the correct structure and configuration files.

Then edit the pubspec.yaml file to add in the dependencies: section something like:

dependencies:
  flutter_pcsc:
    path: /home/rousseau/Documents/github/flutter_pcsc/flutter_pcsc

Of course you adapt the path to the location of your flutter_pcsc directory.

The source code of the sample application lib/main.dart is:

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';

import 'package:flutter_pcsc/flutter_pcsc.dart';

void main() {
  MyApp? myApp;

  runZonedGuarded(() async {
    WidgetsFlutterBinding.ensureInitialized();
    FlutterError.onError = (FlutterErrorDetails details) {
      FlutterError.dumpErrorToConsole(details);
      myApp?.addError(details.toString());
    };

    runApp(myApp = MyApp());
  }, (Object error, StackTrace stack) {
    myApp?.addError(error.toString());
  });
}

class MyApp extends StatelessWidget {
  final GlobalKey<_MyAppBodyState> _myAppKey = GlobalKey();

  MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(
            title: const Text('PCSC example app'),
          ),
          body: MyAppBody(key: _myAppKey)),
    );
  }

  void addError(String msg) {
    _myAppKey.currentState?.messages.add(Message.error(msg));
  }
}

class MyAppBody extends StatefulWidget {
  const MyAppBody({required Key key}) : super(key: key);

  @override
  _MyAppBodyState createState() {
    return _MyAppBodyState();
  }
}

enum MessageType { info, error }

class Message {
  final String content;
  final MessageType type;
  Message(this.type, this.content);

  static info(String content) {
    return Message(MessageType.info, content);
  }

  static error(String content) {
    return Message(MessageType.error, content);
  }
}

class _MyAppBodyState extends State<MyAppBody> {
  static const List<int> selectAppletCommand = [
    0x00, 0xA4, 0x04, 0x00, 0x0A, 0xA0, 0x00, 0x00, 0x00, 0x62,
    0x03, 0x01, 0x0C, 0x06, 0x01
  ];
  static const List<int> appCommand = [
    0x00, 0x00, 0x00, 0x00
  ];
  final ScrollController _scrollController = ScrollController();

  final List<Message> messages = [];

  @override
  void initState() {
    super.initState();
    helloWorld();
  }

  Future<void> helloWorld() async {
    /* establish PCSC context */
    int ctx = await Pcsc.establishContext(PcscSCope.user);
    CardStruct? card;
    try {
      /* get the reader list */
      List<String> readers = await Pcsc.listReaders(ctx);

      if (readers.isEmpty) {
        messages.add(Message.error('Could not detect any reader'));
      } else {
        /* use the first reader */
        String reader = readers[0];
        setState(() {
          messages.add(Message.info('Using reader: $reader'));
        });

        /* connect to the card */
        card = await Pcsc.cardConnect(
            ctx, reader, PcscShare.shared, PcscProtocol.any);

        /* send select applet APDU */
        var response = await Pcsc.transmit(card, selectAppletCommand);
            messages.add(Message.info('Card returned: ${hexDump(response)}'));

        /* send applet test command */
        response = await Pcsc.transmit(card, appCommand);
        var sw = response.sublist(response.length - 2);
        var bytes = response.sublist(0, response.length - 2);
        var text = utf8.decode(bytes);

        messages.add(Message.info('Card returned: ${hexDump(response)}'));

        if (sw[0] != 0x90 || sw[1] != 0x00) {
          setState(() {
            messages
                .add(Message.error('Card returned an error: ${hexDump(sw)}'));
          });
        } else {
          setState(() {
            messages.add(Message.info('text is: ${text}'));
          });
        }
      }
    } finally {
      if (card != null) {
        try {
          /* disconnect from the card */
          await Pcsc.cardDisconnect(card.hCard, PcscDisposition.resetCard);
        } on Exception catch (e) {
          messages.add(Message.error(e.toString()));
        }
      }
      try {
        /* release PCSC context */
        await Pcsc.releaseContext(ctx);
      } on Exception catch (e) {
        messages.add(Message.error(e.toString()));
      }
    }
  }

  static String hexDump(List<int> csn) {
    return csn
        .map((i) => i.toRadixString(16).padLeft(2, '0').toUpperCase())
        .join(' ');
  }

  _scrollToBottom() {
    _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
  }

  @override
  Widget build(BuildContext context) {
    TextStyle errorStyle = const TextStyle(color: Colors.red);
    WidgetsBinding.instance?.addPostFrameCallback((_) => _scrollToBottom());
    return Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
      Expanded(
          child: Column(children: [
        Expanded(
            child: ListView(
                controller: _scrollController,
                children: messages
                    .map((e) => Text(e.content,
                        style: e.type == MessageType.error ? errorStyle : null))
                    .toList())),
        Container(
            margin: const EdgeInsets.all(10),
            child: ElevatedButton(
                onPressed: () async {
                  await tryAgain();
                },
                child: const Text("Try again")))
      ]))
    ]);
  }

  tryAgain() async {
    messages.clear();
    await helloWorld();
  }
}

Output

$ flutter run
hanging current working directory to: /home/rousseau/Documents/flutter/blog
Launching lib/main.dart on Linux in debug mode...
Building Linux application...                                           
Syncing files to device Linux...                                    55ms

Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).

💪 Running with sound null safety 💪

An Observatory debugger and profiler on Linux is available at:
http://127.0.0.1:34019/ORqpNzBQLEQ=/
The Flutter DevTools debugger and profiler on Linux is available at:
http://127.0.0.1:9100?uri=http://127.0.0.1:34019/ORqpNzBQLEQ=/

Flutter is a framework to create graphical applications. So when you run the sample code you get a new window.

Remarks

The code is long because it is a complete Flutter application so you have to handle the graphical interface.

I asked Fabien for a Dart only interface but flutter_pcsc depends on some Flutter mechanisms to handle asynchronism. So it is not possible to write a PC/SC application for the console just using Dart. But if you use Dart I guess you also use flutter.

Despite of his (nice) name Fabien Rousseau is not part of my direct family. So I do not have a potential conflict of interest here 😜.


Conclusion

Thanks to Fabien for telling about his Dart/Flutter project.

If you work on a Free Software PC/SC wrapper that is not yet in my list please let me know.

Friday, October 15, 2021

What happened 20 years ago?

The 14th of October 2001 I became a Debian Developer (or DD).

You can get some more details from https://nm.debian.org/person/rousseau/

History of my Debian packages

Unsurprisingly I mostly maintain smart card related packages.

2001

  • pcsc-perl Perl interface to the PC/SC smart card library
  • pcsc-tools Some tools to use with smart cards and PC/SC

2002

  • ifd-gempc
    • libgempc410 - PC/SC driver for the GemPC 410, 412, 413 and 415 smart card readers
    • libgempc430 - PC/SC driver for the GemPC 430, 432, 435 smart card readers
  • pcsc-lite
    • pcscd - Middleware to access a smart card using PC/SC (daemon side) 
    • libpcsclite1 - Middleware to access a smart card using PC/SC (library) 
    • libpcsclite-dev - Middleware to access a smart card using PC/SC (development files)

2003

  • ccid PC/SC driver for USB CCID smart card readers
  • pilot-link
    • libpisock9 - library for communicating with a PalmOS PDA
    • libpisync1 - synchronization library for PalmOS devices
    • pilot-link - tools to communicate with a PalmOS PDA
    • python-pisock - Python module to communicate with PalmOS PDA
    • libpisock-dev - development files for communicating with a PalmOS PDA
    • python-pisock-dbg - Python module to communicate with PalmOS PDA (debug extension)

    • I will stoped maintaining pilot-link in 2019 because I do not have a Palm pilot any more.

2004

  • jpilot 
    • jpilot - graphical app. to modify the contents of your Palm Pilot's DBs
    • jpilot-plugins - plugins for jpilot (Palm Pilot desktop)
       
    • I will stoped maintaining jpilot in 2019 for the same reason I stopped maintaining pilot-link

2005

  • asedriveiiie
    • libasedrive-serial - PC/SC driver for the Athena ASEDrive IIIe serial smart card reader
    • libasedrive-usb - PC/SC driver for the Athena ASEDrive IIIe USB smart card reader  

2007

  • coolkey
    • coolkey - Smart Card PKCS #11 cryptographic module
    • libckyapplet1 - Smart Card Coolkey applet
    • libckyapplet1-dev - Smart Card Coolkey applet development files 

2008

  • pykcs11 - PKCS#11 wrapper for Python
  • pyscard - Python3 wrapper above PC/SC API

2009

  • pam-pkcs11 - Fully featured PAM module for using PKCS#11 smart cards
  • colormake - simple wrapper around make to colorize output

2013

  • vgrabbj - grabs an image from a camera and puts it in jpg/png format
  • acsccid - PC/SC driver for ACS USB CCID smart card readers
    • I just sponsor this package because the package maintainer is not a Debian Developer

2016

  • grisbi - personal finance management program

2017

  • 0ad - Real-time strategy game of ancient warfare
  • 0ad-data - Real-time strategy game of ancient warfare (data files)

2020

  • libnfc
    • libnfc-bin - Near Field Communication (NFC) binaries
    • libnfc-dev - Near Field Communication (NFC) library (development files)
    • libnfc-examples - Near Field Communication (NFC) examples
    • libnfc-pn53x-examples - Near Field Communication (NFC) examples for PN53x chips only
    • libnfc6 - Near Field Communication (NFC) library 

You can have a list of my packages and their status at my Debian QA page.


Why?

I maintain Debian packages for different reasons.

  • I maintain the package upstream.
    • For example I am the author and maintainer of the CCID driver. And I also maintain the Debian package for this software.
  • I use the package and it has been orphaned. It may have been orphaned in Debian or also orphaned upstream.
    • For example I am not the original author of pcsc-lite. I first started providing bug reports, then patches. I got a write access on the source code repository. I made new releases. And eventually I am on the only maintainer of pcsc-lite.


Conclusion

I think Debian is a very nice project. I do plan to continue maintaining my packages for the next 10 or 20 years. Maybe more :-)

Tuesday, October 12, 2021

No more error logs when a USB reader is removed

When a USB reader was removed you got error logs from the CCID driver. Something like:

00000000 [140295925790464] ccid_usb.c:871:WriteUSB() write failed (1/22): -4 LIBUSB_ERROR_NO_DEVICE
00000506 [140295934183168] ccid_usb.c:871:WriteUSB() write failed (1/22): -4 LIBUSB_ERROR_NO_DEVICE

 

Why?

The first error is because IFDHICCPresence() sends the CCID command PC_to_RDR_GetSlotStatus to know if a card is present.

The second error is because IFDHCloseChannel() sends the CCID command PC_to_RDR_IccPowerOff to power off the card.

But since the USB device has been removed these 2 CCID commands fails and logs the error.  

 

Solution

I had to modify both pcsc-lite (the middleware) and libccid (the smart card reader driver) so they both cooperate to fix the problem.

Now when pcscd detects that a reader has been removed it uses IFDHSetCapabilities() with the new tag TAG_IFD_DEVICE_REMOVED to indicate to the driver that the reader has been removed and that there is no point in trying to send commands to a no more connected reader.

 

Bug reports

The problem has been initially reported by a Fedora user at "Bug 2011128 - pcscd spam syslog when the laptop is undocked and the reader is connected through the dock".

Then Jakub Jelen from RedHat forwarded the bug upstream (to me) at "Reader disconnects are noisy in journal/logs #110" so I am aware of the problem.

The problem was then fixed 5 days later in "Use TAG_IFD_DEVICE_REMOVED when the reader is removed" for pcsc-lite and "Avoid logging errors when a reader is removed" for libccid.

Report upstream

The important point here is that users must reports bugs to their distribution. And distributions must report the problems upstream. It works well (in general).

What does not work is if you know of a problem and the problem does not come to me. For example this bug "Memory leak in pcscd" has been reported to pfSense 3 months ago and no one from pfSense took the time to report the bug upstream (to me). They just discussed about how to remove/disable pcsc-lite from pfSense instead.

I don't have a pfSense account so I can't comment on their bug tracker. I can't create an account on the bug trackers of every project that use my software. That is not how it is supposed to work. Distributors should forward bugs to the upstream projects.

I don't know if the problem is because pfSense is a commercial product or if it is because it uses FreeBSD. (be careful, a troll may be hidden in the previous sentence).


Conclusion

Don't be shy, report bugs.

Don't be shy, forward bugs upstream.

Friday, October 1, 2021

New version of pcsc-lite: 1.9.4

I just released a new version of pcsc-lite 1.9.4.
pcsc-lite is a Free Software implementation of the PC/SC (or WinSCard) API for Unix systems.

This version fixes a memory leak when libusb is used for the hotplug mechanism. GNU/Linux systems should use libudev instead of libusb for hotplug so they should not be concerned. FreeBSD systems and other non-Linux systems had the problem.

The memory leak problem was also reported on the pfSense project at https://redmine.pfsense.org/issues/12095.

Changes:

1.9.4: Ludovic Rousseau
1 October 2021

  •  fix a memory leak when libusb is used for hotplug (i.e. non-Linux systems)