1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0

//! Error types used by this crate.
//!
//! [`Error`] is a wrapper enum for all error types that are used.
//!
//! The two main classes of errors are:
//! - [`SmartcardError`], for problems on the reader/smartcard layer
//! - [`StatusBytes`], which models error statuses reported by the OpenPGP
//! card application

use card_backend::SmartcardError;

/// Enum wrapper for the different error types of this crate
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum Error {
    #[error("Error interacting with smartcard: {0}")]
    Smartcard(SmartcardError),

    #[error("OpenPGP card error status: {0}")]
    CardStatus(StatusBytes),

    #[error("Command too long ({0} bytes)")]
    CommandTooLong(usize),

    #[error("Unexpected response length: {0}")]
    ResponseLength(usize),

    #[error("Data not found: {0}")]
    NotFound(String),

    #[error("Couldn't parse data: {0}")]
    ParseError(String),

    #[error("Unsupported algorithm: {0}")]
    UnsupportedAlgo(String),

    #[error("Unsupported feature: {0}")]
    UnsupportedFeature(String),

    // FIXME: placeholder, remove again later?
    #[error("Internal error: {0}")]
    InternalError(String),
}

impl From<StatusBytes> for Error {
    fn from(oce: StatusBytes) -> Self {
        Error::CardStatus(oce)
    }
}

impl From<SmartcardError> for Error {
    fn from(sce: SmartcardError) -> Self {
        Error::Smartcard(sce)
    }
}

/// OpenPGP card "Status Bytes" (ok statuses and errors)
#[derive(thiserror::Error, Debug, PartialEq, Eq, Copy, Clone)]
#[non_exhaustive]
pub enum StatusBytes {
    #[error("Command correct")]
    Ok,

    #[error("Command correct, [{0}] bytes available in response")]
    OkBytesAvailable(u8),

    #[error("Selected file or DO in termination state")]
    TerminationState,

    #[error("Password not checked, {0} allowed retries")]
    PasswordNotChecked(u8),

    #[error("Execution error with non-volatile memory unchanged")]
    ExecutionErrorNonVolatileMemoryUnchanged,

    #[error("Triggering by the card {0}")]
    TriggeringByCard(u8),

    #[error("Memory failure")]
    MemoryFailure,

    #[error("Security-related issues (reserved for UIF in this application)")]
    SecurityRelatedIssues,

    #[error("Wrong length (Lc and/or Le)")]
    WrongLength,

    #[error("Logical channel not supported")]
    LogicalChannelNotSupported,

    #[error("Secure messaging not supported")]
    SecureMessagingNotSupported,

    #[error("Last command of the chain expected")]
    LastCommandOfChainExpected,

    #[error("Command chaining not supported")]
    CommandChainingNotSupported,

    #[error("Security status not satisfied")]
    SecurityStatusNotSatisfied,

    #[error("Authentication method blocked")]
    AuthenticationMethodBlocked,

    #[error("Condition of use not satisfied")]
    ConditionOfUseNotSatisfied,

    #[error("Expected secure messaging DOs missing (e. g. SM-key)")]
    ExpectedSecureMessagingDOsMissing,

    #[error("SM data objects incorrect (e. g. wrong TLV-structure in command data)")]
    SMDataObjectsIncorrect,

    #[error("Incorrect parameters in the command data field")]
    IncorrectParametersCommandDataField,

    #[error("File or application not found")]
    FileOrApplicationNotFound,

    #[error("Referenced data, reference data or DO not found")]
    ReferencedDataNotFound,

    #[error("Wrong parameters P1-P2")]
    WrongParametersP1P2,

    #[error("Instruction code (INS) not supported or invalid")]
    INSNotSupported,

    #[error("Class (CLA) not supported")]
    CLANotSupported,

    #[error("No precise diagnosis")]
    NoPreciseDiagnosis,

    #[error("Unknown OpenPGP card status: [{0:x}, {1:x}]")]
    UnknownStatus(u8, u8),
}

impl From<(u8, u8)> for StatusBytes {
    fn from(status: (u8, u8)) -> Self {
        match (status.0, status.1) {
            (0x90, 0x00) => StatusBytes::Ok,
            (0x61, bytes) => StatusBytes::OkBytesAvailable(bytes),

            (0x62, 0x85) => StatusBytes::TerminationState,
            (0x63, 0xC0..=0xCF) => StatusBytes::PasswordNotChecked(status.1 & 0xf),
            (0x64, 0x00) => StatusBytes::ExecutionErrorNonVolatileMemoryUnchanged,
            (0x64, 0x02..=0x80) => StatusBytes::TriggeringByCard(status.1),
            (0x65, 0x01) => StatusBytes::MemoryFailure,
            (0x66, 0x00) => StatusBytes::SecurityRelatedIssues,
            (0x67, 0x00) => StatusBytes::WrongLength,
            (0x68, 0x81) => StatusBytes::LogicalChannelNotSupported,
            (0x68, 0x82) => StatusBytes::SecureMessagingNotSupported,
            (0x68, 0x83) => StatusBytes::LastCommandOfChainExpected,
            (0x68, 0x84) => StatusBytes::CommandChainingNotSupported,
            (0x69, 0x82) => StatusBytes::SecurityStatusNotSatisfied,
            (0x69, 0x83) => StatusBytes::AuthenticationMethodBlocked,
            (0x69, 0x85) => StatusBytes::ConditionOfUseNotSatisfied,
            (0x69, 0x87) => StatusBytes::ExpectedSecureMessagingDOsMissing,
            (0x69, 0x88) => StatusBytes::SMDataObjectsIncorrect,
            (0x6A, 0x80) => StatusBytes::IncorrectParametersCommandDataField,
            (0x6A, 0x82) => StatusBytes::FileOrApplicationNotFound,
            (0x6A, 0x88) => StatusBytes::ReferencedDataNotFound,
            (0x6B, 0x00) => StatusBytes::WrongParametersP1P2,
            (0x6D, 0x00) => StatusBytes::INSNotSupported,
            (0x6E, 0x00) => StatusBytes::CLANotSupported,
            (0x6F, 0x00) => StatusBytes::NoPreciseDiagnosis,
            _ => StatusBytes::UnknownStatus(status.0, status.1),
        }
    }
}