From 7842b1b9091101544ae83f30ca85a92f9517ca43 Mon Sep 17 00:00:00 2001 From: hanez Date: Fri, 3 Oct 2025 21:35:28 +0200 Subject: [PATCH] Added more PC/SC stdlib and example code. (0.17.10) --- CMakeLists.txt | 2 +- examples/classes_demo.fun | 7 ++++ examples/pcsc_example.fun | 85 +++++++++++++++++++++++++++++++++++---- lib/io/pcsc.fun | 73 +++++++++++++++++++++++++++++++-- 4 files changed, 155 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 057b6d5..71db2ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(fun VERSION 0.17.9 LANGUAGES C) +project(fun VERSION 0.17.10 LANGUAGES C) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) diff --git a/examples/classes_demo.fun b/examples/classes_demo.fun index 3db659f..329ba96 100755 --- a/examples/classes_demo.fun +++ b/examples/classes_demo.fun @@ -80,6 +80,12 @@ print("counter inc -> " + to_string(c.inc())) // 2 print("counter add(5) -> " + to_string(c.add(5))) // 7 print("counter current value = " + to_string(c["value"])) +class HaveFun() + fun have_fun(this) + print("Have Fun!") + +HaveFun().have_fun() + /* Expected output: typeof(Point) = Class typeof(p) = Point(10, -2) @@ -92,4 +98,5 @@ counter inc -> 1 counter inc -> 2 counter add(5) -> 7 counter current value = 7 +Have Fun! */ diff --git a/examples/pcsc_example.fun b/examples/pcsc_example.fun index 4cf7384..46a9030 100755 --- a/examples/pcsc_example.fun +++ b/examples/pcsc_example.fun @@ -1,14 +1,83 @@ #!/usr/bin/env fun +/* + * This file is part of the Fun programming language. + * https://hanez.org/project/fun/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-10-02 + */ + // Minimal PCSC example for Fun language // Establish context and list readers (prints [] if none or unsupported) -class Pcsc() - fun list_readers(this) - ctx = pcsc_establish() - readers = pcsc_list_readers(ctx) - print(readers) - _ = pcsc_release(ctx) +// This example is very smartcard specific, but it works in my test environment. +// The card I am using is a german "eTicket" (local transport ticket). -p = Pcsc() -p.list_readers() +//include +include + +p = PCSC() + +readers = p.get_readers() +number rc = len(readers) + +print("Found " + to_string(rc) + " readers:") + +for i in range(0, rc) + print(" " + to_string(i+1) + ". " + readers[i]) +print("Dump:") +print(readers) + +// Select Applet +string apdu = "00a4040c0cD2760001354B414E4D30310000" +print("Transmit APDU (Select Applet): " + apdu) +result = p.transmit(apdu) + +print("Response:") +print(" Data: " + to_string(len(result["data"])) + " bytes") +print(" SW1: " + to_string(result["sw1"])) +print(" SW2: " + to_string(result["sw2"])) +print(" Code: " + to_string(result["code"])) +print("Dump:") +print(result) + +// Call the random number generator (RNG) +apdu = "0084000008" +print("Transmit APDU (Call the random number generator (RNG): " + apdu) +result = p.transmit(apdu) + +print("Response:") +print(" Data: " + to_string(len(result["data"])) + " bytes") +print(" SW1: " + to_string(result["sw1"])) +print(" SW2: " + to_string(result["sw2"])) +print(" Code: " + to_string(result["code"])) +print("Dump:") +print(result) + +/* Possible output: +Found 2 readers: + 1. OMNIKEY CardMan (076B:5321) 5321 00 00 + 2. OMNIKEY CardMan (076B:5321) 5321 00 01 +Dump: +[OMNIKEY CardMan (076B:5321) 5321 00 00, OMNIKEY CardMan (076B:5321) 5321 00 01] +Transmit APDU (Select Applet): 00a4040c0cD2760001354B414E4D30310000 +Response: + Data: 251 bytes + SW1: 144 + SW2: 0 + Code: 0 +Dump: +{"data": [224, 129, 248, 226, 24, 192, 1, 1, 129, 15, 0, 0, 5, 250, 136, 243, 17, 36, 236, 105, 135, 46, 236, 105, 135, 128, 2, 7, 131, 228, 7, 192, 1, 2, 130, 2, 0, 162, 231, 3, 192, 1, 3, 236, 29, 192, 1, 4, 134, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 2, 0, 0, 195, 2, 1, 9, 144, 16, 9, 8, 7, 6, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 29, 192, 1, 9, 131, 20, 0, 0, 0, 1, 136, 243, 41, 106, 136, 142, 136, 42, 38, 33, 139, 156, 45, 30, 191, 125, 132, 2, 7, 1, 233, 29, 192, 1, 8, 131, 20, 0, 0, 0, 1, 136, 243, 41, 106, 136, 142, 136, 42, 38, 33, 139, 156, 45, 30, 191, 125, 132, 2, 7, 1, 233, 29, 192, 1, 7, 131, 20, 0, 0, 0, 1, 136, 243, 41, 106, 136, 142, 136, 42, 38, 33, 139, 156, 45, 30, 191, 125, 132, 2, 7, 1, 233, 29, 192, 1, 6, 131, 20, 0, 0, 15, 22, 136, 243, 31, 143, 136, 142, 136, 42, 37, 55, 0, 1, 47, 55, 191, 125, 132, 2, 7, 1, 233, 29, 192, 1, 5, 131, 20, 0, 0, 15, 21, 136, 243, 7, 216, 136, 143, 136, 42, 37, 55, 0, 1, 37, 58, 191, 125, 132, 2, 7, 1], "sw1": 144, "sw2": 0, "code": 0} +Transmit APDU (Call the random number generator (RNG): 0084000008 +Response: + Data: 8 bytes + SW1: 144 + SW2: 0 + Code: 0 +Dump: +{"data": [25, 179, 169, 196, 57, 36, 89, 139], "sw1": 144, "sw2": 0, "code": 0} +*/ diff --git a/lib/io/pcsc.fun b/lib/io/pcsc.fun index 6ce4667..4a82e07 100644 --- a/lib/io/pcsc.fun +++ b/lib/io/pcsc.fun @@ -11,14 +11,13 @@ * Added: 2025-10-02 */ -// __ns_alias__: PCSC - // PCSC stdlib abstraction class wrapping VM opcodes. // Provides snake_case methods and hex helpers. // All methods are defensive and work even if PCSC is unsupported (returning []/0 or a default map). -#include +include +/* class PCSC() // ---- hex helpers moved to lib/hex.fun ---- @@ -78,3 +77,71 @@ class PCSC() m["sw2"] = res["sw2"] m["code"] = res["code"] return m +*/ + +class PCSC() + fun get_readers(this) + ctx = pcsc_establish() + readers = pcsc_list_readers(ctx) + _ = pcsc_release(ctx) + return readers + + // Transmit a raw hex APDU to the first available reader and print the result map + fun transmit(this, hex_apdu) + ctx = pcsc_establish() + readers = pcsc_list_readers(ctx) + if (readers == nil) + print([]) + _ = pcsc_release(ctx) + return 0 + if (len(readers) == 0) + print([]) + _ = pcsc_release(ctx) + return 0 + // Actually selecting the second reader hardcode. This class is not in a very early stage of development. + handle = pcsc_connect(ctx, readers[1]) + apdu = this.hex_to_bytes(hex_apdu) + res = pcsc_transmit(handle, apdu) + _ = pcsc_disconnect(handle) + _ = pcsc_release(ctx) + return res + + fun hex_digit(this, ch) + m = {} + m["0"] = 0 + m["1"] = 1 + m["2"] = 2 + m["3"] = 3 + m["4"] = 4 + m["5"] = 5 + m["6"] = 6 + m["7"] = 7 + m["8"] = 8 + m["9"] = 9 + m["a"] = 10 + m["A"] = 10 + m["b"] = 11 + m["B"] = 11 + m["c"] = 12 + m["C"] = 12 + m["d"] = 13 + m["D"] = 13 + m["e"] = 14 + m["E"] = 14 + m["f"] = 15 + m["F"] = 15 + v = m[ch] + if v == nil + return 0 + return v + + fun hex_to_bytes(this, hex) + s = to_string(hex) + out = [] + number i = 0 + number n = len(s) + while i + 1 < n + number b = this.hex_digit(substr(s, i, 1)) * 16 + this.hex_digit(substr(s, i + 1, 1)) + push(out, b) + i = i + 2 + return out