moon_card

    ISO 7816 APDU and EMV BER-TLV transaction toolkit for MoonBit.

    Download zip
    Author
    Version
    0.3.0
    License
    Apache-2.0
    Last updated
    6 days ago
    Downloads
    36

    No dependencies

    #MoonCard 0.3.0

    面向终端交易脚本的离线日志分析:PPSE/AID、GPO/AFL、跨记录数据和CDOL长度检查。失败状态不会推进交易,应用重选会清除旧状态,AFL未读完整会报告失败。

    2026-10-30修订。Apache-2.0。核心为纯MoonBit库,Node只承担CLI文件读取。

    新增ISO 7816过程字解析:61xx GET RESPONSE多段合并、6Cxx修正Le重试;支持外部项目的send:/resp:日志与部分AID选择。SELECT中的PDOL和记录中的CDOL使用严格DOL解析,约束GPO与GENERATE AC输入长度。失败恢复链、缺失响应和非法DOL不会静默通过。

    #运行

    需要MoonBit工具链与Node.js。核心库不依赖Node,CLI使用Node读取文件。

    moon test --target js moon run cmd/main --target js -- session examples/sessions/happy.log moon run cmd/main --target js -- session examples/external/wrapperTestSW_61.log moon run cmd/main --target js -- session examples/external/wrapperTestSW_6C.log

    session命令发现fail时返回1;review输出供人工复核,退出码为0。

    #当前能力与验证

    新版功能和复现任务见本轮复核。Windows本地完成JS、wasm、wasm-gc的check/build/test及独立消费工程。外部样例来源、固定提交和Apache许可证见样例来源。部分捕获输出review表示需要人工复核,不能当作完整交易通过。

    不是EMV内核或认证工具;不处理密码学,不连接真实读卡器。示例为构造日志,pass仅表示已实现检查未发现错误。

    ApduError

    pub(all) suberror ApduError {
    Truncated
    BadLength
    LeTooLarge(Int)
    DataTooLarge(Int)
    }

    DolError

    pub(all) suberror DolError {
    InvalidDol
    }

    TlvError

    pub(all) suberror TlvError {
    Truncated
    BadTag
    BadLength
    TooDeep
    }

    AflEntry

    pub(all) struct AflEntry {
    sfi : Int
    first_record : Int
    last_record : Int
    oda_count : Int
    }

    Apdu

    pub(all) struct Apdu {
    cla : Int
    ins : Int
    p1 : Int
    p2 : Int
    data : Array[Int]
    le : Int?
    }

    Apdu::encode

    fn Apdu::encode(self : Apdu) -> Array[Int] raise ApduError

    Apdu::encode_extended

    fn Apdu::encode_extended(self : Apdu) -> Array[Int] raise ApduError

    Apdu::new

    fn Apdu::new(cla : Int, ins : Int, p1 : Int, p2 : Int, data? : Array[Int], le? : Int) -> Apdu

    ApduResponse

    pub(all) struct ApduResponse {
    data : Array[Int]
    sw : Int
    }

    CardData

    pub(all) struct CardData {
    pan : String?
    expiry : String?
    cardholder : String?
    label : String?
    }

    GpoInfo

    pub(all) struct GpoInfo {
    aip : Array[Int]
    afl : Array[Int]
    }

    SessionIssue

    pub(all) struct SessionIssue {
    step : Int
    code : String
    severity : String
    detail : String
    }

    SessionReport

    pub(all) struct SessionReport {
    verdict : String
    candidates : Array[String]
    selected_aid : String?
    aip : String?
    afl : Array[AflEntry]
    records : Array[(Int, Int)]
    issues : Array[SessionIssue]
    steps : Int
    }

    Tlv

    pub(all) struct Tlv {
    tag : Int
    constructed : Bool
    value : Array[Int]
    children : Array[Tlv]
    }

    Track2

    pub(all) struct Track2 {
    pan : String
    expiry : String
    service_code : String
    }

    TxReport

    pub(all) struct TxReport {
    steps : Array[TxStep]
    errors : Array[String]
    pan : String?
    expiry : String?
    cardholder : String?
    label : String?
    }

    TxStep

    pub(all) struct TxStep {
    direction : String
    bytes : Array[Int]
    apdu : Apdu?
    response : ApduResponse?
    tlv : Array[Tlv]
    note : String
    }

    TxSummary

    pub(all) struct TxSummary {
    commands : Int
    responses : Int
    successes : Int
    errors : Int
    }

    analyze_session

    fn analyze_session(raw_report : TxReport) -> SessionReport

    ascii_bytes

    fn ascii_bytes(text : String) -> Array[Int]

    ascii_of

    fn ascii_of(bytes : Array[Int]) -> String

    bcd_to_string

    fn bcd_to_string(bytes : Array[Int]) -> String?

    card_data_from_tlv

    fn card_data_from_tlv(items : Array[Tlv]) -> CardData

    cardholder_from_tlv

    fn cardholder_from_tlv(items : Array[Tlv]) -> String?

    cdol_total_length

    fn cdol_total_length(fields : Array[(Int, Int)]) -> Int

    collect_tag_values

    fn collect_tag_values(items : Array[Tlv], tag : Int) -> Array[Array[Int]]

    collect_tags

    fn collect_tags(items : Array[Tlv]) -> Array[Int]

    command_name

    fn command_name(apdu : Apdu) -> String

    encode_tlv

    fn encode_tlv(item : Tlv) -> Array[Int]

    encode_tlvs

    fn encode_tlvs(items : Array[Tlv]) -> Array[Int]

    expiry_from_tlv

    fn expiry_from_tlv(items : Array[Tlv]) -> String?

    find_path

    fn find_path(items : Array[Tlv], path : Array[Int]) -> Tlv?

    find_tag

    fn find_tag(items : Array[Tlv], tag : Int) -> Tlv?

    flatten_tlvs

    fn flatten_tlvs(items : Array[Tlv]) -> Array[Tlv]

    format_expiry

    fn format_expiry(yymmdd : String) -> String

    generate_ac

    fn generate_ac(p1 : Int, cdol_data : Array[Int]) -> Apdu

    get_data

    fn get_data(tag : Int) -> Apdu

    gpo

    fn gpo(pdol_data : Array[Int]) -> Apdu

    hex_decode

    fn hex_decode(text : String) -> Array[Int]?

    hex_encode

    fn hex_encode(bytes : Array[Int]) -> String

    hex_int

    fn hex_int(value : Int, width : Int) -> String

    hex_nibble_char

    fn hex_nibble_char(value : Int) -> Char

    label_from_tlv

    fn label_from_tlv(items : Array[Tlv]) -> String?

    luhn_valid

    fn luhn_valid(digits : String) -> Bool

    mask_pan

    fn mask_pan(pan : String) -> String

    pan_from_tlv

    fn pan_from_tlv(items : Array[Tlv]) -> String?

    parse_afl

    fn parse_afl(afl : Array[Int]) -> Array[AflEntry]

    parse_apdu

    fn parse_apdu(bytes : Array[Int]) -> Apdu raise ApduError

    parse_cdol

    fn parse_cdol(value : Array[Int]) -> Array[(Int, Int)]

    parse_dol

    fn parse_dol(bytes : Array[Int]) -> Array[(Int, Int)] raise DolError

    parse_gpo_response

    fn parse_gpo_response(bytes : Array[Int]) -> GpoInfo? raise TlvError

    parse_record

    fn parse_record(bytes : Array[Int]) -> Array[Tlv]? raise TlvError

    parse_response

    fn parse_response(bytes : Array[Int]) -> ApduResponse raise ApduError

    parse_tlv

    fn parse_tlv(bytes : Array[Int]) -> Array[Tlv] raise TlvError

    parse_track2

    fn parse_track2(value : Array[Int]) -> Track2?

    parse_transaction_log

    fn parse_transaction_log(text : String) -> TxReport

    read_record

    fn read_record(record : Int, sfi : Int) -> Apdu

    render_session

    fn render_session(report : SessionReport) -> String

    render_transaction

    fn render_transaction(report : TxReport) -> String

    render_transaction_csv

    fn render_transaction_csv(report : TxReport) -> String

    render_transaction_json

    fn render_transaction_json(report : TxReport) -> String

    resolve_apdu_procedures

    fn resolve_apdu_procedures(raw : TxReport) -> TxReport

    select_aid

    fn select_aid(aid : Array[Int]) -> Apdu

    select_ppse

    fn select_ppse() -> Apdu

    service_code_text

    fn service_code_text(code : String) -> String

    status_description

    fn status_description(sw : Int) -> String

    summarize_transaction

    fn summarize_transaction(report : TxReport) -> TxSummary

    tag_name

    fn tag_name(tag : Int) -> String

    tag_to_hex

    fn tag_to_hex(tag : Int) -> String

    validate_expiry

    fn validate_expiry(yymmdd : String) -> Array[String]

    validate_pan

    fn validate_pan(pan : String) -> Array[String]

    validate_service_code

    fn validate_service_code(code : String) -> Array[String]

    verify_pin

    fn verify_pin(pin_block : Array[Int]) -> Apdu