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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
//! The V2G PKI: reading ISO 15118 certificates and validating a chain to a
//! trust anchor.
//!
//! A signature that verifies proves *the key that signed these bytes made this
//! signature*, and nothing about **whose** key that was. The `GenChallenge`
//! binding in [`super`] narrows *which session*; this module is the other half.
//! Without both, a signature verified against a key from an unvalidated
//! certificate proves only that whoever sent the certificate also made it.
//!
//! # What ISO 15118 fixes
//!
//! X.509 is a large grammar and RFC 5280 §6.1 a large algorithm. ISO 15118 pins
//! almost every input to both, which is what makes this tractable in a `no_std`
//! crate with no dependencies:
//!
//! | | |
//! |---|---|
//! | Signature algorithm | `ecdsa-with-SHA256` \[V2G2-006\] (`-SHA512` for -20's higher level) |
//! | Curve | `secp256r1` \[V2G2-007\], `secp521r1` in -20 |
//! | Path length | at most 3 non-self-signed certificates \[V2G2-009\] |
//! | Certificate size | at most 800 bytes DER \[V2G2-010\]; the schema enforces it |
//! | Extensions | the set in Annex F, `KeyUsage` and `BasicConstraints` critical in every profile |
//! | Name constraints, policy mapping | not used |
//!
//! What is implemented is basic path validation — signatures, validity periods,
//! `BasicConstraints`, `pathLenConstraint`, `KeyUsage`, the critical-extension
//! rule — plus the Annex F profile of whichever leaf the caller names. A trust
//! anchor may sit anywhere in the path: \[V2G2-927\] makes a Private Operator
//! Root an anchor for SECC certificates and \[V2G2-868\] has it sign leaves
//! directly.
//!
//! # What is **not** here: revocation
//!
//! No OCSP, no CRL. A revoked contract certificate validates here exactly as a
//! live one does, and that is a limitation to plan around.
//!
//! The standard says why it is hard rather than merely undone — Annex F's note
//! that "as access to OCSP services can not be guaranteed during charging, the
//! usage of OCSP can only be recommended but not be mandatory", and
//! \[V2G2-868\] dropping it from private environments. A vehicle in a basement
//! has no path to a responder; a station has one and a clearing house behind it.
//!
//! So the answer belongs where it already exists. That back end is asking
//! whether to authorize this contract at all, and
//! [`Validated::subject_common_name`] is the `EMAID` to ask about. An OCSP
//! client inside a sans-I/O crate would put a network round trip in a charge
//! loop with a 25 ms budget.
//!
//! # Time is a real input
//!
//! [`chain::validate`] takes seconds since the Unix epoch — deliberately not
//! [`session::Instant`](crate::session::Instant), which is a monotonic count for
//! measuring timeouts where a validity period needs a wall clock. \[V2G2-886\]
//! lets a vehicle choose its own time accuracy and \[V2G2-910\] makes checking
//! validity periods a *should*, so a vehicle with no trustworthy time has to say
//! so rather than pass a number it does not have.
//!
//! # Example
//!
//! ```no_run
//! use iso15118::pnc::pki::{Profile, chain};
//! # use iso15118::pnc::{PncError, Suite};
//! # struct Backend;
//! # impl iso15118::pnc::pki::VerifyWith for Backend {
//! # fn verify_with(&self, _: Suite, _: &[u8], _: &[u8], _: &[u8]) -> Result<(), PncError> { Ok(()) }
//! # }
//! # fn f(leaf: &[u8], sub_ca: &[u8], v2g_root: &[u8], now: i64) -> Result<(), chain::ChainError> {
//! // Leaf first, as both generations put it on the wire.
//! let validated = chain::validate(
//! &[leaf, sub_ca],
//! &[v2g_root],
//! Profile::ContractCertificate,
//! now,
//! &Backend,
//! )?;
//!
//! // ...and *this* is the key a Plug & Charge signature is checked against.
//! let emaid = validated.subject_common_name();
//! let key = validated.public_key();
//! # let _ = (emaid, key);
//! # Ok(())
//! # }
//! ```
pub use ;
pub use ;
pub use DerError;
use crate;
/// Verifies a signature against a public key that arrived in a certificate.
///
/// A sibling of [`Verify`](crate::pnc::Verify) and separate from it on purpose.
/// `Verify` is implemented by a type that *holds* a key — a session's contract
/// key, a station's own key — and that is the right shape when the key is
/// configuration. A chain's keys are not configuration: each one arrives inside
/// the certificate below it, and there is a new one at every step.
///
/// `public_key` is an uncompressed SEC1 point (`0x04 ‖ X ‖ Y`), exactly as the
/// certificate carried it. `signature` is the raw `r ‖ s` pair — this crate
/// converts X.509's DER `ECDSA-Sig-Value` before calling, so a backend
/// implements one signature format rather than two.
/// The `Domain Component` a V2G Root Certificate must carry — Table F.1.
pub const DC_V2G: &str = "V2G";
/// The `Domain Component` an SECC Certificate must carry — Table F.2.
pub const DC_CPO: &str = "CPO";
/// Which Annex F profile a chain's leaf is supposed to meet.
///
/// \[V2G2-884\]: "Each certificate used in this standard shall comply to the
/// appropriate profile specified in this annex". Which profile applies is a
/// fact about *why* the caller is validating, and nothing in the certificate
/// says it — so the caller does.
///
/// That it is a parameter rather than a guess is the point of \[V2G2-925\]: a
/// leaf "shall be treated as invalid, if the trust anchor at the end of the
/// chain does not match the specific root certificate required for a certain
/// use". A chain that validates beautifully to the wrong root is exactly the
/// failure that requirement names, and only the caller knows which root was
/// meant.